Skip to content

Instantly share code, notes, and snippets.

@amobiz
Created October 1, 2015 05:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save amobiz/2435713ddec72e7ce87e to your computer and use it in GitHub Desktop.
Save amobiz/2435713ddec72e7ce87e to your computer and use it in GitHub Desktop.
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