From e75d0d6030c5ff4fe66ba236a93d42a94bf83328 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Fri, 19 Apr 2024 12:47:57 +0900 Subject: [PATCH 1/3] [incubator-kie-drools-5854] [new-parser] improve drools-drl-parser-testsa to test with old and new parsers - Old and new parser coverage using 2 surefire test executions - Fixed Descr common property issues to keep backward compatibility - A few test cases remaining without backward compatibility ("Backward Compatibility Notes" left) because the old parser seems to be wrong. - A few expr test cases remaining without backward compatibility ("Backward Compatibility Notes" left). Error code/message don't seem to be important. Also the new parser ones are better. --- drools-drl/drools-drl-parser-tests/pom.xml | 38 ++ .../drl/parser/antlr4/DRLExprParserTest.java | 91 ++-- .../drl/parser/antlr4/DRLParserTest.java | 3 + .../antlr4/DescrCommonPropertyTest.java | 195 +++++--- .../drl/parser/antlr4/DescrDumperTest.java | 3 +- .../drl/parser/antlr4/MiscDRLParserTest.java | 422 ++++++++++-------- .../drl/parser/antlr4/ParserTestUtils.java | 37 ++ .../org/drools/drl/parser/antlr4/DRLLexer.g4 | 9 +- .../org/drools/drl/parser/antlr4/DRLParser.g4 | 30 +- .../java/org/drools/drl/parser/DrlParser.java | 4 +- .../drl/parser/antlr4/DRLVisitorImpl.java | 9 +- .../drools/drl/parser/antlr4/DescrHelper.java | 48 +- .../drools/drl/parser/antlr4/LexerHelper.java | 171 +++++++ .../drl/parser/antlr4/ParserHelper.java | 2 +- 14 files changed, 757 insertions(+), 305 deletions(-) create mode 100644 drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java create mode 100644 drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java diff --git a/drools-drl/drools-drl-parser-tests/pom.xml b/drools-drl/drools-drl-parser-tests/pom.xml index 4f3a19c8127..1e26095a3bd 100644 --- a/drools-drl/drools-drl-parser-tests/pom.xml +++ b/drools-drl/drools-drl-parser-tests/pom.xml @@ -83,4 +83,42 @@ + + + default + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + default-test + + + true + + + + + old-parser-test + + + false + + + + test + + + + + + + + diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLExprParserTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLExprParserTest.java index 3f3b256dd50..30a316e24b7 100644 --- a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLExprParserTest.java +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLExprParserTest.java @@ -30,6 +30,8 @@ import org.drools.drl.ast.descr.ConstraintConnectiveDescr; import org.drools.drl.ast.descr.RelationalExprDescr; import org.drools.drl.parser.DrlExprParser; +import org.drools.drl.parser.DrlExprParserFactory; +import org.drools.drl.parser.DrlParser; import org.drools.drl.parser.DroolsParserException; import org.drools.drl.parser.impl.Operator; import org.junit.jupiter.api.AfterEach; @@ -49,17 +51,17 @@ public class DRLExprParserTest { DrlExprParser parser; @BeforeEach - public void setUp() throws Exception { - this.parser = new Drl6ExprParserAntlr4(LanguageLevelOption.DRL6); + public void setUp() { + this.parser = DrlExprParserFactory.getDrlExprParser(LanguageLevelOption.DRL6); } @AfterEach - public void tearDown() throws Exception { + public void tearDown() { this.parser = null; } @Test - public void testSimpleExpression() throws Exception { + public void testSimpleExpression() { String source = "a > b"; ConstraintConnectiveDescr result = parser.parse( source ); assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); @@ -387,13 +389,26 @@ public void testMismatchedInput() { assertThat(parser.hasErrors()).isTrue(); assertThat(parser.getErrors()).hasSize(1); DroolsParserException exception = parser.getErrors().get(0); - assertThat(exception.getErrorCode()).isEqualTo("ERR 102"); - assertThat(exception.getLineNumber()).isEqualTo(1); - assertThat(exception.getColumn()).isEqualTo(1); - assertThat(exception.getOffset()).isEqualTo(1); - assertThat(exception.getMessage()) - .startsWithIgnoringCase("[ERR 102] Line 1:1 mismatched input '' expecting ") - .contains("TIME_INTERVAL", "DRL_STRING_LITERAL", "?/", "boolean", "byte", "char", "double", "float", "int", "long", "new", "short", "super", "DECIMAL_LITERAL", "HEX_LITERAL", "FLOAT_LITERAL", "BOOL_LITERAL", "STRING_LITERAL", "null", "(", "[", ".", "<", "!", "~", "++", "--", "+", "-", "*", "/", "IDENTIFIER"); + + // Backward Compatibility Notes: + // Antlr4 gives a different error code/message from antlr3 for this case. + // Backward compatibility doesn't seem to be required in this case. + if (DrlParser.ANTLR4_PARSER_ENABLED) { + assertThat(exception.getErrorCode()).isEqualTo("ERR 102"); + assertThat(exception.getLineNumber()).isEqualTo(1); + assertThat(exception.getColumn()).isEqualTo(1); + assertThat(exception.getOffset()).isEqualTo(1); + assertThat(exception.getMessage()) + .startsWithIgnoringCase("[ERR 102] Line 1:1 mismatched input '' expecting ") + .contains("TIME_INTERVAL", "DRL_STRING_LITERAL", "?/", "boolean", "byte", "char", "double", "float", "int", "long", "new", "short", "super", "DECIMAL_LITERAL", "HEX_LITERAL", "FLOAT_LITERAL", "BOOL_LITERAL", "STRING_LITERAL", "null", "(", "[", ".", "<", "!", "~", "++", "--", "+", "-", "*", "/", "IDENTIFIER"); + } else { + assertThat(exception.getErrorCode()).isEqualTo("ERR 101"); + assertThat(exception.getLineNumber()).isEqualTo(1); + assertThat(exception.getColumn()).isEqualTo(1); + assertThat(exception.getOffset()).isEqualTo(1); + assertThat(exception.getMessage()) + .isEqualToIgnoringCase("[ERR 101] Line 1:1 no viable alternative at input ''"); + } } @Test @@ -403,26 +418,48 @@ public void testExtraneousInput() { assertThat(parser.hasErrors()).isTrue(); assertThat(parser.getErrors()).hasSize(1); DroolsParserException exception = parser.getErrors().get(0); - assertThat(exception.getErrorCode()).isEqualTo("ERR 109"); - assertThat(exception.getLineNumber()).isEqualTo(1); - assertThat(exception.getColumn()).isEqualTo(3); - assertThat(exception.getOffset()).isEqualTo(3); - assertThat(exception.getMessage()) - .startsWithIgnoringCase("[ERR 109] Line 1:3 extraneous input ';' expecting ") - .contains("TIME_INTERVAL", "DRL_STRING_LITERAL", "?/", "boolean", "byte", "char", "double", "float", "int", "long", "new", "short", "super", "DECIMAL_LITERAL", "HEX_LITERAL", "FLOAT_LITERAL", "BOOL_LITERAL", "STRING_LITERAL", "null", "(", "[", ".", "<", "!", "~", "++", "--", "+", "-", "*", "/", "IDENTIFIER"); + + // Backward Compatibility Notes: + // Antlr4 gives a different error code/message from antlr3 for this case. + // Backward compatibility doesn't seem to be required in this case. + if (DrlParser.ANTLR4_PARSER_ENABLED) { + assertThat(exception.getErrorCode()).isEqualTo("ERR 109"); + assertThat(exception.getLineNumber()).isEqualTo(1); + assertThat(exception.getColumn()).isEqualTo(3); + assertThat(exception.getOffset()).isEqualTo(3); + assertThat(exception.getMessage()) + .startsWithIgnoringCase("[ERR 109] Line 1:3 extraneous input ';' expecting ") + .contains("TIME_INTERVAL", "DRL_STRING_LITERAL", "?/", "boolean", "byte", "char", "double", "float", "int", "long", "new", "short", "super", "DECIMAL_LITERAL", "HEX_LITERAL", "FLOAT_LITERAL", "BOOL_LITERAL", "STRING_LITERAL", "null", "(", "[", ".", "<", "!", "~", "++", "--", "+", "-", "*", "/", "IDENTIFIER"); + } else { + assertThat(exception.getErrorCode()).isEqualTo("ERR 101"); + assertThat(exception.getLineNumber()).isEqualTo(1); + assertThat(exception.getColumn()).isEqualTo(3); + assertThat(exception.getOffset()).isEqualTo(3); + assertThat(exception.getMessage()) + .isEqualToIgnoringCase("[ERR 101] Line 1:3 no viable alternative at input ';'"); + } } + @Test public void testNoViableAlt() { String source = "x.int"; parser.parse(source); - assertThat(parser.hasErrors()).isTrue(); - assertThat(parser.getErrors()).hasSize(1); - DroolsParserException exception = parser.getErrors().get(0); - assertThat(exception.getErrorCode()).isEqualTo("ERR 101"); - assertThat(exception.getLineNumber()).isEqualTo(1); - assertThat(exception.getColumn()).isEqualTo(2); - assertThat(exception.getOffset()).isEqualTo(2); - assertThat(exception.getMessage()) - .isEqualToIgnoringCase("[ERR 101] Line 1:2 no viable alternative at input '.int'"); + + // Backward Compatibility Notes: + // Old expr parser (DRL6Expressions) allows this expression because it's too tolerant (fail at runtime anyway). + // Backward compatibility doesn't seem to be required in this case. (But we may align with the old tolerant behavior.) + if (DrlParser.ANTLR4_PARSER_ENABLED) { + assertThat(parser.hasErrors()).isTrue(); + assertThat(parser.getErrors()).hasSize(1); + DroolsParserException exception = parser.getErrors().get(0); + assertThat(exception.getErrorCode()).isEqualTo("ERR 101"); + assertThat(exception.getLineNumber()).isEqualTo(1); + assertThat(exception.getColumn()).isEqualTo(2); + assertThat(exception.getOffset()).isEqualTo(2); + assertThat(exception.getMessage()) + .isEqualToIgnoringCase("[ERR 101] Line 1:2 no viable alternative at input '.int'"); + } else { + assertThat(parser.hasErrors()).isFalse(); + } } } diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLParserTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLParserTest.java index 8aecc0fb056..f792effd235 100644 --- a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLParserTest.java +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DRLParserTest.java @@ -17,6 +17,9 @@ import static org.drools.drl.parser.antlr4.DRLParserHelper.createDrlParser; import static org.drools.drl.parser.antlr4.DRLParserHelper.parse; +/** + * This test class is specific to new antlr4 parser. + */ class DRLParserTest { private static final String drl = diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java index bc3010e6987..2bd15f79c01 100644 --- a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java @@ -1,3 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package org.drools.drl.parser.antlr4; import org.drools.drl.ast.descr.AccumulateDescr; @@ -30,6 +48,8 @@ import org.drools.drl.ast.descr.UnitDescr; import org.drools.drl.ast.descr.WindowDeclarationDescr; import org.drools.drl.ast.descr.WindowReferenceDescr; +import org.drools.drl.parser.DrlParser; +import org.drools.drl.parser.DroolsParserException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -41,15 +61,21 @@ */ class DescrCommonPropertyTest { - private DRLParserWrapper parser; + private DrlParser parser; @BeforeEach public void setUp() { - parser = new DRLParserWrapper(); + parser = ParserTestUtils.getParser(); } - @AfterEach - public void tearDown() { + private PackageDescr parseAndGetPackageDescr(String drl) { + try { + PackageDescr pkg = parser.parse(null, drl); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + return pkg; + } catch (DroolsParserException e) { + throw new RuntimeException(e); + } } private void assertProperties(BaseDescr descr, int startCharacter, int endCharacter, int line, int column, int endLine, int endColumn) { @@ -64,8 +90,8 @@ private void assertProperties(BaseDescr descr, int startCharacter, int endCharac @Test void packageDescr() { final String source = "package foo.bar.baz"; - final PackageDescr pkg = parser.parse(source); - assertProperties(pkg, 0, 18, 1, 0, 1, 18); + final PackageDescr pkg = parseAndGetPackageDescr(source); + assertProperties(pkg, 0, 19, 1, 0, 1, 18); } @Test @@ -74,17 +100,18 @@ void ruleDescr() { " when\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); - assertProperties(rule, 0, 30, 1, 0, 4, 2); + assertProperties(rule, 0, 31, 1, 0, 4, 2); } @Test void unitDescr() { - final String source = "unit Foo;"; - final PackageDescr pkg = parser.parse(source); + final String source = "package abc;\n" + + "unit Foo;"; + final PackageDescr pkg = parseAndGetPackageDescr(source); final UnitDescr unit = pkg.getUnit(); - assertProperties(unit, 0, 8, 1, 0, 1, 8); + assertProperties(unit, 13, 22, 2, 0, 2, 8); } @Test @@ -92,9 +119,9 @@ void queryDescr() { final String source = "query \"MyQuery\"\n" + " Foo()\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final QueryDescr query = (QueryDescr) pkg.getRules().get(0); - assertProperties(query, 0, 26, 1, 0, 3, 2); + assertProperties(query, 0, 27, 1, 0, 3, 2); } @Test @@ -102,41 +129,41 @@ void functionDescr() { final String source = "function void myFunction(String data) {\n" + " foo();\n" + "}"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final FunctionDescr function = pkg.getFunctions().get(0); - assertProperties(function, 0, 49, 1, 0, 3, 0); + assertProperties(function, 0, 50, 1, 0, 3, 0); } @Test void globalDescr() { final String source = "global java.util.List myList"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final GlobalDescr global = pkg.getGlobals().get(0); - assertProperties(global, 0, 27, 1, 0, 1, 27); + assertProperties(global, 0, 28, 1, 0, 1, 27); } @Test void functionImportDescr() { final String source = "import function org.drools.core.util.DateUtils.*"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final FunctionImportDescr functionImport = pkg.getFunctionImports().get(0); - assertProperties(functionImport, 0, 47, 1, 0, 1, 47); + assertProperties(functionImport, 0, 48, 1, 0, 1, 47); } @Test void importDescr() { final String source = "import org.drools.core.util.DateUtils"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final ImportDescr importDescr = pkg.getImports().get(0); - assertProperties(importDescr, 0, 36, 1, 0, 1, 36); + assertProperties(importDescr, 0, 37, 1, 0, 1, 36); } @Test void accumulateImportDescr() { final String source = "import accumulate org.example.MyAccUtils.sum mySum"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final AccumulateImportDescr accumulateImport = pkg.getAccumulateImports().get(0); - assertProperties(accumulateImport, 0, 49, 1, 0, 1, 49); + assertProperties(accumulateImport, 0, 50, 1, 0, 1, 49); } @Test @@ -144,11 +171,11 @@ void typeDeclarationDescr() { final String source = "declare MyType\n" + " name : String\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final TypeDeclarationDescr typeDeclaration = pkg.getTypeDeclarations().get(0); // startCharacter = 8 looks a little odd ("declare" is not included in the Descr), but it keeps the same as the old implementation. We may change it in the future. - assertProperties(typeDeclaration, 8, 33, 1, 8, 3, 2); + assertProperties(typeDeclaration, 8, 34, 1, 8, 3, 2); } @Test @@ -156,9 +183,9 @@ void entryPointDeclarationDescr() { final String source = "declare entry-point MyEntryPoint\n" + " @foo( true )\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final EntryPointDeclarationDescr entryPointDeclaration = pkg.getEntryPointDeclarations().stream().findFirst().get(); - assertProperties(entryPointDeclaration, 8, 50, 1, 8, 3, 2); + assertProperties(entryPointDeclaration, 8, 51, 1, 8, 3, 2); } @Test @@ -168,9 +195,9 @@ void windowDeclarationDescr() { " over window:length( 10, $s.symbol )\n" + " from entry-point stStream\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final WindowDeclarationDescr windowDeclaration = pkg.getWindowDeclarations().stream().findFirst().get(); - assertProperties(windowDeclaration, 8, 139, 1, 8, 5, 2); + assertProperties(windowDeclaration, 8, 140, 1, 8, 5, 2); } @Test @@ -181,10 +208,10 @@ void annotationDescr() { " $p : Person( name == \"Mario\" ) @watch(!*, age)\n" + "then\n" + "end\n"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); AnnotationDescr annotation = rule.getLhs().getAllPatternDescr().get(0).getAnnotations().stream().findFirst().get(); - assertProperties(annotation, 65, 79, 4, 33, 4, 47); + assertProperties(annotation, 65, 80, 4, 33, 4, 47); } @Test @@ -192,10 +219,17 @@ void typeFieldDescr() { final String source = "declare MyType\n" + " name : String\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final TypeDeclarationDescr typeDeclaration = pkg.getTypeDeclarations().get(0); TypeFieldDescr typeField = typeDeclaration.getFields().get("name"); - assertProperties(typeField, 17, 29, 2, 2, 2, 14); + // Backward Compatibility Notes: + // The old DRL6Parser uses only the attribute value token for common properties (seem to be wrong), so startCharacter and column are different between old and new parser. + // Backward compatibility doesn't seem to be required in this case. + if (DrlParser.ANTLR4_PARSER_ENABLED) { + assertProperties(typeField, 17, 30, 2, 2, 2, 14); + } else { + assertProperties(typeField, 24, 30, 2, 9, 2, 14); + } } @Test @@ -206,10 +240,19 @@ void attributeDescr() { " when\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); - assertProperties(rule.getAttributes().get("salience"), 10, 20, 2, 2, 2, 12); - assertProperties(rule.getAttributes().get("agenda-group"), 24, 46, 3, 2, 3, 24); + + // Backward Compatibility Notes: + // The old DRL6Parser uses only the attribute value token for common properties (seem to be wrong), so startCharacter and column are different between old and new parser. + // Backward compatibility doesn't seem to be required in this case. (If do it, the code would be unnecessarily complicated.) + if (DrlParser.ANTLR4_PARSER_ENABLED) { + assertProperties(rule.getAttributes().get("salience"), 10, 21, 2, 2, 2, 12); + assertProperties(rule.getAttributes().get("agenda-group"), 24, 47, 3, 2, 3, 24); + } else { + assertProperties(rule.getAttributes().get("salience"), 19, 21, 2, 11, 2, 12); + assertProperties(rule.getAttributes().get("agenda-group"), 37, 47, 3, 15, 3, 24); + } } @Test @@ -219,10 +262,10 @@ void patternDescr() { " $p : Person( name == \"Mario\" )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); - assertProperties(pattern, 19, 48, 3, 4, 3, 33); + assertProperties(pattern, 19, 49, 3, 4, 3, 33); } @Test @@ -232,10 +275,10 @@ void orDescr() { " ( $p : Person( name == \"Mario\" ) or $p : Person( name == \"Luigi\" ) )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); OrDescr or = (OrDescr) rule.getLhs().getDescrs().get(0); - assertProperties(or, 21, 84, 3, 6, 3, 69); + assertProperties(or, 21, 85, 3, 6, 3, 69); } @Test @@ -245,10 +288,10 @@ void andDescr() { " ( $p1 : Person( name == \"Mario\" ) and $p2 : Person( name == \"Luigi\" ) )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); AndDescr and = rule.getLhs(); - assertProperties(and, 21, 87, 3, 6, 3, 72); + assertProperties(and, 19, 90, 3, 4, 3, 74); } @Test @@ -258,10 +301,10 @@ void forallDescr() { " forall( $p : Person( name == \"Mario\" ) )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); ForallDescr forall = (ForallDescr) rule.getLhs().getDescrs().get(0); - assertProperties(forall, 19, 58, 3, 4, 3, 43); + assertProperties(forall, 19, 59, 3, 4, 3, 43); } @Test @@ -271,23 +314,23 @@ void accumulateDescr() { " accumulate( $p : Person( name == \"Mario\" ), $sum : sum($p.getAge()) )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); AccumulateDescr accumulate = (AccumulateDescr) pattern.getSource(); - assertProperties(accumulate, 19, 87, 3, 4, 3, 72); + assertProperties(accumulate, 19, 88, 3, 4, 3, 72); } @Test void behaviorDescr() { final String source = "rule X when StockTick( symbol==\"ACME\") over window:length(10) then end";; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); BehaviorDescr behavior = pattern.getBehaviors().get(0); // "over" is not included in BehaviorDescr - assertProperties(behavior, 44, 60, 1, 44, 1, 60); + assertProperties(behavior, 44, 61, 1, 44, 1, 60); } @Test @@ -298,13 +341,20 @@ void fromDescr() { " Child() from $children\n" + " then\n" + "end";; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(1); FromDescr from = (FromDescr) pattern.getSource(); - // "from" is not included in FromDescr - assertProperties(from, 64, 72, 4, 17, 4, 25); + // Backward Compatibility Notes: + // The old DRL6Parser doesn't populate common properties of FromDescr (seem to be wrong). + // Backward compatibility doesn't seem to be required in this case. + if (DrlParser.ANTLR4_PARSER_ENABLED) { + // "from" is not included in FromDescr + assertProperties(from, 64, 73, 4, 17, 4, 25); + } else { + assertProperties(from, -1, -1, -1, -1, -1, -1); + } } @Test @@ -314,12 +364,12 @@ void collectDescr() { " ArrayList() from collect( Person( age > 21 ) );\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); CollectDescr collect = (CollectDescr) pattern.getSource(); - assertProperties(collect, 35, 63, 3, 21, 3, 49); + assertProperties(collect, 35, 64, 3, 21, 3, 49); } @Test @@ -329,11 +379,19 @@ void entryPointDescr() { " StockTick() from entry-point \"stream\"\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); EntryPointDescr entryPoint = (EntryPointDescr) pattern.getSource(); - assertProperties(entryPoint, 35, 54, 3, 21, 3, 40); + + // Backward Compatibility Notes: + // The old DRL6Parser doesn't populate common properties of EntryPointDescr (seem to be wrong). + // Backward compatibility doesn't seem to be required in this case. + if (DrlParser.ANTLR4_PARSER_ENABLED) { + assertProperties(entryPoint, 35, 55, 3, 21, 3, 40); + } else { + assertProperties(entryPoint, -1, -1, -1, -1, -1, -1); + } } @Test @@ -343,11 +401,18 @@ void windowReferenceDescr() { " StockTick() from window MyWindow\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); WindowReferenceDescr windowReference = (WindowReferenceDescr) pattern.getSource(); - assertProperties(windowReference, 35, 49, 3, 21, 3, 35); + // Backward Compatibility Notes: + // The old DRL6Parser doesn't populate common properties of WindowReferenceDescr (seem to be wrong). + // Backward compatibility doesn't seem to be required in this case. + if (DrlParser.ANTLR4_PARSER_ENABLED) { + assertProperties(windowReference, 35, 50, 3, 21, 3, 35); + } else { + assertProperties(windowReference, -1, -1, -1, -1, -1, -1); + } } @Test @@ -357,11 +422,11 @@ void exprConstraintDescr() { " Person( age > 21 )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); ExprConstraintDescr exprConstraint = (ExprConstraintDescr) pattern.getDescrs().get(0); - assertProperties(exprConstraint, 26, 33, 3, 12, 3, 19); + assertProperties(exprConstraint, 26, 33, 3, 12, 3, 18); } @Test @@ -371,10 +436,10 @@ void existDescr() { " exists( Person( age > 21 ) )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); ExistsDescr exists = (ExistsDescr) rule.getLhs().getDescrs().get(0); - assertProperties(exists, 18, 45, 3, 4, 3, 31); + assertProperties(exists, 18, 46, 3, 4, 3, 31); } @Test @@ -384,10 +449,10 @@ void notDescr() { " not( Person( age > 21 ) )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); NotDescr not = (NotDescr) rule.getLhs().getDescrs().get(0); - assertProperties(not, 18, 42, 3, 4, 3, 28); + assertProperties(not, 18, 43, 3, 4, 3, 28); } @Test @@ -397,9 +462,9 @@ void evalDescr() { " eval( 1 + 1 == 2 )\n" + " then\n" + "end"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); EvalDescr eval = (EvalDescr) rule.getLhs().getDescrs().get(0); - assertProperties(eval, 18, 35, 3, 4, 3, 21); + assertProperties(eval, 18, 36, 3, 4, 3, 21); } } diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrDumperTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrDumperTest.java index ab644f7dafb..1d038907c89 100644 --- a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrDumperTest.java +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrDumperTest.java @@ -26,6 +26,7 @@ import org.drools.drl.ast.descr.BindingDescr; import org.drools.drl.ast.descr.ConstraintConnectiveDescr; import org.drools.drl.parser.DrlExprParser; +import org.drools.drl.parser.DrlExprParserFactory; import org.drools.mvel.evaluators.MatchesEvaluatorsDefinition; import org.drools.mvel.evaluators.SetEvaluatorsDefinition; import org.junit.jupiter.api.BeforeEach; @@ -358,7 +359,7 @@ public void testProcessImplicitConstraints() throws Exception { } public ConstraintConnectiveDescr parse( final String constraint ) { - DrlExprParser parser = new Drl6ExprParserAntlr4(LanguageLevelOption.DRL6); + DrlExprParser parser = DrlExprParserFactory.getDrlExprParser(LanguageLevelOption.DRL6); ConstraintConnectiveDescr result = parser.parse( constraint ); assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java index 14c30ca67cf..e617a4dfb49 100644 --- a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/MiscDRLParserTest.java @@ -2,6 +2,8 @@ import java.io.BufferedReader; import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -42,6 +44,8 @@ import org.drools.drl.ast.descr.TypeDeclarationDescr; import org.drools.drl.ast.descr.TypeFieldDescr; import org.drools.drl.ast.descr.WindowDeclarationDescr; +import org.drools.drl.parser.DrlParser; +import org.drools.drl.parser.DroolsParserException; import org.drools.drl.parser.impl.Operator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -57,19 +61,20 @@ */ class MiscDRLParserTest { - private DRLParserWrapper parser; + private DrlParser parser; @BeforeEach public void setUp() { - parser = new DRLParserWrapper(); + parser = ParserTestUtils.getParser(); } - @AfterEach - public void tearDown() { - } - - private String readResource(final String filename) throws Exception { - Path path = Paths.get(getClass().getResource(filename).toURI()); + private String readResource(final String filename) { + Path path; + try { + path = Paths.get(getClass().getResource(filename).toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } final StringBuilder sb = new StringBuilder(); try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { for (String line; (line = br.readLine()) != null; ) { @@ -77,67 +82,82 @@ private String readResource(final String filename) throws Exception { sb.append("\n"); } } catch (IOException e) { - e.printStackTrace(); + throw new UncheckedIOException(e); } return sb.toString(); } + private PackageDescr parseAndGetPackageDescr(String drl) { + try { + PackageDescr pkg = parser.parse(null, drl); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + return pkg; + } catch (DroolsParserException e) { + throw new RuntimeException(e); + } + } + + private PackageDescr parseAndGetPackageDescrWithoutErrorCheck(String drl) { + try { + return parser.parse(null, drl); + } catch (DroolsParserException e) { + throw new RuntimeException(e); + } + } + private RuleDescr parseAndGetFirstRuleDescr(String drl) { - PackageDescr pkg = parser.parse(drl); - assertThat(parser.hasErrors()).as(parser.getErrorMessages().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(drl); assertThat(pkg.getRules()).isNotEmpty(); return pkg.getRules().get(0); } - private RuleDescr parseAndGetFirstRuleDescrFromFile(String filename) throws Exception { - return parseAndGetFirstRuleDescr(readResource(filename)); + private PackageDescr parseAndGetPackageDescrFromFile(String filename) { + return parseAndGetPackageDescr(readResource(filename)); } - private PackageDescr parseAndGetPackageDescrFromFile(String filename) throws Exception { - return parser.parse(readResource(filename)); + private RuleDescr parseAndGetFirstRuleDescrFromFile(String filename) { + return parseAndGetFirstRuleDescr(readResource(filename)); } private QueryDescr parseAndGetFirstQueryDescr(String drl) { - PackageDescr pkg = parser.parse(drl); - assertThat(parser.hasErrors()).as(parser.getErrorMessages().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(drl); assertThat(pkg.getRules()).isNotEmpty(); Optional optQuery = pkg.getRules().stream().filter(QueryDescr.class::isInstance).map(QueryDescr.class::cast).findFirst(); assertThat(optQuery).isPresent(); return optQuery.get(); } - private QueryDescr parseAndGetFirstQueryDescrFromFile(String filename) throws Exception { + private QueryDescr parseAndGetFirstQueryDescrFromFile(String filename) { return parseAndGetFirstQueryDescr(readResource(filename)); } @Test void parse_validPackage() { final String source = "package foo.bar.baz"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("foo.bar.baz"); } @Test void parse_packageWithErrorNode() { final String source = "package 12 foo.bar.baz"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescrWithoutErrorCheck(source); assertThat(parser.hasErrors()).isTrue(); - assertThat(pkg.getName()).isEqualTo("foo.bar.baz"); + assertThat(pkg).isNull(); } @Test void parse_packageWithAllErrorNode() { final String source = "package 12 12312 231"; - final PackageDescr pkg = parser.parse(source); + final PackageDescr pkg = parseAndGetPackageDescrWithoutErrorCheck(source); assertThat(parser.hasErrors()).isTrue(); - assertThat(pkg.getName()).isEmpty(); + assertThat(pkg).isNull(); } @Test void parse_import() { final String source = "package foo; import com.foo.Bar; import com.foo.Baz;"; - PackageDescr pkg = parser.parse(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg =parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("foo"); assertThat(pkg.getImports()).hasSize(2); ImportDescr impdescr = pkg.getImports().get(0); @@ -158,26 +178,25 @@ void parse_functionImport() { "import function java.lang.Math.min;\n" + "import foo.bar.*\n" + "import baz.Baz"; - PackageDescr pkg = parser.parse(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("foo"); assertThat(pkg.getImports()).hasSize(2); ImportDescr impdescr = pkg.getImports().get(0); assertThat(impdescr.getTarget()).isEqualTo("foo.bar.*"); assertThat(impdescr.getStartCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget())); - assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length() - 1); + assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length()); impdescr = pkg.getImports().get(1); assertThat(impdescr.getTarget()).isEqualTo("baz.Baz"); assertThat(impdescr.getStartCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget())); - assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length() - 1); + assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length()); assertThat(pkg.getFunctionImports()).hasSize(2); impdescr = pkg.getFunctionImports().get(0); assertThat(impdescr.getTarget()).isEqualTo("java.lang.Math.max"); assertThat(impdescr.getStartCharacter()).isEqualTo(source.indexOf("import function " + impdescr.getTarget())); - assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import function " + impdescr.getTarget()) + ("import function " + impdescr.getTarget()).length() - 1); + assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import function " + impdescr.getTarget()) + ("import function " + impdescr.getTarget()).length()); impdescr = pkg.getFunctionImports().get(1); assertThat(impdescr.getTarget()).isEqualTo("java.lang.Math.min"); @@ -191,15 +210,14 @@ void parse_globalWithComplexType() { "import com.foo.Bar\n" + "global java.util.List> aList;\n" + "global Integer aNumber"; - PackageDescr pkg = parser.parse(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("foo.bar.baz"); assertThat(pkg.getImports()).hasSize(1); ImportDescr impdescr = pkg.getImports().get(0); assertThat(impdescr.getTarget()).isEqualTo("com.foo.Bar"); assertThat(impdescr.getStartCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget())); - assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length() - 1); + assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length()); assertThat(pkg.getGlobals()).hasSize(2); @@ -215,13 +233,13 @@ void parse_globalWithComplexType() { assertThat(global.getIdentifier()).isEqualTo("aNumber"); assertThat(global.getStartCharacter()).isEqualTo(source.indexOf("global " + global.getType())); assertThat(global.getEndCharacter()).isEqualTo(source.indexOf("global " + global.getType() + " " + global.getIdentifier()) + - ("global " + global.getType() + " " + global.getIdentifier()).length() - 1); + ("global " + global.getType() + " " + global.getIdentifier()).length()); } @Test void parse_globalWithOrWithoutSemi() throws Exception { String source = readResource("globals.drl"); - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getRules()).hasSize(1); @@ -242,7 +260,7 @@ void parse_globalWithOrWithoutSemi() throws Exception { @Test void parse_functionImportWithNotExist() throws Exception { String source = readResource("test_FunctionImport.drl"); - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getFunctionImports()).hasSize(2); @@ -265,9 +283,7 @@ void parse_fromComplexAccessor() { "\n" + " o.addError(\"Invalid customer id\"); \n" + "end \n"; - PackageDescr pkg = parser.parse(source); - - assertThat(parser.hasErrors()).as(parser.getErrorMessages().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); assertThat(rule.getName()).isEqualTo("Invalid customer id"); @@ -291,8 +307,7 @@ void parse_fromWithInlineList() { " System.err.println(\"Invalid customer id found!\"); \n" + " o.addError(\"Invalid customer id\"); \n" + "end \n"; - PackageDescr pkg = parser.parse(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); assertThat(rule.getName()).isEqualTo("XYZ"); @@ -311,13 +326,11 @@ void parse_fromWithInlineListMethod() { " System.err.println(\"Invalid customer id found!\"); \n" + " o.addError(\"Invalid customer id\"); \n" + "end \n"; - PackageDescr pkg = parser.parse(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); assertThat(rule.getName()).isEqualTo("XYZ"); - assertThat(parser.hasErrors()).isFalse(); PatternDescr number = (PatternDescr) rule.getLhs().getDescrs().get(1); assertThat(((FromDescr) number.getSource()).getDataSource().toString()).isEqualToIgnoringWhitespace("[1, 2, 3].sublist(1, 2)"); @@ -333,14 +346,11 @@ void parse_fromWithInlineListIndex() { " System.err.println(\"Invalid customer id found!\"); \n" + " o.addError(\"Invalid customer id\"); \n" + "end \n"; - PackageDescr pkg = parser.parse(source); - - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); assertThat(rule.getName()).isEqualTo("XYZ"); - assertThat(parser.hasErrors()).isFalse(); PatternDescr number = (PatternDescr) rule.getLhs().getDescrs().get(1); assertThat(((FromDescr) number.getSource()).getDataSource().toString()).isEqualToIgnoringWhitespace("[1, 2, 3][1]"); } @@ -352,7 +362,7 @@ void parse_ruleWithoutEnd() { " o: Order( ) \n" + " then \n" + " System.err.println(\"Invalid customer id found!\"); \n"; - parser.parse(source); + parseAndGetPackageDescrWithoutErrorCheck(source); assertThat(parser.hasErrors()).isTrue(); } @@ -367,8 +377,7 @@ void parse_orWithSpecialBind() { " then \n" + " System.out.println( \"Rule: A and (B or C or D) Fired. pdo1: \" + pdo1 + \" pdo2: \"+ pdo2); \n" + "end\n"; - PackageDescr pkg = parser.parse(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); AndDescr lhs = rule.getLhs(); @@ -387,7 +396,7 @@ void parse_orWithSpecialBind() { @Test void parse_compatibleRestriction() { String source = "package com.sample rule test when Test( ( text == null || text2 matches \"\" ) ) then end"; - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("com.sample"); RuleDescr rule = pkg.getRules().get(0); @@ -399,7 +408,7 @@ void parse_compatibleRestriction() { @Test void parse_simpleConstraint() { String source = "package com.sample rule test when Cheese( type == 'stilton', price > 10 ) then end"; - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("com.sample"); RuleDescr rule = pkg.getRules().get(0); @@ -417,7 +426,7 @@ void parse_simpleConstraint() { @Test void parse_stringEscapes() { String source = "package com.sample rule test when Cheese( type matches \"\\..*\\\\.\" ) then end"; - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("com.sample"); RuleDescr rule = pkg.getRules().get(0); assertThat(rule.getName()).isEqualTo("test"); @@ -433,7 +442,7 @@ void parse_stringEscapes() { @Test void parse_dialectWithSingleQuotation() { final String source = "dialect 'mvel'"; - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); AttributeDescr attr = pkg.getAttributes().get(0); assertThat(attr.getName()).isEqualTo("dialect"); assertThat(attr.getValue()).isEqualTo("mvel"); @@ -442,7 +451,7 @@ void parse_dialectWithSingleQuotation() { @Test void parse_dialectWithDoubleQuotation() { final String source = "dialect \"mvel\""; - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); AttributeDescr attr = pkg.getAttributes().get(0); assertThat(attr.getName()).isEqualTo("dialect"); assertThat(attr.getValue()).isEqualTo("mvel"); @@ -451,11 +460,13 @@ void parse_dialectWithDoubleQuotation() { @Test void parse_emptyRuleWithoutWhen() throws Exception { String source = readResource("empty_rule.drl"); // without WHEN - parser.parse(source); + RuleDescr ruleDescr = parseAndGetFirstRuleDescr(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isTrue(); + assertThat(ruleDescr).isNotNull(); - // Note : RuleParserTest.testEmptyRule allows this DRL, but I think is doesn't make sense to pass this DRL + assertThat(ruleDescr.getName()).isEqualTo("empty"); + assertThat(ruleDescr.getLhs()).isNotNull(); + assertThat(ruleDescr.getConsequence()).isNotNull(); } @Test @@ -465,17 +476,15 @@ void parse_keywordCollisions() throws Exception { // Note: eol_funny_business.drl is modified from the one under drools-test-coverage to be more realistic. // e.g. "package" is not allowed in a package value in Java, so it doesn't make sense to test. (Right to raise a parser error) - PackageDescr pkg = parser.parse(source); - - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); - + PackageDescr pkg = parseAndGetPackageDescr(source); + assertThat(pkg.getRules()).hasSize(1); } @Test void parse_ternaryExpression() throws Exception { String source = readResource("ternary_expression.drl"); - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); final RuleDescr rule = pkg.getRules().get(0); assertThat(pkg.getRules()).hasSize(1); @@ -491,7 +500,7 @@ void parse_functionWithArrays() throws Exception { // new String[3] {"a","b","c"} is invalid in Java (Cannot define dimension expressions when an array initializer is provided) // , so it doesn't make sense to test. (Right to raise a parser error) - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); assertThat(pkg.getName()).isEqualTo("foo"); assertThat(pkg.getRules()).hasSize(1); @@ -510,9 +519,8 @@ void parse_functionWithArrays() throws Exception { @Test void parse_almostEmptyRule() throws Exception { String source = readResource("almost_empty_rule.drl"); - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); assertThat(pkg).isNotNull(); RuleDescr rule = pkg.getRules().get(0); @@ -525,9 +533,7 @@ void parse_almostEmptyRule() throws Exception { @Test void parse_quotedStringNameRule() throws Exception { String source = readResource("quoted_string_name_rule.drl"); - PackageDescr pkg = parser.parse(source); - - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); assertThat(rule).isNotNull(); @@ -540,9 +546,7 @@ void parse_quotedStringNameRule() throws Exception { @Test void parse_noLoop() throws Exception { String source = readResource("no-loop.drl"); - PackageDescr pkg = parser.parse(source); - - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); assertThat(rule).isNotNull(); @@ -556,9 +560,7 @@ void parse_noLoop() throws Exception { @Test void parse_autofocus() throws Exception { String source = readResource("autofocus.drl"); - PackageDescr pkg = parser.parse(source); - - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); RuleDescr rule = pkg.getRules().get(0); assertThat(rule).isNotNull(); @@ -572,9 +574,9 @@ void parse_autofocus() throws Exception { @Test void parse_ruleFlowGroup() throws Exception { String source = readResource("ruleflowgroup.drl"); - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); + - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); RuleDescr rule = pkg.getRules().get(0); assertThat(rule).isNotNull(); @@ -588,12 +590,12 @@ void parse_ruleFlowGroup() throws Exception { @Test void parse_consequenceWithDeclaration() throws Exception { String source = readResource("declaration-in-consequence.drl"); - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); // Note : Removed "i\i;" from the original declaration-in-consequence.drl under drools-test-coverage // because it's not a valid java expression and doesn't make sense to test. (Right to raise a parser error) - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = pkg.getRules().get(0); assertThat(rule).isNotNull(); @@ -619,10 +621,10 @@ void parse_consequenceWithDeclaration() throws Exception { @Test void parse_or() { final String text = "rule X when Person(age < 42, location==\"atlanta\") \nor\nPerson(name==\"bob\") then end"; - PackageDescr pkg = parser.parse(text); + PackageDescr pkg = parseAndGetPackageDescr(text); RuleDescr rule = pkg.getRules().get(0); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(rule).isNotNull(); @@ -634,10 +636,10 @@ void parse_or() { @Test void parse_lhsWithStringQuotes() { final String text = "rule X when Person( location==\"atlanta\\\"\") then end\n"; - PackageDescr pkg = parser.parse(text); + PackageDescr pkg = parseAndGetPackageDescr(text); RuleDescr rule = pkg.getRules().get(0); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(rule).isNotNull(); @@ -650,9 +652,9 @@ void parse_lhsWithStringQuotes() { @Test void parse_lhsWithStringQuotesEscapeChars() { final String text = "rule X when Cheese( $x: type, type == \"s\\tti\\\"lto\\nn\" ) then end\n"; - PackageDescr pkg = parser.parse(text); + PackageDescr pkg = parseAndGetPackageDescr(text); RuleDescr rule = pkg.getRules().get(0); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(rule).isNotNull(); @@ -702,9 +704,9 @@ void parse_literalBoolAndNegativeNumbersRule() throws Exception { @Test void parse_emptyPattern() throws Exception { String source = readResource("test_EmptyPattern.drl"); - PackageDescr pkg = parser.parse(source); + PackageDescr pkg = parseAndGetPackageDescr(source); + - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); assertThat(pkg.getRules()).hasSize(1); final RuleDescr ruleDescr = pkg.getRules().get(0); @@ -913,7 +915,7 @@ public void parse_LineNumberIncludingCommentsInRHS() throws Exception { PackageDescr pkg = parseAndGetPackageDescrFromFile( "test_CommentLineNumbersInConsequence.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + final String rhs = (String) ((RuleDescr) pkg.getRules().get( 0 )).getConsequence(); String expected = "\\s*//woot$\\s*first;$\\s*$\\s*//$\\s*$\\s*/\\* lala$\\s*$\\s*\\*/$\\s*second;$\\s*"; @@ -926,7 +928,7 @@ public void parse_LhsSemicolonDelim() throws Exception { final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( "lhs_semicolon_delim.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(rule).isNotNull(); @@ -977,7 +979,7 @@ public void parse_NotNode() throws Exception { final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( "rule_not.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(rule).isNotNull(); assertThat(rule.getName()).isEqualTo("simple_rule"); @@ -1003,7 +1005,7 @@ public void parse_NotExistWithBrackets() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "not_exist_with_brackets.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); @@ -1430,7 +1432,7 @@ void parenthesesAndOrOr() { " (Foo(x == 1) and Bar(x == 2)) or (Foo(x == 3) or Bar(x == 4))\n" + " then\n" + "end"; - PackageDescr pkg = parser.parse(drl); + PackageDescr pkg = parseAndGetPackageDescr(drl); assertThat(pkg.getRules()).hasSize(1); final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); @@ -1462,7 +1464,7 @@ void parenthesesOrAndAnd() { " (Foo(x == 1) or Bar(x == 2)) and (Foo(x == 3) and Bar(x == 4))\n" + " then\n" + "end"; - PackageDescr pkg = parser.parse(drl); + PackageDescr pkg = parseAndGetPackageDescr(drl); assertThat(pkg.getRules()).hasSize(1); final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); @@ -1491,7 +1493,7 @@ void multipleLevelNestAndOrOrOrAnd() throws Exception { " (Foo(x == 1) and (Bar(x == 2) or Foo(x == 3))) or (Bar(x == 4) or (Foo(x == 5) and Bar(x == 6)))\n" + " then\n" + "end"; - PackageDescr pkg = parser.parse(drl); + PackageDescr pkg = parseAndGetPackageDescr(drl); assertThat(pkg.getRules()).hasSize(1); final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); @@ -1531,7 +1533,7 @@ void multipleLevelNestWithThreeOrSiblings() throws Exception { " (A() or (B() or C() or (D() and E())))\n" + " then\n" + "end"; - PackageDescr pkg = parser.parse(drl); + PackageDescr pkg = parseAndGetPackageDescr(drl); assertThat(pkg.getRules()).hasSize(1); final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); @@ -1566,7 +1568,7 @@ public void existsMultipleLevelNestWithThreeOrSiblings() throws Exception { " exists(A() or (B() or C() or (D() and E())))\n" + " then\n" + "end"; - PackageDescr pkg = parser.parse(drl); + PackageDescr pkg = parseAndGetPackageDescr(drl); assertThat(pkg.getRules()).hasSize(1); final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); @@ -1770,7 +1772,7 @@ public void parse_Attributes() throws Exception { public void parse_Attributes2() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "rule_attributes2.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + List rules = pkg.getRules(); assertThat(rules.size()).isEqualTo(3); @@ -1813,10 +1815,10 @@ public void parse_Attributes2() throws Exception { public void parse_AttributeRefract() throws Exception { final String source = "rule Test refract when Person() then end"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( source ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); assertThat(rule.getName()).isEqualTo("Test"); @@ -1893,7 +1895,7 @@ public void parse_Calendars() throws Exception { public void parse_Calendars2() throws Exception { final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( "rule_calendars_attribute2.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(rule.getName()).isEqualTo("simple_rule"); assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "bar();"); @@ -1981,7 +1983,7 @@ public void parse_Enumeration() throws Exception { public void parse_ExtraLhsNewline() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "extra_lhs_newline.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + } @Test @@ -2068,7 +2070,7 @@ public void parse_RuleNamesStartingWithNumbers() throws Exception { public void parse_EvalWithNewline() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "eval_with_newline.drl"); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + } @Test @@ -2401,7 +2403,7 @@ public void parse_Semicolon() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "semicolon.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(pkg.getName()).isEqualTo("org.drools.mvel.compiler"); assertThat(pkg.getGlobals().size()).isEqualTo(1); @@ -2559,7 +2561,7 @@ public void parse_AccumulateMnemonic() throws Exception { " $a1 : average( $price ) )\n" + "then\n" + "end\n"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( drl ); assertThat(pkg.getRules().size()).isEqualTo(1); @@ -2591,7 +2593,7 @@ public void parse_AccumulateMnemonic2() throws Exception { " average( $price ) )\n" + "then\n" + "end\n"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( drl ); assertThat(pkg.getRules().size()).isEqualTo(1); @@ -2625,7 +2627,7 @@ public void parse_ImportAccumulate() throws Exception { " $v2 : baz2( $price ) )\n" + "then\n" + "end\n"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( drl ); assertThat(pkg.getAccumulateImports().size()).isEqualTo(2); @@ -2799,7 +2801,7 @@ public void parse_PluggableOperators() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "pluggable_operators.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(pkg.getRules().size()).isEqualTo(1); final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); @@ -2845,7 +2847,7 @@ public void parse_PluggableOperators() throws Exception { public void parse_RuleMetadata() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "Rule_with_Metadata.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + // @fooAttribute(barValue) // @fooAtt2(barVal2) @@ -2863,7 +2865,7 @@ public void parse_RuleExtends() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "Rule_with_Extends.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = pkg.getRules().get( 0 ); assertThat(rule.getParentName() != null).isTrue(); @@ -2883,7 +2885,7 @@ public void parse_RuleExtends() throws Exception { public void parse_TypeDeclarationWithFields() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "declare_type_with_fields.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + List td = pkg.getTypeDeclarations(); assertThat(td.size()).isEqualTo(3); @@ -2933,7 +2935,7 @@ public void parse_TypeDeclarationWithFields() throws Exception { public void parenthesesOneLevelNestWithThreeSiblings() throws Exception { final PackageDescr pkg = parseAndGetPackageDescrFromFile( "Rule_with_nested_LHS.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = pkg.getRules().get( 0 ); assertThat(rule.getName()).isEqualTo("test"); @@ -2974,9 +2976,9 @@ public void parenthesesOneLevelNestWithThreeSiblings() throws Exception { public void parse_EntryPoint() throws Exception { final String text = "rule X when StockTick( symbol==\"ACME\") from entry-point StreamA then end"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( text ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = pkg.getRules().get( 0 ); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); @@ -2994,9 +2996,9 @@ public void parse_EntryPoint() throws Exception { public void parse_EntryPoint2() throws Exception { final String text = "rule X when StockTick( symbol==\"ACME\") from entry-point \"StreamA\" then end"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( text ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = pkg.getRules().get( 0 ); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); @@ -3014,8 +3016,8 @@ public void parse_EntryPoint2() throws Exception { public void parse_SlidingWindow() throws Exception { final String text = "rule X when StockTick( symbol==\"ACME\") over window:length(10) then end"; - PackageDescr pkg = parser.parse( text ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr( text ); + RuleDescr rule = pkg.getRules().get( 0 ); PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); @@ -3037,10 +3039,10 @@ public void parse_SlidingWindow() throws Exception { public void parse_RuleOldSyntax1() throws Exception { final String source = "rule \"Test\" when ( not $r :LiteralRestriction( operator == Operator.EQUAL ) ) then end"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( source ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); assertThat(rule.getName()).isEqualTo("Test"); @@ -3058,10 +3060,10 @@ public void parse_RuleOldSyntax1() throws Exception { public void parse_RuleOldSyntax2() throws Exception { final String source = "rule \"Test\" when ( $r :LiteralRestriction( operator == Operator.EQUAL ) ) then end"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( source ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); assertThat(rule.getName()).isEqualTo("Test"); @@ -3079,7 +3081,7 @@ public void parse_TypeWithMetaData() throws Exception { PackageDescr pkg = parseAndGetPackageDescrFromFile( "type_with_meta.drl" ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + final List declarations = pkg.getTypeDeclarations(); @@ -3142,7 +3144,7 @@ public void parse_FromFollowedByQuery() throws Exception { final String text = "rule X when Cheese() from $cheesery ?person( \"Mark\", 42; ) then end"; RuleDescr rule = parseAndGetFirstRuleDescr( text ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); assertThat(pattern.getObjectType()).isEqualTo("Cheese"); @@ -3162,7 +3164,7 @@ public void parse_FromWithTernaryFollowedByQuery() throws Exception { final String text = "rule X when Cheese() from (isFull ? $cheesery : $market) ?person( \"Mark\", 42; ) then end"; RuleDescr rule = parseAndGetFirstRuleDescr( text ); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); assertThat(pattern.getObjectType()).isEqualTo("Cheese"); @@ -3341,7 +3343,7 @@ public void parse_EntryPointDeclaration() throws Exception { " @source(\"jndi://queues/events\")\n" + " @foo( true )\n" + "end"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( text ); assertThat(pkg.getName()).isEqualTo("org.drools"); @@ -3364,7 +3366,7 @@ public void parse_WindowDeclaration() throws Exception { " over window:length( 10, $s.symbol )\n" + " from entry-point stStream\n" + "end"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( text ); assertThat(pkg.getName()).isEqualTo("org.drools"); @@ -3399,7 +3401,7 @@ public void parse_WindowUsage() throws Exception { " StockTick() from window Y\n" + "then\n" + "end\n"; - PackageDescr pkg = parser.parse( + PackageDescr pkg = parseAndGetPackageDescr( text ); assertThat(pkg.getName()).isEqualTo("org.drools"); @@ -3425,7 +3427,7 @@ public void endInRhs() throws Exception { "then\n" + " System.out.println($s.endsWith(\"xyz\"));\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text ); + PackageDescr packageDescr = parseAndGetPackageDescr(text ); RuleDescr ruleDescr = packageDescr.getRules().get(0); assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("System.out.println($s.endsWith(\"xyz\"));"); @@ -3440,7 +3442,7 @@ public void endTokenInRhs() throws Exception { "then\n" + " int end = 10;\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text ); + PackageDescr packageDescr = parseAndGetPackageDescr(text ); RuleDescr ruleDescr = packageDescr.getRules().get(0); assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("int end = 10;"); @@ -3455,7 +3457,7 @@ public void ruleTokenInRhs() throws Exception { "then\n" + " int rule = 10;\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text ); + PackageDescr packageDescr = parseAndGetPackageDescr(text ); RuleDescr ruleDescr = packageDescr.getRules().get(0); assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("int rule = 10;"); @@ -3469,7 +3471,7 @@ void semicolonEnd() { " $s : String()\n" + "then\n" + " delete($s);end\n"; // no space after semicolon - PackageDescr packageDescr = parser.parse(text ); + PackageDescr packageDescr = parseAndGetPackageDescr(text ); RuleDescr ruleDescr = packageDescr.getRules().get(0); assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("delete($s);"); @@ -3483,7 +3485,7 @@ void braceEnd() { " $p : Person()\n" + "then\n" + " modify($p) { setAge(2) }end\n"; // no space after right brace - PackageDescr packageDescr = parser.parse(text ); + PackageDescr packageDescr = parseAndGetPackageDescr(text ); RuleDescr ruleDescr = packageDescr.getRules().get(0); assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("modify($p) { setAge(2) }"); @@ -3497,12 +3499,86 @@ void parenthesisEnd() { " $p : Person()\n" + "then\n" + " retract($p)end\n"; // no space after right parenthesis - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("retract($p)"); } + @Test + void endAndRuleInSameLine() { + final String text = "package org.drools\n" + + "rule R1\n" + + "when\n" + + " $p : Person()\n" + + "then\n" + + " retract($p)\n" + + "end rule R2 when Person() then end"; // 'rule' after 'end' in the same line + PackageDescr packageDescr = parseAndGetPackageDescr(text); + + List ruleDescrList = packageDescr.getRules(); + assertThat(ruleDescrList).hasSize(2); + assertThat(ruleDescrList.get(0).getName()).isEqualTo("R1"); + assertThat(ruleDescrList.get(1).getName()).isEqualTo("R2"); + } + + @Test + void endAndAttributeAndRuleInSameLine() { + final String text = "package org.drools\n" + + "rule R1\n" + + "when\n" + + " $p : Person()\n" + + "then\n" + + " retract($p)\n" + + "end no-loop true rule R2 when Person() then end"; // 'end', attribute, 'rule' in the same line + PackageDescr packageDescr = parseAndGetPackageDescr(text); + + List ruleDescrList = packageDescr.getRules(); + assertThat(ruleDescrList).hasSize(2); + assertThat(ruleDescrList.get(0).getName()).isEqualTo("R1"); + assertThat(ruleDescrList.get(1).getName()).isEqualTo("R2"); + assertThat(packageDescr.getAttributes().get(0).getName()).isEqualTo("no-loop"); // package level attribute + } + + @Test + void endAndSingleLineCommentAndRule() { + final String text = "package org.drools\n" + + "rule R1\n" + + "when\n" + + " $p : Person()\n" + + "then\n" + + " retract($p)\n" + + "end\n" + + "// comment\n" + + "rule R2 when Person() then end"; + PackageDescr packageDescr = parseAndGetPackageDescr(text); + + List ruleDescrList = packageDescr.getRules(); + assertThat(ruleDescrList).hasSize(2); + assertThat(ruleDescrList.get(0).getName()).isEqualTo("R1"); + assertThat(ruleDescrList.get(1).getName()).isEqualTo("R2"); + } + + @Test + void endAndMultiLineCommentAndRule() { + final String text = "package org.drools\n" + + "rule R1\n" + + "when\n" + + " $p : Person()\n" + + "then\n" + + " retract($p)\n" + + "end\n" + + "/* comment\n" + + "comment */\n" + + "rule R2 when Person() then end"; + PackageDescr packageDescr = parseAndGetPackageDescr(text); + + List ruleDescrList = packageDescr.getRules(); + assertThat(ruleDescrList).hasSize(2); + assertThat(ruleDescrList.get(0).getName()).isEqualTo("R1"); + assertThat(ruleDescrList.get(1).getName()).isEqualTo("R2"); + } + @Test void singleQuoteInRhsWithSpace() { String consequence = getResultConsequence(" System.out.println( 'singleQuoteInRhs' );\n"); @@ -3535,7 +3611,7 @@ private String getResultConsequence(String rhs) { "then\n" + rhs + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); return ruleDescr.getConsequence().toString(); @@ -3551,7 +3627,7 @@ void ruleDescrProperties() { " retract($p);\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); @@ -3588,7 +3664,7 @@ void constraintOperators(String constraint) { "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); AndDescr lhs = ruleDescr.getLhs(); @@ -3620,7 +3696,7 @@ void halfConstraintOperators(String constraint) { "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); AndDescr lhs = ruleDescr.getLhs(); @@ -3637,7 +3713,7 @@ void nullSafeDereferencing() { " $p : Person(address!.city == $city)\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()).isEqualToIgnoringWhitespace("address!.city == $city"); } @@ -3650,7 +3726,7 @@ void nullSafeDereferencingMethodCall() { " $p : Person(address!.city!.startsWith(\"M\"))\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()).isEqualToIgnoringWhitespace("address!.city!.startsWith(\"M\")"); } @@ -3663,7 +3739,7 @@ void nullSafeDereferencingMethodCallBindVariable() { " $p : Person( $containsL : address!.city.contains(\"L\") )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()).isEqualToIgnoringWhitespace("$containsL : address!.city.contains(\"L\")"); } @@ -3676,7 +3752,7 @@ void groupedConstraints() { " $p : Person( address.(city.startsWith(\"I\") && city.length() == 5 ) )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()) .as("prefix should be appended to each element") @@ -3691,7 +3767,7 @@ void groupedConstraintsWithNullSafeDereferencing() { " $p : Person( address!.(city!.startsWith(\"I\") && city!.length() == 5 ) )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()) .as("prefix should be appended to each element") @@ -3701,8 +3777,8 @@ void groupedConstraintsWithNullSafeDereferencing() { @Test public void functionWithStringLiteral() { final String text = "function String star(String s) { return \"*\"; }"; - PackageDescr packageDescr = parser.parse(text); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr packageDescr = parseAndGetPackageDescr(text); + FunctionDescr function = packageDescr.getFunctions().get( 0 ); @@ -3716,8 +3792,8 @@ public void functionWithStringLiteral() { @Test public void functionWithStringLiteralAddition() { final String text = "function String addStar(String s) { return s + \"*\"; }"; - PackageDescr packageDescr = parser.parse(text); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr packageDescr = parseAndGetPackageDescr(text); + FunctionDescr function = packageDescr.getFunctions().get( 0 ); @@ -3734,8 +3810,8 @@ public void functionWithMultipleBlockStatements() { " String result = s + \"*\";\n" + " return result;\n" + "}"; - PackageDescr packageDescr = parser.parse(text); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr packageDescr = parseAndGetPackageDescr(text); + FunctionDescr function = packageDescr.getFunctions().get( 0 ); @@ -3754,8 +3830,8 @@ void lhsPatternAnnotation() { " $p : Person( name == \"Mario\" ) @watch(!*, age)\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr packageDescr = parseAndGetPackageDescr(text); + RuleDescr ruleDescr = packageDescr.getRules().get(0); PatternDescr patternDescr = (PatternDescr) ruleDescr.getLhs().getDescrs().get(0); @@ -3773,7 +3849,7 @@ void fromNew() { " $p : Person() from new Person(\"John\", 30)\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); PatternDescr patternDescr = (PatternDescr) ruleDescr.getLhs().getDescrs().get(0); FromDescr fromDescr = (FromDescr) patternDescr.getSource(); @@ -3788,8 +3864,7 @@ public void expiresWithTimeLiteralValue() { " @expires( value = 2s, policy = TIME_SOFT )\n" + "end"; - PackageDescr packageDescr = parser.parse(text); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr packageDescr = parseAndGetPackageDescr(text); AnnotationDescr annotationDescr = packageDescr .getTypeDeclarations().get(0) @@ -3807,7 +3882,7 @@ void ooPath() { " $man: Man( /wife/children[age > 10] )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()) .isEqualToIgnoringWhitespace("/wife/children[age > 10]"); @@ -3827,7 +3902,7 @@ void ooPathWithBindingInBrackets() { " Man( /wife[$age : age] )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()) .isEqualToIgnoringWhitespace("/wife[$age : age]"); @@ -3840,7 +3915,7 @@ void ooPathWithBindingInParentheses() { " Man( $toy: /wife/children[age > 10]/toys )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()) .isEqualToIgnoringWhitespace("$toy: /wife/children[age > 10]/toys"); @@ -3854,7 +3929,7 @@ void ooPathWithBackReference() { "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()) .isEqualToIgnoringWhitespace("$toy: /wife/children/toys[ name.length == ../name.length ]"); @@ -3868,7 +3943,7 @@ void ooPathMixedWithStandardConstraint() { "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()) .isEqualToIgnoringWhitespace("/wife[$age : age] && age > $age"); @@ -3882,7 +3957,7 @@ void inlineCast() { " $a : ICA( someB#ICB.onlyConcrete() == \"Hello\" )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()).isEqualToIgnoringWhitespace("someB#ICB.onlyConcrete() == \"Hello\""); } @@ -3895,7 +3970,7 @@ void inlineCastMultiple() { " $a : ICA( someB#ICB.someC#ICC.onlyConcrete() == \"Hello\" )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()).isEqualToIgnoringWhitespace("someB#ICB.someC#ICC.onlyConcrete() == \"Hello\""); } @@ -3908,7 +3983,7 @@ void inlineCastThis() { " $o : Object( this#Person.name == \"Mark\" )\n" + "then\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); ExprConstraintDescr constraintDescr = getFirstExprConstraintDescr(packageDescr); assertThat(constraintDescr.toString()).isEqualToIgnoringWhitespace("this#Person.name == \"Mark\""); } @@ -3987,8 +4062,7 @@ public void queryNoArgument() throws Exception { public void traitExtendsMultiple() throws Exception { final String source = "declare trait FatherTrait extends com.sample.ParentTrait, UncleTrait, org.test.GrandParentTrait end"; - PackageDescr pkg = parser.parse(source); - assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + PackageDescr pkg = parseAndGetPackageDescr(source); final List declarations = pkg.getTypeDeclarations(); @@ -4034,13 +4108,13 @@ void namedConsequenceDo() { "then[FoundMark]\n" + " $r.addValue(\"Found \" + $p1.getName());\n" + "end"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); NamedConsequenceDescr namedConsequenceDescr = (NamedConsequenceDescr) ruleDescr.getLhs().getDescrs().get(2); assertThat(namedConsequenceDescr.getName()).isEqualTo("FoundMark"); - assertThat(ruleDescr.getConsequence().toString()).isEqualTo("$r.addValue($p2.getName() + \" is older than \" + $p1.getName());"); - assertThat(ruleDescr.getNamedConsequences().get("FoundMark").toString()).isEqualTo("$r.addValue(\"Found \" + $p1.getName());"); + assertThat(ruleDescr.getConsequence().toString().trim()).isEqualTo("$r.addValue($p2.getName() + \" is older than \" + $p1.getName());"); + assertThat(ruleDescr.getNamedConsequences().get("FoundMark").toString().trim()).isEqualTo("$r.addValue(\"Found \" + $p1.getName());"); } @Test @@ -4057,7 +4131,7 @@ void namedConsequenceIfElse() { "then[t2]\n" + " results.add( $a.getType().toUpperCase() );\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); ConditionalBranchDescr conditionalBranchDescr = (ConditionalBranchDescr) ruleDescr.getLhs().getDescrs().get(1); assertThat(conditionalBranchDescr.getCondition().getContent().toString()).isEqualTo("$a.price > Cheese.BASE_PRICE"); @@ -4079,7 +4153,7 @@ void namedConsequenceIfElseBreak() { "then[t2]\n" + " results.add( $a.getType().toUpperCase() );\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); ConditionalBranchDescr conditionalBranchDescr = (ConditionalBranchDescr) ruleDescr.getLhs().getDescrs().get(1); assertThat(conditionalBranchDescr.getCondition().getContent().toString()).isEqualTo("$a.price > Cheese.BASE_PRICE"); @@ -4101,7 +4175,7 @@ void namedConsequenceNestedIf() { "then[t2]\n" + " results.add( $a.getType().toUpperCase() );\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); ConditionalBranchDescr conditionalBranchDescr = (ConditionalBranchDescr) ruleDescr.getLhs().getDescrs().get(1); assertThat(conditionalBranchDescr.getCondition().getContent().toString()).isEqualTo("$a.price > Cheese.BASE_PRICE"); @@ -4124,7 +4198,7 @@ void namedConsequenceAfterExists() { "then[FoundMark]\n" + " $r.addValue(\"Found \" + $p1.getName());\n" + "end"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); NamedConsequenceDescr namedConsequenceDescr = (NamedConsequenceDescr) ruleDescr.getLhs().getDescrs().get(2); assertThat(namedConsequenceDescr.getName()).isEqualTo("FoundMark"); @@ -4143,7 +4217,7 @@ void namedConsequenceAfterNot() { "then[FoundMark]\n" + " $r.addValue(\"NotFound \" + $p1.getName());\n" + "end"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); NamedConsequenceDescr namedConsequenceDescr = (NamedConsequenceDescr) ruleDescr.getLhs().getDescrs().get(2); assertThat(namedConsequenceDescr.getName()).isEqualTo("NotFoundMark"); @@ -4163,7 +4237,7 @@ void namedConsequenceIfElseAfterEval() { "then[t2]\n" + " results.add( $a.getType().toUpperCase() );\n" + "end\n"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); ConditionalBranchDescr conditionalBranchDescr = (ConditionalBranchDescr) ruleDescr.getLhs().getDescrs().get(2); assertThat(conditionalBranchDescr.getCondition().getContent().toString()).isEqualTo("$a.price > Cheese.BASE_PRICE"); @@ -4184,7 +4258,7 @@ void namedConsequenceAfterEnclosed() { "then[FoundMark]\n" + " $r.addValue(\"Found \" + $p1.getName());\n" + "end"; - PackageDescr packageDescr = parser.parse(text); + PackageDescr packageDescr = parseAndGetPackageDescr(text); RuleDescr ruleDescr = packageDescr.getRules().get(0); NamedConsequenceDescr namedConsequenceDescr = (NamedConsequenceDescr) ruleDescr.getLhs().getDescrs().get(2); assertThat(namedConsequenceDescr.getName()).isEqualTo("FoundMarkOrMario"); diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java new file mode 100644 index 00000000000..6d653827a0f --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java @@ -0,0 +1,37 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.drools.drl.parser.antlr4; + +import org.drools.drl.ast.descr.PackageDescr; +import org.drools.drl.parser.DrlParser; +import org.drools.drl.parser.DroolsParserException; + +public class ParserTestUtils { + + private ParserTestUtils() { + // It is a utility class, so it should not be instantiated. + } + + /** + * Returns a DrlParser which encapsulates an old or new parser depending on system property + */ + public static DrlParser getParser() { + return new DrlParser(); + } +} diff --git a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLLexer.g4 b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLLexer.g4 index 631d2a49a18..52716591ad9 100644 --- a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLLexer.g4 +++ b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLLexer.g4 @@ -12,6 +12,10 @@ import JavaLexer; } return input; } + + public boolean isRhsDrlEnd() { + return new LexerHelper(_input).isRhsDrlEnd(); + } } ///////////////// @@ -164,7 +168,8 @@ DrlUnicodeEscape mode RHS; RHS_WS : [ \t\r\n\u000C]+ -> channel(HIDDEN); -DRL_RHS_END : 'end' [ \t]* SEMI? [ \t]* ('\n' | '\r\n' | EOF) {setText("end");} -> popMode; +//DRL_RHS_END : 'end' [ \t]* SEMI? [ \t]* ('\n' | '\r\n' | EOF) { setText("end"); } -> popMode; +DRL_RHS_END : {isRhsDrlEnd()}? DRL_END -> popMode; RHS_STRING_LITERAL // cannot reuse DRL_STRING_LITERAL because Actions are ignored in referenced rules @@ -184,4 +189,4 @@ RHS_CHUNK | RBRACE | COMMA | SEMI - ; + ; \ No newline at end of file diff --git a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4 b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4 index 0d6c42b8d72..95f47011ad4 100644 --- a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4 +++ b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl/parser/antlr4/DRLParser.g4 @@ -17,24 +17,24 @@ import DRL6Expressions, JavaParser; compilationUnit : packagedef? unitdef? drlStatementdef* ; drlStatementdef - : importdef - | globaldef - | declaredef - | ruledef - | attributes - | functiondef - | querydef + : importdef SEMI? + | globaldef SEMI? + | declaredef SEMI? + | ruledef SEMI? + | attributes SEMI? + | functiondef SEMI? + | querydef SEMI? ; packagedef : PACKAGE name=drlQualifiedName SEMI? ; unitdef : DRL_UNIT name=drlQualifiedName SEMI? ; -importdef : IMPORT (DRL_FUNCTION|STATIC)? drlQualifiedName (DOT MUL)? SEMI? #importStandardDef - | IMPORT (DRL_ACCUMULATE|DRL_ACC) drlQualifiedName IDENTIFIER SEMI? #importAccumulateDef +importdef : IMPORT (DRL_FUNCTION|STATIC)? drlQualifiedName (DOT MUL)? #importStandardDef + | IMPORT (DRL_ACCUMULATE|DRL_ACC) drlQualifiedName IDENTIFIER #importAccumulateDef ; -globaldef : DRL_GLOBAL type drlIdentifier SEMI? ; +globaldef : DRL_GLOBAL type drlIdentifier ; /** * declare := DECLARE @@ -60,15 +60,15 @@ declaredef : DRL_DECLARE ( * END */ -typeDeclaration : DRL_TRAIT? name=drlQualifiedName (EXTENDS superTypes+=drlQualifiedName (COMMA superTypes+=drlQualifiedName)* )? drlAnnotation* field* DRL_END SEMI?; +typeDeclaration : DRL_TRAIT? name=drlQualifiedName (EXTENDS superTypes+=drlQualifiedName (COMMA superTypes+=drlQualifiedName)* )? drlAnnotation* field* DRL_END ; // entryPointDeclaration := ENTRY-POINT stringId annotation* END -entryPointDeclaration : DRL_ENTRY_POINT name=stringId drlAnnotation* DRL_END SEMI?; +entryPointDeclaration : DRL_ENTRY_POINT name=stringId drlAnnotation* DRL_END ; // windowDeclaration := WINDOW ID annotation* lhsPatternBind END -windowDeclaration : DRL_WINDOW name=IDENTIFIER drlAnnotation* lhsPatternBind DRL_END SEMI?; +windowDeclaration : DRL_WINDOW name=IDENTIFIER drlAnnotation* lhsPatternBind DRL_END ; // field := label fieldType (EQUALS_ASSIGN conditionalExpression)? annotation* SEMICOLON? @@ -76,11 +76,11 @@ field : label type (ASSIGN initExpr=conditionalOrExpression)? drlAnnotation* SEM // rule := RULE stringId (EXTENDS stringId)? annotation* attributes? lhs? rhs END -ruledef : DRL_RULE name=stringId (EXTENDS parentName=stringId)? drlAnnotation* attributes? lhs rhs DRL_RHS_END ; +ruledef : DRL_RULE name=stringId (EXTENDS parentName=stringId)? drlAnnotation* attributes? lhs? rhs DRL_RHS_END ; // query := QUERY stringId parameters? annotation* lhsExpression END -querydef : DRL_QUERY name=stringId parameters? drlAnnotation* lhsExpression+ DRL_END SEMI?; +querydef : DRL_QUERY name=stringId parameters? drlAnnotation* lhsExpression+ DRL_END ; // parameters := LEFT_PAREN ( parameter ( COMMA parameter )* )? RIGHT_PAREN parameters : LPAREN ( parameter ( COMMA parameter )* )? RPAREN ; diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlParser.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlParser.java index 4bad571401f..5e6fb78c6f2 100644 --- a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlParser.java +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlParser.java @@ -60,7 +60,9 @@ public class DrlParser { private Resource resource = null; public static final String ANTLR4_PARSER_ENABLED_PROPERTY = "drools.drl.antlr4.parser.enabled"; - public static final boolean ANTLR4_PARSER_ENABLED = Boolean.parseBoolean(System.getProperty(ANTLR4_PARSER_ENABLED_PROPERTY, "true")); // default is true + + // TODO: temporarily removed 'final' for testing purposes. This should be final + public static boolean ANTLR4_PARSER_ENABLED = Boolean.parseBoolean(System.getProperty(ANTLR4_PARSER_ENABLED_PROPERTY, "true")); // default is true public static final LanguageLevelOption DEFAULT_LANGUAGE_LEVEL = LanguageLevelOption.DRL6; private final LanguageLevelOption languageLevel; diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java index 526aaddf24e..83452cdc43c 100644 --- a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DRLVisitorImpl.java @@ -148,6 +148,11 @@ public UnitDescr visitUnitdef(DRLParser.UnitdefContext ctx) { .build(); } + @Override + public BaseDescr visitDrlStatementdef(DRLParser.DrlStatementdefContext ctx) { + return visitDescrChildren(ctx).get(0); // only one child. Ignore SEMICOLON + } + @Override public GlobalDescr visitGlobaldef(DRLParser.GlobaldefContext ctx) { return BaseDescrFactory.builder(new GlobalDescr(ctx.drlIdentifier().getText(), ctx.type().getText())) @@ -290,7 +295,9 @@ public RuleDescr visitRuledef(DRLParser.RuledefContext ctx) { List lhsDescrList = visitLhs(ctx.lhs()); lhsDescrList.forEach(descr -> rootDescr.addDescr(descr)); slimLhsRootDescr(rootDescr); - DescrHelper.refreshRootProperties(rootDescr); + DescrHelper.populateCommonProperties(rootDescr, ctx.lhs().lhsExpression()); + } else { + ruleDescr.setLhs(new AndDescr()); } if (ctx.rhs() != null) { diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DescrHelper.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DescrHelper.java index 9cdc65b94dc..b091b8f588f 100644 --- a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DescrHelper.java +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/DescrHelper.java @@ -21,8 +21,11 @@ import java.util.List; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.TerminalNode; import org.drools.drl.ast.descr.AndDescr; +import org.drools.drl.ast.descr.AttributeDescr; import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.descr.ExprConstraintDescr; import org.drools.drl.ast.descr.PatternDescr; /** @@ -35,29 +38,38 @@ private DescrHelper() { } public static T populateCommonProperties(T descr, ParserRuleContext ctx) { - descr.setStartCharacter(ctx.getStart().getStartIndex()); - // TODO: Current DRL6Parser adds +1 for EndCharacter but it doesn't look reasonable. At the moment, I don't add. Instead, I fix unit tests. - // I will revisit if this is the right approach. - descr.setEndCharacter(ctx.getStop().getStopIndex()); - descr.setLocation(ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()); - descr.setEndLocation(ctx.getStop().getLine(), ctx.getStop().getCharPositionInLine() + ctx.getStop().getText().length() - 1); // last column of the end token + + if (descr instanceof ExprConstraintDescr) { + // Backward Compatibility Notes: + // Old DRL6Parser.constraint() has slightly different behavior for ExprConstraintDescr. Keep it for backward compatibility + // When we will update LanguageLevel, we can align this with other Descr. + descr.setStartCharacter(ctx.getStart().getStartIndex()); + descr.setEndCharacter(ctx.getStop().getStopIndex()); + descr.setLocation(ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()); + descr.setEndLocation(ctx.getStop().getLine(), ctx.getStop().getCharPositionInLine()); + } else { + descr.setStartCharacter(ctx.getStart().getStartIndex()); + // Backward Compatibility Notes: + // Old DRL6Parser adds +1 for EndCharacter (except ExprConstraintDescr). This new parser follows the same to keep the backward compatibility. + // However, it doesn't look reasonable. When we will update LanguageLevel, we can remove this +1. + descr.setEndCharacter(ctx.getStop().getStopIndex() + 1); + descr.setLocation(ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()); + descr.setEndLocation(ctx.getStop().getLine(), ctx.getStop().getCharPositionInLine() + ctx.getStop().getText().length() - 1); // last column of the end token + } return descr; } - /** - * LHS rootDescr requires special handling for properties, because it rearranges its children. - */ - public static AndDescr refreshRootProperties(AndDescr descr) { - List childDescrs = descr.getDescrs(); - if (childDescrs.isEmpty()) { + public static T populateCommonProperties(T descr, List ctxList) { + if (ctxList.isEmpty()) { return descr; } - BaseDescr firstChild = childDescrs.get(0); - BaseDescr lastChild = childDescrs.get(childDescrs.size() - 1); - descr.setStartCharacter(firstChild.getStartCharacter()); - descr.setEndCharacter(lastChild.getEndCharacter()); - descr.setLocation(firstChild.getLine(), firstChild.getColumn()); - descr.setEndLocation(lastChild.getEndLine(), lastChild.getEndColumn()); + ParserRuleContext firstCtx = ctxList.get(0); + ParserRuleContext lastCtx = ctxList.get(ctxList.size() - 1); + + descr.setStartCharacter(firstCtx.getStart().getStartIndex()); + descr.setEndCharacter(lastCtx.getStop().getStopIndex() + 1); + descr.setLocation(firstCtx.getStart().getLine(), firstCtx.getStart().getCharPositionInLine()); + descr.setEndLocation(lastCtx.getStop().getLine(), lastCtx.getStop().getCharPositionInLine() + lastCtx.getStop().getText().length() - 1); // last column of the end token return descr; } diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java new file mode 100644 index 00000000000..a4a975196ad --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java @@ -0,0 +1,171 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.drools.drl.parser.antlr4; + +import java.util.List; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.IntStream; +import org.drools.drl.parser.lang.DroolsSoftKeywords; + +/** + * Helper class for lexer. It requires instance creation to keep track of the lookahead counter. + */ +public class LexerHelper { + + private static final List semiAndWS = List.of(';', ' ', '\t', '\n', '\r'); + private static final List statementKeywordsList = List.of(ParserHelper.statementKeywords); + private static final List attributeKeywordsList = List.of(DroolsSoftKeywords.SALIENCE, + DroolsSoftKeywords.ENABLED, + DroolsSoftKeywords.NO + "-" + DroolsSoftKeywords.LOOP, + DroolsSoftKeywords.AUTO + "-" + DroolsSoftKeywords.FOCUS, + DroolsSoftKeywords.LOCK + "-" + DroolsSoftKeywords.ON + "-" + DroolsSoftKeywords.ACTIVE, + DroolsSoftKeywords.AGENDA + "-" + DroolsSoftKeywords.GROUP, + DroolsSoftKeywords.ACTIVATION + "-" + DroolsSoftKeywords.GROUP, + DroolsSoftKeywords.RULEFLOW + "-" + DroolsSoftKeywords.GROUP, + DroolsSoftKeywords.DATE + "-" + DroolsSoftKeywords.EFFECTIVE, + DroolsSoftKeywords.DATE + "-" + DroolsSoftKeywords.EXPIRES, + DroolsSoftKeywords.DIALECT, + DroolsSoftKeywords.CALENDARS, + DroolsSoftKeywords.TIMER, + DroolsSoftKeywords.DURATION, + DroolsSoftKeywords.REFRACT, + DroolsSoftKeywords.DIRECT); + + private final CharStream input; + private int lookAheadCounter; + + public LexerHelper(CharStream input) { + this.input = input; + this.lookAheadCounter = 1; + } + + /** + * Determine if the current token is the end of a RHS DRL by lookahead. + * 1. 'end' + * 2. skip semi, WS, and comment + * 3. next token should be EOF or statement or attribute keyword + * + * TODO: This method is low-level and may be too complex in order to keep backward compatibility. + * This could be refactored by going back to a parser rather than the lexer island mode. + */ + boolean isRhsDrlEnd() { + if (!validateDrlEnd()) { + return false; + } + skipSemiAndWSAndComment(); + + return validateEOForNextStatement(); + } + + private boolean validateDrlEnd() { + return captureNextToken().equals(DroolsSoftKeywords.END); + } + + private String captureNextToken() { + StringBuilder sb = new StringBuilder(); + while (true) { + int la = input.LA(lookAheadCounter); + if (semiAndWS.contains((char) la) || la == IntStream.EOF) { + break; + } + sb.append((char) la); + lookAheadCounter++; + } + return sb.toString(); // never null + } + + private void skipSemiAndWSAndComment() { + while (true) { + skipSemiAndWS(); + if (input.LA(lookAheadCounter) == '/') { + boolean skipped = skipComment(); + if (!skipped) { + // found non-comment token + break; + } + // if comment is found and skipped, continue to skip semi and WS + } else { + // found non-comment token + break; + } + } + } + + private void skipSemiAndWS() { + while (true) { + int la = input.LA(lookAheadCounter); + if (!semiAndWS.contains((char) la)) { + break; + } + lookAheadCounter++; + } + } + + // if comment is found, skip it and return true + private boolean skipComment() { + boolean skipped = false; + // skip single line comment + int la1 = input.LA(lookAheadCounter); + int la2 = input.LA(lookAheadCounter + 1); + if (la1 == '/' && la2 == '/') { + // skip single line comment + skipSingleLineComment(); + skipped = true; + } else if (la1 == '/' && la2 == '*') { + // skip multi line comment + skipMultiLineComment(); + skipped = true; + } + + return skipped; + } + + private void skipSingleLineComment() { + while (true) { + int la = input.LA(lookAheadCounter); + if (la == '\n' || la == IntStream.EOF) { // this can handle `\r\n` as well + break; + } + lookAheadCounter++; + } + } + + private void skipMultiLineComment() { + while (true) { + int la = input.LA(lookAheadCounter); + if (la == IntStream.EOF) { + break; + } + if (la == '*' && input.LA(lookAheadCounter + 1) == '/') { + lookAheadCounter += 2; + break; + } + lookAheadCounter++; + } + } + + private boolean validateEOForNextStatement() { + if (input.LA(lookAheadCounter) == IntStream.EOF) { + return true; + } + String nextToken = captureNextToken(); + return statementKeywordsList.contains(nextToken) || attributeKeywordsList.contains(nextToken); + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/ParserHelper.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/ParserHelper.java index 504233edee7..fed7024c20c 100644 --- a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/ParserHelper.java +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/ParserHelper.java @@ -91,7 +91,7 @@ * by the DRL parser */ public class ParserHelper { - public final String[] statementKeywords = new String[]{ + public static final String[] statementKeywords = new String[]{ DroolsSoftKeywords.PACKAGE, DroolsSoftKeywords.UNIT, DroolsSoftKeywords.IMPORT, From fa142c3ff8f3c461ce199c75b61dcc61d35f1271 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Tue, 23 Apr 2024 15:07:31 +0900 Subject: [PATCH 2/3] Update drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jiří Locker --- .../drools/drl/parser/antlr4/LexerHelper.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java index a4a975196ad..479eebce85e 100644 --- a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/antlr4/LexerHelper.java @@ -92,20 +92,10 @@ private String captureNextToken() { } private void skipSemiAndWSAndComment() { - while (true) { + do { + // skip semi and WS, and repeat that so long as it's followed by a valid and skipped comment skipSemiAndWS(); - if (input.LA(lookAheadCounter) == '/') { - boolean skipped = skipComment(); - if (!skipped) { - // found non-comment token - break; - } - // if comment is found and skipped, continue to skip semi and WS - } else { - // found non-comment token - break; - } - } + } while (skipComment()); } private void skipSemiAndWS() { From 93f38d9f84a5bf2e52d38b0846d81fb41b11503a Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Tue, 23 Apr 2024 15:12:37 +0900 Subject: [PATCH 3/3] - removed unused import --- .../org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java | 2 -- .../test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java index 2bd15f79c01..25c51ca9b7f 100644 --- a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/DescrCommonPropertyTest.java @@ -40,7 +40,6 @@ import org.drools.drl.ast.descr.OrDescr; import org.drools.drl.ast.descr.PackageDescr; import org.drools.drl.ast.descr.PatternDescr; -import org.drools.drl.ast.descr.PatternSourceDescr; import org.drools.drl.ast.descr.QueryDescr; import org.drools.drl.ast.descr.RuleDescr; import org.drools.drl.ast.descr.TypeDeclarationDescr; @@ -50,7 +49,6 @@ import org.drools.drl.ast.descr.WindowReferenceDescr; import org.drools.drl.parser.DrlParser; import org.drools.drl.parser.DroolsParserException; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java index 6d653827a0f..f0d1f854d26 100644 --- a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl/parser/antlr4/ParserTestUtils.java @@ -18,9 +18,7 @@ */ package org.drools.drl.parser.antlr4; -import org.drools.drl.ast.descr.PackageDescr; import org.drools.drl.parser.DrlParser; -import org.drools.drl.parser.DroolsParserException; public class ParserTestUtils {