mal/docs/web/mal.js
Nicolas Boulenguez 14ab099cea gensym: hide the counter in an environment, define inc in stepA.
tests: check that `inc` is present in stepA.
nasm: split lines in mal_startup_string for readability.
objpascal: remove obsolete .orig file
swift: remove an unneeded line in template
swift4: remove duplicate definition of `or` macro
2019-05-11 16:37:26 +02:00

976 lines
27 KiB
JavaScript

var max_history_length = 1000;
function jq_load_history(jq) {
if (localStorage['mal_history']) {
var lines = JSON.parse(localStorage['mal_history']);
if (lines.length > max_history_length) {
lines = lines.slice(lines.length-max_history_length);
}
jq.SetHistory(lines);
}
}
function jq_save_history(jq) {
var lines = jq.GetHistory();
localStorage['mal_history'] = JSON.stringify(lines);
}
var readline = {
'readline': function(prompt_str) {
return prompt(prompt_str);
}};
// Node vs browser behavior
var types = {};
if (typeof module === 'undefined') {
var exports = types;
}
// General functions
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';
case 'function': return 'function';
case 'string': return obj[0] == '\u029e' ? 'keyword' : 'string';
default: throw new Error("Unknown type '" + typeof(obj) + "'");
}
}
}
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)))) {
return false;
}
switch (ota) {
case 'symbol': return a.value === b.value;
case 'list':
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; }
}
return true;
case 'hash-map':
if (Object.keys(a).length !== Object.keys(b).length) { return false; }
for (var k in a) {
if (! _equal_Q(a[k], b[k])) { return false; }
}
return true;
default:
return 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));
}
Object.defineProperty(new_obj, "__meta__", {
enumerable: false,
writable: true
});
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; }
function _number_Q(obj) { return typeof obj === 'number'; }
function _string_Q(obj) {
return typeof obj === 'string' && obj[0] !== '\u029e';
}
// Symbols
function Symbol(name) {
this.value = 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; }
// Keywords
function _keyword(obj) {
if (typeof obj === 'string' && obj[0] === '\u029e') {
return obj;
} else {
return "\u029e" + obj;
}
}
function _keyword_Q(obj) {
return typeof obj === 'string' && obj[0] === '\u029e';
}
// Functions
function _function(Eval, Env, ast, env, params) {
var fn = function() {
return Eval(ast, new Env(env, params, arguments));
};
fn.__meta__ = null;
fn.__ast__ = ast;
fn.__gen_env__ = function(args) { return new Env(env, params, args); };
fn._ismacro_ = false;
return fn;
}
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;
};
function _fn_Q(obj) { return _function_Q(obj) && !obj._ismacro_; }
function _macro_Q(obj) { return _function_Q(obj) && !!obj._ismacro_; }
// Lists
function _list() { return Array.prototype.slice.call(arguments, 0); }
function _list_Q(obj) { return Array.isArray(obj) && !obj.__isvector__; }
// Vectors
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__; }
// Hash Maps
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);
}
function _hash_map_Q(hm) {
return typeof hm === "object" &&
!Array.isArray(hm) &&
!(hm === null) &&
!(hm instanceof Symbol) &&
!(hm instanceof Atom);
}
function _assoc_BANG(hm) {
if (arguments.length % 2 !== 1) {
throw new Error("Odd number of assoc arguments");
}
for (var i=1; i<arguments.length; i+=2) {
var ktoken = arguments[i],
vtoken = arguments[i+1];
if (typeof ktoken !== "string") {
throw new Error("expected hash-map key string, got: " + (typeof ktoken));
}
hm[ktoken] = vtoken;
}
return hm;
}
function _dissoc_BANG(hm) {
for (var i=1; i<arguments.length; i++) {
var ktoken = arguments[i];
delete hm[ktoken];
}
return hm;
}
// Atoms
function Atom(val) { this.val = val; }
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._number_Q = types._number_Q = _number_Q;
exports._string_Q = types._string_Q = _string_Q;
exports._symbol = types._symbol = _symbol;
exports._symbol_Q = types._symbol_Q = _symbol_Q;
exports._keyword = types._keyword = _keyword;
exports._keyword_Q = types._keyword_Q = _keyword_Q;
exports._function = types._function = _function;
exports._function_Q = types._function_Q = _function_Q;
exports._fn_Q = types._fn_Q = _fn_Q;
exports._macro_Q = types._macro_Q = _macro_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;
// Node vs browser behavior
var reader = {};
if (typeof module !== 'undefined') {
} else {
var exports = reader;
}
function Reader(tokens) {
// copy
this.tokens = tokens.map(function (a) { return a; });
this.position = 0;
}
Reader.prototype.next = function() { return this.tokens[this.position++]; }
Reader.prototype.peek = function() { return this.tokens[this.position]; }
function tokenize(str) {
var re = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)/g;
var results = [];
while ((match = re.exec(str)[1]) != '') {
if (match[0] === ';') { continue; }
results.push(match);
}
return results;
}
function read_atom (reader) {
var token = reader.next();
//console.log("read_atom:", token);
if (token.match(/^-?[0-9]+$/)) {
return parseInt(token,10) // integer
} else if (token.match(/^-?[0-9][0-9.]*$/)) {
return parseFloat(token,10); // float
} else if (token[0] === "\"") {
if (token.slice(-1) !== "\"") {
throw new Error("expected '\"', got EOF");
}
return token.slice(1,token.length-1)
.replace(/\\(.)/g, function (_, c) { return c === "n" ? "\n" : c})
} else if (token[0] === ":") {
return types._keyword(token.slice(1));
} else if (token === "nil") {
return null;
} else if (token === "true") {
return true;
} else if (token === "false") {
return false;
} else {
return types._symbol(token); // symbol
}
}
// read list of tokens
function read_list(reader, start, end) {
start = start || '(';
end = end || ')';
var ast = [];
var token = reader.next();
if (token !== start) {
throw new Error("expected '" + start + "'");
}
while ((token = reader.peek()) !== end) {
if (!token) {
throw new Error("expected '" + end + "', got EOF");
}
ast.push(read_form(reader));
}
reader.next();
return ast;
}
// read vector of tokens
function read_vector(reader) {
var lst = read_list(reader, '[', ']');
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(null, lst);
}
function read_form(reader) {
var token = reader.peek();
switch (token) {
// reader macros/transforms
case ';': return null; // Ignore comments
case '\'': reader.next();
return [types._symbol('quote'), read_form(reader)];
case '`': reader.next();
return [types._symbol('quasiquote'), read_form(reader)];
case '~': reader.next();
return [types._symbol('unquote'), read_form(reader)];
case '~@': reader.next();
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];
case '@': reader.next();
return [types._symbol('deref'), read_form(reader)];
// list
case ')': throw new Error("unexpected ')'");
case '(': return read_list(reader);
// vector
case ']': throw new Error("unexpected ']'");
case '[': return read_vector(reader);
// hash-map
case '}': throw new Error("unexpected '}'");
case '{': return read_hash_map(reader);
// atom
default: return read_atom(reader);
}
}
function BlankException(msg) {
}
function read_str(str) {
var tokens = tokenize(str);
if (tokens.length === 0) { throw new BlankException(); }
return read_form(new Reader(tokens))
}
exports.Reader = reader.Reader = Reader;
exports.BlankException = reader.BlankException = BlankException;
exports.tokenize = reader.tokenize = tokenize;
exports.read_form = reader.read_form = read_form;
exports.read_str = reader.read_str = read_str;
// Node vs browser behavior
var printer = {};
if (typeof module !== 'undefined') {
// map output/print to console.log
printer.println = exports.println = function () {
console.log.apply(console, arguments);
};
}
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 (obj[0] === '\u029e') {
return ':' + obj.slice(1);
} else if (_r) {
return '"' + obj.replace(/\\/g, "\\\\")
.replace(/"/g, '\\"')
.replace(/\n/g, "\\n") + '"'; // string
} else {
return obj;
}
case 'keyword':
return ':' + obj.slice(1);
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;
// Node vs browser behavior
var interop = {};
if (typeof module === 'undefined') {
var exports = interop,
GLOBAL = window;
}
function resolve_js(str) {
if (str.match(/\./)) {
var re = /^(.*)\.[^\.]*$/,
match = re.exec(str);
return [eval(match[1]), eval(str)];
} else {
return [GLOBAL, eval(str)];
}
}
function js_to_mal(obj) {
if (obj === null || obj === undefined) {
return null;
}
var cache = [];
var str = JSON.stringify(obj, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
cache = null; // Enable garbage collection
return JSON.parse(str);
}
exports.resolve_js = interop.resolve_js = resolve_js;
exports.js_to_mal = interop.js_to_mal = js_to_mal;
// 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.constructor || key.constructor.name !== 'Symbol') {
throw new Error("env.find key must be a symbol")
}
if (key.value in this.data) { return this; }
else if (this.outer) { return this.outer.find(key); }
else { return null; }
};
Env.prototype.set = function(key, value) {
if (!key.constructor || key.constructor.name !== 'Symbol') {
throw new Error("env.set key must be a symbol")
}
this.data[key.value] = value;
return value;
};
Env.prototype.get = function(key) {
if (!key.constructor || key.constructor.name !== 'Symbol') {
throw new Error("env.get key must be a symbol")
}
var env = this.find(key);
if (!env) { throw new Error("'" + key.value + "' not found"); }
return env.data[key.value];
};
exports.Env = env.Env = Env;
// Node vs browser behavior
var core = {};
if (typeof module === 'undefined') {
var exports = core;
} else {
}
// 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.println.apply({}, Array.prototype.map.call(arguments,function(exp) {
return printer._pr_str(exp, true);
}));
}
function println() {
printer.println.apply({}, Array.prototype.map.call(arguments,function(exp) {
return printer._pr_str(exp, false);
}));
}
function slurp(f) {
if (typeof require !== 'undefined') {
return require('fs').readFileSync(f, 'utf-8');
} else {
var req = new XMLHttpRequest();
req.open("GET", f, false);
req.send();
if (req.status == 200) {
return req.responseText;
} else {
throw new Error("Failed to slurp file: " + f);
}
}
}
// Number functions
function time_ms() { return new Date().getTime(); }
// 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 (hm != null && 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) {
if (idx < lst.length) { return lst[idx]; }
else { throw new Error("nth: index out of range"); }
}
function first(lst) { return (lst === null) ? null : lst[0]; }
function rest(lst) { return (lst == null) ? [] : lst.slice(1); }
function empty_Q(lst) { return lst.length === 0; }
function count(s) {
if (Array.isArray(s)) { return s.length; }
else if (s === null) { return 0; }
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 seq(obj) {
if (types._list_Q(obj)) {
return obj.length > 0 ? obj : null;
} else if (types._vector_Q(obj)) {
return obj.length > 0 ? Array.prototype.slice.call(obj, 0): null;
} else if (types._string_Q(obj)) {
return obj.length > 0 ? obj.split('') : null;
} else if (obj === null) {
return null;
} else {
throw new Error("seq: called on non-sequence");
}
}
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;
}
function js_eval(str) {
return interop.js_to_mal(eval(str.toString()));
}
function js_method_call(object_method_str) {
var args = Array.prototype.slice.call(arguments, 1),
r = interop.resolve_js(object_method_str),
obj = r[0], f = r[1];
var res = f.apply(obj, args);
return interop.js_to_mal(res);
}
// 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,
'number?': types._number_Q,
'string?': types._string_Q,
'symbol': types._symbol,
'symbol?': types._symbol_Q,
'keyword': types._keyword,
'keyword?': types._keyword_Q,
'fn?': types._fn_Q,
'macro?': types._macro_Q,
'pr-str': pr_str,
'str': str,
'prn': prn,
'println': println,
'readline': readline.readline,
'read-string': reader.read_str,
'slurp': slurp,
'<' : 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;},
"time-ms": time_ms,
'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,
'apply': apply,
'map': map,
'conj': conj,
'seq': seq,
'with-meta': with_meta,
'meta': meta,
'atom': types._atom,
'atom?': types._atom_Q,
"deref": deref,
"reset!": reset_BANG,
"swap!": swap_BANG,
'js-eval': js_eval,
'.': js_method_call
};
exports.ns = core.ns = ns;
if (typeof module !== 'undefined') {
}
// read
function READ(str) {
return reader.read_str(str);
}
// eval
function is_pair(x) {
return types._sequential_Q(x) && x.length > 0;
}
function quasiquote(ast) {
if (!is_pair(ast)) {
return [types._symbol("quote"), ast];
} else if (types._symbol_Q(ast[0]) && 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))];
} else {
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]) &&
env.find(ast[0]) &&
env.get(ast[0])._ismacro_;
}
function macroexpand(ast, env) {
while (is_macro_call(ast, env)) {
var mac = env.get(ast[0]);
ast = mac.apply(mac, ast.slice(1));
}
return ast;
}
function eval_ast(ast, env) {
if (types._symbol_Q(ast)) {
return env.get(ast);
} else if (types._list_Q(ast)) {
return ast.map(function(a) { return EVAL(a, env); });
} 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)) {
var new_hm = {};
for (k in ast) {
new_hm[EVAL(k, env)] = EVAL(ast[k], env);
}
return new_hm;
} else {
return ast;
}
}
function _EVAL(ast, env) {
while (true) {
//printer.println("EVAL:", printer._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 eval_ast(ast, env);
}
if (ast.length === 0) {
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], EVAL(a1[i+1], let_env));
}
ast = a2;
env = let_env;
break;
case "quote":
return a1;
case "quasiquote":
ast = quasiquote(a1);
break;
case 'defmacro!':
var func = EVAL(a2, env);
func._ismacro_ = true;
return env.set(a1, func);
case 'macroexpand':
return macroexpand(a1, env);
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 {
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];
if (f.__ast__) {
ast = f.__ast__;
env = f.__gen_env__(el.slice(1));
} else {
return f.apply(f, el.slice(1));
}
}
}
}
function EVAL(ast, env) {
var result = _EVAL(ast, env);
return (typeof result !== "undefined") ? result : null;
}
// print
function PRINT(exp) {
return printer._pr_str(exp, true);
}
// repl
var repl_env = new Env();
var rep = function(str) { return PRINT(EVAL(READ(str), repl_env)); };
// core.js: defined using javascript
for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns[n]); }
repl_env.set(types._symbol('eval'), function(ast) {
return EVAL(ast, repl_env); });
repl_env.set(types._symbol('*ARGV*'), []);
// core.mal: defined using the language itself
rep("(def! *host-language* \"javascript\")")
rep("(def! not (fn* (a) (if a false true)))");
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))");
rep("(def! inc (fn* [x] (+ x 1)))");
rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))");
rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))");
if (typeof process !== 'undefined' && process.argv.length > 2) {
repl_env.set(types._symbol('*ARGV*'), process.argv.slice(3));
rep('(load-file "' + process.argv[2] + '")');
process.exit(0);
}
// repl loop
if (typeof require !== 'undefined' && require.main === module) {
// Synchronous node.js commandline mode
rep("(println (str \"Mal [\" *host-language* \"]\"))");
while (true) {
var line = readline.readline("user> ");
if (line === null) { break; }
try {
if (line) { printer.println(rep(line)); }
} catch (exc) {
if (exc instanceof reader.BlankException) { continue }
if (exc instanceof Error) { console.warn(exc.stack) }
else { console.warn("Error: " + printer._pr_str(exc, true)) }
}
}
}