Lispy.js with ZEN style.
//Remove all white spaces at the beginning and end of the string //From Douglas Crockford's 'Javascript: The Good Parts' String.prototype.trim = function () { return this.replace(/^s+|s+$/g, ''); }; //This is the equivalent of Python's str.split(None) method //which has a different splitting algorithm when None is passed as a parameter. //http://docs.python.org/library/stdtypes.html#str.split String.prototype.pysplit = function () { return this.replace(/s+/g, ' ').trim().split(' '); }; Math.add = function (a, b) { return a + b; }; Math.sub = function (a, b) { return a - b; }; Math.mul = function (a, b) { return a * b; }; Math.div = function (a, b) { return a / b; }; Math.gt = function (a, b) { return a > b; }; Math.lt = function (a, b) { return a < b; }; Math.ge = function (a, b) { return a >= b; }; Math.le = function (a, b) { return a <= b; }; Math.eq = function (a, b) { return a === b; }; Math.mod = function (a, b) { return a % b; }; //################ Symbol, Procedure, Env classes var Symbol = String; var environment = function (spec) { var i, env = {}, outer = spec.outer || {}; var get_outer = function () { return outer; }; var find = function (variable) { if (env.hasOwnProperty(variable)) { return env; } else { return outer.find(variable); } }; if (0 !== spec.params.length) { for (i = 0; i < spec.params.length; i += 1) { env[spec.params[i]] = spec.args[i]; } } env.get_outer = get_outer; env.find = find; return env; }; var add_globals = function (env) { //Cannot use for..in on built-in objects like Math in JS. //So need to include all methods manually var mathMethods = ['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'random', 'round', 'sin', 'sqrt', 'tan'], i; for (i = 0; i < mathMethods.length; i += 1) { env[mathMethods[i]] = Math[mathMethods[i]]; } env['+'] = Math.add; env['-'] = Math.sub; env['*'] = Math.mul; env['/'] = Math.div; env['>'] = Math.gt; env['<'] = Math.lt; env['>='] = Math.ge; env['<='] = Math.le; env['='] = Math.eq; env['remainder'] = Math.mod; env['equal?'] = Math.eq; env['eq?'] = Math.eq; //'eq?':op.is_ ;Need to find Object Equality operator in JS env['length'] = function (x) { return x.length; }; env['cons'] = function (x, y) { var arr = [x]; return arr.concat(y); }; env['car'] = function (x) { return (x.length !== 0) ? x[0] : null; }; env['cdr'] = function (x) { return (x.length > 1) ? x.slice(1) : null; }; env['append'] = function (x, y) { return x.concat(y); }; env['list'] = function () { return Array.prototype.slice.call(arguments); }; //'list':lambda *x:list(x) env['list?'] = function (x) { return x && typeof x === 'object' && x.constructor === Array ; }; //'list?': lambda x:isa(x,list) env['null?'] = function (x) { return (!x || x.length === 0); }; env['symbol?'] = function (x) { return typeof x === 'string'; }; return env; }; var global_env = add_globals(environment({params: [], args: [], outer: undefined})); //################ eval var eval = function (x, env) { env = env || global_env; return ((analyze(x)) (env)); }; //################ analyze var analyze = function (x) { if (typeof x === 'string') { //variable reference return function (env) { return env.find(x.valueOf())[x.valueOf()];}; } else if (typeof x === 'number') { //constant literal return function (env) { return x; }; } else if (x[0] === 'quote') { //(quote exp) var qval = x[1]; return function (env) { return qval; }; } else if (x[0] === 'if') { //(if test conseq alt) return function (pproc, cproc, aproc) { return function (env) { if (pproc(env)) { return cproc(env); } else { return aproc(env); } }; }(analyze(x[1]), analyze(x[2]), analyze(x[3])); } else if (x[0] === 'set!') { //(set! var exp) return function (vvar, vproc) { return function (env) { env.find(vvar)[vvar] = vproc(env); }; }(x[1], analyze(x[2])); } else if (x[0] === 'define') { //(define var exp) return function (vvar, vproc) { return function (env) { env[vvar] = vproc(env); }; }(x[1], analyze(x[2])); } else if (x[0] === 'lambda') { //(lambda (var*) exp) return analyze_lambda(x); } else if (x[0] === 'begin') { //(begin exp*) x.shift(); return analyze_sequence(x); } else { //(proc exp*) var aprocs = x.map(analyze); var fproc = aprocs.shift(); return function (env) { var opprocs = aprocs.map(function (aproc) {return aproc(env);}); return fproc(env).apply(env, opprocs); }; } }; var analyze_lambda = function (x) { var vars = x[1]; var bproc = analyze_sequence([x[2]]); return function (env) { return function () { return bproc(environment({params: vars, args: arguments, outer: env })); }; }; }; var analyze_sequence = function (x) { var procs = x.map(analyze); return function (env) { var result; var i; for (i = 0; i < procs.length; i += 1) { result = procs[i](env); } return result; }; }; //################ parse, read, and user interaction var atom = function (token) { if (isNaN(token)) { return token; } else { return +token; //Cast to number. Nice trick from Douglas Crockford's Javascript: The Good Parts } }; var tokenize = function (s) { return s.replace(/(/g, ' ( ').replace(/)/g, ' ) ').pysplit(); }; var read_from = function (tokens) { if (0 === tokens.length) { throw { name: 'SyntaxError', message: 'unexpected EOF while reading' }; } var token = tokens.shift(); if ('(' === token) { var L = []; while (')' !== tokens[0]) { L.push(read_from(tokens)); } tokens.shift(); // pop off ')' return L; } else { if (')' === token) { throw { name: 'SyntaxError', message: 'unexpected )' }; } else { return atom(token); } } }; var read = function (s) { return read_from(tokenize(s)); }; var parse = read; var to_string = function (exp) { }; var debug = function (s) { try { document.getElementById('debugdiv').innerHTML = eval(parse(s)); } catch (e) { document.getElementById('debugdiv').innerHTML = e.name + ': ' + e.message; } };
Posted on 2011-12-27 13:53 with js in 1.345 sec.