geforkt von Mirrors/FastAsyncWorldEdit
Parser improvements
- After a closing brace or a semicolon, a new expression starts. This fixes "{}-1" and ";-1" returning an error. - Empty statements and empty block statements are now fully supported - Renamed PrefixOperator to UnaryOperator - Added postincrement(x++), postdecrement(x--) and factorial(x!) operators
Dieser Commit ist enthalten in:
Ursprung
77d1317964
Commit
8e0539adf1
@ -25,7 +25,6 @@ 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;
|
||||||
@ -33,6 +32,7 @@ import com.sk89q.worldedit.expression.lexer.tokens.Token;
|
|||||||
import com.sk89q.worldedit.expression.runtime.Constant;
|
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.RValue;
|
import com.sk89q.worldedit.expression.runtime.RValue;
|
||||||
|
import com.sk89q.worldedit.expression.runtime.Sequence;
|
||||||
import com.sk89q.worldedit.expression.runtime.Variable;
|
import com.sk89q.worldedit.expression.runtime.Variable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,7 +71,7 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RValue parse() throws ParserException {
|
private RValue parse() throws ParserException {
|
||||||
final RValue ret = parseInternal(true);
|
final RValue ret = parseMultipleStatements();
|
||||||
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);
|
||||||
@ -79,7 +79,44 @@ public class Parser {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final RValue parseInternal(boolean isStatement) throws ParserException {
|
private RValue parseMultipleStatements() throws ParserException {
|
||||||
|
List<RValue> statements = new ArrayList<RValue>();
|
||||||
|
loop: while (true) {
|
||||||
|
if (position >= tokens.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (peek().id()) {
|
||||||
|
case ';':
|
||||||
|
++position;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
statements.add(parseBlock());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '}':
|
||||||
|
break loop;
|
||||||
|
|
||||||
|
default:
|
||||||
|
statements.add(parseExpression());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (statements.size()) {
|
||||||
|
case 0:
|
||||||
|
throw new ParserException(-1, "No statement found.");
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return statements.get(0);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new Sequence(position, statements.toArray(new RValue[statements.size()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final RValue parseExpression() 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
|
||||||
@ -121,20 +158,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 '}':
|
case '}':
|
||||||
|
case ';':
|
||||||
break loop;
|
break loop;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
if (expressionStart) {
|
if (expressionStart) {
|
||||||
halfProcessed.add(new PrefixOperator((OperatorToken) current));
|
halfProcessed.add(new UnaryOperator((OperatorToken) current));
|
||||||
} else {
|
} else {
|
||||||
halfProcessed.add(current);
|
halfProcessed.add(current);
|
||||||
}
|
}
|
||||||
@ -150,12 +182,8 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStatement) {
|
|
||||||
return ParserProcessors.processStatement(halfProcessed);
|
|
||||||
} else {
|
|
||||||
return ParserProcessors.processExpression(halfProcessed);
|
return ParserProcessors.processExpression(halfProcessed);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Token peek() {
|
private Token peek() {
|
||||||
@ -180,7 +208,7 @@ public class Parser {
|
|||||||
List<RValue> args = new ArrayList<RValue>();
|
List<RValue> args = new ArrayList<RValue>();
|
||||||
|
|
||||||
loop: while (true) {
|
loop: while (true) {
|
||||||
args.add(parseInternal(false));
|
args.add(parseExpression());
|
||||||
|
|
||||||
final Token current = peek();
|
final Token current = peek();
|
||||||
++position;
|
++position;
|
||||||
@ -209,7 +237,7 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
++position;
|
++position;
|
||||||
|
|
||||||
final RValue ret = parseInternal(false);
|
final RValue ret = parseExpression();
|
||||||
|
|
||||||
if (peek().id() != ')') {
|
if (peek().id() != ')') {
|
||||||
throw new ParserException(peek().getPosition(), "Unmatched opening bracket");
|
throw new ParserException(peek().getPosition(), "Unmatched opening bracket");
|
||||||
@ -225,7 +253,11 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
++position;
|
++position;
|
||||||
|
|
||||||
final RValue ret = parseInternal(true);
|
if (peek().id() == '}') {
|
||||||
|
return new Sequence(peek().getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
final RValue ret = parseMultipleStatements();
|
||||||
|
|
||||||
if (peek().id() != '}') {
|
if (peek().id() != '}') {
|
||||||
throw new ParserException(peek().getPosition(), "Unmatched opening brace");
|
throw new ParserException(peek().getPosition(), "Unmatched opening brace");
|
||||||
|
@ -11,7 +11,6 @@ import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken;
|
|||||||
import com.sk89q.worldedit.expression.lexer.tokens.Token;
|
import com.sk89q.worldedit.expression.lexer.tokens.Token;
|
||||||
import com.sk89q.worldedit.expression.runtime.RValue;
|
import com.sk89q.worldedit.expression.runtime.RValue;
|
||||||
import com.sk89q.worldedit.expression.runtime.Operators;
|
import com.sk89q.worldedit.expression.runtime.Operators;
|
||||||
import com.sk89q.worldedit.expression.runtime.Sequence;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper classfor Parser. Contains processors for statements and operators.
|
* Helper classfor Parser. Contains processors for statements and operators.
|
||||||
@ -30,6 +29,9 @@ public final class ParserProcessors {
|
|||||||
unaryOpMap.put("~", "inv");
|
unaryOpMap.put("~", "inv");
|
||||||
unaryOpMap.put("++", "inc");
|
unaryOpMap.put("++", "inc");
|
||||||
unaryOpMap.put("--", "dec");
|
unaryOpMap.put("--", "dec");
|
||||||
|
unaryOpMap.put("x++", "postinc");
|
||||||
|
unaryOpMap.put("x--", "postdec");
|
||||||
|
unaryOpMap.put("x!", "fac");
|
||||||
|
|
||||||
final Object[][][] binaryOpsLA = {
|
final Object[][][] binaryOpsLA = {
|
||||||
{
|
{
|
||||||
@ -126,41 +128,6 @@ public final class ParserProcessors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static RValue processStatement(LinkedList<Identifiable> input) throws ParserException {
|
|
||||||
LinkedList<Identifiable> lhs = new LinkedList<Identifiable>();
|
|
||||||
LinkedList<Identifiable> rhs = new LinkedList<Identifiable>();
|
|
||||||
boolean semicolonFound = false;
|
|
||||||
|
|
||||||
for (Identifiable identifiable : input) {
|
|
||||||
if (semicolonFound) {
|
|
||||||
rhs.addLast(identifiable);
|
|
||||||
} else {
|
|
||||||
if (identifiable.id() == ';') {
|
|
||||||
semicolonFound = true;
|
|
||||||
} else {
|
|
||||||
lhs.addLast(identifiable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rhs.isEmpty()) {
|
|
||||||
if (lhs.isEmpty()) {
|
|
||||||
return new Sequence(semicolonFound ? input.get(0).getPosition() : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return processExpression(lhs);
|
|
||||||
} else if (lhs.isEmpty()) {
|
|
||||||
return processStatement(rhs);
|
|
||||||
} else {
|
|
||||||
assert (semicolonFound);
|
|
||||||
|
|
||||||
RValue lhsInvokable = processExpression(lhs);
|
|
||||||
RValue rhsInvokable = processStatement(rhs);
|
|
||||||
|
|
||||||
return new Sequence(lhsInvokable.getPosition(), lhsInvokable, rhsInvokable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static RValue processExpression(LinkedList<Identifiable> input) throws ParserException {
|
static RValue processExpression(LinkedList<Identifiable> input) throws ParserException {
|
||||||
return processBinaryOpsRA(input, binaryOpMapsRA.length - 1);
|
return processBinaryOpsRA(input, binaryOpMapsRA.length - 1);
|
||||||
}
|
}
|
||||||
@ -253,16 +220,41 @@ public final class ParserProcessors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static RValue processUnaryOps(LinkedList<Identifiable> input) throws ParserException {
|
private static RValue processUnaryOps(LinkedList<Identifiable> input) throws ParserException {
|
||||||
|
// Preprocess postfix operators into prefix operators
|
||||||
|
final Identifiable center;
|
||||||
|
LinkedList<UnaryOperator> postfixes = new LinkedList<UnaryOperator>();
|
||||||
|
do {
|
||||||
if (input.isEmpty()) {
|
if (input.isEmpty()) {
|
||||||
throw new ParserException(-1, "Expression missing.");
|
throw new ParserException(-1, "Expression missing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
RValue ret = (RValue) input.removeLast();
|
final Identifiable last = input.removeLast();
|
||||||
|
if (last instanceof OperatorToken) {
|
||||||
|
System.out.println("Found postfix: "+last);
|
||||||
|
postfixes.addFirst(new UnaryOperator(last.getPosition(), "x"+((OperatorToken)last).operator));
|
||||||
|
}
|
||||||
|
else if (last instanceof UnaryOperator) {
|
||||||
|
System.out.println("Found postfix: "+last);
|
||||||
|
postfixes.addFirst(new UnaryOperator(last.getPosition(), "x"+((UnaryOperator)last).operator));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
center = last;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
if (!(center instanceof RValue)) {
|
||||||
|
throw new ParserException(center.getPosition(), "Expected expression, found "+center);
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addAll(postfixes);
|
||||||
|
|
||||||
|
RValue ret = (RValue) center;
|
||||||
while (!input.isEmpty()) {
|
while (!input.isEmpty()) {
|
||||||
final Identifiable last = input.removeLast();
|
final Identifiable last = input.removeLast();
|
||||||
final int lastPosition = last.getPosition();
|
final int lastPosition = last.getPosition();
|
||||||
if (last instanceof PrefixOperator) {
|
if (last instanceof UnaryOperator) {
|
||||||
final String operator = ((PrefixOperator) last).operator;
|
final String operator = ((UnaryOperator) last).operator;
|
||||||
if (operator.equals("+")) {
|
if (operator.equals("+")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package com.sk89q.worldedit.expression.parser;
|
|
||||||
|
|
||||||
import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parser uses this pseudo-token to mark operators as prefix operators.
|
|
||||||
*
|
|
||||||
* @author TomyLobo
|
|
||||||
*/
|
|
||||||
public class PrefixOperator extends PseudoToken {
|
|
||||||
final String operator;
|
|
||||||
|
|
||||||
public PrefixOperator(OperatorToken operatorToken) {
|
|
||||||
super(operatorToken.getPosition());
|
|
||||||
operator = operatorToken.operator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char id() {
|
|
||||||
return 'p';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "PrefixOperator(" + operator + ")";
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.sk89q.worldedit.expression.parser;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.expression.lexer.tokens.OperatorToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parser uses this pseudo-token to mark operators as unary operators.
|
||||||
|
*
|
||||||
|
* @author TomyLobo
|
||||||
|
*/
|
||||||
|
public class UnaryOperator extends PseudoToken {
|
||||||
|
final String operator;
|
||||||
|
|
||||||
|
public UnaryOperator(OperatorToken operatorToken) {
|
||||||
|
this(operatorToken.getPosition(), operatorToken.operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnaryOperator(int position, String operator) {
|
||||||
|
super(position);
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char id() {
|
||||||
|
return 'p';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UnaryOperator(" + operator + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -161,6 +161,7 @@ public final class Operators {
|
|||||||
return lhs.assign(Math.pow(lhs.getValue(), rhs.getValue()));
|
return lhs.assign(Math.pow(lhs.getValue(), rhs.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final double inc(LValue x) throws EvaluationException {
|
public static final double inc(LValue x) throws EvaluationException {
|
||||||
return x.assign(x.getValue() + 1);
|
return x.assign(x.getValue() + 1);
|
||||||
}
|
}
|
||||||
@ -169,6 +170,41 @@ public final class Operators {
|
|||||||
return x.assign(x.getValue() - 1);
|
return x.assign(x.getValue() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final double postinc(LValue x) throws EvaluationException {
|
||||||
|
final double oldValue = x.getValue();
|
||||||
|
x.assign(oldValue + 1);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double postdec(LValue x) throws EvaluationException {
|
||||||
|
final double oldValue = x.getValue();
|
||||||
|
x.assign(oldValue - 1);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final double[] factorials = new double[171];
|
||||||
|
static {
|
||||||
|
double accum = 1;
|
||||||
|
factorials[0] = 1;
|
||||||
|
for (int i = 1; i < factorials.length; ++i) {
|
||||||
|
factorials[i] = accum *= i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double fac(RValue x) throws EvaluationException {
|
||||||
|
int n = (int) x.getValue();
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n >= factorials.length) {
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return factorials[n];
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren