mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 09:47:38 +00:00
Merge pull request #264 from sk89q/expression
Expression parser changes
This commit is contained in:
commit
0f764187d0
@ -2808,10 +2808,15 @@ public class EditSession {
|
|||||||
final RValue typeVariable = expression.getVariable("type", false);
|
final RValue typeVariable = expression.getVariable("type", false);
|
||||||
final RValue dataVariable = expression.getVariable("data", 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) {
|
final ArbitraryShape shape = new ArbitraryShape(region) {
|
||||||
@Override
|
@Override
|
||||||
protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial) {
|
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 {
|
try {
|
||||||
if (expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ(), defaultMaterial.getType(), defaultMaterial.getData()) <= 0) {
|
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 y = expression.getVariable("y", false);
|
||||||
final RValue z = expression.getVariable("z", 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<BlockVector, BaseBlock> queue = new DoubleArrayList<BlockVector, BaseBlock>(false);
|
final DoubleArrayList<BlockVector, BaseBlock> queue = new DoubleArrayList<BlockVector, BaseBlock>(false);
|
||||||
|
|
||||||
@ -2848,13 +2854,11 @@ public class EditSession {
|
|||||||
// transform
|
// transform
|
||||||
expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ());
|
expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ());
|
||||||
|
|
||||||
final Vector sourceScaled = new Vector(x.getValue(), y.getValue(), z.getValue());
|
final BlockVector sourcePosition = environment.toWorld(scaled.getX(), scaled.getY(), scaled.getZ());
|
||||||
|
|
||||||
// unscale, unoffset, round-nearest
|
|
||||||
final BlockVector sourcePosition = sourceScaled.multiply(unit).add(zero2).toBlockPoint();
|
|
||||||
|
|
||||||
// read block from world
|
// 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 operation
|
||||||
queue.put(position, material);
|
queue.put(position, material);
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -24,11 +24,15 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
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.Lexer;
|
||||||
import com.sk89q.worldedit.expression.lexer.tokens.Token;
|
import com.sk89q.worldedit.expression.lexer.tokens.Token;
|
||||||
import com.sk89q.worldedit.expression.parser.Parser;
|
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.Constant;
|
||||||
import com.sk89q.worldedit.expression.runtime.EvaluationException;
|
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.RValue;
|
||||||
import com.sk89q.worldedit.expression.runtime.ReturnException;
|
import com.sk89q.worldedit.expression.runtime.ReturnException;
|
||||||
import com.sk89q.worldedit.expression.runtime.Variable;
|
import com.sk89q.worldedit.expression.runtime.Variable;
|
||||||
@ -42,7 +46,7 @@ import com.sk89q.worldedit.expression.runtime.Variable;
|
|||||||
* Arithmetic: +, -, *, /, % (modulo), ^ (power), - (unary), --, ++ (prefix only)
|
* Arithmetic: +, -, *, /, % (modulo), ^ (power), - (unary), --, ++ (prefix only)
|
||||||
* Comparison: <=, >=, >, <, ==, !=, ~= (near)
|
* 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
|
* Constants: e, pi
|
||||||
*
|
*
|
||||||
@ -63,7 +67,8 @@ public class Expression {
|
|||||||
private final Map<String, RValue> variables = new HashMap<String, RValue>();
|
private final Map<String, RValue> variables = new HashMap<String, RValue>();
|
||||||
private final String[] variableNames;
|
private final String[] variableNames;
|
||||||
private RValue root;
|
private RValue root;
|
||||||
private final Map<Integer, double[]> megabuf = new HashMap<Integer, double[]>();
|
private final Functions functions = new Functions();
|
||||||
|
private ExpressionEnvironment environment;
|
||||||
|
|
||||||
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
|
public static Expression compile(String expression, String... variableNames) throws ExpressionException {
|
||||||
return new Expression(expression, variableNames);
|
return new Expression(expression, variableNames);
|
||||||
@ -153,7 +158,15 @@ public class Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Integer, double[]> getMegabuf() {
|
public Functions getFunctions() {
|
||||||
return megabuf;
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionEnvironment getEnvironment() {
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnvironment(ExpressionEnvironment environment) {
|
||||||
|
this.environment = environment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ public interface Identifiable {
|
|||||||
*
|
*
|
||||||
* PseudoTokens:
|
* PseudoTokens:
|
||||||
* p - UnaryOperator
|
* p - UnaryOperator
|
||||||
|
* V - UnboundVariable
|
||||||
*
|
*
|
||||||
* Nodes:
|
* Nodes:
|
||||||
* c - Constant
|
* 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.Conditional;
|
||||||
import com.sk89q.worldedit.expression.runtime.Constant;
|
import com.sk89q.worldedit.expression.runtime.Constant;
|
||||||
import com.sk89q.worldedit.expression.runtime.For;
|
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.Functions;
|
||||||
import com.sk89q.worldedit.expression.runtime.LValue;
|
import com.sk89q.worldedit.expression.runtime.LValue;
|
||||||
import com.sk89q.worldedit.expression.runtime.RValue;
|
import com.sk89q.worldedit.expression.runtime.RValue;
|
||||||
@ -84,6 +85,9 @@ public class Parser {
|
|||||||
final Token token = peek();
|
final Token token = peek();
|
||||||
throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token);
|
throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret.bindVariables(expression, false);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,9 +271,6 @@ public class Parser {
|
|||||||
default:
|
default:
|
||||||
throw new ParserException(current.getPosition(), "Unexpected keyword '" + keyword + "'");
|
throw new ParserException(current.getPosition(), "Unexpected keyword '" + keyword + "'");
|
||||||
}
|
}
|
||||||
switch (1) {
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -331,14 +332,13 @@ public class Parser {
|
|||||||
if (next.id() == '(') {
|
if (next.id() == '(') {
|
||||||
halfProcessed.add(parseFunctionCall(identifierToken));
|
halfProcessed.add(parseFunctionCall(identifierToken));
|
||||||
} else {
|
} else {
|
||||||
// Ugly hack to make temporary variables work while not sacrificing error reporting.
|
final RValue variable = expression.getVariable(identifierToken.value, false);
|
||||||
final boolean isSimpleAssignment = next instanceof OperatorToken && ((OperatorToken) next).operator.equals("=");
|
|
||||||
RValue variable = expression.getVariable(identifierToken.value, isSimpleAssignment);
|
|
||||||
if (variable == null) {
|
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;
|
expressionStart = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ public class Parser {
|
|||||||
return tokens.get(position);
|
return tokens.get(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Identifiable parseFunctionCall(IdentifierToken identifierToken) throws ParserException {
|
private Function parseFunctionCall(IdentifierToken identifierToken) throws ParserException {
|
||||||
consumeCharacter('(');
|
consumeCharacter('(');
|
||||||
|
|
||||||
try {
|
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;
|
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.
|
* An if/else statement or a ternary operator.
|
||||||
*
|
*
|
||||||
* @author TomyLobo
|
* @author TomyLobo
|
||||||
*/
|
*/
|
||||||
public class Conditional extends Node {
|
public class Conditional extends Node {
|
||||||
RValue condition;
|
private RValue condition;
|
||||||
RValue truePart;
|
private RValue truePart;
|
||||||
RValue falsePart;
|
private RValue falsePart;
|
||||||
|
|
||||||
public Conditional(int position, RValue condition, RValue truePart, RValue falsePart) {
|
public Conditional(int position, RValue condition, RValue truePart, RValue falsePart) {
|
||||||
super(position);
|
super(position);
|
||||||
@ -76,4 +79,15 @@ public class Conditional extends Node {
|
|||||||
|
|
||||||
return new Conditional(getPosition(), newCondition, truePart.optimize(), falsePart == null ? null : falsePart.optimize());
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.expression.runtime;
|
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.
|
* A Java/C-style for loop.
|
||||||
*
|
*
|
||||||
@ -54,6 +57,7 @@ public class For extends Node {
|
|||||||
ret = body.getValue();
|
ret = body.getValue();
|
||||||
} catch (BreakException e) {
|
} catch (BreakException e) {
|
||||||
if (e.doContinue) {
|
if (e.doContinue) {
|
||||||
|
//noinspection UnnecessaryContinue
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -87,4 +91,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 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());
|
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.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
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)
|
* Wrapper for a Java method and its arguments (other Nodes)
|
||||||
*
|
*
|
||||||
@ -102,10 +105,19 @@ public class Function extends Node {
|
|||||||
|
|
||||||
if (optimizable) {
|
if (optimizable) {
|
||||||
return new Constant(position, invokeMethod(method, optimizedArgs));
|
return new Constant(position, invokeMethod(method, optimizedArgs));
|
||||||
} else if (this instanceof LValueFunction) {
|
|
||||||
return new LValueFunction(position, method, ((LValueFunction) this).setter, optimizedArgs);
|
|
||||||
} else {
|
} else {
|
||||||
return new Function(position, method, optimizedArgs);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,10 @@ public final class Functions {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.method.getParameterTypes().length != args.length) { // TODO: optimize
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int accum = 0;
|
int accum = 0;
|
||||||
for (RValue argument : args) {
|
for (RValue argument : args) {
|
||||||
accum <<= 2;
|
accum <<= 2;
|
||||||
@ -113,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>>();
|
private static final Map<String, List<Overload>> functions = new HashMap<String, List<Overload>>();
|
||||||
@ -273,6 +277,7 @@ public final class Functions {
|
|||||||
|
|
||||||
|
|
||||||
private static final Map<Integer, double[]> gmegabuf = new HashMap<Integer, double[]>();
|
private static final Map<Integer, double[]> gmegabuf = new HashMap<Integer, double[]>();
|
||||||
|
private final Map<Integer, double[]> megabuf = new HashMap<Integer, double[]>();
|
||||||
|
|
||||||
private static double[] getSubBuffer(Map<Integer, double[]> megabuf, Integer key) {
|
private static double[] getSubBuffer(Map<Integer, double[]> megabuf, Integer key) {
|
||||||
double[] ret = megabuf.get(key);
|
double[] ret = megabuf.get(key);
|
||||||
@ -302,18 +307,18 @@ public final class Functions {
|
|||||||
|
|
||||||
@Dynamic
|
@Dynamic
|
||||||
public static double megabuf(RValue index) throws EvaluationException {
|
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
|
@Dynamic
|
||||||
public static double megabuf(RValue index, double value) throws EvaluationException {
|
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
|
@Dynamic
|
||||||
public static double closest(RValue x, RValue y, RValue z, RValue index, RValue count, RValue stride) throws EvaluationException {
|
public static double closest(RValue x, RValue y, RValue z, RValue index, RValue count, RValue stride) throws EvaluationException {
|
||||||
return findClosest(
|
return findClosest(
|
||||||
Expression.getInstance().getMegabuf(),
|
Expression.getInstance().getFunctions().megabuf,
|
||||||
x.getValue(),
|
x.getValue(),
|
||||||
y.getValue(),
|
y.getValue(),
|
||||||
z.getValue(),
|
z.getValue(),
|
||||||
@ -370,4 +375,60 @@ public final class Functions {
|
|||||||
public static double randint(RValue max) throws EvaluationException {
|
public static double randint(RValue max) throws EvaluationException {
|
||||||
return random.nextInt((int) Math.floor(max.getValue()));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.expression.runtime;
|
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.
|
* 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 interface LValue extends RValue {
|
||||||
public double assign(double value) throws EvaluationException;
|
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 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
|
* 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 {
|
public class LValueFunction extends Function implements LValue {
|
||||||
private final Object[] setterArgs;
|
private final Object[] setterArgs;
|
||||||
final Method setter;
|
private final Method setter;
|
||||||
|
|
||||||
LValueFunction(int position, Method getter, Method setter, RValue... args) {
|
LValueFunction(int position, Method getter, Method setter, RValue... args) {
|
||||||
super(position, getter, args);
|
super(position, getter, args);
|
||||||
|
assert (getter.isAnnotationPresent(Dynamic.class));
|
||||||
|
|
||||||
setterArgs = new Object[args.length + 1];
|
setterArgs = new Object[args.length + 1];
|
||||||
System.arraycopy(args, 0, setterArgs, 0, args.length);
|
System.arraycopy(args, 0, setterArgs, 0, args.length);
|
||||||
@ -48,4 +52,25 @@ public class LValueFunction extends Function implements LValue {
|
|||||||
setterArgs[setterArgs.length - 1] = value;
|
setterArgs[setterArgs.length - 1] = value;
|
||||||
return invokeMethod(setter, setterArgs);
|
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;
|
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.
|
* A node in the execution tree of an expression.
|
||||||
*
|
*
|
||||||
@ -42,4 +45,9 @@ public abstract class Node implements RValue {
|
|||||||
public final int getPosition() {
|
public final int getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.expression.runtime;
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.expression.Expression;
|
||||||
import com.sk89q.worldedit.expression.Identifiable;
|
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.
|
* 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 double getValue() throws EvaluationException;
|
||||||
|
|
||||||
public RValue optimize() 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;
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.expression.Expression;
|
||||||
|
import com.sk89q.worldedit.expression.parser.ParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A return statement.
|
* A return statement.
|
||||||
*
|
*
|
||||||
@ -47,4 +50,11 @@ public class Return extends Node {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "return " + value;
|
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.ArrayList;
|
||||||
import java.util.List;
|
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.
|
* 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()]));
|
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;
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.expression.Expression;
|
||||||
|
import com.sk89q.worldedit.expression.parser.ParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple-style for loop.
|
* A simple-style for loop.
|
||||||
*
|
*
|
||||||
@ -58,6 +61,7 @@ public class SimpleFor extends Node {
|
|||||||
ret = body.getValue();
|
ret = body.getValue();
|
||||||
} catch (BreakException e) {
|
} catch (BreakException e) {
|
||||||
if (e.doContinue) {
|
if (e.doContinue) {
|
||||||
|
//noinspection UnnecessaryContinue
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -84,4 +88,14 @@ public class SimpleFor extends Node {
|
|||||||
|
|
||||||
return new SimpleFor(getPosition(), (LValue) counter.optimize(), first.optimize(), last.optimize(), body.optimize());
|
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;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.expression.Expression;
|
||||||
|
import com.sk89q.worldedit.expression.parser.ParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A switch/case construct.
|
* A switch/case construct.
|
||||||
*
|
*
|
||||||
* @author TomyLobo
|
* @author TomyLobo
|
||||||
*/
|
*/
|
||||||
public class Switch extends Node implements RValue {
|
public class Switch extends Node implements RValue {
|
||||||
private final RValue parameter;
|
private RValue parameter;
|
||||||
private final Map<Double, Integer> valueMap;
|
private final Map<Double, Integer> valueMap;
|
||||||
private final RValue[] caseStatements;
|
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) {
|
public Switch(int position, RValue parameter, List<Double> values, List<RValue> caseStatements, RValue defaultCase) {
|
||||||
this(position, parameter, invertList(values), caseStatements, defaultCase);
|
this(position, parameter, invertList(values), caseStatements, defaultCase);
|
||||||
@ -154,9 +157,7 @@ public class Switch extends Node implements RValue {
|
|||||||
final RValue invokable = defaultCase.optimize();
|
final RValue invokable = defaultCase.optimize();
|
||||||
|
|
||||||
if (invokable instanceof Sequence) {
|
if (invokable instanceof Sequence) {
|
||||||
for (RValue subInvokable : ((Sequence) invokable).sequence) {
|
Collections.addAll(newSequence, ((Sequence) invokable).sequence);
|
||||||
newSequence.add(subInvokable);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
newSequence.add(invokable);
|
newSequence.add(invokable);
|
||||||
}
|
}
|
||||||
@ -191,4 +192,17 @@ public class Switch extends Node implements RValue {
|
|||||||
|
|
||||||
return new Switch(getPosition(), optimizedParameter, newValueMap, newSequence, defaultCase.optimize());
|
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;
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.expression.Expression;
|
||||||
|
import com.sk89q.worldedit.expression.parser.ParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A variable.
|
* A variable.
|
||||||
*
|
*
|
||||||
@ -51,4 +54,14 @@ public final class Variable extends Node implements LValue {
|
|||||||
public double assign(double value) {
|
public double assign(double value) {
|
||||||
return this.value = 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;
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.expression.Expression;
|
||||||
|
import com.sk89q.worldedit.expression.parser.ParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A while loop.
|
* A while loop.
|
||||||
*
|
*
|
||||||
@ -70,6 +73,7 @@ public class While extends Node {
|
|||||||
ret = body.getValue();
|
ret = body.getValue();
|
||||||
} catch (BreakException e) {
|
} catch (BreakException e) {
|
||||||
if (e.doContinue) {
|
if (e.doContinue) {
|
||||||
|
//noinspection UnnecessaryContinue
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -112,4 +116,12 @@ public class While extends Node {
|
|||||||
|
|
||||||
return new While(getPosition(), newCondition, body.optimize(), footChecked);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user