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:
TomyLobo
2011-10-23 05:15:23 +02:00
parent 9c070c323f
commit ee79abff67
9 changed files with 345 additions and 19 deletions

View File

@ -28,6 +28,7 @@ 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;
@ -36,6 +37,7 @@ import com.sk89q.worldedit.expression.runtime.Constant;
import com.sk89q.worldedit.expression.runtime.Functions;
import com.sk89q.worldedit.expression.runtime.Invokable;
import com.sk89q.worldedit.expression.runtime.Operators;
import com.sk89q.worldedit.expression.runtime.Sequence;
public class Parser {
private final class NullToken extends Token {
@ -66,7 +68,7 @@ public class Parser {
}
private Invokable parse() throws ParserException {
final Invokable ret = parseInternal();
final Invokable ret = parseInternal(true);
if (position < tokens.size()) {
final Token token = peek();
throw new ParserException(token.getPosition(), "Extra token at the end of the input: " + token);
@ -74,7 +76,7 @@ public class Parser {
return ret;
}
private final Invokable parseInternal() throws ParserException {
private final Invokable parseInternal(boolean isStatement) throws ParserException {
LinkedList<Identifiable> halfProcessed = new LinkedList<Identifiable>();
// process brackets, numbers, functions, variables and detect prefix operators
@ -112,8 +114,15 @@ public class Parser {
expressionStart = false;
break;
case '{':
halfProcessed.add(parseBlock());
halfProcessed.add(new CharacterToken(-1, ';'));
expressionStart = false;
break;
case ',':
case ')':
case '}':
break loop;
case 'o':
@ -135,8 +144,57 @@ public class Parser {
}
}
// process binary operators
return processBinaryOps(halfProcessed, binaryOpMaps.length - 1);
if (isStatement) {
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;
@ -146,6 +204,7 @@ public class Parser {
final Object[][][] binaryOps = {
{
{ "^", "pow" },
{ "**", "pow" },
},
{
{ "*", "mul" },
@ -177,6 +236,15 @@ public class Parser {
{
{ "||", "or" },
},
{
{ "=", "ass" },
{ "+=", "aadd" },
{ "-=", "asub" },
{ "*=", "amul" },
{ "/=", "adiv" },
{ "%=", "amod" },
{ "^=", "aexp" },
},
};
@SuppressWarnings("unchecked")
@ -205,6 +273,8 @@ public class Parser {
unaryOpMap.put("-", "neg");
unaryOpMap.put("!", "not");
unaryOpMap.put("~", "inv");
unaryOpMap.put("++", "inc");
unaryOpMap.put("--", "dec");
}
private Invokable processBinaryOps(LinkedList<Identifiable> input, int level) throws ParserException {
@ -315,7 +385,7 @@ public class Parser {
List<Invokable> args = new ArrayList<Invokable>();
loop: while (true) {
args.add(parseInternal());
args.add(parseInternal(false));
final Token current = peek();
++position;
@ -345,7 +415,7 @@ public class Parser {
}
++position;
final Invokable ret = parseInternal();
final Invokable ret = parseInternal(false);
if (peek().id() != ')') {
throw new ParserException(peek().getPosition(), "Unmatched opening bracket");
@ -354,4 +424,20 @@ public class Parser {
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;
}
}