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.BinaryOperator.Associativity; import openmods.calc.OperatorDictionary; import openmods.calc.UnaryOperator; public class PrefixParser<E> implements IAstParser<E> { private final OperatorDictionary<E> operators; private final IExprNodeFactory<E> exprNodeFactory; public PrefixParser(OperatorDictionary<E> operators, IExprNodeFactory<E> exprNodeFactory) { this.operators = operators; this.exprNodeFactory = exprNodeFactory; } private static Token next(Iterator<Token> input) { try { return input.next(); } catch (NoSuchElementException e) { throw new UnfinishedExpressionException(); } } protected IExprNode<E> parseNode(ICompilerState<E> state, PeekingIterator<Token> input) { final Token token = next(input); return parseNode(state, input, token); } private IExprNode<E> parseNode(ICompilerState<E> state, PeekingIterator<Token> input, final Token firstToken) { if (firstToken.type.isValue()) return exprNodeFactory.createValueNode(firstToken); switch (firstToken.type) { case SYMBOL: return exprNodeFactory.createSymbolGetNode(firstToken.value); case MODIFIER: return parseModifierNode(firstToken.value, state, input); case LEFT_BRACKET: return parseNestedNode(firstToken.value, state, input); default: throw new IllegalArgumentException("Unexpected token: " + firstToken); } } private IExprNode<E> parseNestedNode(String openingBracket, ICompilerState<E> state, PeekingIterator<Token> input) { final String closingBracket = TokenUtils.getClosingBracket(openingBracket); if (openingBracket.equals("(")) { final Token operationToken = next(input); final String operationName = operationToken.value; if (operationToken.type == TokenType.SYMBOL) { final ISymbolCallStateTransition<E> stateTransition = state.getStateForSymbolCall(operationName); final List<IExprNode<E>> args = collectArgs(openingBracket, closingBracket, input, stateTransition.getState()); return stateTransition.createRootNode(args); } else if (operationToken.type == TokenType.OPERATOR) { final List<IExprNode<E>> args = collectArgs(openingBracket, closingBracket, input, state); if (args.size() == 1) { final UnaryOperator<E> unaryOperator = operators.getUnaryOperator(operationName); Preconditions.checkState(unaryOperator != null, "Invalid unary operator '%s'", operationName); return exprNodeFactory.createUnaryOpNode(unaryOperator, args.get(0)); } else if (args.size() > 1) { final BinaryOperator<E> binaryOperator = operators.getBinaryOperator(operationName); Preconditions.checkState(binaryOperator != null, "Invalid binary operator '%s'", operationName); return compileBinaryOpNode(binaryOperator, args); } else { throw new IllegalArgumentException("Called operator " + operationName + " without any arguments"); } } else { // bit non-standard, but meh final IExprNode<E> target = parseNode(state, input, operationToken); final List<IExprNode<E>> args = collectArgs(openingBracket, closingBracket, input, state); return exprNodeFactory.createBinaryOpNode(operators.getDefaultOperator(), target, exprNodeFactory.createBracketNode(openingBracket, closingBracket, args)); } } else { // not parenthesis, so probably data structure final List<IExprNode<E>> args = collectArgs(openingBracket, closingBracket, input, state); return exprNodeFactory.createBracketNode(openingBracket, closingBracket, args); } } private List<IExprNode<E>> collectArgs(String openingBracket, String closingBracket, PeekingIterator<Token> input, ICompilerState<E> state) { final List<IExprNode<E>> args = Lists.newArrayList(); while (true) { final Token argToken = input.peek(); if (argToken.type == TokenType.SEPARATOR) { // comma is whitespace next(input); } else if (argToken.type == TokenType.RIGHT_BRACKET) { Preconditions.checkState(argToken.value.equals(closingBracket), "Unmatched brackets: '%s' and '%s'", openingBracket, argToken.value); next(input); break; } else { final IAstParser<E> newParser = state.getParser(); final IExprNode<E> parsedNode = newParser.parse(state, input); args.add(parsedNode); } } return args; } private IExprNode<E> parseModifierNode(String modifier, ICompilerState<E> state, PeekingIterator<Token> input) { final IModifierStateTransition<E> stateTransition = state.getStateForModifier(modifier); final ICompilerState<E> newState = stateTransition.getState(); final IAstParser<E> newParser = newState.getParser(); final IExprNode<E> parsedNode = newParser.parse(newState, input); return stateTransition.createRootNode(parsedNode); } private IExprNode<E> compileBinaryOpNode(BinaryOperator<E> op, List<IExprNode<E>> args) { if (op.associativity == Associativity.LEFT) { IExprNode<E> left = args.get(0); IExprNode<E> right = args.get(1); for (int i = 2; i < args.size(); i++) { left = exprNodeFactory.createBinaryOpNode(op, left, right); right = args.get(i); } return exprNodeFactory.createBinaryOpNode(op, left, right); } else { final int lastArg = args.size() - 1; IExprNode<E> left = args.get(lastArg - 1); IExprNode<E> right = args.get(lastArg); for (int i = lastArg - 2; i >= 0; i--) { right = exprNodeFactory.createBinaryOpNode(op, left, right); left = args.get(i); } return exprNodeFactory.createBinaryOpNode(op, left, right); } } @Override public IExprNode<E> parse(ICompilerState<E> state, PeekingIterator<Token> input) { return parseNode(state, input); } }