From aef3b0024ab5dc817caebb7d4c1f207dc1635427 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 21 Sep 2013 18:16:36 +0200 Subject: [PATCH 1/5] Fixed the overloader --- .../com/sk89q/worldedit/expression/runtime/Functions.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java index ad790e164..3d679baab 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java @@ -78,6 +78,10 @@ public final class Functions { return false; } + if (this.method.getParameterTypes().length != args.length) { // TODO: optimize + return false; + } + int accum = 0; for (RValue argument : args) { accum <<= 2; From 4fa5daf974a7cbadd77aaf69a0912e70c8ed1e97 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 21 Sep 2013 18:22:18 +0200 Subject: [PATCH 2/5] Moved megabuf to the Functions class --- .../java/com/sk89q/worldedit/expression/Expression.java | 7 ++++--- .../com/sk89q/worldedit/expression/runtime/Functions.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/expression/Expression.java b/src/main/java/com/sk89q/worldedit/expression/Expression.java index a6b6e23a7..1b34c2032 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.lexer.tokens.Token; 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.Functions; import com.sk89q.worldedit.expression.runtime.RValue; import com.sk89q.worldedit.expression.runtime.ReturnException; import com.sk89q.worldedit.expression.runtime.Variable; @@ -63,7 +64,7 @@ public class Expression { private final Map variables = new HashMap(); private final String[] variableNames; private RValue root; - private final Map megabuf = new HashMap(); + private final Functions functions = new Functions(); public static Expression compile(String expression, String... variableNames) throws ExpressionException { return new Expression(expression, variableNames); @@ -153,7 +154,7 @@ public class Expression { } } - public Map getMegabuf() { - return megabuf; + public Functions getFunctions() { + return functions; } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java index 3d679baab..2817415dc 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java @@ -277,6 +277,7 @@ public final class Functions { private static final Map gmegabuf = new HashMap(); + private final Map megabuf = new HashMap(); private static double[] getSubBuffer(Map megabuf, Integer key) { double[] ret = megabuf.get(key); @@ -306,18 +307,18 @@ public final class Functions { @Dynamic public static double megabuf(RValue index) throws EvaluationException { - return getBufferItem(Expression.getInstance().getMegabuf(), (int) index.getValue()); + return getBufferItem(Expression.getInstance().getFunctions().megabuf, (int) index.getValue()); } @Dynamic public static double megabuf(RValue index, double value) throws EvaluationException { - return setBufferItem(Expression.getInstance().getMegabuf(), (int) index.getValue(), value); + return setBufferItem(Expression.getInstance().getFunctions().megabuf, (int) index.getValue(), value); } @Dynamic public static double closest(RValue x, RValue y, RValue z, RValue index, RValue count, RValue stride) throws EvaluationException { return findClosest( - Expression.getInstance().getMegabuf(), + Expression.getInstance().getFunctions().megabuf, x.getValue(), y.getValue(), z.getValue(), From 57bb5470eb37e37ad936ff972d14cb8e91356025 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 21 Sep 2013 19:00:20 +0200 Subject: [PATCH 3/5] Allowed usage of non-existant variables in all LValue expressions. --- .../worldedit/expression/Identifiable.java | 1 + .../worldedit/expression/parser/Parser.java | 18 +++--- .../expression/parser/UnboundVariable.java | 61 +++++++++++++++++++ .../expression/runtime/Conditional.java | 20 +++++- .../worldedit/expression/runtime/For.java | 13 ++++ .../expression/runtime/Function.java | 16 ++++- .../expression/runtime/Functions.java | 2 +- .../worldedit/expression/runtime/LValue.java | 7 +++ .../expression/runtime/LValueFunction.java | 27 +++++++- .../worldedit/expression/runtime/Node.java | 8 +++ .../worldedit/expression/runtime/RValue.java | 4 ++ .../worldedit/expression/runtime/Return.java | 10 +++ .../expression/runtime/Sequence.java | 12 ++++ .../expression/runtime/SimpleFor.java | 13 ++++ .../worldedit/expression/runtime/Switch.java | 20 +++++- .../expression/runtime/Variable.java | 13 ++++ .../worldedit/expression/runtime/While.java | 11 ++++ 17 files changed, 238 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/expression/parser/UnboundVariable.java diff --git a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java index 168829f15..5ce398a27 100644 --- a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java +++ b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java @@ -38,6 +38,7 @@ public interface Identifiable { * * PseudoTokens: * p - UnaryOperator + * V - UnboundVariable * * Nodes: * c - Constant 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 5c406532b..9ebb71a0d 100644 --- a/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java +++ b/src/main/java/com/sk89q/worldedit/expression/parser/Parser.java @@ -33,6 +33,7 @@ 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.Function; import com.sk89q.worldedit.expression.runtime.Functions; import com.sk89q.worldedit.expression.runtime.LValue; import com.sk89q.worldedit.expression.runtime.RValue; @@ -84,6 +85,9 @@ public class Parser { final Token token = peek(); throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token); } + + ret.bindVariables(expression, false); + return ret; } @@ -267,9 +271,6 @@ public class Parser { default: throw new ParserException(current.getPosition(), "Unexpected keyword '" + keyword + "'"); } - switch (1) { - default: - } break; @@ -331,13 +332,12 @@ public class Parser { if (next.id() == '(') { halfProcessed.add(parseFunctionCall(identifierToken)); } else { - // Ugly hack to make temporary variables work while not sacrificing error reporting. - final boolean isSimpleAssignment = next instanceof OperatorToken && ((OperatorToken) next).operator.equals("="); - RValue variable = expression.getVariable(identifierToken.value, isSimpleAssignment); + final RValue variable = expression.getVariable(identifierToken.value, false); if (variable == null) { - throw new ParserException(current.getPosition(), "Variable '" + identifierToken.value + "' not found"); + halfProcessed.add(new UnboundVariable(identifierToken.getPosition(), identifierToken.value)); + } else { + halfProcessed.add(variable); } - halfProcessed.add(variable); } expressionStart = false; break; @@ -388,7 +388,7 @@ public class Parser { return tokens.get(position); } - private Identifiable parseFunctionCall(IdentifierToken identifierToken) throws ParserException { + private Function parseFunctionCall(IdentifierToken identifierToken) throws ParserException { consumeCharacter('('); try { diff --git a/src/main/java/com/sk89q/worldedit/expression/parser/UnboundVariable.java b/src/main/java/com/sk89q/worldedit/expression/parser/UnboundVariable.java new file mode 100644 index 000000000..13495c3f5 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/parser/UnboundVariable.java @@ -0,0 +1,61 @@ +package com.sk89q.worldedit.expression.parser; + +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.runtime.EvaluationException; +import com.sk89q.worldedit.expression.runtime.LValue; +import com.sk89q.worldedit.expression.runtime.RValue; + +public class UnboundVariable extends PseudoToken implements LValue { + public final String name; + + public UnboundVariable(int position, String name) { + super(position); + this.name = name; + // TODO Auto-generated constructor stub + } + + @Override + public char id() { + // TODO Auto-generated method stub + return 'V'; + } + + @Override + public String toString() { + return "UnboundVariable(" + name + ")"; + } + + @Override + public double getValue() throws EvaluationException { + throw new EvaluationException(getPosition(), "Tried to evaluate unbound variable!"); + } + + @Override + public LValue optimize() throws EvaluationException { + throw new EvaluationException(getPosition(), "Tried to optimize unbound variable!"); + } + + @Override + public double assign(double value) throws EvaluationException { + throw new EvaluationException(getPosition(), "Tried to assign unbound variable!"); + } + + public RValue bind(Expression expression, boolean isLValue) throws ParserException { + final RValue variable = expression.getVariable(name, isLValue); + if (variable == null) { + throw new ParserException(getPosition(), "Variable '" + name + "' not found"); + } + + return variable; + } + + @Override + public LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + final RValue variable = expression.getVariable(name, preferLValue); + if (variable == null) { + throw new ParserException(getPosition(), "Variable '" + name + "' not found"); + } + + return (LValue) variable; + } +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Conditional.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Conditional.java index 3c1e52617..89c13d3de 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Conditional.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Conditional.java @@ -19,15 +19,18 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * An if/else statement or a ternary operator. * * @author TomyLobo */ public class Conditional extends Node { - RValue condition; - RValue truePart; - RValue falsePart; + private RValue condition; + private RValue truePart; + private RValue falsePart; public Conditional(int position, RValue condition, RValue truePart, RValue falsePart) { super(position); @@ -76,4 +79,15 @@ public class Conditional extends Node { return new Conditional(getPosition(), newCondition, truePart.optimize(), falsePart == null ? null : falsePart.optimize()); } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + condition = condition.bindVariables(expression, false); + truePart = truePart.bindVariables(expression, false); + if (falsePart != null) { + falsePart = falsePart.bindVariables(expression, false); + } + + return this; + } } 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 3fa4a0cb6..950e9ebd2 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/For.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/For.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A Java/C-style for loop. * @@ -87,4 +90,14 @@ public class For extends Node { //return new Sequence(getPosition(), init.optimize(), new While(getPosition(), condition, new Sequence(getPosition(), body, increment), false)).optimize(); return new For(getPosition(), init.optimize(), newCondition, increment.optimize(), body.optimize()); } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + init = init.bindVariables(expression, false); + condition = condition.bindVariables(expression, false); + increment = increment.bindVariables(expression, false); + body = body.bindVariables(expression, false); + + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Function.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Function.java index ec123fe68..f4cb56719 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Function.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Function.java @@ -24,6 +24,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * Wrapper for a Java method and its arguments (other Nodes) * @@ -102,10 +105,19 @@ public class Function extends Node { if (optimizable) { return new Constant(position, invokeMethod(method, optimizedArgs)); - } else if (this instanceof LValueFunction) { - return new LValueFunction(position, method, ((LValueFunction) this).setter, optimizedArgs); } else { return new Function(position, method, optimizedArgs); } } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + final Class[] parameters = method.getParameterTypes(); + for (int i = 0; i < args.length; ++i) { + final boolean argumentPrefersLValue = LValue.class.isAssignableFrom(parameters[i]); + args[i] = args[i].bindVariables(expression, argumentPrefersLValue); + } + + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java index 2817415dc..0ca3198ae 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java @@ -117,7 +117,7 @@ public final class Functions { } } - throw new NoSuchMethodException(); + throw new NoSuchMethodException(); // TODO: return null (check for side-effects first) } private static final Map> functions = new HashMap>(); diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/LValue.java b/src/main/java/com/sk89q/worldedit/expression/runtime/LValue.java index d2eb977ca..d909e7fb4 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/LValue.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/LValue.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A value that can be used on the left side of an assignment. * @@ -26,4 +29,8 @@ package com.sk89q.worldedit.expression.runtime; */ public interface LValue extends RValue { public double assign(double value) throws EvaluationException; + + public LValue optimize() throws EvaluationException; + + public LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException; } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/LValueFunction.java b/src/main/java/com/sk89q/worldedit/expression/runtime/LValueFunction.java index 3d110d23f..ce13fea7f 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/LValueFunction.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/LValueFunction.java @@ -21,6 +21,9 @@ package com.sk89q.worldedit.expression.runtime; import java.lang.reflect.Method; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * Wrapper for a pair of Java methods and their arguments (other Nodes), forming an LValue * @@ -28,10 +31,11 @@ import java.lang.reflect.Method; */ public class LValueFunction extends Function implements LValue { private final Object[] setterArgs; - final Method setter; + private final Method setter; LValueFunction(int position, Method getter, Method setter, RValue... args) { super(position, getter, args); + assert (getter.isAnnotationPresent(Dynamic.class)); setterArgs = new Object[args.length + 1]; System.arraycopy(args, 0, setterArgs, 0, args.length); @@ -48,4 +52,25 @@ public class LValueFunction extends Function implements LValue { setterArgs[setterArgs.length - 1] = value; return invokeMethod(setter, setterArgs); } + + @Override + public LValue optimize() throws EvaluationException { + final RValue optimized = super.optimize(); + if (optimized == this) { + return this; + } + + if (optimized instanceof Function) { + return new LValueFunction(optimized.getPosition(), method, setter, ((Function) optimized).args); + } + + return (LValue) optimized; + } + + @Override + public LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + super.bindVariables(expression, preferLValue); + + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Node.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Node.java index 193350535..e0c7d184a 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Node.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Node.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A node in the execution tree of an expression. * @@ -42,4 +45,9 @@ public abstract class Node implements RValue { public final int getPosition() { return position; } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/RValue.java b/src/main/java/com/sk89q/worldedit/expression/runtime/RValue.java index 5fd2fc145..863577d5b 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/RValue.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/RValue.java @@ -19,7 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; import com.sk89q.worldedit.expression.Identifiable; +import com.sk89q.worldedit.expression.parser.ParserException; /** * A value that can be used on the right side of an assignment. @@ -30,4 +32,6 @@ public interface RValue extends Identifiable { public double getValue() throws EvaluationException; public RValue optimize() throws EvaluationException; + + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException; } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Return.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Return.java index 460918451..fb3e42336 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Return.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Return.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A return statement. * @@ -47,4 +50,11 @@ public class Return extends Node { public String toString() { return "return " + value; } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + value = value.bindVariables(expression, false); + + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Sequence.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Sequence.java index 563ba8010..88edf50c2 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Sequence.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Sequence.java @@ -22,6 +22,9 @@ package com.sk89q.worldedit.expression.runtime; import java.util.ArrayList; import java.util.List; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A sequence of operations, usually separated by semicolons in the input stream. * @@ -94,4 +97,13 @@ public class Sequence extends Node { return new Sequence(getPosition(), newSequence.toArray(new RValue[newSequence.size()])); } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + for (int i = 0; i < sequence.length; ++i) { + sequence[i] = sequence[i].bindVariables(expression, false); + } + + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java b/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java index 6c32ec66b..1b04fc919 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A simple-style for loop. * @@ -84,4 +87,14 @@ public class SimpleFor extends Node { return new SimpleFor(getPosition(), (LValue) counter.optimize(), first.optimize(), last.optimize(), body.optimize()); } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + counter = counter.bindVariables(expression, true); + first = first.bindVariables(expression, false); + last = last.bindVariables(expression, false); + body = body.bindVariables(expression, false); + + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java index 0a295e3ca..7520381f3 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java @@ -26,16 +26,19 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A switch/case construct. * * @author TomyLobo */ public class Switch extends Node implements RValue { - private final RValue parameter; + private RValue parameter; private final Map valueMap; private final RValue[] caseStatements; - private final RValue defaultCase; + private RValue defaultCase; public Switch(int position, RValue parameter, List values, List caseStatements, RValue defaultCase) { this(position, parameter, invertList(values), caseStatements, defaultCase); @@ -191,4 +194,17 @@ public class Switch extends Node implements RValue { return new Switch(getPosition(), optimizedParameter, newValueMap, newSequence, defaultCase.optimize()); } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + parameter = parameter.bindVariables(expression, false); + + for (int i = 0; i < caseStatements.length; ++i) { + caseStatements[i] = caseStatements[i].bindVariables(expression, false); + } + + defaultCase = defaultCase.bindVariables(expression, false); + + return this; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Variable.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Variable.java index eb1debea5..22477fdeb 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Variable.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Variable.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A variable. * @@ -51,4 +54,14 @@ public final class Variable extends Node implements LValue { public double assign(double value) { return this.value = value; } + + @Override + public LValue optimize() throws EvaluationException { + return this; + } + + @Override + public LValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + return this; + } } 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 7fb87248d..cee82f30f 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/While.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/While.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.expression.runtime; +import com.sk89q.worldedit.expression.Expression; +import com.sk89q.worldedit.expression.parser.ParserException; + /** * A while loop. * @@ -112,4 +115,12 @@ public class While extends Node { return new While(getPosition(), newCondition, body.optimize(), footChecked); } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + condition = condition.bindVariables(expression, false); + body = body.bindVariables(expression, false); + + return this; + } } From eba1114b39735cd5548d68af1daae51807d78d81 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 21 Sep 2013 19:06:47 +0200 Subject: [PATCH 4/5] IntelliJ demanded attention. --- src/main/java/com/sk89q/worldedit/expression/runtime/For.java | 1 + .../com/sk89q/worldedit/expression/runtime/SimpleFor.java | 1 + .../java/com/sk89q/worldedit/expression/runtime/Switch.java | 4 +--- .../java/com/sk89q/worldedit/expression/runtime/While.java | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) 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 950e9ebd2..4164fd3a3 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/For.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/For.java @@ -57,6 +57,7 @@ public class For extends Node { ret = body.getValue(); } catch (BreakException e) { if (e.doContinue) { + //noinspection UnnecessaryContinue continue; } else { 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 index 1b04fc919..801e9709d 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/SimpleFor.java @@ -61,6 +61,7 @@ public class SimpleFor extends Node { ret = body.getValue(); } catch (BreakException e) { if (e.doContinue) { + //noinspection UnnecessaryContinue continue; } else { break; diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java index 7520381f3..cdf259038 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Switch.java @@ -157,9 +157,7 @@ public class Switch extends Node implements RValue { final RValue invokable = defaultCase.optimize(); if (invokable instanceof Sequence) { - for (RValue subInvokable : ((Sequence) invokable).sequence) { - newSequence.add(subInvokable); - } + Collections.addAll(newSequence, ((Sequence) invokable).sequence); } else { newSequence.add(invokable); } 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 cee82f30f..f68dcb660 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/While.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/While.java @@ -73,6 +73,7 @@ public class While extends Node { ret = body.getValue(); } catch (BreakException e) { if (e.doContinue) { + //noinspection UnnecessaryContinue continue; } else { break; From 8badb09399f290de85b170ffee1db24a30c99b77 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Sat, 21 Sep 2013 14:35:57 +0200 Subject: [PATCH 5/5] Added query{,Abs,Rel}(x,y,z,typevar,datavar) to the expression parser. --- .../java/com/sk89q/worldedit/EditSession.java | 18 +++--- .../WorldEditExpressionEnvironment.java | 59 +++++++++++++++++++ .../worldedit/expression/Expression.java | 14 ++++- .../runtime/ExpressionEnvironment.java | 16 +++++ .../expression/runtime/Functions.java | 56 ++++++++++++++++++ 5 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/WorldEditExpressionEnvironment.java create mode 100644 src/main/java/com/sk89q/worldedit/expression/runtime/ExpressionEnvironment.java diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index 016aa9c1f..dc0138e7f 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2808,10 +2808,15 @@ public class EditSession { final RValue typeVariable = expression.getVariable("type", false); final RValue dataVariable = expression.getVariable("data", false); + final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero); + expression.setEnvironment(environment); + final ArbitraryShape shape = new ArbitraryShape(region) { @Override protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial) { - final Vector scaled = new Vector(x, y, z).subtract(zero).divide(unit); + final Vector current = new Vector(x, y, z); + environment.setCurrentBlock(current); + final Vector scaled = current.subtract(zero).divide(unit); try { if (expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ(), defaultMaterial.getType(), defaultMaterial.getData()) <= 0) { @@ -2837,7 +2842,8 @@ public class EditSession { final RValue y = expression.getVariable("y", false); final RValue z = expression.getVariable("z", false); - Vector zero2 = zero.add(0.5, 0.5, 0.5); + final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero); + expression.setEnvironment(environment); final DoubleArrayList queue = new DoubleArrayList(false); @@ -2848,13 +2854,11 @@ public class EditSession { // transform expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ()); - final Vector sourceScaled = new Vector(x.getValue(), y.getValue(), z.getValue()); - - // unscale, unoffset, round-nearest - final BlockVector sourcePosition = sourceScaled.multiply(unit).add(zero2).toBlockPoint(); + final BlockVector sourcePosition = environment.toWorld(scaled.getX(), scaled.getY(), scaled.getZ()); // read block from world - BaseBlock material = new BaseBlock(world.getBlockType(sourcePosition), world.getBlockData(sourcePosition)); + // TODO: use getBlock here once the reflection is out of the way + final BaseBlock material = new BaseBlock(world.getBlockType(sourcePosition), world.getBlockData(sourcePosition)); // queue operation queue.put(position, material); diff --git a/src/main/java/com/sk89q/worldedit/WorldEditExpressionEnvironment.java b/src/main/java/com/sk89q/worldedit/WorldEditExpressionEnvironment.java new file mode 100644 index 000000000..4fd46aa06 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/WorldEditExpressionEnvironment.java @@ -0,0 +1,59 @@ +package com.sk89q.worldedit; + +import com.sk89q.worldedit.expression.runtime.ExpressionEnvironment; + +class WorldEditExpressionEnvironment implements ExpressionEnvironment { + private final Vector unit; + private final Vector zero2; + private Vector current = new Vector(); + private EditSession editSession; + + public WorldEditExpressionEnvironment(EditSession editSession, Vector unit, Vector zero) { + this.editSession = editSession; + this.unit = unit; + this.zero2 = zero.add(0.5, 0.5, 0.5); + } + + public BlockVector toWorld(double x, double y, double z) { + // unscale, unoffset, round-nearest + return new Vector(x, y, z).multiply(unit).add(zero2).toBlockPoint(); + } + + public Vector toWorldRel(double x, double y, double z) { + return current.add(x, y, z); + } + + @Override + public int getBlockType(double x, double y, double z) { + return editSession.getBlockType(toWorld(x, y, z)); + } + + @Override + public int getBlockData(double x, double y, double z) { + return editSession.getBlockData(toWorld(x, y, z)); + } + + @Override + public int getBlockTypeAbs(double x, double y, double z) { + return editSession.getBlockType(new Vector(x, y, z)); + } + + @Override + public int getBlockDataAbs(double x, double y, double z) { + return editSession.getBlockData(new Vector(x, y, z)); + } + + @Override + public int getBlockTypeRel(double x, double y, double z) { + return editSession.getBlockType(toWorldRel(x, y, z)); + } + + @Override + public int getBlockDataRel(double x, double y, double z) { + return editSession.getBlockData(toWorldRel(x, y, z)); + } + + public void setCurrentBlock(Vector current) { + this.current = current; + } +} diff --git a/src/main/java/com/sk89q/worldedit/expression/Expression.java b/src/main/java/com/sk89q/worldedit/expression/Expression.java index 1b34c2032..f78ab76b3 100644 --- a/src/main/java/com/sk89q/worldedit/expression/Expression.java +++ b/src/main/java/com/sk89q/worldedit/expression/Expression.java @@ -24,9 +24,12 @@ import java.util.List; import java.util.Map; import java.util.Stack; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.expression.lexer.Lexer; import com.sk89q.worldedit.expression.lexer.tokens.Token; import com.sk89q.worldedit.expression.parser.Parser; +import com.sk89q.worldedit.expression.runtime.ExpressionEnvironment; import com.sk89q.worldedit.expression.runtime.Constant; import com.sk89q.worldedit.expression.runtime.EvaluationException; import com.sk89q.worldedit.expression.runtime.Functions; @@ -43,7 +46,7 @@ import com.sk89q.worldedit.expression.runtime.Variable; * Arithmetic: +, -, *, /, % (modulo), ^ (power), - (unary), --, ++ (prefix only) * Comparison: <=, >=, >, <, ==, !=, ~= (near) * - * Supported functions: abs, acos, asin, atan, atan2, cbrt, ceil, cos, cosh, exp, floor, ln, log, log10, max, max, min, min, rint, round, sin, sinh, sqrt, tan, tanh + * Supported functions: abs, acos, asin, atan, atan2, cbrt, ceil, cos, cosh, exp, floor, ln, log, log10, max, max, min, min, rint, round, sin, sinh, sqrt, tan, tanh and more. (See the Functions class or the wiki) * * Constants: e, pi * @@ -65,6 +68,7 @@ public class Expression { private final String[] variableNames; private RValue root; private final Functions functions = new Functions(); + private ExpressionEnvironment environment; public static Expression compile(String expression, String... variableNames) throws ExpressionException { return new Expression(expression, variableNames); @@ -157,4 +161,12 @@ public class Expression { public Functions getFunctions() { return functions; } + + public ExpressionEnvironment getEnvironment() { + return environment; + } + + public void setEnvironment(ExpressionEnvironment environment) { + this.environment = environment; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/ExpressionEnvironment.java b/src/main/java/com/sk89q/worldedit/expression/runtime/ExpressionEnvironment.java new file mode 100644 index 000000000..5ac316834 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/ExpressionEnvironment.java @@ -0,0 +1,16 @@ +package com.sk89q.worldedit.expression.runtime; + +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.Vector; + +/** + * Represents a way to access blocks in a world. Has to accept non-rounded coordinates. + */ +public interface ExpressionEnvironment { + int getBlockType(double x, double y, double z); + int getBlockData(double x, double y, double z); + int getBlockTypeAbs(double x, double y, double z); + int getBlockDataAbs(double x, double y, double z); + int getBlockTypeRel(double x, double y, double z); + int getBlockDataRel(double x, double y, double z); +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java index 0ca3198ae..018d217ea 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java @@ -375,4 +375,60 @@ public final class Functions { public static double randint(RValue max) throws EvaluationException { return random.nextInt((int) Math.floor(max.getValue())); } + + + private static double queryInternal(LValue type, LValue data, double typeId, double dataValue) throws EvaluationException { + // Compare to input values and determine return value + final double ret = typeId == type.getValue() && typeId == type.getValue() ? 1.0 : 0.0; + + type.assign(typeId); + data.assign(dataValue); + + return ret; + } + + @Dynamic + public static double query(RValue x, RValue y, RValue z, LValue type, LValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + final double typeId = environment.getBlockType(xp, yp, zp); + final double dataValue = environment.getBlockData(xp, yp, zp); + + return queryInternal(type, data, typeId, dataValue); + } + + @Dynamic + public static double queryAbs(RValue x, RValue y, RValue z, LValue type, LValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + final double typeId = environment.getBlockTypeAbs(xp, yp, zp); + final double dataValue = environment.getBlockDataAbs(xp, yp, zp); + + return queryInternal(type, data, typeId, dataValue); + } + + @Dynamic + public static double queryRel(RValue x, RValue y, RValue z, LValue type, LValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + final double typeId = environment.getBlockTypeRel(xp, yp, zp); + final double dataValue = environment.getBlockDataRel(xp, yp, zp); + + return queryInternal(type, data, typeId, dataValue); + } }