Added support for if/else to the expression parser. Basically same syntax as Java.

Also added a test case.
This commit is contained in:
TomyLobo 2011-11-22 04:11:58 +01:00
parent 5071885d10
commit aa43975e34
3 changed files with 112 additions and 7 deletions

View File

@ -26,9 +26,11 @@
import com.sk89q.worldedit.expression.Identifiable;
import com.sk89q.worldedit.expression.lexer.tokens.IdentifierToken;
import com.sk89q.worldedit.expression.lexer.tokens.KeywordToken;
import com.sk89q.worldedit.expression.lexer.tokens.NumberToken;
import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken;
import com.sk89q.worldedit.expression.lexer.tokens.Token;
import com.sk89q.worldedit.expression.runtime.Conditional;
import com.sk89q.worldedit.expression.runtime.Constant;
import com.sk89q.worldedit.expression.runtime.Functions;
import com.sk89q.worldedit.expression.runtime.RValue;
@ -71,7 +73,7 @@ public static final RValue parse(List<Token> tokens, Map<String, RValue> variabl
}
private RValue parse() throws ParserException {
final RValue ret = parseMultipleStatements();
final RValue ret = parseStatements(false);
if (position < tokens.size()) {
final Token token = peek();
throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token);
@ -79,28 +81,76 @@ private RValue parse() throws ParserException {
return ret;
}
private RValue parseMultipleStatements() throws ParserException {
private RValue parseStatements(boolean singleStatement) throws ParserException {
List<RValue> statements = new ArrayList<RValue>();
loop: while (true) {
if (position >= tokens.size()) {
break;
}
switch (peek().id()) {
final Token current = peek();
switch (current.id()) {
case ';':
++position;
if (singleStatement) {
break loop;
}
break;
case '{':
statements.add(parseBlock());
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;
final Token next = peek();
if ((next instanceof KeywordToken) && ((KeywordToken) next).value.equals("else")) {
++position;
falsePart = parseStatements(true);
} else {
falsePart = null;
}
statements.add(new Conditional(current.getPosition(), condition, truePart, falsePart));
break;
default:
throw new ParserException(current.getPosition(), "Unimplemented keyword '" + keyword + "'");
}
if (singleStatement) {
break loop;
}
break;
default:
statements.add(parseExpression());
break;
if (peek().id() == ';') {
++position;
if (singleStatement) {
break loop;
}
break;
}
else {
break loop;
}
}
}
@ -258,7 +308,7 @@ private final RValue parseBlock() throws ParserException {
return new Sequence(peek().getPosition());
}
final RValue ret = parseMultipleStatements();
final RValue ret = parseStatements(false);
if (peek().id() != '}') {
throw new ParserException(peek().getPosition(), "Unmatched opening brace");

View File

@ -0,0 +1,37 @@
package com.sk89q.worldedit.expression.runtime;
public class Conditional extends Node {
RValue condition;
RValue truePart;
RValue falsePart;
public Conditional(int position, RValue condition, RValue truePart, RValue falsePart) {
super(position);
this.condition = condition;
this.truePart = truePart;
this.falsePart = falsePart;
}
@Override
public double getValue() throws EvaluationException {
if (condition.getValue() > 0.0) {
return truePart.getValue();
}
else {
return falsePart == null ? 0 : falsePart.getValue();
}
}
@Override
public char id() {
return 't';
}
@Override
public String toString() {
return "if ("+condition+") { "+truePart+" } else { "+falsePart+" }";
}
//TODO: optimizer
}

View File

@ -10,7 +10,7 @@
public class ExpressionTest {
@Test
public void testEvaluate() throws Exception {
public void testEvaluate() throws ExpressionException {
// check
assertEquals(1-2+3, simpleEval("1-2+3"), 0);
@ -69,7 +69,25 @@ public void testAssign() throws ExpressionException {
assertEquals(5, foo.getVariable("c").getValue(), 0);
}
private double simpleEval(String expression) throws Exception {
@Test
public void testIf() throws ExpressionException {
assertEquals(40, simpleEval("if (1) x=4; else y=5; x*10+y;"), 0);
assertEquals(5, simpleEval("if (0) x=4; else y=5; x*10+y;"), 0);
// test 'dangling else'
final Expression expression1 = Expression.compile("if (1) if (0) x=4; else y=5;", "x", "y");
expression1.evaluate(1, 2);
assertEquals(1, expression1.getVariable("x").getValue(), 0);
assertEquals(5, expression1.getVariable("y").getValue(), 0);
// test if the if construct is correctly recognized as a statement
final Expression expression2 = Expression.compile("if (0) if (1) x=5; y=4;", "x", "y");
expression2.evaluate(1, 2);
assertEquals(4, expression2.getVariable("y").getValue(), 0);
}
private double simpleEval(String expression) throws ExpressionException {
return Expression.compile(expression).evaluate();
}
}