Last active
December 4, 2021 06:22
-
-
Save lilpolymath/76adc44a84bc93e467a560afa5180adf to your computer and use it in GitHub Desktop.
An expression evaluator in JavaScript.
This file contains hidden or 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
const WHITESPACE = /\s/; | |
const NUMBER = /[0-9]/; | |
const OPERATORS = /[+\-/*()^]/; | |
const NICHED_OPERATORS = /[+\-/*^]/; | |
const NUMALPHABET = /[0-9\.]/; | |
const OPERATOR_PRECEDENCE = { '*': 2, '/': 2, '+': 1, '-': 1 }; | |
const tokenize = expression => { | |
let cursor = 0; | |
let tokens = []; | |
while (cursor < expression.length) { | |
let char = expression[cursor]; | |
if (WHITESPACE.test(char)) { | |
cursor++; | |
continue; | |
} else if (NUMBER.test(char)) { | |
let value = ''; | |
while (NUMALPHABET.test(char)) { | |
value += char; | |
char = expression[++cursor]; | |
} | |
let number = parseFloat(value); | |
tokens.push(number); | |
continue; | |
} else if (OPERATORS.test(char)) { | |
tokens.push(char); | |
cursor++; | |
continue; | |
} | |
throw new Error(`Invalid token ${char} at position ${cursor}.`); | |
} | |
return tokens; | |
}; | |
const evaluate = tokens => { | |
let cursor = 0; | |
let operands = []; | |
for (let cursor = 0; cursor < tokens.length; cursor++) { | |
let char = tokens[cursor]; | |
if (OPERATORS.test(char)) { | |
let a = operands.pop(); | |
let b = operands.pop(); | |
if (char === '+') { | |
operands.push(b + a); | |
} else if (char === '-') { | |
operands.push(b - a); | |
} else if (char === '*') { | |
operands.push(b * a); | |
} else if (char === '^') { | |
operands.push(Math.pow(b, a)); | |
} else if (char === '/') { | |
operands.push(b / a); | |
} | |
continue; | |
} | |
operands.push(char); | |
continue; | |
} | |
return operands; | |
}; | |
const checkPrecedence = (operators, nextToken) => { | |
if (operators.length === 0) { | |
return false; | |
} | |
const lastOperator = operators[operators.length - 1]; | |
return OPERATOR_PRECEDENCE[lastOperator] >= OPERATOR_PRECEDENCE[nextToken]; | |
}; | |
const infixToPostfix = tokened => { | |
let result = []; | |
let operators = []; | |
for (let cursor = 0; cursor < tokened.length; cursor++) { | |
let char = tokened[cursor]; | |
if (typeof char === 'number') { | |
result.push(char); | |
continue; | |
} | |
if (NICHED_OPERATORS.test(char)) { | |
while (checkPrecedence(operators, char)) { | |
result.push(operators.pop()); | |
} | |
operators.push(char); | |
continue; | |
} | |
if (char === '(') { | |
operators.push(char); | |
continue; | |
} | |
if (char === ')') { | |
while (operators[operators.length - 1] !== '(' && operators.length > 0) { | |
result.push(operators.pop()); | |
} | |
operators.pop(); | |
continue; | |
} | |
throw new Error(`Unparsed token ${char} at position ${cursor}`); | |
} | |
while (operators.length > 0) { | |
result.push(operators.pop()); | |
} | |
return result; | |
}; | |
console.log(evaluate(infixToPostfix(tokenize('2 + (4 - 2) + 4')))); | |
module.exports = { | |
tokenize, | |
evaluate, | |
infixToPostfix, | |
checkPrecedence, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment