// Minimal Tile Definition for TileDemo. isRhino = false; if (isRhino) { __global__ = new Object(); forward = turn = new Object(); } // Base class function Node() { } Node.prototype.name = "Node" Node.prototype.eval = function(ctxt) { return this } Node.prototype.toString = function() { return this.name } Node.prototype.ev = function() { return this.eval(Global) } Node.prototype.isNode = function() { return true }; // easy error check Node.prototype.tileClass = "Tile"; Node.prototype.printString = function () { return this.toString()}; Node.prototype.toCode = function () { return this.toString()}; // Context function Context(parent) { this.local = new Object(); this.parent = parent; } Context.prototype = new Node() Context.prototype.name = "Context" Context.prototype.get = function(name) { if (this.local[name] == undefined) return this.parent.get(name) return this.local[name] } Context.prototype.set = function(name, value) { return this.local[name] = value } Context.prototype.keys = function() { var result = new Array(); for (var key in this.local) { result.push(key); } return result; } // Answer a useful object for parts bin. // Get object for a prim value, or App for a function, etc. Context.prototype.partsAt = function(name) { var getter = new Get(new Prim(name)); var value = this.get(name); var type = value.type(); var returnType = value.type()[type.length - 1]; if (value.name == "Func" || (value.name == "Prim" && value.p.isFunction == true)) { var argTypes = new Array(); for (var i = 0; i < type.length - 1; i++) { argTypes.push((5).asNode()); // TODO: example value should depend on the type. } return new App(getter, argTypes.asNode()); } return getter; } Context.prototype.getters = function() { return this.keys().map(function(each) {return new Get(new Prim(each))}); } Global = new Context(null); Global.get = function(name) { return this.local[name] } Global.set = function(name, value) { return this.local[name] = value } // Primitive Value for Number, String, and Function function Prim(p) { this.p = p.valueOf() } Prim.prototype = new Node() Prim.prototype.name = "Prim" Prim.prototype.tileClass = "PrimTile"; Prim.prototype.toString = function() { return "[" + this.p.toString() + "]"}; Prim.prototype.eval = function(ctxt) { return this.p }; Prim.prototype.toCode = function() { return this.p.printString() }; // type is an array represent arguments with a result, // e.g. ["String","Number"] is a function String -> Number (like length) // ["Any"] is any type Prim.prototype.type = function() { return this.p.type(); } Boolean.prototype.type = function() { return ["Boolean"] }; Number.prototype.type = function() { return ["Number"] }; String.prototype.type = function() { return ["String"] }; Function.prototype.type = function() { return this.signature; } Function.prototype.signature = ["Any"]; Function.prototype.isFunction = true; Number.prototype.asNode = function() { return new Prim(this) }; String.prototype.asNode = function() { return new Prim(this) }; Function.prototype.asNode = function() { return new Prim(this) }; // Accessors function Get(prop) { this.prop = prop } Get.prototype = new Node() Get.prototype.name = "Get" Get.prototype.eval = function(ctxt) { return ctxt.get(this.prop.eval(ctxt)) } Get.prototype.toString = function() { return this.prop.p } Get.prototype.toCode = function () { return this.prop.p }; // Just a utility function String.prototype.get = function () { return new Get(this.asNode()) } function Set(prop, val) { this.prop = prop this.val = val } Set.prototype = new Node(); Set.prototype.name = "Set"; Set.prototype.tileClass = "SetTile"; Set.prototype.eval = function(ctxt) { return ctxt.set(this.prop.eval(ctxt), this.val.eval(ctxt)) } Set.prototype.toString = function() { return this.prop.toString() + " = " + this.val.toString(); } Set.prototype.toCode = function () { return "(" + this.prop.p + " = " + this.val.toCode() + ")"; } // Array function Arr(elements) { this.elements = elements } Arr.prototype = new Node() Arr.prototype.name = "Arr" Arr.prototype.tileClass = "ArrTile" Arr.prototype.map = function(f) { var newElements = new Array(this.elements.length) for (var idx = 0; idx < this.elements.length; idx++) { newElements[idx] = f(this.elements[idx]); } return newElements; } Arr.prototype.eval = function(ctxt) { var newElements = new Array(this.elements.length) for (var idx=0; idx < this.elements.length; idx++) newElements[idx] = this.elements[idx].eval(ctxt) return newElements } Arr.prototype.toString = function() { return "[" + this.elements.toString() + "]" } Arr.prototype.toCode = function () { return this.elements.reduce( function(x, y) { return x == "[" ? "[" + y.toCode() : x + "," + y.toCode() }, "[" ) + "]"; } Array.prototype.asNode = function() { return new Arr(this) } // Function Application function App(func, args) { func.isNode(); args.isNode() this.func = func this.args = args } App.prototype = new Node() App.prototype.name = "App" App.prototype.tileClass = "AppTile"; App.prototype.eval = function(ctxt) { var func = this.func.eval(ctxt); return func.apply(ctxt, this.args); } App.prototype.toString = function() { return this.func.toString() + " " + this.args.toString(); } // Get an argument node and replace it as a proto value. App.prototype.takeAt = function(index) { var result = this.args.elements[index]; this.args.elements[index] = (5).asNode(); return result; } Prim.prototype.apply = function (ctxt, args) { var primArgs = args.eval(ctxt) return (this.p).apply(ctxt, primArgs); } function Func (formals, body, env) { formals.isNode(); body.isNode(); env.isNode(); this.formals = formals; this.body = body; this.env = env; } Func.prototype = new Node(); Func.prototype.name = "Func"; Func.prototype.tileClass = "FuncTile"; Func.prototype.type = function() { var signature = new Array(); for (var i = 0; i < this.formals.elements.length; i++) { signature.push("Any"); } signature.push("Any"); return signature; } Func.prototype.apply = function(ctxt, args) { var activation = new Context(this.env); for (var idx = 0; idx < this.formals.elements.length; idx++) { activation.set(this.formals.elements[idx].p, args.elements[idx].p); } activation.set("thisContext", activation); return this.body.eval(activation); } Func.prototype.toString = function () { return "function " + this.formals.toString() + " {" + this.body.toString() + "}"; } // Answer Get node at the index in the formals Func.prototype.getNodeAt = function(index) { return new Get(this.formals.elements[index]); } function If(cond, trueBranch, falseBranch) { cond.isNode(); trueBranch.isNode(); falseBranch.isNode(); this.cond = cond; this.trueBranch = trueBranch; this.falseBranch = falseBranch; } If.prototype = new Node(); If.prototype.name = "If"; If.prototype.tileClass = "IfTile"; If.prototype.toString = function() { return "if (" + this.cond.toString() + ") " + trueBranch.toString() + " else " + falseBranch.toString() } If.prototype.eval = function(ctxt) { return (this.cond.eval(ctxt) ? this.trueBranch : this.falseBranch).eval(ctxt) } function Seq(before, after) { this.before = before; this.after = after } Seq.prototype = new Node() Seq.prototype.name = "Seq" Seq.prototype.tileClass = "SeqTile" Seq.prototype.eval = function(ctxt) { this.before.eval(ctxt) return this.after.eval(ctxt) } function Repeat(body) { body.isNode(); this.body = body; } Repeat.prototype = new Node(); Repeat.prototype.name = "Repeat"; Repeat.prototype.tileClass = "RepeatTile"; Repeat.prototype.eval = function(ctxt) { while (true) this.body.eval(ctxt) } // Primitive functions var add = function (x, y) { return x + y; }; var sub = function (x, y) { return x - y; }; var eq = function (x, y) { return x == y; }; add.signature = ["Number", "Number", "Number"]; sub.signature = ["Number", "Number", "Number"]; eq.signature = ["Any", "Any", "Boolean"]; forward.signature = ["Number", "Number"]; turn.signature = ["Number", "Number"]; Global.set("add", new Prim(add)); Global.set("sub", new Prim(sub)); Global.set("eq", new Prim(eq)); Global.set("forward", new Prim(forward)); Global.set("turn", new Prim(turn)); test1 = function () { show("-- Primitive Test --"); show((1).asNode().name == "Prim"); show((1).asNode().p == 1); // show("-- Context Test --"); // child = new Context(Global); // child.set("hello", "world"); // show(child.get("hello") == "world"); // show(Global.get("hello") == undefined); // Global.set("hop", "step"); // show(Global.get("hop") == "step"); // show(child.keys()[0] == "hello"); show("-- Accessor Test --"); var env = new Context(Global); (new Set(("magicNum").asNode(), (42).asNode())).eval(env); show((new Get(("magicNum").asNode())).eval(env) == 42); show("-- Primitive Function Test --"); show((new App( new Get(("add").asNode()), [(3).asNode(), (4).asNode()].asNode())).ev() == 7); show("-- Array Test --"); env.set("x", 4); arr = [(3).asNode(), new Get(("x").asNode())].asNode().eval(env); show(arr[0] == 3); show(arr[1] == 4); show("-- Apply Test --"); app = (new App(new Get(("add").asNode()), [(3).asNode(), new Get(("x").asNode())].asNode())) show(app.eval(env) == 7); show(app.takeAt(1).prop.p == "x"); show(app.args.elements[1].p == 5); done(); } test2 = function () { env = new Context(Global); show("-- Function Test --"); var f = new Func([("x").asNode()].asNode(), (new App(new Get(("add").asNode()), [(3).asNode(), new Get(("x").asNode())].asNode())), Global); var a = new App(f, [(5).asNode()].asNode()); show(a.ev() == 8); show("-- Function Definition Test --"); (new Set(("funcName").asNode(), f)).eval(env); show((new App(("funcName").get(), [(7).asNode()].asNode(), env)).eval(env) == 10); show("-- Type Test --"); show((1).asNode().type()[0] == "Number"); show(("hello").asNode().type()[0] == "String"); show(Global.get("add").type()[0] == "Number"); show("-- Parts Bin Prototype Test --"); env.set("seven", 7); show(env.partsAt("seven").prop.p == "seven"); show(env.partsAt("add").name == "App"); show(env.partsAt("add").ev() == 10); show(env.partsAt("funcName").eval(env) == 8); show("-- If Test --"); show(new If(new Prim(true), new Prim(5), new Prim(6)).ev() == 5); show(new If(new Prim(false), new Prim(5), new Prim(6)).ev() == 6); show("-- To Code Test --"); show(new Get(("getter").asNode()).toCode() == "getter") show((new Set(("hello").asNode(), ("world").asNode())).toCode() == "(hello = \"world\")") show([("step").asNode(), ("hello").asNode()].asNode().toCode() == "[\"step\",\"hello\"]") show((1).asNode().p == 1); done(); } test = function () { test1(); test2() } if (isRhino) { show= print; done= function() {}; Number.prototype.printString = toString; String.prototype.printString = function () { return "\"" + this.toString() + "\""; } Array.prototype.reduce = function(f, z) { var r = z; for (var idx= 0; idx < this.length; idx ++) r = f(r, this[idx]); return r; } } else { var lines= ""; show= function (line) {lines+= line + "\r\n"} done= function () { alert(lines); lines= "" } }