public FormulaParser(Iterator<Token> tokens) { this.lexer = peekingIterator(filter(tokens, token -> token.getType() != TokenType.WHITESPACE)); }
private SymbolNode symbol(Token token) { assert token.getType() == TokenType.SYMBOL; return new SymbolNode(token); }
public SymbolNode(Token token) { assert token.getType() == TokenType.SYMBOL; this.name = token.getString(); this.sourceRange = new SourceRange(token); }
private FormulaNode booleanLiteral() { Token token = lexer.next(); assert token.getType() == TokenType.BOOLEAN_LITERAL; boolean value = token.getString().toLowerCase().equals("true"); SourceRange source = new SourceRange(token); return new ConstantNode(value, source); }
/** * Consume and return the next token if it is of type {@code tokenType}, or * throw an {@link FormulaSyntaxException} if there are no remaining tokens or * the next token is of the wrong type. * @param tokenType * @return */ private Token expect(TokenType tokenType) { if(lexer.hasNext()) { Token token = lexer.next(); if (token.getType() == tokenType) { return token; } } throw new FormulaSyntaxException("Expected " + tokenType); }
private FormulaNode enumLiteral() { Token token = lexer.next(); assert token.getType() == TokenType.SYMBOL; return new ConstantNode(token, new SourceRange(token)); }
private FormulaNode stringLiteral() { Token token = lexer.next(); assert token.getType() == TokenType.STRING_LITERAL; return new ConstantNode(token.getString(), new SourceRange(token)); }
private FormulaNode unary() { // <unary> ::= + <unary> | - <unary> | <unary2> if(!lexer.hasNext()) { throw new FormulaSyntaxException("Unexpected end of formula"); } Token token = lexer.peek(); if(token.getType() == TokenType.OPERATOR) { if(token.getString().equals("-") || token.getString().equals("+")) { Token opToken = lexer.next(); FormulaFunction op = function(opToken); FormulaNode operand = unary(); SourceRange sourceRange = new SourceRange(opToken.getStart(), operand.getSourceRange().getEnd()); return new FunctionCallNode(op, singletonList(operand), sourceRange); } } return unary2(); }
private FormulaNode unary2() { if(!lexer.hasNext()) { throw new FormulaSyntaxException("Unexpected end of formula"); } Token token = lexer.peek(); if(token.getType() == TokenType.OPERATOR) { if(token.getString().equals("!")) { Token opToken = lexer.next(); FormulaFunction op = function(opToken); FormulaNode operand = primary(); SourceRange sourceRange = new SourceRange(opToken.getStart(), operand.getSourceRange().getEnd()); return new FunctionCallNode(op, singletonList(operand), sourceRange); } } return primary(); }
private FormulaNode call(Token functionToken) { FormulaFunction function = function(functionToken); expect(TokenType.PAREN_START); List<FormulaNode> arguments = new ArrayList<>(); while(true) { if (!lexer.hasNext()) { throw new FormulaSyntaxException("Unexpected end of formula"); } TokenType nextToken = lexer.peek().getType(); if (nextToken == TokenType.COMMA) { // Consume comma and parse next argument lexer.next(); continue; } if (nextToken == TokenType.PAREN_END) { // consume paren and complete argument list Token closingParen = lexer.next(); return new FunctionCallNode(function, arguments, new SourceRange(functionToken, closingParen)); } // Otherwise parse the next argument arguments.add(expression()); } }
private void expect(String string, Token... tokens) { System.out.println("Tokenizing [" + string + "]"); FormulaLexer tokenizer = new FormulaLexer(string); int expectedIndex = 0; while (!tokenizer.isEndOfInput()) { String expectedString; Token expected; if(expectedIndex < tokens.length) { expected = tokens[expectedIndex]; expectedString = expected.toString(); } else { expected = null; expectedString = "<NOTHING>"; } Token actual = tokenizer.next(); System.out.println(String.format("Expected: %15s, got %s", expectedString, actual.toString())); if(expected != null) { assertEquals("tokenStart", expected.getTokenStartColumn(), actual.getTokenStartColumn()); assertEquals("text", expected.getString(), actual.getString()); assertEquals("type", expected.getType(), actual.getType()); if (!expected.equals(actual)) { System.err.println("Unexpected result!"); throw new AssertionError(); } } expectedIndex ++; } }
private FormulaNode primary() { switch (lexer.peek().getType()) { case SYMBOL: if(lexer.peek().getString().matches("t\\d{10}")) { return enumLiteral(); } else { return symbolOrCall(); } case NUMBER: return number(); case BOOLEAN_LITERAL: return booleanLiteral(); case STRING_LITERAL: return stringLiteral(); case PAREN_START: Token openToken = lexer.next(); FormulaNode e = expression(); Token closeToken = expect(TokenType.PAREN_END); return new GroupNode(e, new SourceRange(openToken, closeToken)); default: throw new FormulaSyntaxException(new SourceRange(lexer.peek()), "Expected a symbol, a number, a string, or '('"); } }