Added while loops to the expression parser.

Also added a test case.
Iterations are currently limited to 256 maximum.
This commit is contained in:
TomyLobo 2011-11-22 06:07:22 +01:00
parent aa43975e34
commit f217be0bdf
5 changed files with 96 additions and 4 deletions

View File

@ -107,7 +107,7 @@ public class Lexer {
characterTokens.add(';'); characterTokens.add(';');
} }
private static final Set<String> keywords = new HashSet<String>(Arrays.asList("if", "else")); private static final Set<String> keywords = new HashSet<String>(Arrays.asList("if", "else", "while", "do"));
private static final Pattern numberPattern = Pattern.compile("^([0-9]*(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)"); private static final Pattern numberPattern = Pattern.compile("^([0-9]*(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)");
private static final Pattern identifierPattern = Pattern.compile("^([A-Za-z][0-9A-Za-z_]*)"); private static final Pattern identifierPattern = Pattern.compile("^([A-Za-z][0-9A-Za-z_]*)");

View File

@ -36,6 +36,7 @@ import com.sk89q.worldedit.expression.runtime.Functions;
import com.sk89q.worldedit.expression.runtime.RValue; import com.sk89q.worldedit.expression.runtime.RValue;
import com.sk89q.worldedit.expression.runtime.Sequence; import com.sk89q.worldedit.expression.runtime.Sequence;
import com.sk89q.worldedit.expression.runtime.Variable; import com.sk89q.worldedit.expression.runtime.Variable;
import com.sk89q.worldedit.expression.runtime.While;
/** /**
* Processes a list of tokens into an executable tree. * Processes a list of tokens into an executable tree.
@ -112,7 +113,7 @@ public class Parser {
case 'k': case 'k':
final String keyword = ((KeywordToken) current).value; final String keyword = ((KeywordToken) current).value;
switch (keyword.charAt(0)) { switch (keyword.charAt(0)) {
case 'i': // if case 'i': { // if
++position; ++position;
final RValue condition = parseBracket(); final RValue condition = parseBracket();
final RValue truePart = parseStatements(true); final RValue truePart = parseStatements(true);
@ -128,6 +129,31 @@ public class Parser {
statements.add(new Conditional(current.getPosition(), condition, truePart, falsePart)); statements.add(new Conditional(current.getPosition(), condition, truePart, falsePart));
break; 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);
final Token next = peek();
if (!(next instanceof KeywordToken) || !((KeywordToken) next).value.equals("while")) {
throw new ParserException(current.getPosition(), "Expected while");
}
++position;
final RValue condition = parseBracket();
statements.add(new While(current.getPosition(), condition, body, true));
break;
}
default: default:
throw new ParserException(current.getPosition(), "Unimplemented keyword '" + keyword + "'"); throw new ParserException(current.getPosition(), "Unimplemented keyword '" + keyword + "'");

View File

@ -30,8 +30,12 @@ public class Conditional extends Node {
@Override @Override
public String toString() { public String toString() {
if (falsePart == null) {
return "if ("+condition+") { "+truePart+" }";
} else {
return "if ("+condition+") { "+truePart+" } else { "+falsePart+" }"; return "if ("+condition+") { "+truePart+" } else { "+falsePart+" }";
} }
}
//TODO: optimizer //TODO: optimizer
} }

View File

@ -0,0 +1,57 @@
package com.sk89q.worldedit.expression.runtime;
public class While extends Node {
RValue condition;
RValue body;
boolean footChecked;
public While(int position, RValue condition, RValue body, boolean footChecked) {
super(position);
this.condition = condition;
this.body = body;
this.footChecked = footChecked;
}
@Override
public double getValue() throws EvaluationException {
int iterations = 0;
double ret = 0.0;
if (footChecked) {
do {
ret = body.getValue();
++iterations;
if (iterations > 256) {
throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations.");
}
} while (condition.getValue() > 0.0);
} else {
while (condition.getValue() > 0.0) {
ret = body.getValue();
++iterations;
if (iterations > 256) {
throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations.");
}
}
}
return ret;
}
@Override
public char id() {
return 't';
}
@Override
public String toString() {
if (footChecked) {
return "do { "+body+" } while ("+condition+")";
} else {
return "while ("+condition+") { "+body+" }";
}
}
//TODO: optimizer
}

View File

@ -84,7 +84,12 @@ public class ExpressionTest {
final Expression expression2 = Expression.compile("if (0) if (1) x=5; y=4;", "x", "y"); final Expression expression2 = Expression.compile("if (0) if (1) x=5; y=4;", "x", "y");
expression2.evaluate(1, 2); expression2.evaluate(1, 2);
assertEquals(4, expression2.getVariable("y").getValue(), 0); assertEquals(4, expression2.getVariable("y").getValue(), 0);
}
@Test
public void testWhile() throws ExpressionException {
assertEquals(5, simpleEval("c=5; a=0; while (c > 0) { ++a; --c; } a"), 0);
assertEquals(5, simpleEval("c=5; a=0; do { ++a; --c; } while (c > 0) a"), 0);
} }
private double simpleEval(String expression) throws ExpressionException { private double simpleEval(String expression) throws ExpressionException {