Skip to content

Instantly share code, notes, and snippets.

@lilpolymath
Last active December 4, 2021 06:22
Show Gist options
  • Save lilpolymath/76adc44a84bc93e467a560afa5180adf to your computer and use it in GitHub Desktop.
Save lilpolymath/76adc44a84bc93e467a560afa5180adf to your computer and use it in GitHub Desktop.
An expression evaluator in JavaScript.
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