geforkt von Mirrors/FastAsyncWorldEdit
Allowed usage of non-existant variables in all LValue expressions.
Dieser Commit ist enthalten in:
Ursprung
4fa5daf974
Commit
57bb5470eb
@ -38,6 +38,7 @@ public interface Identifiable {
|
||||
*
|
||||
* PseudoTokens:
|
||||
* p - UnaryOperator
|
||||
* V - UnboundVariable
|
||||
*
|
||||
* Nodes:
|
||||
* c - Constant
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<String, List<Overload>> functions = new HashMap<String, List<Overload>>();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Double, Integer> valueMap;
|
||||
private final RValue[] caseStatements;
|
||||
private final RValue defaultCase;
|
||||
private RValue defaultCase;
|
||||
|
||||
public Switch(int position, RValue parameter, List<Double> values, List<RValue> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren