Skip to content

Instantly share code, notes, and snippets.

@Jxck
Created August 25, 2012 08:12
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Jxck/3462396 to your computer and use it in GitHub Desktop.
Save Jxck/3462396 to your computer and use it in GitHub Desktop.
implement calculator by js with BNF parser
DEBUG = false;
TEST = true;
noop = function() {};
log = assert = noop;
if (DEBUG) {
log = console.log.bind(console);
}
if (TEST) {
assert = function(a,b,m) { console.assert(a.toString() === b.toString(), m); };
log('test lexer');
assert(lexer('12'), [12]);
assert(lexer('- 12 + 3'), ['-', 12, '+', 3]);
assert(lexer('12+345-678*910'), [12, '+', 345, '-', 678, '*', 910]);
log('test parser');
assert(parser([12]), 12);
assert(parser([1, '+', 2]), 3);
assert(parser([12, '+', 3]), 15);
assert(parser([12, '+', 345, '-', 678, '*', 910]), -616623);
log('test all');
assert(parser(lexer('4/2')), 2);
assert(parser(lexer('4-23+5')), -14);
assert(parser(lexer('12+345-678*910')), -616623);
assert(parser(lexer('(2*3)/(2-1+1)+11')), 14);
}
function lexer(str) {
var buf = [];
var tmp = '';
for (var i = 0; i < str.length; i++) {
var c = str[i];
if (c === ' ') continue;
if (c === '+' || c === '-'
|| c === '*' || c === '/'
|| c === '(' || c === ')') {
if (tmp) {
// if tmp exists, that is a number
// so push before operator
buf.push(Number(tmp));
tmp = '';
}
buf.push(c);
continue;
}
tmp += c;
}
if (tmp) buf.push(Number(tmp));
return buf;
}
function parser(lexed) {
log(lexed);
// expr : term { (+|-) term }
// term : fact { (*|/) fact }
// fact : number | '(' expr ')'
function fact() {
var c = lexed.shift(); log('1', c);
if (typeof(c) === 'number') {
return c;
}
if (c === '(') {
c = expr(); log('2', c);
if (lexed.shift() !== ')') throw 'invalid expr';
return c;
}
throw 'invalid expr';
}
function term() {
var c = fact(); log('3', c);
while (lexed[0] === '*' || lexed[0] === '/') {
var o = lexed.shift(); log('4', o);
if (o === '*') c = c * term();
if (o === '/') c = c / term();
}
return c;
}
function expr() {
var c = term(); log('5', c);
while (lexed[0] === '+' || lexed[0] === '-') {
var o = lexed.shift(); log('6', o);
if (o === '+') c = c + term();
if (o === '-') c = c - term();
}
return c;
}
return expr();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment