[added userdata objects for native methods to work on (2007-03-13)
kragen@pobox.com**20070412112433] {
hunk ./bicicleta.ml 11
+let sys_expr = Call(Name "prog", "sys") ;;
hunk ./bicicleta.ml 16
+          (* XXX maybe unify Literal+AnonLiteral,
+             Derivation+AnonDerivation, using string option? *)
hunk ./bicicleta.ml 24
-    | StringConstant string -> BicStr string
-    | Number num -> BicStr num  (* XXX for now *)
+    | Number num -> wrap_userdata env num "integer"
+    | StringConstant string -> wrap_userdata env string "string"
hunk ./bicicleta.ml 27
+  and wrap_userdata env datum typename = 
+    let userdata = BicStr datum  (* XXX for now *)
+    in eval env (AnonDerivation(Call(sys_expr, typename),
+        Definition("userdata", NativeMethod (fun _ -> userdata), NoDefs)))                               
hunk ./bicicleta.ml 43
-    | Derive(name, methodself, body, env, next) -> 
+    | Derive(_, methodself, body, env, _) ->
hunk ./bicicleta.ml 46
-          | none -> eval env body)
+          | none -> eval env body)  (* XXX why does this compile?! lc none *)
hunk ./bicicleta.ml 48
-    | _ -> (Error("trying to apply non-method", "")) ;;
+    | _ -> (Error("trying to apply non-method", ""))
+;;
hunk ./bicicleta.ml 208
-    assert ((eval Phi (StringConstant "foo")) = BicStr "foo") ;
-    assert ((eval Phi (Call(Literal("self", 
-           Definition("foo", StringConstant("quux"), NoDefs)), "foo"))) 
-           = BicStr "quux") ;
+    let string_value_is expr str = 
+      (eval Phi (Call(expr, "userdata"))) = BicStr str
+    in
+    assert (string_value_is (StringConstant "foo") "foo") ;
+    assert (string_value_is (Call(Literal("self", 
+               Definition("foo", StringConstant("quux"), NoDefs)), "foo"))
+           "quux") ;
hunk ./bicicleta.ml 240
-    assert ((eval Phi (Call(Call(Call(booleans, "true"), "if_true"), "()"))) 
-           = BicStr "no consequent given") ;
-    assert ((eval Phi (Call(Derivation(Call(Call(booleans, "true"), 
+    assert (string_value_is(Call(Call(Call(booleans, "true"), 
+                                     "if_true"), "()"))
+                           "no consequent given") ;
+    assert (string_value_is(Call(Derivation(Call(Call(booleans, "true"), 
hunk ./bicicleta.ml 248
-           "()"))) 
-           = BicStr "is true") ;
-    assert ((eval Phi (Call(Derivation(Call(Call(booleans, "false"),
+           "()")) 
+           "is true") ;
+    assert (string_value_is (Call(Derivation(Call(Call(booleans, "false"),
hunk ./bicicleta.ml 255
-           "()"))) 
-           = BicStr "is false") ;
-
-    (* evaluation with native method *)
-    let native_add = NativeMethod (fun env -> 
-      let x = eval env (Call(Name "self", "arg1"))
-      and y = eval env (Call(Name "self", "arg2"))
-      in match (x, y) with
-          (BicStr xs, BicStr ys) -> 
-            BicStr (string_of_int (int_of_string xs + int_of_string ys))
-        | _ -> Error ("invalid addition args", "??"))  (* saving the invalid args is a pain *)
-    in let adder = Literal("self", Definition("()", native_add, NoDefs))
-    in
-    assert((eval Phi (Call(
-        AnonDerivation(adder, 
-                      Definition("arg1", StringConstant "3", 
-                      Definition("arg2", StringConstant "4", 
-                      NoDefs))
-        ), "()"))) = BicStr "7");
+           "()")) 
+           "is false") ;
hunk ./bicicleta.ml 415
-    assert((eval Phi (parse "{}{\n\tx = \"foo\"\n\ty=\"bar\"}.x"))
-        = BicStr("foo"));
-    assert((eval Phi (parse "{\n\tx = \"foo\"\n\ty=\"bar\"}.y"))
-        = BicStr("bar"));
+    assert(string_value_is (parse "{}{\n\tx = \"foo\"\n\ty=\"bar\"}.x") "foo");
+    assert(string_value_is (parse "{\n\tx = \"foo\"\n\ty=\"bar\"}.y") "bar");
+
+    (* evaluation with native method *)
+    let native_add = NativeMethod (fun env -> 
+      let x = eval env (Call(Name "method", "arg1"))
+      and y = eval env (Call(Name "method", "arg2"))
+      in match (x, y) with
+          (BicStr xs, BicStr ys) -> 
+            BicStr (string_of_int (int_of_string xs + int_of_string ys))
+              (* saving the invalid args is a pain, so we don't bother *)
+        | _ -> Error ("invalid addition args", "??"))
+    in let intrinsics = Derive("integer_add", Some "method", native_add, Phi,
+                              ProtoObject)
+    in let addition = parse "intrinsics { 3.userdata, 4.userdata }.integer_add"
+    in let seven = eval (Add("intrinsics", intrinsics, Phi)) addition
+    in 
+    assert(seven = BicStr "7");
+      (* XXX the above is a little strange.  If we just eval "3" in an
+         environment without "prog", we get an evaluation error saying
+         it needs "prog"; and likewise if we eval "3.beanstalk"; but
+         if we eval "3.userdata", we get the right thing.  How does
+         that work? *)
hunk ./changes 35
+- I wonder if I should disallow positional args in {}?  I couldn't
+  figure out why this wasn't working:
+    three {self:
+        add = native_add
+        '+' = {m: self.add(self, m.arg1)}
+    } + four
+  But maybe UI decisions like that should wait until I have a UI.
addfile ./natives
hunk ./natives 1
-
+How do I do "primitive" or "native" methods?  If I stick native data
+in ProtoObject, then I can attach methods to the native data just like
+I would to anything else.
+
+I'm a little unhappy with having two different kinds of native objects
+--- native methods and native objects.  But if I am going to have only
+one, then it needs to be possible to derive from it, since there's no
+other way in the language to bring two things together.
+
+Maybe the solution is for the object to have a "metatable" that
+handles method lookup.
+
+Oh hey.  Here we go.  objectget key on ProtoObject currently has a
+single base case that returns "method not found".  Suppose instead
+that we specialize that on the encapsulated native object to return
+various other things, like sums and quotients and the like?  Whatever
+we return from objectget will be applied to the whole object, so it
+can get other things from the object, like args.  Then addition
+becomes something like
+
+    '+' = {op: arg1 = 3, '()' = self.userdata{other=op.arg1.userdata}.add}.
+
+which is slightly less concise than 
+
+    add = {op: arg1 = 3, '()' = self.userdata.add(op.arg1.userdata)}
+
+but can be factored into
+
+    primops = {op: arg1 = 3, '()' = self.userdata{other=op.arg1.userdata}}
+    '+' = {op: arg1 = 3, '()' = self.primops(op.arg1).add}.
+
+So then we have an objectget_int and an objectget_string and so on.
+objectget_int of "add" returns a method (! objectget always returns
+methods!) which, when applied to the object, will fetch its
+nativedata, call its "other" method, fetch the nativedata from that,
+and then add them together and return the result.  Unfortunately this
+still needs some kind of NativeMethod object.
+
+Can we get by with just NativeMethod objects instead of putting
+userdata into ProtoObject?  NativeMethod just encapsulates a function
+that returns a bicobj, so apparently not --- even if your NativeMethod
+calls other NativeMethods on the same object, those methods need some
+way to communicate, and all that's available is for them to return
+bicobjs...
+
+OK, all this stuff is complicated.  Here's a simpler approach.
+
+Userdata objects have no methods and you can't inherit from them.  We
+put our native methods, which you *can* inherit from and which have a
+'()' method, in a "primitives" namespace wrapped around "prog"; native
+methods include things like "integer_add", which takes two arguments.
+
+The expression "3" is evaluated as the expression 
+"prog.sys.integer { userdata = x }", where x is the userdata object
+representing the number 3.  This gives prog.sys.integer an opportunity
+to provide whatever methods it wants, say, '+', which can do things
+like call "primitives.integer_add" with userdata objects as arguments,
+and wrap the result in a prog.sys.integer.
+
+There is no wrapped primitive object in ProtoObject.
}
