Implement and test Parser (with temporary error handling)
This commit is contained in:
parent
b959fac770
commit
6ab69a1613
@ -0,0 +1,262 @@
|
|||||||
|
package it.cavallium.warppi.math.rules.dsl.frontend;
|
||||||
|
|
||||||
|
import it.cavallium.warppi.math.MathematicalSymbols;
|
||||||
|
import it.cavallium.warppi.math.rules.RuleType;
|
||||||
|
import it.cavallium.warppi.math.rules.dsl.Pattern;
|
||||||
|
import it.cavallium.warppi.math.rules.dsl.PatternRule;
|
||||||
|
import it.cavallium.warppi.math.rules.dsl.patterns.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a list of tokens to a list of <code>PatternRule</code>s.
|
||||||
|
*/
|
||||||
|
public class Parser {
|
||||||
|
private final List<Token> tokens;
|
||||||
|
private int current = 0;
|
||||||
|
|
||||||
|
public Parser(List<Token> tokens) {
|
||||||
|
this.tokens = tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PatternRule> parse() {
|
||||||
|
return rules();
|
||||||
|
}
|
||||||
|
|
||||||
|
// rules = { rule } , EOF ;
|
||||||
|
private List<PatternRule> rules() {
|
||||||
|
List<PatternRule> rules = new ArrayList<>();
|
||||||
|
while (!atEnd()) {
|
||||||
|
rules.add(rule());
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rule = rule header , rule body ;
|
||||||
|
// rule header = rule type , IDENTIFIER , COLON ;
|
||||||
|
// rule body = pattern , ARROW , replacements ;
|
||||||
|
private PatternRule rule() {
|
||||||
|
RuleType type = ruleType();
|
||||||
|
String name = matchOrFail(IDENTIFIER).lexeme;
|
||||||
|
matchOrFail(COLON);
|
||||||
|
Pattern target = pattern();
|
||||||
|
matchOrFail(ARROW);
|
||||||
|
List<Pattern> replacements = replacements();
|
||||||
|
return new PatternRule(name, type, target, replacements);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rule type = REDUCTION | EXPANSION | CALCULATION | EXISTENCE ;
|
||||||
|
private RuleType ruleType() {
|
||||||
|
switch (pop().type) {
|
||||||
|
case REDUCTION:
|
||||||
|
return RuleType.REDUCTION;
|
||||||
|
case EXPANSION:
|
||||||
|
return RuleType.EXPANSION;
|
||||||
|
case CALCULATION:
|
||||||
|
return RuleType.CALCULATION;
|
||||||
|
case EXISTENCE:
|
||||||
|
return RuleType.EXISTENCE;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Expected rule type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// pattern = equation ;
|
||||||
|
private Pattern pattern() {
|
||||||
|
return equation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// replacements = pattern
|
||||||
|
// | LEFT_BRACKET , patterns , RIGHT_BRACKET ;
|
||||||
|
// patterns = [ pattern , { COMMA , pattern } , [ COMMA ] ] ;
|
||||||
|
private List<Pattern> replacements() {
|
||||||
|
if (match(LEFT_BRACKET) == null) {
|
||||||
|
return Collections.singletonList(pattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match(RIGHT_BRACKET) != null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
List<Pattern> pats = new ArrayList<>();
|
||||||
|
do {
|
||||||
|
pats.add(pattern());
|
||||||
|
} while (match(COMMA) != null && peek().type != RIGHT_BRACKET);
|
||||||
|
matchOrFail(RIGHT_BRACKET);
|
||||||
|
return pats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// equation = sum , [ EQUALS , sum ] ;
|
||||||
|
private Pattern equation() {
|
||||||
|
Pattern pat = sum();
|
||||||
|
if (match(EQUALS) != null) {
|
||||||
|
pat = new EquationPattern(pat, sum());
|
||||||
|
}
|
||||||
|
return pat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum = product , { ( PLUS | MINUS | PLUS_MINUS ) product } ;
|
||||||
|
private Pattern sum() {
|
||||||
|
return matchLeftAssoc(this::product, Map.ofEntries(
|
||||||
|
Map.entry(PLUS, SumPattern::new),
|
||||||
|
Map.entry(MINUS, SubtractionPattern::new),
|
||||||
|
Map.entry(PLUS_MINUS, SumSubtractionPattern::new)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// product = unary , { ( TIMES | DIVIDE ) unary } ;
|
||||||
|
private Pattern product() {
|
||||||
|
return matchLeftAssoc(this::unary, Map.ofEntries(
|
||||||
|
Map.entry(TIMES, MultiplicationPattern::new),
|
||||||
|
Map.entry(DIVIDE, DivisionPattern::new)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// unary = ( PLUS | MINUS ) unary
|
||||||
|
// | power ;
|
||||||
|
private Pattern unary() {
|
||||||
|
if (match(PLUS) != null) {
|
||||||
|
return unary();
|
||||||
|
} else if (match(MINUS) != null) {
|
||||||
|
return new NegativePattern(unary());
|
||||||
|
} else {
|
||||||
|
return power();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// power = ( function | primary ) , [ POWER , power ] ;
|
||||||
|
private Pattern power() {
|
||||||
|
Pattern pat = functionOrPrimary();
|
||||||
|
if (match(POWER) != null) {
|
||||||
|
pat = new PowerPattern(pat, power());
|
||||||
|
}
|
||||||
|
return pat;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern functionOrPrimary() {
|
||||||
|
Pattern function = tryFunction();
|
||||||
|
return function != null ? function : primary();
|
||||||
|
}
|
||||||
|
|
||||||
|
// function = ( ARCCOS | ARCSIN | ARCTAN | COS | SIN | SQRT | TAN ) , LEFT_PAREN , sum , RIGHT_PAREN
|
||||||
|
// | ( LOG | ROOT ) LEFT_PAREN , sum , COMMA , sum , RIGHT_PAREN ;
|
||||||
|
private Pattern tryFunction() {
|
||||||
|
final Map<TokenType, Function<Pattern, Pattern>> oneArg = Map.ofEntries(
|
||||||
|
Map.entry(ARCCOS, ArcCosinePattern::new),
|
||||||
|
Map.entry(ARCSIN, ArcSinePattern::new),
|
||||||
|
Map.entry(ARCTAN, ArcTangentPattern::new),
|
||||||
|
Map.entry(COS, CosinePattern::new),
|
||||||
|
Map.entry(SIN, SinePattern::new),
|
||||||
|
Map.entry(SQRT, arg -> new RootPattern(new NumberPattern(new BigDecimal(2)), arg)),
|
||||||
|
Map.entry(TAN, TangentPattern::new)
|
||||||
|
);
|
||||||
|
final Map<TokenType, BiFunction<Pattern, Pattern, Pattern>> twoArg = Map.ofEntries(
|
||||||
|
Map.entry(LOG, LogarithmPattern::new),
|
||||||
|
Map.entry(ROOT, RootPattern::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
final TokenType curType = peek().type;
|
||||||
|
if (oneArg.containsKey(curType)) {
|
||||||
|
pop();
|
||||||
|
return oneArgFunction(oneArg.get(curType));
|
||||||
|
} else if (twoArg.containsKey(curType)) {
|
||||||
|
pop();
|
||||||
|
return twoArgFunction(twoArg.get(curType));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern oneArgFunction(final Function<Pattern, Pattern> constructor) {
|
||||||
|
matchOrFail(LEFT_PAREN);
|
||||||
|
final Pattern arg = pattern();
|
||||||
|
matchOrFail(RIGHT_PAREN);
|
||||||
|
return constructor.apply(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern twoArgFunction(final BiFunction<Pattern, Pattern, Pattern> constructor) {
|
||||||
|
matchOrFail(LEFT_PAREN);
|
||||||
|
final Pattern firstArg = pattern();
|
||||||
|
matchOrFail(COMMA);
|
||||||
|
final Pattern secondArg = pattern();
|
||||||
|
matchOrFail(RIGHT_PAREN);
|
||||||
|
return constructor.apply(firstArg, secondArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// primary = NUMBER | constant | IDENTIFIER | UNDEFINED
|
||||||
|
// | LEFT_PAREN sum RIGHT_PAREN ;
|
||||||
|
// constant = PI | E ;
|
||||||
|
private Pattern primary() {
|
||||||
|
Token curToken = pop();
|
||||||
|
switch (curToken.type) {
|
||||||
|
case PI:
|
||||||
|
return new ConstantPattern(MathematicalSymbols.PI);
|
||||||
|
case E:
|
||||||
|
return new ConstantPattern(MathematicalSymbols.EULER_NUMBER);
|
||||||
|
case UNDEFINED:
|
||||||
|
return new UndefinedPattern();
|
||||||
|
case NUMBER:
|
||||||
|
return new NumberPattern(new BigDecimal(curToken.lexeme));
|
||||||
|
case IDENTIFIER:
|
||||||
|
return new SubFunctionPattern(curToken.lexeme);
|
||||||
|
case LEFT_PAREN:
|
||||||
|
final Pattern grouped = sum();
|
||||||
|
matchOrFail(RIGHT_PAREN);
|
||||||
|
return grouped;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected " + curToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern matchLeftAssoc(
|
||||||
|
final Supplier<Pattern> operandParser,
|
||||||
|
final Map<TokenType, BiFunction<Pattern, Pattern, Pattern>> operators
|
||||||
|
) {
|
||||||
|
Pattern pat = operandParser.get();
|
||||||
|
while (operators.containsKey(peek().type)) {
|
||||||
|
final Token operatorToken = pop();
|
||||||
|
final BiFunction<Pattern, Pattern, Pattern> constructor = operators.get(operatorToken.type);
|
||||||
|
pat = constructor.apply(pat, operandParser.get());
|
||||||
|
}
|
||||||
|
return pat;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token matchOrFail(final TokenType expectedType) {
|
||||||
|
final Token matched = match(expectedType);
|
||||||
|
if (matched == null) {
|
||||||
|
throw new RuntimeException("Expected " + expectedType);
|
||||||
|
}
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token match(final TokenType expectedType) {
|
||||||
|
final Token curToken = tokens.get(current);
|
||||||
|
if (curToken.type == expectedType) {
|
||||||
|
current++;
|
||||||
|
return curToken;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token pop() {
|
||||||
|
final Token curToken = tokens.get(current);
|
||||||
|
current++;
|
||||||
|
return curToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token peek() {
|
||||||
|
return tokens.get(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean atEnd() {
|
||||||
|
return tokens.get(current).type == EOF;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,500 @@
|
|||||||
|
package it.cavallium.warppi.math.rules.dsl.frontend;
|
||||||
|
|
||||||
|
import it.cavallium.warppi.math.MathematicalSymbols;
|
||||||
|
import it.cavallium.warppi.math.rules.RuleType;
|
||||||
|
import it.cavallium.warppi.math.rules.dsl.Pattern;
|
||||||
|
import it.cavallium.warppi.math.rules.dsl.PatternRule;
|
||||||
|
import it.cavallium.warppi.math.rules.dsl.patterns.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ParserTest {
|
||||||
|
@Test
|
||||||
|
public void noRules() {
|
||||||
|
final List<Token> tokens = Collections.singletonList(
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
assertEquals(Collections.emptyList(), parser.parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validRuleMultipleReplacements() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(IDENTIFIER, "TestRule_123", 10),
|
||||||
|
new Token(COLON, ":", 22),
|
||||||
|
new Token(IDENTIFIER, "x", 26),
|
||||||
|
new Token(PLUS, "+", 28),
|
||||||
|
new Token(IDENTIFIER, "y", 30),
|
||||||
|
new Token(TIMES, "*", 32),
|
||||||
|
new Token(IDENTIFIER, "z", 34),
|
||||||
|
new Token(EQUALS, "=", 36),
|
||||||
|
new Token(MINUS, "-", 38),
|
||||||
|
new Token(LEFT_PAREN, "(", 39),
|
||||||
|
new Token(IDENTIFIER, "a_123", 40),
|
||||||
|
new Token(PLUS_MINUS, "+-", 46),
|
||||||
|
new Token(NUMBER, "3", 49),
|
||||||
|
new Token(DIVIDE, "/", 51),
|
||||||
|
new Token(NUMBER, "2.2", 53),
|
||||||
|
new Token(RIGHT_PAREN, ")", 56),
|
||||||
|
new Token(ARROW, "->", 58),
|
||||||
|
new Token(LEFT_BRACKET, "[", 61),
|
||||||
|
new Token(IDENTIFIER, "x", 67),
|
||||||
|
new Token(POWER, "^", 68),
|
||||||
|
new Token(IDENTIFIER, "a_123", 69),
|
||||||
|
new Token(EQUALS, "=", 75),
|
||||||
|
new Token(COS, "cos", 77),
|
||||||
|
new Token(LEFT_PAREN, "(", 80),
|
||||||
|
new Token(PI, "pi", 81),
|
||||||
|
new Token(RIGHT_PAREN, ")", 83),
|
||||||
|
new Token(MINUS, "-", 85),
|
||||||
|
new Token(LOG, "log", 87),
|
||||||
|
new Token(LEFT_PAREN, "(", 90),
|
||||||
|
new Token(E, "e", 91),
|
||||||
|
new Token(COMMA, ",", 92),
|
||||||
|
new Token(E, "e", 94),
|
||||||
|
new Token(RIGHT_PAREN, ")", 95),
|
||||||
|
new Token(COMMA, ",", 96),
|
||||||
|
new Token(UNDEFINED, "undefined", 113),
|
||||||
|
new Token(COMMA, ",", 122),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 138),
|
||||||
|
new Token(EOF, "", 140)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
|
||||||
|
// x + y * z = -(a_123 +- 3 / 2.2)
|
||||||
|
final Pattern target = new EquationPattern(
|
||||||
|
new SumPattern(
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new MultiplicationPattern(
|
||||||
|
new SubFunctionPattern("y"),
|
||||||
|
new SubFunctionPattern("z")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new NegativePattern(new SumSubtractionPattern(
|
||||||
|
new SubFunctionPattern("a_123"),
|
||||||
|
new DivisionPattern(
|
||||||
|
new NumberPattern(new BigDecimal(3)),
|
||||||
|
new NumberPattern(new BigDecimal("2.2"))
|
||||||
|
)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
// x^a_123 = cos(pi) - log(e, e)
|
||||||
|
final Pattern replacement1 = new EquationPattern(
|
||||||
|
new PowerPattern(
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new SubFunctionPattern("a_123")
|
||||||
|
),
|
||||||
|
new SubtractionPattern(
|
||||||
|
new CosinePattern(new ConstantPattern(MathematicalSymbols.PI)),
|
||||||
|
new LogarithmPattern(
|
||||||
|
new ConstantPattern(MathematicalSymbols.EULER_NUMBER),
|
||||||
|
new ConstantPattern(MathematicalSymbols.EULER_NUMBER)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final Pattern replacement2 = new UndefinedPattern();
|
||||||
|
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
|
||||||
|
"TestRule_123",
|
||||||
|
RuleType.REDUCTION,
|
||||||
|
target,
|
||||||
|
replacement1,
|
||||||
|
replacement2
|
||||||
|
));
|
||||||
|
|
||||||
|
assertEquals(expected, parser.parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validRuleNoReplacements() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(PLUS, "+", 0),
|
||||||
|
new Token(IDENTIFIER, "y", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
|
||||||
|
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
|
||||||
|
"test",
|
||||||
|
RuleType.EXISTENCE,
|
||||||
|
new SumPattern(
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new SubFunctionPattern("y")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
assertEquals(expected, parser.parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validRuleOneReplacement() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(MINUS, "-", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(TIMES, "*", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(MINUS, "-", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(POWER, "^", 0),
|
||||||
|
new Token(NUMBER, "2", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
|
||||||
|
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
|
||||||
|
"test",
|
||||||
|
RuleType.REDUCTION,
|
||||||
|
new MultiplicationPattern(
|
||||||
|
new NegativePattern(new SubFunctionPattern("x")),
|
||||||
|
new SubFunctionPattern("x")
|
||||||
|
),
|
||||||
|
new NegativePattern(new PowerPattern(
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new NumberPattern(new BigDecimal(2))
|
||||||
|
))
|
||||||
|
));
|
||||||
|
assertEquals(expected, parser.parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validRuleOneReplacementBrackets() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(TIMES, "*", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(POWER, "^", 0),
|
||||||
|
new Token(NUMBER, "2", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
|
||||||
|
final List<PatternRule> expected = Collections.singletonList(new PatternRule(
|
||||||
|
"test",
|
||||||
|
RuleType.REDUCTION,
|
||||||
|
new MultiplicationPattern(
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new SubFunctionPattern("x")
|
||||||
|
),
|
||||||
|
new PowerPattern(
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new NumberPattern(new BigDecimal(2))
|
||||||
|
)
|
||||||
|
));
|
||||||
|
assertEquals(expected, parser.parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleValidRules() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(IDENTIFIER, "test1", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(PLUS, "+", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
|
||||||
|
new Token(EXPANSION, "expansion", 0),
|
||||||
|
new Token(IDENTIFIER, "test2", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(MINUS, "-", 0),
|
||||||
|
new Token(MINUS, "-", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
|
||||||
|
new Token(CALCULATION, "calculation", 0),
|
||||||
|
new Token(IDENTIFIER, "test3", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(NUMBER, "1", 0),
|
||||||
|
new Token(PLUS, "+", 0),
|
||||||
|
new Token(NUMBER, "1", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(NUMBER, "2", 0),
|
||||||
|
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
|
||||||
|
final List<PatternRule> expected = Arrays.asList(
|
||||||
|
new PatternRule(
|
||||||
|
"test1",
|
||||||
|
RuleType.REDUCTION,
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new SubFunctionPattern("x")
|
||||||
|
),
|
||||||
|
new PatternRule(
|
||||||
|
"test2",
|
||||||
|
RuleType.EXPANSION,
|
||||||
|
new SubFunctionPattern("x"),
|
||||||
|
new NegativePattern(new NegativePattern(new SubFunctionPattern("x")))
|
||||||
|
),
|
||||||
|
new PatternRule(
|
||||||
|
"test3",
|
||||||
|
RuleType.CALCULATION,
|
||||||
|
new SumPattern(
|
||||||
|
new NumberPattern(new BigDecimal(1)),
|
||||||
|
new NumberPattern(new BigDecimal(1))
|
||||||
|
),
|
||||||
|
new NumberPattern(new BigDecimal(2))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assertEquals(expected, parser.parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The EOF token is inserted by the lexer, therefore it can only be missing
|
||||||
|
// in case of programming errors, and not directly because of user input.
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingEof() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(PLUS, "+", 0),
|
||||||
|
new Token(IDENTIFIER, "y", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void incompleteRule() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(PLUS, "+", 0),
|
||||||
|
new Token(IDENTIFIER, "y", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingRuleType() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void unexpectedTokenPrimary() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(PLUS, "+", 0),
|
||||||
|
new Token(CALCULATION, "calculation", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingRuleName() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingColon() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingArrow() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingRightBracket() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(REDUCTION, "reduction", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingOneArgFunctionLeftParen() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(SIN, "sin", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(RIGHT_PAREN, ")", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingOneArgFunctionRightParen() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(SIN, "sin", 0),
|
||||||
|
new Token(LEFT_PAREN, "(", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingTwoArgFunctionLeftParen() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(LOG, "log", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(COMMA, ",", 0),
|
||||||
|
new Token(IDENTIFIER, "y", 0),
|
||||||
|
new Token(RIGHT_PAREN, ")", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingTwoArgFunctionComma() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(LOG, "log", 0),
|
||||||
|
new Token(LEFT_PAREN, "(", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(IDENTIFIER, "y", 0),
|
||||||
|
new Token(RIGHT_PAREN, ")", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingTwoArgFunctionRightParen() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(LOG, "log", 0),
|
||||||
|
new Token(LEFT_PAREN, "(", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(COMMA, ",", 0),
|
||||||
|
new Token(IDENTIFIER, "y", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void missingExpressionRightParen() {
|
||||||
|
final List<Token> tokens = Arrays.asList(
|
||||||
|
new Token(EXISTENCE, "existence", 0),
|
||||||
|
new Token(IDENTIFIER, "test", 0),
|
||||||
|
new Token(COLON, ":", 0),
|
||||||
|
new Token(LEFT_PAREN, "(", 0),
|
||||||
|
new Token(IDENTIFIER, "x", 0),
|
||||||
|
new Token(ARROW, "->", 0),
|
||||||
|
new Token(LEFT_BRACKET, "[", 0),
|
||||||
|
new Token(RIGHT_BRACKET, "]", 0),
|
||||||
|
new Token(EOF, "", 0)
|
||||||
|
);
|
||||||
|
final Parser parser = new Parser(tokens);
|
||||||
|
parser.parse();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user