diff --git a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java index 331256ca5..7a11c6ffd 100644 --- a/src/main/java/com/sk89q/worldedit/expression/Identifiable.java +++ b/src/main/java/com/sk89q/worldedit/expression/Identifiable.java @@ -41,8 +41,9 @@ public interface Identifiable { * * Invokables: * c - Constant - * f - Function * v - Variable + * f - Function + * l - LValueFunction * */ public abstract char id(); diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Constant.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Constant.java index 92d226134..42147f6d3 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Constant.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Constant.java @@ -24,7 +24,7 @@ package com.sk89q.worldedit.expression.runtime; * * @author TomyLobo */ -public final class Constant extends RValue { +public final class Constant extends Node { private final double value; public Constant(int position, double value) { 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 6aeeb08f6..711237324 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Function.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Function.java @@ -29,7 +29,7 @@ import java.lang.reflect.Method; * * @author TomyLobo */ -public class Function extends RValue { +public class Function extends Node { /** * Add this annotation on functions that don't always return the same value for the same inputs. */ @@ -47,8 +47,12 @@ public class Function extends RValue { @Override public final double getValue() throws EvaluationException { + return invokeMethod(method, args); + } + + protected static final double invokeMethod(Method method, Object[] args) throws EvaluationException { try { - return (Double) method.invoke(null, (Object[]) args); + return (Double) method.invoke(null, args); } catch (InvocationTargetException e) { if (e.getTargetException() instanceof EvaluationException) { throw (EvaluationException) e.getTargetException(); @@ -79,7 +83,7 @@ public class Function extends RValue { } @Override - public RValue optimize() throws EvaluationException { + public Node optimize() throws EvaluationException { final RValue[] optimizedArgs = new RValue[args.length]; boolean optimizable = !method.isAnnotationPresent(Dynamic.class); int position = getPosition(); @@ -96,7 +100,9 @@ public class Function extends RValue { } if (optimizable) { - return new Constant(position, getValue()); + 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); } 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 6d63bad80..f61efd53f 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java @@ -25,6 +25,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.sk89q.worldedit.expression.runtime.Function.Dynamic; + /** * Contains all functions that can be used in expressions. * @@ -34,13 +36,24 @@ public final class Functions { private static class Overload { private final Method method; private final int mask; + private final boolean isSetter; public Overload(Method method) throws IllegalArgumentException { this.method = method; + boolean isSetter = false; int accum = 0; Class[] parameters = method.getParameterTypes(); for (Class parameter : parameters) { + if (isSetter) { + throw new IllegalArgumentException("Method takes arguments that can't be cast to RValue."); + } + + if (double.class.equals(parameter)) { + isSetter = true; + continue; + } + if (!RValue.class.isAssignableFrom(parameter)) { throw new IllegalArgumentException("Method takes arguments that can't be cast to RValue."); } @@ -54,9 +67,14 @@ public final class Functions { } } mask = accum; + this.isSetter = isSetter; } - public boolean matches(RValue... args) { + public boolean matches(boolean isSetter, RValue... args) { + if (this.isSetter != isSetter) { + return false; + } + int accum = 0; for (RValue argument : args) { accum <<= 2; @@ -74,14 +92,21 @@ public final class Functions { public static final Function getFunction(int position, String name, RValue... args) throws NoSuchMethodException { - return new Function(position, getMethod(name, args), args); + final Method getter = getMethod(name, false, args); + try { + Method setter = getMethod(name, true, args); + return new LValueFunction(position, getter, setter, args); + } + catch (NoSuchMethodException e) { + return new Function(position, getter, args); + } } - private static Method getMethod(String name, RValue... args) throws NoSuchMethodException { + private static Method getMethod(String name, boolean isSetter, RValue... args) throws NoSuchMethodException { final List overloads = functions.get(name); if (overloads != null) { for (Overload overload : overloads) { - if (overload.matches(args)) { + if (overload.matches(isSetter, args)) { return overload.method; } } @@ -93,125 +118,130 @@ public final class Functions { private static final Map> functions = new HashMap>(); static { for (Method method : Functions.class.getMethods()) { - final String methodName = method.getName(); - try { - Overload overload = new Overload(method); - - List overloads = functions.get(methodName); - if (overloads == null) { - functions.put(methodName, overloads = new ArrayList()); - } - - overloads.add(overload); + addFunction(method); } catch(IllegalArgumentException e) {} } } - public static final double sin(RValue x) throws Exception { + public static void addFunction(Method method) throws IllegalArgumentException { + final String methodName = method.getName(); + + Overload overload = new Overload(method); + + List overloads = functions.get(methodName); + if (overloads == null) { + functions.put(methodName, overloads = new ArrayList()); + } + + overloads.add(overload); + } + + + public static final double sin(RValue x) throws EvaluationException { return Math.sin(x.getValue()); } - public static final double cos(RValue x) throws Exception { + public static final double cos(RValue x) throws EvaluationException { return Math.cos(x.getValue()); } - public static final double tan(RValue x) throws Exception { + public static final double tan(RValue x) throws EvaluationException { return Math.tan(x.getValue()); } - public static final double asin(RValue x) throws Exception { + public static final double asin(RValue x) throws EvaluationException { return Math.asin(x.getValue()); } - public static final double acos(RValue x) throws Exception { + public static final double acos(RValue x) throws EvaluationException { return Math.acos(x.getValue()); } - public static final double atan(RValue x) throws Exception { + public static final double atan(RValue x) throws EvaluationException { return Math.atan(x.getValue()); } - public static final double atan2(RValue y, RValue x) throws Exception { + public static final double atan2(RValue y, RValue x) throws EvaluationException { return Math.atan2(y.getValue(), x.getValue()); } - public static final double sinh(RValue x) throws Exception { + public static final double sinh(RValue x) throws EvaluationException { return Math.sinh(x.getValue()); } - public static final double cosh(RValue x) throws Exception { + public static final double cosh(RValue x) throws EvaluationException { return Math.cosh(x.getValue()); } - public static final double tanh(RValue x) throws Exception { + public static final double tanh(RValue x) throws EvaluationException { return Math.tanh(x.getValue()); } - public static final double sqrt(RValue x) throws Exception { + public static final double sqrt(RValue x) throws EvaluationException { return Math.sqrt(x.getValue()); } - public static final double cbrt(RValue x) throws Exception { + public static final double cbrt(RValue x) throws EvaluationException { return Math.cbrt(x.getValue()); } - public static final double abs(RValue x) throws Exception { + public static final double abs(RValue x) throws EvaluationException { return Math.abs(x.getValue()); } - public static final double min(RValue a, RValue b) throws Exception { + public static final double min(RValue a, RValue b) throws EvaluationException { return Math.min(a.getValue(), b.getValue()); } - public static final double min(RValue a, RValue b, RValue c) throws Exception { + public static final double min(RValue a, RValue b, RValue c) throws EvaluationException { return Math.min(a.getValue(), Math.min(b.getValue(), c.getValue())); } - public static final double max(RValue a, RValue b) throws Exception { + public static final double max(RValue a, RValue b) throws EvaluationException { return Math.max(a.getValue(), b.getValue()); } - public static final double max(RValue a, RValue b, RValue c) throws Exception { + public static final double max(RValue a, RValue b, RValue c) throws EvaluationException { return Math.max(a.getValue(), Math.max(b.getValue(), c.getValue())); } - public static final double ceil(RValue x) throws Exception { + public static final double ceil(RValue x) throws EvaluationException { return Math.ceil(x.getValue()); } - public static final double floor(RValue x) throws Exception { + public static final double floor(RValue x) throws EvaluationException { return Math.floor(x.getValue()); } - public static final double rint(RValue x) throws Exception { + public static final double rint(RValue x) throws EvaluationException { return Math.rint(x.getValue()); } - public static final double round(RValue x) throws Exception { + public static final double round(RValue x) throws EvaluationException { return Math.round(x.getValue()); } - public static final double exp(RValue x) throws Exception { + public static final double exp(RValue x) throws EvaluationException { return Math.exp(x.getValue()); } - public static final double ln(RValue x) throws Exception { + public static final double ln(RValue x) throws EvaluationException { return Math.log(x.getValue()); } - public static final double log(RValue x) throws Exception { + public static final double log(RValue x) throws EvaluationException { return Math.log(x.getValue()); } - public static final double log10(RValue x) throws Exception { + public static final double log10(RValue x) throws EvaluationException { return Math.log10(x.getValue()); } @@ -230,4 +260,17 @@ public final class Functions { return 0; } + + + private static final double[] megabuf = new double[1024]; + + @Dynamic + public static final double megabuf(RValue index) throws EvaluationException { + return megabuf[(int) index.getValue()]; + } + + @Dynamic + public static final double megabuf(RValue index, double value) throws EvaluationException { + return megabuf[(int) index.getValue()] = value; + } } 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 e868bd800..db7eede1c 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/LValue.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/LValue.java @@ -24,10 +24,6 @@ package com.sk89q.worldedit.expression.runtime; * * @author TomyLobo */ -public abstract class LValue extends RValue { - public LValue(int position) { - super(position); - } - - public abstract double assign(double value) throws EvaluationException; +public interface LValue extends RValue { + public double assign(double value) throws EvaluationException; } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/LValueFunction.java b/src/main/java/com/sk89q/worldedit/expression/runtime/LValueFunction.java new file mode 100644 index 000000000..706ef77f4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/LValueFunction.java @@ -0,0 +1,51 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.worldedit.expression.runtime; + +import java.lang.reflect.Method; + +/** + * Wrapper for a Java method and its arguments (other Invokables) + * + * @author TomyLobo + */ +public class LValueFunction extends Function implements LValue { + private final Object[] setterArgs; + final Method setter; + + LValueFunction(int position, Method getter, Method setter, RValue... args) { + super(position, getter, args); + + setterArgs = new Object[args.length + 1]; + System.arraycopy(args, 0, setterArgs, 0, args.length); + this.setter = setter; + } + + @Override + public char id() { + return 'l'; + } + + @Override + public double assign(double value) throws EvaluationException { + setterArgs[setterArgs.length - 1] = value; + return invokeMethod(setter, setterArgs); + } +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Node.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Node.java new file mode 100644 index 000000000..169c73111 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Node.java @@ -0,0 +1,45 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010, 2011 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.worldedit.expression.runtime; + +/** + * A value that can be used on the right side of an assignment. + * + * @author TomyLobo + */ +public abstract class Node implements RValue { + private final int position; + + public Node(int position) { + this.position = position; + } + + @Override + public abstract String toString(); + + public Node optimize() throws EvaluationException { + return this; + } + + @Override + public int getPosition() { + return position; + } +} 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 2b8bab984..1d84d79a1 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/RValue.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/RValue.java @@ -26,25 +26,7 @@ import com.sk89q.worldedit.expression.Identifiable; * * @author TomyLobo */ -public abstract class RValue implements Identifiable { - private final int position; - - public RValue(int position) { - super(); - this.position = position; - } - - public abstract double getValue() throws EvaluationException; - - @Override - public abstract String toString(); - - public RValue optimize() throws EvaluationException { - return this; - } - - @Override - public int getPosition() { - return position; - } +public interface RValue extends Identifiable { + public double getValue() throws EvaluationException; + public Node optimize() throws EvaluationException; } 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 cc700db22..d5a0aab6e 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Sequence.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Sequence.java @@ -27,7 +27,7 @@ import java.util.List; * * @author TomyLobo */ -public class Sequence extends RValue { +public class Sequence extends Node { private final RValue[] sequence; public Sequence(int position, RValue... sequence) { @@ -66,7 +66,7 @@ public class Sequence extends RValue { } @Override - public RValue optimize() throws EvaluationException { + public Node optimize() throws EvaluationException { List newSequence = new ArrayList(); for (RValue invokable : sequence) { 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 f61e3e428..d9afd8163 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Variable.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Variable.java @@ -24,7 +24,7 @@ package com.sk89q.worldedit.expression.runtime; * * @author TomyLobo */ -public final class Variable extends LValue { +public final class Variable extends Node implements LValue { public double value; public Variable(double value) {