// Node vs browser behavior var types = {}; if (typeof module === 'undefined') { var exports = types; } else { // map output/print to console.log 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'; } else { switch (typeof(obj)) { case 'number': return 'number'; case 'function': return 'function'; case 'string': return 'string'; default: throw new Error("Unknown type '" + typeof(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 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' : 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;