mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 09:47:38 +00:00
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
This commit is contained in:
parent
77d1317964
commit
8e0539adf1
@ -25,7 +25,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.NumberToken;
|
||||
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.Functions;
|
||||
import com.sk89q.worldedit.expression.runtime.RValue;
|
||||
import com.sk89q.worldedit.expression.runtime.Sequence;
|
||||
import com.sk89q.worldedit.expression.runtime.Variable;
|
||||
|
||||
/**
|
||||
@ -71,7 +71,7 @@ public class Parser {
|
||||
}
|
||||
|
||||
private RValue parse() throws ParserException {
|
||||
final RValue ret = parseInternal(true);
|
||||
final RValue ret = parseMultipleStatements();
|
||||
if (position < tokens.size()) {
|
||||
final Token token = peek();
|
||||
throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token);
|
||||
@ -79,7 +79,44 @@ public class Parser {
|
||||
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>();
|
||||
|
||||
// process brackets, numbers, functions, variables and detect prefix operators
|
||||
@ -121,20 +158,15 @@ public class Parser {
|
||||
expressionStart = false;
|
||||
break;
|
||||
|
||||
case '{':
|
||||
halfProcessed.add(parseBlock());
|
||||
halfProcessed.add(new CharacterToken(-1, ';'));
|
||||
expressionStart = false;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
case ')':
|
||||
case '}':
|
||||
case ';':
|
||||
break loop;
|
||||
|
||||
case 'o':
|
||||
if (expressionStart) {
|
||||
halfProcessed.add(new PrefixOperator((OperatorToken) current));
|
||||
halfProcessed.add(new UnaryOperator((OperatorToken) current));
|
||||
} else {
|
||||
halfProcessed.add(current);
|
||||
}
|
||||
@ -150,11 +182,7 @@ public class Parser {
|
||||
}
|
||||
}
|
||||
|
||||
if (isStatement) {
|
||||
return ParserProcessors.processStatement(halfProcessed);
|
||||
} else {
|
||||
return ParserProcessors.processExpression(halfProcessed);
|
||||
}
|
||||
return ParserProcessors.processExpression(halfProcessed);
|
||||
}
|
||||
|
||||
|
||||
@ -180,7 +208,7 @@ public class Parser {
|
||||
List<RValue> args = new ArrayList<RValue>();
|
||||
|
||||
loop: while (true) {
|
||||
args.add(parseInternal(false));
|
||||
args.add(parseExpression());
|
||||
|
||||
final Token current = peek();
|
||||
++position;
|
||||
@ -209,7 +237,7 @@ public class Parser {
|
||||
}
|
||||
++position;
|
||||
|
||||
final RValue ret = parseInternal(false);
|
||||
final RValue ret = parseExpression();
|
||||
|
||||
if (peek().id() != ')') {
|
||||
throw new ParserException(peek().getPosition(), "Unmatched opening bracket");
|
||||
@ -225,7 +253,11 @@ public class Parser {
|
||||
}
|
||||
++position;
|
||||
|
||||
final RValue ret = parseInternal(true);
|
||||
if (peek().id() == '}') {
|
||||
return new Sequence(peek().getPosition());
|
||||
}
|
||||
|
||||
final RValue ret = parseMultipleStatements();
|
||||
|
||||
if (peek().id() != '}') {
|
||||
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.runtime.RValue;
|
||||
import com.sk89q.worldedit.expression.runtime.Operators;
|
||||
import com.sk89q.worldedit.expression.runtime.Sequence;
|
||||
|
||||
/**
|
||||
* Helper classfor Parser. Contains processors for statements and operators.
|
||||
@ -30,6 +29,9 @@ public final class ParserProcessors {
|
||||
unaryOpMap.put("~", "inv");
|
||||
unaryOpMap.put("++", "inc");
|
||||
unaryOpMap.put("--", "dec");
|
||||
unaryOpMap.put("x++", "postinc");
|
||||
unaryOpMap.put("x--", "postdec");
|
||||
unaryOpMap.put("x!", "fac");
|
||||
|
||||
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 {
|
||||
return processBinaryOpsRA(input, binaryOpMapsRA.length - 1);
|
||||
}
|
||||
@ -253,16 +220,41 @@ public final class ParserProcessors {
|
||||
}
|
||||
|
||||
private static RValue processUnaryOps(LinkedList<Identifiable> input) throws ParserException {
|
||||
if (input.isEmpty()) {
|
||||
throw new ParserException(-1, "Expression missing.");
|
||||
// Preprocess postfix operators into prefix operators
|
||||
final Identifiable center;
|
||||
LinkedList<UnaryOperator> postfixes = new LinkedList<UnaryOperator>();
|
||||
do {
|
||||
if (input.isEmpty()) {
|
||||
throw new ParserException(-1, "Expression missing.");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
RValue ret = (RValue) input.removeLast();
|
||||
input.addAll(postfixes);
|
||||
|
||||
RValue ret = (RValue) center;
|
||||
while (!input.isEmpty()) {
|
||||
final Identifiable last = input.removeLast();
|
||||
final int lastPosition = last.getPosition();
|
||||
if (last instanceof PrefixOperator) {
|
||||
final String operator = ((PrefixOperator) last).operator;
|
||||
if (last instanceof UnaryOperator) {
|
||||
final String operator = ((UnaryOperator) last).operator;
|
||||
if (operator.equals("+")) {
|
||||
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()));
|
||||
}
|
||||
|
||||
|
||||
public static final double inc(LValue x) throws EvaluationException {
|
||||
return x.assign(x.getValue() + 1);
|
||||
}
|
||||
@ -169,6 +170,41 @@ public final class Operators {
|
||||
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
|
||||
private static boolean almostEqual2sComplement(double A, double B, long maxUlps) {
|
||||
|
Loading…
Reference in New Issue
Block a user