Created
August 1, 2011 18:41
-
-
Save StanAngeloff/1118731 to your computer and use it in GitHub Desktop.
A very tiny Lisp-like implementation in JavaScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on http://www.flownet.com/ron/lisp/l.py | |
// | |
this.TinyLisp = (function(code, env) { | |
function reduce(array, block) { | |
var previous = array[0]; | |
for (var i = 1, length = array.length - 1; i < length; i ++) { | |
previous = block(array[i], previous); | |
} | |
return previous; | |
}; | |
var globalenv = { | |
'+': function() { return reduce(arguments, function(value, previous) { return previous + value; }); }, | |
'-': function() { return reduce(arguments, function(value, previous) { return previous - value; }); }, | |
'*': function() { return reduce(arguments, function(value, previous) { return previous * value; }); }, | |
'/': function() { return reduce(arguments, function(value, previous) { return previous / value; }); }, | |
'>': function(left, right) { return left > right; }, | |
'>=': function(left, right) { return left >= right; }, | |
'<=': function(left, right) { return left <= right; }, | |
'<': function(left, right) { return left < right; }, | |
'if': function(cond, left, right) { return (cond ? left : right); }, | |
'->': function() { | |
var args = Array.prototype.slice.call(arguments), env = args.pop(); | |
for (var i = 0, length = args.length; i < length; i ++) { | |
env = env[args[i]]; | |
} | |
return env; | |
} | |
}; | |
function evaluate(tokens, env) { | |
if (Object.prototype.toString.apply(tokens) === "[object Array]" && tokens.length > 0) { | |
for (var i = 0, length = tokens.length, token, apply; token = tokens[i], i < length; i ++) { | |
tokens[i] = evaluate(token, env); | |
} | |
apply = (env[tokens[0]] || tokens[0]); | |
if (typeof (apply) !== 'function') { | |
apply = function() { return tokens[0]; }; | |
} | |
tokens = apply.apply(this, tokens.slice(1).concat([env])); | |
} | |
return tokens; | |
}; | |
function transform(token) { | |
if (token.indexOf('"') === 0) { | |
return token.substring(1, token.length - 1); | |
} | |
var number = parseFloat(token); | |
if (isNaN(number)) { | |
return token; | |
} | |
return number; | |
}; | |
function tokenize(code) { | |
var tokens = [], queue = ''; | |
for (var i = 0, length = code.length, char; char = code.charAt(i), i < length; i ++) { | |
if (char === '(' || char === ')' || /\s/.exec(char)) { | |
if (queue.length) { | |
tokens.push(queue); | |
} | |
if (char.replace(/\s+/g, '').length) { | |
tokens.push(char); | |
} | |
queue = ''; | |
} else { | |
queue = queue + char; | |
} | |
} | |
return tokens; | |
}; | |
function parse(code) { | |
var tokens = tokenize(code), | |
result = [], stack = [result], tmp; | |
for (var i = 0, length = tokens.length, token; token = tokens[i], i < length; i ++) { | |
if (token === '(') { | |
tmp = []; | |
stack[0].push(tmp); | |
stack = [tmp].concat(stack); | |
} else if (token === ')') { | |
if (stack.length) { | |
stack = stack.slice(1); | |
} | |
} else { | |
stack[0].push(transform(token)); | |
} | |
} | |
return result; | |
}; | |
function run(code, env) { | |
env || (env = {}); | |
for (var key in globalenv) { | |
env[key] = globalenv[key]; | |
} | |
return evaluate(parse(code), env); | |
}; | |
return run(code, env); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage