Parser { public List<PatternRule> parse() { if (used) { throw new IllegalStateException("Parser.parse can only be called once per instance"); } used = true; return rules(); } Parser(final List<Token> tokens, final Consumer<? super DslError> errorReporter); List<PatternRule> parse(); Token getSubFunctionIdentifier(final SubFunctionPattern subFunction);  }
@Test void noRules() { final List<Token> tokens = Collections.singletonList( new Token(EOF, "", 0) ); final Parser parser = new Parser(tokens, errors::add); assertEquals(Collections.emptyList(), parser.parse()); }
@Test void multipleParseCalls() { final List<Token> tokens = Collections.singletonList( new Token(EOF, "", 0) ); final Parser parser = new Parser(tokens, errors::add); parser.parse(); assertThrows(IllegalStateException.class, parser::parse); }
@Test 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, errors::add); 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")) ) )) ); 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 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, errors::add); final List<PatternRule> expected = Collections.singletonList(new PatternRule( "test", RuleType.EXISTENCE, new SumPattern( new SubFunctionPattern("x"), new SubFunctionPattern("y") ) )); assertEquals(expected, parser.parse()); }
@Test 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, errors::add); 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 void validRuleOneReplacementBrackets() { final List<Token> tokens = Arrays.asList( new Token(REDUCTION, "reduction", 0), new Token(IDENTIFIER, "test", 0), new Token(COLON, ":", 0), new Token(NUMBER, "1", 0), new Token(DIVIDE, "/", 0), new Token(LEFT_PAREN, "(", 0), new Token(IDENTIFIER, "x", 0), new Token(TIMES, "*", 0), new Token(IDENTIFIER, "x", 0), new Token(RIGHT_PAREN, ")", 0), new Token(ARROW, "->", 0), new Token(LEFT_BRACKET, "[", 0), new Token(IDENTIFIER, "x", 0), new Token(POWER, "^", 0), new Token(MINUS, "-", 0), new Token(NUMBER, "2", 0), new Token(RIGHT_BRACKET, "]", 0), new Token(EOF, "", 0) ); final Parser parser = new Parser(tokens, errors::add); final List<PatternRule> expected = Collections.singletonList(new PatternRule( "test", RuleType.REDUCTION, new DivisionPattern( new NumberPattern(new BigDecimal(1)), new MultiplicationPattern( new SubFunctionPattern("x"), new SubFunctionPattern("x") ) ), new PowerPattern( new SubFunctionPattern("x"), new NegativePattern( new NumberPattern(new BigDecimal(2)) ) ) )); assertEquals(expected, parser.parse()); }
@Test 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, errors::add); 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()); }
@Test void subFunctionIdentifiers() { final List<ReferenceEqualityToken> rule0x = new ArrayList<>(); final List<ReferenceEqualityToken> rule1x = new ArrayList<>(); final List<ReferenceEqualityToken> rule1y = new ArrayList<>(); final List<ReferenceEqualityToken> rule2x = new ArrayList<>(); final List<ReferenceEqualityToken> rule3x = new ArrayList<>(); final List<Token> tokens = Arrays.asList( new Token(REDUCTION, "reduction", 0), new Token(IDENTIFIER, "test1", 0), new Token(COLON, ":", 0), new Token(PLUS, "+", 0), addIdentifierToken(rule0x, "x"), new Token(ARROW, "->", 0), addIdentifierToken(rule0x, "x"), new Token(EXPANSION, "expansion", 0), new Token(IDENTIFIER, "test2", 0), new Token(COLON, ":", 0), addIdentifierToken(rule1x, "x"), new Token(POWER, "^", 0), new Token(MINUS, "-", 0), addIdentifierToken(rule1y, "y"), new Token(ARROW, "->", 0), new Token(NUMBER, "1", 0), new Token(DIVIDE, "/", 0), addIdentifierToken(rule1x, "x"), new Token(POWER, "^", 0), addIdentifierToken(rule1y, "y"), new Token(CALCULATION, "expansion", 0), new Token(IDENTIFIER, "test2", 0), new Token(COLON, ":", 0), addIdentifierToken(rule2x, "x"), new Token(ARROW, "->", 0), addIdentifierToken(rule2x, "x"), new Token(PLUS, "+", 0), new Token(NUMBER, "0", 0), new Token(CALCULATION, "expansion", 0), new Token(IDENTIFIER, "test2", 0), new Token(COLON, ":", 0), addIdentifierToken(rule3x, "x"), new Token(ARROW, "->", 0), addIdentifierToken(rule3x, "x"), new Token(PLUS, "+", 0), new Token(NUMBER, "0", 0), new Token(EOF, "", 0) ); final Parser parser = new Parser(tokens, errors::add); final List<PatternRule> rules = parser.parse(); assertEquals(rule0x, getSubFunctionIdentifiers(parser, rules.get(0), "x")); assertEquals(rule1x, getSubFunctionIdentifiers(parser, rules.get(1), "x")); assertEquals(rule1y, getSubFunctionIdentifiers(parser, rules.get(1), "y")); assertEquals(rule2x, getSubFunctionIdentifiers(parser, rules.get(2), "x")); assertEquals(rule3x, getSubFunctionIdentifiers(parser, rules.get(3), "x")); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(EOF, "", 0) )); assertEquals(expectedErrors, errors); }
@Test void missingRuleType() { final List<Token> tokens = Arrays.asList( new Token(IDENTIFIER, "test", 0), new Token(EOF, "", 0) ); final Parser parser = new Parser(tokens, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(IDENTIFIER, "test", 0), REDUCTION, EXPANSION, CALCULATION, EXISTENCE )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(CALCULATION, "calculation", 0) )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(COLON, ":", 0), IDENTIFIER )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(IDENTIFIER, "x", 0), COLON )); assertEquals(expectedErrors, errors); }
@Test 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, "y", 0), new Token(EOF, "", 0) ); final Parser parser = new Parser(tokens, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(IDENTIFIER, "y", 0), ARROW )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(EOF, "", 0), RIGHT_BRACKET )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(IDENTIFIER, "x", 0), LEFT_PAREN )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(ARROW, "->", 0), RIGHT_PAREN )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(IDENTIFIER, "x", 0), LEFT_PAREN )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(IDENTIFIER, "y", 0), COMMA )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(ARROW, "->", 0), RIGHT_PAREN )); assertEquals(expectedErrors, errors); }
@Test 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, errors::add); assertTrue(parser.parse().isEmpty()); final List<DslError> expectedErrors = Collections.singletonList(new UnexpectedToken( new Token(ARROW, "->", 0), RIGHT_PAREN )); assertEquals(expectedErrors, errors); }
@Test void recoveryToNextRule() { final List<Token> tokens = Arrays.asList( new Token(REDUCTION, "reduction", 0), new Token(IDENTIFIER, "test1", 0), new Token(COLON, ":", 0), new Token(IDENTIFIER, "x", 0), new Token(TIMES, "+", 0), new Token(ARROW, "->", 0), new Token(IDENTIFIER, "x", 0), new Token(EXPANSION, "expansion", 0), new Token(IDENTIFIER, "test2", 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, errors::add); final List<PatternRule> expectedRules = Collections.singletonList(new PatternRule( "test3", RuleType.CALCULATION, new SumPattern( new NumberPattern(new BigDecimal(1)), new NumberPattern(new BigDecimal(1)) ), new NumberPattern(new BigDecimal(2)) )); assertEquals(expectedRules, parser.parse()); final List<DslError> expectedErrors = Arrays.asList( new UnexpectedToken(new Token(ARROW, "->", 0)), new UnexpectedToken(new Token(IDENTIFIER, "x", 0), COLON) ); assertEquals(expectedErrors, errors); }