private Identifiable parseFunctionCall(IdentifierToken identifierToken) throws ParserException { consumeCharacter('('); try { if (peek().id() == ')') { ++position; return Functions.getFunction(identifierToken.getPosition(), identifierToken.value); } List<RValue> args = new ArrayList<RValue>(); loop: while (true) { args.add(parseExpression(false)); final Token current = peek(); ++position; switch (current.id()) { case ',': continue; case ')': break loop; default: throw new ParserException(current.getPosition(), "Unmatched opening bracket"); } } return Functions.getFunction( identifierToken.getPosition(), identifierToken.value, args.toArray(new RValue[args.size()])); } catch (NoSuchMethodException e) { throw new ParserException( identifierToken.getPosition(), "Function '" + identifierToken.value + "' not found", e); } }
private RValue parseStatements(boolean singleStatement) throws ParserException { List<RValue> statements = new ArrayList<RValue>(); loop: while (true) { if (position >= tokens.size()) { break; } final Token current = peek(); switch (current.id()) { case '{': consumeCharacter('{'); statements.add(parseStatements(false)); consumeCharacter('}'); if (singleStatement) { break loop; } break; case '}': break loop; case 'k': final String keyword = ((KeywordToken) current).value; switch (keyword.charAt(0)) { case 'i': { // if ++position; final RValue condition = parseBracket(); final RValue truePart = parseStatements(true); final RValue falsePart; if (hasKeyword("else")) { ++position; falsePart = parseStatements(true); } else { falsePart = null; } statements.add( new Conditional(current.getPosition(), condition, truePart, falsePart)); break; } case 'w': { // while ++position; final RValue condition = parseBracket(); final RValue body = parseStatements(true); statements.add(new While(current.getPosition(), condition, body, false)); break; } case 'd': { // do ++position; final RValue body = parseStatements(true); consumeKeyword("while"); final RValue condition = parseBracket(); statements.add(new While(current.getPosition(), condition, body, true)); break; } case 'f': { // for ++position; consumeCharacter('('); int oldPosition = position; final RValue init = parseExpression(true); // if ((init instanceof LValue) && ) if (peek().id() == ';') { ++position; final RValue condition = parseExpression(true); consumeCharacter(';'); final RValue increment = parseExpression(true); consumeCharacter(')'); final RValue body = parseStatements(true); statements.add(new For(current.getPosition(), init, condition, increment, body)); } else { position = oldPosition; final Token variableToken = peek(); if (!(variableToken instanceof IdentifierToken)) { throw new ParserException(variableToken.getPosition(), "Expected identifier"); } // In theory, I should have to create non-existant variables here. // However, the java-for parsing attempt further up already takes care of that :) RValue variable = variables.get(((IdentifierToken) variableToken).value); if (!(variable instanceof LValue)) { throw new ParserException(variableToken.getPosition(), "Expected variable"); } ++position; final Token equalsToken = peek(); if (!(equalsToken instanceof OperatorToken) || !((OperatorToken) equalsToken).operator.equals("=")) { throw new ParserException( variableToken.getPosition(), "Expected '=' or a term and ';'"); } ++position; final RValue first = parseExpression(true); consumeCharacter(','); final RValue last = parseExpression(true); consumeCharacter(')'); final RValue body = parseStatements(true); statements.add( new SimpleFor(current.getPosition(), (LValue) variable, first, last, body)); } break; } case 'b': // break ++position; statements.add(new Break(current.getPosition(), false)); break; case 'c': // continue ++position; statements.add(new Break(current.getPosition(), true)); break; case 'r': // return ++position; statements.add(new Return(current.getPosition(), parseExpression(true))); if (peek().id() == ';') { ++position; break; } else { break loop; } default: throw new ParserException( current.getPosition(), "Unimplemented keyword '" + keyword + "'"); } if (singleStatement) { break loop; } break; default: statements.add(parseExpression(true)); if (peek().id() == ';') { ++position; if (singleStatement) { break loop; } break; } else { break loop; } } } switch (statements.size()) { case 0: if (singleStatement) { throw new ParserException(peek().getPosition(), "Statement expected."); } else { return new Sequence(peek().getPosition()); } case 1: return statements.get(0); default: return new Sequence( peek().getPosition(), statements.toArray(new RValue[statements.size()])); } }
private void assertCharacter(char character) throws ParserException { final Token next = peek(); if (next.id() != character) { throw new ParserException(next.getPosition(), "Expected '" + character + "'"); } }
private final RValue parseExpression(boolean canBeEmpty) throws ParserException { LinkedList<Identifiable> halfProcessed = new LinkedList<Identifiable>(); // process brackets, numbers, functions, variables and detect prefix operators boolean expressionStart = true; loop: while (position < tokens.size()) { final Token current = peek(); switch (current.id()) { case '0': halfProcessed.add(new Constant(current.getPosition(), ((NumberToken) current).value)); ++position; expressionStart = false; break; case 'i': final IdentifierToken identifierToken = (IdentifierToken) current; ++position; final Token next = peek(); if (next.id() == '(') { halfProcessed.add(parseFunctionCall(identifierToken)); } else { RValue variable = variables.get(identifierToken.value); if (variable == null) { if (next instanceof OperatorToken && ((OperatorToken) next).operator.equals("=")) { // Ugly hack to make temporary variables work while not sacrificing error reporting. variables.put(identifierToken.value, variable = new Variable(0)); } else { throw new ParserException( current.getPosition(), "Variable '" + identifierToken.value + "' not found"); } } halfProcessed.add(variable); } expressionStart = false; break; case '(': halfProcessed.add(parseBracket()); expressionStart = false; break; case ',': case ')': case '}': case ';': break loop; case 'o': if (expressionStart) { // Preprocess prefix operators into unary operators halfProcessed.add(new UnaryOperator((OperatorToken) current)); } else { halfProcessed.add(current); } ++position; expressionStart = true; break; default: halfProcessed.add(current); ++position; expressionStart = false; break; } } if (halfProcessed.isEmpty() && canBeEmpty) { return new Sequence(peek().getPosition()); } return ParserProcessors.processExpression(halfProcessed); }