package openmods.calc.parsing; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.PeekingIterator; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import openmods.calc.BinaryOperator; import openmods.calc.Operator; import openmods.calc.OperatorDictionary; import openmods.calc.UnaryOperator; import openmods.utils.Stack; public class InfixParser<E> implements IAstParser<E> { private static final String CALL_OPENING_BRACKET = "("; private final OperatorDictionary<E> operators; private IExprNodeFactory<E> exprNodeFactory; public InfixParser(OperatorDictionary<E> operators, IExprNodeFactory<E> exprNodeFactory) { this.exprNodeFactory = exprNodeFactory; this.operators = operators; } private static Token next(Iterator<Token> input) { try { return input.next(); } catch (NoSuchElementException e) { throw new UnfinishedExpressionException(); } } @Override public IExprNode<E> parse(ICompilerState<E> state, PeekingIterator<Token> input) { final Stack<IExprNode<E>> nodeStack = Stack.create(); final Stack<Operator<E>> operatorStack = Stack.create(); final BinaryOperator<E> defaultOperator = operators.getDefaultOperator(); boolean pushedNonOperatorLastLoop = false; while (input.hasNext()) { final Token token = input.peek(); boolean pushedNonOperatorThisLoop = true; if (token.type.isExpressionTerminator()) break; next(input); if (token.type.isValue()) { nodeStack.push(exprNodeFactory.createValueNode(token)); } else if (token.type.isSymbol()) { Preconditions.checkArgument(token.type != TokenType.SYMBOL_WITH_ARGS, "Symbol '%s' can't be used in infix mode", token.value); if (input.hasNext()) { final Token nextToken = input.peek(); if (nextToken.type == TokenType.LEFT_BRACKET && nextToken.value.equals(CALL_OPENING_BRACKET)) { input.next(); final String openingBracket = nextToken.value; final String closingBracket = TokenUtils.getClosingBracket(openingBracket); final ISymbolCallStateTransition<E> stateTransition = state.getStateForSymbolCall(token.value); final List<IExprNode<E>> childrenNodes = collectChildren(input, openingBracket, closingBracket, stateTransition.getState()); nodeStack.push(stateTransition.createRootNode(childrenNodes)); } else { nodeStack.push(exprNodeFactory.createSymbolGetNode(token.value)); } } else { nodeStack.push(exprNodeFactory.createSymbolGetNode(token.value)); } } else if (token.type == TokenType.MODIFIER) { final IModifierStateTransition<E> stateTransition = state.getStateForModifier(token.value); final ICompilerState<E> newState = stateTransition.getState(); final IAstParser<E> newParser = newState.getParser(); final IExprNode<E> parsedNode = newParser.parse(newState, input); nodeStack.push(stateTransition.createRootNode(parsedNode)); } else if (token.type == TokenType.LEFT_BRACKET) { final String openingBracket = token.value; final String closingBracket = TokenUtils.getClosingBracket(openingBracket); final List<IExprNode<E>> childrenNodes = collectChildren(input, openingBracket, closingBracket, state); nodeStack.push(exprNodeFactory.createBracketNode(openingBracket, closingBracket, childrenNodes)); } else if (token.type == TokenType.OPERATOR) { final Operator<E> op; if (!pushedNonOperatorLastLoop) { // i.e. pushed operator or parsing started op = operators.getUnaryOperator(token.value); Preconditions.checkArgument(op != null, "No unary version of operator: %s", token.value); } else { op = operators.getBinaryOperator(token.value); Preconditions.checkArgument(op != null, "Invalid operator: %s", token.value); } pushOperator(nodeStack, operatorStack, op); pushedNonOperatorThisLoop = false; } else { throw new InvalidTokenException(token); } if (pushedNonOperatorLastLoop && pushedNonOperatorThisLoop) { // simulate push of operator in last loop final IExprNode<E> thisLoopPush = nodeStack.pop(); pushOperator(nodeStack, operatorStack, defaultOperator); nodeStack.push(thisLoopPush); } pushedNonOperatorLastLoop = pushedNonOperatorThisLoop; } while (!operatorStack.isEmpty()) { final Operator<E> op = operatorStack.pop(); pushOperator(nodeStack, op); } if (nodeStack.size() != 1) throw new NonExpressionException("Stack: " + nodeStack.printContents()); return nodeStack.pop(); } private List<IExprNode<E>> collectChildren(PeekingIterator<Token> input, String openingBracket, String closingBracket, ICompilerState<E> compilerState) { final List<IExprNode<E>> args = Lists.newArrayList(); { if (!input.hasNext()) throw new UnmatchedBracketsException(openingBracket); if (input.peek().type == TokenType.RIGHT_BRACKET) { Token token = next(input); Preconditions.checkState(token.value.equals(closingBracket), "Unmatched brackets: '%s' and '%s'", openingBracket, token.value); return args; } } while (true) { final IAstParser<E> newParser = compilerState.getParser(); final IExprNode<E> parsedNode = newParser.parse(compilerState, input); args.add(parsedNode); final Token token = next(input); if (token.type == TokenType.RIGHT_BRACKET) { if (!token.value.equals(closingBracket)) throw new UnmatchedBracketsException(openingBracket, token.value); return args; } else { Preconditions.checkState(token.type == TokenType.SEPARATOR, "Expected arg separator, got %s", token); } } } private void pushOperator(Stack<IExprNode<E>> output, Stack<Operator<E>> operatorStack, Operator<E> newOp) { while (!operatorStack.isEmpty()) { final Operator<E> top = operatorStack.peek(0); if (!newOp.isLessThan(top)) break; operatorStack.pop(); pushOperator(output, top); } operatorStack.push(newOp); } private void pushOperator(Stack<IExprNode<E>> nodeStack, Operator<E> op) { if (op instanceof BinaryOperator) { final IExprNode<E> right = nodeStack.pop(); final IExprNode<E> left = nodeStack.pop(); nodeStack.push(exprNodeFactory.createBinaryOpNode((BinaryOperator<E>)op, left, right)); } else if (op instanceof UnaryOperator) { final IExprNode<E> arg = nodeStack.pop(); nodeStack.push(exprNodeFactory.createUnaryOpNode((UnaryOperator<E>)op, arg)); } else throw new IllegalStateException("Unknown type of operator: " + op.getClass()); } }