diff --git a/src/main/java/com/sk89q/worldedit/expression/Expression.java b/src/main/java/com/sk89q/worldedit/expression/Expression.java index 8c08a45f0..3155067f3 100644 --- a/src/main/java/com/sk89q/worldedit/expression/Expression.java +++ b/src/main/java/com/sk89q/worldedit/expression/Expression.java @@ -29,6 +29,7 @@ import com.sk89q.worldedit.expression.parser.Parser; import com.sk89q.worldedit.expression.runtime.Constant; import com.sk89q.worldedit.expression.runtime.EvaluationException; import com.sk89q.worldedit.expression.runtime.RValue; +import com.sk89q.worldedit.expression.runtime.ReturnException; import com.sk89q.worldedit.expression.runtime.Variable; /** @@ -97,7 +98,12 @@ public class Expression { ((Variable) invokable).value = values[i]; } - return root.getValue(); + try { + return root.getValue(); + } + catch (ReturnException e) { + return e.getValue(); + } } public void optimize() throws EvaluationException { diff --git a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java index ba6c402be..b14c4b46f 100644 --- a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java +++ b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java @@ -48,6 +48,8 @@ public interface Identifiable { * I - Conditional * w - While * F - For + * r - Return + * b - Break (includes continue) * */ public abstract char id(); diff --git a/src/main/java/com/sk89q/worldedit/expression/lexer/Lexer.java b/src/main/java/com/sk89q/worldedit/expression/lexer/Lexer.java index 7d2be6d7a..769a8a5f3 100644 --- a/src/main/java/com/sk89q/worldedit/expression/lexer/Lexer.java +++ b/src/main/java/com/sk89q/worldedit/expression/lexer/Lexer.java @@ -107,7 +107,7 @@ public class Lexer { characterTokens.add(';'); } - private static final Set keywords = new HashSet(Arrays.asList("if", "else", "while", "do", "for")); + private static final Set keywords = new HashSet(Arrays.asList("if", "else", "while", "do", "for", "break", "continue", "return")); 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_]*)"); 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 07ae7ee68..eb727b3db 100644 --- a/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java +++ b/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java @@ -30,11 +30,13 @@ 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.Break; 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.RValue; +import com.sk89q.worldedit.expression.runtime.Return; import com.sk89q.worldedit.expression.runtime.Sequence; import com.sk89q.worldedit.expression.runtime.Variable; import com.sk89q.worldedit.expression.runtime.While; @@ -163,6 +165,28 @@ public class Parser { 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 + "'"); } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Break.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Break.java new file mode 100644 index 000000000..1c9d069b4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Break.java @@ -0,0 +1,28 @@ +package com.sk89q.worldedit.expression.runtime; + +public class Break extends Node { + boolean doContinue; + + public Break(int position, boolean doContinue) { + super(position); + + this.doContinue = doContinue; + } + + @Override + public double getValue() throws EvaluationException { + throw new BreakException(doContinue); + } + + @Override + public char id() { + return 'b'; + } + + @Override + public String toString() { + return doContinue ? "continue" : "break"; + } + + //TODO: optimizer +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/BreakException.java b/src/main/java/com/sk89q/worldedit/expression/runtime/BreakException.java new file mode 100644 index 000000000..3f83e4e58 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/BreakException.java @@ -0,0 +1,13 @@ +package com.sk89q.worldedit.expression.runtime; + +public class BreakException extends EvaluationException { + private static final long serialVersionUID = 1L; + + final boolean doContinue; + + public BreakException(boolean doContinue) { + super(-1, doContinue ? "'continue' encountered outside a loop" : "'break' encountered outside a loop"); + + this.doContinue = doContinue; + } +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/For.java b/src/main/java/com/sk89q/worldedit/expression/runtime/For.java index 6178d52ab..d97e18248 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/For.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/For.java @@ -21,11 +21,21 @@ public class For extends Node { double ret = 0.0; for (init.getValue(); condition.getValue() > 0; increment.getValue()) { - ret = body.getValue(); - ++iterations; if (iterations > 256) { throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations."); } + ++iterations; + + try { + ret = body.getValue(); + } + catch (BreakException e) { + if (e.doContinue) { + continue; + } else { + break; + } + } } return ret; diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Return.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Return.java new file mode 100644 index 000000000..64d3d6ff4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Return.java @@ -0,0 +1,28 @@ +package com.sk89q.worldedit.expression.runtime; + +public class Return extends Node { + RValue value; + + public Return(int position, RValue value) { + super(position); + + this.value = value; + } + + @Override + public double getValue() throws EvaluationException { + throw new ReturnException(value.getValue()); + } + + @Override + public char id() { + return 'r'; + } + + @Override + public String toString() { + return "return "+value; + } + + //TODO: optimizer +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/ReturnException.java b/src/main/java/com/sk89q/worldedit/expression/runtime/ReturnException.java new file mode 100644 index 000000000..208f1c87e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/ReturnException.java @@ -0,0 +1,17 @@ +package com.sk89q.worldedit.expression.runtime; + +public class ReturnException extends EvaluationException { + private static final long serialVersionUID = 1L; + + final double value; + + public ReturnException(double value) { + super(-1); + + this.value = value; + } + + public double getValue() { + return value; + } +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/While.java b/src/main/java/com/sk89q/worldedit/expression/runtime/While.java index 42df4a219..4fb93cc22 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/While.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/While.java @@ -20,19 +20,39 @@ public class While extends Node { if (footChecked) { do { - ret = body.getValue(); - ++iterations; if (iterations > 256) { throw new EvaluationException(getPosition(), "Loop exceeded 256 iterations."); } + ++iterations; + + try { + ret = body.getValue(); + } + catch (BreakException e) { + if (e.doContinue) { + continue; + } else { + break; + } + } } 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."); } + ++iterations; + + try { + ret = body.getValue(); + } + catch (BreakException e) { + if (e.doContinue) { + continue; + } else { + break; + } + } } }