|
#!/usr/bin/env node |
|
|
|
function lex(string) { |
|
var tokens = string.match(/\(|\)|\d+(\.\d+)?|\w+|[+*\/-]/g); |
|
return tokens.map(function(token) { |
|
return /^\d/.test(token) ? parseFloat(token) : token; |
|
}); |
|
} |
|
|
|
function parse(tokens) { |
|
var nodes = []; |
|
tokens.shift(); |
|
while (token = tokens.shift()) { |
|
if (token == "(") { |
|
tokens.unshift(token); |
|
nodes.push(parse(tokens)); |
|
} else if (token == ")") { |
|
return nodes; |
|
} else { |
|
nodes.push(token); |
|
} |
|
} |
|
} |
|
|
|
function interpret(node) { |
|
if (Array.isArray(node)) { |
|
var fn = interpret(node[0]); |
|
var args = node.slice(1).map(interpret); |
|
return fn.apply(null, args); |
|
} else if (typeof node === "number") { |
|
return node; |
|
} else { |
|
return environment[node]; |
|
} |
|
} |
|
|
|
function compile(node) { |
|
if (Array.isArray(node)) { |
|
var operator = node[0]; |
|
var values = node.slice(1).map(compile); |
|
return "(" + values.join(operator) + ")"; |
|
} else { |
|
return node; |
|
} |
|
} |
|
|
|
var reduce = Array.prototype.reduce; |
|
var environment = {}; |
|
environment["+"] = function() { return reduce.call(arguments, function(a,n) { return a + n; }, 0); }; |
|
environment["*"] = function() { return reduce.call(arguments, function(a,n) { return a * n; }, 1); }; |
|
environment["-"] = function(a,b) { return a - b; }; |
|
environment["/"] = function(a,b) { return a / b; }; |
|
|
|
function run(code) { |
|
var tokens = lex(code); |
|
var ast = parse(tokens.slice()); |
|
var compiled = compile(ast); |
|
var interpretedResult = interpret(ast); |
|
var compiledResult = eval(compiled); |
|
|
|
console.log("tokens =>", tokens); |
|
console.log("ast =>", ast); |
|
console.log("compiled =>", compiled); |
|
console.log(""); |
|
console.log("interpret(parse(lex(code))) =>", interpretedResult); |
|
console.log("eval(compile(parse(lex(code)))) =>", compiledResult); |
|
} |
|
|
|
process.stdout.write("example: (+ 1 2 (* 3.1415 42) (- 3 4) (/ 5 6))\n"); |
|
process.stdout.write("> "); |
|
process.stdin.resume(); |
|
process.stdin.setEncoding('utf8'); |
|
process.stdin.on('data', function(code) { |
|
run(code) |
|
process.stdout.write("> "); |
|
}); |