diff --git a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java index b14c4b46f..1ef0f15b9 100644 --- a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java +++ b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java @@ -50,6 +50,7 @@ public interface Identifiable { * F - For * r - Return * b - Break (includes continue) + * S - SimpleFor * */ public abstract char id(); diff --git a/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java b/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java index af4ac9514..e276a93e1 100644 --- a/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java +++ b/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java @@ -35,9 +35,11 @@ import com.sk89q.worldedit.expression.runtime.Conditional; import com.sk89q.worldedit.expression.runtime.Constant; import com.sk89q.worldedit.expression.runtime.For; import com.sk89q.worldedit.expression.runtime.Functions; +import com.sk89q.worldedit.expression.runtime.LValue; import com.sk89q.worldedit.expression.runtime.RValue; import com.sk89q.worldedit.expression.runtime.Return; import com.sk89q.worldedit.expression.runtime.Sequence; +import com.sk89q.worldedit.expression.runtime.SimpleFor; import com.sk89q.worldedit.expression.runtime.Variable; import com.sk89q.worldedit.expression.runtime.While; @@ -153,15 +155,50 @@ public class Parser { case 'f': { // for ++position; consumeCharacter('('); + int oldPosition = position; final RValue init = parseExpression(true); - consumeCharacter(';'); - final RValue condition = parseExpression(true); - consumeCharacter(';'); - final RValue increment = parseExpression(true); - consumeCharacter(')'); - final RValue body = parseStatements(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)); + 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; } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java b/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java new file mode 100644 index 000000000..463bb4e1b --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java @@ -0,0 +1,58 @@ +package com.sk89q.worldedit.expression.runtime; + +public class SimpleFor extends Node { + LValue counter; + RValue first; + RValue last; + RValue body; + + public SimpleFor(int position, LValue counter, RValue first, RValue last, RValue body) { + super(position); + + this.counter = counter; + this.first = first; + this.last = last; + this.body = body; + } + + @Override + public double getValue() throws EvaluationException { + int iterations = 0; + double ret = 0.0; + + double firstValue = first.getValue(); + double lastValue = last.getValue(); + + for (double i = firstValue; i <= lastValue; ++i) { + if (iterations > 256) { + throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations."); + } + ++iterations; + + try { + counter.assign(i); + ret = body.getValue(); + } catch (BreakException e) { + if (e.doContinue) { + continue; + } else { + break; + } + } + } + + return ret; + } + + @Override + public char id() { + return 'S'; + } + + @Override + public String toString() { + return "for (" + counter + " = " + first + ", " + last + ") { " + body + " }"; + } + + //TODO: optimizer +} diff --git a/src/test/java/com/sk89q/worldedit/expression/ExpressionTest.java b/src/test/java/com/sk89q/worldedit/expression/ExpressionTest.java index 0bec185ec..bb420f0ae 100644 --- a/src/test/java/com/sk89q/worldedit/expression/ExpressionTest.java +++ b/src/test/java/com/sk89q/worldedit/expression/ExpressionTest.java @@ -95,6 +95,7 @@ public class ExpressionTest { @Test public void testFor() throws ExpressionException { assertEquals(5, simpleEval("a=0; for (i=0; i<5; ++i) { ++a; } a"), 0); + assertEquals(12345, simpleEval("y=0; for (i=1,5) { y *= 10; y += i; } y"), 0); } private double simpleEval(String expression) throws ExpressionException {