Skip to content

Instantly share code, notes, and snippets.

@wavebeem
Last active April 5, 2016 21:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wavebeem/f88f0e9a594c5621aef1721f40e63d05 to your computer and use it in GitHub Desktop.
Save wavebeem/f88f0e9a594c5621aef1721f40e63d05 to your computer and use it in GitHub Desktop.
var util = require("util");
var P = require("parsimmon");
// Throw away whitespace surrounding a string.
function operator(s) {
return P.optWhitespace
.then(P.string(s))
.skip(P.optWhitespace);
}
// This is where the magic happens. It takes the parser representing the next
// precedence level, and the parser the actually recognizes the operators
// themselves (+-*/), and parses out a list of things separated by these
// operators, and combines them into arrays that look like.
//
// ["Add",
// ["Multiply",
// ["Number", 3],
// ["Number", 4]],
// ["Number", 10]]
function leftBinaryOperator(next, op) {
// This function takes (a, [operator, b]) and returns [operator, a, b].
function operatorReducer(acc, pair) {
return [pair[0], acc, pair[1]];
}
// Takes (a, [[op1, b1], [op2, b2], ...]).
// Returns [op2, [op1, a, b1], b2], or simply 'a' if the list is empty.
function combineOperators(first, rest) {
return rest.reduce(operatorReducer, first);
}
return P.seqMap(next, P.seq(op, next).many(), combineOperators);
}
// This could be more complicated than just number literals. Variables could go
// here, or function calls, or other data literals.
var Num =
P.regex(/[0-9]+/).map(n => ["Number", +n]);
// Turn the operator symbols into names for the tree structure
var OpMul = operator("*").result("Multiply");
var OpDiv = operator("/").result("Divide");
var OpAdd = operator("+").result("Add");
var OpSub = operator("-").result("Subtract");
// Pair up the operators with same precedence level.
var MulDiv = P.alt(OpMul, OpDiv);
var AddSub = P.alt(OpAdd, OpSub);
// MathParser1 and MathParser2 work exactly the same way. This just shows off an
// explicit naming of each precedence level, vs simply ordering them in a list.
var MathParser1 = (function() {
var Mul = leftBinaryOperator(Num, MulDiv);
var Add = leftBinaryOperator(Mul, AddSub);
return Add;
}());
var MathParser2 =
[MulDiv, AddSub].reduce(leftBinaryOperator, Num);
var input = "2 + 3 * 4 - 3 / 1";
var result = MathParser2.parse(input).value;
var txt = util.inspect(result, {depth: null, colors: true});
console.log(txt);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment