package com.wesleyhome.math.equation;
import static com.wesleyhome.math.equation.EquationGrammerUtil.getOperatorFromToken;
import static com.wesleyhome.math.equation.EquationGrammerUtil.getType;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.DECIMAL;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.DIV;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.FRACTION;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.FSEP;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.IDENT;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.INTEGER;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.LPAREN;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.MINUS;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.MULT;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.PLUS;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.POW;
import static com.wesleyhome.math.equation.grammer.EquationGrammarParser.RPAREN;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Queue;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import com.wesleyhome.math.equation.grammer.EquationGrammarParser.NegexpContext;
class InfixEvaluator {
private final String expression;
InfixEvaluator(final String expression) {
this.expression = expression;
}
EquationNode getNode(final TerminalNode token) {
Queue<TerminalNode> queue = new ArrayDeque<TerminalNode>();
queue.add(token);
return getNode(queue);
}
EquationNode getNode(final Queue<TerminalNode> tokenList) {
Deque<EquationNode> outputQueue = new ArrayDeque<EquationNode>();
Deque<Operator> operatorStack = new ArrayDeque<Operator>();
while (!tokenList.isEmpty()) {
TerminalNode nextNode = tokenList.poll();
int type = getType(nextNode);
switch (type) {
case IDENT:
TerminalNode peek = tokenList.peek();
if (peek != null && getType(peek) == LPAREN) {
String functionName = nextNode.getText();
// Function call
Queue<TerminalNode> newTokenList = new ArrayDeque<TerminalNode>();
int passParens = 0;
tokenList.poll();
nextNode = tokenList.poll();
int type2 = getType(nextNode);
while (true) {
if (type2 == LPAREN) {
passParens++;
}
if (type2 == RPAREN) {
if (passParens == 0) {
break;
}
passParens--;
}
newTokenList.add(nextNode);
nextNode = tokenList.poll();
type2 = getType(nextNode);
}
outputQueue.push(getFunctionNode(functionName, newTokenList));
} else {
outputQueue.push(new VariableNode(nextNode.getText()));
}
break;
case INTEGER:
case DECIMAL:
outputQueue.push(new NumericNode(nextNode.getText()));
break;
case FRACTION:
outputQueue.push(new FractionNode(nextNode.getText()));
break;
case MINUS: {
ParseTree parent = nextNode.getParent();
if (parent instanceof NegexpContext) {
nextNode = tokenList.poll();
EquationNode node = null;
if (LPAREN == getType(nextNode)) {
Queue<TerminalNode> newTokenList = new ArrayDeque<TerminalNode>();
int passParens = 0;
nextNode = tokenList.poll();
int type2 = getType(nextNode);
while (true) {
if (type2 == LPAREN) {
passParens++;
}
if (type2 == RPAREN) {
if (passParens == 0) {
break;
}
passParens--;
}
newTokenList.add(nextNode);
nextNode = tokenList.poll();
type2 = getType(nextNode);
}
node = getNode(newTokenList);
} else {
node = getNode(nextNode);
}
outputQueue.push(new NegateNode(node));
break;
}
}
case PLUS:
case MULT:
case DIV:
case POW: {
Operator o1 = getOperatorFromToken(nextNode);
Operator o2 = operatorStack.peek();
while (o2 != null
&& (o1.isLeftAssociative() && o1.getPrecedence() <= o2.getPrecedence() || o1.getPrecedence() < o2.getPrecedence())) {
popExpression(outputQueue, operatorStack.pop());
o2 = operatorStack.peek();
}
operatorStack.push(o1);
break;
}
case LPAREN: {
Queue<TerminalNode> newTokenList = new ArrayDeque<TerminalNode>();
int passParens = 0;
nextNode = tokenList.poll();
int type2 = getType(nextNode);
while (true) {
if (type2 == LPAREN) {
passParens++;
}
if (type2 == RPAREN) {
if (passParens == 0) {
break;
}
passParens--;
}
newTokenList.add(nextNode);
nextNode = tokenList.poll();
type2 = getType(nextNode);
}
outputQueue.push(getNode(newTokenList));
}
}
}
while (!operatorStack.isEmpty()) {
Operator pop = operatorStack.pop();
popExpression(outputQueue, pop);
}
return outputQueue.pop();
}
public void popExpression(final Deque<EquationNode> outputQueue, final Operator o2) {
EquationNode rhs = outputQueue.poll();
EquationNode lhs = outputQueue.poll();
outputQueue.push(new OperationNode(lhs, o2, rhs));
}
private FunctionNode getFunctionNode(final String functionName, final Queue<TerminalNode> newTokenList) {
Queue<Queue<TerminalNode>> parameterList = split(newTokenList);
return getFunctionNode(parameterList, functionName);
}
private FunctionNode getFunctionNode(final Queue<Queue<TerminalNode>> parameterList, final String functionName) {
FunctionNode functionNode = new FunctionNode(functionName);
Function function = FunctionFactory.getFunction(functionName);
int totalParameters = parameterList.size();
EquationNode node1 = getNode(parameterList.poll());
functionNode.setParamOneNode(node1);
if (function.isBinary()) {
if (totalParameters == 2) {
functionNode.setParamTwoNode(getNode(parameterList.poll()));
} else {
functionNode.setParamTwoNode(getFunctionNode(parameterList, functionName));
}
}
return functionNode;
}
private Queue<Queue<TerminalNode>> split(final Queue<TerminalNode> tokenList) {
Queue<Queue<TerminalNode>> list = new ArrayDeque<Queue<TerminalNode>>();
while (!tokenList.isEmpty()) {
Queue<TerminalNode> newTokenList = new ArrayDeque<TerminalNode>();
TerminalNode poll = tokenList.poll();
int type = getType(poll);
while (type != FSEP) {
newTokenList.add(poll);
if (tokenList.isEmpty()) {
break;
}
poll = tokenList.poll();
type = getType(poll);
}
list.add(newTokenList);
}
return list;
}
EquationNode compile() {
StringEquationParser parser = new StringEquationParser();
List<TerminalNode> savedTokenList = parser.parseEquation(expression);
Queue<TerminalNode> tokenList = new ArrayDeque<TerminalNode>(savedTokenList);
EquationNode rootNode = getNode(tokenList);
return rootNode;
}
}