aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-04-02 22:23:37 -0500
committerJoel Martin <github@martintribe.org>2014-04-02 22:23:37 -0500
commitea81a8087bcd7953b083a2be9db447f75e7ebf56 (patch)
tree6cf47a2dbd55d42efc4a901eaabdec952f40ce89 /js
parent1617910ad342a55762f3ddabb975849d843cff85 (diff)
downloadmal-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/Makefile3
-rw-r--r--js/core.js193
-rw-r--r--js/env.js40
-rw-r--r--js/printer.js44
-rw-r--r--js/reader.js18
-rw-r--r--js/step1_read_print.js3
-rw-r--r--js/step2_eval.js13
-rw-r--r--js/step3_env.js18
-rw-r--r--js/step4_if_fn_do.js25
-rw-r--r--js/step5_tco.js96
-rw-r--r--js/step6_file.js96
-rw-r--r--js/step7_quote.js113
-rw-r--r--js/step8_macros.js135
-rw-r--r--js/step9_interop.js147
-rw-r--r--js/stepA_more.js165
-rw-r--r--js/types.js423
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;