Skip to content

Commit

Permalink
Add support for native JS objects/functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Distad committed Mar 28, 2012
1 parent 25abd2f commit 5509cc4
Showing 1 changed file with 77 additions and 20 deletions.
97 changes: 77 additions & 20 deletions javascripts/repl.js
Expand Up @@ -22,21 +22,21 @@ StringStream.prototype.rem = function() { return this.string.substr(this.index);
function Cons(car, cdr) {
this.car = car;
this.cdr = cdr;
this.type = "Cons";
this.__type = "Cons";
}

NIL = new Cons(); // JS objects only == themselves, so this is OK for comparisons

function Symbol(sym) {
this.sym = sym;
this.type = "Symbol";
this.__type = "Symbol";
}

function Fn(form, fn) {
this.form = form;
this.fn = fn;
this.evalArgs = true;
this.type = "Fn";
this.__type = "Fn";
}

(function($) {
Expand Down Expand Up @@ -72,12 +72,14 @@ function Fn(form, fn) {
}

function init() {
var builtins = ["car", "cdr", "cons", "type",
var builtins = ["car", "cdr", "cons", "type", "call",
{"quote": {evalArgs: false}},
{"eval": {fn: evalForm}},
{"print": {fn: printForm}},
{"fn": {evalArgs: false, fn: makeFn}},
{"def": {evalArgs: false}}];
{"def": {evalArgs: false}},
{"refer": {evalArgs: false}},
{"..": {evalArgs: false, fn: jsAttrChain}}];
$.each(builtins, function(i, sym) {
var ext = {}, name = sym;
if (typeof(sym) == "object") {
Expand All @@ -89,13 +91,60 @@ function Fn(form, fn) {
$.extend(newFn, ext);
Bindings.Core.prototype[name] = newFn;
});
Bindings.Core.prototype["true"] = true;
Bindings.Core.prototype["false"] = false;
REPL.bindings = new Bindings.Core();
}

function UnterminatedInputError() {
this.restartRead = true;
}

function call() {
var jsFn = arguments[0], args = Array.apply(null, arguments).slice(1);
return jsFn.apply(jsFn, args);
}

function jsAttrChain() {
var jsObj = evalForm.call(this, arguments[0]);
var attrs = Array.apply(null, arguments).slice(1);
for (i in attrs) {
var attr = attrs[i];
switch (type(attr)) {
case "Symbol":
jsObj = jsObj[attr.sym];
break;
case "Cons":
var args = evalList.call(this, cdr(attr));
jsObj = jsObj[car(attr).sym].apply(jsObj, listToArray(args));
break;
default:
throw("attribute access requires lists or symbols");
}
}
return jsObj;
}

function refer() {
for (i in arguments) {
var ref = arguments[i];
switch (type(ref)) {
case "Cons":
this[car(ref).sym] = eval(cdr(ref));
break;
case "Symbol":
this[ref.sym] = eval(ref.sym);
break;
case "string":
this[ref] = eval(ref);
break;
default:
throw("invalid refer: " + ref);
}
}
return NIL;
}

function def(sym, binding) {
if (type(sym) != "Symbol") throw("must bind to symbol");
if (binding === undefined)
Expand All @@ -117,7 +166,8 @@ function Fn(form, fn) {
function cdr(cons) { return cons.cdr; }

function type(form) {
return form.type || typeof(form);
if (form === null) return "null";
return form.__type || typeof(form);
}

REPL.readerFn = function(input) {
Expand Down Expand Up @@ -178,7 +228,7 @@ function Fn(form, fn) {
function readList(input, idx) {
input.jump();
var cur = input.getc();
if (cur == ".") {
if (cur == "." && input.peek() == " ") {
var form = readForm(input);
input.jump();
if (input.getc() != ")") throw('only one form may come after " . "');
Expand Down Expand Up @@ -207,23 +257,30 @@ function Fn(form, fn) {

function evalForm(form) {
switch (type(form)) {
case "number":
case "string":
return form;
case "Symbol":
var binding = this[form.sym];
if (binding === undefined) throw("undefined symbol: " + form.sym);
if (binding === null) throw("unbound symbol: " + form.sym);
return binding;
case "Cons":
var fn = evalForm.call(this, car(form));
if (type(fn) != "Fn") throw(printForm(fn) + " is not a function");
var args = cdr(form);
if (fn.evalArgs) args = evalList.call(this, args);
this.__curForm = form;
var rval = fn.fn.apply(this, listToArray(args));
this.__curForm = undefined;
return rval;

if (type(fn) == "function") {
var args = listToArray(evalList.call(this, cdr(form)));
return fn.call(this, args);
} else if (type(fn) == "Fn") {
var args = cdr(form);
if (fn.evalArgs) args = evalList.call(this, args);

this.__curForm = form;
var rval = fn.fn.apply(this, listToArray(args));
this.__curForm = undefined;
return rval;

} else
throw(printForm(fn) + " is not a function");
default:
return form;
}
}

Expand All @@ -245,10 +302,10 @@ function Fn(form, fn) {
}

function printForm(form) {
if (form === null) return "null";
if (form === undefined) return "undefined";
if (form == NIL) return "()";
switch(type(form)) {
case "number":
return form.toString();
case "string":
return '"'+form+'"';
case "Symbol":
Expand All @@ -258,7 +315,7 @@ function Fn(form, fn) {
case "Fn":
return printForm(form.form);
default:
throw('type "'+type(form)+'" is invalid');
return form.toString();
}
}

Expand Down

0 comments on commit 5509cc4

Please sign in to comment.