Last active
April 5, 2016 21:36
-
-
Save wavebeem/f88f0e9a594c5621aef1721f40e63d05 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 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