import { forEach, last } from "ramda"; import { isNumber, isConstant, isVariable, isOperator, isNamedFunction, isLeftParenthesis, isRightParenthesis, } from "../queries"; import ASTNode from "./node"; import { Token } from "../config"; import { Variables } from "../../reduce-ast"; import { InvalidExpressionError } from "../../InvalidExpressionError"; const addOperandNode = (nodes: ASTNode[], token: Token) => { const node = new ASTNode(token); nodes.push(node); }; const addOperatorNode = (nodes: ASTNode[], token: Token) => { const node = new ASTNode(token); if (token.args && token.args > 1) { node.setRight(nodes.pop() || null); } node.setLeft(nodes.pop() || null); nodes.push(node); }; const parse = (tokens: Token[], variables: Variables) => { const ops: Token[] = []; const nodes: ASTNode[] = []; forEach((token) => { if (isVariable(token) && variables[token.value as string] === undefined) { throw new TypeError(`Could not find variable ${token.value}`); } if (isNumber(token) || isVariable(token) || isConstant(token)) { addOperandNode(nodes, token); } if (isLeftParenthesis(token)) { ops.push(token); } if (isRightParenthesis(token)) { while (last(ops) && !isLeftParenthesis(last(ops))) { addOperatorNode(nodes, ops.pop() as Token); } ops.pop(); } if (isOperator(token) || isNamedFunction(token)) { while ( last(ops) && ((last(ops) as Token).precedence as number) >= (token.precedence as number) && !isLeftParenthesis(token) ) { addOperatorNode(nodes, ops.pop() as Token); } ops.push(token); } }, tokens); while (ops.length > 0) { addOperatorNode(nodes, ops.pop() as Token); } if (nodes.length > 1) { throw new InvalidExpressionError("Could not parse, too many nodes"); } return nodes.pop() as ASTNode; }; export default parse;