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 java.util.Map;
import com.sk89q.worldedit.expression.Identifiable; import com.sk89q.worldedit.expression.Identifiable;
import com.sk89q.worldedit.expression.lexer.tokens.IdentifierToken; 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.NumberToken;
import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken; import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken;
import com.sk89q.worldedit.expression.lexer.tokens.Token; 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.Constant;
import com.sk89q.worldedit.expression.runtime.Functions; import com.sk89q.worldedit.expression.runtime.Functions;
import com.sk89q.worldedit.expression.runtime.RValue; import com.sk89q.worldedit.expression.runtime.RValue;
@ -71,7 +73,7 @@ public class Parser {
} }
private RValue parse() throws ParserException { private RValue parse() throws ParserException {
final RValue ret = parseMultipleStatements(); final RValue ret = parseStatements(false);
if (position < tokens.size()) { if (position < tokens.size()) {
final Token token = peek(); final Token token = peek();
throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token); throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token);
@ -79,29 +81,77 @@ public class Parser {
return ret; return ret;
} }
private RValue parseMultipleStatements() throws ParserException { private RValue parseStatements(boolean singleStatement) throws ParserException {
List<RValue> statements = new ArrayList<RValue>(); List<RValue> statements = new ArrayList<RValue>();
loop: while (true) { loop: while (true) {
if (position >= tokens.size()) { if (position >= tokens.size()) {
break; break;
} }
switch (peek().id()) { final Token current = peek();
switch (current.id()) {
case ';': case ';':
++position; ++position;
if (singleStatement) {
break loop;
}
break; break;
case '{': case '{':
statements.add(parseBlock()); statements.add(parseBlock());
if (singleStatement) {
break loop;
}
break; break;
case '}': case '}':
break loop; 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: default:
statements.add(parseExpression()); statements.add(parseExpression());
if (peek().id() == ';') {
++position;
if (singleStatement) {
break loop;
}
break; break;
} }
else {
break loop;
}
}
} }
switch (statements.size()) { switch (statements.size()) {
@ -258,7 +308,7 @@ public class Parser {
return new Sequence(peek().getPosition()); return new Sequence(peek().getPosition());
} }
final RValue ret = parseMultipleStatements(); final RValue ret = parseStatements(false);
if (peek().id() != '}') { if (peek().id() != '}') {
throw new ParserException(peek().getPosition(), "Unmatched opening brace"); 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 @@ import com.sk89q.worldedit.expression.parser.ParserException;
public class ExpressionTest { public class ExpressionTest {
@Test @Test
public void testEvaluate() throws Exception { public void testEvaluate() throws ExpressionException {
// check // check
assertEquals(1-2+3, simpleEval("1-2+3"), 0); assertEquals(1-2+3, simpleEval("1-2+3"), 0);
@ -69,7 +69,25 @@ public class ExpressionTest {
assertEquals(5, foo.getVariable("c").getValue(), 0); 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(); return Expression.compile(expression).evaluate();
} }
} }