Created
October 1, 2015 05:50
-
-
Save amobiz/2435713ddec72e7ce87e to your computer and use it in GitHub Desktop.
This file contains 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
var log = console.log.bind(console); | |
function eval(expression) { | |
var tokens, value, resolve; | |
resolve = resolver(); | |
tokens = parse(expression); | |
if (evaluate(tokens, resolve)) { | |
log(expression + '=' + resolve.get()); | |
} else { | |
log('invalid expression:' + expression); | |
} | |
} | |
function resolver() { | |
var _resolved = []; | |
var _resolver = function (value) { | |
_resolved.push(value); | |
return true; | |
}; | |
_resolver.reset = function (value) { | |
_resolved = [value]; | |
return true; | |
}; | |
_resolver.get = function (i) { | |
return _resolved[i||0]; | |
}; | |
return _resolver; | |
} | |
function evaluate(tokens, resolve) { | |
var pointer = 0; | |
var marker = []; | |
return expression(resolve) && exhaustiveness(); | |
// expression := operation | integer | |
function expression(resolve) { | |
return operation(resolve) || integer(resolve); | |
} | |
function exhaustiveness() { | |
return pointer === tokens.length; | |
} | |
// operation := operand | operation operator operand | |
function operation(resolve) { | |
var _resolve; | |
// 注意這個內部的 resolver | |
// 內部的運算都是透過這個內部的 resolver | |
_resolve = resolver(); | |
mark(); | |
// 如果獲得一個 operand,嘗試看看能否得到一個 operation | |
if (operand(_resolve)) { | |
commit(); | |
_operation(_resolve); | |
// 得出最終結果才呼叫外部的 resolver | |
return resolve(_resolve.get()); | |
} | |
rollback(); | |
function _operation(_resolve) { | |
mark(); | |
if (operator(_resolve) && operand(_resolve)) { | |
commit(); | |
_resolve.reset(calc(_resolve)); | |
_operation(_resolve); | |
return; | |
} | |
rollback(); | |
} | |
} | |
function integer(resolve) { | |
var value = parseInt(peak()); | |
if (!isNaN(value)) { | |
advance(); | |
return resolve(value); | |
} | |
} | |
function operator(resolve) { | |
var operators = { | |
'*': function(op1, op2) { | |
return op1 * op2; | |
}, | |
'/': function(op1, op2) { | |
return op1 / op2; | |
}, | |
'+': function(op1, op2) { | |
return op1 + op2; | |
}, | |
'-': function(op1, op2) { | |
return op1 - op2; | |
} | |
}; | |
var token = peak(); | |
if (operators[token]) { | |
advance(); | |
return resolve(operators[token]); | |
} | |
} | |
function operand(resolve) { | |
return group(resolve) || integer(resolve); | |
} | |
// group := '(' operation ')' | |
function group(resolve) { | |
var _resolve = resolver(); | |
// 注意這裡以 && 串接了一般函數 | |
mark(); | |
if (match('(') && operation(_resolve) && match(')')) { | |
commit(); | |
return resolve(_resolve.get()); | |
} | |
rollback(); | |
} | |
function calc(resolved) { | |
var op1 = resolved.get(0); | |
var op = resolved.get(1); | |
var op2 = resolved.get(2); | |
return op(op1, op2); | |
} | |
function match(expected) { | |
if (peak() === expected) { | |
advance(); | |
return true; | |
} | |
} | |
function peak() { | |
return tokens[pointer]; | |
} | |
function advance() { | |
pointer = pointer + 1; | |
} | |
function mark() { | |
marker.push(pointer); | |
} | |
function commit() { | |
marker.pop(); | |
} | |
function rollback() { | |
pointer = marker.pop(); | |
} | |
} | |
function parse(expression) { | |
var i = 0, tokens = []; | |
while (tokenize()) { | |
} | |
return tokens; | |
function tokenize(token) { | |
var c; | |
if (i >= expression.length) { | |
flush(); | |
return false; | |
} | |
c = expression[i++]; | |
switch (c) { | |
case '0': | |
case '1': | |
case '2': | |
case '3': | |
case '4': | |
case '5': | |
case '6': | |
case '7': | |
case '8': | |
case '9': | |
return tokenize(token ? token + c : c); | |
} | |
flush(); | |
switch (c) { | |
case '(': | |
case ')': | |
case '*': | |
case '/': | |
case '+': | |
case '-': | |
push(c); | |
break; | |
} | |
return true; | |
function flush() { | |
if (token) { | |
tokens.push(token); | |
} | |
} | |
function push(c) { | |
if (c !== ' ') { | |
tokens.push(c); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment