Added switch/case to the expression parser.

Also added a test case.
This commit is contained in:
TomyLobo 2011-12-04 09:13:51 +01:00
parent 61b2ea4007
commit a8e64fd8bc
5 changed files with 157 additions and 4 deletions

View File

@ -51,6 +51,7 @@ public interface Identifiable {
* r - Return * r - Return
* b - Break (includes continue) * b - Break (includes continue)
* S - SimpleFor * S - SimpleFor
* C - Switch
* </pre> * </pre>
*/ */
public abstract char id(); public abstract char id();

View File

@ -109,7 +109,7 @@ public class Lexer {
characterTokens.add(':'); characterTokens.add(':');
} }
private static final Set<String> keywords = new HashSet<String>(Arrays.asList("if", "else", "while", "do", "for", "break", "continue", "return", "switch", "case")); private static final Set<String> keywords = new HashSet<String>(Arrays.asList("if", "else", "while", "do", "for", "break", "continue", "return", "switch", "case", "default"));
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_]*)");

View File

@ -39,6 +39,7 @@ import com.sk89q.worldedit.expression.runtime.RValue;
import com.sk89q.worldedit.expression.runtime.Return; import com.sk89q.worldedit.expression.runtime.Return;
import com.sk89q.worldedit.expression.runtime.Sequence; import com.sk89q.worldedit.expression.runtime.Sequence;
import com.sk89q.worldedit.expression.runtime.SimpleFor; import com.sk89q.worldedit.expression.runtime.SimpleFor;
import com.sk89q.worldedit.expression.runtime.Switch;
import com.sk89q.worldedit.expression.runtime.While; import com.sk89q.worldedit.expression.runtime.While;
/** /**
@ -134,7 +135,11 @@ public class Parser {
break; break;
} }
case 'd': { // do case 'd': { // do/default
if (hasKeyword("default")) {
break loop;
}
++position; ++position;
final RValue body = parseStatements(true); final RValue body = parseStatements(true);
@ -199,7 +204,11 @@ public class Parser {
statements.add(new Break(current.getPosition(), false)); statements.add(new Break(current.getPosition(), false));
break; break;
case 'c': // continue case 'c': // continue/case
if (hasKeyword("case")) {
break loop;
}
++position; ++position;
statements.add(new Break(current.getPosition(), true)); statements.add(new Break(current.getPosition(), true));
break; break;
@ -211,8 +220,55 @@ public class Parser {
expectSemicolon = true; expectSemicolon = true;
break; break;
case 's': // switch
++position;
final RValue parameter = parseBracket();
final List<Double> values = new ArrayList<Double>();
final List<RValue> caseStatements = new ArrayList<RValue>();
RValue defaultCase = null;
consumeCharacter('{');
while (peek().id() != '}') {
if (position >= tokens.size()) {
throw new ParserException(current.getPosition(), "Expected '}' instead of EOF");
}
if (defaultCase != null) {
throw new ParserException(current.getPosition(), "Expected '}' instead of " + peek());
}
if (hasKeyword("case")) {
++position;
final Token valueToken = peek();
if (!(valueToken instanceof NumberToken)) {
throw new ParserException(current.getPosition(), "Expected number instead of " + peek());
}
++position;
values.add(((NumberToken) valueToken).value);
consumeCharacter(':');
caseStatements.add(parseStatements(false));
} else if (hasKeyword("default")) {
++position;
consumeCharacter(':');
defaultCase = parseStatements(false);
} else {
throw new ParserException(current.getPosition(), "Expected 'case' or 'default' instead of " + peek());
}
}
consumeCharacter('}');
statements.add(new Switch(current.getPosition(), parameter, values, caseStatements, defaultCase));
break;
default:
throw new ParserException(current.getPosition(), "Unexpected keyword '" + keyword + "'");
}
switch (1) {
default: default:
throw new ParserException(current.getPosition(), "Unimplemented keyword '" + keyword + "'");
} }
break; break;

View File

@ -0,0 +1,85 @@
package com.sk89q.worldedit.expression.runtime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class Switch extends Node implements RValue {
private final RValue parameter;
private final Map<Double, Integer> valueMap = new HashMap<Double, Integer>();
private final RValue[] caseStatements;
private final RValue defaultCase;
public Switch(int position, RValue parameter, List<Double> values, List<RValue> caseStatements, RValue defaultCase) {
super(position);
this.parameter = parameter;
assert(values.size() == caseStatements.size());
for (int i = 0; i < values.size(); ++i) {
valueMap.put(values.get(i), i);
}
this.caseStatements = caseStatements.toArray(new RValue[caseStatements.size()]);
this.defaultCase = defaultCase;
}
@Override
public char id() {
return 'W';
}
@Override
public double getValue() throws EvaluationException {
final double parameter = this.parameter.getValue();
try {
double ret = 0.0;
final Integer index = valueMap.get(parameter);
if (index != null) {
for (int i = index; i < caseStatements.length; ++i) {
ret = caseStatements[i].getValue();
}
}
return defaultCase == null ? ret : defaultCase.getValue();
} catch (BreakException e) {
return 0.0;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("switch (");
sb.append(parameter);
sb.append(") { ");
for (int i = 0; i < caseStatements.length; ++i) {
RValue caseStatement = caseStatements[i];
sb.append("case ");
for (Entry<Double, Integer> entry : valueMap.entrySet()) {
if (entry.getValue() == i) {
sb.append(entry.getKey());
break;
}
}
sb.append(": ");
sb.append(caseStatement);
sb.append(' ');
}
if (defaultCase != null) {
sb.append("default: ");
sb.append(defaultCase);
sb.append(' ');
}
sb.append("}");
return sb.toString();
}
}

View File

@ -99,6 +99,17 @@ public class ExpressionTest {
assertEquals(12345, simpleEval("y=0; for (i=1,5) { y *= 10; y += i; } y"), 0); assertEquals(12345, simpleEval("y=0; for (i=1,5) { y *= 10; y += i; } y"), 0);
} }
@Test
public void testSwitch() throws ExpressionException {
assertEquals(523, simpleEval("x=1;y=2;z=3;switch (1) { case 1: x=5; break; case 2: y=6; break; default: z=7 } x*100+y*10+z"), 0);
assertEquals(163, simpleEval("x=1;y=2;z=3;switch (2) { case 1: x=5; break; case 2: y=6; break; default: z=7 } x*100+y*10+z"), 0);
assertEquals(127, simpleEval("x=1;y=2;z=3;switch (3) { case 1: x=5; break; case 2: y=6; break; default: z=7 } x*100+y*10+z"), 0);
assertEquals(567, simpleEval("x=1;y=2;z=3;switch (1) { case 1: x=5; case 2: y=6; default: z=7 } x*100+y*10+z"), 0);
assertEquals(167, simpleEval("x=1;y=2;z=3;switch (2) { case 1: x=5; case 2: y=6; default: z=7 } x*100+y*10+z"), 0);
assertEquals(127, simpleEval("x=1;y=2;z=3;switch (3) { case 1: x=5; case 2: y=6; default: z=7 } x*100+y*10+z"), 0);
}
private double simpleEval(String expressionString) throws ExpressionException { private double simpleEval(String expressionString) throws ExpressionException {
final Expression expression = compile(expressionString); final Expression expression = compile(expressionString);
return expression.evaluate(); return expression.evaluate();