diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-02 22:23:37 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-02 22:23:37 -0500 |
| commit | ea81a8087bcd7953b083a2be9db447f75e7ebf56 (patch) | |
| tree | 6cf47a2dbd55d42efc4a901eaabdec952f40ce89 /js | |
| parent | 1617910ad342a55762f3ddabb975849d843cff85 (diff) | |
| download | mal-ea81a8087bcd7953b083a2be9db447f75e7ebf56.tar.gz mal-ea81a8087bcd7953b083a2be9db447f75e7ebf56.zip | |
All: split types into types, env, printer, core.
- types: low-level mapping to the implementation language.
- core: functions on types that are exposed directly to mal.
- printer: implementation called by pr-str, str, prn, println.
- env: the environment implementation
- Also, unindent all TCO while loops so that the diff of step4 and
step5 are minimized.
Diffstat (limited to 'js')
| -rw-r--r-- | js/Makefile | 3 | ||||
| -rw-r--r-- | js/core.js | 193 | ||||
| -rw-r--r-- | js/env.js | 40 | ||||
| -rw-r--r-- | js/printer.js | 44 | ||||
| -rw-r--r-- | js/reader.js | 18 | ||||
| -rw-r--r-- | js/step1_read_print.js | 3 | ||||
| -rw-r--r-- | js/step2_eval.js | 13 | ||||
| -rw-r--r-- | js/step3_env.js | 18 | ||||
| -rw-r--r-- | js/step4_if_fn_do.js | 25 | ||||
| -rw-r--r-- | js/step5_tco.js | 96 | ||||
| -rw-r--r-- | js/step6_file.js | 96 | ||||
| -rw-r--r-- | js/step7_quote.js | 113 | ||||
| -rw-r--r-- | js/step8_macros.js | 135 | ||||
| -rw-r--r-- | js/step9_interop.js | 147 | ||||
| -rw-r--r-- | js/stepA_more.js | 165 | ||||
| -rw-r--r-- | js/types.js | 423 |
16 files changed, 810 insertions, 722 deletions
diff --git a/js/Makefile b/js/Makefile index cb57644..2b7aa88 100644 --- a/js/Makefile +++ b/js/Makefile @@ -1,7 +1,8 @@ TESTS = tests/types.js tests/reader.js tests/step5_tco.js -SOURCES = node_readline.js types.js reader.js stepA_more.js +SOURCES = node_readline.js types.js reader.js printer.js \ + env.js core.js stepA_more.js WEB_SOURCES = $(SOURCES:node_readline.js=josh_readline.js) all: mal.js mal_web.js diff --git a/js/core.js b/js/core.js new file mode 100644 index 0000000..48bbe16 --- /dev/null +++ b/js/core.js @@ -0,0 +1,193 @@ +// Node vs browser behavior +var core = {}; +if (typeof module === 'undefined') { + var exports = core; +} else { + var types = require('./types'), + printer = require('./printer'); +} + +// Errors/Exceptions +function mal_throw(exc) { throw exc; } + + +// String functions +function pr_str() { + return Array.prototype.map.call(arguments,function(exp) { + return printer._pr_str(exp, true); + }).join(" "); +} + +function str() { + return Array.prototype.map.call(arguments,function(exp) { + return printer._pr_str(exp, false); + }).join(""); +} + +function prn() { + printer.print.apply({}, Array.prototype.map.call(arguments,function(exp) { + return printer._pr_str(exp, true); + })); +} + +function println() { + printer.print.apply({}, Array.prototype.map.call(arguments,function(exp) { + return printer._pr_str(exp, false); + })); +} + + +// Hash Map functions +function assoc(src_hm) { + var hm = types._clone(src_hm); + var args = [hm].concat(Array.prototype.slice.call(arguments, 1)); + return types._assoc_BANG.apply(null, args); +} + +function dissoc(src_hm) { + var hm = types._clone(src_hm); + var args = [hm].concat(Array.prototype.slice.call(arguments, 1)); + return types._dissoc_BANG.apply(null, args); +} + +function get(hm, key) { + if (key in hm) { + return hm[key]; + } else { + return null; + } +} + +function contains_Q(hm, key) { + if (key in hm) { return true; } else { return false; } +} + +function keys(hm) { return Object.keys(hm); } +function vals(hm) { return Object.keys(hm).map(function(k) { return hm[k]; }); } + + +// Sequence functions +function cons(a, b) { return [a].concat(b); } + +function concat(lst) { + lst = lst || []; + return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1)); +} + +function nth(lst, idx) { return lst[idx]; } + +function first(lst) { return lst[0]; } + +function rest(lst) { return lst.slice(1); } + +function empty_Q(lst) { return lst.length === 0; } + +function count(s) { + if (Array.isArray(s)) { return s.length; } + else { return Object.keys(s).length; } +} + +function conj(lst) { + if (types._list_Q(lst)) { + return Array.prototype.slice.call(arguments, 1).reverse().concat(lst); + } else { + var v = lst.concat(Array.prototype.slice.call(arguments, 1)); + v.__isvector__ = true; + return v; + } +} + +function apply(f) { + var args = Array.prototype.slice.call(arguments, 1); + return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1])); +} + +function map(f, lst) { + return lst.map(function(el){ return f(el); }); +} + + +// Metadata functions +function with_meta(obj, m) { + var new_obj = types._clone(obj); + new_obj.__meta__ = m; + return new_obj; +} + +function meta(obj) { + // TODO: support symbols and atoms + if ((!types._sequential_Q(obj)) && + (!(types._hash_map_Q(obj))) && + (!(types._function_Q(obj)))) { + throw new Error("attempt to get metadata from: " + types._obj_type(obj)); + } + return obj.__meta__; +} + + +// Atom functions +function deref(atm) { return atm.val; } +function reset_BANG(atm, val) { return atm.val = val; } +function swap_BANG(atm, f) { + var args = [atm.val].concat(Array.prototype.slice.call(arguments, 2)); + atm.val = f.apply(f, args); + return atm.val; +} + + +// types.ns is namespace of type functions +var ns = {'type': types._obj_type, + '=': types._equal_Q, + 'throw': mal_throw, + 'nil?': types._nil_Q, + 'true?': types._true_Q, + 'false?': types._false_Q, + 'symbol': types._symbol, + 'symbol?': types._symbol_Q, + 'pr-str': pr_str, + 'str': str, + 'prn': prn, + 'println': println, + '<' : function(a,b){return a<b;}, + '<=' : function(a,b){return a<=b;}, + '>' : function(a,b){return a>b;}, + '>=' : function(a,b){return a>=b;}, + '+' : function(a,b){return a+b;}, + '-' : function(a,b){return a-b;}, + '*' : function(a,b){return a*b;}, + '/' : function(a,b){return a/b;}, + + 'list': types._list, + 'list?': types._list_Q, + 'vector': types._vector, + 'vector?': types._vector_Q, + 'hash-map': types._hash_map, + 'map?': types._hash_map_Q, + 'assoc': assoc, + 'dissoc': dissoc, + 'get': get, + 'contains?': contains_Q, + 'keys': keys, + 'vals': vals, + + 'sequential?': types._sequential_Q, + 'cons': cons, + 'concat': concat, + 'nth': nth, + 'first': first, + 'rest': rest, + 'empty?': empty_Q, + 'count': count, + 'conj': conj, + 'apply': apply, + 'map': map, + + 'with-meta': with_meta, + 'meta': meta, + 'atom': types._atom, + 'atom?': types._atom_Q, + "deref": deref, + "reset!": reset_BANG, + "swap!": swap_BANG}; + +exports.ns = core.ns = ns; diff --git a/js/env.js b/js/env.js new file mode 100644 index 0000000..3c9eac8 --- /dev/null +++ b/js/env.js @@ -0,0 +1,40 @@ +// Node vs browser behavior +var env = {}; +if (typeof module === 'undefined') { + var exports = env; +} + +// Env implementation +function Env(outer, binds, exprs) { + this.data = {}; + this.outer = outer || null; + + if (binds && exprs) { + // Returns a new Env with symbols in binds bound to + // corresponding values in exprs + // TODO: check types of binds and exprs and compare lengths + for (var i=0; i<binds.length;i++) { + if (binds[i].value === "&") { + // variable length arguments + this.data[binds[i+1].value] = Array.prototype.slice.call(exprs, i); + break; + } else { + this.data[binds[i].value] = exprs[i]; + } + } + } + return this; +} +Env.prototype.find = function (key) { + if (key in this.data) { return this; } + else if (this.outer) { return this.outer.find(key); } + else { return null; } +}; +Env.prototype.set = function(key, value) { this.data[key] = value; return value; }, +Env.prototype.get = function(key) { + var env = this.find(key); + if (!env) { throw new Error("'" + key + "' not found"); } + return env.data[key]; +}; + +exports.Env = env.Env = Env; diff --git a/js/printer.js b/js/printer.js new file mode 100644 index 0000000..0d84cc6 --- /dev/null +++ b/js/printer.js @@ -0,0 +1,44 @@ +// Node vs browser behavior +var printer = {}; +if (typeof module !== 'undefined') { + var types = require('./types'); + // map output/print to console.log + var print = exports.print = function () { console.log.apply(console, arguments); }; +} else { + var exports = printer; +} + +function _pr_str(obj, print_readably) { + if (typeof print_readably === 'undefined') { print_readably = true; } + var _r = print_readably; + var ot = types._obj_type(obj); + switch (ot) { + case 'list': + var ret = obj.map(function(e) { return _pr_str(e,_r); }); + return "(" + ret.join(' ') + ")"; + case 'vector': + var ret = obj.map(function(e) { return _pr_str(e,_r); }); + return "[" + ret.join(' ') + "]"; + case 'hash-map': + var ret = []; + for (var k in obj) { + ret.push(_pr_str(k,_r), _pr_str(obj[k],_r)); + } + return "{" + ret.join(' ') + "}"; + case 'string': + if (print_readably) { + return '"' + obj.replace(/\\/, "\\\\").replace(/"/g, '\\"') + '"'; + } else { + return obj; + } + case 'nil': + return "nil"; + case 'atom': + return "(atom " + _pr_str(obj.val,_r) + ")"; + default: + return obj.toString(); + } +} + +exports._pr_str = printer._pr_str = _pr_str; + diff --git a/js/reader.js b/js/reader.js index da51088..f19010d 100644 --- a/js/reader.js +++ b/js/reader.js @@ -40,7 +40,7 @@ function read_atom (reader) { } else if (token === "false") { return false; } else { - return types.symbol(token); // symbol + return types._symbol(token); // symbol } } @@ -66,13 +66,13 @@ function read_list(reader, start, end) { // read vector of tokens function read_vector(reader) { var lst = read_list(reader, '[', ']'); - return types.vector.apply(types.vector, lst); + return types._vector.apply(null, lst); } // read hash-map key/value pairs function read_hash_map(reader) { var lst = read_list(reader, '{', '}'); - return types.hash_map.apply(types.hash_map, lst); + return types._hash_map.apply(null, lst); } function read_form(reader) { @@ -81,18 +81,18 @@ function read_form(reader) { // reader macros/transforms case ';': return null; // Ignore comments case '\'': reader.next(); - return [types.symbol('quote'), read_form(reader)]; + return [types._symbol('quote'), read_form(reader)]; case '`': reader.next(); - return [types.symbol('quasiquote'), read_form(reader)]; + return [types._symbol('quasiquote'), read_form(reader)]; case '~': reader.next(); - return [types.symbol('unquote'), read_form(reader)]; + return [types._symbol('unquote'), read_form(reader)]; case '~@': reader.next(); - return [types.symbol('splice-unquote'), read_form(reader)]; + return [types._symbol('splice-unquote'), read_form(reader)]; case '^': reader.next(); var meta = read_form(reader); - return [types.symbol('with-meta'), read_form(reader), meta]; + return [types._symbol('with-meta'), read_form(reader), meta]; case '@': reader.next(); - return [types.symbol('deref'), read_form(reader)]; + return [types._symbol('deref'), read_form(reader)]; // list case ')': throw new Error("unexpected ')'"); diff --git a/js/step1_read_print.js b/js/step1_read_print.js index ee027d7..264b6c6 100644 --- a/js/step1_read_print.js +++ b/js/step1_read_print.js @@ -1,5 +1,6 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -16,7 +17,7 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl diff --git a/js/step2_eval.js b/js/step2_eval.js index f2cb8b1..f5efa2c 100644 --- a/js/step2_eval.js +++ b/js/step2_eval.js @@ -1,5 +1,6 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,15 +12,15 @@ function READ(str) { // eval function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env[ast]; - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -31,7 +32,7 @@ function eval_ast(ast, env) { } function _EVAL(ast, env) { - if (!types.list_Q(ast)) { + if (!types._list_Q(ast)) { return eval_ast(ast, env); } @@ -47,7 +48,7 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl diff --git a/js/step3_env.js b/js/step3_env.js index 5b6e802..41e21c1 100644 --- a/js/step3_env.js +++ b/js/step3_env.js @@ -1,5 +1,7 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,15 +13,15 @@ function READ(str) { // eval function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -31,7 +33,7 @@ function eval_ast(ast, env) { } function _EVAL(ast, env) { - if (!types.list_Q(ast)) { + if (!types._list_Q(ast)) { return eval_ast(ast, env); } @@ -42,7 +44,7 @@ function _EVAL(ast, env) { var res = EVAL(a2, env); return env.set(a1, res); case "let*": - var let_env = new types.Env(env); + var let_env = new Env(env); for (var i=0; i < a1.length; i+=2) { let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } @@ -60,11 +62,11 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } diff --git a/js/step4_if_fn_do.js b/js/step4_if_fn_do.js index d33ec04..37803ef 100644 --- a/js/step4_if_fn_do.js +++ b/js/step4_if_fn_do.js @@ -1,5 +1,8 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; +var core = require('./core'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,15 +14,15 @@ function READ(str) { // eval function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -31,7 +34,7 @@ function eval_ast(ast, env) { } function _EVAL(ast, env) { - if (!types.list_Q(ast)) { + if (!types._list_Q(ast)) { return eval_ast(ast, env); } @@ -42,7 +45,7 @@ function _EVAL(ast, env) { var res = EVAL(a2, env); return env.set(a1, res); case "let*": - var let_env = new types.Env(env); + var let_env = new Env(env); for (var i=0; i < a1.length; i+=2) { let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } @@ -59,7 +62,7 @@ function _EVAL(ast, env) { } case "fn*": return function() { - return EVAL(a2, new types.Env(env, a1, arguments)); + return EVAL(a2, new Env(env, a1, arguments)); }; default: var el = eval_ast(ast, env), f = el[0]; @@ -74,16 +77,16 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } -// Import types functions -for (var n in types.ns) { repl_env.set(n, types.ns[n]); } +// Import core functions +for (var n in core.ns) { repl_env.set(n, core.ns[n]); } // Defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/js/step5_tco.js b/js/step5_tco.js index 20a9583..2d1793d 100644 --- a/js/step5_tco.js +++ b/js/step5_tco.js @@ -1,5 +1,8 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; +var core = require('./core'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,15 +14,15 @@ function READ(str) { // eval function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -32,45 +35,48 @@ function eval_ast(ast, env) { function _EVAL(ast, env) { while (true) { - if (!types.list_Q(ast)) { - return eval_ast(ast, env); + + //console.log("EVAL:", types._pr_str(ast, true)); + if (!types._list_Q(ast)) { + return eval_ast(ast, env); + } + + // apply list + var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; + switch (a0.value) { + case "def!": + var res = EVAL(a2, env); + return env.set(a1, res); + case "let*": + var let_env = new Env(env); + for (var i=0; i < a1.length; i+=2) { + let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } - - // apply list - var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; - switch (a0.value) { - case "def!": - var res = EVAL(a2, env); - return env.set(a1, res); - case "let*": - var let_env = new types.Env(env); - for (var i=0; i < a1.length; i+=2) { - let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); - } - return EVAL(a2, let_env); - case "do": - eval_ast(ast.slice(1, -1), env); - ast = ast[ast.length-1]; - break; - case "if": - var cond = EVAL(a1, env); - if (cond === null || cond === false) { - ast = (typeof a3 !== "undefined") ? a3 : null; - } else { - ast = a2; - } - break; - case "fn*": - return types.new_function(EVAL, a2, env, a1); - default: - var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; - if (meta && meta.exp) { - ast = meta.exp; - env = new types.Env(meta.env, meta.params, el.slice(1)); - } else { - return f.apply(f, el.slice(1)); - } + return EVAL(a2, let_env); + case "do": + eval_ast(ast.slice(1, -1), env); + ast = ast[ast.length-1]; + break; + case "if": + var cond = EVAL(a1, env); + if (cond === null || cond === false) { + ast = (typeof a3 !== "undefined") ? a3 : null; + } else { + ast = a2; } + break; + case "fn*": + return types._function(EVAL, Env, a2, env, a1); + default: + var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; + if (meta && meta.exp) { + ast = meta.exp; + env = new Env(meta.env, meta.params, el.slice(1)); + } else { + return f.apply(f, el.slice(1)); + } + } + } } @@ -81,16 +87,16 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } -// Import types functions -for (var n in types.ns) { repl_env.set(n, types.ns[n]); } +// Import core functions +for (var n in core.ns) { repl_env.set(n, core.ns[n]); } // Defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); diff --git a/js/step6_file.js b/js/step6_file.js index 6a014eb..df216da 100644 --- a/js/step6_file.js +++ b/js/step6_file.js @@ -1,5 +1,8 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; +var core = require('./core'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,15 +14,15 @@ function READ(str) { // eval function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -32,45 +35,48 @@ function eval_ast(ast, env) { function _EVAL(ast, env) { while (true) { - if (!types.list_Q(ast)) { - return eval_ast(ast, env); + + //console.log("EVAL:", types._pr_str(ast, true)); + if (!types._list_Q(ast)) { + return eval_ast(ast, env); + } + + // apply list + var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; + switch (a0.value) { + case "def!": + var res = EVAL(a2, env); + return env.set(a1, res); + case "let*": + var let_env = new Env(env); + for (var i=0; i < a1.length; i+=2) { + let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } - - // apply list - var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; - switch (a0.value) { - case "def!": - var res = EVAL(a2, env); - return env.set(a1, res); - case "let*": - var let_env = new types.Env(env); - for (var i=0; i < a1.length; i+=2) { - let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); - } - return EVAL(a2, let_env); - case "do": - eval_ast(ast.slice(1, -1), env); - ast = ast[ast.length-1]; - break; - case "if": - var cond = EVAL(a1, env); - if (cond === null || cond === false) { - ast = (typeof a3 !== "undefined") ? a3 : null; - } else { - ast = a2; - } - break; - case "fn*": - return types.new_function(EVAL, a2, env, a1); - default: - var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; - if (meta && meta.exp) { - ast = meta.exp; - env = new types.Env(meta.env, meta.params, el.slice(1)); - } else { - return f.apply(f, el.slice(1)); - } + return EVAL(a2, let_env); + case "do": + eval_ast(ast.slice(1, -1), env); + ast = ast[ast.length-1]; + break; + case "if": + var cond = EVAL(a1, env); + if (cond === null || cond === false) { + ast = (typeof a3 !== "undefined") ? a3 : null; + } else { + ast = a2; } + break; + case "fn*": + return types._function(EVAL, Env, a2, env, a1); + default: + var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; + if (meta && meta.exp) { + ast = meta.exp; + env = new Env(meta.env, meta.params, el.slice(1)); + } else { + return f.apply(f, el.slice(1)); + } + } + } } @@ -81,16 +87,16 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } -// Import types functions -for (var n in types.ns) { repl_env.set(n, types.ns[n]); } +// Import core functions +for (var n in core.ns) { repl_env.set(n, core.ns[n]); } _ref('read-string', reader.read_str); _ref('eval', function(ast) { return EVAL(ast, repl_env); }); diff --git a/js/step7_quote.js b/js/step7_quote.js index 6d23595..9721d59 100644 --- a/js/step7_quote.js +++ b/js/step7_quote.js @@ -1,5 +1,8 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; +var core = require('./core'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,31 +14,31 @@ function READ(str) { // eval function is_pair(x) { - return types.sequential_Q(x) && x.length > 0; + return types._sequential_Q(x) && x.length > 0; } function quasiquote(ast) { if (!is_pair(ast)) { - return [types.symbol("quote"), ast]; + return [types._symbol("quote"), ast]; } else if (ast[0].value === 'unquote') { return ast[1]; } else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') { - return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; + return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; } else { - return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; + return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; } } function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -48,50 +51,52 @@ function eval_ast(ast, env) { function _EVAL(ast, env) { while (true) { - //console.log("EVAL:", types._pr_str(ast, true)); - if (!types.list_Q(ast)) { - return eval_ast(ast, env); + + //console.log("EVAL:", types._pr_str(ast, true)); + if (!types._list_Q(ast)) { + return eval_ast(ast, env); + } + + // apply list + var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; + switch (a0.value) { + case "def!": + var res = EVAL(a2, env); + return env.set(a1, res); + case "let*": + var let_env = new Env(env); + for (var i=0; i < a1.length; i+=2) { + let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } - - // apply list - var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; - switch (a0.value) { - case "def!": - var res = EVAL(a2, env); - return env.set(a1, res); - case "let*": - var let_env = new types.Env(env); - for (var i=0; i < a1.length; i+=2) { - let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); - } - return EVAL(a2, let_env); - case "quote": - return a1; - case "quasiquote": - return EVAL(quasiquote(a1), env); - case "do": - eval_ast(ast.slice(1, -1), env); - ast = ast[ast.length-1]; - break; - case "if": - var cond = EVAL(a1, env); - if (cond === null || cond === false) { - ast = (typeof a3 !== "undefined") ? a3 : null; - } else { - ast = a2; - } - break; - case "fn*": - return types.new_function(EVAL, a2, env, a1); - default: - var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; - if (meta && meta.exp) { - ast = meta.exp; - env = new types.Env(meta.env, meta.params, el.slice(1)); - } else { - return f.apply(f, el.slice(1)); - } + return EVAL(a2, let_env); + case "quote": + return a1; + case "quasiquote": + return EVAL(quasiquote(a1), env); + case "do": + eval_ast(ast.slice(1, -1), env); + ast = ast[ast.length-1]; + break; + case "if": + var cond = EVAL(a1, env); + if (cond === null || cond === false) { + ast = (typeof a3 !== "undefined") ? a3 : null; + } else { + ast = a2; } + break; + case "fn*": + return types._function(EVAL, Env, a2, env, a1); + default: + var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; + if (meta && meta.exp) { + ast = meta.exp; + env = new Env(meta.env, meta.params, el.slice(1)); + } else { + return f.apply(f, el.slice(1)); + } + } + } } @@ -102,16 +107,16 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } -// Import types functions -for (var n in types.ns) { repl_env.set(n, types.ns[n]); } +// Import core functions +for (var n in core.ns) { repl_env.set(n, core.ns[n]); } _ref('read-string', reader.read_str); _ref('eval', function(ast) { return EVAL(ast, repl_env); }); diff --git a/js/step8_macros.js b/js/step8_macros.js index 65d7a87..3ad3e31 100644 --- a/js/step8_macros.js +++ b/js/step8_macros.js @@ -1,5 +1,8 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; +var core = require('./core'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,24 +14,24 @@ function READ(str) { // eval function is_pair(x) { - return types.sequential_Q(x) && x.length > 0; + return types._sequential_Q(x) && x.length > 0; } function quasiquote(ast) { if (!is_pair(ast)) { - return [types.symbol("quote"), ast]; + return [types._symbol("quote"), ast]; } else if (ast[0].value === 'unquote') { return ast[1]; } else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') { - return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; + return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; } else { - return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; + return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; } } function is_macro_call(ast, env) { - return types.list_Q(ast) && - types.symbol_Q(ast[0]) && + return types._list_Q(ast) && + types._symbol_Q(ast[0]) && env.find(ast[0].value) && env.get(ast[0].value)._ismacro_; } @@ -42,15 +45,15 @@ function macroexpand(ast, env) { } function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -63,59 +66,61 @@ function eval_ast(ast, env) { function _EVAL(ast, env) { while (true) { - //console.log("EVAL:", types._pr_str(ast, true)); - if (!types.list_Q(ast)) { - return eval_ast(ast, env); + + //console.log("EVAL:", types._pr_str(ast, true)); + if (!types._list_Q(ast)) { + return eval_ast(ast, env); + } + + // apply list + ast = macroexpand(ast, env); + if (!types._list_Q(ast)) { return ast; } + + var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; + switch (a0.value) { + case "def!": + var res = EVAL(a2, env); + return env.set(a1, res); + case "let*": + var let_env = new Env(env); + for (var i=0; i < a1.length; i+=2) { + let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } - - // apply list - ast = macroexpand(ast, env); - if (!types.list_Q(ast)) { return ast; } - - var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; - switch (a0.value) { - case "def!": - var res = EVAL(a2, env); - return env.set(a1, res); - case "let*": - var let_env = new types.Env(env); - for (var i=0; i < a1.length; i+=2) { - let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); - } - return EVAL(a2, let_env); - case "quote": - return a1; - case "quasiquote": - return EVAL(quasiquote(a1), env); - case 'defmacro!': - var func = EVAL(a2, env); - func._ismacro_ = true; - return env.set(a1, func); - case 'macroexpand': - return macroexpand(a1, env); - case "do": - eval_ast(ast.slice(1, -1), env); - ast = ast[ast.length-1]; - break; - case "if": - var cond = EVAL(a1, env); - if (cond === null || cond === false) { - ast = (typeof a3 !== "undefined") ? a3 : null; - } else { - ast = a2; - } - break; - case "fn*": - return types.new_function(EVAL, a2, env, a1); - default: - var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; - if (meta && meta.exp) { - ast = meta.exp; - env = new types.Env(meta.env, meta.params, el.slice(1)); - } else { - return f.apply(f, el.slice(1)); - } + return EVAL(a2, let_env); + case "quote": + return a1; + case "quasiquote": + return EVAL(quasiquote(a1), env); + case 'defmacro!': + var func = EVAL(a2, env); + func._ismacro_ = true; + return env.set(a1, func); + case 'macroexpand': + return macroexpand(a1, env); + case "do": + eval_ast(ast.slice(1, -1), env); + ast = ast[ast.length-1]; + break; + case "if": + var cond = EVAL(a1, env); + if (cond === null || cond === false) { + ast = (typeof a3 !== "undefined") ? a3 : null; + } else { + ast = a2; } + break; + case "fn*": + return types._function(EVAL, Env, a2, env, a1); + default: + var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; + if (meta && meta.exp) { + ast = meta.exp; + env = new Env(meta.env, meta.params, el.slice(1)); + } else { + return f.apply(f, el.slice(1)); + } + } + } } @@ -126,16 +131,16 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } -// Import types functions -for (var n in types.ns) { repl_env.set(n, types.ns[n]); } +// Import core functions +for (var n in core.ns) { repl_env.set(n, core.ns[n]); } _ref('read-string', reader.read_str); _ref('eval', function(ast) { return EVAL(ast, repl_env); }); diff --git a/js/step9_interop.js b/js/step9_interop.js index bfc01cb..3c83e51 100644 --- a/js/step9_interop.js +++ b/js/step9_interop.js @@ -1,5 +1,8 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; +var core = require('./core'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,24 +14,24 @@ function READ(str) { // eval function is_pair(x) { - return types.sequential_Q(x) && x.length > 0; + return types._sequential_Q(x) && x.length > 0; } function quasiquote(ast) { if (!is_pair(ast)) { - return [types.symbol("quote"), ast]; + return [types._symbol("quote"), ast]; } else if (ast[0].value === 'unquote') { return ast[1]; } else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') { - return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; + return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; } else { - return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; + return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; } } function is_macro_call(ast, env) { - return types.list_Q(ast) && - types.symbol_Q(ast[0]) && + return types._list_Q(ast) && + types._symbol_Q(ast[0]) && env.find(ast[0].value) && env.get(ast[0].value)._ismacro_; } @@ -42,15 +45,15 @@ function macroexpand(ast, env) { } function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -63,65 +66,67 @@ function eval_ast(ast, env) { function _EVAL(ast, env) { while (true) { - //console.log("EVAL:", types._pr_str(ast, true)); - if (!types.list_Q(ast)) { - return eval_ast(ast, env); + + //console.log("EVAL:", types._pr_str(ast, true)); + if (!types._list_Q(ast)) { + return eval_ast(ast, env); + } + + // apply list + ast = macroexpand(ast, env); + if (!types._list_Q(ast)) { return ast; } + + var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; + switch (a0.value) { + case "def!": + var res = EVAL(a2, env); + return env.set(a1, res); + case "let*": + var let_env = new Env(env); + for (var i=0; i < a1.length; i+=2) { + let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } - - // apply list - ast = macroexpand(ast, env); - if (!types.list_Q(ast)) { return ast; } - - var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; - switch (a0.value) { - case "def!": - var res = EVAL(a2, env); - return env.set(a1, res); - case "let*": - var let_env = new types.Env(env); - for (var i=0; i < a1.length; i+=2) { - let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); - } - return EVAL(a2, let_env); - case "quote": - return a1; - case "quasiquote": - return EVAL(quasiquote(a1), env); - case 'defmacro!': - var func = EVAL(a2, env); - func._ismacro_ = true; - return env.set(a1, func); - case 'macroexpand': - return macroexpand(a1, env); - case "js*": - return eval(a1.toString()); - case ".": - var el = eval_ast(ast.slice(2), env), - f = eval(a1.toString()); - return f.apply(f, el); - case "do": - eval_ast(ast.slice(1, -1), env); - ast = ast[ast.length-1]; - break; - case "if": - var cond = EVAL(a1, env); - if (cond === null || cond === false) { - ast = (typeof a3 !== "undefined") ? a3 : null; - } else { - ast = a2; - } - break; - case "fn*": - return types.new_function(EVAL, a2, env, a1); - default: - var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; - if (meta && meta.exp) { - ast = meta.exp; - env = new types.Env(meta.env, meta.params, el.slice(1)); - } else { - return f.apply(f, el.slice(1)); - } + return EVAL(a2, let_env); + case "quote": + return a1; + case "quasiquote": + return EVAL(quasiquote(a1), env); + case 'defmacro!': + var func = EVAL(a2, env); + func._ismacro_ = true; + return env.set(a1, func); + case 'macroexpand': + return macroexpand(a1, env); + case "js*": + return eval(a1.toString()); + case ".": + var el = eval_ast(ast.slice(2), env), + f = eval(a1.toString()); + return f.apply(f, el); + case "do": + eval_ast(ast.slice(1, -1), env); + ast = ast[ast.length-1]; + break; + case "if": + var cond = EVAL(a1, env); + if (cond === null || cond === false) { + ast = (typeof a3 !== "undefined") ? a3 : null; + } else { + ast = a2; } + break; + case "fn*": + return types._function(EVAL, Env, a2, env, a1); + default: + var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; + if (meta && meta.exp) { + ast = meta.exp; + env = new Env(meta.env, meta.params, el.slice(1)); + } else { + return f.apply(f, el.slice(1)); + } + } + } } @@ -132,16 +137,16 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } -// Import types functions -for (var n in types.ns) { repl_env.set(n, types.ns[n]); } +// Import core functions +for (var n in core.ns) { repl_env.set(n, core.ns[n]); } _ref('read-string', reader.read_str); _ref('eval', function(ast) { return EVAL(ast, repl_env); }); diff --git a/js/stepA_more.js b/js/stepA_more.js index 2778649..a4e1bda 100644 --- a/js/stepA_more.js +++ b/js/stepA_more.js @@ -1,5 +1,8 @@ var types = require('./types'); var reader = require('./reader'); +var printer = require('./printer'); +var Env = require('./env').Env; +var core = require('./core'); if (typeof module !== 'undefined') { var readline = require('./node_readline'); } @@ -11,24 +14,24 @@ function READ(str) { // eval function is_pair(x) { - return types.sequential_Q(x) && x.length > 0; + return types._sequential_Q(x) && x.length > 0; } function quasiquote(ast) { if (!is_pair(ast)) { - return [types.symbol("quote"), ast]; + return [types._symbol("quote"), ast]; } else if (ast[0].value === 'unquote') { return ast[1]; } else if (is_pair(ast[0]) && ast[0][0].value === 'splice-unquote') { - return [types.symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; + return [types._symbol("concat"), ast[0][1], quasiquote(ast.slice(1))]; } else { - return [types.symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; + return [types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))]; } } function is_macro_call(ast, env) { - return types.list_Q(ast) && - types.symbol_Q(ast[0]) && + return types._list_Q(ast) && + types._symbol_Q(ast[0]) && env.find(ast[0].value) && env.get(ast[0].value)._ismacro_; } @@ -42,15 +45,15 @@ function macroexpand(ast, env) { } function eval_ast(ast, env) { - if (types.symbol_Q(ast)) { + if (types._symbol_Q(ast)) { return env.get(ast); - } else if (types.list_Q(ast)) { + } else if (types._list_Q(ast)) { return ast.map(function(a) { return EVAL(a, env); }); - } else if (types.vector_Q(ast)) { + } else if (types._vector_Q(ast)) { var v = ast.map(function(a) { return EVAL(a, env); }); v.__isvector__ = true; return v; - } else if (types.hash_map_Q(ast)) { + } else if (types._hash_map_Q(ast)) { var new_hm = {}; for (k in ast) { new_hm[EVAL(k, env)] = EVAL(ast[k], env); @@ -63,76 +66,78 @@ function eval_ast(ast, env) { function _EVAL(ast, env) { while (true) { - //console.log("EVAL:", types._pr_str(ast, true)); - if (!types.list_Q(ast)) { - return eval_ast(ast, env); + + //console.log("EVAL:", types._pr_str(ast, true)); + if (!types._list_Q(ast)) { + return eval_ast(ast, env); + } + + // apply list + ast = macroexpand(ast, env); + if (!types._list_Q(ast)) { return ast; } + + var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; + switch (a0.value) { + case "def!": + var res = EVAL(a2, env); + return env.set(a1, res); + case "let*": + var let_env = new Env(env); + for (var i=0; i < a1.length; i+=2) { + let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); } - - // apply list - ast = macroexpand(ast, env); - if (!types.list_Q(ast)) { return ast; } - - var a0 = ast[0], a1 = ast[1], a2 = ast[2], a3 = ast[3]; - switch (a0.value) { - case "def!": - var res = EVAL(a2, env); - return env.set(a1, res); - case "let*": - var let_env = new types.Env(env); - for (var i=0; i < a1.length; i+=2) { - let_env.set(a1[i].value, EVAL(a1[i+1], let_env)); - } - return EVAL(a2, let_env); - case "quote": - return a1; - case "quasiquote": - return EVAL(quasiquote(a1), env); - case 'defmacro!': - var func = EVAL(a2, env); - func._ismacro_ = true; - return env.set(a1, func); - case 'macroexpand': - return macroexpand(a1, env); - case "js*": - return eval(a1.toString()); - case ".": - var el = eval_ast(ast.slice(2), env), - f = eval(a1.toString()); - return f.apply(f, el); - case "try*": - try { - return EVAL(a1, env); - } catch (exc) { - if (a2 && a2[0].value === "catch*") { - if (exc instanceof Error) { exc = exc.message; } - return EVAL(a2[2], new types.Env(env, [a2[1]], [exc])); - } else { - throw exc; - } - } - case "do": - eval_ast(ast.slice(1, -1), env); - ast = ast[ast.length-1]; - break; - case "if": - var cond = EVAL(a1, env); - if (cond === null || cond === false) { - ast = (typeof a3 !== "undefined") ? a3 : null; - } else { - ast = a2; - } - break; - case "fn*": - return types.new_function(EVAL, a2, env, a1); - default: - var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; - if (meta && meta.exp) { - ast = meta.exp; - env = new types.Env(meta.env, meta.params, el.slice(1)); + return EVAL(a2, let_env); + case "quote": + return a1; + case "quasiquote": + return EVAL(quasiquote(a1), env); + case 'defmacro!': + var func = EVAL(a2, env); + func._ismacro_ = true; + return env.set(a1, func); + case 'macroexpand': + return macroexpand(a1, env); + case "js*": + return eval(a1.toString()); + case ".": + var el = eval_ast(ast.slice(2), env), + f = eval(a1.toString()); + return f.apply(f, el); + case "try*": + try { + return EVAL(a1, env); + } catch (exc) { + if (a2 && a2[0].value === "catch*") { + if (exc instanceof Error) { exc = exc.message; } + return EVAL(a2[2], new Env(env, [a2[1]], [exc])); } else { - return f.apply(f, el.slice(1)); + throw exc; } } + case "do": + eval_ast(ast.slice(1, -1), env); + ast = ast[ast.length-1]; + break; + case "if": + var cond = EVAL(a1, env); + if (cond === null || cond === false) { + ast = (typeof a3 !== "undefined") ? a3 : null; + } else { + ast = a2; + } + break; + case "fn*": + return types._function(EVAL, Env, a2, env, a1); + default: + var el = eval_ast(ast, env), f = el[0], meta = f.__meta__; + if (meta && meta.exp) { + ast = meta.exp; + env = new Env(meta.env, meta.params, el.slice(1)); + } else { + return f.apply(f, el.slice(1)); + } + } + } } @@ -143,16 +148,16 @@ function EVAL(ast, env) { // print function PRINT(exp) { - return types._pr_str(exp, true); + return printer._pr_str(exp, true); } // repl -var repl_env = new types.Env(); +var repl_env = new Env(); var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); }; _ref = function (k,v) { repl_env.set(k, v); } -// Import types functions -for (var n in types.ns) { repl_env.set(n, types.ns[n]); } +// Import core functions +for (var n in core.ns) { repl_env.set(n, core.ns[n]); } _ref('readline', readline.readline) _ref('read-string', reader.read_str); diff --git a/js/types.js b/js/types.js index 18fad0a..7fd2962 100644 --- a/js/types.js +++ b/js/types.js @@ -7,59 +7,17 @@ if (typeof module === 'undefined') { var print = exports.print = function () { console.log.apply(console, arguments); }; } -// General utility functions - -// Clone a function -Function.prototype.clone = function() { - var that = this; - var temp = function () { return that.apply(this, arguments); }; - for( key in this ) { - temp[key] = this[key]; - } - return temp; -}; - -function _clone (obj) { - var new_obj; - switch (obj_type(obj)) { - case 'list': - new_obj = obj.slice(0); - break; - case 'vector': - new_obj = obj.slice(0); - new_obj.__isvector__ = true; - break; - case 'hash-map': - new_obj = {}; - for (var k in obj) { - if (obj.hasOwnProperty(k)) { new_obj[k] = obj[k]; } - } - break; - case 'function': - new_obj = obj.clone(); - break; - default: - throw new Error("clone of non-collection: " + obj_type(obj)); - } - return new_obj; -} - - - - -function nil_Q(a) { return a === null ? true : false; } -function true_Q(a) { return a === true ? true : false; } -function false_Q(a) { return a === false ? true : false; } - -function obj_type(obj) { - if (symbol_Q(obj)) { return 'symbol'; } - else if (list_Q(obj)) { return 'list'; } - else if (vector_Q(obj)) { return 'vector'; } - else if (hash_map_Q(obj)) { return 'hash-map'; } - else if (nil_Q(obj)) { return 'nil'; } - else if (true_Q(obj)) { return 'true'; } - else if (false_Q(obj)) { return 'false'; } - else if (atom_Q(obj)) { return 'atom'; } +// General fucnctions + +function _obj_type(obj) { + if (_symbol_Q(obj)) { return 'symbol'; } + else if (_list_Q(obj)) { return 'list'; } + else if (_vector_Q(obj)) { return 'vector'; } + else if (_hash_map_Q(obj)) { return 'hash-map'; } + else if (_nil_Q(obj)) { return 'nil'; } + else if (_true_Q(obj)) { return 'true'; } + else if (_false_Q(obj)) { return 'false'; } + else if (_atom_Q(obj)) { return 'atom'; } else { switch (typeof(obj)) { case 'number': return 'number'; @@ -70,82 +28,12 @@ function obj_type(obj) { } } -function _pr_str(obj, print_readably) { - if (typeof print_readably === 'undefined') { print_readably = true; } - var _r = print_readably; - var ot = obj_type(obj); - switch (ot) { - case 'list': - var ret = obj.map(function(e) { return _pr_str(e,_r); }); - return "(" + ret.join(' ') + ")"; - case 'vector': - var ret = obj.map(function(e) { return _pr_str(e,_r); }); - return "[" + ret.join(' ') + "]"; - case 'hash-map': - var ret = []; - for (var k in obj) { - ret.push(_pr_str(k,_r), _pr_str(obj[k],_r)); - } - return "{" + ret.join(' ') + "}"; - case 'string': - if (print_readably) { - return '"' + obj.replace(/\\/, "\\\\").replace(/"/g, '\\"') + '"'; - } else { - return obj; - } - case 'nil': - return "nil"; - case 'atom': - return "(atom " + _pr_str(obj.val,_r) + ")"; - default: - return obj.toString(); - } -} - -function pr_str() { - return Array.prototype.map.call(arguments,function(exp) { - return _pr_str(exp, true); - }).join(" "); -} - -function str() { - return Array.prototype.map.call(arguments,function(exp) { - return _pr_str(exp, false); - }).join(""); -} - -function prn() { - print.apply({}, Array.prototype.map.call(arguments,function(exp) { - return _pr_str(exp, true); - })); -} - -function println() { - print.apply({}, Array.prototype.map.call(arguments,function(exp) { - return _pr_str(exp, false); - })); -} - -function with_meta(obj, m) { - var new_obj = _clone(obj); - new_obj.__meta__ = m; - return new_obj; -} - -function meta(obj) { - // TODO: support symbols and atoms - if ((!sequential_Q(obj)) && - (!(hash_map_Q(obj))) && - (!(function_Q(obj)))) { - throw new Error("attempt to get metadata from: " + obj_type(obj)); - } - return obj.__meta__; -} +function _sequential_Q(lst) { return _list_Q(lst) || _vector_Q(lst); } -function equal_Q (a, b) { - var ota = obj_type(a), otb = obj_type(b); - if (!(ota === otb || (sequential_Q(a) && sequential_Q(b)))) { +function _equal_Q (a, b) { + var ota = _obj_type(a), otb = _obj_type(b); + if (!(ota === otb || (_sequential_Q(a) && _sequential_Q(b)))) { return false; } switch (ota) { @@ -154,7 +42,7 @@ function equal_Q (a, b) { case 'vector': if (a.length !== b.length) { return false; } for (var i=0; i<a.length; i++) { - if (! equal_Q(a[i], b[i])) { return false; } + if (! _equal_Q(a[i], b[i])) { return false; } } return true; case 'hash-map': @@ -172,6 +60,37 @@ function equal_Q (a, b) { } +function _clone (obj) { + var new_obj; + switch (_obj_type(obj)) { + case 'list': + new_obj = obj.slice(0); + break; + case 'vector': + new_obj = obj.slice(0); + new_obj.__isvector__ = true; + break; + case 'hash-map': + new_obj = {}; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { new_obj[k] = obj[k]; } + } + break; + case 'function': + new_obj = obj.clone(); + break; + default: + throw new Error("clone of non-collection: " + _obj_type(obj)); + } + return new_obj; +} + + +// Scalars +function _nil_Q(a) { return a === null ? true : false; } +function _true_Q(a) { return a === true ? true : false; } +function _false_Q(a) { return a === false ? true : false; } + // Symbols function Symbol(name) { @@ -179,68 +98,61 @@ function Symbol(name) { return this; } Symbol.prototype.toString = function() { return this.value; } - -function symbol(name) { return new Symbol(name); } - -function symbol_Q(obj) { return obj instanceof Symbol; } +function _symbol(name) { return new Symbol(name); } +function _symbol_Q(obj) { return obj instanceof Symbol; } // Functions -function new_function(func, exp, env, params) { +function _function(Eval, Env, exp, env, params) { var f = function() { // TODO: figure out why this throws with 'and' macro //throw new Error("Attempt to invoke mal function directly"); - return func(exp, new Env(env, params, arguments)); + return Eval(exp, new Env(env, params, arguments)); }; f.__meta__ = {exp: exp, env: env, params: params}; return f; - } -function function_Q(f) { return typeof f == "function"; } - +function _function_Q(obj) { return typeof obj == "function"; } +Function.prototype.clone = function() { + var that = this; + var temp = function () { return that.apply(this, arguments); }; + for( key in this ) { + temp[key] = this[key]; + } + return temp; +}; -// Errors/Exceptions -function mal_throw(exc) { throw exc; } +// Lists +function _list() { return Array.prototype.slice.call(arguments, 0); } +function _list_Q(obj) { return Array.isArray(obj) && !obj.__isvector__; } // Vectors -function vector() { +function _vector() { var v = Array.prototype.slice.call(arguments, 0); v.__isvector__ = true; return v; } +function _vector_Q(obj) { return Array.isArray(obj) && obj.__isvector__; } -function vector_Q(v) { return Array.isArray(v) && v.__isvector__; } - - -// Lists - -function list() { - return Array.prototype.slice.call(arguments, 0); -} - -function list_Q(lst) { return Array.isArray(lst) && !lst.__isvector__; } // Hash Maps - -function hash_map() { +function _hash_map() { if (arguments.length % 2 === 1) { throw new Error("Odd number of hash map arguments"); } var args = [{}].concat(Array.prototype.slice.call(arguments, 0)); - return assoc_BANG.apply(null, args); + return _assoc_BANG.apply(null, args); } - -function hash_map_Q(hm) { +function _hash_map_Q(hm) { return typeof hm === "object" && !Array.isArray(hm) && - !(hm === null) && + !(hm === null) && !(hm instanceof Atom); } - -function assoc_BANG(hm) { +function _assoc_BANG(hm) { if (arguments.length % 2 !== 1) { throw new Error("Odd number of assoc arguments"); } @@ -258,14 +170,7 @@ function assoc_BANG(hm) { } return hm; } - -function assoc(src_hm) { - var hm = _clone(src_hm); - var args = [hm].concat(Array.prototype.slice.call(arguments, 1)); - return assoc_BANG.apply(null, args); -} - -function dissoc_BANG(hm) { +function _dissoc_BANG(hm) { for (var i=1; i<arguments.length; i++) { var ktoken = arguments[i]; delete hm[ktoken]; @@ -273,166 +178,32 @@ function dissoc_BANG(hm) { return hm; } -function dissoc(src_hm) { - var hm = _clone(src_hm); - var args = [hm].concat(Array.prototype.slice.call(arguments, 1)); - return dissoc_BANG.apply(null, args); -} - -function get(hm, key) { - if (key in hm) { - return hm[key]; - } else { - return null; - } -} - -function contains_Q(hm, key) { - if (key in hm) { return true; } else { return false; } -} - -function keys(hm) { return Object.keys(hm); } -function vals(hm) { return Object.keys(hm).map(function(k) { return hm[k]; }); } - // Atoms function Atom(val) { this.val = val; } -function atom(val) { return new Atom(val); } -function atom_Q(atm) { return atm instanceof Atom; } -function deref(atm) { return atm.val; } -function reset_BANG(atm, val) { return atm.val = val; } -function swap_BANG(atm, f) { - var args = [atm.val].concat(Array.prototype.slice.call(arguments, 2)); - atm.val = f.apply(f, args); - return atm.val; -} - - -// Sequence operations -function sequential_Q(lst) { return list_Q(lst) || vector_Q(lst); } - -function nth(lst, idx) { return lst[idx]; } - -function count(s) { - if (Array.isArray(s)) { return s.length; } - else { return Object.keys(s).length; } -} - -function empty_Q(lst) { return lst.length === 0; } - -function cons(a, b) { return [a].concat(b); } - -function concat(lst) { - lst = lst || []; - return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1)); -} - -function conj(lst) { - if (list_Q(lst)) { - return Array.prototype.slice.call(arguments, 1).reverse().concat(lst); - } else { - var v = lst.concat(Array.prototype.slice.call(arguments, 1)); - v.__isvector__ = true; - return v; - } -} - -function first(lst) { return lst[0]; } - -function rest(lst) { return lst.slice(1); } - - - -// General list related functions -function apply(f) { - var args = Array.prototype.slice.call(arguments, 1); - return f.apply(f, args.slice(0, args.length-1).concat(args[args.length-1])); -} - -function map(f, lst) { - return lst.map(function(el){ return f(el); }); -} - - -// Env implementation -function Env(outer, binds, exprs) { - this.data = {}; - this.outer = outer || null; - - if (binds && exprs) { - // Returns a new Env with symbols in binds bound to - // corresponding values in exprs - // TODO: check types of binds and exprs and compare lengths - for (var i=0; i<binds.length;i++) { - if (binds[i].value === "&") { - // variable length arguments - this.data[binds[i+1].value] = Array.prototype.slice.call(exprs, i); - break; - } else { - this.data[binds[i].value] = exprs[i]; - } - } - } - return this; -} -Env.prototype.find = function (key) { - if (key in this.data) { return this; } - else if (this.outer) { return this.outer.find(key); } - else { return null; } -}; -Env.prototype.set = function(key, value) { this.data[key] = value; return value; }, -Env.prototype.get = function(key) { - var env = this.find(key); - if (!env) { throw new Error("'" + key + "' not found"); } - return env.data[key]; -}; - -// types.ns is namespace of type functions -var ns = {'pr-str': pr_str, 'str': str, 'prn': prn, 'println': println, - 'with-meta': with_meta, 'meta': meta, - type: obj_type, '=': equal_Q, - symbol: symbol, 'symbol?': symbol_Q, - 'nil?': nil_Q, 'true?': true_Q, 'false?': false_Q, - '<' : function(a,b){return a<b;}, - '<=' : function(a,b){return a<=b;}, - '>' : function(a,b){return a>b;}, - '>=' : function(a,b){return a>=b;}, - '+' : function(a,b){return a+b;}, - '-' : function(a,b){return a-b;}, - '*' : function(a,b){return a*b;}, - '/' : function(a,b){return a/b;}, - 'throw': mal_throw, - 'list': list, 'list?': list_Q, - 'vector': vector, 'vector?': vector_Q, - 'hash-map': hash_map, 'map?': hash_map_Q, - 'assoc': assoc, 'dissoc': dissoc, 'get': get, - 'contains?': contains_Q, 'keys': keys, 'vals': vals, - 'atom': atom, 'atom?': atom_Q, - "deref": deref, "reset!": reset_BANG, "swap!": swap_BANG, - 'sequential?': sequential_Q, 'cons': cons, 'nth': nth, - 'empty?': empty_Q, 'count': count, 'concat': concat, - 'conj': conj, 'first': first, 'rest': rest, - 'apply': apply, 'map': map}; - -exports.ns = types.ns = ns; -exports._pr_str = types._pr_str = _pr_str; -exports.prn = types.prn = prn; -exports.Env = types.Env = Env; - -exports.symbol = types.symbol = symbol; -exports.symbol_Q = types.symbol_Q = symbol_Q; -exports.hash_map = types.hash_map = hash_map; -exports.hash_map_Q = types.hash_map_Q = hash_map_Q; -exports.new_function = types.new_function = new_function; -exports.list = types.list = list; -exports.list_Q = types.list_Q = list_Q; -exports.vector = types.vector = vector; -exports.vector_Q = types.vector_Q = vector_Q; - -exports.sequential_Q = types.sequential_Q = sequential_Q; -exports.cons = types.cons = cons; -exports.concat = types.concat = concat; -exports.first = types.first = first; -exports.rest = types.rest = rest; -exports.apply = types.apply = apply; -exports.map = types.map = map; +function _atom(val) { return new Atom(val); } +function _atom_Q(atm) { return atm instanceof Atom; } + + +// Exports +exports._obj_type = types._obj_type = _obj_type; +exports._sequential_Q = types._sequential_Q = _sequential_Q; +exports._equal_Q = types._equal_Q = _equal_Q; +exports._clone = types._clone = _clone; +exports._nil_Q = types._nil_Q = _nil_Q; +exports._true_Q = types._true_Q = _true_Q; +exports._false_Q = types._false_Q = _false_Q; +exports._symbol = types._symbol = _symbol; +exports._symbol_Q = types._symbol_Q = _symbol_Q; +exports._function = types._function = _function; +exports._function_Q = types._function_Q = _function_Q; +exports._list = types._list = _list; +exports._list_Q = types._list_Q = _list_Q; +exports._vector = types._vector = _vector; +exports._vector_Q = types._vector_Q = _vector_Q; +exports._hash_map = types._hash_map = _hash_map; +exports._hash_map_Q = types._hash_map_Q = _hash_map_Q; +exports._assoc_BANG = types._assoc_BANG = _assoc_BANG; +exports._dissoc_BANG = types._dissoc_BANG = _dissoc_BANG; +exports._atom = types._atom = _atom; +exports._atom_Q = types._atom_Q = _atom_Q; |
