mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 09:47:38 +00:00
The expression parser can now parse more than a simple expression
- Added sequencing (; and {}). - Added =, +=, -=, *=, /=, %=, ^= to the expression parser. (left-associative for now, will change later) - Added pre-increment(++) and pre-decrement(--) operators. - Adjusted/added tests.
This commit is contained in:
parent
9c070c323f
commit
ee79abff67
@ -78,4 +78,8 @@ public class Expression {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return root.toString();
|
return root.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Invokable getVariable(String name) {
|
||||||
|
return variables.get(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
package com.sk89q.worldedit.expression.lexer;
|
package com.sk89q.worldedit.expression.lexer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -43,15 +44,28 @@ public class Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final DecisionTree operatorTree = new DecisionTree(null,
|
private final DecisionTree operatorTree = new DecisionTree(null,
|
||||||
'-', new DecisionTree("-"),
|
'+', new DecisionTree("+",
|
||||||
'+', new DecisionTree("+"),
|
'=', new DecisionTree("+="),
|
||||||
'*', new DecisionTree("*",
|
'+', new DecisionTree("++")
|
||||||
'*', new DecisionTree("^")
|
|
||||||
),
|
),
|
||||||
'/', new DecisionTree("/"),
|
'-', new DecisionTree("-",
|
||||||
'%', new DecisionTree("%"),
|
'=', new DecisionTree("-="),
|
||||||
'^', new DecisionTree("^"),
|
'-', new DecisionTree("--")
|
||||||
'=', new DecisionTree(null, // not implemented
|
),
|
||||||
|
'*', new DecisionTree("*",
|
||||||
|
'=', new DecisionTree("*="),
|
||||||
|
'*', new DecisionTree("**")
|
||||||
|
),
|
||||||
|
'/', new DecisionTree("/",
|
||||||
|
'=', new DecisionTree("/=")
|
||||||
|
),
|
||||||
|
'%', new DecisionTree("%",
|
||||||
|
'=', new DecisionTree("%=")
|
||||||
|
),
|
||||||
|
'^', new DecisionTree("^",
|
||||||
|
'=', new DecisionTree("^=")
|
||||||
|
),
|
||||||
|
'=', new DecisionTree("=",
|
||||||
'=', new DecisionTree("==")
|
'=', new DecisionTree("==")
|
||||||
),
|
),
|
||||||
'!', new DecisionTree("!",
|
'!', new DecisionTree("!",
|
||||||
@ -81,8 +95,13 @@ public class Lexer {
|
|||||||
characterTokens.add(',');
|
characterTokens.add(',');
|
||||||
characterTokens.add('(');
|
characterTokens.add('(');
|
||||||
characterTokens.add(')');
|
characterTokens.add(')');
|
||||||
|
characterTokens.add('{');
|
||||||
|
characterTokens.add('}');
|
||||||
|
characterTokens.add(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Set<String> keywords = new HashSet<String>(Arrays.asList("if", "else"));
|
||||||
|
|
||||||
private static final Pattern numberPattern = Pattern.compile("^([0-9]*(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)");
|
private static final Pattern numberPattern = Pattern.compile("^([0-9]*(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)");
|
||||||
private static final Pattern identifierPattern = Pattern.compile("^([A-Za-z][0-9A-Za-z_]*)");
|
private static final Pattern identifierPattern = Pattern.compile("^([A-Za-z][0-9A-Za-z_]*)");
|
||||||
|
|
||||||
@ -128,7 +147,12 @@ public class Lexer {
|
|||||||
if (identifierMatcher.lookingAt()) {
|
if (identifierMatcher.lookingAt()) {
|
||||||
String identifierPart = identifierMatcher.group(1);
|
String identifierPart = identifierMatcher.group(1);
|
||||||
if (!identifierPart.isEmpty()) {
|
if (!identifierPart.isEmpty()) {
|
||||||
tokens.add(new IdentifierToken(position, identifierPart));
|
if (keywords.contains(identifierPart)) {
|
||||||
|
tokens.add(new KeywordToken(position, identifierPart));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tokens.add(new IdentifierToken(position, identifierPart));
|
||||||
|
}
|
||||||
|
|
||||||
position += identifierPart.length();
|
position += identifierPart.length();
|
||||||
continue;
|
continue;
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
// $Id$
|
||||||
|
/*
|
||||||
|
* WorldEdit
|
||||||
|
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.expression.lexer.tokens;
|
||||||
|
|
||||||
|
public class KeywordToken extends Token {
|
||||||
|
public final String value;
|
||||||
|
|
||||||
|
public KeywordToken(int position, String value) {
|
||||||
|
super(position);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char id() {
|
||||||
|
return 'i';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "KeywordToken(" + value + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.sk89q.worldedit.expression.Identifiable;
|
import com.sk89q.worldedit.expression.Identifiable;
|
||||||
|
import com.sk89q.worldedit.expression.lexer.tokens.CharacterToken;
|
||||||
import com.sk89q.worldedit.expression.lexer.tokens.IdentifierToken;
|
import com.sk89q.worldedit.expression.lexer.tokens.IdentifierToken;
|
||||||
import com.sk89q.worldedit.expression.lexer.tokens.NumberToken;
|
import com.sk89q.worldedit.expression.lexer.tokens.NumberToken;
|
||||||
import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken;
|
import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken;
|
||||||
@ -36,6 +37,7 @@ import com.sk89q.worldedit.expression.runtime.Constant;
|
|||||||
import com.sk89q.worldedit.expression.runtime.Functions;
|
import com.sk89q.worldedit.expression.runtime.Functions;
|
||||||
import com.sk89q.worldedit.expression.runtime.Invokable;
|
import com.sk89q.worldedit.expression.runtime.Invokable;
|
||||||
import com.sk89q.worldedit.expression.runtime.Operators;
|
import com.sk89q.worldedit.expression.runtime.Operators;
|
||||||
|
import com.sk89q.worldedit.expression.runtime.Sequence;
|
||||||
|
|
||||||
public class Parser {
|
public class Parser {
|
||||||
private final class NullToken extends Token {
|
private final class NullToken extends Token {
|
||||||
@ -66,7 +68,7 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Invokable parse() throws ParserException {
|
private Invokable parse() throws ParserException {
|
||||||
final Invokable ret = parseInternal();
|
final Invokable ret = parseInternal(true);
|
||||||
if (position < tokens.size()) {
|
if (position < tokens.size()) {
|
||||||
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);
|
||||||
@ -74,7 +76,7 @@ public class Parser {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Invokable parseInternal() throws ParserException {
|
private final Invokable parseInternal(boolean isStatement) throws ParserException {
|
||||||
LinkedList<Identifiable> halfProcessed = new LinkedList<Identifiable>();
|
LinkedList<Identifiable> halfProcessed = new LinkedList<Identifiable>();
|
||||||
|
|
||||||
// process brackets, numbers, functions, variables and detect prefix operators
|
// process brackets, numbers, functions, variables and detect prefix operators
|
||||||
@ -112,8 +114,15 @@ public class Parser {
|
|||||||
expressionStart = false;
|
expressionStart = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
halfProcessed.add(parseBlock());
|
||||||
|
halfProcessed.add(new CharacterToken(-1, ';'));
|
||||||
|
expressionStart = false;
|
||||||
|
break;
|
||||||
|
|
||||||
case ',':
|
case ',':
|
||||||
case ')':
|
case ')':
|
||||||
|
case '}':
|
||||||
break loop;
|
break loop;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
@ -135,8 +144,57 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// process binary operators
|
if (isStatement) {
|
||||||
return processBinaryOps(halfProcessed, binaryOpMaps.length - 1);
|
return processStatement(halfProcessed);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// process binary operators
|
||||||
|
return processExpression(halfProcessed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Invokable processStatement(LinkedList<Identifiable> input) throws ParserException {
|
||||||
|
LinkedList<Identifiable> lhs = new LinkedList<Identifiable>();
|
||||||
|
LinkedList<Identifiable> rhs = new LinkedList<Identifiable>();
|
||||||
|
boolean semicolonFound = false;
|
||||||
|
|
||||||
|
for (Iterator<Identifiable> it = input.descendingIterator(); it.hasNext();) {
|
||||||
|
Identifiable identifiable = it.next();
|
||||||
|
if (semicolonFound) {
|
||||||
|
lhs.addFirst(identifiable);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (identifiable.id() == ';') {
|
||||||
|
semicolonFound = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rhs.addFirst(identifiable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs.isEmpty()) {
|
||||||
|
if (rhs.isEmpty()) {
|
||||||
|
return new Sequence(semicolonFound ? input.get(0).getPosition() : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return processExpression(rhs);
|
||||||
|
}
|
||||||
|
else if (rhs.isEmpty()) {
|
||||||
|
return processStatement(lhs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(semicolonFound);
|
||||||
|
|
||||||
|
Invokable rhsInvokable = processExpression(rhs);
|
||||||
|
Invokable lhsInvokable = processStatement(lhs);
|
||||||
|
|
||||||
|
return new Sequence(position, lhsInvokable, rhsInvokable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Invokable processExpression(LinkedList<Identifiable> input) throws ParserException {
|
||||||
|
return processBinaryOps(input, binaryOpMaps.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<String, String>[] binaryOpMaps;
|
private static final Map<String, String>[] binaryOpMaps;
|
||||||
@ -146,6 +204,7 @@ public class Parser {
|
|||||||
final Object[][][] binaryOps = {
|
final Object[][][] binaryOps = {
|
||||||
{
|
{
|
||||||
{ "^", "pow" },
|
{ "^", "pow" },
|
||||||
|
{ "**", "pow" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
{ "*", "mul" },
|
{ "*", "mul" },
|
||||||
@ -177,6 +236,15 @@ public class Parser {
|
|||||||
{
|
{
|
||||||
{ "||", "or" },
|
{ "||", "or" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
{ "=", "ass" },
|
||||||
|
{ "+=", "aadd" },
|
||||||
|
{ "-=", "asub" },
|
||||||
|
{ "*=", "amul" },
|
||||||
|
{ "/=", "adiv" },
|
||||||
|
{ "%=", "amod" },
|
||||||
|
{ "^=", "aexp" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -205,6 +273,8 @@ public class Parser {
|
|||||||
unaryOpMap.put("-", "neg");
|
unaryOpMap.put("-", "neg");
|
||||||
unaryOpMap.put("!", "not");
|
unaryOpMap.put("!", "not");
|
||||||
unaryOpMap.put("~", "inv");
|
unaryOpMap.put("~", "inv");
|
||||||
|
unaryOpMap.put("++", "inc");
|
||||||
|
unaryOpMap.put("--", "dec");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Invokable processBinaryOps(LinkedList<Identifiable> input, int level) throws ParserException {
|
private Invokable processBinaryOps(LinkedList<Identifiable> input, int level) throws ParserException {
|
||||||
@ -315,7 +385,7 @@ public class Parser {
|
|||||||
List<Invokable> args = new ArrayList<Invokable>();
|
List<Invokable> args = new ArrayList<Invokable>();
|
||||||
|
|
||||||
loop: while (true) {
|
loop: while (true) {
|
||||||
args.add(parseInternal());
|
args.add(parseInternal(false));
|
||||||
|
|
||||||
final Token current = peek();
|
final Token current = peek();
|
||||||
++position;
|
++position;
|
||||||
@ -345,7 +415,7 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
++position;
|
++position;
|
||||||
|
|
||||||
final Invokable ret = parseInternal();
|
final Invokable ret = parseInternal(false);
|
||||||
|
|
||||||
if (peek().id() != ')') {
|
if (peek().id() != ')') {
|
||||||
throw new ParserException(peek().getPosition(), "Unmatched opening bracket");
|
throw new ParserException(peek().getPosition(), "Unmatched opening bracket");
|
||||||
@ -354,4 +424,20 @@ public class Parser {
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Invokable parseBlock() throws ParserException {
|
||||||
|
if (peek().id() != '{') {
|
||||||
|
throw new ParserException(peek().getPosition(), "Unexpected character in parseBlock");
|
||||||
|
}
|
||||||
|
++position;
|
||||||
|
|
||||||
|
final Invokable ret = parseInternal(true);
|
||||||
|
|
||||||
|
if (peek().id() != '}') {
|
||||||
|
throw new ParserException(peek().getPosition(), "Unmatched opening brace");
|
||||||
|
}
|
||||||
|
++position;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
// $Id$
|
||||||
|
/*
|
||||||
|
* WorldEdit
|
||||||
|
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
|
public abstract class Assignable extends Invokable {
|
||||||
|
public Assignable(int position) {
|
||||||
|
super(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract double assign(double value) throws EvaluationException;
|
||||||
|
}
|
@ -21,10 +21,22 @@ package com.sk89q.worldedit.expression.runtime;
|
|||||||
|
|
||||||
public final class Operators {
|
public final class Operators {
|
||||||
public static final Function getOperator(int position, String name, Invokable lhs, Invokable rhs) throws NoSuchMethodException {
|
public static final Function getOperator(int position, String name, Invokable lhs, Invokable rhs) throws NoSuchMethodException {
|
||||||
|
if (lhs instanceof Assignable) {
|
||||||
|
try {
|
||||||
|
return new Function(position, Operators.class.getMethod(name, Assignable.class, Invokable.class), lhs, rhs);
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException e) {}
|
||||||
|
}
|
||||||
return new Function(position, Operators.class.getMethod(name, Invokable.class, Invokable.class), lhs, rhs);
|
return new Function(position, Operators.class.getMethod(name, Invokable.class, Invokable.class), lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Function getOperator(int position, String name, Invokable argument) throws NoSuchMethodException {
|
public static final Function getOperator(int position, String name, Invokable argument) throws NoSuchMethodException {
|
||||||
|
if (argument instanceof Assignable) {
|
||||||
|
try {
|
||||||
|
return new Function(position, Operators.class.getMethod(name, Assignable.class), argument);
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException e) {}
|
||||||
|
}
|
||||||
return new Function(position, Operators.class.getMethod(name, Invokable.class), argument);
|
return new Function(position, Operators.class.getMethod(name, Invokable.class), argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +128,43 @@ public final class Operators {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final double ass(Assignable lhs, Invokable rhs) throws EvaluationException {
|
||||||
|
return lhs.assign(rhs.invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double aadd(Assignable lhs, Invokable rhs) throws EvaluationException {
|
||||||
|
return lhs.assign(lhs.invoke() + rhs.invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double asub(Assignable lhs, Invokable rhs) throws EvaluationException {
|
||||||
|
return lhs.assign(lhs.invoke() - rhs.invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double amul(Assignable lhs, Invokable rhs) throws EvaluationException {
|
||||||
|
return lhs.assign(lhs.invoke() * rhs.invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double adiv(Assignable lhs, Invokable rhs) throws EvaluationException {
|
||||||
|
return lhs.assign(lhs.invoke() / rhs.invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double amod(Assignable lhs, Invokable rhs) throws EvaluationException {
|
||||||
|
return lhs.assign(lhs.invoke() % rhs.invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double aexp(Assignable lhs, Invokable rhs) throws EvaluationException {
|
||||||
|
return lhs.assign(Math.pow(lhs.invoke(), rhs.invoke()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double inc(Assignable x) throws EvaluationException {
|
||||||
|
return x.assign(x.invoke() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double dec(Assignable x) throws EvaluationException {
|
||||||
|
return x.assign(x.invoke() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
|
// Usable AlmostEqual function, based on http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
|
||||||
private static boolean almostEqual2sComplement(double A, double B, long maxUlps) {
|
private static boolean almostEqual2sComplement(double A, double B, long maxUlps) {
|
||||||
// Make sure maxUlps is non-negative and small enough that the
|
// Make sure maxUlps is non-negative and small enough that the
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
// $Id$
|
||||||
|
/*
|
||||||
|
* WorldEdit
|
||||||
|
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Sequence extends Invokable {
|
||||||
|
private final Invokable[] sequence;
|
||||||
|
|
||||||
|
public Sequence(int position, Invokable... sequence) {
|
||||||
|
super(position);
|
||||||
|
|
||||||
|
this.sequence = sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char id() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double invoke() throws EvaluationException {
|
||||||
|
double ret = 0;
|
||||||
|
for (Invokable invokable : sequence) {
|
||||||
|
ret = invokable.invoke();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder("seq(");
|
||||||
|
boolean first = true;
|
||||||
|
for (Invokable invokable : sequence) {
|
||||||
|
if (!first) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(invokable);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.append(')').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Invokable optimize() throws EvaluationException {
|
||||||
|
List<Invokable> newSequence = new ArrayList<Invokable>();
|
||||||
|
|
||||||
|
for (Invokable invokable : sequence) {
|
||||||
|
invokable = invokable.optimize();
|
||||||
|
if (invokable instanceof Sequence) {
|
||||||
|
for (Invokable subInvokable : ((Sequence) invokable).sequence) {
|
||||||
|
newSequence.add(subInvokable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newSequence.add(invokable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Sequence(getPosition(), newSequence.toArray(new Invokable[newSequence.size()]));
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.expression.runtime;
|
package com.sk89q.worldedit.expression.runtime;
|
||||||
|
|
||||||
public final class Variable extends Invokable {
|
public final class Variable extends Assignable {
|
||||||
public double value;
|
public double value;
|
||||||
|
|
||||||
public Variable(double value) {
|
public Variable(double value) {
|
||||||
@ -41,4 +41,9 @@ public final class Variable extends Invokable {
|
|||||||
public char id() {
|
public char id() {
|
||||||
return 'v';
|
return 'v';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double assign(double value) {
|
||||||
|
return this.value = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ public class ExpressionTest {
|
|||||||
assertEquals(1-2+3, simpleEval("1-2+3"), 0);
|
assertEquals(1-2+3, simpleEval("1-2+3"), 0);
|
||||||
|
|
||||||
// check unary ops
|
// check unary ops
|
||||||
assertEquals(2+ +4, simpleEval("2++4"), 0);
|
assertEquals(2+ +4, simpleEval("2+ +4"), 0);
|
||||||
assertEquals(2- -4, simpleEval("2--4"), 0);
|
assertEquals(2- -4, simpleEval("2- -4"), 0);
|
||||||
assertEquals(2*-4, simpleEval("2*-4"), 0);
|
assertEquals(2*-4, simpleEval("2*-4"), 0);
|
||||||
|
|
||||||
// check functions
|
// check functions
|
||||||
@ -31,7 +31,7 @@ public class ExpressionTest {
|
|||||||
public void testErrors() throws ExpressionException {
|
public void testErrors() throws ExpressionException {
|
||||||
// test lexer errors
|
// test lexer errors
|
||||||
try {
|
try {
|
||||||
Expression.compile("{");
|
Expression.compile("#");
|
||||||
fail("Error expected");
|
fail("Error expected");
|
||||||
} catch (LexerException e) {
|
} catch (LexerException e) {
|
||||||
assertEquals("Error position", 0, e.getPosition());
|
assertEquals("Error position", 0, e.getPosition());
|
||||||
@ -60,6 +60,15 @@ public class ExpressionTest {
|
|||||||
} catch (ParserException e) {}
|
} catch (ParserException e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAssign() throws ExpressionException {
|
||||||
|
Expression foo = Expression.compile("{a=x} b=y; c=z", "x", "y", "z", "a", "b", "c");
|
||||||
|
foo.evaluate(2, 3, 5);
|
||||||
|
assertEquals(2, foo.getVariable("a").invoke(), 0);
|
||||||
|
assertEquals(3, foo.getVariable("b").invoke(), 0);
|
||||||
|
assertEquals(5, foo.getVariable("c").invoke(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
private double simpleEval(String expression) throws Exception {
|
private double simpleEval(String expression) throws Exception {
|
||||||
return Expression.compile(expression).evaluate();
|
return Expression.compile(expression).evaluate();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user