This is a simple parser for interpreting simple math expressions.
It recognizes positive integers, parenthesis, and +,-,*, and /.
Click the show source button to see how it works!
Firefox will treat leading 0's as Octal so 011 == 9 and NOT 11!
Click the show source button to see how it works!
Firefox will treat leading 0's as Octal so 011 == 9 and NOT 11!
/** * Recursive Decent Parser for evaluating simple Math equations * Author: Travis Payton * Date: 2/14/13 * Time: 3:50 PM * */ /* This Recursive Decent Parser uses the following Grammar: INPUT -> EXPRESSION ; EXPRESSION -> TERM { "+" | "-" TERM } TERM -> (FACTOR) { "*" | "/" FACTOR } FACTOR -> NUMBER ( "(" EXPRESSION ")" ) NUMBER -> DIGIT { DIGIT } DIGIT -> 0|1|...|9 It will recognize simple math expressions and evaluate them. Based off of Ty Anibaba's "Mathematical Expression Parser Using Recursive Descent Parsing" Article http://www.codeproject.com/Articles/318667/Mathematical-Expression-Parser-Using-Recursive-Des */ // global variables var lastreadtoken = null; var parseme = ""; // The string to parse / try to evaluate var curr = 0; // The current position we are at in the string; // Helper function for determining if a character is a number function isNumber() { return !isNaN(parseInt(parseme[curr])) && isFinite(parseme[curr]); } // Called when Evaluate! is clicked // Initializes global variables, and calls getExpression // outputs whatever results it could find function parse() { parseme = $("#inputString").val(); curr = 0; lastreadtoken = null; // Remove all the white space parseme = parseme.replace(/ /g, ""); // clear out the error and warnings from any previous attempts $("#errors").html(""); $("#warnings").html(""); $("#output").html(""); var result = getExpression(); if (!nextIs(";")) $("#warnings").append("Expected ';' at end of expression! Here's what I've evaluated so far:"); $("#output").html(result.Result); } // getExpression calls getTerm(), and tries to evaluate the expression // EXPRESSION -> TERM { "+" | "-" TERM } function getExpression() { var x = getTerm(); if (x == null) return null; while (true) { if (nextIs("+")) { var y = getTerm(); if (y.canEval && x.canEval) x.Result = x.Value = x.Value + y.Value; else { x.Value = "(" + x.Value + " + " + y.Value + ")"; $("#errors").append("Cannot evaluate '"+x.Value+"
'
"); x.CanEval = false; } } else if (nextIs("-")) { var y = getTerm(); if (y.canEval && x.canEval) x.Result = x.Value = x.Value - y.Value; else { x.Value = "(" + x.Value + " - " + y.Value + ")"; $("#errors").append("Cannot evaluate '"+x.Value+"
'
"); x.CanEval = false; } } else { break; } } x.Result = x.Value; return x; } // getTerm calls get Factor, and tries to evaluate Terms // TERM -> (FACTOR) { "*" | "/" FACTOR } function getTerm() { var x = getFactor(); if (x == null) return null; while (true) { if (nextIs("*")) { var y = getFactor(); if (y.canEval && x.canEval) x.Result = x.Value = x.Value * y.Value; else { x.Value = "(" + x.Value + " * " + y.Value + ")"; $("#errors").append("Cannot evaluate '"+x.Value+"
'
"); x.canEval = false; } } else if (nextIs("/" || "\\")) { var y = getFactor(); if (y.canEval && x.canEval) x.Result = x.Value = x.Value / y.Value; else { x.Value = "(" + x.Value + "/" + y.Value + ")"; $("#errors").append("Cannot evaluate '"+x.Value+"
'
"); x.canEval = false; } } else { break; } } return x; } // getFactor calls getNext() which returns a token. It also // FACTOR -> NUMBER ( "(" EXPRESSION ")" ) function getFactor() { var x = getNext(); lastreadtoken = null; if (x != null && x.canEval) return x; if (x != null) { if (x.Value == "(") { x = getExpression(); if (x == null || !nextIs(")")) { $("#warnings").append("Missing closing parenthesis!
"); } } } lastreadtoken = null; return x; } // getNext gets the next token to be parsed. // It recognizes +, -, *, /, (, ), and positive integers // NUMBER -> DIGIT { DIGIT } // DIGIT -> 0|1|...|9 function getNext() { if (lastreadtoken != null) { var tok = lastreadtoken; lastreadtoken = null; return tok; } if (parseme != null && parseme.length > curr) { if (isNumber()) { var val = parseme[curr]; curr++; while (isNumber() && curr < parseme.length) { val += parseme[curr++] } lastreadtoken = {Result:parseInt(val), Value:parseInt(val), canEval:true}; return lastreadtoken; } if (parseme[curr] == "(" || parseme[curr] == ")") { lastreadtoken = {Value:parseme[curr++], Result:null, canEval:false}; return lastreadtoken; } if (parseme[curr] == "+" || parseme[curr] == "-" || parseme[curr] == "*" || parseme[curr] == "/" || parseme[curr] == "\\") { lastreadtoken = {Value:parseme[curr++], Result:null, canEval:false}; return lastreadtoken; } if (parseme[curr] == ";") { lastreadtoken = {Value:parseme[curr++], Result:null, canEval:false }; return lastreadtoken; } else { $("#errors").append("Unrecognized symbol: '" + parseme[curr] + "
' at position " + (curr++) + "
"); } } return null; } // Helper function, calls getNext(), and returns a boolean // for whether or not the next Token matches the passed argument function nextIs(c) { var t = getNext(); if (t != null) { if (t.Value == c) { lastreadtoken = null; return true; } else lastreadtoken = t; } return false; }