diff --git a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderRulesConfigurationImpl.java b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderRulesConfigurationImpl.java index 0c90262aa28..48e3e4971f1 100644 --- a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderRulesConfigurationImpl.java +++ b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderRulesConfigurationImpl.java @@ -127,7 +127,6 @@ public KnowledgeBuilderRulesConfigurationImpl(CompositeConfiguration + + + + 4.0.0 + + org.drools + drools-drl + 999-SNAPSHOT + + + org.drools + drools-drl-parser-tests + + Drools :: DRL :: Parser :: Tests + + + org.drools.drl.parser.tests + + + + + + org.assertj + assertj-core + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + ch.qos.logback + logback-classic + test + + + + org.drools + drools-drl-parser + test + + + org.drools + drools-compiler + test + + + org.drools + drools-mvel + test + + + + diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DRLExprParserTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DRLExprParserTest.java new file mode 100644 index 00000000000..1f27a9c861e --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DRLExprParserTest.java @@ -0,0 +1,271 @@ +/** + * 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.drl10.parser; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.drools.drl.ast.descr.AtomicExprDescr; +import org.drools.drl.ast.descr.BindingDescr; +import org.drools.drl.ast.descr.ConnectiveType; +import org.drools.drl.ast.descr.ConstraintConnectiveDescr; +import org.drools.drl.ast.descr.RelationalExprDescr; +import org.drools.drl.parser.DrlExprParser; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * DRLExprTreeTest + */ +public class DRLExprParserTest { + + DrlExprParser parser; + + @BeforeEach + public void setUp() throws Exception { + this.parser = new Drl10ExprParser(LanguageLevelOption.DRL6); + } + + @AfterEach + public void tearDown() throws Exception { + this.parser = null; + } + + @Test + public void testSimpleExpression() throws Exception { + String source = "a > b"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + RelationalExprDescr expr = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + } + + @Test + public void testAndConnective() throws Exception { + String source = "a > b && 10 != 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr expr = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + + expr = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("!="); + left = (AtomicExprDescr) expr.getLeft(); + right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("10"); + assertThat(right.getExpression()).isEqualTo("20"); + } + + @Test + public void testConnective2() throws Exception { + String source = "(a > b || 10 != 20) && someMethod(10) == 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + ConstraintConnectiveDescr or = (ConstraintConnectiveDescr) result.getDescrs().get( 0 ); + assertThat(or.getConnective()).isEqualTo(ConnectiveType.OR); + assertThat(or.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr expr = (RelationalExprDescr) or.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + + expr = (RelationalExprDescr) or.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("!="); + left = (AtomicExprDescr) expr.getLeft(); + right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("10"); + assertThat(right.getExpression()).isEqualTo("20"); + + expr = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("=="); + left = (AtomicExprDescr) expr.getLeft(); + right = (AtomicExprDescr) expr.getRight(); + assertThat(left.getExpression()).isEqualTo("someMethod(10)"); + assertThat(right.getExpression()).isEqualTo("20"); + + } + + @Test + public void testBinding() throws Exception { + String source = "$x : property"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + BindingDescr bind = (BindingDescr) result.getDescrs().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("property"); + } + + @Test + public void testBindingConstraint() throws Exception { + String source = "$x : property > value"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + RelationalExprDescr rel = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(rel.getOperator()).isEqualTo(">"); + + BindingDescr bind = (BindingDescr) rel.getLeft(); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("property"); + + AtomicExprDescr right = (AtomicExprDescr) rel.getRight(); + assertThat(right.getExpression()).isEqualTo("value"); + } + + @Test + public void testBindingWithRestrictions() throws Exception { + String source = "$x : property > value && < 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr rel = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(rel.getOperator()).isEqualTo(">"); + + BindingDescr bind = (BindingDescr) rel.getLeft(); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("property"); + + AtomicExprDescr right = (AtomicExprDescr) rel.getRight(); + assertThat(right.getExpression()).isEqualTo("value"); + + rel = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(rel.getOperator()).isEqualTo("<"); + + AtomicExprDescr left = (AtomicExprDescr) rel.getLeft(); + assertThat(left.getExpression()).isEqualTo("property"); + + right = (AtomicExprDescr) rel.getRight(); + assertThat(right.getExpression()).isEqualTo("20"); + } + + @Test + public void testDoubleBinding() throws Exception { + String source = "$x : x.m( 1, a ) && $y : y[z].foo"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + BindingDescr bind = (BindingDescr) result.getDescrs().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("x.m( 1, a )"); + + bind = (BindingDescr) result.getDescrs().get( 1 ); + assertThat(bind.getVariable()).isEqualTo("$y"); + assertThat(bind.getExpression()).isEqualTo("y[z].foo"); + } + + @Test + public void testDeepBinding() throws Exception { + String source = "($a : a > $b : b[10].prop || 10 != 20) && $x : someMethod(10) == 20"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(2); + + ConstraintConnectiveDescr or = (ConstraintConnectiveDescr) result.getDescrs().get( 0 ); + assertThat(or.getConnective()).isEqualTo(ConnectiveType.OR); + assertThat(or.getDescrs().size()).isEqualTo(2); + + RelationalExprDescr expr = (RelationalExprDescr) or.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + BindingDescr leftBind = (BindingDescr) expr.getLeft(); + BindingDescr rightBind = (BindingDescr) expr.getRight(); + assertThat(leftBind.getVariable()).isEqualTo("$a"); + assertThat(leftBind.getExpression()).isEqualTo("a"); + assertThat(rightBind.getVariable()).isEqualTo("$b"); + assertThat(rightBind.getExpression()).isEqualTo("b[10].prop"); + + expr = (RelationalExprDescr) or.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("!="); + AtomicExprDescr leftExpr = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr rightExpr = (AtomicExprDescr) expr.getRight(); + assertThat(leftExpr.getExpression()).isEqualTo("10"); + assertThat(rightExpr.getExpression()).isEqualTo("20"); + + expr = (RelationalExprDescr) result.getDescrs().get( 1 ); + assertThat(expr.getOperator()).isEqualTo("=="); + leftBind = (BindingDescr) expr.getLeft(); + rightExpr = (AtomicExprDescr) expr.getRight(); + assertThat(leftBind.getVariable()).isEqualTo("$x"); + assertThat(leftBind.getExpression()).isEqualTo("someMethod(10)"); + assertThat(rightExpr.getExpression()).isEqualTo("20"); + + } + + @Test + @Timeout(10000L) + public void testNestedExpression() throws Exception { + // DROOLS-982 + String source = "(((((((((((((((((((((((((((((((((((((((((((((((((( a > b ))))))))))))))))))))))))))))))))))))))))))))))))))"; + ConstraintConnectiveDescr result = parser.parse( source ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(result.getConnective()).isEqualTo(ConnectiveType.AND); + assertThat(result.getDescrs().size()).isEqualTo(1); + + RelationalExprDescr expr = (RelationalExprDescr) result.getDescrs().get( 0 ); + assertThat(expr.getOperator()).isEqualTo(">"); + + AtomicExprDescr left = (AtomicExprDescr) expr.getLeft(); + AtomicExprDescr right = (AtomicExprDescr) expr.getRight(); + + assertThat(left.getExpression()).isEqualTo("a"); + assertThat(right.getExpression()).isEqualTo("b"); + } +} diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DRLParserTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DRLParserTest.java new file mode 100644 index 00000000000..35c4638ebc1 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DRLParserTest.java @@ -0,0 +1,95 @@ +package org.drools.drl10.parser; + +import java.util.List; + +import org.drools.drl.ast.descr.AnnotationDescr; +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.GlobalDescr; +import org.drools.drl.ast.descr.PackageDescr; +import org.drools.drl.ast.descr.PatternDescr; +import org.drools.drl.ast.descr.RuleDescr; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.drools.drl10.parser.DRLParserHelper.computeTokenIndex; +import static org.drools.drl10.parser.DRLParserHelper.createDrlParser; +import static org.drools.drl10.parser.DRLParserHelper.parse; + +class DRLParserTest { + + private static final String drl = + "package org.test;\n" + + "import org.test.model.Person;\n" + + "global String result;\n" + + "rule TestRule @Test(true) no-loop salience 15 when \n" + + " $p:Person( age >= 18 )\n" + + "then\n" + + " int a = 4;\n" + + " System.out.println($p.getName());\n" + + "end\n"; + + @Test + void parse_basicRule() { + PackageDescr packageDescr = parse(drl); + assertThat(packageDescr.getName()).isEqualTo("org.test"); + + assertThat(packageDescr.getImports().size()).isEqualTo(1); + assertThat(packageDescr.getImports().get(0).getTarget()).isEqualTo("org.test.model.Person"); + + assertThat(packageDescr.getGlobals().size()).isEqualTo(1); + GlobalDescr globalDescr = packageDescr.getGlobals().get(0); + assertThat(globalDescr.getType()).isEqualTo("String"); + assertThat(globalDescr.getIdentifier()).isEqualTo("result"); + + assertThat(packageDescr.getRules().size()).isEqualTo(1); + RuleDescr ruleDescr = packageDescr.getRules().get(0); + + AnnotationDescr annotationDescr = ruleDescr.getAnnotation("Test"); + assertThat(annotationDescr).isNotNull(); + assertThat(annotationDescr.getValue()).isEqualTo("true"); + + assertThat(ruleDescr.getAttributes().size()).isEqualTo(2); + assertThat(ruleDescr.getAttributes().get("no-loop")).isNotNull(); + AttributeDescr salience = ruleDescr.getAttributes().get("salience"); + assertThat(salience).isNotNull(); + assertThat(salience.getValue()).isEqualTo("15"); + + assertThat(ruleDescr.getName()).isEqualTo("TestRule"); + + assertThat(ruleDescr.getLhs().getDescrs().size()).isEqualTo(1); + PatternDescr patternDescr = (PatternDescr) ruleDescr.getLhs().getDescrs().get(0); + assertThat(patternDescr.getIdentifier()).isEqualTo("$p"); + assertThat(patternDescr.getObjectType()).isEqualTo("Person"); + + List constraints = patternDescr.getConstraint().getDescrs(); + assertThat(constraints.size()).isEqualTo(1); + ExprConstraintDescr expr = (ExprConstraintDescr) constraints.get(0); + assertThat(expr.getExpression()).isEqualTo("age >= 18"); + + assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("int a = 4; System.out.println($p.getName());"); + } + + @Test + void computeTokenIndex_basicRule() { + DRLParser parser = createDrlParser(drl); + parser.compilationUnit(); + + assertThat((int) computeTokenIndex(parser, 1, 0)).isEqualTo(0); + assertThat((int) computeTokenIndex(parser, 1, 1)).isEqualTo(0); + assertThat((int) computeTokenIndex(parser, 1, 7)).isEqualTo(0); + assertThat((int) computeTokenIndex(parser, 1, 8)).isEqualTo(1); + assertThat((int) computeTokenIndex(parser, 1, 9)).isEqualTo(2); + assertThat((int) computeTokenIndex(parser, 1, 9)).isEqualTo(2); + assertThat((int) computeTokenIndex(parser, 1, 12)).isEqualTo(3); + assertThat((int) computeTokenIndex(parser, 1, 13)).isEqualTo(4); + assertThat((int) computeTokenIndex(parser, 1, 17)).isEqualTo(5); + assertThat((int) computeTokenIndex(parser, 1, 18)).isEqualTo(6); + assertThat((int) computeTokenIndex(parser, 2, 0)).isEqualTo(6); + assertThat((int) computeTokenIndex(parser, 2, 1)).isEqualTo(7); + assertThat((int) computeTokenIndex(parser, 2, 6)).isEqualTo(7); + assertThat((int) computeTokenIndex(parser, 2, 7)).isEqualTo(8); + // Skip RHS token assertion as it is fluid part at the moment. + } +} diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DescrDumperTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DescrDumperTest.java new file mode 100644 index 00000000000..d98ec8ec895 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/DescrDumperTest.java @@ -0,0 +1,368 @@ +/** + * 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.drl10.parser; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.drools.compiler.lang.DescrDumper; +import org.drools.compiler.lang.DumperContext; +import org.drools.drl.ast.descr.AtomicExprDescr; +import org.drools.drl.ast.descr.BindingDescr; +import org.drools.drl.ast.descr.ConstraintConnectiveDescr; +import org.drools.drl.parser.DrlExprParser; +import org.drools.mvel.evaluators.MatchesEvaluatorsDefinition; +import org.drools.mvel.evaluators.SetEvaluatorsDefinition; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.kie.internal.builder.conf.LanguageLevelOption; + +public class DescrDumperTest { + + private DescrDumper dumper; + + @BeforeEach + public void setUp() throws Exception { + // configure operators + new SetEvaluatorsDefinition(); + new MatchesEvaluatorsDefinition(); + + dumper = new DescrDumper(); + } + + @Test + public void testDump() throws Exception { + String input = "price > 10 && < 20 || == $val || == 30"; + String expected = "( price > 10 && price < 20 || price == $val || price == 30 )"; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpMatches() throws Exception { + String input = "type.toString matches \"something\\swith\\tsingle escapes\""; + String expected = "type.toString ~= \"something\\swith\\tsingle escapes\""; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpMatches2() throws Exception { + String input = "type.toString matches 'something\\swith\\tsingle escapes'"; + String expected = "type.toString ~= \"something\\swith\\tsingle escapes\""; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpMatches3() throws Exception { + String input = "this[\"content\"] matches \"hello ;=\""; + String expected = "this[\"content\"] ~= \"hello ;=\""; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpContains() throws Exception { + String input = "list contains \"b\""; + String expected = "list contains \"b\""; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpContains2() throws Exception { + String input = "list not contains \"b\""; + String expected = "!( list contains \"b\" )"; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpExcludes() throws Exception { + String input = "list excludes \"b\""; + String expected = "!( list contains \"b\" )"; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpExcludes2() throws Exception { + String input = "list not excludes \"b\""; + String expected = "list contains \"b\""; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test @Disabled + public void testDumpWithDateAttr() throws Exception { + String input = "son.birthDate == \"01-jan-2000\""; + String expected = "son.birthDate == org.drools.util.DateUtils.parseDate( \"01-jan-2000\" )"; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpComplex() throws Exception { + String input = "a ( > 60 && < 70 ) || ( > 50 && < 55 ) && a3 == \"black\" || a == 40 && a3 == \"pink\" || a == 12 && a3 == \"yellow\" || a3 == \"blue\""; + String expected = "( ( a > 60 && a < 70 || a > 50 && a < 55 ) && a3 == \"black\" || a == 40 && a3 == \"pink\" || a == 12 && a3 == \"yellow\" || a3 == \"blue\" )"; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpBindings() throws Exception { + String input = "$x : property > value"; + String expected = "property > value"; + + ConstraintConnectiveDescr descr = parse( input ); + DumperContext ctx = new DumperContext(); + String result = dumper.dump( descr, + ctx ); + + assertThat(result).isEqualTo(expected); + assertThat(ctx.getBindings().size()).isEqualTo(1); + BindingDescr bind = ctx.getBindings().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("property"); + } + + @Test + public void testDumpBindings2() throws Exception { + String input = "( $a : a > $b : b[10].prop || 10 != 20 ) && $x : someMethod(10) == 20"; + String expected = "( a > b[10].prop || 10 != 20 ) && someMethod(10) == 20"; + + ConstraintConnectiveDescr descr = parse( input ); + DumperContext ctx = new DumperContext(); + String result = dumper.dump( descr, + ctx ); + + assertThat(result).isEqualTo(expected); + assertThat(ctx.getBindings().size()).isEqualTo(3); + BindingDescr bind = ctx.getBindings().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$a"); + assertThat(bind.getExpression()).isEqualTo("a"); + bind = ctx.getBindings().get( 1 ); + assertThat(bind.getVariable()).isEqualTo("$b"); + assertThat(bind.getExpression()).isEqualTo("b[10].prop"); + bind = ctx.getBindings().get( 2 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("someMethod(10)"); + } + + @Test + public void testDumpBindings3() throws Exception { + String input = "( $a : a > $b : b[10].prop || 10 != 20 ) && $x : someMethod(10)"; + String expected = "( a > b[10].prop || 10 != 20 )"; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpBindings4() throws Exception { + String input = "( $a : a > $b : b[10].prop || $x : someMethod(10) ) && 10 != 20"; + String expected = "( a > b[10].prop ) && 10 != 20"; + + ConstraintConnectiveDescr descr = parse( input ); + String result = dumper.dump( descr ); + + assertThat(result).isEqualTo(expected); + } + + @Test + public void testDumpBindingsWithRestriction() throws Exception { + String input = "$x : age > 10 && < 20 || > 30"; + String expected = "( age > 10 && age < 20 || age > 30 )"; + + ConstraintConnectiveDescr descr = parse( input ); + DumperContext ctx = new DumperContext(); + String result = dumper.dump( descr, + ctx ); + + assertThat(result).isEqualTo(expected); + assertThat(ctx.getBindings().size()).isEqualTo(1); + BindingDescr bind = ctx.getBindings().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("age"); + } + + @Test + public void testDumpBindingsComplexOp() throws Exception { + String input = "$x : age in (10, 20, $someVal)"; + String expected = "( age == 10 || age == 20 || age == $someVal )"; + + ConstraintConnectiveDescr descr = parse( input ); + DumperContext ctx = new DumperContext(); + String result = dumper.dump( descr, + ctx ); + + assertThat(result).isEqualTo(expected); + assertThat(ctx.getBindings().size()).isEqualTo(1); + BindingDescr bind = ctx.getBindings().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("age"); + } + + @Test + public void testDumpBindingsComplexOp2() throws Exception { + String input = "$x : age not in (10, 20, $someVal)"; + String expected = "age != 10 && age != 20 && age != $someVal"; + + ConstraintConnectiveDescr descr = parse( input ); + DumperContext ctx = new DumperContext(); + String result = dumper.dump( descr, + ctx ); + + assertThat(result).isEqualTo(expected); + assertThat(ctx.getBindings().size()).isEqualTo(1); + BindingDescr bind = ctx.getBindings().get( 0 ); + assertThat(bind.getVariable()).isEqualTo("$x"); + assertThat(bind.getExpression()).isEqualTo("age"); + } + + @Test + public void testProcessInlineCast() throws Exception { + String expr = "field1#Class.field2"; + String expectedInstanceof = "field1 instanceof Class"; + String expectedcasted = "((Class)field1).field2"; + AtomicExprDescr atomicExpr = new AtomicExprDescr(expr); + ConstraintConnectiveDescr ccd = new ConstraintConnectiveDescr( ); + ccd.addDescr( atomicExpr ); + String[] instanceofAndCastedExpr = dumper.processImplicitConstraints(expr, atomicExpr, ccd, ccd.getDescrs().indexOf( atomicExpr ), null); + assertThat(ccd.getDescrs().size()).isEqualTo(2); + assertThat(ccd.getDescrs().get(0).toString()).isEqualTo(expectedInstanceof); + assertThat(atomicExpr.getRewrittenExpression()).isEqualTo(expectedcasted); + + expr = "field1#Class1.field2#Class2.field3"; + String expectedInstanceof1 = "field1 instanceof Class1"; + String expectedInstanceof2 = "((Class1)field1).field2 instanceof Class2"; + expectedcasted = "((Class2)((Class1)field1).field2).field3"; + atomicExpr = new AtomicExprDescr(expr); + ccd = new ConstraintConnectiveDescr( ); + instanceofAndCastedExpr = dumper.processImplicitConstraints(expr, atomicExpr, ccd, ccd.getDescrs().indexOf( atomicExpr ), null); + assertThat(ccd.getDescrs().get(0).toString()).isEqualTo(expectedInstanceof1); + assertThat(ccd.getDescrs().get(1).toString()).isEqualTo(expectedInstanceof2); + assertThat(instanceofAndCastedExpr[1]).isEqualTo(expectedcasted); + assertThat(atomicExpr.getRewrittenExpression()).isEqualTo(expectedcasted); + } + + @Test + public void testProcessNullSafeDereferencing() throws Exception { + String expr = "field1!.field2"; + String expectedNullCheck = "field1 != null"; + String expectedExpr = "field1.field2"; + AtomicExprDescr atomicExpr = new AtomicExprDescr(expr); + ConstraintConnectiveDescr ccd = new ConstraintConnectiveDescr( ); + String[] nullCheckAndExpr = dumper.processImplicitConstraints( expr, atomicExpr, ccd, ccd.getDescrs().indexOf( atomicExpr ), null ); + assertThat(ccd.getDescrs().get(0).toString()).isEqualTo(expectedNullCheck); + assertThat(nullCheckAndExpr[1]).isEqualTo(expectedExpr); + assertThat(atomicExpr.getRewrittenExpression()).isEqualTo(expectedExpr); + + expr = "field1!.field2!.field3"; + String expectedNullCheck1 = "field1 != null"; + String expectedNullCheck2 = "field1.field2 != null"; + expectedExpr = "field1.field2.field3"; + atomicExpr = new AtomicExprDescr(expr); + ccd = new ConstraintConnectiveDescr( ); + nullCheckAndExpr = dumper.processImplicitConstraints( expr, atomicExpr, ccd, ccd.getDescrs().indexOf( atomicExpr ), null ); + assertThat(ccd.getDescrs().get(0).toString()).isEqualTo(expectedNullCheck1); + assertThat(ccd.getDescrs().get(1).toString()).isEqualTo(expectedNullCheck2); + assertThat(nullCheckAndExpr[1]).isEqualTo(expectedExpr); + assertThat(atomicExpr.getRewrittenExpression()).isEqualTo(expectedExpr); + } + + @Test + public void testProcessImplicitConstraints() throws Exception { + String expr = "field1#Class!.field2"; + String expectedConstraints = "field1 instanceof Class"; + String expectedExpr = "((Class)field1).field2"; + AtomicExprDescr atomicExpr = new AtomicExprDescr(expr); + ConstraintConnectiveDescr ccd = new ConstraintConnectiveDescr( ); + String[] constraintsAndExpr = dumper.processImplicitConstraints( expr, atomicExpr, ccd, ccd.getDescrs().indexOf( atomicExpr ), null ); + assertThat(ccd.getDescrs().get(0).toString()).isEqualTo(expectedConstraints); + assertThat(constraintsAndExpr[1]).isEqualTo(expectedExpr); + assertThat(atomicExpr.getRewrittenExpression()).isEqualTo(expectedExpr); + + expr = "field1!.field2#Class.field3"; + String expectedConstraints1 = "field1 != null"; + String expectedConstraints2 = "field1.field2 instanceof Class"; + expectedExpr = "((Class)field1.field2).field3"; + atomicExpr = new AtomicExprDescr(expr); + ccd = new ConstraintConnectiveDescr( ); + constraintsAndExpr = dumper.processImplicitConstraints( expr, atomicExpr, ccd, ccd.getDescrs().indexOf( atomicExpr ), null ); + assertThat(ccd.getDescrs().get(0).toString()).isEqualTo(expectedConstraints1); + assertThat(ccd.getDescrs().get(1).toString()).isEqualTo(expectedConstraints2); + assertThat(constraintsAndExpr[1]).isEqualTo(expectedExpr); + assertThat(atomicExpr.getRewrittenExpression()).isEqualTo(expectedExpr); + + expr = "field1#Class.field2!.field3"; + expectedConstraints1 = "field1 instanceof Class"; + expectedConstraints2 = "((Class)field1).field2 != null"; + expectedExpr = "((Class)field1).field2.field3"; + atomicExpr = new AtomicExprDescr(expr); + ccd = new ConstraintConnectiveDescr( ); + constraintsAndExpr = dumper.processImplicitConstraints( expr, atomicExpr, ccd, ccd.getDescrs().indexOf( atomicExpr ), null ); + assertThat(ccd.getDescrs().get(0).toString()).isEqualTo(expectedConstraints1); + assertThat(ccd.getDescrs().get(1).toString()).isEqualTo(expectedConstraints2); + assertThat(constraintsAndExpr[1]).isEqualTo(expectedExpr); + assertThat(atomicExpr.getRewrittenExpression()).isEqualTo(expectedExpr); + } + + public ConstraintConnectiveDescr parse( final String constraint ) { + DrlExprParser parser = new Drl10ExprParser(LanguageLevelOption.DRL6); + ConstraintConnectiveDescr result = parser.parse( constraint ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + return result; + } + +} diff --git a/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/MiscDRLParserTest.java b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/MiscDRLParserTest.java new file mode 100644 index 00000000000..6353be53ac7 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/java/org/drools/drl10/parser/MiscDRLParserTest.java @@ -0,0 +1,3443 @@ +package org.drools.drl10.parser; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; + +import org.drools.drl.ast.descr.AccumulateDescr; +import org.drools.drl.ast.descr.AccumulateImportDescr; +import org.drools.drl.ast.descr.AndDescr; +import org.drools.drl.ast.descr.AnnotationDescr; +import org.drools.drl.ast.descr.AttributeDescr; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.descr.BehaviorDescr; +import org.drools.drl.ast.descr.CollectDescr; +import org.drools.drl.ast.descr.EntryPointDeclarationDescr; +import org.drools.drl.ast.descr.EntryPointDescr; +import org.drools.drl.ast.descr.EvalDescr; +import org.drools.drl.ast.descr.ExistsDescr; +import org.drools.drl.ast.descr.ExprConstraintDescr; +import org.drools.drl.ast.descr.ForallDescr; +import org.drools.drl.ast.descr.FromDescr; +import org.drools.drl.ast.descr.FunctionDescr; +import org.drools.drl.ast.descr.GlobalDescr; +import org.drools.drl.ast.descr.ImportDescr; +import org.drools.drl.ast.descr.MVELExprDescr; +import org.drools.drl.ast.descr.NotDescr; +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.QueryDescr; +import org.drools.drl.ast.descr.RuleDescr; +import org.drools.drl.ast.descr.TypeDeclarationDescr; +import org.drools.drl.ast.descr.TypeFieldDescr; +import org.drools.drl.ast.descr.WindowDeclarationDescr; +import org.drools.drl10.parser.DRLParserWrapper; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/* + * This test class is ported from org.drools.mvel.compiler.lang.RuleParserTest + */ +class MiscDRLParserTest { + + private DRLParserWrapper parser; + + @BeforeEach + public void setUp() { + parser = new DRLParserWrapper(); + } + + @AfterEach + public void tearDown() { + } + + private String readResource(final String filename) throws Exception { + Path path = Paths.get(getClass().getResource(filename).toURI()); + final StringBuilder sb = new StringBuilder(); + try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + for (String line; (line = br.readLine()) != null; ) { + sb.append(line); + sb.append("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + return sb.toString(); + } + + private RuleDescr parseAndGetFirstRuleDescr(String drl) { + PackageDescr pkg = parser.parse(drl); + assertThat(parser.hasErrors()).as(parser.getErrorMessages().toString()).isFalse(); + 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) throws Exception { + return parser.parse(readResource(filename)); + } + + private QueryDescr parseAndGetFirstQueryDescr(String drl) { + PackageDescr pkg = parser.parse(drl); + assertThat(parser.hasErrors()).as(parser.getErrorMessages().toString()).isFalse(); + 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 { + return parseAndGetFirstQueryDescr(readResource(filename)); + } + + @Test + void parse_validPackage() { + final String source = "package foo.bar.baz"; + final PackageDescr pkg = parser.parse(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); + assertThat(parser.hasErrors()).isTrue(); + assertThat(pkg.getName()).isEqualTo("foo.bar.baz"); + } + + @Test + void parse_packageWithAllErrorNode() { + final String source = "package 12 12312 231"; + final PackageDescr pkg = parser.parse(source); + assertThat(parser.hasErrors()).isTrue(); + assertThat(pkg.getName()).isEmpty(); + } + + @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(); + assertThat(pkg.getName()).isEqualTo("foo"); + assertThat(pkg.getImports()).hasSize(2); + 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()); + + impdescr = pkg.getImports().get(1); + assertThat(impdescr.getTarget()).isEqualTo("com.foo.Baz"); + assertThat(impdescr.getStartCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget())); + assertThat(impdescr.getEndCharacter()).isEqualTo(source.indexOf("import " + impdescr.getTarget()) + ("import " + impdescr.getTarget()).length()); + } + + @Test + void parse_functionImport() { + final String source = "package foo\n" + + "import function java.lang.Math.max\n" + + "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(); + 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); + + 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(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); + + impdescr = pkg.getFunctionImports().get(1); + assertThat(impdescr.getTarget()).isEqualTo("java.lang.Math.min"); + 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()); + } + + @Test + void parse_globalWithComplexType() { + final String source = "package foo.bar.baz\n" + + "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(); + 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(pkg.getGlobals()).hasSize(2); + + GlobalDescr global = pkg.getGlobals().get(0); + assertThat(global.getType()).isEqualTo("java.util.List>"); + assertThat(global.getIdentifier()).isEqualTo("aList"); + 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()); + + global = pkg.getGlobals().get(1); + assertThat(global.getType()).isEqualTo("Integer"); + 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); + } + + @Test + void parse_globalWithOrWithoutSemi() throws Exception { + String source = readResource("globals.drl"); + PackageDescr pkg = parser.parse(source); + + assertThat(pkg.getRules()).hasSize(1); + + final RuleDescr rule = pkg.getRules().get(0); + assertThat(rule.getLhs().getDescrs()).hasSize(1); + + assertThat(pkg.getImports()).hasSize(1); + assertThat(pkg.getGlobals()).hasSize(2); + + final GlobalDescr foo = pkg.getGlobals().get(0); + assertThat(foo.getType()).isEqualTo("java.lang.String"); + assertThat(foo.getIdentifier()).isEqualTo("foo"); + final GlobalDescr bar = pkg.getGlobals().get(1); + assertThat(bar.getType()).isEqualTo("java.lang.Integer"); + assertThat(bar.getIdentifier()).isEqualTo("bar"); + } + + @Test + void parse_functionImportWithNotExist() throws Exception { + String source = readResource("test_FunctionImport.drl"); + PackageDescr pkg = parser.parse(source); + + assertThat(pkg.getFunctionImports()).hasSize(2); + + assertThat(pkg.getFunctionImports().get(0).getTarget()).isEqualTo("abd.def.x"); + assertThat(pkg.getFunctionImports().get(0).getStartCharacter()).isNotSameAs(-1); + assertThat(pkg.getFunctionImports().get(0).getEndCharacter()).isNotSameAs(-1); + assertThat(pkg.getFunctionImports().get(1).getTarget()).isEqualTo("qed.wah.*"); + assertThat(pkg.getFunctionImports().get(1).getStartCharacter()).isNotSameAs(-1); + assertThat(pkg.getFunctionImports().get(1).getEndCharacter()).isNotSameAs(-1); + } + + @Test + void parse_fromComplexAccessor() { + String source = "rule \"Invalid customer id\" ruleflow-group \"validate\" lock-on-active true \n" + + " when \n" + + " o: Order( ) \n" + + " not( Customer( ) from customerService.getCustomer(o.getCustomerId()) ) \n" + + " then \n" + + " 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.getErrorMessages().toString()).isFalse(); + + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule.getName()).isEqualTo("Invalid customer id"); + + assertThat(rule.getLhs().getDescrs()).hasSize(2); + + NotDescr not = (NotDescr) rule.getLhs().getDescrs().get(1); + PatternDescr customer = (PatternDescr) not.getDescrs().get(0); + + assertThat(customer.getObjectType()).isEqualTo("Customer"); + assertThat(((FromDescr) customer.getSource()).getDataSource().getText()).isEqualTo("customerService.getCustomer(o.getCustomerId())"); + } + + @Test + void parse_fromWithInlineList() { + String source = "rule XYZ \n" + + " when \n" + + " o: Order( ) \n" + + " not( Number( ) from [1, 2, 3] ) \n" + + " then \n" + + " 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(); + + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule.getName()).isEqualTo("XYZ"); + + PatternDescr number = (PatternDescr) ((NotDescr) rule.getLhs().getDescrs().get(1)).getDescrs().get(0); + assertThat(((FromDescr) number.getSource()).getDataSource().toString()).isEqualToIgnoringWhitespace("[1, 2, 3]"); + } + + @Test + void parse_fromWithInlineListMethod() { + String source = "rule XYZ \n" + + " when \n" + + " o: Order( ) \n" + + " Number( ) from [1, 2, 3].sublist(1, 2) \n" + + " then \n" + + " 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(); + + 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)"); + } + + @Test + void parse_fromWithInlineListIndex() { + String source = "rule XYZ \n" + + " when \n" + + " o: Order( ) \n" + + " Number( ) from [1, 2, 3][1] \n" + + " then \n" + + " 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(); + + 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]"); + } + + @Test + void parse_ruleWithoutEnd() { + String source = "rule \"Invalid customer id\" \n" + + " when \n" + + " o: Order( ) \n" + + " then \n" + + " System.err.println(\"Invalid customer id found!\"); \n"; + parser.parse(source); + assertThat(parser.hasErrors()).isTrue(); + } + + @Test + void parse_orWithSpecialBind() { + String source = "rule \"A and (B or C or D)\" \n" + + " when \n" + + " pdo1 : ParametricDataObject( paramID == 101, stringValue == \"1000\" ) and \n" + + " pdo2 :(ParametricDataObject( paramID == 101, stringValue == \"1001\" ) or \n" + + " ParametricDataObject( paramID == 101, stringValue == \"1002\" ) or \n" + + " ParametricDataObject( paramID == 101, stringValue == \"1003\" )) \n" + + " 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(); + + RuleDescr rule = pkg.getRules().get(0); + AndDescr lhs = rule.getLhs(); + assertThat(lhs.getDescrs()).hasSize(2); + + PatternDescr pdo1 = (PatternDescr) lhs.getDescrs().get(0); + assertThat(pdo1.getIdentifier()).isEqualTo("pdo1"); + + OrDescr or = (OrDescr) rule.getLhs().getDescrs().get(1); + assertThat(or.getDescrs()).hasSize(3); + for (BaseDescr pdo2 : or.getDescrs()) { + assertThat(((PatternDescr) pdo2).getIdentifier()).isEqualTo("pdo2"); + } + } + + @Test + void parse_compatibleRestriction() { + String source = "package com.sample rule test when Test( ( text == null || text2 matches \"\" ) ) then end"; + PackageDescr pkg = parser.parse(source); + + assertThat(pkg.getName()).isEqualTo("com.sample"); + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule.getName()).isEqualTo("test"); + ExprConstraintDescr expr = (ExprConstraintDescr) ((PatternDescr) rule.getLhs().getDescrs().get(0)).getDescrs().get(0); + assertThat(expr.getText()).isEqualTo("( text == null || text2 matches \"\" )"); + } + + @Test + void parse_simpleConstraint() { + String source = "package com.sample rule test when Cheese( type == 'stilton', price > 10 ) then end"; + PackageDescr pkg = parser.parse(source); + + assertThat(pkg.getName()).isEqualTo("com.sample"); + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule.getName()).isEqualTo("test"); + + assertThat(rule.getLhs().getDescrs()).hasSize(1); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); + + AndDescr constraint = (AndDescr) pattern.getConstraint(); + assertThat(constraint.getDescrs()).hasSize(2); + assertThat(constraint.getDescrs().get(0)).hasToString("type == \"stilton\""); + assertThat(constraint.getDescrs().get(1)).hasToString("price > 10"); + } + + @Test + void parse_stringEscapes() { + String source = "package com.sample rule test when Cheese( type matches \"\\..*\\\\.\" ) then end"; + PackageDescr pkg = parser.parse(source); + assertThat(pkg.getName()).isEqualTo("com.sample"); + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule.getName()).isEqualTo("test"); + + assertThat(rule.getLhs().getDescrs()).hasSize(1); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); + + AndDescr constraint = (AndDescr) pattern.getConstraint(); + assertThat(constraint.getDescrs()).hasSize(1); + assertThat(constraint.getDescrs().get(0)).hasToString("type matches \"\\..*\\\\.\""); + } + + @Test + void parse_dialectWithSingleQuotation() { + final String source = "dialect 'mvel'"; + PackageDescr pkg = parser.parse(source); + AttributeDescr attr = pkg.getAttributes().get(0); + assertThat(attr.getName()).isEqualTo("dialect"); + assertThat(attr.getValue()).isEqualTo("mvel"); + } + + @Test + void parse_dialectWithDoubleQuotation() { + final String source = "dialect \"mvel\""; + PackageDescr pkg = parser.parse(source); + AttributeDescr attr = pkg.getAttributes().get(0); + assertThat(attr.getName()).isEqualTo("dialect"); + assertThat(attr.getValue()).isEqualTo("mvel"); + } + + @Test + void parse_emptyRuleWithoutWhen() throws Exception { + String source = readResource("empty_rule.drl"); // without WHEN + parser.parse(source); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isTrue(); + + // Note : RuleParserTest.testEmptyRule allows this DRL, but I think is doesn't make sense to pass this DRL + } + + @Test + void parse_keywordCollisions() throws Exception { + String source = readResource("eol_funny_business.drl"); // keywords everywhere + + // 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(); + + assertThat(pkg.getRules()).hasSize(1); + } + + @Test + void parse_ternaryExpression() throws Exception { + String source = readResource("ternary_expression.drl"); + PackageDescr pkg = parser.parse(source); + + final RuleDescr rule = pkg.getRules().get(0); + assertThat(pkg.getRules()).hasSize(1); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace("if (speed > speedLimit ? true : false;) pullEmOver();"); + } + + @Test + void parse_functionWithArrays() throws Exception { + String source = readResource("function_arrays.drl"); + + // Note: function_arrays.drl is modified from the one under drools-test-coverage to be more realistic. + // 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); + + assertThat(pkg.getName()).isEqualTo("foo"); + assertThat(pkg.getRules()).hasSize(1); + + final RuleDescr rule = pkg.getRules().get(0); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace("yourFunction(new String[] {\"a\",\"b\",\"c\"});"); + + final FunctionDescr func = pkg.getFunctions().get(0); + + assertThat(func.getReturnType()).isEqualTo("String[]"); + assertThat(func.getParameterNames().get(0)).isEqualTo("args[]"); + assertThat(func.getParameterTypes().get(0)).isEqualTo("String"); + } + + @Test + void parse_almostEmptyRule() throws Exception { + String source = readResource("almost_empty_rule.drl"); + PackageDescr pkg = parser.parse(source); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + assertThat(pkg).isNotNull(); + + RuleDescr rule = pkg.getRules().get(0); + + assertThat(rule.getName()).isEqualTo("almost_empty"); + assertThat(rule.getLhs()).isNotNull(); + assertThat(((String) rule.getConsequence()).trim()).isEmpty(); + } + + @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(); + + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("quoted string name"); + assertThat(rule.getLhs()).isNotNull(); + assertThat(((String) rule.getConsequence()).trim()).isEmpty(); + } + + @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(); + + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("rule1"); + final AttributeDescr att = rule.getAttributes().get("no-loop"); + assertThat(att.getValue()).isEqualTo("false"); + assertThat(att.getName()).isEqualTo("no-loop"); + } + + @Test + void parse_autofocus() throws Exception { + String source = readResource("autofocus.drl"); + PackageDescr pkg = parser.parse(source); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("rule1"); + final AttributeDescr att = rule.getAttributes().get("auto-focus"); + assertThat(att.getValue()).isEqualTo("true"); + assertThat(att.getName()).isEqualTo("auto-focus"); + } + + @Test + void parse_ruleFlowGroup() throws Exception { + String source = readResource("ruleflowgroup.drl"); + PackageDescr pkg = parser.parse(source); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + RuleDescr rule = pkg.getRules().get(0); + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("rule1"); + final AttributeDescr att = rule.getAttributes().get("ruleflow-group"); + assertThat(att.getValue()).isEqualTo("a group"); + assertThat(att.getName()).isEqualTo("ruleflow-group"); + } + + @Test + void parse_consequenceWithDeclaration() throws Exception { + String source = readResource("declaration-in-consequence.drl"); + PackageDescr pkg = parser.parse(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(); + + assertThat(rule.getName()).isEqualTo("myrule"); + + final String expected = "int i = 0; i = 1; i / 1; i == 1; i(i); i = 'i'; i.i.i; ii; i=\"i\"; ++i;" + + "i++; --i; i--; i += i; i -= i; i *= i; i /= i;" + + "int i = 5;" + "for(int j; j 0).isTrue(); + assertThat(((String) rule.getConsequence()).indexOf("--") > 0).isTrue(); + assertThat(((String) rule.getConsequence()).indexOf("+=") > 0).isTrue(); + assertThat(((String) rule.getConsequence()).indexOf("==") > 0).isTrue(); + assertThat(((String) rule.getConsequence()).indexOf("i++") > 0).isTrue(); + // note, need to assert that "i++" is preserved as is, no extra spaces. + } + + @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); + RuleDescr rule = pkg.getRules().get(0); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(rule).isNotNull(); + + AndDescr lhs = rule.getLhs(); + assertThat(lhs.getDescrs()).hasSize(1); + assertThat(((OrDescr) lhs.getDescrs().get(0)).getDescrs()).hasSize(2); + } + + @Test + void parse_lhsWithStringQuotes() { + final String text = "rule X when Person( location==\"atlanta\\\"\") then end\n"; + PackageDescr pkg = parser.parse(text); + RuleDescr rule = pkg.getRules().get(0); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(rule).isNotNull(); + + AndDescr lhs = rule.getLhs(); + ExprConstraintDescr constr = (ExprConstraintDescr) ((PatternDescr) lhs.getDescrs().get(0)).getDescrs().get(0); + + assertThat(constr.getText()).isEqualToIgnoringWhitespace("location==\"atlanta\\\"\""); + } + + @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); + RuleDescr rule = pkg.getRules().get(0); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(rule).isNotNull(); + + AndDescr lhs = rule.getLhs(); + ExprConstraintDescr constr = (ExprConstraintDescr) ((PatternDescr) lhs.getDescrs().get(0)).getDescrs().get(1); + + assertThat(constr.getText()).isEqualToIgnoringWhitespace("type == \"s\\tti\\\"lto\\nn\""); + } + + @Test + void parse_literalBoolAndNegativeNumbersRule() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("literal_bool_and_negative.drl"); + + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat(rule.getLhs()).isNotNull(); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace("cons();"); + + final AndDescr lhs = rule.getLhs(); + assertThat(lhs.getDescrs()).hasSize(3); + + PatternDescr pattern = (PatternDescr) lhs.getDescrs().get(0); + assertThat(pattern.getConstraint().getDescrs()).hasSize(1); + AndDescr fieldAnd = (AndDescr) pattern.getConstraint(); + ExprConstraintDescr fld = (ExprConstraintDescr) fieldAnd.getDescrs().get(0); + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace("bar == false"); + + pattern = (PatternDescr) lhs.getDescrs().get(1); + assertThat(pattern.getConstraint().getDescrs()).hasSize(1); + + fieldAnd = (AndDescr) pattern.getConstraint(); + fld = (ExprConstraintDescr) fieldAnd.getDescrs().get(0); + + assertThat(fld.getText()).isEqualToIgnoringWhitespace("boo > -42"); + + pattern = (PatternDescr) lhs.getDescrs().get(2); + assertThat(pattern.getConstraint().getDescrs()).hasSize(1); + + fieldAnd = (AndDescr) pattern.getConstraint(); + fld = (ExprConstraintDescr) fieldAnd.getDescrs().get(0); + + assertThat(fld.getText()).isEqualToIgnoringWhitespace("boo > -42.42"); + } + + @Test + void parse_emptyPattern() throws Exception { + String source = readResource("test_EmptyPattern.drl"); + PackageDescr pkg = parser.parse(source); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + assertThat(pkg.getRules()).hasSize(1); + final RuleDescr ruleDescr = pkg.getRules().get(0); + assertThat(ruleDescr.getName()).isEqualTo("simple rule"); + assertThat(ruleDescr.getLhs()).isNotNull(); + assertThat(ruleDescr.getLhs().getDescrs()).hasSize(1); + final PatternDescr patternDescr = (PatternDescr) ruleDescr.getLhs().getDescrs().get(0); + assertThat(patternDescr.getConstraint().getDescrs()).isEmpty(); // this + assertThat(patternDescr.getObjectType()).isEqualTo("Cheese"); + } + + @Test + void parse_simpleMethodCallWithFrom() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("test_SimpleMethodCallWithFrom.drl"); + final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); + final FromDescr from = (FromDescr) pattern.getSource(); + final MVELExprDescr method = (MVELExprDescr) from.getDataSource(); + + assertThat(method.getExpression()).isEqualToIgnoringWhitespace("something.doIt( foo,bar,42,\"hello\",[ a : \"b\", \"something\" : 42, \"a\" : foo, x : [x:y]],\"end\", [a, \"b\", 42] )"); + } + + @Test + void parse_simpleFunctionCallWithFrom() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("test_SimpleFunctionCallWithFrom.drl"); + final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get(0); + final FromDescr from = (FromDescr) pattern.getSource(); + final MVELExprDescr func = (MVELExprDescr) from.getDataSource(); + + assertThat(func.getExpression()).isEqualToIgnoringWhitespace("doIt( foo,bar,42,\"hello\",[ a : \"b\", \"something\" : 42, \"a\" : foo, x : [x:y]],\"end\", [a, \"b\", 42] )"); + } + + @Test + void parse_simpleAccessorWithFrom() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("test_SimpleAccessorWithFrom.drl"); + final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final FromDescr from = (FromDescr) pattern.getSource(); + final MVELExprDescr accessor = (MVELExprDescr) from.getDataSource(); + + assertThat(accessor.getExpression()).isEqualTo("something.doIt"); + } + + @Test + void parse_simpleAccessorAndArgWithFrom() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("test_SimpleAccessorArgWithFrom.drl"); + final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final FromDescr from = (FromDescr) pattern.getSource(); + final MVELExprDescr accessor = (MVELExprDescr) from.getDataSource(); + + assertThat(accessor.getExpression()).isEqualTo("something.doIt[\"key\"]"); + } + + @Test + void parse_complexChainedAccessor() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("test_ComplexChainedCallWithFrom.drl"); + + final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final FromDescr from = (FromDescr) pattern.getSource(); + final MVELExprDescr accessor = (MVELExprDescr) from.getDataSource(); + + assertThat(accessor.getExpression()).isEqualToIgnoringWhitespace("doIt1( foo,bar,42,\"hello\",[ a : \"b\"], [a, \"b\", 42] ).doIt2(bar, [a, \"b\", 42]).field[\"key\"]"); + } + + @Test + void parse_from() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("from.drl"); + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("using_from"); + + assertThat(rule.getLhs().getDescrs()).hasSize(9); + } + + @Test + void parse_simpleRuleWithBindings() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("simple_rule.drl"); + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("simple_rule"); + + assertThat(rule.getConsequenceLine()).isEqualTo(22); + assertThat(rule.getConsequencePattern()).isEqualTo(2); + + final AndDescr lhs = rule.getLhs(); + + assertThat(lhs).isNotNull(); + + assertThat(lhs.getDescrs()).hasSize(3); + + // Check first pattern + final PatternDescr first = (PatternDescr) lhs.getDescrs().get(0); + assertThat(first.getIdentifier()).isEqualTo("foo3"); + assertThat(first.getObjectType()).isEqualTo("Bar"); + + assertThat(first.getConstraint().getDescrs()).hasSize(1); + + AndDescr fieldAnd = (AndDescr) first.getConstraint(); + ExprConstraintDescr constraint = (ExprConstraintDescr) fieldAnd.getDescrs().get(0); + assertThat(constraint).isNotNull(); + + assertThat(constraint.getExpression()).isEqualToIgnoringWhitespace("a==3"); + + // Check second pattern + final PatternDescr second = (PatternDescr) lhs.getDescrs().get(1); + assertThat(second.getIdentifier()).isEqualTo("foo4"); + assertThat(second.getObjectType()).isEqualTo("Bar"); + + // no constraints, only a binding + fieldAnd = (AndDescr) second.getConstraint(); + assertThat(fieldAnd.getDescrs()).hasSize(1); + + final ExprConstraintDescr binding = (ExprConstraintDescr) second.getConstraint().getDescrs().get(0); + assertThat(binding.getExpression()).isEqualToIgnoringWhitespace("a4:a==4"); + + // Check third pattern + final PatternDescr third = (PatternDescr) lhs.getDescrs().get(2); + assertThat(third.getIdentifier()).isNull(); + assertThat(third.getObjectType()).isEqualTo("Baz"); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace("if ( a == b ) { " + " assert( foo3 );" + "} else {" + " retract( foo4 );" + "}" + " System.out.println( a4 );"); + } + + @Test + void parse_multipleRestrictionsConstraint() throws Exception { + RuleDescr rule = parseAndGetFirstRuleDescrFromFile("restrictions_test.drl"); + assertThat(rule).isNotNull(); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace("consequence();"); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat(rule.getLhs().getDescrs()).hasSize(2); + + // The first pattern, with 2 restrictions on a single field (plus a + // connective) + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + assertThat( pattern.getConstraint().getDescrs()).hasSize(1); + + AndDescr and = (AndDescr) pattern.getConstraint(); + ExprConstraintDescr fld = (ExprConstraintDescr) and.getDescrs().get( 0 ); + assertThat(fld.getExpression()).isEqualTo("age > 30 && < 40"); + + // the second col, with 2 fields, the first with 2 restrictions, the + // second field with one + pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(pattern.getObjectType()).isEqualTo("Vehicle"); + assertThat(pattern.getConstraint().getDescrs()).hasSize(2); + + and = (AndDescr) pattern.getConstraint(); + fld = (ExprConstraintDescr) and.getDescrs().get( 0 ); + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace( "type == \"sedan\" || == \"wagon\""); + + // now the second field + fld = (ExprConstraintDescr) and.getDescrs().get( 1 ); + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace( "age < 3"); + } + +//------------------------------------------------------------------------- +// DROOLS-7271 : ported from RuleParserTest +// Failing tests are annotated with @Disabled. We can fix issues one by one +//------------------------------------------------------------------------- + + @Disabled("Priority : Mid | implement Descr lineNumber") + @Test + public void parse_LineNumberInAST() throws Exception { + // also see testSimpleExpander to see how this works with an expander + // (should be the same). + + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "simple_rule.drl" ); + + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("simple_rule"); + + assertThat(rule.getConsequenceLine()).isEqualTo(22); + assertThat(rule.getConsequencePattern()).isEqualTo(2); + + final AndDescr lhs = rule.getLhs(); + + assertThat(lhs).isNotNull(); + + assertThat(lhs.getDescrs().size()).isEqualTo(3); + + // Check first pattern + final PatternDescr first = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(first.getIdentifier()).isEqualTo("foo3"); + assertThat(first.getObjectType()).isEqualTo("Bar"); + assertThat(first.getConstraint().getDescrs().size()).isEqualTo(1); + + // Check second pattern + final PatternDescr second = (PatternDescr) lhs.getDescrs().get( 1 ); + assertThat(second.getIdentifier()).isEqualTo("foo4"); + assertThat(second.getObjectType()).isEqualTo("Bar"); + + final PatternDescr third = (PatternDescr) lhs.getDescrs().get( 2 ); + assertThat(third.getObjectType()).isEqualTo("Baz"); + + assertThat(first.getLine()).isEqualTo(19); + assertThat(second.getLine()).isEqualTo(20); + assertThat(third.getLine()).isEqualTo(21); + } + + @Test + 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*"; + assertThat(Pattern.compile(expected, + Pattern.DOTALL | Pattern.MULTILINE).matcher(rhs).matches()).isTrue(); + } + + @Test + 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(); + + assertThat(rule.getName()).isEqualTo("simple_rule"); + + final AndDescr lhs = rule.getLhs(); + + assertThat(lhs).isNotNull(); + + assertThat(lhs.getDescrs().size()).isEqualTo(3); + + // System.err.println( lhs.getDescrs() ); + + // Check first pattern + final PatternDescr first = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(first.getIdentifier()).isEqualTo("foo3"); + assertThat(first.getObjectType()).isEqualTo("Bar"); + + assertThat(first.getConstraint().getDescrs().size()).isEqualTo(1); + + // LiteralDescr constraint = (LiteralDescr) first.getDescrs().get( 0 ); + AndDescr and = (AndDescr) first.getConstraint(); + ExprConstraintDescr fld = (ExprConstraintDescr) and.getDescrs().get( 0 ); + assertThat(fld).isNotNull(); + + assertThat(fld.getExpression()).isEqualTo("a==3"); + + // Check second pattern + final PatternDescr second = (PatternDescr) lhs.getDescrs().get( 1 ); + assertThat(second.getIdentifier()).isEqualTo("foo4"); + assertThat(second.getObjectType()).isEqualTo("Bar"); + + assertThat(second.getDescrs().size()).isEqualTo(1); + + final ExprConstraintDescr fieldBindingDescr = (ExprConstraintDescr) second.getDescrs().get( 0 ); + assertThat(fieldBindingDescr.getExpression()).isEqualTo("a4:a==4"); + + // Check third pattern + final PatternDescr third = (PatternDescr) lhs.getDescrs().get( 2 ); + assertThat(third.getIdentifier()).isNull(); + assertThat(third.getObjectType()).isEqualTo("Baz"); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace("if ( a == b ) { " + " assert( foo3 );" + "} else {" + " retract( foo4 );" + "}" + " System.out.println( a4 );"); + } + + @Test + 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"); + + final AndDescr lhs = rule.getLhs(); + assertThat(lhs.getDescrs().size()).isEqualTo(1); + final NotDescr not = (NotDescr) lhs.getDescrs().get( 0 ); + assertThat(not.getDescrs().size()).isEqualTo(1); + final PatternDescr pattern = (PatternDescr) not.getDescrs().get( 0 ); + + assertThat(pattern.getObjectType()).isEqualTo("Cheese"); + assertThat(pattern.getConstraint().getDescrs().size()).isEqualTo(1); + + final AndDescr and = (AndDescr) pattern.getConstraint(); + final ExprConstraintDescr fld = (ExprConstraintDescr) and.getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualTo("type == \"stilton\""); + } + + @Test + 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 ); + + assertThat(rule).isNotNull(); + assertThat(rule.getName()).isEqualTo("simple_rule"); + + final AndDescr lhs = rule.getLhs(); + assertThat(lhs.getDescrs().size()).isEqualTo(2); + final NotDescr not = (NotDescr) lhs.getDescrs().get( 0 ); + assertThat(not.getDescrs().size()).isEqualTo(1); + final PatternDescr pattern = (PatternDescr) not.getDescrs().get( 0 ); + + assertThat(pattern.getObjectType()).isEqualTo("Cheese"); + + final ExistsDescr ex = (ExistsDescr) lhs.getDescrs().get(1 ); + assertThat(ex.getDescrs().size()).isEqualTo(1); + final PatternDescr exPattern = (PatternDescr) ex.getDescrs().get( 0 ); + assertThat(exPattern.getObjectType()).isEqualTo("Foo"); + } + + @Test + public void parse_SimpleQuery() throws Exception { + final QueryDescr query = parseAndGetFirstQueryDescrFromFile( + "simple_query.drl" ); + + assertThat(query).isNotNull(); + + assertThat(query.getName()).isEqualTo("simple_query"); + + final AndDescr lhs = query.getLhs(); + + assertThat(lhs).isNotNull(); + + assertThat(lhs.getDescrs().size()).isEqualTo(3); + + // Check first pattern + final PatternDescr first = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(first.getIdentifier()).isEqualTo("foo3"); + assertThat(first.getObjectType()).isEqualTo("Bar"); + + assertThat(first.getConstraint().getDescrs().size()).isEqualTo(1); + + AndDescr and = (AndDescr) first.getConstraint(); + ExprConstraintDescr fld = (ExprConstraintDescr) and.getDescrs().get( 0 ); + assertThat(fld).isNotNull(); + + assertThat(fld.getExpression()).isEqualTo("a==3"); + + // Check second pattern + final PatternDescr second = (PatternDescr) lhs.getDescrs().get( 1 ); + assertThat(second.getIdentifier()).isEqualTo("foo4"); + assertThat(second.getObjectType()).isEqualTo("Bar"); + + assertThat(second.getDescrs().size()).isEqualTo(1); + // check it has field bindings. + final ExprConstraintDescr bindingDescr = (ExprConstraintDescr) second.getDescrs().get( 0 ); + assertThat(bindingDescr.getExpression()).isEqualTo("a4:a==4"); + } + + @Test + public void parse_QueryRuleMixed() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "query_and_rule.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(4); // as queries are rules + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getName()).isEqualTo("bar"); + + QueryDescr query = (QueryDescr) pkg.getRules().get( 1 ); + assertThat(query.getName()).isEqualTo("simple_query"); + + rule = (RuleDescr) pkg.getRules().get( 2 ); + assertThat(rule.getName()).isEqualTo("bar2"); + + query = (QueryDescr) pkg.getRules().get( 3 ); + assertThat(query.getName()).isEqualTo("simple_query2"); + } + + @Test + public void parse_MultipleRules() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "multiple_rules.drl" ); + + final List rules = pkg.getRules(); + + assertThat(rules.size()).isEqualTo(2); + + final RuleDescr rule0 = rules.get( 0 ); + assertThat(rule0.getName()).isEqualTo("Like Stilton"); + + final RuleDescr rule1 = rules.get( 1 ); + assertThat(rule1.getName()).isEqualTo("Like Cheddar"); + + // checkout the first rule + AndDescr lhs = rule1.getLhs(); + assertThat(lhs).isNotNull(); + assertThat(lhs.getDescrs().size()).isEqualTo(1); + assertThat((String) rule0.getConsequence()).isEqualToIgnoringWhitespace( "System.out.println(\"I like \" + t);"); + + // Check first pattern + PatternDescr first = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(first.getObjectType()).isEqualTo("Cheese"); + + // checkout the second rule + lhs = rule1.getLhs(); + assertThat(lhs).isNotNull(); + assertThat(lhs.getDescrs().size()).isEqualTo(1); + assertThat((String) rule1.getConsequence()).isEqualToIgnoringWhitespace( "System.out.println(\"I like \" + t);"); + + // Check first pattern + first = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(first.getObjectType()).isEqualTo("Cheese"); + } + + @Disabled("Priority : low | Not yet support DSL") + @Test + public void parse_ExpanderLineSpread() throws Exception { +// final DrlParser parser = new DrlParser(LanguageLevelOption.DRL6); +// final PackageDescr pkg = parser.parse( this.getReader( "expander_spread_lines.dslr" ), +// this.getReader( "complex.dsl" ) ); +// +// assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); +// +// final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); +// assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); +// +// final OrDescr or = (OrDescr) rule.getLhs().getDescrs().get( 0 ); +// assertThat(or.getDescrs().size()).isEqualTo(2); +// assertThat( (String) rule.getConsequence() ).isNotNull(); + + } + + @Disabled("Priority : low | Not yet support DSL") + @Test + public void parse_ExpanderMultipleConstraints() throws Exception { +// final DrlParser parser = new DrlParser(LanguageLevelOption.DRL6); +// final PackageDescr pkg = parser.parse( this.getReader( "expander_multiple_constraints.dslr" ), +// this.getReader( "multiple_constraints.dsl" ) ); +// +// assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); +// +// final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); +// assertThat(rule.getLhs().getDescrs().size()).isEqualTo(2); +// +// PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); +// assertThat(pattern.getObjectType()).isEqualTo("Person"); +// +// assertThat(pattern.getConstraint().getDescrs().size()).isEqualTo(2); +// assertThat(((ExprConstraintDescr) pattern.getConstraint().getDescrs().get(0)).getExpression()).isEqualTo("age < 42"); +// assertThat(((ExprConstraintDescr) pattern.getConstraint().getDescrs().get(1)).getExpression()).isEqualTo("location==atlanta"); +// +// pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); +// assertThat(pattern.getObjectType()).isEqualTo("Bar"); +// +// assertThat( (String) rule.getConsequence() ).isNotNull(); + + } + + @Disabled("Priority : low | Not yet support DSL") + @Test + public void parse_ExpanderMultipleConstraintsFlush() throws Exception { +// final DrlParser parser = new DrlParser(LanguageLevelOption.DRL6); +// // this is similar to the other test, but it requires a flush to add the +// // constraints +// final PackageDescr pkg = parser.parse( this.getReader( "expander_multiple_constraints_flush.dslr" ), +// this.getReader( "multiple_constraints.dsl" ) ); +// +// assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); +// +// final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); +// assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); +// +// final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); +// assertThat(pattern.getObjectType()).isEqualTo("Person"); +// +// assertThat(pattern.getConstraint().getDescrs().size()).isEqualTo(2); +// assertThat(((ExprConstraintDescr) pattern.getConstraint().getDescrs().get(0)).getExpression()).isEqualTo("age < 42"); +// assertThat(((ExprConstraintDescr) pattern.getConstraint().getDescrs().get(1)).getExpression()).isEqualTo("location==atlanta"); +// +// assertThat( (String) rule.getConsequence() ).isNotNull(); + + } + + @Test + public void parse_BasicBinding() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "basic_binding.drl" ); + + final RuleDescr ruleDescr = (RuleDescr) pkg.getRules().get( 0 ); + + final AndDescr lhs = ruleDescr.getLhs(); + assertThat(lhs.getDescrs().size()).isEqualTo(1); + final PatternDescr cheese = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(cheese.getObjectType()).isEqualTo("Cheese"); + assertThat(cheese.getConstraint().getDescrs().size()).isEqualTo(1); + final ExprConstraintDescr fieldBinding = (ExprConstraintDescr) cheese.getDescrs().get( 0 ); + assertThat(fieldBinding.getExpression()).isEqualToIgnoringWhitespace("$type:type"); + } + + @Test + public void parse_BoundVariables() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "bindings.drl" ); + + final RuleDescr ruleDescr = (RuleDescr) pkg.getRules().get( 0 ); + + final AndDescr lhs = ruleDescr.getLhs(); + assertThat(lhs.getDescrs().size()).isEqualTo(2); + final PatternDescr cheese = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(cheese.getObjectType()).isEqualTo("Cheese"); + assertThat(cheese.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fieldBinding = (ExprConstraintDescr) cheese.getDescrs().get( 0 ); + assertThat(fieldBinding.getExpression()).isEqualTo("$type : type == \"stilton\""); + + final PatternDescr person = (PatternDescr) lhs.getDescrs().get( 1 ); + assertThat(person.getDescrs().size()).isEqualTo(2); + fieldBinding = (ExprConstraintDescr) person.getDescrs().get( 0 ); + assertThat(fieldBinding.getExpression()).isEqualTo("$name : name == \"bob\""); + + ExprConstraintDescr fld = (ExprConstraintDescr) person.getDescrs().get( 1 ); + assertThat(fld.getExpression()).isEqualTo("likes == $type"); + } + + @Test + public void parse_OrNesting() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "or_nesting.drl" ); + + assertThat(pkg).isNotNull(); + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final OrDescr or = (OrDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(or.getDescrs().size()).isEqualTo(2); + + final PatternDescr first = (PatternDescr) or.getDescrs().get( 0 ); + assertThat(first.getObjectType()).isEqualTo("Person"); + + final AndDescr and = (AndDescr) or.getDescrs().get( 1 ); + assertThat(and.getDescrs().size()).isEqualTo(2); + + final PatternDescr left = (PatternDescr) and.getDescrs().get( 0 ); + assertThat(left.getObjectType()).isEqualTo("Person"); + + final PatternDescr right = (PatternDescr) and.getDescrs().get( 1 ); + assertThat(right.getObjectType()).isEqualTo("Cheese"); + } + + /** Test that explicit "&&", "||" works as expected */ + @Test + public void parse_AndOrRules() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "and_or_rule.drl" ); + + assertThat(pkg).isNotNull(); + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + + // we will have 3 children under the main And node + final AndDescr and = rule.getLhs(); + assertThat(and.getDescrs().size()).isEqualTo(3); + + PatternDescr left = (PatternDescr) and.getDescrs().get( 0 ); + PatternDescr right = (PatternDescr) and.getDescrs().get( 1 ); + assertThat(left.getObjectType()).isEqualTo("Person"); + assertThat(right.getObjectType()).isEqualTo("Cheese"); + + assertThat(left.getConstraint().getDescrs().size()).isEqualTo(1); + + ExprConstraintDescr fld = (ExprConstraintDescr) left.getConstraint().getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualTo("name == \"mark\""); + + assertThat(right.getConstraint().getDescrs().size()).isEqualTo(1); + + fld = (ExprConstraintDescr) right.getConstraint().getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualTo("type == \"stilton\""); + + // now the "||" part + final OrDescr or = (OrDescr) and.getDescrs().get( 2 ); + assertThat(or.getDescrs().size()).isEqualTo(2); + left = (PatternDescr) or.getDescrs().get( 0 ); + right = (PatternDescr) or.getDescrs().get( 1 ); + assertThat(left.getObjectType()).isEqualTo("Person"); + assertThat(right.getObjectType()).isEqualTo("Cheese"); + assertThat(left.getConstraint().getDescrs().size()).isEqualTo(1); + + fld = (ExprConstraintDescr) left.getConstraint().getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualTo("name == \"mark\""); + + assertThat(right.getConstraint().getDescrs().size()).isEqualTo(1); + + fld = (ExprConstraintDescr) right.getConstraint().getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualTo("type == \"stilton\""); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "System.out.println( \"Mark and Michael\" );"); + } + + /** test basic foo : Fact() || Fact() stuff */ + @Test + public void parse_OrWithBinding() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "or_binding.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(2); + + final OrDescr or = (OrDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(or.getDescrs().size()).isEqualTo(2); + + final PatternDescr leftPattern = (PatternDescr) or.getDescrs().get( 0 ); + assertThat(leftPattern.getObjectType()).isEqualTo("Person"); + assertThat(leftPattern.getIdentifier()).isEqualTo("foo"); + + final PatternDescr rightPattern = (PatternDescr) or.getDescrs().get( 1 ); + assertThat(rightPattern.getObjectType()).isEqualTo("Person"); + assertThat(rightPattern.getIdentifier()).isEqualTo("foo"); + + final PatternDescr cheeseDescr = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(cheeseDescr.getObjectType()).isEqualTo("Cheese"); + assertThat(cheeseDescr.getIdentifier()).isEqualTo(null); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "System.out.println( \"Mark and Michael\" + bar );"); + } + + /** test basic foo : Fact() || Fact() stuff binding to an "or" */ + @Test + public void parse_OrBindingComplex() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "or_binding_complex.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final OrDescr or = (OrDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(or.getDescrs().size()).isEqualTo(2); + + // first fact + final PatternDescr firstFact = (PatternDescr) or.getDescrs().get( 0 ); + assertThat(firstFact.getObjectType()).isEqualTo("Person"); + assertThat(firstFact.getIdentifier()).isEqualTo("foo"); + + // second "option" + final PatternDescr secondFact = (PatternDescr) or.getDescrs().get( 1 ); + assertThat(secondFact.getObjectType()).isEqualTo("Person"); + assertThat(secondFact.getConstraint().getDescrs().size()).isEqualTo(1); + assertThat(secondFact.getIdentifier()).isEqualTo("foo"); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "System.out.println( \"Mark and Michael\" + bar );"); + } + + @Test + public void parse_OrBindingWithBrackets() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "or_binding_with_brackets.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final OrDescr or = (OrDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(or.getDescrs().size()).isEqualTo(2); + + // first fact + final PatternDescr firstFact = (PatternDescr) or.getDescrs().get( 0 ); + assertThat(firstFact.getObjectType()).isEqualTo("Person"); + assertThat(firstFact.getIdentifier()).isEqualTo("foo"); + + // second "option" + final PatternDescr secondFact = (PatternDescr) or.getDescrs().get( 0 ); + assertThat(secondFact.getObjectType()).isEqualTo("Person"); + assertThat(secondFact.getIdentifier()).isEqualTo("foo"); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "System.out.println( \"Mark and Michael\" + bar );"); + } + + @Test + void parenthesesOrAndOr() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "brackets_precedence.drl" ); + + assertThat(pkg.getRules()).hasSize(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + + final AndDescr rootAnd = (AndDescr) rule.getLhs(); + + assertThat(rootAnd.getDescrs()).hasSize(2); + + final OrDescr leftOr = (OrDescr) rootAnd.getDescrs().get( 0 ); + + assertThat(leftOr.getDescrs()).hasSize(2); + final NotDescr not = (NotDescr) leftOr.getDescrs().get( 0 ); + final PatternDescr foo1 = (PatternDescr) not.getDescrs().get( 0 ); + assertThat(foo1.getObjectType()).isEqualTo("Foo"); + final PatternDescr foo2 = (PatternDescr) leftOr.getDescrs().get( 1 ); + assertThat(foo2.getObjectType()).isEqualTo("Foo"); + + final OrDescr rightOr = (OrDescr) rootAnd.getDescrs().get( 1 ); + + assertThat(rightOr.getDescrs()).hasSize(2); + final PatternDescr shoes = (PatternDescr) rightOr.getDescrs().get( 0 ); + assertThat(shoes.getObjectType()).isEqualTo("Shoes"); + final PatternDescr butt = (PatternDescr) rightOr.getDescrs().get( 1 ); + assertThat(butt.getObjectType()).isEqualTo("Butt"); + } + + @Test + void parenthesesAndOrOr() { + final String drl = "rule and_or_or\n" + + " when\n" + + " (Foo(x == 1) and Bar(x == 2)) or (Foo(x == 3) or Bar(x == 4))\n" + + " then\n" + + "end"; + PackageDescr pkg = parser.parse(drl); + + assertThat(pkg.getRules()).hasSize(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); + final AndDescr rootAnd = (AndDescr) rule.getLhs(); + assertThat(rootAnd.getDescrs()).hasSize(1); + + final OrDescr topOr = (OrDescr) rootAnd.getDescrs().get(0); + assertThat(topOr.getDescrs()).hasSize(2); + + final AndDescr leftAnd = (AndDescr) topOr.getDescrs().get(0); + assertThat(leftAnd.getDescrs()).hasSize(2); + final PatternDescr foo1 = (PatternDescr) leftAnd.getDescrs().get(0); + assertThat(foo1.getObjectType()).isEqualTo("Foo"); + final PatternDescr bar1 = (PatternDescr) leftAnd.getDescrs().get(1); + assertThat(bar1.getObjectType()).isEqualTo("Bar"); + + final OrDescr rightOr = (OrDescr) topOr.getDescrs().get(1); + assertThat(rightOr.getDescrs()).hasSize(2); + final PatternDescr foo2 = (PatternDescr) rightOr.getDescrs().get(0); + assertThat(foo2.getObjectType()).isEqualTo("Foo"); + final PatternDescr bar2 = (PatternDescr) rightOr.getDescrs().get(1); + assertThat(bar2.getObjectType()).isEqualTo("Bar"); + } + + @Test + void parenthesesOrAndAnd() { + final String drl = "rule or_and_and\n" + + " when\n" + + " (Foo(x == 1) or Bar(x == 2)) and (Foo(x == 3) and Bar(x == 4))\n" + + " then\n" + + "end"; + PackageDescr pkg = parser.parse(drl); + + assertThat(pkg.getRules()).hasSize(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); + final AndDescr rootAnd = (AndDescr) rule.getLhs(); + assertThat(rootAnd.getDescrs()).hasSize(2); + + final OrDescr leftOr = (OrDescr) rootAnd.getDescrs().get(0); + assertThat(leftOr.getDescrs()).hasSize(2); + final PatternDescr foo1 = (PatternDescr) leftOr.getDescrs().get(0); + assertThat(foo1.getObjectType()).isEqualTo("Foo"); + final PatternDescr bar1 = (PatternDescr) leftOr.getDescrs().get(1); + assertThat(bar1.getObjectType()).isEqualTo("Bar"); + + final AndDescr rightAnd = (AndDescr) rootAnd.getDescrs().get(1); + assertThat(rightAnd.getDescrs()).hasSize(2); + final PatternDescr foo2 = (PatternDescr) rightAnd.getDescrs().get(0); + assertThat(foo2.getObjectType()).isEqualTo("Foo"); + final PatternDescr bar2 = (PatternDescr) rightAnd.getDescrs().get(1); + assertThat(bar2.getObjectType()).isEqualTo("Bar"); + } + + @Test + void multipleLevelNestAndOrOrOrAnd() throws Exception { + final String drl = "rule and_or_or_or_and\n" + + " when\n" + + " (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); + + assertThat(pkg.getRules()).hasSize(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); + final AndDescr rootAnd = (AndDescr) rule.getLhs(); + assertThat(rootAnd.getDescrs()).hasSize(1); + + final OrDescr topOr = (OrDescr) rootAnd.getDescrs().get(0); + assertThat(topOr.getDescrs()).hasSize(2); + + final AndDescr leftAnd = (AndDescr) topOr.getDescrs().get(0); + assertThat(leftAnd.getDescrs()).hasSize(2); + final PatternDescr foo1 = (PatternDescr) leftAnd.getDescrs().get(0); + assertThat(foo1.getObjectType()).isEqualTo("Foo"); + final OrDescr leftOr = (OrDescr) leftAnd.getDescrs().get(1); + assertThat(leftOr.getDescrs()).hasSize(2); + final PatternDescr bar1 = (PatternDescr) leftOr.getDescrs().get(0); + assertThat(bar1.getObjectType()).isEqualTo("Bar"); + final PatternDescr foo2 = (PatternDescr) leftOr.getDescrs().get(1); + assertThat(foo2.getObjectType()).isEqualTo("Foo"); + + final OrDescr rightOr = (OrDescr) topOr.getDescrs().get(1); + assertThat(rightOr.getDescrs()).hasSize(2); + final PatternDescr bar2 = (PatternDescr) rightOr.getDescrs().get(0); + assertThat(bar2.getObjectType()).isEqualTo("Bar"); + final AndDescr rightAnd = (AndDescr) rightOr.getDescrs().get(1); + assertThat(rightAnd.getDescrs()).hasSize(2); + final PatternDescr foo3 = (PatternDescr) rightAnd.getDescrs().get(0); + assertThat(foo3.getObjectType()).isEqualTo("Foo"); + final PatternDescr bar3 = (PatternDescr) rightAnd.getDescrs().get(1); + assertThat(bar3.getObjectType()).isEqualTo("Bar"); + } + + @Test + void multipleLevelNestWithThreeOrSiblings() throws Exception { + final String drl = "rule nest_or_siblings\n" + + " when\n" + + " (A() or (B() or C() or (D() and E())))\n" + + " then\n" + + "end"; + PackageDescr pkg = parser.parse(drl); + + assertThat(pkg.getRules()).hasSize(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); + final AndDescr rootAnd = (AndDescr) rule.getLhs(); + assertThat(rootAnd.getDescrs()).hasSize(1); + + final OrDescr topOr = (OrDescr) rootAnd.getDescrs().get(0); + assertThat(topOr.getDescrs()).hasSize(2); + + final PatternDescr leftPattern = (PatternDescr) topOr.getDescrs().get(0); + assertThat(leftPattern.getObjectType()).isEqualTo("A"); + + final OrDescr rightOr = (OrDescr) topOr.getDescrs().get(1); + assertThat(rightOr.getDescrs()).as("top level Or has 3 sibling children").hasSize(3); + final PatternDescr bPattern = (PatternDescr) rightOr.getDescrs().get(0); + assertThat(bPattern.getObjectType()).isEqualTo("B"); + final PatternDescr cPattern = (PatternDescr) rightOr.getDescrs().get(1); + assertThat(cPattern.getObjectType()).isEqualTo("C"); + final AndDescr deAnd = (AndDescr) rightOr.getDescrs().get(2); + assertThat(deAnd.getDescrs()).hasSize(2); + + final PatternDescr dPattern = (PatternDescr) deAnd.getDescrs().get(0); + assertThat(dPattern.getObjectType()).isEqualTo("D"); + final PatternDescr ePattern = (PatternDescr) deAnd.getDescrs().get(1); + assertThat(ePattern.getObjectType()).isEqualTo("E"); + } + + @Test + public void existsMultipleLevelNestWithThreeOrSiblings() throws Exception { + final String drl = "rule nest_or_siblings\n" + + " when\n" + + " exists(A() or (B() or C() or (D() and E())))\n" + + " then\n" + + "end"; + PackageDescr pkg = parser.parse(drl); + + assertThat(pkg.getRules()).hasSize(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get(0); + final AndDescr rootAnd = (AndDescr) rule.getLhs(); + assertThat(rootAnd.getDescrs()).hasSize(1); + + final ExistsDescr topExists = (ExistsDescr) rootAnd.getDescrs().get(0); + assertThat(topExists.getDescrs()).hasSize(1); + + final OrDescr topOr = (OrDescr) topExists.getDescrs().get(0); + assertThat(topOr.getDescrs()).hasSize(2); + + final PatternDescr leftPattern = (PatternDescr) topOr.getDescrs().get(0); + assertThat(leftPattern.getObjectType()).isEqualTo("A"); + + final OrDescr rightOr = (OrDescr) topOr.getDescrs().get(1); + assertThat(rightOr.getDescrs()).hasSize(3); + final PatternDescr bPattern = (PatternDescr) rightOr.getDescrs().get(0); + assertThat(bPattern.getObjectType()).isEqualTo("B"); + final PatternDescr cPattern = (PatternDescr) rightOr.getDescrs().get(1); + assertThat(cPattern.getObjectType()).isEqualTo("C"); + final AndDescr deAnd = (AndDescr) rightOr.getDescrs().get(2); + assertThat(deAnd.getDescrs()).hasSize(2); + + final PatternDescr dPattern = (PatternDescr) deAnd.getDescrs().get(0); + assertThat(dPattern.getObjectType()).isEqualTo("D"); + final PatternDescr ePattern = (PatternDescr) deAnd.getDescrs().get(1); + assertThat(ePattern.getObjectType()).isEqualTo("E"); + } + + @Test + public void parse_EvalMultiple() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "eval_multiple.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(4); + + final EvalDescr eval = (EvalDescr) rule.getLhs().getDescrs().get(0 ); + assertThat((String) eval.getContent()).isEqualToIgnoringWhitespace( "abc(\"foo\") + 5"); + + final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(pattern.getObjectType()).isEqualTo("Foo"); + + } + + @Test + public void parse_WithEval() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "with_eval.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(3); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(pattern.getObjectType()).isEqualTo("Foo"); + pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(pattern.getObjectType()).isEqualTo("Bar"); + + final EvalDescr eval = (EvalDescr) rule.getLhs().getDescrs().get( 2 ); + assertThat((String) eval.getContent()).isEqualToIgnoringWhitespace( "abc(\"foo\")"); + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "Kapow"); + } + + @Test + public void parse_WithRetval() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "with_retval.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + final PatternDescr col = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(col.getConstraint().getDescrs().size()).isEqualTo(1); + assertThat(col.getObjectType()).isEqualTo("Foo"); + final ExprConstraintDescr fld = (ExprConstraintDescr) col.getConstraint().getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace("name== (a + b)"); + } + + @Test + public void parse_WithPredicate() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "with_predicate.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + final PatternDescr col = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + AndDescr and = (AndDescr) col.getConstraint(); + assertThat(and.getDescrs().size()).isEqualTo(2); + + final ExprConstraintDescr field = (ExprConstraintDescr) col.getDescrs().get( 0 ); + final ExprConstraintDescr pred = (ExprConstraintDescr) and.getDescrs().get( 1 ); + assertThat(field.getExpression()).isEqualToIgnoringWhitespace("$age2:age"); + assertThat(pred.getExpression()).isEqualToIgnoringWhitespace( "$age2 == $age1+2"); + } + + @Test + public void parse_NotWithConstraint() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "not_with_constraint.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(2); + + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final ExprConstraintDescr fieldBinding = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fieldBinding.getExpression()).isEqualToIgnoringWhitespace("$likes:like"); + + final NotDescr not = (NotDescr) rule.getLhs().getDescrs().get( 1 ); + pattern = (PatternDescr) not.getDescrs().get( 0 ); + + final ExprConstraintDescr fld = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace("type == $likes"); + } + + @Disabled("Priority : Mid | Implement Descr lineNumber") + @Test + public void parse_Functions() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "functions.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(2); + + final List functions = pkg.getFunctions(); + assertThat(functions.size()).isEqualTo(2); + + FunctionDescr func = functions.get( 0 ); + assertThat(func.getName()).isEqualTo("functionA"); + assertThat(func.getReturnType()).isEqualTo("String"); + assertThat(func.getParameterNames().size()).isEqualTo(2); + assertThat(func.getParameterTypes().size()).isEqualTo(2); + assertThat(func.getLine()).isEqualTo(19); + assertThat(func.getColumn()).isEqualTo(0); + + assertThat(func.getParameterTypes().get(0)).isEqualTo("String"); + assertThat(func.getParameterNames().get(0)).isEqualTo("s"); + + assertThat(func.getParameterTypes().get(1)).isEqualTo("Integer"); + assertThat(func.getParameterNames().get(1)).isEqualTo("i"); + + assertThat(func.getBody()).isEqualToIgnoringWhitespace( "foo();"); + + func = functions.get( 1 ); + assertThat(func.getName()).isEqualTo("functionB"); + assertThat(func.getText()).isEqualToIgnoringWhitespace( "bar();"); + } + + @Test + public void parse_Comment() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "comment.drl" ); + + assertThat(pkg).isNotNull(); + + assertThat(pkg.getName()).isEqualTo("foo.bar"); + } + + @Test + public void parse_Attributes() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "rule_attributes.drl" ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "bar();"); + + final Map attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(6); + + AttributeDescr at = (AttributeDescr) attrs.get( "salience" ); + assertThat(at.getName()).isEqualTo("salience"); + assertThat(at.getValue()).isEqualTo("42"); + + at = (AttributeDescr) attrs.get( "agenda-group" ); + assertThat(at.getName()).isEqualTo("agenda-group"); + assertThat(at.getValue()).isEqualTo("my_group"); + + at = (AttributeDescr) attrs.get( "no-loop" ); + assertThat(at.getName()).isEqualTo("no-loop"); + assertThat(at.getValue()).isEqualTo("true"); + + at = (AttributeDescr) attrs.get( "duration" ); + assertThat(at.getName()).isEqualTo("duration"); + assertThat(at.getValue()).isEqualTo("42"); + + at = (AttributeDescr) attrs.get( "activation-group" ); + assertThat(at.getName()).isEqualTo("activation-group"); + assertThat(at.getValue()).isEqualTo("my_activation_group"); + + at = (AttributeDescr) attrs.get( "lock-on-active" ); + assertThat(at.getName()).isEqualTo("lock-on-active"); + assertThat(at.getValue()).isEqualTo("true"); + } + + @Test + 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); + + RuleDescr rule = rules.get( 0 ); + assertThat(rule.getName()).isEqualTo("rule1"); + Map attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(2); + AttributeDescr at = (AttributeDescr) attrs.get( "salience" ); + assertThat(at.getName()).isEqualTo("salience"); + assertThat(at.getValue()).isEqualTo("(42)"); + at = (AttributeDescr) attrs.get( "agenda-group" ); + assertThat(at.getName()).isEqualTo("agenda-group"); + assertThat(at.getValue()).isEqualTo("my_group"); + + rule = rules.get( 1 ); + assertThat(rule.getName()).isEqualTo("rule2"); + attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(2); + at = (AttributeDescr) attrs.get( "salience" ); + assertThat(at.getName()).isEqualTo("salience"); + assertThat(at.getValue()).isEqualTo("(Integer.MIN_VALUE)"); + at = (AttributeDescr) attrs.get( "no-loop" ); + assertThat(at.getName()).isEqualTo("no-loop"); + + rule = rules.get( 2 ); + assertThat(rule.getName()).isEqualTo("rule3"); + attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(2); + at = (AttributeDescr) attrs.get( "enabled" ); + assertThat(at.getName()).isEqualTo("enabled"); + assertThat(at.getValue()).isEqualTo("(Boolean.TRUE)"); + at = (AttributeDescr) attrs.get( "activation-group" ); + assertThat(at.getName()).isEqualTo("activation-group"); + assertThat(at.getValue()).isEqualTo("my_activation_group"); + + } + + @Test + public void parse_AttributeRefract() throws Exception { + final String source = "rule Test refract when Person() then end"; + + PackageDescr pkg = parser.parse( + source ); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + + assertThat(rule.getName()).isEqualTo("Test"); + Map attributes = rule.getAttributes(); + assertThat(attributes.size()).isEqualTo(1); + AttributeDescr refract = attributes.get( "refract" ); + assertThat(refract).isNotNull(); + assertThat(refract.getValue()).isEqualTo("true"); + + } + + @Test + public void parse_EnabledExpression() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "rule_enabled_expression.drl" ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "bar();"); + + final Map attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(3); + + AttributeDescr at = (AttributeDescr) attrs.get( "enabled" ); + assertThat(at.getName()).isEqualTo("enabled"); + assertThat(at.getValue()).isEqualTo("( 1 + 1 == 2 )"); + + at = (AttributeDescr) attrs.get( "salience" ); + assertThat(at.getName()).isEqualTo("salience"); + assertThat(at.getValue()).isEqualTo("( 1+2 )"); + + at = (AttributeDescr) attrs.get( "lock-on-active" ); + assertThat(at.getName()).isEqualTo("lock-on-active"); + assertThat(at.getValue()).isEqualTo("true"); + } + + @Test + public void parse_DurationExpression() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "rule_duration_expression.drl" ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "bar();"); + + final Map attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(2); + + AttributeDescr at = (AttributeDescr) attrs.get( "duration" ); + assertThat(at.getName()).isEqualTo("duration"); + assertThat(at.getValue()).isEqualTo("1h30m"); + + at = (AttributeDescr) attrs.get( "lock-on-active" ); + assertThat(at.getName()).isEqualTo("lock-on-active"); + assertThat(at.getValue()).isEqualTo("true"); + } + + @Test + public void parse_Calendars() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "rule_calendars_attribute.drl" ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "bar();"); + + final Map attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(2); + + AttributeDescr at = (AttributeDescr) attrs.get( "calendars" ); + assertThat(at.getName()).isEqualTo("calendars"); + assertThat(at.getValue()).isEqualTo("[ \"cal1\" ]"); + + at = (AttributeDescr) attrs.get( "lock-on-active" ); + assertThat(at.getName()).isEqualTo("lock-on-active"); + assertThat(at.getValue()).isEqualTo("true"); + } + + @Test + 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();"); + + final Map attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(2); + + AttributeDescr at = (AttributeDescr) attrs.get( "calendars" ); + assertThat(at.getName()).isEqualTo("calendars"); + assertThat(at.getValue()).isEqualTo("[ \"cal 1\", \"cal 2\", \"cal 3\" ]"); + + at = (AttributeDescr) attrs.get( "lock-on-active" ); + assertThat(at.getName()).isEqualTo("lock-on-active"); + assertThat(at.getValue()).isEqualTo("true"); + } + + @Disabled("Priority : Low | Not written in docs nor other unit tests. Drop the support?") + @Test + public void parse_Attributes_alternateSyntax() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "rule_attributes_alt.drl" ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "bar();"); + + final Map attrs = rule.getAttributes(); + assertThat(attrs.size()).isEqualTo(6); + + AttributeDescr at = (AttributeDescr) attrs.get( "salience" ); + assertThat(at.getName()).isEqualTo("salience"); + assertThat(at.getValue()).isEqualTo("42"); + + at = (AttributeDescr) attrs.get( "agenda-group" ); + assertThat(at.getName()).isEqualTo("agenda-group"); + assertThat(at.getValue()).isEqualTo("my_group"); + + at = (AttributeDescr) attrs.get( "no-loop" ); + assertThat(at.getName()).isEqualTo("no-loop"); + assertThat(at.getValue()).isEqualTo("true"); + + at = (AttributeDescr) attrs.get( "lock-on-active" ); + assertThat(at.getName()).isEqualTo("lock-on-active"); + assertThat(at.getValue()).isEqualTo("true"); + + at = (AttributeDescr) attrs.get( "duration" ); + assertThat(at.getName()).isEqualTo("duration"); + assertThat(at.getValue()).isEqualTo("42"); + + at = (AttributeDescr) attrs.get( "activation-group" ); + assertThat(at.getName()).isEqualTo("activation-group"); + assertThat(at.getValue()).isEqualTo("my_activation_group"); + } + + @Test + public void parse_Enumeration() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "enumeration.drl" ); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + final PatternDescr col = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(col.getObjectType()).isEqualTo("Foo"); + assertThat(col.getConstraint().getDescrs().size()).isEqualTo(1); + final ExprConstraintDescr fld = (ExprConstraintDescr) col.getConstraint().getDescrs().get( 0 ); + + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace("bar == Foo.BAR"); + } + + @Test + public void parse_ExtraLhsNewline() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "extra_lhs_newline.drl" ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + } + + @Disabled("Priority : Low | Implement soundslike") + @Test + public void parse_SoundsLike() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "soundslike_operator.drl" ); + + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + PatternDescr pat = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + + pat.getConstraint(); + } + + @Test + public void parse_PackageAttributes() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "package_attributes.drl" ); + + AttributeDescr at = (AttributeDescr) pkg.getAttributes().get( 0 ); + assertThat(at.getName()).isEqualTo("agenda-group"); + assertThat(at.getValue()).isEqualTo("x"); + at = (AttributeDescr) pkg.getAttributes().get( 1 ); + assertThat(at.getName()).isEqualTo("dialect"); + assertThat(at.getValue()).isEqualTo("java"); + + assertThat(pkg.getRules().size()).isEqualTo(2); + + assertThat(pkg.getImports().size()).isEqualTo(2); + + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getName()).isEqualTo("bar"); + at = (AttributeDescr) rule.getAttributes().get( "agenda-group" ); + assertThat(at.getName()).isEqualTo("agenda-group"); + assertThat(at.getValue()).isEqualTo("x"); + at = (AttributeDescr) rule.getAttributes().get( "dialect" ); + assertThat(at.getName()).isEqualTo("dialect"); + assertThat(at.getValue()).isEqualTo("java"); + + rule = (RuleDescr) pkg.getRules().get( 1 ); + assertThat(rule.getName()).isEqualTo("baz"); + at = (AttributeDescr) rule.getAttributes().get( "dialect" ); + assertThat(at.getName()).isEqualTo("dialect"); + assertThat(at.getValue()).isEqualTo("mvel"); + at = (AttributeDescr) rule.getAttributes().get( "agenda-group" ); + assertThat(at.getName()).isEqualTo("agenda-group"); + assertThat(at.getValue()).isEqualTo("x"); + + } + + @Test + public void parse_StatementOrdering1() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "statement_ordering_1.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(2); + + assertThat(((RuleDescr) pkg.getRules().get(0)).getName()).isEqualTo("foo"); + assertThat(((RuleDescr) pkg.getRules().get(1)).getName()).isEqualTo("bar"); + + assertThat(pkg.getFunctions().size()).isEqualTo(2); + + assertThat(((FunctionDescr) pkg.getFunctions().get(0)).getName()).isEqualTo("cheeseIt"); + assertThat(((FunctionDescr) pkg.getFunctions().get(1)).getName()).isEqualTo("uncheeseIt"); + + assertThat(pkg.getImports().size()).isEqualTo(4); + assertThat(((ImportDescr) pkg.getImports().get(0)).getTarget()).isEqualTo("im.one"); + assertThat(((ImportDescr) pkg.getImports().get(1)).getTarget()).isEqualTo("im.two"); + assertThat(((ImportDescr) pkg.getImports().get(2)).getTarget()).isEqualTo("im.three"); + assertThat(((ImportDescr) pkg.getImports().get(3)).getTarget()).isEqualTo("im.four"); + } + + @Test + public void parse_RuleNamesStartingWithNumbers() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "rule_names_number_prefix.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(2); + + assertThat(((RuleDescr) pkg.getRules().get(0)).getName()).isEqualTo("1. Do Stuff!"); + assertThat(((RuleDescr) pkg.getRules().get(1)).getName()).isEqualTo("2. Do More Stuff!"); + } + + @Test + public void parse_EvalWithNewline() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "eval_with_newline.drl"); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + } + + @Disabled("Priority : Mid | implement Descr lineNumber") + @Test + public void parse_EndPosition() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "test_EndPosition.drl" ); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + final PatternDescr col = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(col.getLine()).isEqualTo(21); + assertThat(col.getEndLine()).isEqualTo(23); + } + + @Test + public void parse_QualifiedClassname() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "qualified_classname.drl" ); + + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + + final PatternDescr p = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + + assertThat(p.getObjectType()).isEqualTo("com.cheeseco.Cheese"); + } + + @Test + public void parse_Accumulate() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulate.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr outPattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final AccumulateDescr accum = (AccumulateDescr) outPattern.getSource(); + assertThat(accum.getInitCode()).isEqualToIgnoringWhitespace( "int x = 0 ;"); + assertThat(accum.getActionCode()).isEqualToIgnoringWhitespace( "x++;"); + assertThat(accum.getReverseCode()).isNull(); + assertThat(accum.getResultCode()).isEqualToIgnoringWhitespace( "new Integer(x)"); + + assertThat(accum.isExternalFunction()).isFalse(); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + } + + @Test + public void parse_AccumulateWithBindings() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulate_with_bindings.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr outPattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final AccumulateDescr accum = (AccumulateDescr) outPattern.getSource(); + assertThat(outPattern.getIdentifier()).isEqualToIgnoringWhitespace( "$counter"); + assertThat(accum.getInitCode()).isEqualToIgnoringWhitespace( "int x = 0 ;"); + assertThat(accum.getActionCode()).isEqualToIgnoringWhitespace( "x++;"); + assertThat(accum.getResultCode()).isEqualToIgnoringWhitespace( "new Integer(x)"); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + } + + @Test + public void parse_Collect() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "collect.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr outPattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final CollectDescr collect = (CollectDescr) outPattern.getSource(); + + final PatternDescr pattern = (PatternDescr) collect.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + } + + @Test + public void parse_Predicate2() throws Exception { + // predicates are also prefixed by the eval keyword + final RuleDescr rule = parseAndGetFirstRuleDescr( + "rule X when Foo(eval( $var.equals(\"xyz\") )) then end" ); + + final PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final List< ? > constraints = pattern.getConstraint().getDescrs(); + assertThat(constraints.size()).isEqualTo(1); + + final ExprConstraintDescr predicate = (ExprConstraintDescr) constraints.get( 0 ); + assertThat(predicate.getExpression()).isEqualToIgnoringWhitespace("eval( $var.equals(\"xyz\") )"); + } + + @Test + public void parse_EscapedStrings() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "escaped-string.drl" ); + + assertThat(rule).isNotNull(); + + assertThat(rule.getName()).isEqualTo("test_Quotes"); + + final String expected = "String s = \"\\\"\\n\\t\\\\\";"; + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( expected); + } + + @Test + public void parse_NestedCEs() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "nested_conditional_elements.drl" ); + + assertThat(rule).isNotNull(); + + final AndDescr root = rule.getLhs(); + final NotDescr not1 = (NotDescr) root.getDescrs().get( 0 ); + final AndDescr and1 = (AndDescr) not1.getDescrs().get( 0 ); + + final PatternDescr state = (PatternDescr) and1.getDescrs().get( 0 ); + final NotDescr not2 = (NotDescr) and1.getDescrs().get( 1 ); + final AndDescr and2 = (AndDescr) not2.getDescrs().get( 0 ); + final PatternDescr person = (PatternDescr) and2.getDescrs().get( 0 ); + final PatternDescr cheese = (PatternDescr) and2.getDescrs().get( 1 ); + + final PatternDescr person2 = (PatternDescr) root.getDescrs().get( 1 ); + final OrDescr or = (OrDescr) root.getDescrs().get( 2 ); + final PatternDescr cheese2 = (PatternDescr) or.getDescrs().get( 0 ); + final PatternDescr cheese3 = (PatternDescr) or.getDescrs().get( 1 ); + + assertThat("State").isEqualTo(state.getObjectType()); + assertThat("Person").isEqualTo(person.getObjectType()); + assertThat("Cheese").isEqualTo(cheese.getObjectType()); + assertThat("Person").isEqualTo(person2.getObjectType()); + assertThat("Cheese").isEqualTo(cheese2.getObjectType()); + assertThat("Cheese").isEqualTo(cheese3.getObjectType()); + } + + @Test + public void parse_Forall() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "forall.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final ForallDescr forall = (ForallDescr) rule.getLhs().getDescrs().get(0 ); + + assertThat(forall.getDescrs().size()).isEqualTo(2); + final PatternDescr pattern = forall.getBasePattern(); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + final List remaining = forall.getRemainingPatterns(); + assertThat(remaining.size()).isEqualTo(1); + final PatternDescr cheese = (PatternDescr) remaining.get( 0 ); + assertThat(cheese.getObjectType()).isEqualTo("Cheese"); + } + + @Test + public void parse_ForallWithFrom() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "forallwithfrom.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final ForallDescr forall = (ForallDescr) rule.getLhs().getDescrs().get( 0 ); + + assertThat(forall.getDescrs().size()).isEqualTo(2); + final PatternDescr pattern = forall.getBasePattern(); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + assertThat(((FromDescr) pattern.getSource()).getDataSource().toString()).isEqualTo("$village"); + final List remaining = forall.getRemainingPatterns(); + assertThat(remaining.size()).isEqualTo(1); + final PatternDescr cheese = (PatternDescr) remaining.get( 0 ); + assertThat(cheese.getObjectType()).isEqualTo("Cheese"); + assertThat(((FromDescr) cheese.getSource()).getDataSource().toString()).isEqualTo("$cheesery"); + } + + @Test + public void parse_Memberof() throws Exception { + final String text = "rule X when Country( $cities : city )\nPerson( city memberOf $cities )\n then end"; + AndDescr descrs = parseAndGetFirstRuleDescr( + text).getLhs(); + + assertThat(descrs.getDescrs().size()).isEqualTo(2); + PatternDescr pat = (PatternDescr) descrs.getDescrs().get( 1 ); + ExprConstraintDescr fieldConstr = (ExprConstraintDescr) pat.getConstraint().getDescrs().get( 0 ); + + assertThat(fieldConstr.getExpression()).isEqualTo("city memberOf $cities"); + } + + @Test + public void parse_NotMemberof() throws Exception { + final String text = "rule X when Country( $cities : city )\nPerson( city not memberOf $cities ) then end\n"; + AndDescr descrs = parseAndGetFirstRuleDescr( + text).getLhs(); + + assertThat(descrs.getDescrs().size()).isEqualTo(2); + PatternDescr pat = (PatternDescr) descrs.getDescrs().get( 1 ); + ExprConstraintDescr fieldConstr = (ExprConstraintDescr) pat.getConstraint().getDescrs().get( 0 ); + + assertThat(fieldConstr.getExpression()).isEqualTo("city not memberOf $cities"); + } + + @Test + public void parse_InOperator() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "in_operator_test.drl" ); + + assertThat(rule).isNotNull(); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "consequence();"); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(2); + + // The first pattern, with 2 restrictions on a single field (plus a + // connective) + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + assertThat(pattern.getConstraint().getDescrs().size()).isEqualTo(1); + + ExprConstraintDescr fld = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + assertThat(fld.getExpression()).isEqualTo("age > 30 && < 40"); + + // the second col, with 2 fields, the first with 2 restrictions, the + // second field with one + pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(pattern.getObjectType()).isEqualTo("Vehicle"); + assertThat(pattern.getConstraint().getDescrs().size()).isEqualTo(2); + + fld = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace("type in ( \"sedan\", \"wagon\" )"); + + // now the second field + fld = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 1 ); + assertThat(fld.getExpression()).isEqualTo("age < 3"); + + } + + @Test + public void parse_NotInOperator() throws Exception { + final RuleDescr rule = parseAndGetFirstRuleDescrFromFile( + "notin_operator_test.drl" ); + + assertThat(rule).isNotNull(); + + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "consequence();"); + assertThat(rule.getName()).isEqualTo("simple_rule"); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(2); + + // The first pattern, with 2 restrictions on a single field (plus a + // connective) + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + assertThat(pattern.getConstraint().getDescrs().size()).isEqualTo(1); + + ExprConstraintDescr fld = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + assertThat(fld.getExpression()).isEqualTo("age > 30 && < 40"); + + // the second col, with 2 fields, the first with 2 restrictions, the + // second field with one + pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(pattern.getObjectType()).isEqualTo("Vehicle"); + assertThat(pattern.getConstraint().getDescrs().size()).isEqualTo(2); + + fld = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + assertThat(fld.getExpression()).isEqualToIgnoringWhitespace("type not in ( \"sedan\", \"wagon\" )"); + + // now the second field + fld = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 1 ); + assertThat(fld.getExpression()).isEqualTo("age < 3"); + + } + + @Test + public void parse_CheckOrDescr() throws Exception { + final String text = "rule X when Person( eval( age == 25 ) || ( eval( name.equals( \"bob\" ) ) && eval( age == 30 ) ) ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + assertThat(AndDescr.class).isEqualTo(pattern.getConstraint().getClass()); + + assertThat(pattern.getConstraint().getDescrs().get(0).getClass()).isEqualTo(ExprConstraintDescr.class); + + } + + @Test + public void parse_ConstraintAndConnective() throws Exception { + final String text = "rule X when Person( age < 42 && location==\"atlanta\") then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualToIgnoringWhitespace("age < 42 && location==\"atlanta\""); + } + + @Test + public void parse_ConstraintOrConnective() throws Exception { + final String text = "rule X when Person( age < 42 || location==\"atlanta\") then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualToIgnoringWhitespace("age < 42 || location==\"atlanta\""); + } + + @Test + public void parse_Restrictions() throws Exception { + final String text = "rule X when Foo( bar > 1 || == 1 ) then end\n"; + + AndDescr descrs = (AndDescr) parseAndGetFirstRuleDescr( + text ).getLhs(); + + assertThat(descrs.getDescrs().size()).isEqualTo(1); + PatternDescr pat = (PatternDescr) descrs.getDescrs().get( 0 ); + ExprConstraintDescr fieldConstr = (ExprConstraintDescr) pat.getConstraint().getDescrs().get( 0 ); + + assertThat(fieldConstr.getExpression()).isEqualTo("bar > 1 || == 1"); + } + + @Test + 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); + assertThat(pkg.getRules().size()).isEqualTo(3); + + final RuleDescr rule1 = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule1.getLhs().getDescrs().size()).isEqualTo(2); + + final RuleDescr query1 = (RuleDescr) pkg.getRules().get( 1 ); + assertThat(query1.getLhs().getDescrs().size()).isEqualTo(3); + + final RuleDescr rule2 = (RuleDescr) pkg.getRules().get( 2 ); + assertThat(rule2.getLhs().getDescrs().size()).isEqualTo(2); + } + + @Test + public void parse_Eval() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "eval_parsing.drl" ); + + assertThat(pkg.getName()).isEqualTo("org.drools.mvel.compiler"); + assertThat(pkg.getRules().size()).isEqualTo(1); + + final RuleDescr rule1 = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule1.getLhs().getDescrs().size()).isEqualTo(1); + } + + @Test + public void parse_AccumulateReverse() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulateReverse.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final AccumulateDescr accum = (AccumulateDescr) out.getSource(); + assertThat(accum.getInitCode()).isEqualToIgnoringWhitespace( "int x = 0 ;" + ); + assertThat(accum.getActionCode()).isEqualToIgnoringWhitespace( "x++;" + ); + assertThat(accum.getReverseCode()).isEqualToIgnoringWhitespace( "x--;" + ); + assertThat(accum.getResultCode()).isEqualToIgnoringWhitespace( "new Integer(x)" + ); + assertThat(accum.isExternalFunction()).isFalse(); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + } + + @Test + public void parse_AccumulateExternalFunction() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulateExternalFunction.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final AccumulateDescr accum = (AccumulateDescr) out.getSource(); + assertThat(accum.getFunctions().get( 0 ).getParams()[0]).isEqualToIgnoringWhitespace( "$age" + ); + assertThat(accum.getFunctions().get( 0 ).getFunction()).isEqualToIgnoringWhitespace( "average" + ); + assertThat(accum.isExternalFunction()).isTrue(); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Person"); + } + + @Test + public void parse_CollectWithNestedFrom() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "collect_with_nested_from.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final CollectDescr collect = (CollectDescr) out.getSource(); + + PatternDescr person = (PatternDescr) collect.getInputPattern(); + assertThat(person.getObjectType()).isEqualTo("Person"); + + final CollectDescr collect2 = (CollectDescr) person.getSource(); + + final PatternDescr people = collect2.getInputPattern(); + assertThat(people.getObjectType()).isEqualTo("People"); + } + + @Test + public void parse_AccumulateWithNestedFrom() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulate_with_nested_from.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final AccumulateDescr accumulate = (AccumulateDescr) out.getSource(); + + PatternDescr person = (PatternDescr) accumulate.getInputPattern(); + assertThat(person.getObjectType()).isEqualTo("Person"); + + final CollectDescr collect2 = (CollectDescr) person.getSource(); + + final PatternDescr people = collect2.getInputPattern(); + assertThat(people.getObjectType()).isEqualTo("People"); + } + + @Test + public void parse_AccumulateMultipleFunctions() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulateMultipleFunctions.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(out.getObjectType()).isEqualTo("Object"); + AccumulateDescr accum = (AccumulateDescr) out.getSource(); + assertThat(accum.isExternalFunction()).isTrue(); + + List functions = accum.getFunctions(); + assertThat(functions.size()).isEqualTo(3); + assertThat(functions.get(0).getFunction()).isEqualTo("average"); + assertThat(functions.get(0).getBind()).isEqualTo("$a1"); + assertThat(functions.get(0).getParams()[0]).isEqualTo("$price"); + + assertThat(functions.get(1).getFunction()).isEqualTo("min"); + assertThat(functions.get(1).getBind()).isEqualTo("$m1"); + assertThat(functions.get(1).getParams()[0]).isEqualTo("$price"); + + assertThat(functions.get(2).getFunction()).isEqualTo("max"); + assertThat(functions.get(2).getBind()).isEqualTo("$M1"); + assertThat(functions.get(2).getParams()[0]).isEqualTo("$price"); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Cheese"); + } + + @Test + public void parse_AccumulateMnemonic() throws Exception { + String drl = "package org.drools.mvel.compiler\n" + + "rule \"Accumulate 1\"\n" + + "when\n" + + " acc( Cheese( $price : price ),\n" + + " $a1 : average( $price ) )\n" + + "then\n" + + "end\n"; + PackageDescr pkg = parser.parse( + drl ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(out.getObjectType()).isEqualTo("Object"); + AccumulateDescr accum = (AccumulateDescr) out.getSource(); + assertThat(accum.isExternalFunction()).isTrue(); + + List functions = accum.getFunctions(); + assertThat(functions.size()).isEqualTo(1); + assertThat(functions.get(0).getFunction()).isEqualTo("average"); + assertThat(functions.get(0).getBind()).isEqualTo("$a1"); + assertThat(functions.get(0).getParams()[0]).isEqualTo("$price"); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Cheese"); + } + + @Test + public void parse_AccumulateMnemonic2() throws Exception { + String drl = "package org.drools.mvel.compiler\n" + + "rule \"Accumulate 1\"\n" + + "when\n" + + " Number() from acc( Cheese( $price : price ),\n" + + " average( $price ) )\n" + + "then\n" + + "end\n"; + PackageDescr pkg = parser.parse( + drl ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(out.getObjectType()).isEqualTo("Number"); + AccumulateDescr accum = (AccumulateDescr) out.getSource(); + assertThat(accum.isExternalFunction()).isTrue(); + + List functions = accum.getFunctions(); + assertThat(functions.size()).isEqualTo(1); + assertThat(functions.get(0).getFunction()).isEqualTo("average"); + assertThat(functions.get(0).getParams()[0]).isEqualTo("$price"); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Cheese"); + } + + @Test + public void parse_ImportAccumulate() throws Exception { + String drl = "package org.drools.mvel.compiler\n" + + "import acc foo.Bar baz\n" + + "import accumulate foo.Bar2 baz2\n" + + "rule \"Accumulate 1\"\n" + + "when\n" + + " acc( Cheese( $price : price ),\n" + + " $v1 : baz( $price ), \n" + + " $v2 : baz2( $price ) )\n" + + "then\n" + + "end\n"; + PackageDescr pkg = parser.parse( + drl ); + + assertThat(pkg.getAccumulateImports().size()).isEqualTo(2); + AccumulateImportDescr imp = (AccumulateImportDescr) pkg.getAccumulateImports().get(0); + assertThat(imp.getTarget()).isEqualTo("foo.Bar"); + assertThat(imp.getFunctionName()).isEqualTo("baz"); + + imp = (AccumulateImportDescr) pkg.getAccumulateImports().get(1); + assertThat(imp.getTarget()).isEqualTo("foo.Bar2"); + assertThat(imp.getFunctionName()).isEqualTo("baz2"); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(out.getObjectType()).isEqualTo("Object"); + AccumulateDescr accum = (AccumulateDescr) out.getSource(); + assertThat(accum.isExternalFunction()).isTrue(); + + List functions = accum.getFunctions(); + assertThat(functions.size()).isEqualTo(2); + assertThat(functions.get(0).getFunction()).isEqualTo("baz"); + assertThat(functions.get(0).getBind()).isEqualTo("$v1"); + assertThat(functions.get(0).getParams()[0]).isEqualTo("$price"); + + assertThat(functions.get(1).getFunction()).isEqualTo("baz2"); + assertThat(functions.get(1).getBind()).isEqualTo("$v2"); + assertThat(functions.get(1).getParams()[0]).isEqualTo("$price"); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Cheese"); + } + + @Test + public void parse_AccumulateMultipleFunctionsConstraint() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulateMultipleFunctionsConstraint.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + PatternDescr out = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(out.getObjectType()).isEqualTo("Object"); + assertThat(out.getConstraint().getDescrs().size()).isEqualTo(2); + assertThat(out.getConstraint().getDescrs().get(0).toString()).isEqualTo("$a1 > 10 && $M1 <= 100"); + assertThat(out.getConstraint().getDescrs().get(1).toString()).isEqualTo("$m1 == 5"); + AccumulateDescr accum = (AccumulateDescr) out.getSource(); + assertThat(accum.isExternalFunction()).isTrue(); + + List functions = accum.getFunctions(); + assertThat(functions.size()).isEqualTo(3); + assertThat(functions.get(0).getFunction()).isEqualTo("average"); + assertThat(functions.get(0).getBind()).isEqualTo("$a1"); + assertThat(functions.get(0).getParams()[0]).isEqualTo("$price"); + + assertThat(functions.get(1).getFunction()).isEqualTo("min"); + assertThat(functions.get(1).getBind()).isEqualTo("$m1"); + assertThat(functions.get(1).getParams()[0]).isEqualTo("$price"); + + assertThat(functions.get(2).getFunction()).isEqualTo("max"); + assertThat(functions.get(2).getBind()).isEqualTo("$M1"); + assertThat(functions.get(2).getParams()[0]).isEqualTo("$price"); + + final PatternDescr pattern = (PatternDescr) accum.getInputPattern(); + assertThat(pattern.getObjectType()).isEqualTo("Cheese"); + } + + @Test + public void parse_OrCE() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "or_ce.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(2); + + final PatternDescr person = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(person.getObjectType()).isEqualTo("Person"); + assertThat(person.getIdentifier()).isEqualTo("$p"); + + final OrDescr or = (OrDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(or.getDescrs().size()).isEqualTo(2); + + final PatternDescr cheese1 = (PatternDescr) or.getDescrs().get( 0 ); + assertThat(cheese1.getObjectType()).isEqualTo("Cheese"); + assertThat(cheese1.getIdentifier()).isEqualTo("$c"); + final PatternDescr cheese2 = (PatternDescr) or.getDescrs().get( 1 ); + assertThat(cheese2.getObjectType()).isEqualTo("Cheese"); + assertThat(cheese2.getIdentifier()).isNull(); + } + + @Test + public void parse_RuleSingleLine() throws Exception { + final String text = "rule \"another test\" salience 10 when eval( true ) then System.out.println(1); end"; + RuleDescr rule = parseAndGetFirstRuleDescr( + text ); + + assertThat(rule.getName()).isEqualTo("another test"); + assertThat((String)rule.getConsequence()).isEqualToIgnoringWhitespace("System.out.println(1); "); + } + + @Test + public void parse_RuleTwoLines() throws Exception { + final String text = "rule \"another test\" salience 10 when eval( true ) then System.out.println(1);\n end"; + RuleDescr rule = parseAndGetFirstRuleDescr( + text ); + + assertThat(rule.getName()).isEqualTo("another test"); + assertThat((String)rule.getConsequence()).isEqualToIgnoringWhitespace("System.out.println(1);\n "); + } + + @Test + public void parse_RuleParseLhs3() throws Exception { + final String text = "rule X when (or\nnot Person()\n(and Cheese()\nMeat()\nWine())) then end"; + AndDescr pattern = parseAndGetFirstRuleDescr( + text ).getLhs(); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + OrDescr or = (OrDescr) pattern.getDescrs().get( 0 ); + assertThat(or.getDescrs().size()).isEqualTo(2); + NotDescr not = (NotDescr) or.getDescrs().get( 0 ); + AndDescr and = (AndDescr) or.getDescrs().get( 1 ); + assertThat(not.getDescrs().size()).isEqualTo(1); + PatternDescr person = (PatternDescr) not.getDescrs().get( 0 ); + assertThat(person.getObjectType()).isEqualTo("Person"); + assertThat(and.getDescrs().size()).isEqualTo(3); + PatternDescr cheese = (PatternDescr) and.getDescrs().get( 0 ); + assertThat(cheese.getObjectType()).isEqualTo("Cheese"); + PatternDescr meat = (PatternDescr) and.getDescrs().get( 1 ); + assertThat(meat.getObjectType()).isEqualTo("Meat"); + PatternDescr wine = (PatternDescr) and.getDescrs().get( 2 ); + assertThat(wine.getObjectType()).isEqualTo("Wine"); + + } + + @Test + public void parse_AccumulateMultiPattern() throws Exception { + final PackageDescr pkg = parseAndGetPackageDescrFromFile( + "accumulate_multi_pattern.drl" ); + + assertThat(pkg.getRules().size()).isEqualTo(1); + final RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + + final PatternDescr outPattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + final AccumulateDescr accum = (AccumulateDescr) outPattern.getSource(); + assertThat(outPattern.getIdentifier()).isEqualToIgnoringWhitespace( "$counter" + ); + assertThat(accum.getInitCode()).isEqualToIgnoringWhitespace( "int x = 0 ;" + ); + assertThat(accum.getActionCode()).isEqualToIgnoringWhitespace( "x++;" + ); + assertThat(accum.getResultCode()).isEqualToIgnoringWhitespace( "new Integer(x)" + ); + + final AndDescr and = (AndDescr) accum.getInput(); + assertThat(and.getDescrs().size()).isEqualTo(2); + final PatternDescr person = (PatternDescr) and.getDescrs().get( 0 ); + final PatternDescr cheese = (PatternDescr) and.getDescrs().get( 1 ); + assertThat(person.getObjectType()).isEqualTo("Person"); + assertThat(cheese.getObjectType()).isEqualTo("Cheese"); + } + + @Test + 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 ); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(5); + + final PatternDescr eventA = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(eventA.getIdentifier()).isEqualTo("$a"); + assertThat(eventA.getObjectType()).isEqualTo("EventA"); + + final PatternDescr eventB = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(eventB.getIdentifier()).isEqualTo("$b"); + assertThat(eventB.getObjectType()).isEqualTo("EventB"); + assertThat(eventB.getConstraint().getDescrs().size()).isEqualTo(1); + assertThat(eventB.getConstraint().getDescrs().size()).isEqualTo(1); + + final ExprConstraintDescr fcdB = (ExprConstraintDescr) eventB.getConstraint().getDescrs().get( 0 ); + assertThat(fcdB.getExpression()).isEqualTo("this after[1,10] $a || this not after[15,20] $a"); + + final PatternDescr eventC = (PatternDescr) rule.getLhs().getDescrs().get( 2 ); + assertThat(eventC.getIdentifier()).isEqualTo("$c"); + assertThat(eventC.getObjectType()).isEqualTo("EventC"); + assertThat(eventC.getConstraint().getDescrs().size()).isEqualTo(1); + final ExprConstraintDescr fcdC = (ExprConstraintDescr) eventC.getConstraint().getDescrs().get( 0 ); + assertThat(fcdC.getExpression()).isEqualTo("this finishes $b"); + + final PatternDescr eventD = (PatternDescr) rule.getLhs().getDescrs().get( 3 ); + assertThat(eventD.getIdentifier()).isEqualTo("$d"); + assertThat(eventD.getObjectType()).isEqualTo("EventD"); + assertThat(eventD.getConstraint().getDescrs().size()).isEqualTo(1); + final ExprConstraintDescr fcdD = (ExprConstraintDescr) eventD.getConstraint().getDescrs().get( 0 ); + assertThat(fcdD.getExpression()).isEqualTo("this not starts $a"); + + final PatternDescr eventE = (PatternDescr) rule.getLhs().getDescrs().get( 4 ); + assertThat(eventE.getIdentifier()).isEqualTo("$e"); + assertThat(eventE.getObjectType()).isEqualTo("EventE"); + assertThat(eventE.getConstraint().getDescrs().size()).isEqualTo(1); + + ExprConstraintDescr fcdE = (ExprConstraintDescr) eventE.getConstraint().getDescrs().get( 0 ); + assertThat(fcdE.getExpression()).isEqualTo("this not before[1, 10] $b || after[1, 10] $c && this after[1, 5] $d"); + } + + @Test + 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) + RuleDescr rule = pkg.getRules().get( 0 ); + assertThat(rule.getAnnotationNames().contains("fooMeta1")).isTrue(); + assertThat(rule.getAnnotation("fooMeta1").getValue()).isEqualTo("barVal1"); + assertThat(rule.getAnnotationNames().contains("fooMeta2")).isTrue(); + assertThat(rule.getAnnotation("fooMeta2").getValue()).isEqualTo("barVal2"); + assertThat((String) rule.getConsequence()).isEqualToIgnoringWhitespace( "System.out.println(\"Consequence\");" + ); + } + + @Test + 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(); + assertThat(rule.getParentName()).isEqualTo("rule1"); + + AndDescr lhs = rule.getLhs(); + assertThat(lhs).isNotNull(); + assertThat(lhs.getDescrs().size()).isEqualTo(1); + + PatternDescr pattern = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(pattern.getObjectType()).isEqualTo("foo"); + assertThat(pattern.getIdentifier()).isEqualTo("$foo"); + + } + + @Test + 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); + + TypeDeclarationDescr d = td.get( 0 ); + assertThat(d.getTypeName()).isEqualTo("SomeFact"); + assertThat(d.getFields().size()).isEqualTo(2); + assertThat(d.getFields().containsKey("name")).isTrue(); + assertThat(d.getFields().containsKey("age")).isTrue(); + + TypeFieldDescr f = d.getFields().get("name" ); + assertThat(f.getPattern().getObjectType()).isEqualTo("String"); + + f = d.getFields().get( "age" ); + assertThat(f.getPattern().getObjectType()).isEqualTo("Integer"); + + d = td.get( 1 ); + assertThat(d.getTypeName()).isEqualTo("AnotherFact"); + + TypeDeclarationDescr type = td.get( 2 ); + assertThat(type.getTypeName()).isEqualTo("Person"); + + assertThat(type.getAnnotation("role").getValue()).isEqualTo("fact"); + assertThat(type.getAnnotation("doc").getValue("descr")).isEqualTo("\"Models a person\""); + assertThat(type.getAnnotation("doc").getValue("author")).isEqualTo("\"Bob\""); + assertThat(type.getAnnotation("doc").getValue("date")).isEqualTo("Calendar.getInstance().getDate()"); + + assertThat(type.getFields().size()).isEqualTo(2); + TypeFieldDescr field = type.getFields().get( "name" ); + assertThat(field.getFieldName()).isEqualTo("name"); + assertThat(field.getPattern().getObjectType()).isEqualTo("String"); + assertThat(field.getInitExpr()).isEqualTo("\"John Doe\""); + assertThat(field.getAnnotation("length").getValue("max")).isEqualTo("50"); + assertThat( field.getAnnotation( "key" ) ).isNotNull(); + + field = type.getFields().get( "age" ); + assertThat(field.getFieldName()).isEqualTo("age"); + assertThat(field.getPattern().getObjectType()).isEqualTo("int"); + assertThat(field.getInitExpr()).isEqualTo("-1"); + assertThat(field.getAnnotation("ranged").getValue("min")).isEqualTo("0"); + assertThat(field.getAnnotation("ranged").getValue("max")).isEqualTo("150"); + assertThat(field.getAnnotation("ranged").getValue("unknown")).isEqualTo("-1"); + + } + + @Test + 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"); + + AndDescr lhs = rule.getLhs(); + assertThat(lhs).isNotNull(); + assertThat(lhs.getDescrs().size()).isEqualTo(2); + + PatternDescr a = (PatternDescr) lhs.getDescrs().get( 0 ); + assertThat(a.getObjectType()).isEqualTo("A"); + + OrDescr or = (OrDescr) lhs.getDescrs().get( 1 ); + assertThat(or.getDescrs().size()).isEqualTo(3); + + AndDescr and1 = (AndDescr) or.getDescrs().get( 0 ); + assertThat(and1.getDescrs().size()).isEqualTo(2); + PatternDescr b = (PatternDescr) and1.getDescrs().get( 0 ); + PatternDescr c = (PatternDescr) and1.getDescrs().get( 1 ); + assertThat(b.getObjectType()).isEqualTo("B"); + assertThat(c.getObjectType()).isEqualTo("C"); + + AndDescr and2 = (AndDescr) or.getDescrs().get( 1 ); + assertThat(and2.getDescrs().size()).isEqualTo(2); + PatternDescr d = (PatternDescr) and2.getDescrs().get( 0 ); + PatternDescr e = (PatternDescr) and2.getDescrs().get( 1 ); + assertThat(d.getObjectType()).isEqualTo("D"); + assertThat(e.getObjectType()).isEqualTo("E"); + + AndDescr and3 = (AndDescr) or.getDescrs().get( 2 ); + assertThat(and3.getDescrs().size()).isEqualTo(2); + PatternDescr f = (PatternDescr) and3.getDescrs().get( 0 ); + PatternDescr g = (PatternDescr) and3.getDescrs().get( 1 ); + assertThat(f.getObjectType()).isEqualTo("F"); + assertThat(g.getObjectType()).isEqualTo("G"); + } + + @Test + 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( + text ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + RuleDescr rule = pkg.getRules().get( 0 ); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualTo("symbol==\"ACME\""); + + assertThat(pattern.getSource()).isNotNull(); + EntryPointDescr entry = (EntryPointDescr) pattern.getSource(); + assertThat(entry.getEntryId()).isEqualTo("StreamA"); + } + + @Test + 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( + text ); + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + + RuleDescr rule = pkg.getRules().get( 0 ); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualTo("symbol==\"ACME\""); + + assertThat(pattern.getSource()).isNotNull(); + EntryPointDescr entry = (EntryPointDescr) pattern.getSource(); + assertThat(entry.getEntryId()).isEqualTo("StreamA"); + } + + @Test + 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(); + + RuleDescr rule = pkg.getRules().get( 0 ); + PatternDescr pattern = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualTo("symbol==\"ACME\""); + + List behaviors = pattern.getBehaviors(); + assertThat(behaviors).isNotNull(); + assertThat(behaviors.size()).isEqualTo(1); + BehaviorDescr descr = behaviors.get( 0 ); + assertThat(descr.getType()).isEqualTo("window"); + assertThat(descr.getSubType()).isEqualTo("length"); + assertThat(descr.getParameters().get(0)).isEqualTo("10"); + } + + @Test + public void parse_RuleOldSyntax1() throws Exception { + final String source = "rule \"Test\" when ( not $r :LiteralRestriction( operator == Operator.EQUAL ) ) then end"; + + PackageDescr pkg = parser.parse( + source ); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + + assertThat(rule.getName()).isEqualTo("Test"); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + assertThat(((NotDescr) rule.getLhs().getDescrs().get(0)).getDescrs().size()).isEqualTo(1); + NotDescr notDescr = (NotDescr) rule.getLhs().getDescrs().get( 0 ); + PatternDescr patternDescr = (PatternDescr) notDescr.getDescrs().get( 0 ); + assertThat(patternDescr.getIdentifier()).isEqualTo("$r"); + assertThat(patternDescr.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fieldConstraintDescr = (ExprConstraintDescr) patternDescr.getDescrs().get( 0 ); + assertThat(fieldConstraintDescr.getExpression()).isEqualToIgnoringWhitespace("operator == Operator.EQUAL"); + } + + @Test + public void parse_RuleOldSyntax2() throws Exception { + final String source = "rule \"Test\" when ( $r :LiteralRestriction( operator == Operator.EQUAL ) ) then end"; + + PackageDescr pkg = parser.parse( + source ); + + assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); + RuleDescr rule = (RuleDescr) pkg.getRules().get( 0 ); + + assertThat(rule.getName()).isEqualTo("Test"); + assertThat(rule.getLhs().getDescrs().size()).isEqualTo(1); + PatternDescr patternDescr = (PatternDescr) rule.getLhs().getDescrs().get( 0 ); + assertThat(patternDescr.getIdentifier()).isEqualTo("$r"); + assertThat(patternDescr.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fieldConstraintDescr = (ExprConstraintDescr) patternDescr.getDescrs().get( 0 ); + assertThat(fieldConstraintDescr.getExpression()).isEqualToIgnoringWhitespace("operator == Operator.EQUAL"); + } + + @Test + 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(); + + assertThat(declarations.size()).isEqualTo(3); + } + + @Test + public void parse_NullConstraints() throws Exception { + final String text = "rule X when Person( name == null ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(1); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualTo("name == null"); + assertThat(fcd.getPosition()).isEqualTo(0); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.NAMED); + } + + @Test + public void parse_PositionalConstraintsOnly() throws Exception { + final String text = "rule X when Person( \"Mark\", 42; ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(2); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualTo("\"Mark\""); + assertThat(fcd.getPosition()).isEqualTo(0); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.POSITIONAL); + fcd = (ExprConstraintDescr) pattern.getDescrs().get( 1 ); + assertThat(fcd.getExpression()).isEqualTo("42"); + assertThat(fcd.getPosition()).isEqualTo(1); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.POSITIONAL); + } + + @Test + public void parse_IsQuery() throws Exception { + final String text = "rule X when ?person( \"Mark\", 42; ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.isQuery()).isTrue(); + + assertThat(pattern.getDescrs().size()).isEqualTo(2); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualTo("\"Mark\""); + assertThat(fcd.getPosition()).isEqualTo(0); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.POSITIONAL); + fcd = (ExprConstraintDescr) pattern.getDescrs().get( 1 ); + assertThat(fcd.getExpression()).isEqualTo("42"); + assertThat(fcd.getPosition()).isEqualTo(1); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.POSITIONAL); + } + + @Test + public void parse_FromFollowedByQuery() throws Exception { + // the 'from' expression requires a ";" to disambiguate the "?" + // prefix for queries from the ternary operator "? :" + 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"); + assertThat(pattern.getSource().getText()).isEqualTo("from $cheesery"); + assertThat(pattern.isQuery()).isFalse(); + + pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(pattern.getObjectType()).isEqualTo("person"); + assertThat(pattern.isQuery()).isTrue(); + + } + + @Test + public void parse_FromWithTernaryFollowedByQuery() throws Exception { + // the 'from' expression requires a ";" to disambiguate the "?" + // prefix for queries from the ternary operator "? :" + 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"); + assertThat(pattern.getSource().getText()).isEqualToIgnoringWhitespace("from (isFull ? $cheesery : $market)"); + assertThat(pattern.isQuery()).isFalse(); + + pattern = (PatternDescr) rule.getLhs().getDescrs().get( 1 ); + assertThat(pattern.getObjectType()).isEqualTo("person"); + assertThat(pattern.isQuery()).isTrue(); + + } + + @Disabled("Priority : Low | Implement multi-value annotation. Not written in docs") + @Test + public void parse_MultiValueAnnotationsBackwardCompatibility() throws Exception { + // multiple values with no keys are parsed as a single value + final String text = "rule X @ann1( val1, val2 ) @ann2( \"val1\", \"val2\" ) when then end"; + RuleDescr rule = parseAndGetFirstRuleDescr( + text ); + + AnnotationDescr ann = rule.getAnnotation("ann1" ); + assertThat(ann).isNotNull(); + assertThat(ann.getValue()).isEqualTo("val1, val2"); + + ann = rule.getAnnotation( "ann2" ); + assertThat(ann).isNotNull(); + assertThat(ann.getValue()).isEqualTo("\"val1\", \"val2\""); + } + + @Test + public void parse_PositionalsAndNamedConstraints() throws Exception { + final String text = "rule X when Person( \"Mark\", 42; location == \"atlanta\" ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(3); + ExprConstraintDescr fcd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(fcd.getExpression()).isEqualTo("\"Mark\""); + assertThat(fcd.getPosition()).isEqualTo(0); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.POSITIONAL); + fcd = (ExprConstraintDescr) pattern.getDescrs().get( 1 ); + assertThat(fcd.getExpression()).isEqualTo("42"); + assertThat(fcd.getPosition()).isEqualTo(1); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.POSITIONAL); + + fcd = (ExprConstraintDescr) pattern.getDescrs().get( 2 ); + assertThat(fcd.getExpression()).isEqualTo("location == \"atlanta\""); + assertThat(fcd.getPosition()).isEqualTo(2); + assertThat(fcd.getType()).isEqualTo(ExprConstraintDescr.Type.NAMED); + + } + + @Test + public void parse_UnificationBinding() throws Exception { + final String text = "rule X when $p := Person( $name := name, $loc : location ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getIdentifier()).isEqualTo("$p"); + assertThat(pattern.isUnification()).isTrue(); + + assertThat(pattern.getDescrs().size()).isEqualTo(2); + ExprConstraintDescr bindingDescr = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(bindingDescr.getExpression()).isEqualTo("$name := name"); + + bindingDescr = (ExprConstraintDescr) pattern.getDescrs().get( 1 ); + assertThat(bindingDescr.getExpression()).isEqualTo("$loc : location"); + + } + + @Test + public void parse_BigLiterals() { + final String text = "rule X when Primitives( bigInteger == (10I), " + + " bigDecimal == (10B), " + + " bigInteger < 50I, " + + " bigDecimal < 50.2B ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getDescrs().size()).isEqualTo(4); + ExprConstraintDescr ecd = (ExprConstraintDescr) pattern.getDescrs().get( 0 ); + assertThat(ecd.getExpression()).isEqualTo("bigInteger == (10I)"); + + ecd = (ExprConstraintDescr) pattern.getDescrs().get( 1 ); + assertThat(ecd.getExpression()).isEqualTo("bigDecimal == (10B)"); + + ecd = (ExprConstraintDescr) pattern.getDescrs().get( 2 ); + assertThat(ecd.getExpression()).isEqualTo("bigInteger < 50I"); + + ecd = (ExprConstraintDescr) pattern.getDescrs().get( 3 ); + assertThat(ecd.getExpression()).isEqualTo("bigDecimal < 50.2B"); + } + + @Test + public void parse_BindingComposite() throws Exception { + final String text = "rule X when Person( $name : name == \"Bob\" || $loc : location == \"Montreal\" ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getObjectType()).isEqualTo("Person"); + assertThat(pattern.isUnification()).isFalse(); + + // embedded bindings are extracted at compile time + List< ? > constraints = pattern.getDescrs(); + assertThat(constraints.size()).isEqualTo(1); + assertThat(((ExprConstraintDescr) constraints.get(0)).getExpression()).isEqualTo("$name : name == \"Bob\" || $loc : location == \"Montreal\""); + } + + @Test + public void parse_BindingCompositeWithMethods() throws Exception { + final String text = "rule X when Person( $name : name.toUpperCase() == \"Bob\" || $loc : location[0].city == \"Montreal\" ) then end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getObjectType()).isEqualTo("Person"); + assertThat(pattern.isUnification()).isFalse(); + + // embedded bindings are extracted at compile time + List< ? > constraints = pattern.getDescrs(); + assertThat(constraints.size()).isEqualTo(1); + assertThat(((ExprConstraintDescr) constraints.get(0)).getExpression()).isEqualTo("$name : name.toUpperCase() == \"Bob\" || $loc : location[0].city == \"Montreal\""); + } + + @Test + public void parse_PluggableOperators2() throws Exception { + final String text = "rule \"tt\"\n" + + " dialect \"mvel\"\n" + + "when\n" + + " exists (TelephoneCall( this finishes [1m] \"25-May-2011\" ))\n" + + "then\n" + + "end"; + PatternDescr pattern = (PatternDescr) ((ExistsDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 )).getDescrs().get( 0 ); + + assertThat(pattern.getObjectType()).isEqualTo("TelephoneCall"); + ExprConstraintDescr constr = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + assertThat(constr.getText()).isEqualTo("this finishes [1m] \"25-May-2011\""); + + } + + @Test + public void parse_InlineEval() throws Exception { + final String text = "rule \"inline eval\"\n" + + "when\n" + + " Person( eval( name.startsWith(\"b\") && name.finishesWith(\"b\")) )\n" + + "then\n" + + "end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getObjectType()).isEqualTo("Person"); + ExprConstraintDescr constr = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + assertThat(constr.getText()).isEqualToIgnoringWhitespace("eval( name.startsWith(\"b\") && name.finishesWith(\"b\"))"); + + } + + @Test + public void parse_InfinityLiteral() throws Exception { + final String text = "rule \"infinity\"\n" + + "when\n" + + " StockTick( this after[-*,*] $another )\n" + + "then\n" + + "end"; + PatternDescr pattern = (PatternDescr) parseAndGetFirstRuleDescr( + text ).getLhs().getDescrs().get( 0 ); + + assertThat(pattern.getObjectType()).isEqualTo("StockTick"); + ExprConstraintDescr constr = (ExprConstraintDescr) pattern.getConstraint().getDescrs().get( 0 ); + assertThat(constr.getText()).isEqualTo("this after[-*,*] $another"); + + } + + @Test + public void parse_EntryPointDeclaration() throws Exception { + final String text = "package org.drools\n" + + "declare entry-point eventStream\n" + + " @source(\"jndi://queues/events\")\n" + + " @foo( true )\n" + + "end"; + PackageDescr pkg = parser.parse( + text ); + + assertThat(pkg.getName()).isEqualTo("org.drools"); + assertThat(pkg.getEntryPointDeclarations().size()).isEqualTo(1); + + EntryPointDeclarationDescr epd = pkg.getEntryPointDeclarations().iterator().next(); + + assertThat(epd.getEntryPointId()).isEqualTo("eventStream"); + assertThat(epd.getAnnotations().size()).isEqualTo(2); + assertThat(epd.getAnnotation("source").getValue()).isEqualTo("\"jndi://queues/events\""); + assertThat(epd.getAnnotation("foo").getValue()).isEqualTo("true"); + } + + @Test + public void parse_WindowDeclaration() throws Exception { + final String text = "package org.drools\n" + + "declare window Ticks\n" + + " @doc(\"last 10 stock ticks\")\n" + + " $s : StockTick( source == \"NYSE\" )\n" + + " over window:length( 10, $s.symbol )\n" + + " from entry-point stStream\n" + + "end"; + PackageDescr pkg = parser.parse( + text ); + + assertThat(pkg.getName()).isEqualTo("org.drools"); + assertThat(pkg.getWindowDeclarations().size()).isEqualTo(1); + + WindowDeclarationDescr wdd = pkg.getWindowDeclarations().iterator().next(); + + assertThat(wdd.getName()).isEqualTo("Ticks"); + assertThat(wdd.getAnnotations().size()).isEqualTo(1); + assertThat(wdd.getAnnotation("doc").getValue()).isEqualTo("\"last 10 stock ticks\""); + + PatternDescr pd = wdd.getPattern(); + assertThat(pd).isNotNull(); + assertThat(pd.getIdentifier()).isEqualTo("$s"); + assertThat(pd.getObjectType()).isEqualTo("StockTick"); + assertThat(pd.getSource().getText()).isEqualTo("stStream"); + + assertThat(pd.getBehaviors().size()).isEqualTo(1); + BehaviorDescr bd = pd.getBehaviors().get( 0 ); + assertThat(bd.getType()).isEqualTo("window"); + assertThat(bd.getSubType()).isEqualTo("length"); + assertThat(bd.getParameters().size()).isEqualTo(2); + assertThat(bd.getParameters().get(0)).isEqualTo("10"); + assertThat(bd.getParameters().get(1)).isEqualTo("$s.symbol"); + } + + @Disabled("Priority : Mid | Implement using declared window. Not written in docs, but unit tests found.") + @Test + public void parse_WindowUsage() throws Exception { + final String text = "package org.drools\n" + + "rule X\n" + + "when\n" + + " StockTick() from window Y\n" + + "then\n" + + "end\n"; + PackageDescr pkg = parser.parse( + text ); + + assertThat(pkg.getName()).isEqualTo("org.drools"); + assertThat(pkg.getRules().size()).isEqualTo(1); + + RuleDescr rd = pkg.getRules().get(0); + + assertThat(rd.getName()).isEqualTo("X"); + assertThat(rd.getLhs().getDescrs().size()).isEqualTo(1); + + PatternDescr pd = (PatternDescr) rd.getLhs().getDescrs().get(0); + assertThat(pd).isNotNull(); + assertThat(pd.getObjectType()).isEqualTo("StockTick"); + assertThat(pd.getSource().getText()).isEqualTo("Y"); + } + + @Test + public void endInRhs() throws Exception { + final String text = "package org.drools\n" + + "rule X\n" + + "when\n" + + " $s : String()\n" + + "then\n" + + " System.out.println($s.endsWith(\"xyz\"));\n" + + "end\n"; + PackageDescr packageDescr = parser.parse(text ); + + RuleDescr ruleDescr = packageDescr.getRules().get(0); + assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("System.out.println($s.endsWith(\"xyz\"));"); + } + + @Test + public void endTokenInRhs() throws Exception { + final String text = "package org.drools\n" + + "rule X\n" + + "when\n" + + " $s : String()\n" + + "then\n" + + " int end = 10;\n" + + "end\n"; + PackageDescr packageDescr = parser.parse(text ); + + RuleDescr ruleDescr = packageDescr.getRules().get(0); + assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("int end = 10;"); + } + + @Test + public void ruleTokenInRhs() throws Exception { + final String text = "package org.drools\n" + + "rule X\n" + + "when\n" + + " $s : String()\n" + + "then\n" + + " int rule = 10;\n" + + "end\n"; + PackageDescr packageDescr = parser.parse(text ); + + RuleDescr ruleDescr = packageDescr.getRules().get(0); + assertThat(ruleDescr.getConsequence().toString()).isEqualToIgnoringWhitespace("int rule = 10;"); + } +} diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/logback-test.xml b/drools-drl/drools-drl-parser-tests/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..7740901e7d3 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %date{HH:mm:ss.SSS} [%thread] %-5level %class{36}.%method:%line - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_Extends.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_Extends.drl new file mode 100644 index 00000000000..74cff04d3c1 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_Extends.drl @@ -0,0 +1,25 @@ +/** + * 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.compiler + +rule test_rule extends rule1 + when + $foo : foo() + then + +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_Metadata.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_Metadata.drl new file mode 100644 index 00000000000..122634a9b26 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_Metadata.drl @@ -0,0 +1,27 @@ +/** + * 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.compiler + +rule test_rule + @fooMeta1(barVal1) + @fooMeta2(barVal2) + when + then + System.out.println("Consequence"); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_nested_LHS.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_nested_LHS.drl new file mode 100644 index 00000000000..c009e6ec210 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/Rule_with_nested_LHS.drl @@ -0,0 +1,28 @@ +/** + * 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.compiler + +rule test + when + A() + ( B() and C() ) or + ( D() and E() ) or + ( F() and G() ) + then + +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate.drl new file mode 100755 index 00000000000..7f3f4ce75f9 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + +rule "AccumulateParserTest" +when + Integer() from accumulate( Person( age > 21 ), + init( int x = 0; ), + action( x++; ), + result( new Integer(x) ) ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateExternalFunction.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateExternalFunction.drl new file mode 100755 index 00000000000..43ee672e90b --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateExternalFunction.drl @@ -0,0 +1,23 @@ +/** + * 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. + */ + +rule "AccumulateReverseParserTest" +when + Number() from accumulate( Person( $age : age > 21 ), + average( $age ) ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateMultipleFunctions.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateMultipleFunctions.drl new file mode 100755 index 00000000000..0557d308f43 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateMultipleFunctions.drl @@ -0,0 +1,30 @@ +/** + * 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.compiler + +rule "Accumulate 1" +when + accumulate( Cheese( $price : price ), + $a1 : average( $price ), + $m1 : min( $price ), + $M1 : max( $price ) // binds are optional, but it makes no sense to not have a binding in this case + ) +then + // do something +end + diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateMultipleFunctionsConstraint.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateMultipleFunctionsConstraint.drl new file mode 100755 index 00000000000..9c146f05edb --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateMultipleFunctionsConstraint.drl @@ -0,0 +1,32 @@ +/** + * 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.compiler + +rule "Accumulate 1" +when + accumulate( Cheese( $price : price ); + $a1 : average( $price ), + $m1 : min( $price ), + $M1 : max( $price ); // binds are optional, but it makes no sense to not have a binding in this case + $a1 > 10 && $M1 <= 100, + $m1 == 5 // inline evals + ) +then + // do something +end + diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateReverse.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateReverse.drl new file mode 100755 index 00000000000..76872623dda --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulateReverse.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + +rule "AccumulateReverseParserTest" +when + Integer() from accumulate( Person( age > 21 ), + init( int x = 0; ), + action( x++; ), + reverse( x--; ), + result( new Integer(x) ) ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_multi_pattern.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_multi_pattern.drl new file mode 100755 index 00000000000..8d3f6828f50 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_multi_pattern.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + +rule "AccumulateMultiPatternParserTest" +when + $counter:Integer() from accumulate( $person : Person( age > 21 ) and Cheese( type == $person.likes ), + init( int x = 0; ), + action( x++; ), + result( new Integer(x) ) ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_with_bindings.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_with_bindings.drl new file mode 100755 index 00000000000..75d70816931 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_with_bindings.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + +rule "AccumulateParserTest" +when + $counter:Integer() from accumulate( $person : Person( age > 21 ), + init( int x = 0; ), + action( x++; ), + result( new Integer(x) ) ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_with_nested_from.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_with_nested_from.drl new file mode 100755 index 00000000000..3696836b1f2 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/accumulate_with_nested_from.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + +rule "AccumulateParserTest" +when + // below statement makes no sense, but is useful to test parsing recursiveness + $personList : ArrayList() from accumulate( Person( $age : age > 21 || < 10 ) from collect( People() from $town.getPeople() ), + max( $age ) ); +then +end + diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/almost_empty_rule.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/almost_empty_rule.drl new file mode 100644 index 00000000000..5155aa035de --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/almost_empty_rule.drl @@ -0,0 +1,22 @@ +/** + * 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. + */ + + +rule almost_empty + when + then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/and_or_rule.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/and_or_rule.drl new file mode 100644 index 00000000000..e81b1d4a18f --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/and_or_rule.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +import org.drools.compiler.Person + +rule simple_rule + when + Person(name == "mark") and Cheese(type == "stilton") + Person(name == "mark") or Cheese(type == "stilton") + then + System.out.println( "Mark and Michael" ); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/autofocus.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/autofocus.drl new file mode 100644 index 00000000000..48fa4c52a19 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/autofocus.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + + +rule rule1 + auto-focus true + when + not Cheese(type == "stilton") + then + funky(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/basic_binding.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/basic_binding.drl new file mode 100644 index 00000000000..7c48ee595b5 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/basic_binding.drl @@ -0,0 +1,27 @@ +/** + * 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.compiler.test; + +import org.drools.compiler.Cheese; + +rule "like cheddar" + when + Cheese( $type:type ) + then + System.out.println("I like " + $type); +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/bindings.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/bindings.drl new file mode 100644 index 00000000000..6aabea7bd1b --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/bindings.drl @@ -0,0 +1,29 @@ +/** + * 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.compiler.test; + +import org.drools.compiler.Cheese; +import org.drools.compiler.Person; + +rule "Who likes Stilton" + when + Cheese($type : type == "stilton") + $person : Person( $name : name == "bob", likes == $type) + then + System.out.println( $name + " likes " + $type); +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/brackets_precedence.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/brackets_precedence.drl new file mode 100644 index 00000000000..8259a28773c --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/brackets_precedence.drl @@ -0,0 +1,23 @@ +/** + * 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. + */ + + +rule simple_rule + when + ( (not Foo(x=="a") or Foo(x=="y") ) and ( Shoes() or Butt() ) ) + then +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/collect.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/collect.drl new file mode 100755 index 00000000000..839e2c6c19e --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/collect.drl @@ -0,0 +1,22 @@ +/** + * 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. + */ + +rule "CollectParserTest" +when + $personList : ArrayList() from collect( Person( age > 21 ) ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/collect_with_nested_from.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/collect_with_nested_from.drl new file mode 100755 index 00000000000..146b9e46c6a --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/collect_with_nested_from.drl @@ -0,0 +1,24 @@ +/** + * 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. + */ + +rule "CollectParserTest" +when + // below statement makes no sense, but is useful to test parsing recursiveness + $personList : ArrayList() from collect( $p : Person( age > 21 || age < 10 ) from collect( People() from $town.getPeople() ) ); +then +end + diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/comment.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/comment.drl new file mode 100644 index 00000000000..e46ecbe4601 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/comment.drl @@ -0,0 +1,32 @@ +/** + * 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. + */ + +//this starts with a comment +package foo.bar + +//and another comment + +/* +yet + another + style +*/ + +rule "test" + when + then +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/complex.dsl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/complex.dsl new file mode 100644 index 00000000000..b2cf4cb9c84 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/complex.dsl @@ -0,0 +1,5 @@ +#place your comments here - this is just a description for your own purposes. +[when]There is a Person with name of {name}=Person(name=="{name}") +[when]Person is at least {age} years old and lives in {location}=Person(age > {age}, location == "{location}") +[then]Log "{message}"=System.out.println("{message}"); +[when]Or=or diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/declaration-in-consequence.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/declaration-in-consequence.drl new file mode 100644 index 00000000000..836a53dcd57 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/declaration-in-consequence.drl @@ -0,0 +1,48 @@ +/** + * 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. + */ + + + +rule myrule + when + then + int i = 0; + i = 1; + i / 1; + i == 1; + i(i); + i = 'i'; + i.i.i; + ii; + i="i"; + ++i; + i++; + --i; + i--; + i += i; + i -= i; + i *= i; + i /= i; + int i = 5; + for(int j; j 21, $likes : likes ) + Cheese( type == $likes ) ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/forallwithfrom.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/forallwithfrom.drl new file mode 100644 index 00000000000..d387cf9c647 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/forallwithfrom.drl @@ -0,0 +1,23 @@ +/** + * 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. + */ + +rule "ForallParserTest" +when + forall( Person( age > 21, $likes : likes ) from $village + Cheese( type == $likes ) from $cheesery ); +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/from.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/from.drl new file mode 100644 index 00000000000..47f918be139 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/from.drl @@ -0,0 +1,31 @@ +/** + * 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. + */ + +rule using_from + when + Foo() from bar.baz + Foo() from bar.baz["key"] + Foo() from bar.baz[$key] + Foo() from bar.baz[1] + Whee(bar=="baz") from whee("y") + f: Foo(la==2) from bar.la(x) + Bam() from wa() + Kah() from la.wa(42, 42.42, false, null) + Bam(a=="c") + then + whee(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/function_arrays.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/function_arrays.drl new file mode 100644 index 00000000000..98df27bf96a --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/function_arrays.drl @@ -0,0 +1,31 @@ +/** + * 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 foo + +function String[] yourFunction(String args[]) { + baz(); +} + +rule "new rule" + + when + Something() + then + yourFunction(new String[] {"a","b","c"}); + +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/functions.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/functions.drl new file mode 100644 index 00000000000..2a543650f58 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/functions.drl @@ -0,0 +1,43 @@ +/** + * 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. + */ + + +import java.lang.String + +function String functionA(String s, Integer i) { + + foo(); + +} + +function void functionB() { + bar(); +} + + +rule something + when + then +end + +rule "one more thing" + when + then +end + + + diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/globals.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/globals.drl new file mode 100644 index 00000000000..ed6d45ff31f --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/globals.drl @@ -0,0 +1,30 @@ +/** + * 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.compiler.test; + +import org.drools.compiler.Cheese; + +global java.lang.String foo +global java.lang.Integer bar; + +rule baz + when + Cheese( ) + then + +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/in_operator_test.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/in_operator_test.drl new file mode 100644 index 00000000000..54f5225c3e8 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/in_operator_test.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + +//testing 'in' operator + +rule simple_rule + when + Person(age > 30 && < 40) + Vehicle(type in ( "sedan", "wagon" ), age < 3) + then + consequence(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/lhs_semicolon_delim.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/lhs_semicolon_delim.drl new file mode 100644 index 00000000000..3d4d2df7c75 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/lhs_semicolon_delim.drl @@ -0,0 +1,29 @@ +/** + * 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. + */ + + +rule simple_rule + when + foo3 : Bar(a==3) ; foo4 : Bar(a4:a==4) ; Baz() + then + if ( a == b ) { + assert( foo3 ); + } else { + retract( foo4 ); + } + System.out.println( a4 ); +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/literal_bool_and_negative.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/literal_bool_and_negative.drl new file mode 100644 index 00000000000..a1d01a65573 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/literal_bool_and_negative.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + +//check that it can handle true/false literals, and +//negative numbers +rule simple_rule + when + Foo(bar == false) + Foo(boo > -42) + Foo(boo > -42.42) + then + cons(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/multiple_constraints.dsl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/multiple_constraints.dsl new file mode 100644 index 00000000000..92a142ec007 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/multiple_constraints.dsl @@ -0,0 +1,6 @@ +#place your comments here - this is just a description for your own purposes. +[when]There is a Person with=Person() +[when]- age less than {age}=age < {age} +[when]- location is '{city}'=location=={city} +[when]Bar bar black sheep=Bar() +[then]Log "{message}"=System.out.println("{message}"); diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/multiple_rules.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/multiple_rules.drl new file mode 100644 index 00000000000..067e55fdb9d --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/multiple_rules.drl @@ -0,0 +1,34 @@ +/** + * 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.compiler.test; + +import org.drools.integrationtests.Cheese; + +rule "Like Stilton" + when + Cheese( t:type == "stilton" ) + then + System.out.println("I like " + t); +end + +rule "Like Cheddar" + when + Cheese( t:type == "cheddar" ) + then + System.out.println("I like " + t ); +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/nested_conditional_elements.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/nested_conditional_elements.drl new file mode 100755 index 00000000000..0b11f17c562 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/nested_conditional_elements.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + +rule "test nested CEs" + when + not ( State( $state : state ) and + not( Person( status == $state, $likes : likes ) and + Cheese( type == $likes ) ) ) + Person( name == "Bob" ) + ( Cheese( price == 10 ) or Cheese( type == "brie" ) ) + then + results.add("OK"); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/no-loop.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/no-loop.drl new file mode 100644 index 00000000000..768fa8775c8 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/no-loop.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + + +rule rule1 + no-loop false + when + not Cheese(type == "stilton") + then + funky(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/not_exist_with_brackets.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/not_exist_with_brackets.drl new file mode 100644 index 00000000000..8ec3fd4f86e --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/not_exist_with_brackets.drl @@ -0,0 +1,26 @@ +/** + * 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 HR1 + +rule simple_rule + when + not ( Cheese(type == "stilton") ) + exists ( Foo() ) + then + funky(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/not_with_constraint.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/not_with_constraint.drl new file mode 100644 index 00000000000..573fe4808d6 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/not_with_constraint.drl @@ -0,0 +1,31 @@ +/** + * 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.compiler.test; + +import org.drools.compiler.Cheese; + +global java.util.List list; +global java.lang.Integer five; + +rule "not rule test" + when + $person : Person( $likes:like ) + not Cheese( type == $likes ) + then + list.add( $person ); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/notin_operator_test.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/notin_operator_test.drl new file mode 100644 index 00000000000..a2eae254405 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/notin_operator_test.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + +//testing not 'in' operator + +rule simple_rule + when + Person(age > 30 && < 40) + Vehicle(type not in ( "sedan", "wagon" ), age < 3) + then + consequence(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding.drl new file mode 100644 index 00000000000..87907a60b7a --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +import org.drools.compiler.Person + +rule simple_rule + when + foo : ( Person(name == "mark") or Person(type == "fan") ) + Cheese(type == "green") + then + System.out.println( "Mark and Michael" + bar ); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding_complex.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding_complex.drl new file mode 100644 index 00000000000..2090d8f8565 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding_complex.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + + +rule simple_rule + when + foo : ( Person(name == "mark") + or + Person(type == "fan") ) + then + System.out.println( "Mark and Michael" + bar ); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding_with_brackets.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding_with_brackets.drl new file mode 100644 index 00000000000..a96bae49029 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_binding_with_brackets.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + +rule simple_rule + when + foo : ( + Person(name == "mark") or Person(type == "fan") + ) + then + System.out.println( "Mark and Michael" + bar ); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_ce.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_ce.drl new file mode 100644 index 00000000000..3e576f091a8 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_ce.drl @@ -0,0 +1,26 @@ +/** + * 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.compiler + +rule "testing OR CE" +when + $p : Person( name == "bob" ) + $c : Cheese( type == $p.likes ) or Cheese( price == 10 ) +then + // do something +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_nesting.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_nesting.drl new file mode 100644 index 00000000000..ecf9ddb2338 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/or_nesting.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +import org.drools.compiler.Person + +rule simple_rule + when + Person(name == "mark") or + ( Person(type == "fan") and Cheese(type == "green") ) + then + System.out.println( "Mark and Michael" + bar ); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/package_attributes.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/package_attributes.drl new file mode 100644 index 00000000000..156f5c58f6d --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/package_attributes.drl @@ -0,0 +1,40 @@ +/** + * 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 com.foo; + +agenda-group "x" + +import goo.ber +import wee.waa + + +dialect "java" + + + + +rule bar + when + then +end + +rule baz + dialect "mvel" + when + then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/pluggable_operators.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/pluggable_operators.drl new file mode 100644 index 00000000000..59c31d2c8e7 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/pluggable_operators.drl @@ -0,0 +1,28 @@ +/** + * 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.compiler + +rule "test pluggable operators" +when + $a : EventA() + $b : EventB( this after[1,10] $a || this not after[15,20] $a ) + $c : EventC( this finishes $b ) + $d : EventD( this not starts $a ) + $e : EventE( this not before[1, 10] $b || after[1, 10] $c && this after[1, 5] $d ) +then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/qualified_classname.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/qualified_classname.drl new file mode 100644 index 00000000000..9347834bbbb --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/qualified_classname.drl @@ -0,0 +1,25 @@ +/** + * 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.compiler.test; + +rule "Who likes Stilton" + when + com.cheeseco.Cheese($type : type == "stilton") + then + System.out.println( $name + " likes " + $type); +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/query_and_rule.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/query_and_rule.drl new file mode 100644 index 00000000000..e9b9be3edd2 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/query_and_rule.drl @@ -0,0 +1,47 @@ +/** + * 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 foo + +rule bar + when + Baz() + then + Boo() +end + +query "simple_query" + foo3 : Bar(a==3) + foo4 : Bar(a4:a==4) + Baz() + +end + +rule bar2 + when + Baz() + then + Boo() +end + +query "simple_query2" + foo3 : Bar(a==3) + foo4 : Bar(a4:a==4) + Baz() + +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/quoted_string_name_rule.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/quoted_string_name_rule.drl new file mode 100644 index 00000000000..9eda0358e56 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/quoted_string_name_rule.drl @@ -0,0 +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. + */ + +rule "quoted string name" + when + then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/restrictions_test.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/restrictions_test.drl new file mode 100644 index 00000000000..edf674db5ed --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/restrictions_test.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + +//this is for showing off all the new multi restriction stuff + +rule simple_rule + when + Person(age > 30 && < 40) + Vehicle(type == "sedan" || == "wagon", age < 3) + then + consequence(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes.drl new file mode 100644 index 00000000000..faf0d0c3351 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes.drl @@ -0,0 +1,33 @@ +/** + * 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. + */ + + + + +rule simple_rule + // attributes keywork (and colon) is totally optional + salience 42 + agenda-group "my_group" + no-loop + duration 42 + activation-group "my_activation_group" + lock-on-active true + when + Foo() + then + bar(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes2.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes2.drl new file mode 100644 index 00000000000..cece840aa59 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes2.drl @@ -0,0 +1,46 @@ +/** + * 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 foo.bar + + +rule rule1 + salience (42) + agenda-group "my_group" + when + Foo() + then + bar(); +end + +rule rule2 + salience (Integer.MIN_VALUE) + no-loop + when + Foo() + then + bar(); +end + +rule rule3 + enabled (Boolean.TRUE) + activation-group "my_activation_group" + when + Foo() + then + bar(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes_alt.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes_alt.drl new file mode 100644 index 00000000000..fafc7032171 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_attributes_alt.drl @@ -0,0 +1,28 @@ +/** + * 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. + */ + + + + +rule simple_rule + attributes: + salience 42, agenda-group "my_group", no-loop, lock-on-active, duration 42, activation-group "my_activation_group" + when + Foo() + then + bar(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_calendars_attribute.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_calendars_attribute.drl new file mode 100644 index 00000000000..2605f494aa9 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_calendars_attribute.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + + +rule simple_rule + calendars "cal1" + lock-on-active true + when + Foo() + then + bar(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_calendars_attribute2.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_calendars_attribute2.drl new file mode 100644 index 00000000000..31040f9e95f --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_calendars_attribute2.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + + +rule simple_rule + calendars "cal 1", "cal 2", "cal 3" + lock-on-active true + when + Foo() + then + bar(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_duration_expression.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_duration_expression.drl new file mode 100644 index 00000000000..c0ca3ba4430 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_duration_expression.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + + +rule simple_rule + duration ( 1h30m ) + lock-on-active true + when + Foo() + then + bar(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_enabled_expression.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_enabled_expression.drl new file mode 100644 index 00000000000..12550c159c7 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_enabled_expression.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +rule simple_rule + enabled ( 1 + 1 == 2 ) + salience ( 1+2 ) + lock-on-active true + when + Foo() + then + bar(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_names_number_prefix.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_names_number_prefix.drl new file mode 100644 index 00000000000..cd14e5699a6 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_names_number_prefix.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +rule "1. Do Stuff!" + when + then +end + +rule "2. Do More Stuff!" + when + then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_not.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_not.drl new file mode 100644 index 00000000000..96a2448ca41 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/rule_not.drl @@ -0,0 +1,24 @@ +/** + * 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. + */ + + +rule simple_rule + when + not Cheese(type == "stilton") + then + funky(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/ruleflowgroup.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/ruleflowgroup.drl new file mode 100755 index 00000000000..8b48eea0fe9 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/ruleflowgroup.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + + +rule rule1 + ruleflow-group "a group" + when + not Cheese(type == "stilton") + then + funky(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/semicolon.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/semicolon.drl new file mode 100644 index 00000000000..7d25f230e25 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/semicolon.drl @@ -0,0 +1,43 @@ +/** + * 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.mvel.compiler + +global java.util.List list; + +rule "rule1" +when + Pattern1(); + Pattern2() from x.y.z; +then + System.out.println("Test"); +end; + +query "query1" + Pattern5(); + Pattern6(); + Pattern7(); +end; + +rule "rule2" +when + Pattern3(); + Pattern4() from collect( Pattern5() ); +then + System.out.println("Test"); +end; + diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/simple_query.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/simple_query.drl new file mode 100644 index 00000000000..2532abc0b6c --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/simple_query.drl @@ -0,0 +1,25 @@ +/** + * 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. + */ + + + +query "simple_query" + foo3 : Bar(a==3) + foo4 : Bar(a4:a==4) + Baz() + +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/simple_rule.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/simple_rule.drl new file mode 100644 index 00000000000..afc7e6480b0 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/simple_rule.drl @@ -0,0 +1,29 @@ +/** + * 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. + */ +rule simple_rule + when + foo3 : Bar(a==3) + foo4 : Bar(a4:a==4) + Baz() + then + if ( a == b ) { + assert( foo3 ); + } else { + retract( foo4 ); + } + System.out.println( a4 ); +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/soundslike_operator.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/soundslike_operator.drl new file mode 100644 index 00000000000..0b6c21d5bc4 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/soundslike_operator.drl @@ -0,0 +1,30 @@ +/** + * 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 nesting; + + + + +rule "test something" + + when + p: Person( name soundslike "Michael" ) + then + p.name = "goober" + System.out.println(p.name) +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/statement_ordering_1.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/statement_ordering_1.drl new file mode 100644 index 00000000000..4b0b93995d1 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/statement_ordering_1.drl @@ -0,0 +1,45 @@ +/** + * 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 com.foo; + +import im.one + +import im.two + +rule foo + when + then +end + +function cheeseIt() { + +} + +import im.three; + +rule bar + when + then +end + +function uncheeseIt() { + +} + +import im.four; \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/ternary_expression.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/ternary_expression.drl new file mode 100644 index 00000000000..23013d6b5d2 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/ternary_expression.drl @@ -0,0 +1,26 @@ +/** + * 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 foo + +rule rule_one + when + Foo() + then + if (speed > speedLimit ? true : false;) + pullEmOver(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_CommentLineNumbersInConsequence.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_CommentLineNumbersInConsequence.drl new file mode 100644 index 00000000000..7c7df07bd51 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_CommentLineNumbersInConsequence.drl @@ -0,0 +1,34 @@ +/** + * 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 la + + +rule simple_rule + when + Baz() + then + //woot + first; + + // + + /* lala + + */ + second; +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_ComplexChainedCallWithFrom.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_ComplexChainedCallWithFrom.drl new file mode 100755 index 00000000000..4c50536c900 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_ComplexChainedCallWithFrom.drl @@ -0,0 +1,24 @@ +/** + * 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. + */ + +rule blah + when + Col1() from doIt1( foo,bar,42,"hello",[ a : "b"], [a, "b", 42] ).doIt2(bar, [a, "b", 42]).field["key"] + Col2() + then + partay(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_EmptyPattern.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_EmptyPattern.drl new file mode 100644 index 00000000000..de85f100561 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_EmptyPattern.drl @@ -0,0 +1,26 @@ +/** + * 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.compiler.test; + +import org.drools.compiler.Cheese; + +rule "simple rule" + when + Cheese( ) + then +end \ No newline at end of file diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_EndPosition.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_EndPosition.drl new file mode 100644 index 00000000000..f867e69fe2d --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_EndPosition.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + + + +rule simple_rule + when + Foo( + bar == baz, la==laz + ) + then +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_FunctionImport.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_FunctionImport.drl new file mode 100644 index 00000000000..1b56f045643 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_FunctionImport.drl @@ -0,0 +1,29 @@ +/** + * 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 HR1 + +import function abd.def.x +import function qed.wah.* + +rule simple_rule + when + not ( Cheese(type == "stilton") ) + exists ( Foo() ) + then + funky(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleAccessorArgWithFrom.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleAccessorArgWithFrom.drl new file mode 100644 index 00000000000..2bf358046e0 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleAccessorArgWithFrom.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +rule blah + + when + + Col1() from something.doIt["key"] + Col2() + then + partay(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleAccessorWithFrom.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleAccessorWithFrom.drl new file mode 100644 index 00000000000..da376e84385 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleAccessorWithFrom.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +rule blah + + when + + Col1() from something.doIt + Col2() + then + partay(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleFunctionCallWithFrom.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleFunctionCallWithFrom.drl new file mode 100644 index 00000000000..af156c5ea1c --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleFunctionCallWithFrom.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +rule blah + + when + + Col1() from doIt( foo,bar,42,"hello",[ a : "b", "something" : 42, "a" : foo, x : [x:y]],"end", [a, "b", 42] ) + Col2() + then + partay(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleMethodCallWithFrom.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleMethodCallWithFrom.drl new file mode 100644 index 00000000000..a8e32df1d53 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/test_SimpleMethodCallWithFrom.drl @@ -0,0 +1,27 @@ +/** + * 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. + */ + + +rule blah + + when + + Col1() from something.doIt( foo,bar,42,"hello",[ a : "b", "something" : 42, "a" : foo, x : [x:y]],"end", [a, "b", 42] ) + Col2() + then + partay(); +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/type_with_meta.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/type_with_meta.drl new file mode 100644 index 00000000000..708bd667b0a --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/type_with_meta.drl @@ -0,0 +1,48 @@ +/** + * 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.compiler + +declare NetworkNode + locElevation: java.math.BigDecimal + name: String @key + nodeClass: String + locLongitude: java.math.BigDecimal + nodeType: String + locLatitude: java.math.BigDecimal +end + +declare NetworkConnection + id: String @key + node1: NetworkNode + node2: NetworkNode + hops: Integer +end + +declare NetworkEvent + @role( event ) + @timestamp( creationTime ) + + id: String @key + locElevation: java.math.BigDecimal + description: String + sourceComponent: NetworkNode + locLongitude: java.math.BigDecimal + severity: Integer + creationTime: java.util.Date + locLatitude: java.math.BigDecimal +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_eval.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_eval.drl new file mode 100644 index 00000000000..c2e0145747d --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_eval.drl @@ -0,0 +1,26 @@ +/** + * 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. + */ + + +rule simple_rule + when + Foo() + Bar() + eval(abc("foo")) + then + Kapow +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_predicate.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_predicate.drl new file mode 100644 index 00000000000..41ff2715d8a --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_predicate.drl @@ -0,0 +1,23 @@ +/** + * 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. + */ + + +rule simple_rule + when + Person( $age2:age, $age2 == $age1+2 ) + then +end diff --git a/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_retval.drl b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_retval.drl new file mode 100644 index 00000000000..8db9671fee7 --- /dev/null +++ b/drools-drl/drools-drl-parser-tests/src/test/resources/org/drools/drl10/parser/with_retval.drl @@ -0,0 +1,23 @@ +/** + * 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. + */ + + +rule simple_rule + when + Foo(name== (a + b)) + then +end diff --git a/drools-drl/drools-drl-parser/pom.xml b/drools-drl/drools-drl-parser/pom.xml index 545625e5ad4..0eeb7871009 100644 --- a/drools-drl/drools-drl-parser/pom.xml +++ b/drools-drl/drools-drl-parser/pom.xml @@ -66,7 +66,32 @@ org.antlr antlr-runtime + + org.antlr + antlr4-runtime + + + + org.assertj + assertj-core + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + ch.qos.logback + logback-classic + test + @@ -101,6 +126,26 @@ + + + + org.antlr + antlr4-maven-plugin + ${version.org.antlr4} + + + + antlr4 + + + true + false + src/main/antlr4/org/drools/drl10/parser/ + + + + + diff --git a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRL6Expressions.g4 b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRL6Expressions.g4 new file mode 100644 index 00000000000..ef6e0982672 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRL6Expressions.g4 @@ -0,0 +1,847 @@ +parser grammar DRL6Expressions; + +options { + language = Java; + tokenVocab = DRLLexer; + superClass=DRLExpressions; +} + +@header { + import java.util.LinkedList; + import org.drools.drl.parser.DroolsParserException; + import org.drools.drl.parser.lang.DroolsEditorType; + import org.drools.drl.parser.lang.DroolsParserExceptionFactory; + import org.drools.drl.parser.lang.DroolsSentence; + import org.drools.drl.parser.lang.DroolsSoftKeywords; + import org.drools.drl.parser.lang.Location; + + import org.drools.drl.ast.dsl.AnnotatedDescrBuilder; + import org.drools.drl.ast.dsl.AnnotationDescrBuilder; + + import org.drools.drl.ast.descr.AnnotatedBaseDescr; + import org.drools.drl.ast.descr.AnnotationDescr; + import org.drools.drl.ast.descr.AtomicExprDescr; + import org.drools.drl.ast.descr.BaseDescr; + import org.drools.drl.ast.descr.BindingDescr; + import org.drools.drl.ast.descr.ConstraintConnectiveDescr; + import org.drools.drl.ast.descr.RelationalExprDescr; +} + +@members { + private ParserHelper helper; + + public DRL6Expressions(TokenStream input, + ParserHelper helper ) { + this( input ); + this.helper = helper; + } + + public ParserHelper getHelper() { return helper; } + public boolean hasErrors() { return helper.hasErrors(); } + public List getErrors() { return helper.getErrors(); } + public List getErrorMessages() { return helper.getErrorMessages(); } + public void enableEditorInterface() { helper.enableEditorInterface(); } + public void disableEditorInterface() { helper.disableEditorInterface(); } + public LinkedList getEditorInterface() { return helper.getEditorInterface(); } + public void reportError(RecognitionException ex) { helper.reportError( ex ); } + public void emitErrorMessage(String msg) {} + + private boolean buildDescr; + private int inMap = 0; + private int ternOp = 0; + private boolean hasBindings; + public void setBuildDescr( boolean build ) { this.buildDescr = build; } + public boolean isBuildDescr() { return this.buildDescr; } + + public void setLeftMostExpr( String value ) { helper.setLeftMostExpr( value ); } + public String getLeftMostExpr() { return helper.getLeftMostExpr(); } + + public void setHasBindings( boolean value ) { this.hasBindings = value; } + public boolean hasBindings() { return this.hasBindings; } + + @Override + public final BaseDescr conditionalOrExpressionDescr() throws RecognitionException { + return conditionalOrExpression().result; + } + + private boolean isNotEOF() { + // TODO verify that we can omit the backtracking check + /*if (state.backtracking != 0){ + return false; + }*/ + if (_input.get( _input.index() - 1 ).getType() == DRLLexer.WS){ + return true; + } + if (_input.LA(-1) == DRLLexer.LPAREN){ + return true; + } + return _input.get( _input.index() ).getType() != DRLLexer.EOF; + } + + private boolean notStartWithNewline() { + int currentTokenIndex = _input.index(); // current position in input stream + Token previousHiddenToken = _input.get(currentTokenIndex - 1); + String previousHiddenTokenText = previousHiddenToken.getText(); + return !previousHiddenTokenText.contains("\n"); + } +} + +// Alter code generation so catch-clauses get replace with +// this action. +@rulecatch { +catch (RecognitionException re) { + throw re; +} +} + +// -------------------------------------------------------- +// GENERAL RULES +// -------------------------------------------------------- +literal + : STRING_LITERAL { helper.emit($STRING_LITERAL, DroolsEditorType.STRING_CONST); } + | DRL_STRING_LITERAL { helper.emit($DRL_STRING_LITERAL, DroolsEditorType.STRING_CONST); } + | DECIMAL_LITERAL { helper.emit($DECIMAL_LITERAL, DroolsEditorType.NUMERIC_CONST); } + | HEX_LITERAL { helper.emit($HEX_LITERAL, DroolsEditorType.NUMERIC_CONST); } + | FLOAT_LITERAL { helper.emit($FLOAT_LITERAL, DroolsEditorType.NUMERIC_CONST); } + | BOOL_LITERAL { helper.emit($BOOL_LITERAL, DroolsEditorType.BOOLEAN_CONST); } + | NULL_LITERAL { helper.emit($NULL_LITERAL, DroolsEditorType.NULL_CONST); } + | TIME_INTERVAL { helper.emit($TIME_INTERVAL, DroolsEditorType.NULL_CONST); } + | MUL { helper.emit($MUL, DroolsEditorType.NUMERIC_CONST); } // this means "infinity" in Drools + ; + +operator returns [boolean negated, String opr] +@init{ if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); helper.setHasOperator( true ); } +// TODO verify that we can omit the backtracking check +@after{ if( /*state.backtracking == 0 &&*/ _input.LA( 1 ) != DRLLexer.EOF) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } } + : x=TILDE? + ( op=EQUAL { $negated = false; $opr=($x != null ? $x.text : "")+$op.text; helper.emit($op, DroolsEditorType.SYMBOL); } + | op=NOTEQUAL { $negated = false; $opr=($x != null ? $x.text : "")+$op.text; helper.emit($op, DroolsEditorType.SYMBOL); } + | rop=relationalOp { $negated = $rop.negated; $opr=($x != null ? $x.text : "")+$rop.opr; } + ) + ; + + + +relationalOp returns [boolean negated, String opr, java.util.List params] +@init{ if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); helper.setHasOperator( true ); } +// TODO verify that we can omit the backtracking check +@after{ if( /*state.backtracking == 0 &&*/ _input.LA( 1 ) != DRLLexer.EOF) { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } } + : ( op=LE { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | op=GE { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | op=LT { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | op=GT { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | xop=complexOp { $negated = false; $opr=$op.text; $params = null; helper.emit($op, DroolsEditorType.SYMBOL);} + | not_key nop=neg_operator_key { $negated = true; $opr=$nop.text;} + | cop=operator_key { $negated = false; $opr=$cop.text;} + ) + ; + +complexOp returns [String opr] + : t=TILDE e=ASSIGN { $opr=$t.text+$e.text; } + ; + +typeList + : type (COMMA type)* + ; + +type + : tm=typeMatch + ; + +typeMatch + : primitiveType (LBRACK RBRACK)* + | IDENTIFIER (typeArguments)? (DOT IDENTIFIER (typeArguments)? )* (LBRACK RBRACK)* + ; + +typeArguments + : LT typeArgument (COMMA typeArgument)* GT + ; + +typeArgument + : type + | QUESTION ((extends_key | super_key) type)? + ; + +// -------------------------------------------------------- +// EXPRESSIONS +// -------------------------------------------------------- +// the following dymmy rule is to force the AT symbol to be +// included in the follow set of the expression on the DFAs +dummy + : expression ( AT | SEMI | EOF | IDENTIFIER | RPAREN ) ; + +dummy2 + : relationalExpression EOF; + +// top level entry point for arbitrary expression parsing +expression returns [BaseDescr result] + : left=conditionalExpression { if( buildDescr ) { $result = $left.result; } } + (op=assignmentOperator right=expression)? + ; + +conditionalExpression returns [BaseDescr result] + : left=conditionalOrExpression { if( buildDescr ) { $result = $left.result; } } + ternaryExpression? + ; + +ternaryExpression +@init{ ternOp++; } + : QUESTION ts=expression COLON fs=expression + ; +finally { ternOp--; } + + +fullAnnotation [AnnotatedDescrBuilder inDescrBuilder] returns [AnnotationDescr result] +@init{ String n = ""; AnnotationDescrBuilder annoBuilder = null; } + : AT name=IDENTIFIER { n = $name.text; } ( DOT x=IDENTIFIER { n += "." + $x.text; } )* + { if( buildDescr ) { + if ( inDescrBuilder == null ) { + $result = new AnnotationDescr( n ); + } else { + annoBuilder = inDescrBuilder instanceof AnnotationDescrBuilder ? + ((AnnotationDescrBuilder) inDescrBuilder).newAnnotation( n ) : inDescrBuilder.newAnnotation( n ); + $result = (AnnotationDescr) annoBuilder.getDescr(); + } + } + } + annotationArgs[$result, annoBuilder] + ; + +annotationArgs [AnnotationDescr descr, AnnotatedDescrBuilder inDescrBuilder] + : LPAREN + ( + annotationElementValuePairs[descr, inDescrBuilder] + | value=annotationValue[inDescrBuilder] { if ( buildDescr ) { $descr.setValue( $value.result ); } } + )? + RPAREN + ; + +annotationElementValuePairs [AnnotationDescr descr, AnnotatedDescrBuilder inDescrBuilder] + : annotationElementValuePair[descr, inDescrBuilder] ( COMMA annotationElementValuePair[descr, inDescrBuilder] )* + ; + +annotationElementValuePair [AnnotationDescr descr, AnnotatedDescrBuilder inDescrBuilder] + : key=IDENTIFIER ASSIGN val=annotationValue[inDescrBuilder] { if ( buildDescr ) { $descr.setKeyValue( $key.text, $val.result ); } } + ; + +annotationValue[AnnotatedDescrBuilder inDescrBuilder] returns [Object result] + : exp=expression { if ( buildDescr ) $result = $exp.text; } + | annos=annotationArray[inDescrBuilder] { if ( buildDescr ) $result = $annos.result.toArray(); } + | anno=fullAnnotation[inDescrBuilder] { if ( buildDescr ) $result = $anno.result; } + ; + +annotationArray[AnnotatedDescrBuilder inDescrBuilder] returns [java.util.List result] +@init { $result = new java.util.ArrayList();} + : LBRACE ( anno=annotationValue[inDescrBuilder] { $result.add( $anno.result ); } + ( COMMA anno=annotationValue[inDescrBuilder] { $result.add( $anno.result ); } )* )? + RBRACE + ; + + + +conditionalOrExpression returns [BaseDescr result] + : left=conditionalAndExpression { if( buildDescr ) { $result = $left.result; } } + ( OR + { if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); } + args=fullAnnotation[null]? right=conditionalAndExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newOr(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( $ctx.args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* + ; + +conditionalAndExpression returns [BaseDescr result] + : left=inclusiveOrExpression { if( buildDescr ) { $result = $left.result; } } + ( AND + { if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); } + args=fullAnnotation[null]? right=inclusiveOrExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newAnd(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( $ctx.args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* + ; + +inclusiveOrExpression returns [BaseDescr result] + : left=exclusiveOrExpression { if( buildDescr ) { $result = $left.result; } } + ( BITOR right=exclusiveOrExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newIncOr(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + $result = descr; + } + } + )* + ; + +exclusiveOrExpression returns [BaseDescr result] + : left=andExpression { if( buildDescr ) { $result = $left.result; } } + ( CARET right=andExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newXor(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + $result = descr; + } + } + )* + ; + +andExpression returns [BaseDescr result] + : left=equalityExpression { if( buildDescr ) { $result = $left.result; } } + ( BITAND right=equalityExpression + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newIncAnd(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + $result = descr; + } + } + )* + ; + +equalityExpression returns [BaseDescr result] + : left=instanceOfExpression { if( buildDescr ) { $result = $left.result; } } + ( ( op=EQUAL | op=NOTEQUAL ) + { helper.setHasOperator( true ); + if( _input.LA( 1 ) != DRLLexer.EOF ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + right=instanceOfExpression + { if( buildDescr ) { + $result = new RelationalExprDescr( $op.text, false, null, $left.result, $right.result ); + } + } + )* + ; + +instanceOfExpression returns [BaseDescr result] + : left=inExpression { if( buildDescr ) { $result = $left.result; } } + ( op=instanceof_key + { helper.setHasOperator( true ); + if( _input.LA( 1 ) != DRLLexer.EOF ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + right=type + { if( buildDescr ) { + $result = new RelationalExprDescr( $op.text, false, null, $left.result, new AtomicExprDescr($right.text) ); + } + } + )? + ; + +inExpression returns [BaseDescr result] +@init { ConstraintConnectiveDescr descr = null; BaseDescr leftDescr = null; BindingDescr binding = null; } +@after { if( binding != null && descr != null ) descr.addOrMerge( binding ); } + : left=relationalExpression + { if( buildDescr ) { $result = $left.result; } + if( $left.result instanceof BindingDescr ) { + binding = (BindingDescr)$left.result; + leftDescr = new AtomicExprDescr( binding.getExpression() ); + } else { + leftDescr = $left.result; + } + } + (not_key in=in_key LPAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + e1=expression + { descr = ConstraintConnectiveDescr.newAnd(); + RelationalExprDescr rel1 = new RelationalExprDescr( "!=", false, null, leftDescr, $e1.result ); + descr.addOrMerge( rel1 ); + $result = descr; + } + (COMMA e2=expression + { RelationalExprDescr rel2 = new RelationalExprDescr( "!=", false, null, leftDescr, $e2.result ); + descr.addOrMerge( rel2 ); + } + )* RPAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_END ); } + | in=in_key LPAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + e1=expression + { descr = ConstraintConnectiveDescr.newOr(); + RelationalExprDescr rel1 = new RelationalExprDescr( "==", false, null, leftDescr, $e1.result ); + descr.addOrMerge( rel1 ); + $result = descr; + } + (COMMA e2=expression + { RelationalExprDescr rel2 = new RelationalExprDescr( "==", false, null, leftDescr, $e2.result ); + descr.addOrMerge( rel2 ); + } + )* RPAREN + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_END ); } + )? + ; + +relationalExpression returns [BaseDescr result] +locals [ BaseDescr lsd ] +// TODO access lsd directly instead of through dynamic context here +@init { $relationalExpression::lsd = null; } + : left=shiftExpression + { if( buildDescr ) { + if ( $left.result == null ) { + $result = new AtomicExprDescr( $left.text ); + } else if ( $left.result instanceof AtomicExprDescr ) { + if ( $left.text.equals(((AtomicExprDescr)$left.result).getExpression()) ) { + $result = $left.result; + } else { + $result = new AtomicExprDescr( $left.text ) ; + } + } else if ( $left.result instanceof BindingDescr ) { + if ( $left.text.equals(((BindingDescr)$left.result).getExpression()) ) { + $result = $left.result; + } else { + BindingDescr bind = (BindingDescr) $left.result; + int offset = bind.isUnification() ? 2 : 1; + String fullExpression = $left.text.substring( $left.text.indexOf( ":" ) + offset ).trim(); + $result = new BindingDescr( bind.getVariable(), bind.getExpression(), fullExpression, bind.isUnification() ); + } + } else { + $result = $left.result; + } + // TODO access lsd directly instead of through dynamic context here + $relationalExpression::lsd = $result; + } + } + ( right=orRestriction + { if( buildDescr ) { + $result = $right.result; + // TODO access lsd directly instead of through dynamic context here + $relationalExpression::lsd = $result; + } + } + )* + ; + +orRestriction returns [BaseDescr result] + : left=andRestriction { if( buildDescr ) { $result = $left.result; } } + ( lop=OR args=fullAnnotation[null]? right=andRestriction + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newOr(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( $ctx.args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* EOF? + ; + +andRestriction returns [BaseDescr result] + : left=singleRestriction { if( buildDescr ) { $result = $left.result; } } + ( lop=AND + { if ( isNotEOF() ) helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR ); } + args=fullAnnotation[null]?right=singleRestriction + { if( buildDescr ) { + ConstraintConnectiveDescr descr = ConstraintConnectiveDescr.newAnd(); + descr.addOrMerge( $result ); + descr.addOrMerge( $right.result ); + if ( $ctx.args != null ) { descr.addAnnotation( $args.result ); } + $result = descr; + } + } + )* + ; + +singleRestriction returns [BaseDescr result] + : op=operator + { helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT ); } + ( sa=squareArguments value=shiftExpression + | value=shiftExpression + ) + { if( buildDescr ) { + BaseDescr descr = ( $value.result != null && + ( (!($value.result instanceof AtomicExprDescr)) || + ($value.text.equals(((AtomicExprDescr)$value.result).getExpression())) )) ? + $value.result : + new AtomicExprDescr( $value.text ) ; + $result = new RelationalExprDescr( $op.opr, $op.negated, $ctx.sa != null ? $sa.args : null, $relationalExpression::lsd, descr ); + if( $relationalExpression::lsd instanceof BindingDescr ) { + $relationalExpression::lsd = new AtomicExprDescr( ((BindingDescr)$relationalExpression::lsd).getExpression() ); + } + } + helper.emit( Location.LOCATION_LHS_INSIDE_CONDITION_END ); + } + | LPAREN or=orRestriction RPAREN { $result = $or.result; } + ; + + + +shiftExpression returns [BaseDescr result] + : left=additiveExpression { if( buildDescr ) { $result = $left.result; } } + ( shiftOp additiveExpression )* + ; + +shiftOp + : ( LT LT + | GT GT GT + | GT GT ) + ; + +additiveExpression returns [BaseDescr result] + : left=multiplicativeExpression { if( buildDescr ) { $result = $left.result; } } + ( (ADD | SUB) multiplicativeExpression )* + ; + +multiplicativeExpression returns [BaseDescr result] + : left=unaryExpression { if( buildDescr ) { $result = $left.result; } } + ( ( MUL | DIV | MOD ) unaryExpression )* + ; + +unaryExpression returns [BaseDescr result] + : ADD ue=unaryExpression + { if( buildDescr ) { + $result = $ue.result; + if( $result instanceof AtomicExprDescr ) { + ((AtomicExprDescr)$result).setExpression( "+" + ((AtomicExprDescr)$result).getExpression() ); + } + } } + | SUB ue=unaryExpression + { if( buildDescr ) { + $result = $ue.result; + if( $result instanceof AtomicExprDescr ) { + ((AtomicExprDescr)$result).setExpression( "-" + ((AtomicExprDescr)$result).getExpression() ); + } + } } + | INC primary + | DEC primary + | left=unaryExpressionNotPlusMinus { if( buildDescr ) { $result = $left.result; } } + ; + +unaryExpressionNotPlusMinus returns [BaseDescr result] +@init { boolean isLeft = false; BindingDescr bind = null;} + : TILDE unaryExpression + | BANG ue=unaryExpression + { + if( buildDescr && $ue.result != null ) { + $result = $ue.result.negate(); + } + } + | castExpression + | backReferenceExpression + | { isLeft = helper.getLeftMostExpr() == null;} + ( ({inMap == 0 && ternOp == 0 && _input.LA(2) == DRLLexer.COLON}? (var=IDENTIFIER COLON + { hasBindings = true; helper.emit($var, DroolsEditorType.IDENTIFIER_VARIABLE); helper.emit($COLON, DroolsEditorType.SYMBOL); if( buildDescr ) { bind = new BindingDescr($var.text, null, false); helper.setStart( bind, $var ); } } )) + | ({inMap == 0 && ternOp == 0 && _input.LA(2) == DRLLexer.DRL_UNIFY}? (var=IDENTIFIER DRL_UNIFY + { hasBindings = true; helper.emit($var, DroolsEditorType.IDENTIFIER_VARIABLE); helper.emit($DRL_UNIFY, DroolsEditorType.SYMBOL); if( buildDescr ) { bind = new BindingDescr($var.text, null, true); helper.setStart( bind, $var ); } } )) + )? + + ( left2=xpathPrimary { if( buildDescr ) { $result = $left2.result; } } + | left1=primary { if( buildDescr ) { $result = $left1.result; } } + ) + + (selector)* + { + if( buildDescr ) { + String expr = $text; + if( isLeft ) { + helper.setLeftMostExpr( expr ); + } + if( bind != null ) { + if( bind.isUnification() ) { + expr = expr.substring( expr.indexOf( ":=" ) + 2 ).trim(); + } else { + expr = expr.substring( expr.indexOf( ":" ) + 1 ).trim(); + } + bind.setExpressionAndBindingField( expr ); + helper.setEnd( bind ); + $result = bind; + } + } + } + ((INC|DEC))? + ; + +castExpression + : LPAREN primitiveType RPAREN expr=unaryExpression + | LPAREN type RPAREN unaryExpressionNotPlusMinus + ; + +backReferenceExpression + : (DOT DOT DIV)+ unaryExpressionNotPlusMinus + ; + +primitiveType + : boolean_key + | char_key + | byte_key + | short_key + | int_key + | long_key + | float_key + | double_key + ; + +xpathSeparator + : DIV + | QUESTION_DIV + ; + +xpathPrimary returns [BaseDescr result] + : xpathChunk ({notStartWithNewline()}? xpathChunk)* + ; + +xpathChunk returns [BaseDescr result] + : xpathSeparator IDENTIFIER (DOT IDENTIFIER)* (HASH IDENTIFIER)? (LBRACK xpathExpressionList RBRACK)? + ; + +xpathExpressionList returns [java.util.List exprs] +@init { $exprs = new java.util.ArrayList();} + : f=expression { $exprs.add( $f.text ); } + (COMMA s=expression { $exprs.add( $s.text ); })* + ; + +primary returns [BaseDescr result] + : expr=parExpression { if( buildDescr ) { $result = $expr.result; } } + | nonWildcardTypeArguments (explicitGenericInvocationSuffix | this_key arguments) + | literal { if( buildDescr ) { $result = new AtomicExprDescr( $literal.text, true ); } } + //| this_key ({!helper.validateSpecialID(2)}? DOT IDENTIFIER)* ({helper.validateIdentifierSufix()}? identifierSuffix)? + | super_key superSuffix + | new_key creator + | primitiveType (LBRACK RBRACK)* DOT class_key + //| void_key DOT class_key + | inlineMapExpression + | inlineListExpression + | i1=IDENTIFIER { helper.emit($i1, DroolsEditorType.IDENTIFIER); } + ( + ( d=DOT i2=IDENTIFIER { helper.emit($d, DroolsEditorType.SYMBOL); helper.emit($i2, DroolsEditorType.IDENTIFIER); } ) + | + ( d=(DOT|NULL_SAFE_DOT) LPAREN { helper.emit($d, DroolsEditorType.SYMBOL); helper.emit($LPAREN, DroolsEditorType.SYMBOL); } + expression (COMMA { helper.emit($COMMA, DroolsEditorType.SYMBOL); } expression)* + RPAREN { helper.emit($RPAREN, DroolsEditorType.SYMBOL); } + ) + | + ( h=HASH i2=IDENTIFIER { helper.emit($h, DroolsEditorType.SYMBOL); helper.emit($i2, DroolsEditorType.IDENTIFIER); } ) + | + ( n=NULL_SAFE_DOT i2=IDENTIFIER { helper.emit($n, DroolsEditorType.SYMBOL); helper.emit($i2, DroolsEditorType.IDENTIFIER); } ) + )* (identifierSuffix)? + ; + +inlineListExpression + : LBRACK expressionList? RBRACK + ; + +inlineMapExpression +@init{ inMap++; } + : LBRACK mapExpressionList RBRACK + ; +finally { inMap--; } + +mapExpressionList + : mapEntry (COMMA mapEntry)* + ; + +mapEntry + : expression COLON expression + ; + +parExpression returns [BaseDescr result] + : LPAREN expr=expression RPAREN + { if( buildDescr ) { + $result = $expr.result; + if( $result instanceof AtomicExprDescr ) { + ((AtomicExprDescr)$result).setExpression("(" +((AtomicExprDescr)$result).getExpression() + ")" ); + } + } + } + ; + +identifierSuffix + : (LBRACK { helper.emit($LBRACK, DroolsEditorType.SYMBOL); } + RBRACK { helper.emit($RBRACK, DroolsEditorType.SYMBOL); } )+ + DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } class_key + | (LBRACK { helper.emit($LBRACK, DroolsEditorType.SYMBOL); } + expression + RBRACK { helper.emit($RBRACK, DroolsEditorType.SYMBOL); } )+ // can also be matched by selector, but do here + | arguments +// | DOT class_key +// | DOT explicitGenericInvocation +// | DOT this_key +// | DOT super_key arguments +// | DOT new_key (nonWildcardTypeArguments)? innerCreator + ; + +creator + : nonWildcardTypeArguments? createdName + (arrayCreatorRest | classCreatorRest) + ; + +createdName + : IDENTIFIER typeArguments? + ( DOT IDENTIFIER typeArguments?)* + | primitiveType + ; + +innerCreator + : {!(helper.validateIdentifierKey(DroolsSoftKeywords.INSTANCEOF))}? IDENTIFIER classCreatorRest + ; + +arrayCreatorRest + : LBRACK + ( RBRACK (LBRACK RBRACK)* arrayInitializer + | expression RBRACK ({!helper.validateLT(2,"]")}? LBRACK expression RBRACK)* (LBRACK RBRACK)* + ) + ; + +variableInitializer + : arrayInitializer + | expression + ; + +arrayInitializer + : LBRACE (variableInitializer (COMMA variableInitializer)* (COMMA)? )? RBRACE + ; + +classCreatorRest + : arguments //classBody? //sotty: restored classBody to allow for inline, anonymous classes + ; + +explicitGenericInvocation + : nonWildcardTypeArguments arguments + ; + +nonWildcardTypeArguments + : LT typeList GT + ; + +explicitGenericInvocationSuffix + : super_key superSuffix + | IDENTIFIER arguments + ; + +selector + : DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } super_key superSuffix + | DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } new_key (nonWildcardTypeArguments)? innerCreator + | DOT { helper.emit($DOT, DroolsEditorType.SYMBOL); } + IDENTIFIER { helper.emit($IDENTIFIER, DroolsEditorType.IDENTIFIER); } + (arguments)? + | NULL_SAFE_DOT { helper.emit($NULL_SAFE_DOT, DroolsEditorType.SYMBOL); } + IDENTIFIER { helper.emit($IDENTIFIER, DroolsEditorType.IDENTIFIER); } + (arguments)? + //| DOT this_key + | LBRACK { helper.emit($LBRACK, DroolsEditorType.SYMBOL); } + expression + RBRACK { helper.emit($RBRACK, DroolsEditorType.SYMBOL); } + ; + +superSuffix + : arguments + | DOT IDENTIFIER (arguments)? + ; + +squareArguments returns [java.util.List args] + : LBRACK (el=expressionList { $args = $el.exprs; })? RBRACK + ; + +arguments + : LPAREN { helper.emit($LPAREN, DroolsEditorType.SYMBOL); } + expressionList? + RPAREN { helper.emit($RPAREN, DroolsEditorType.SYMBOL); } + ; + +expressionList returns [java.util.List exprs] +@init { $exprs = new java.util.ArrayList();} + : f=expression { $exprs.add( $f.text ); } + (COMMA s=expression { $exprs.add( $s.text ); })* + ; + +assignmentOperator + : ASSIGN + | ADD_ASSIGN + | SUB_ASSIGN + | MUL_ASSIGN + | DIV_ASSIGN + | AND_ASSIGN + | OR_ASSIGN + | XOR_ASSIGN + | MOD_ASSIGN + | LT LT ASSIGN + | GT GT GT ASSIGN + | GT GT ASSIGN + ; + +// -------------------------------------------------------- +// KEYWORDS +// -------------------------------------------------------- +extends_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.EXTENDS))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +super_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.SUPER))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +instanceof_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.INSTANCEOF))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +boolean_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.BOOLEAN))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +char_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.CHAR))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +byte_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.BYTE))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +short_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.SHORT))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +int_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.INT))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +float_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.FLOAT))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +long_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.LONG))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +double_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.DOUBLE))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +void_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.VOID))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +this_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.THIS))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +class_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.CLASS))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +new_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.NEW))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +not_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.NOT))}? id=DRL_NOT { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +in_key + : {(helper.validateIdentifierKey(DroolsSoftKeywords.IN))}? id=DRL_IN { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +operator_key + // TODO get rid of the DRL_MATCHES token or introduce DRL_CONTAINS etc. for consistency. + : {(helper.isPluggableEvaluator(false))}? id=(IDENTIFIER|DRL_MATCHES) { helper.emit($id, DroolsEditorType.KEYWORD); } + ; + +neg_operator_key + : {(helper.isPluggableEvaluator(true))}? id=IDENTIFIER { helper.emit($id, DroolsEditorType.KEYWORD); } + ; diff --git a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRLLexer.g4 b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRLLexer.g4 new file mode 100644 index 00000000000..8aac81873cc --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRLLexer.g4 @@ -0,0 +1,161 @@ +lexer grammar DRLLexer; + +import JavaLexer; + +@members { + public String normalizeString( String input ) { + if( input != null && (input.length() == 2 || input.length() >= 4) ) { + input = input.substring( 1, input.length() - 1 ); + input = input.replaceAll( "\'", "'" ); + input = input.replaceAll( "\"", "\\\"" ); + input = "\"" + input + "\""; + } + return input; + } +} + +///////////////// +// KEYWORDS +///////////////// + +// These keywords are already declared in JavaLexer. They should not be overriden with different names, or else Vocabulary's literalName will be null. +// So no need to declare by DRLLexer +// PACKAGE : 'package'; +// IMPORT : 'import'; +// STATIC : 'static'; +// EXTENDS : 'extends'; +// SUPER : 'super'; + +// DRL keywords +DRL_UNIT : 'unit'; +DRL_FUNCTION : 'function'; +DRL_GLOBAL : 'global'; +DRL_DECLARE : 'declare'; +DRL_RULE : 'rule'; +DRL_QUERY : 'query'; +DRL_WHEN : 'when'; +DRL_THEN : 'then' -> pushMode(RHS); +DRL_END : 'end'; + +DRL_AND : 'and'; +DRL_OR : 'or'; + +DRL_EXISTS : 'exists'; +DRL_NOT : 'not'; +DRL_IN : 'in'; +DRL_FROM : 'from'; +DRL_COLLECT : 'collect'; +DRL_MATCHES : 'matches'; +DRL_MEMBEROF : 'memberOf'; +DRL_ACCUMULATE : 'accumulate'; +DRL_ACC : 'acc'; +DRL_INIT : 'init'; +DRL_ACTION : 'action'; +DRL_REVERSE : 'reverse'; +DRL_RESULT : 'result'; +DRL_ENTRY_POINT : 'entry-point'; +DRL_EVAL : 'eval'; +DRL_FORALL : 'forall'; +DRL_OVER : 'over'; + +// temporal operators +DRL_AFTER : 'after'; +DRL_BEFORE : 'before'; +DRL_COINCIDES : 'coincides'; +DRL_DURING : 'during'; +DRL_INCLUDES : 'includes'; +DRL_FINISHES : 'finishes'; +DRL_FINISHED_BY : 'finishedby'; +DRL_MEETS : 'meets'; +DRL_MET_BY : 'metby'; +DRL_OVERLAPS : 'overlaps'; +DRL_OVERLAPPED_BY : 'overlappedby'; +DRL_STARTS : 'starts'; +DRL_STARTED_BY : 'startedby'; + +DRL_WINDOW : 'window'; + +// attributes +DRL_SALIENCE : 'salience'; +DRL_ENABLED : 'enabled'; +DRL_NO_LOOP : 'no-loop'; +DRL_AUTO_FOCUS : 'auto-focus'; +DRL_LOCK_ON_ACTIVE : 'lock-on-active'; +DRL_REFRACT : 'refract'; +DRL_DIRECT : 'direct'; +DRL_AGENDA_GROUP : 'agenda-group'; +DRL_ACTIVATION_GROUP : 'activation-group'; +DRL_RULEFLOW_GROUP : 'ruleflow-group'; +DRL_DATE_EFFECTIVE : 'date-effective'; +DRL_DATE_EXPIRES : 'date-expires'; +DRL_DIALECT : 'dialect'; +DRL_CALENDARS : 'calendars'; +DRL_TIMER : 'timer'; +DRL_DURATION : 'duration'; + +///////////////// +// LEXER +///////////////// + +TIME_INTERVAL + : (('0'..'9')+ 'd') (('0'..'9')+ 'h')?(('0'..'9')+ 'm')?(('0'..'9')+ 's')?(('0'..'9')+ 'ms'?)? + | (('0'..'9')+ 'h') (('0'..'9')+ 'm')?(('0'..'9')+ 's')?(('0'..'9')+ 'ms'?)? + | (('0'..'9')+ 'm') (('0'..'9')+ 's')?(('0'..'9')+ 'ms'?)? + | (('0'..'9')+ 's') (('0'..'9')+ 'ms'?)? + | (('0'..'9')+ 'ms') + ; + +DRL_STRING_LITERAL + : ('"' ( DrlEscapeSequence | ~('\\'|'"') )* '"') + | ('\'' ( DrlEscapeSequence | ~('\\'|'\'') )* '\'') { setText( normalizeString( getText() ) ); } + ; + +DRL_BIG_DECIMAL_LITERAL + : ('0'..'9')+ [B] + | ('0'..'9')+ '.' ('0'..'9')+ [B] + ; + +DRL_BIG_INTEGER_LITERAL + : ('0'..'9')+ [I] + ; + +///////////////// +// SYMBOLS +///////////////// + +HASH : '#'; +DRL_UNIFY : ':=' ; +NULL_SAFE_DOT : '!.' ; +QUESTION_DIV : '?/' ; + +MISC : '\'' | '\\' | '$' ; + +///////////////// +// Fragment +///////////////// +fragment +DrlEscapeSequence + : '\\' ('b'|'B'|'t'|'n'|'f'|'r'|'"'|'\''|'\\'|'.'|'o'| + 'x'|'a'|'e'|'c'|'d'|'D'|'s'|'S'|'w'|'W'|'p'|'A'| + 'G'|'Z'|'z'|'Q'|'E'|'*'|'['|']'|'('|')'|'$'|'^'| + '{'|'}'|'?'|'+'|'-'|'&'|'|') + | DrlUnicodeEscape + | DrlOctalEscape + ; + +fragment +DrlOctalEscape + : '\\' ('0'..'3') ('0'..'7') ('0'..'7') + | '\\' ('0'..'7') ('0'..'7') + | '\\' ('0'..'7') + ; + +fragment +DrlUnicodeEscape + : '\\' 'u' HexDigit HexDigit HexDigit HexDigit + ; + +mode RHS; +RHS_WS : [ \t\r\n\u000C]+ -> channel(HIDDEN); +DRL_RHS_END : 'end' [ \t]* SEMI? [ \t]* ('\n' | '\r\n' | EOF) {setText("end");} -> popMode; +RHS_CHUNK : ~[ \t\r\n\u000C]+ ; diff --git a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRLParser.g4 b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRLParser.g4 new file mode 100644 index 00000000000..933c013ee6c --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/DRLParser.g4 @@ -0,0 +1,495 @@ +parser grammar DRLParser; + +options { tokenVocab=DRLLexer; } + +import JavaParser; + + /* + * statement := importStatement + * | globalStatement + * | declare + * | rule + * | ruleAttribute + * | function + * | query + * ; + */ +compilationUnit : packagedef? unitdef? drlStatementdef* ; + +drlStatementdef + : importdef + | globaldef + | declaredef + | ruledef + | attributes + | functiondef + | querydef + ; + +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 + ; + +globaldef : DRL_GLOBAL type drlIdentifier SEMI? ; + +/** + * declare := DECLARE + * | (ENTRY-POINT) => entryPointDeclaration + * | (WINDOW) => windowDeclaration + * | (TRAIT) => typeDeclaration (trait) + * | (ENUM) => enumDeclaration + * | typeDeclaration (class) + * END + */ + +declaredef : DRL_DECLARE ( + | entryPointDeclaration + | windowDeclaration + | typeDeclaration + ) DRL_END ; + +/* + * typeDeclaration := [TYPE] qualifiedIdentifier (EXTENDS qualifiedIdentifier)? + * annotation* + * field* + * END + */ + +typeDeclaration : name=drlQualifiedName (EXTENDS superType=drlQualifiedName)? drlAnnotation* field* ; + +// entryPointDeclaration := ENTRY-POINT stringId annotation* END + +entryPointDeclaration : DRL_ENTRY_POINT name=stringId drlAnnotation* ; + +// windowDeclaration := WINDOW ID annotation* lhsPatternBind END + +windowDeclaration : DRL_WINDOW name=stringId drlAnnotation* lhsPatternBind ; + +// field := label fieldType (EQUALS_ASSIGN conditionalExpression)? annotation* SEMICOLON? + +field : label type (ASSIGN initExpr=conditionalOrExpression)? drlAnnotation* SEMI? ; + +// 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 ; + +// query := QUERY stringId parameters? annotation* lhsExpression END + +querydef : DRL_QUERY name=stringId formalParameters? drlAnnotation* lhsExpression+ DRL_END SEMI?; + +lhs : DRL_WHEN lhsExpression* ; + +lhsExpression : LPAREN lhsExpression RPAREN #lhsExpressionEnclosed + | lhsUnary #lhsUnarySingle + | LPAREN DRL_AND lhsExpression+ RPAREN #lhsAnd + | lhsExpression (DRL_AND lhsExpression)+ #lhsAnd + | LPAREN DRL_OR lhsExpression+ RPAREN #lhsOr + | lhsExpression (DRL_OR lhsExpression)+ #lhsOr + ; + +// lhsAnd is used as a label in lhsExpression rule. But some other rules explicitly use the def, so lhsAndDef is declared. +lhsAndDef : LPAREN lhsAndDef RPAREN + | lhsUnary (DRL_AND lhsUnary)* + | LPAREN DRL_AND lhsUnary+ RPAREN + ; + +/* +lhsUnary : ( lhsExists namedConsequence? + | lhsNot namedConsequence? + | lhsEval consequenceInvocation* + | lhsForall + | lhsAccumulate + | LPAREN lhsOr RPAREN namedConsequence? + | lhsPatternBind consequenceInvocation* + ) SEMI? ; +*/ + +lhsUnary : ( + lhsExists + | lhsNot + | lhsEval + | lhsForall + | lhsAccumulate + | lhsPatternBind + ) SEMI? ; + +lhsPatternBind : (label|unif)? ( LPAREN lhsPattern (DRL_OR lhsPattern)* RPAREN | lhsPattern ) ; + +/* +lhsPattern : xpathPrimary (OVER patternFilter)? | + ( QUESTION? qualifiedIdentifier LPAREN positionalConstraints? constraints? RPAREN (OVER patternFilter)? (FROM patternSource)? ) ; +*/ + +lhsPattern : QUESTION? objectType=drlQualifiedName LPAREN positionalConstraints? constraints? RPAREN (DRL_OVER patternFilter)? (DRL_FROM patternSource)? ; +positionalConstraints : constraint (COMMA constraint)* SEMI ; +constraints : constraint (COMMA constraint)* ; +constraint : ( nestedConstraint | conditionalOrExpression ) ; +nestedConstraint : ( IDENTIFIER ( DOT | HASH ) )* IDENTIFIER DOT LPAREN constraints RPAREN ; +conditionalOrExpression : left=conditionalAndExpression (OR right=conditionalAndExpression)* ; +conditionalAndExpression : left=inclusiveOrExpression (AND right=inclusiveOrExpression)* ; +inclusiveOrExpression : left=exclusiveOrExpression (BITOR right=exclusiveOrExpression)* ; +exclusiveOrExpression : left=andExpression (CARET right=andExpression)* ; +andExpression : left=equalityExpression (BITAND right=equalityExpression)* ; +equalityExpression : label? left=instanceOfExpression ( ( op=EQUAL | op=NOTEQUAL ) right=instanceOfExpression )* ; +instanceOfExpression : left=inExpression ( 'instanceof' right=type )? ; +inExpression : left=relationalExpression ( 'not'? 'in' LPAREN drlExpression (COMMA drlExpression)* RPAREN )? ; +relationalExpression : left=drlExpression (right=orRestriction)* ; +orRestriction : left=andRestriction (OR right=andRestriction)* ; +andRestriction : left=singleRestriction (AND right=singleRestriction)* ; +singleRestriction : op=relationalOperator drlExpression ; + +relationalOperator + : EQUAL + | NOTEQUAL + | LE + | GE + | GT + | LT + | temporalOperator + ; + +/* function := FUNCTION type? ID parameters(typed) chunk_{_} */ +functiondef : DRL_FUNCTION typeTypeOrVoid? IDENTIFIER formalParameters block ; + + +/* extending JavaParser qualifiedName */ +drlQualifiedName + : drlIdentifier (DOT drlIdentifier)* + ; + +/* extending JavaParser identifier */ +drlIdentifier + : drlKeywords + | IDENTIFIER + | MODULE + | OPEN + | REQUIRES + | EXPORTS + | OPENS + | TO + | USES + | PROVIDES + | WITH + | TRANSITIVE + | YIELD + | SEALED + | PERMITS + | RECORD + | VAR + ; + +drlKeywords + : DRL_UNIT + | DRL_FUNCTION + | DRL_GLOBAL + | DRL_DECLARE + | DRL_RULE + | DRL_QUERY + | DRL_WHEN + | DRL_THEN + | DRL_END + | DRL_AND + | DRL_OR + | DRL_EXISTS + | DRL_NOT + | DRL_IN + | DRL_FROM + | DRL_MATCHES + | DRL_MEMBEROF + | DRL_ACCUMULATE + | DRL_ACC + | DRL_INIT + | DRL_ACTION + | DRL_REVERSE + | DRL_RESULT + | DRL_ENTRY_POINT + | DRL_EVAL + | DRL_SALIENCE + | DRL_ENABLED + | DRL_NO_LOOP + | DRL_AUTO_FOCUS + | DRL_LOCK_ON_ACTIVE + | DRL_REFRACT + | DRL_DIRECT + | DRL_AGENDA_GROUP + | DRL_ACTIVATION_GROUP + | DRL_RULEFLOW_GROUP + | DRL_DATE_EFFECTIVE + | DRL_DATE_EXPIRES + | DRL_DIALECT + | DRL_CALENDARS + | DRL_TIMER + | DRL_DURATION + ; + +/* extending JavaParser expression */ +drlExpression + : drlPrimary + | drlExpression bop=DOT + ( + drlIdentifier + | methodCall + | THIS + | NEW nonWildcardTypeArguments? innerCreator + | SUPER superSuffix + | explicitGenericInvocation + ) + | drlExpression LBRACK drlExpression RBRACK + | DRL_EVAL LPAREN conditionalOrExpression RPAREN + | methodCall + | NEW drlCreator + | LPAREN annotation* typeType (BITAND typeType)* RPAREN drlExpression + | drlExpression postfix=(INC | DEC) + | prefix=(ADD|SUB|INC|DEC) drlExpression + | prefix=(TILDE|BANG) drlExpression + | drlExpression bop=(MUL|DIV|MOD) drlExpression + | drlExpression bop=(ADD|SUB) drlExpression + | drlExpression (LT LT | GT GT GT | GT GT) drlExpression + | drlExpression bop=(LE | GE | GT | LT) drlExpression + | drlExpression temporalOperator drlExpression + | drlExpression bop=INSTANCEOF (typeType | pattern) + | drlExpression bop=DRL_MATCHES drlExpression + | drlExpression DRL_NOT? DRL_MEMBEROF drlExpression + | drlExpression bop=DRL_UNIFY drlExpression + | drlExpression bop=(EQUAL | NOTEQUAL) drlExpression + | drlExpression bop=BITAND drlExpression + | drlExpression bop=CARET drlExpression + | drlExpression bop=BITOR drlExpression + | drlExpression bop=AND drlExpression + | drlExpression bop=OR drlExpression + | drlExpression bop=QUESTION drlExpression COLON drlExpression + | drlExpression + bop=(ASSIGN | ADD_ASSIGN | SUB_ASSIGN | MUL_ASSIGN | DIV_ASSIGN | AND_ASSIGN | OR_ASSIGN | XOR_ASSIGN | RSHIFT_ASSIGN | URSHIFT_ASSIGN | LSHIFT_ASSIGN | MOD_ASSIGN) + drlExpression + | lambdaExpression // Java8 + | switchExpression // Java17 + + // Java 8 methodReference + | drlExpression COLONCOLON typeArguments? drlIdentifier + | typeType COLONCOLON (typeArguments? drlIdentifier | NEW) + | classType COLONCOLON typeArguments? NEW + ; + +temporalOperator : DRL_NOT? bop=(DRL_AFTER | DRL_BEFORE | DRL_COINCIDES | DRL_DURING | DRL_INCLUDES | DRL_FINISHES | DRL_FINISHED_BY | DRL_MEETS | DRL_MET_BY | DRL_OVERLAPS | DRL_OVERLAPPED_BY | DRL_STARTS | DRL_STARTED_BY) timeAmount? ; + +timeAmount : LBRACK (TIME_INTERVAL | DECIMAL_LITERAL | MUL | SUB MUL) (COMMA (TIME_INTERVAL | DECIMAL_LITERAL | MUL | SUB MUL))* RBRACK ; + +/* extending JavaParser primary */ +drlPrimary + : LPAREN drlExpression RPAREN + | THIS + | SUPER + | drlLiteral + | drlIdentifier + | typeTypeOrVoid DOT CLASS + | nonWildcardTypeArguments (explicitGenericInvocationSuffix | THIS arguments) + | inlineListExpression + | inlineMapExpression + ; + +/* extending JavaParser literal */ +drlLiteral + : integerLiteral + | floatLiteral + | DRL_BIG_DECIMAL_LITERAL + | DRL_BIG_INTEGER_LITERAL + | CHAR_LITERAL + | DRL_STRING_LITERAL + | BOOL_LITERAL + | NULL_LITERAL + | TEXT_BLOCK // Java17 + ; + +inlineListExpression + : LBRACK expressionList? RBRACK + ; + +expressionList + : drlExpression (COMMA drlExpression)* + ; + +inlineMapExpression + : LBRACK mapExpressionList RBRACK + ; + +mapExpressionList + : mapEntry (COMMA mapEntry)* + ; + +mapEntry + : drlExpression COLON drlExpression + ; + +/* + patternFilter := OVER filterDef + filterDef := label ID LEFT_PAREN parameters RIGHT_PAREN +*/ +patternFilter : DRL_WINDOW COLON IDENTIFIER LPAREN expressionList RPAREN ; + +/* + patternSource := FROM + ( fromAccumulate + | fromCollect + | fromEntryPoint + | fromWindow + | fromExpression ) +*/ +patternSource : fromAccumulate + | fromCollect + | fromEntryPoint + | fromExpression + ; + +fromExpression : conditionalOrExpression ; + + +/* +fromAccumulate := ACCUMULATE LEFT_PAREN lhsAnd (COMMA|SEMICOLON) + ( INIT chunk_(_) COMMA ACTION chunk_(_) COMMA + ( REVERSE chunk_(_) COMMA)? RESULT chunk_(_) + | accumulateFunction + ) RIGHT_PAREN +*/ +fromAccumulate : (DRL_ACCUMULATE|DRL_ACC) LPAREN lhsAndDef (COMMA|SEMI) + ( DRL_INIT LPAREN initBlockStatements=blockStatements RPAREN COMMA DRL_ACTION LPAREN actionBlockStatements=blockStatements RPAREN COMMA ( DRL_REVERSE LPAREN reverseBlockStatements=blockStatements RPAREN COMMA)? DRL_RESULT LPAREN expression RPAREN + | accumulateFunction + ) + RPAREN (SEMI)? + ; + +blockStatements : blockStatement* ; + +/* +accumulateFunction := label? ID parameters +*/ +accumulateFunction : label? IDENTIFIER LPAREN drlExpression RPAREN; + +// fromCollect := COLLECT LEFT_PAREN lhsPatternBind RIGHT_PAREN + +fromCollect : DRL_COLLECT LPAREN lhsPatternBind RPAREN ; + +fromEntryPoint : DRL_ENTRY_POINT stringId ; + +/* + lhsExists := EXISTS + ( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or + | LEFT_PAREN lhsOr RIGHT_PAREN + | lhsPatternBind + ) +*/ +// Use lhsExpression instead of lhsOr because lhsExpression has good enough structure +lhsExists : DRL_EXISTS + ( LPAREN lhsExpression RPAREN + | lhsPatternBind + ) + ; + +/* + lhsNot := NOT + ( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or + | LEFT_PAREN lhsOr RIGHT_PAREN + | lhsPatternBind + ) +*/ +// Use lhsExpression instead of lhsOr because lhsExpression has good enough structure +lhsNot : DRL_NOT + ( LPAREN lhsExpression RPAREN + | lhsPatternBind + ) + ; + +/** + * lhsEval := EVAL LEFT_PAREN conditionalExpression RIGHT_PAREN + */ +lhsEval : DRL_EVAL LPAREN conditionalOrExpression RPAREN ; + +/** + * lhsForall := FORALL LEFT_PAREN lhsPatternBind+ RIGHT_PAREN + */ + +lhsForall : DRL_FORALL LPAREN lhsPatternBind+ RPAREN ; + +/** + * lhsAccumulate := (ACCUMULATE|ACC) LEFT_PAREN lhsAnd (COMMA|SEMICOLON) + * accumulateFunctionBinding (COMMA accumulateFunctionBinding)* + * (SEMICOLON constraints)? + * RIGHT_PAREN SEMICOLON? + */ + +lhsAccumulate : (DRL_ACCUMULATE|DRL_ACC) LPAREN lhsAndDef (COMMA|SEMI) + accumulateFunction (COMMA accumulateFunction)* + (SEMI constraints)? + RPAREN (SEMI)? + ; + +rhs : DRL_THEN consequence ; + +consequence : RHS_CHUNK* ; + +stringId : ( IDENTIFIER | DRL_STRING_LITERAL ) ; + +type : (classOrInterfaceType | primitiveType) typeArguments? ( DOT IDENTIFIER typeArguments? )* (LBRACK RBRACK)* ; + +//typeArguments : LT typeArgument (COMMA typeArgument)* GT ; +//typeArgument : QUESTION (( EXTENDS | SUPER ) type )? | type ; + +drlArguments : LPAREN drlArgument (COMMA drlArgument)* RPAREN ; +drlArgument : ( stringId | floatLiteral | BOOL_LITERAL | NULL_LITERAL ) ; + +drlAnnotation : AT name=drlQualifiedName (LPAREN ( drlElementValuePairs | drlElementValue )? RPAREN)? ; + +drlElementValuePairs : drlElementValuePair (COMMA drlElementValuePair)* ; +drlElementValuePair : key=drlIdentifier ASSIGN value=drlElementValue ; + +drlElementValue + : drlExpression + | drlArrayInitializer + ; + +attributes : attribute ( COMMA? attribute )* ; +attribute : name=( 'salience' | 'enabled' ) conditionalOrExpression #expressionAttribute + | name=( 'no-loop' | 'auto-focus' | 'lock-on-active' | 'refract' | 'direct' ) BOOL_LITERAL? #booleanAttribute + | name=( 'agenda-group' | 'activation-group' | 'ruleflow-group' | 'date-effective' | 'date-expires' | 'dialect' ) DRL_STRING_LITERAL #stringAttribute + | name='calendars' DRL_STRING_LITERAL ( COMMA DRL_STRING_LITERAL )* #stringListAttribute + | name='timer' ( DECIMAL_LITERAL | chunk ) #intOrChunkAttribute + | name='duration' ( DECIMAL_LITERAL | TIME_INTERVAL | LPAREN TIME_INTERVAL RPAREN ) #durationAttribute + ; + +chunk : LPAREN .+? RPAREN; + +assignmentOperator : ASSIGN + | ADD_ASSIGN + | SUB_ASSIGN + | MUL_ASSIGN + | DIV_ASSIGN + | AND_ASSIGN + | OR_ASSIGN + | XOR_ASSIGN + | MOD_ASSIGN + | LT LT ASSIGN ; + +label : IDENTIFIER COLON ; +unif : IDENTIFIER DRL_UNIFY ; + +/* extending JavaParser variableInitializer */ +drlVariableInitializer + : arrayInitializer + | drlExpression + ; + + drlCreator + : nonWildcardTypeArguments createdName classCreatorRest + | createdName (drlArrayCreatorRest | classCreatorRest) + ; + + drlArrayCreatorRest + : LBRACK (RBRACK (LBRACK RBRACK)* drlArrayInitializer | expression RBRACK (LBRACK expression RBRACK)* (LBRACK RBRACK)*) + ; + + drlArrayInitializer + : LBRACE (drlVariableInitializer (COMMA drlVariableInitializer)* (COMMA)? )? RBRACE + ; diff --git a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/JavaLexer.g4 b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/JavaLexer.g4 new file mode 100644 index 00000000000..1af884db096 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/JavaLexer.g4 @@ -0,0 +1,245 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr, Sam Harwell + Copyright (c) 2017 Ivan Kochurkin (upgrade to Java 8) + Copyright (c) 2021 Michał Lorek (upgrade to Java 11) + Copyright (c) 2022 Michał Lorek (upgrade to Java 17) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// source: https://github.com/antlr/grammars-v4/tree/master/java/java + +lexer grammar JavaLexer; + +// Keywords + +ABSTRACT: 'abstract'; +ASSERT: 'assert'; +BOOLEAN: 'boolean'; +BREAK: 'break'; +BYTE: 'byte'; +CASE: 'case'; +CATCH: 'catch'; +CHAR: 'char'; +CLASS: 'class'; +CONST: 'const'; +CONTINUE: 'continue'; +DEFAULT: 'default'; +DO: 'do'; +DOUBLE: 'double'; +ELSE: 'else'; +ENUM: 'enum'; +EXTENDS: 'extends'; +FINAL: 'final'; +FINALLY: 'finally'; +FLOAT: 'float'; +FOR: 'for'; +IF: 'if'; +GOTO: 'goto'; +IMPLEMENTS: 'implements'; +IMPORT: 'import'; +INSTANCEOF: 'instanceof'; +INT: 'int'; +INTERFACE: 'interface'; +LONG: 'long'; +NATIVE: 'native'; +NEW: 'new'; +PACKAGE: 'package'; +PRIVATE: 'private'; +PROTECTED: 'protected'; +PUBLIC: 'public'; +RETURN: 'return'; +SHORT: 'short'; +STATIC: 'static'; +STRICTFP: 'strictfp'; +SUPER: 'super'; +SWITCH: 'switch'; +SYNCHRONIZED: 'synchronized'; +THIS: 'this'; +THROW: 'throw'; +THROWS: 'throws'; +TRANSIENT: 'transient'; +TRY: 'try'; +VOID: 'void'; +VOLATILE: 'volatile'; +WHILE: 'while'; + +// Module related keywords +MODULE: 'module'; +OPEN: 'open'; +REQUIRES: 'requires'; +EXPORTS: 'exports'; +OPENS: 'opens'; +TO: 'to'; +USES: 'uses'; +PROVIDES: 'provides'; +WITH: 'with'; +TRANSITIVE: 'transitive'; + +// Local Variable Type Inference +VAR: 'var'; // reserved type name + +// Switch Expressions +YIELD: 'yield'; + +// Records +RECORD: 'record'; + +// Sealed Classes +SEALED: 'sealed'; +PERMITS: 'permits'; +NON_SEALED: 'non-sealed'; + +// Literals + +DECIMAL_LITERAL: ('0' | [1-9] (Digits? | '_'+ Digits)) [lL]?; +HEX_LITERAL: '0' [xX] [0-9a-fA-F] ([0-9a-fA-F_]* [0-9a-fA-F])? [lL]?; +OCT_LITERAL: '0' '_'* [0-7] ([0-7_]* [0-7])? [lL]?; +BINARY_LITERAL: '0' [bB] [01] ([01_]* [01])? [lL]?; + +FLOAT_LITERAL: (Digits '.' Digits? | '.' Digits) ExponentPart? [fFdD]? + | Digits (ExponentPart [fFdD]? | [fFdD]) + ; + +HEX_FLOAT_LITERAL: '0' [xX] (HexDigits '.'? | HexDigits? '.' HexDigits) [pP] [+-]? Digits [fFdD]?; + +BOOL_LITERAL: 'true' + | 'false' + ; + +CHAR_LITERAL: '\'' (~['\\\r\n] | EscapeSequence) '\''; + +STRING_LITERAL: '"' (~["\\\r\n] | EscapeSequence)* '"'; + +TEXT_BLOCK: '"""' [ \t]* [\r\n] (. | EscapeSequence)*? '"""'; + +NULL_LITERAL: 'null'; + +// Separators + +LPAREN: '('; +RPAREN: ')'; +LBRACE: '{'; +RBRACE: '}'; +LBRACK: '['; +RBRACK: ']'; +SEMI: ';'; +COMMA: ','; +DOT: '.'; + +// Operators + +ASSIGN: '='; +GT: '>'; +LT: '<'; +BANG: '!'; +TILDE: '~'; +QUESTION: '?'; +COLON: ':'; +EQUAL: '=='; +LE: '<='; +GE: '>='; +NOTEQUAL: '!='; +AND: '&&'; +OR: '||'; +INC: '++'; +DEC: '--'; +ADD: '+'; +SUB: '-'; +MUL: '*'; +DIV: '/'; +BITAND: '&'; +BITOR: '|'; +CARET: '^'; +MOD: '%'; + +ADD_ASSIGN: '+='; +SUB_ASSIGN: '-='; +MUL_ASSIGN: '*='; +DIV_ASSIGN: '/='; +AND_ASSIGN: '&='; +OR_ASSIGN: '|='; +XOR_ASSIGN: '^='; +MOD_ASSIGN: '%='; +LSHIFT_ASSIGN: '<<='; +RSHIFT_ASSIGN: '>>='; +URSHIFT_ASSIGN: '>>>='; + +// Java 8 tokens + +ARROW: '->'; +COLONCOLON: '::'; + +// Additional symbols not defined in the lexical specification + +AT: '@'; +ELLIPSIS: '...'; + +// Whitespace and comments + +WS: [ \t\r\n\u000C]+ -> channel(HIDDEN); +COMMENT: '/*' .*? '*/' -> channel(HIDDEN); +LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN); + +// Identifiers + +IDENTIFIER: Letter LetterOrDigit*; + +// Fragment rules + +fragment ExponentPart + : [eE] [+-]? Digits + ; + +fragment EscapeSequence + : '\\' [btnfr"'\\] + | '\\' ([0-3]? [0-7])? [0-7] + | '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit + ; + +fragment HexDigits + : HexDigit ((HexDigit | '_')* HexDigit)? + ; + +fragment HexDigit + : [0-9a-fA-F] + ; + +fragment Digits + : [0-9] ([0-9_]* [0-9])? + ; + +fragment LetterOrDigit + : Letter + | [0-9] + ; + +fragment Letter + : [a-zA-Z$_] // these are the "java letters" below 0x7F + | ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate + | [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF + ; + +TEXT : .+? ; diff --git a/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/JavaParser.g4 b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/JavaParser.g4 new file mode 100644 index 00000000000..9e9f0a68513 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/antlr4/org/drools/drl10/parser/JavaParser.g4 @@ -0,0 +1,752 @@ +/* + [The "BSD licence"] + Copyright (c) 2013 Terence Parr, Sam Harwell + Copyright (c) 2017 Ivan Kochurkin (upgrade to Java 8) + Copyright (c) 2021 Michał Lorek (upgrade to Java 11) + Copyright (c) 2022 Michał Lorek (upgrade to Java 17) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// source: https://github.com/antlr/grammars-v4/tree/master/java/java + +parser grammar JavaParser; + +options { tokenVocab=JavaLexer; } + +compilationUnit + : packageDeclaration? importDeclaration* typeDeclaration* + | moduleDeclaration EOF + ; + +packageDeclaration + : annotation* PACKAGE qualifiedName SEMI + ; + +importDeclaration + : IMPORT STATIC? qualifiedName (DOT MUL)? SEMI + ; + +typeDeclaration + : classOrInterfaceModifier* + (classDeclaration | enumDeclaration | interfaceDeclaration | annotationTypeDeclaration | recordDeclaration) + | SEMI + ; + +modifier + : classOrInterfaceModifier + | NATIVE + | SYNCHRONIZED + | TRANSIENT + | VOLATILE + ; + +classOrInterfaceModifier + : annotation + | PUBLIC + | PROTECTED + | PRIVATE + | STATIC + | ABSTRACT + | FINAL // FINAL for class only -- does not apply to interfaces + | STRICTFP + | SEALED // Java17 + | NON_SEALED // Java17 + ; + +variableModifier + : FINAL + | annotation + ; + +classDeclaration + : CLASS identifier typeParameters? + (EXTENDS typeType)? + (IMPLEMENTS typeList)? + (PERMITS typeList)? // Java17 + classBody + ; + +typeParameters + : LT typeParameter (COMMA typeParameter)* GT + ; + +typeParameter + : annotation* identifier (EXTENDS annotation* typeBound)? + ; + +typeBound + : typeType (BITAND typeType)* + ; + +enumDeclaration + : ENUM identifier (IMPLEMENTS typeList)? LBRACE enumConstants? COMMA? enumBodyDeclarations? RBRACE + ; + +enumConstants + : enumConstant (COMMA enumConstant)* + ; + +enumConstant + : annotation* identifier arguments? classBody? + ; + +enumBodyDeclarations + : SEMI classBodyDeclaration* + ; + +interfaceDeclaration + : INTERFACE identifier typeParameters? (EXTENDS typeList)? (PERMITS typeList)? interfaceBody + ; + +classBody + : LBRACE classBodyDeclaration* RBRACE + ; + +interfaceBody + : LBRACE interfaceBodyDeclaration* RBRACE + ; + +classBodyDeclaration + : SEMI + | STATIC? block + | modifier* memberDeclaration + ; + +memberDeclaration + : methodDeclaration + | genericMethodDeclaration + | fieldDeclaration + | constructorDeclaration + | genericConstructorDeclaration + | interfaceDeclaration + | annotationTypeDeclaration + | classDeclaration + | enumDeclaration + | recordDeclaration //Java17 + ; + +/* We use rule this even for void methods which cannot have [] after parameters. + This simplifies grammar and we can consider void to be a type, which + renders the [] matching as a context-sensitive issue or a semantic check + for invalid return type after parsing. + */ +methodDeclaration + : typeTypeOrVoid identifier formalParameters (LBRACK RBRACK)* + (THROWS qualifiedNameList)? + methodBody + ; + +methodBody + : block + | SEMI + ; + +typeTypeOrVoid + : typeType + | VOID + ; + +genericMethodDeclaration + : typeParameters methodDeclaration + ; + +genericConstructorDeclaration + : typeParameters constructorDeclaration + ; + +constructorDeclaration + : identifier formalParameters (THROWS qualifiedNameList)? constructorBody=block + ; + +fieldDeclaration + : typeType variableDeclarators SEMI + ; + +interfaceBodyDeclaration + : modifier* interfaceMemberDeclaration + | SEMI + ; + +interfaceMemberDeclaration + : constDeclaration + | interfaceMethodDeclaration + | genericInterfaceMethodDeclaration + | interfaceDeclaration + | annotationTypeDeclaration + | classDeclaration + | enumDeclaration + | recordDeclaration // Java17 + ; + +constDeclaration + : typeType constantDeclarator (COMMA constantDeclarator)* SEMI + ; + +constantDeclarator + : identifier (LBRACK RBRACK)* ASSIGN variableInitializer + ; + +// Early versions of Java allows brackets after the method name, eg. +// public int[] return2DArray() [] { ... } +// is the same as +// public int[][] return2DArray() { ... } +interfaceMethodDeclaration + : interfaceMethodModifier* interfaceCommonBodyDeclaration + ; + +// Java8 +interfaceMethodModifier + : annotation + | PUBLIC + | ABSTRACT + | DEFAULT + | STATIC + | STRICTFP + ; + +genericInterfaceMethodDeclaration + : interfaceMethodModifier* typeParameters interfaceCommonBodyDeclaration + ; + +interfaceCommonBodyDeclaration + : annotation* typeTypeOrVoid identifier formalParameters (LBRACK RBRACK)* (THROWS qualifiedNameList)? methodBody + ; + +variableDeclarators + : variableDeclarator (COMMA variableDeclarator)* + ; + +variableDeclarator + : variableDeclaratorId (ASSIGN variableInitializer)? + ; + +variableDeclaratorId + : identifier (LBRACK RBRACK)* + ; + +variableInitializer + : arrayInitializer + | expression + ; + +arrayInitializer + : LBRACE (variableInitializer (COMMA variableInitializer)* (COMMA)? )? RBRACE + ; + +classOrInterfaceType + : identifier typeArguments? (DOT identifier typeArguments?)* + ; + +typeArgument + : typeType + | annotation* QUESTION ((EXTENDS | SUPER) typeType)? + ; + +qualifiedNameList + : qualifiedName (COMMA qualifiedName)* + ; + +formalParameters + : LPAREN ( receiverParameter? + | receiverParameter (COMMA formalParameterList)? + | formalParameterList? + ) RPAREN + ; + +receiverParameter + : typeType (identifier DOT)* THIS + ; + +formalParameterList + : formalParameter (COMMA formalParameter)* (COMMA lastFormalParameter)? + | lastFormalParameter + ; + +formalParameter + : variableModifier* typeType variableDeclaratorId + ; + +lastFormalParameter + : variableModifier* typeType annotation* ELLIPSIS variableDeclaratorId + ; + +// local variable type inference +lambdaLVTIList + : lambdaLVTIParameter (COMMA lambdaLVTIParameter)* + ; + +lambdaLVTIParameter + : variableModifier* VAR identifier + ; + +qualifiedName + : identifier (DOT identifier)* + ; + +literal + : integerLiteral + | floatLiteral + | CHAR_LITERAL + | STRING_LITERAL + | BOOL_LITERAL + | NULL_LITERAL + | TEXT_BLOCK // Java17 + ; + +integerLiteral + : DECIMAL_LITERAL + | HEX_LITERAL + | OCT_LITERAL + | BINARY_LITERAL + ; + +floatLiteral + : FLOAT_LITERAL + | HEX_FLOAT_LITERAL + ; + +// ANNOTATIONS +altAnnotationQualifiedName + : (identifier DOT)* AT identifier + ; + +annotation + : (AT qualifiedName | altAnnotationQualifiedName) (LPAREN ( elementValuePairs | elementValue )? RPAREN)? + ; + +elementValuePairs + : elementValuePair (COMMA elementValuePair)* + ; + +elementValuePair + : identifier ASSIGN elementValue + ; + +elementValue + : expression + | annotation + | elementValueArrayInitializer + ; + +elementValueArrayInitializer + : LBRACE (elementValue (COMMA elementValue)*)? (COMMA)? RBRACE + ; + +annotationTypeDeclaration + : AT INTERFACE identifier annotationTypeBody + ; + +annotationTypeBody + : LBRACE (annotationTypeElementDeclaration)* RBRACE + ; + +annotationTypeElementDeclaration + : modifier* annotationTypeElementRest + | SEMI // this is not allowed by the grammar, but apparently allowed by the actual compiler + ; + +annotationTypeElementRest + : typeType annotationMethodOrConstantRest SEMI + | classDeclaration SEMI? + | interfaceDeclaration SEMI? + | enumDeclaration SEMI? + | annotationTypeDeclaration SEMI? + | recordDeclaration SEMI? // Java17 + ; + +annotationMethodOrConstantRest + : annotationMethodRest + | annotationConstantRest + ; + +annotationMethodRest + : identifier LPAREN RPAREN defaultValue? + ; + +annotationConstantRest + : variableDeclarators + ; + +defaultValue + : DEFAULT elementValue + ; + +// MODULES - Java9 + +moduleDeclaration + : OPEN? MODULE qualifiedName moduleBody + ; + +moduleBody + : LBRACE moduleDirective* RBRACE + ; + +moduleDirective + : REQUIRES requiresModifier* qualifiedName SEMI + | EXPORTS qualifiedName (TO qualifiedName)? SEMI + | OPENS qualifiedName (TO qualifiedName)? SEMI + | USES qualifiedName SEMI + | PROVIDES qualifiedName WITH qualifiedName SEMI + ; + +requiresModifier + : TRANSITIVE + | STATIC + ; + +// RECORDS - Java 17 + +recordDeclaration + : RECORD identifier typeParameters? recordHeader + (IMPLEMENTS typeList)? + recordBody + ; + +recordHeader + : LPAREN recordComponentList? RPAREN + ; + +recordComponentList + : recordComponent (COMMA recordComponent)* + ; + +recordComponent + : typeType identifier + ; + +recordBody + : LBRACE classBodyDeclaration* RBRACE + ; + +// STATEMENTS / BLOCKS + +block + : LBRACE blockStatement* RBRACE + ; + +blockStatement + : localVariableDeclaration SEMI + | statement + | localTypeDeclaration + ; + +localVariableDeclaration + : variableModifier* (typeType variableDeclarators | VAR identifier ASSIGN expression) + ; + +identifier + : IDENTIFIER + | MODULE + | OPEN + | REQUIRES + | EXPORTS + | OPENS + | TO + | USES + | PROVIDES + | WITH + | TRANSITIVE + | YIELD + | SEALED + | PERMITS + | RECORD + | VAR + ; + +localTypeDeclaration + : classOrInterfaceModifier* + (classDeclaration | interfaceDeclaration | recordDeclaration) + | SEMI + ; + +statement + : blockLabel=block + | ASSERT expression (COLON expression)? SEMI + | IF parExpression statement (ELSE statement)? + | FOR LPAREN forControl RPAREN statement + | WHILE parExpression statement + | DO statement WHILE parExpression SEMI + | TRY block (catchClause+ finallyBlock? | finallyBlock) + | TRY resourceSpecification block catchClause* finallyBlock? + | SWITCH parExpression LBRACE switchBlockStatementGroup* switchLabel* RBRACE + | SYNCHRONIZED parExpression block + | RETURN expression? SEMI + | THROW expression SEMI + | BREAK identifier? SEMI + | CONTINUE identifier? SEMI + | YIELD expression SEMI // Java17 + | SEMI + | statementExpression=expression SEMI + | switchExpression SEMI? // Java17 + | identifierLabel=identifier COLON statement + ; + +catchClause + : CATCH LPAREN variableModifier* catchType identifier RPAREN block + ; + +catchType + : qualifiedName (BITOR qualifiedName)* + ; + +finallyBlock + : FINALLY block + ; + +resourceSpecification + : LPAREN resources SEMI? RPAREN + ; + +resources + : resource (SEMI resource)* + ; + +resource + : variableModifier* ( classOrInterfaceType variableDeclaratorId | VAR identifier ) ASSIGN expression + | identifier + ; + +/** Matches cases then statements, both of which are mandatory. + * To handle empty cases at the end, we add switchLabel* to statement. + */ +switchBlockStatementGroup + : switchLabel+ blockStatement+ + ; + +switchLabel + : CASE (constantExpression=expression | enumConstantName=IDENTIFIER | typeType varName=identifier) COLON + | DEFAULT COLON + ; + +forControl + : enhancedForControl + | forInit? SEMI expression? SEMI forUpdate=expressionList? + ; + +forInit + : localVariableDeclaration + | expressionList + ; + +enhancedForControl + : variableModifier* (typeType | VAR) variableDeclaratorId COLON expression + ; + +// EXPRESSIONS + +parExpression + : LPAREN expression RPAREN + ; + +expressionList + : expression (COMMA expression)* + ; + +methodCall + : identifier LPAREN expressionList? RPAREN + | THIS LPAREN expressionList? RPAREN + | SUPER LPAREN expressionList? RPAREN + ; + +expression + : primary + | expression bop=DOT + ( + identifier + | methodCall + | THIS + | NEW nonWildcardTypeArguments? innerCreator + | SUPER superSuffix + | explicitGenericInvocation + ) + | expression LBRACK expression RBRACK + | methodCall + | NEW creator + | LPAREN annotation* typeType (BITAND typeType)* RPAREN expression + | expression postfix=(INC | DEC) + | prefix=(ADD|SUB|INC|DEC) expression + | prefix=(TILDE|BANG) expression + | expression bop=(MUL|DIV|MOD) expression + | expression bop=(ADD|SUB) expression + | expression (LT LT | GT GT GT | GT GT) expression + | expression bop=(LE | GE | GT | LT) expression + | expression bop=INSTANCEOF (typeType | pattern) + | expression bop=(EQUAL | NOTEQUAL) expression + | expression bop=BITAND expression + | expression bop=CARET expression + | expression bop=BITOR expression + | expression bop=AND expression + | expression bop=OR expression + | expression bop=QUESTION expression COLON expression + | expression + bop=(ASSIGN | ADD_ASSIGN | SUB_ASSIGN | MUL_ASSIGN | DIV_ASSIGN | AND_ASSIGN | OR_ASSIGN | XOR_ASSIGN | RSHIFT_ASSIGN | URSHIFT_ASSIGN | LSHIFT_ASSIGN | MOD_ASSIGN) + expression + | lambdaExpression // Java8 + | switchExpression // Java17 + + // Java 8 methodReference + | expression COLONCOLON typeArguments? identifier + | typeType COLONCOLON (typeArguments? identifier | NEW) + | classType COLONCOLON typeArguments? NEW + ; + +// Java17 +pattern + : variableModifier* typeType annotation* identifier + ; + +// Java8 +lambdaExpression + : lambdaParameters ARROW lambdaBody + ; + +// Java8 +lambdaParameters + : identifier + | LPAREN formalParameterList? RPAREN + | LPAREN identifier (COMMA identifier)* RPAREN + | LPAREN lambdaLVTIList? RPAREN + ; + +// Java8 +lambdaBody + : expression + | block + ; + +primary + : LPAREN expression RPAREN + | THIS + | SUPER + | literal + | identifier + | typeTypeOrVoid DOT CLASS + | nonWildcardTypeArguments (explicitGenericInvocationSuffix | THIS arguments) + ; + +// Java17 +switchExpression + : SWITCH parExpression LBRACE switchLabeledRule* RBRACE + ; + +// Java17 +switchLabeledRule + : CASE (expressionList | NULL_LITERAL | guardedPattern) (ARROW | COLON) switchRuleOutcome + | DEFAULT (ARROW | COLON) switchRuleOutcome + ; + +// Java17 +guardedPattern + : LPAREN guardedPattern RPAREN + | variableModifier* typeType annotation* identifier (AND expression)* + | guardedPattern AND expression + ; + +// Java17 +switchRuleOutcome + : block + | blockStatement* + ; + +classType + : (classOrInterfaceType DOT)? annotation* identifier typeArguments? + ; + +creator + : nonWildcardTypeArguments createdName classCreatorRest + | createdName (arrayCreatorRest | classCreatorRest) + ; + +createdName + : identifier typeArgumentsOrDiamond? (DOT identifier typeArgumentsOrDiamond?)* + | primitiveType + ; + +innerCreator + : identifier nonWildcardTypeArgumentsOrDiamond? classCreatorRest + ; + +arrayCreatorRest + : LBRACK (RBRACK (LBRACK RBRACK)* arrayInitializer | expression RBRACK (LBRACK expression RBRACK)* (LBRACK RBRACK)*) + ; + +classCreatorRest + : arguments classBody? + ; + +explicitGenericInvocation + : nonWildcardTypeArguments explicitGenericInvocationSuffix + ; + +typeArgumentsOrDiamond + : LT GT + | typeArguments + ; + +nonWildcardTypeArgumentsOrDiamond + : LT GT + | nonWildcardTypeArguments + ; + +nonWildcardTypeArguments + : LT typeList GT + ; + +typeList + : typeType (COMMA typeType)* + ; + +typeType + : annotation* (classOrInterfaceType | primitiveType) (annotation* LBRACK RBRACK)* + ; + +primitiveType + : BOOLEAN + | CHAR + | BYTE + | SHORT + | INT + | LONG + | FLOAT + | DOUBLE + ; + +typeArguments + : LT typeArgument (COMMA typeArgument)* GT + ; + +superSuffix + : arguments + | DOT typeArguments? identifier arguments? + ; + +explicitGenericInvocationSuffix + : SUPER superSuffix + | identifier arguments + ; + +arguments + : LPAREN expressionList? RPAREN + ; \ No newline at end of file diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DRLFactory.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DRLFactory.java index 15bf0addf05..71b23c2fdd3 100644 --- a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DRLFactory.java +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DRLFactory.java @@ -25,6 +25,7 @@ import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.RecognizerSharedState; import org.antlr.runtime.TokenStream; +import org.drools.drl.parser.lang.DRL10Lexer; import org.drools.drl.parser.lang.DRL5Expressions; import org.drools.drl.parser.lang.DRL5Lexer; import org.drools.drl.parser.lang.DRL5Parser; @@ -113,6 +114,8 @@ public static DRLLexer getDRLLexer(CharStream input, LanguageLevelOption languag case DRL6: case DRL6_STRICT: return new DRL6Lexer(input); + case DRL10: + return new DRL10Lexer(); } throw new RuntimeException("Unknown language level"); } diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/Drl6ExprParser.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/Drl6ExprParser.java new file mode 100644 index 00000000000..9fb0186afb7 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/Drl6ExprParser.java @@ -0,0 +1,90 @@ +/** + * 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; + +import java.util.Collections; +import java.util.List; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.RecognizerSharedState; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.descr.ConstraintConnectiveDescr; +import org.drools.drl.parser.lang.DRLExpressions; +import org.drools.drl.parser.lang.DRLLexer; +import org.drools.drl.parser.lang.ParserHelper; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * This is a helper class that provides helper methods to parse expressions + * using both the DRLExpressions parser and the DRLExprTree parser. + */ +public class Drl6ExprParser implements DrlExprParser { + + private ParserHelper helper = null; + + private final LanguageLevelOption languageLevel; + + public Drl6ExprParser(LanguageLevelOption languageLevel) { + this.languageLevel = languageLevel; + } + + /** Parse an expression from text */ + public ConstraintConnectiveDescr parse( final String text ) { + ConstraintConnectiveDescr constraint = null; + try { + DRLLexer lexer = DRLFactory.getDRLLexer(new ANTLRStringStream(text), languageLevel); + CommonTokenStream input = new CommonTokenStream( lexer ); + RecognizerSharedState state = new RecognizerSharedState(); + helper = new ParserHelper( input, state, languageLevel ); + DRLExpressions parser = DRLFactory.getDRLExpressions(input, state, helper, languageLevel); + parser.setBuildDescr( true ); + parser.setLeftMostExpr( null ); // setting initial value just in case + BaseDescr expr = parser.conditionalOrExpression(); + if ( expr != null && !parser.hasErrors() ) { + constraint = ConstraintConnectiveDescr.newAnd(); + constraint.addOrMerge( expr ); + } + } catch ( RecognitionException e ) { + helper.reportError( e ); + } + return constraint; + } + + public String getLeftMostExpr() { + return helper != null ? helper.getLeftMostExpr() : null; + } + + /** + * @return true if there were parser errors. + */ + public boolean hasErrors() { + return helper != null && helper.hasErrors(); + } + + /** + * @return a list of errors found while parsing. + */ + @SuppressWarnings("unchecked") + public List getErrors() { + return helper != null ? helper.getErrors() : Collections.EMPTY_LIST; + } + +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlExprParser.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlExprParser.java index 60c61ac248d..0702a1d1461 100644 --- a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlExprParser.java +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlExprParser.java @@ -18,73 +18,29 @@ */ package org.drools.drl.parser; -import java.util.Collections; import java.util.List; -import org.antlr.runtime.ANTLRStringStream; -import org.antlr.runtime.CommonTokenStream; -import org.antlr.runtime.RecognitionException; -import org.antlr.runtime.RecognizerSharedState; -import org.drools.drl.parser.lang.DRLExpressions; -import org.drools.drl.parser.lang.DRLLexer; -import org.drools.drl.parser.lang.ParserHelper; -import org.drools.drl.ast.descr.BaseDescr; import org.drools.drl.ast.descr.ConstraintConnectiveDescr; -import org.kie.internal.builder.conf.LanguageLevelOption; /** * This is a helper class that provides helper methods to parse expressions * using both the DRLExpressions parser and the DRLExprTree parser. */ -public class DrlExprParser { - - private ParserHelper helper = null; - - private final LanguageLevelOption languageLevel; - - public DrlExprParser(LanguageLevelOption languageLevel) { - this.languageLevel = languageLevel; - } +public interface DrlExprParser { /** Parse an expression from text */ - public ConstraintConnectiveDescr parse( final String text ) { - ConstraintConnectiveDescr constraint = null; - try { - DRLLexer lexer = DRLFactory.getDRLLexer(new ANTLRStringStream(text), languageLevel); - CommonTokenStream input = new CommonTokenStream( lexer ); - RecognizerSharedState state = new RecognizerSharedState(); - helper = new ParserHelper( input, state, languageLevel ); - DRLExpressions parser = DRLFactory.getDRLExpressions(input, state, helper, languageLevel); - parser.setBuildDescr( true ); - parser.setLeftMostExpr( null ); // setting initial value just in case - BaseDescr expr = parser.conditionalOrExpression(); - if ( expr != null && !parser.hasErrors() ) { - constraint = ConstraintConnectiveDescr.newAnd(); - constraint.addOrMerge( expr ); - } - } catch ( RecognitionException e ) { - helper.reportError( e ); - } - return constraint; - } - - public String getLeftMostExpr() { - return helper != null ? helper.getLeftMostExpr() : null; - } + ConstraintConnectiveDescr parse( final String text ); + + String getLeftMostExpr(); /** * @return true if there were parser errors. */ - public boolean hasErrors() { - return helper != null && helper.hasErrors(); - } + boolean hasErrors(); /** * @return a list of errors found while parsing. */ @SuppressWarnings("unchecked") - public List getErrors() { - return helper != null ? helper.getErrors() : Collections.EMPTY_LIST; - } - + List getErrors(); } diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlExprParserFactory.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlExprParserFactory.java new file mode 100644 index 00000000000..9ee116faf37 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/DrlExprParserFactory.java @@ -0,0 +1,20 @@ +package org.drools.drl.parser; + +import org.drools.drl10.parser.Drl10ExprParser; +import org.kie.internal.builder.conf.LanguageLevelOption; + +public class DrlExprParserFactory { + + public static DrlExprParser getDrlExrParser(LanguageLevelOption languageLevel) { + switch (languageLevel) { + case DRL5: + case DRL6: + case DRL6_STRICT: + return new Drl6ExprParser(languageLevel); + case DRL10: + return new Drl10ExprParser(languageLevel); + default: + throw new RuntimeException("Unsupported language level: " + languageLevel); + } + } +} 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 7b852c8dce3..d6ee0c042bb 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 @@ -18,6 +18,8 @@ */ package org.drools.drl.parser; +import org.drools.drl10.parser.DRLParserError; +import org.drools.drl10.parser.DRLParserWrapper; import org.drools.io.InternalResource; import org.drools.drl.ast.descr.PackageDescr; import org.drools.drl.parser.lang.DRLLexer; @@ -164,10 +166,35 @@ public PackageDescr parse(final boolean isEditor, final InputStream is) throws DroolsParserException, IOException { this.resource = resource; String encoding = resource instanceof InternalResource ? ((InternalResource) resource).getEncoding() : null; - - lexer = DRLFactory.buildLexer(is, encoding, languageLevel); - DRLParser parser = DRLFactory.buildParser(lexer, languageLevel); - return compile(isEditor, parser); + System.out.println("### parse : languageLevel = " + languageLevel); + if (languageLevel == LanguageLevelOption.DRL10) { + // new parser based on antlr4 + try { + DRLParserWrapper parser = new DRLParserWrapper(); + PackageDescr packageDescr = parser.parse(is); + for (final DRLParserError drlParserError : parser.getErrors()) { + final ParserError err = new ParserError(resource, + drlParserError.getMessage(), + drlParserError.getLineNumber(), + drlParserError.getColumn()); + this.results.add(err); + } + return !this.hasErrors() ? packageDescr : null; + } catch (Exception e) { + LOG.error("Exception", e); + final ParserError err = new ParserError(resource, + GENERIC_ERROR_MESSAGE + e.toString() + "\n" + Arrays.toString(e.getStackTrace()), + -1, + 0); + this.results.add(err); + throw new DroolsParserException(GENERIC_ERROR_MESSAGE + e.getMessage(), e); + } + } else { + // old parsers based on antlr3 + lexer = DRLFactory.buildLexer(is, encoding, languageLevel); + DRLParser parser = DRLFactory.buildParser(lexer, languageLevel); + return compile(isEditor, parser); + } } /** diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL10Lexer.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL10Lexer.java new file mode 100644 index 00000000000..15f6157bd3e --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL10Lexer.java @@ -0,0 +1,46 @@ +/** + * 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.lang; + +import java.util.List; + +import org.antlr.runtime.Token; +import org.drools.drl.parser.DroolsParserException; + +/** + * No implementation because everything is handled by DRL10Parser + */ +public class DRL10Lexer implements DRLLexer { + + @Override + public String getSourceName() { + throw new UnsupportedOperationException("This method should not be called"); + } + + @Override + public Token nextToken() { + throw new UnsupportedOperationException("This method should not be called"); + } + + @Override + public List getErrors() { + throw new UnsupportedOperationException("This method should not be called"); + } + +} \ No newline at end of file diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLErrorListener.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLErrorListener.java new file mode 100644 index 00000000000..0e5b5985b6c --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLErrorListener.java @@ -0,0 +1,49 @@ +/** + * 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.drl10.parser; + +import java.util.ArrayList; +import java.util.List; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +/** + * Collect errors while parsing DRL + */ +public class DRLErrorListener extends BaseErrorListener { + + private final List errors = new ArrayList<>(); + + public List getErrors() { + return errors; + } + + @Override + public void syntaxError(Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + + errors.add(new DRLParserError(line, charPositionInLine, msg)); + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLExpressions.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLExpressions.java new file mode 100644 index 00000000000..e7aa8102c48 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLExpressions.java @@ -0,0 +1,61 @@ +/** + * 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.drl10.parser; + +import java.util.LinkedList; +import java.util.List; + +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.TokenStream; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.parser.DroolsParserException; +import org.drools.drl.parser.lang.DroolsSentence; + +public abstract class DRLExpressions extends Parser { + public DRLExpressions(TokenStream input) { + super(input); + } + + public abstract void setBuildDescr( boolean build ); + public abstract boolean isBuildDescr(); + + public abstract void setLeftMostExpr( String value ); + public abstract String getLeftMostExpr(); + + public abstract void setHasBindings( boolean value ); + public abstract boolean hasBindings(); + + /* + * This is the original method signature in drools/drools-drl/drools-drl-parser: + * public abstract BaseDescr conditionalOrExpression() throws RecognitionException; + * I changed it here because the conditionalOrExpression() method generated by ANTLR 4 from the conditionalOrExpression rule + * returns ConditionalOrExpressionContext, so it has a return type that's different from what was originally expected here. + * The Descr object is of course inside that context (go to this method's impl to see). + */ + public abstract BaseDescr conditionalOrExpressionDescr() throws RecognitionException; + + public abstract ParserHelper getHelper(); + public abstract boolean hasErrors(); + public abstract List getErrors(); + public abstract List getErrorMessages(); + public abstract void enableEditorInterface(); + public abstract void disableEditorInterface(); + public abstract LinkedList getEditorInterface(); +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserError.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserError.java new file mode 100644 index 00000000000..4b893e11e46 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserError.java @@ -0,0 +1,75 @@ +/** + * 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.drl10.parser; + +/** + * Error information while parsing DRL + */ +public class DRLParserError { + + private int lineNumber; + private int column; + private String message; + + private Exception exception; + + public DRLParserError(int lineNumber, int column, String message) { + this.lineNumber = lineNumber; + this.column = column; + this.message = message; + } + + public DRLParserError(Exception exception) { + this.exception = exception; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public int getColumn() { + return column; + } + + public void setColumn(int column) { + this.column = column; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return "DRLParserError{" + + "lineNumber=" + lineNumber + + ", column=" + column + + ", message='" + message + '\'' + + ", exception=" + exception + + '}'; + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserException.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserException.java new file mode 100644 index 00000000000..833d1aa8df0 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserException.java @@ -0,0 +1,30 @@ +/** + * 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.drl10.parser; + +public class DRLParserException extends RuntimeException { + + public DRLParserException() { + super(); + } + + public DRLParserException(String message) { + super(message); + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserHelper.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserHelper.java new file mode 100644 index 00000000000..f4436bf44b0 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserHelper.java @@ -0,0 +1,126 @@ +/** + * 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.drl10.parser; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.drools.drl.ast.descr.PackageDescr; + +/** + * Collection of static helper methods for DRLParser + */ +public class DRLParserHelper { + + private DRLParserHelper() { + } + + /** + * Entry point for parsing DRL. + * Unlike DRLParserWrapper.parse(), this method does not collect errors. + */ + public static PackageDescr parse(String drl) { + DRLParser drlParser = createDrlParser(drl); + return compilationUnitContext2PackageDescr(drlParser.compilationUnit(), drlParser.getTokenStream()); + } + + public static DRLParser createDrlParser(String drl) { + CharStream charStream = CharStreams.fromString(drl); + return createDrlParser(charStream); + } + + public static DRLParser createDrlParser(InputStream is) { + try { + CharStream charStream = CharStreams.fromStream(is); + return createDrlParser(charStream); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static DRLParser createDrlParser(CharStream charStream) { + DRLLexer drlLexer = new DRLLexer(charStream); + CommonTokenStream commonTokenStream = new CommonTokenStream(drlLexer); + return new DRLParser(commonTokenStream); + } + + /** + * DRLVisitorImpl visits a parse tree and creates a PackageDescr + */ + public static PackageDescr compilationUnitContext2PackageDescr(DRLParser.CompilationUnitContext ctx, TokenStream tokenStream) { + DRLVisitorImpl visitor = new DRLVisitorImpl(tokenStream); + Object descr = visitor.visit(ctx); + if (descr instanceof PackageDescr) { + return (PackageDescr) descr; + } else { + throw new DRLParserException("CompilationUnitContext should produce PackageDescr. descr = " + descr.getClass()); + } + } + + /** + * Given a row and column of the input DRL, return the index of the matched token + */ + public static Integer computeTokenIndex(DRLParser parser, int row, int col) { + for (int i = 0; i < parser.getInputStream().size(); i++) { + Token token = parser.getInputStream().get(i); + int start = token.getCharPositionInLine(); + int stop = token.getCharPositionInLine() + token.getText().length(); + if (token.getLine() > row) { + return token.getTokenIndex() - 1; + } else if (token.getLine() == row && start >= col) { + return token.getTokenIndex() == 0 ? 0 : token.getTokenIndex() - 1; + } else if (token.getLine() == row && start < col && stop >= col) { + return token.getTokenIndex(); + } + } + return null; + } + + /** + * RuleContext.getText() connects all nodes including ErrorNode. This method appends texts only from valid nodes + */ + public static String getTextWithoutErrorNode(ParseTree tree) { + if (tree.getChildCount() == 0) { + return ""; + } + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < tree.getChildCount(); i++) { + ParseTree child = tree.getChild(i); + if (!(child instanceof ErrorNode)) { + if (child instanceof TerminalNode) { + builder.append(child.getText()); + } else { + builder.append(getTextWithoutErrorNode(child)); + } + } + } + + return builder.toString(); + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserWrapper.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserWrapper.java new file mode 100644 index 00000000000..914255e1375 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLParserWrapper.java @@ -0,0 +1,85 @@ +/** + * 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.drl10.parser; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.drools.drl.ast.descr.PackageDescr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.drools.drl10.parser.DRLParserHelper.compilationUnitContext2PackageDescr; + +/** + * Wrapper for DRLParser. Somewhat duplicated from DRLParserHelper, but this class is instantiated and holds errors. + */ +public class DRLParserWrapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(DRLParserWrapper.class); + + private final List errors = new ArrayList<>(); + + /** + * Main entry point for parsing DRL + */ + public PackageDescr parse(String drl) { + DRLParser drlParser = DRLParserHelper.createDrlParser(drl); + return parse(drlParser); + } + + /** + * Main entry point for parsing DRL + */ + public PackageDescr parse(InputStream is) { + DRLParser drlParser = DRLParserHelper.createDrlParser(is); + return parse(drlParser); + } + + private PackageDescr parse(DRLParser drlParser) { + DRLErrorListener errorListener = new DRLErrorListener(); + drlParser.addErrorListener(errorListener); + + DRLParser.CompilationUnitContext cxt = drlParser.compilationUnit(); + + errors.addAll(errorListener.getErrors()); + + try { + return compilationUnitContext2PackageDescr(cxt, drlParser.getTokenStream()); + } catch (Exception e) { + LOGGER.error("Exception while creating PackageDescr", e); + errors.add(new DRLParserError(e)); + return null; + } + } + + public List getErrors() { + return errors; + } + + public List getErrorMessages() { + return errors.stream().map(DRLParserError::getMessage).collect(Collectors.toList()); + } + + public boolean hasErrors() { + return !errors.isEmpty(); + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLVisitorImpl.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLVisitorImpl.java new file mode 100644 index 00000000000..a977fee9882 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DRLVisitorImpl.java @@ -0,0 +1,845 @@ +/** + * 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.drl10.parser; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.RuleNode; +import org.drools.drl.ast.descr.AccumulateDescr; +import org.drools.drl.ast.descr.AccumulateImportDescr; +import org.drools.drl.ast.descr.AndDescr; +import org.drools.drl.ast.descr.AnnotationDescr; +import org.drools.drl.ast.descr.AttributeDescr; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.descr.BehaviorDescr; +import org.drools.drl.ast.descr.CollectDescr; +import org.drools.drl.ast.descr.EntryPointDeclarationDescr; +import org.drools.drl.ast.descr.EntryPointDescr; +import org.drools.drl.ast.descr.EvalDescr; +import org.drools.drl.ast.descr.ExistsDescr; +import org.drools.drl.ast.descr.ExprConstraintDescr; +import org.drools.drl.ast.descr.ForallDescr; +import org.drools.drl.ast.descr.FromDescr; +import org.drools.drl.ast.descr.FunctionDescr; +import org.drools.drl.ast.descr.FunctionImportDescr; +import org.drools.drl.ast.descr.GlobalDescr; +import org.drools.drl.ast.descr.ImportDescr; +import org.drools.drl.ast.descr.MVELExprDescr; +import org.drools.drl.ast.descr.NotDescr; +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; +import org.drools.drl.ast.descr.TypeFieldDescr; +import org.drools.drl.ast.descr.UnitDescr; +import org.drools.drl.ast.descr.WindowDeclarationDescr; + +import static org.drools.drl10.parser.DRLParserHelper.getTextWithoutErrorNode; +import static org.drools.drl10.parser.ParserStringUtils.getTextPreservingWhitespace; +import static org.drools.drl10.parser.ParserStringUtils.getTokenTextPreservingWhitespace; +import static org.drools.drl10.parser.ParserStringUtils.safeStripStringDelimiters; +import static org.drools.drl10.parser.ParserStringUtils.trimThen; +import static org.drools.util.StringUtils.unescapeJava; + +/** + * Visitor implementation for DRLParser. + * Basically, each visit method creates and returns a Descr object traversing the parse tree. + * Finally, visitCompilationUnit() returns a PackageDescr object. + * Try not to depend on DRLVisitorImpl's internal state for clean maintainability + */ +public class DRLVisitorImpl extends DRLParserBaseVisitor { + + private final TokenStream tokenStream; + + public DRLVisitorImpl(TokenStream tokenStream) { + this.tokenStream = tokenStream; + } + + /** + * Main entry point for creating PackageDescr from a parser tree. + */ + @Override + public PackageDescr visitCompilationUnit(DRLParser.CompilationUnitContext ctx) { + PackageDescr packageDescr = new PackageDescr(); + if (ctx.packagedef() != null) { + packageDescr.setName(getTextWithoutErrorNode(ctx.packagedef().name)); + } + List descrList = visitDescrChildren(ctx); + applyChildrenDescrs(packageDescr, descrList); + return packageDescr; + } + + /** + * Add all children Descr to PackageDescr + */ + private void applyChildrenDescrs(PackageDescr packageDescr, List descrList) { + // This bunch of if-blocks will be refactored by DROOLS-7564 + descrList.forEach(descr -> { + if (descr instanceof UnitDescr) { + packageDescr.setUnit((UnitDescr) descr); + } else if (descr instanceof GlobalDescr) { + packageDescr.addGlobal((GlobalDescr) descr); + } else if (descr instanceof FunctionImportDescr) { + packageDescr.addFunctionImport((FunctionImportDescr) descr); + } else if (descr instanceof AccumulateImportDescr) { + packageDescr.addAccumulateImport((AccumulateImportDescr) descr); + } else if (descr instanceof ImportDescr) { + packageDescr.addImport((ImportDescr) descr); + } else if (descr instanceof FunctionDescr) { + FunctionDescr functionDescr = (FunctionDescr) descr; + functionDescr.setNamespace(packageDescr.getNamespace()); + AttributeDescr dialect = packageDescr.getAttribute("dialect"); + if (dialect != null) { + functionDescr.setDialect(dialect.getValue()); + } + packageDescr.addFunction(functionDescr); + } else if (descr instanceof TypeDeclarationDescr) { + packageDescr.addTypeDeclaration((TypeDeclarationDescr) descr); + } else if (descr instanceof EntryPointDeclarationDescr) { + packageDescr.addEntryPointDeclaration((EntryPointDeclarationDescr) descr); + } else if (descr instanceof WindowDeclarationDescr) { + packageDescr.addWindowDeclaration((WindowDeclarationDescr) descr); + } else if (descr instanceof AttributeDescr) { + packageDescr.addAttribute((AttributeDescr) descr); + } else if (descr instanceof RuleDescr) { // QueryDescr extends RuleDescr + packageDescr.addRule((RuleDescr) descr); + packageDescr.afterRuleAdded((RuleDescr) descr); + } + }); + } + + @Override + public UnitDescr visitUnitdef(DRLParser.UnitdefContext ctx) { + return new UnitDescr(ctx.name.getText()); + } + + @Override + public GlobalDescr visitGlobaldef(DRLParser.GlobaldefContext ctx) { + GlobalDescr globalDescr = new GlobalDescr(ctx.drlIdentifier().getText(), ctx.type().getText()); + populateStartEnd(globalDescr, ctx); + return globalDescr; + } + + @Override + public ImportDescr visitImportStandardDef(DRLParser.ImportStandardDefContext ctx) { + String target = ctx.drlQualifiedName().getText() + (ctx.MUL() != null ? ".*" : ""); + if (ctx.DRL_FUNCTION() != null || ctx.STATIC() != null) { + FunctionImportDescr functionImportDescr = new FunctionImportDescr(); + functionImportDescr.setTarget(target); + populateStartEnd(functionImportDescr, ctx); + return functionImportDescr; + } else { + ImportDescr importDescr = new ImportDescr(); + importDescr.setTarget(target); + populateStartEnd(importDescr, ctx); + return importDescr; + } + } + + @Override + public AccumulateImportDescr visitImportAccumulateDef(DRLParser.ImportAccumulateDefContext ctx) { + AccumulateImportDescr accumulateImportDescr = new AccumulateImportDescr(); + accumulateImportDescr.setTarget(ctx.drlQualifiedName().getText()); + accumulateImportDescr.setFunctionName(ctx.IDENTIFIER().getText()); + return accumulateImportDescr; + } + + @Override + public FunctionDescr visitFunctiondef(DRLParser.FunctiondefContext ctx) { + FunctionDescr functionDescr = new FunctionDescr(); + if (ctx.typeTypeOrVoid() != null) { + functionDescr.setReturnType(ctx.typeTypeOrVoid().getText()); + } else { + functionDescr.setReturnType("void"); + } + functionDescr.setName(ctx.IDENTIFIER().getText()); + + // add function parameters + DRLParser.FormalParametersContext formalParametersContext = ctx.formalParameters(); + DRLParser.FormalParameterListContext formalParameterListContext = formalParametersContext.formalParameterList(); + if (formalParameterListContext != null) { + List formalParameterContexts = formalParameterListContext.formalParameter(); + formalParameterContexts.forEach(formalParameterContext -> { + DRLParser.TypeTypeContext typeTypeContext = formalParameterContext.typeType(); + DRLParser.VariableDeclaratorIdContext variableDeclaratorIdContext = formalParameterContext.variableDeclaratorId(); + functionDescr.addParameter(typeTypeContext.getText(), variableDeclaratorIdContext.getText()); + }); + } + functionDescr.setBody(getTextPreservingWhitespace(ctx.block())); + return functionDescr; + } + + @Override + public BaseDescr visitDeclaredef(DRLParser.DeclaredefContext ctx) { + return visitDescrChildren(ctx).get(0); // only one child + } + + @Override + public TypeDeclarationDescr visitTypeDeclaration(DRLParser.TypeDeclarationContext ctx) { + TypeDeclarationDescr typeDeclarationDescr = new TypeDeclarationDescr(ctx.name.getText()); + if (ctx.EXTENDS() != null) { + typeDeclarationDescr.addSuperType(ctx.superType.getText()); + } + ctx.drlAnnotation().stream() + .map(this::visitDrlAnnotation) + .forEach(typeDeclarationDescr::addAnnotation); + ctx.field().stream() + .map(this::visitField) + .forEach(typeDeclarationDescr::addField); + return typeDeclarationDescr; + } + + @Override + public EntryPointDeclarationDescr visitEntryPointDeclaration(DRLParser.EntryPointDeclarationContext ctx) { + EntryPointDeclarationDescr entryPointDeclarationDescr = new EntryPointDeclarationDescr(); + entryPointDeclarationDescr.setEntryPointId(ctx.name.getText()); + ctx.drlAnnotation().stream() + .map(this::visitDrlAnnotation) + .forEach(entryPointDeclarationDescr::addAnnotation); + return entryPointDeclarationDescr; + } + + @Override + public WindowDeclarationDescr visitWindowDeclaration(DRLParser.WindowDeclarationContext ctx) { + WindowDeclarationDescr windowDeclarationDescr = new WindowDeclarationDescr(); + windowDeclarationDescr.setName(ctx.name.getText()); + ctx.drlAnnotation().stream() + .map(this::visitDrlAnnotation) + .forEach(windowDeclarationDescr::addAnnotation); + windowDeclarationDescr.setPattern((PatternDescr) visitLhsPatternBind(ctx.lhsPatternBind())); + return windowDeclarationDescr; + } + + /** + * entry point for one rule + */ + @Override + public RuleDescr visitRuledef(DRLParser.RuledefContext ctx) { + RuleDescr ruleDescr = new RuleDescr(safeStripStringDelimiters(ctx.name.getText())); + + if (ctx.EXTENDS() != null) { + ruleDescr.setParentName(safeStripStringDelimiters(ctx.parentName.getText())); + } + + ctx.drlAnnotation().stream().map(this::visitDrlAnnotation).forEach(ruleDescr::addAnnotation); + + if (ctx.attributes() != null) { + List descrList = visitDescrChildren(ctx.attributes()); + descrList.stream() + .filter(AttributeDescr.class::isInstance) + .map(AttributeDescr.class::cast) + .forEach(ruleDescr::addAttribute); + } + + if (ctx.lhs() != null) { + List lhsDescrList = visitLhs(ctx.lhs()); + lhsDescrList.forEach(descr -> ruleDescr.getLhs().addDescr(descr)); + slimLhsRootDescr(ruleDescr.getLhs()); + } + + if (ctx.rhs() != null) { + ruleDescr.setConsequenceLocation(ctx.rhs().getStart().getLine(), ctx.rhs().getStart().getCharPositionInLine()); // location of "then" + ruleDescr.setConsequence(trimThen(getTextPreservingWhitespace(ctx.rhs()))); // RHS is just a text + } + + return ruleDescr; + } + + private void slimLhsRootDescr(AndDescr root) { + // Root Descr is always AndDescr. + // For example, if there are nested AndDescr like + // AndDescr + // /\ + // P AndDescr + // /\ + // P P + // is slimmed down to + // AndDescr + // / | \ + // P P P + List descrList = new ArrayList<>(root.getDescrs()); + root.getDescrs().clear(); + descrList.forEach(root::addOrMerge); + } + + @Override + public QueryDescr visitQuerydef(DRLParser.QuerydefContext ctx) { + QueryDescr queryDescr = new QueryDescr(safeStripStringDelimiters(ctx.name.getText())); + + DRLParser.FormalParametersContext formalParametersContext = ctx.formalParameters(); + if (formalParametersContext != null) { + DRLParser.FormalParameterListContext formalParameterListContext = formalParametersContext.formalParameterList(); + List formalParameterContexts = formalParameterListContext.formalParameter(); + formalParameterContexts.forEach(formalParameterContext -> { + DRLParser.TypeTypeContext typeTypeContext = formalParameterContext.typeType(); + DRLParser.VariableDeclaratorIdContext variableDeclaratorIdContext = formalParameterContext.variableDeclaratorId(); + queryDescr.addParameter(typeTypeContext.getText(), variableDeclaratorIdContext.getText()); + }); + } + + ctx.drlAnnotation().stream().map(this::visitDrlAnnotation).forEach(queryDescr::addAnnotation); + + ctx.lhsExpression().stream() + .flatMap(lhsExpressionContext -> visitDescrChildren(lhsExpressionContext).stream()) + .forEach(descr -> queryDescr.getLhs().addDescr(descr)); + + slimLhsRootDescr(queryDescr.getLhs()); + + return queryDescr; + } + + @Override + public AnnotationDescr visitDrlAnnotation(DRLParser.DrlAnnotationContext ctx) { + AnnotationDescr annotationDescr = new AnnotationDescr(ctx.name.getText()); + if (ctx.drlElementValue() != null) { + annotationDescr.setValue(getTextPreservingWhitespace(ctx.drlElementValue())); // single value + } else if (ctx.drlElementValuePairs() != null) { + visitDrlElementValuePairs(ctx.drlElementValuePairs(), annotationDescr); // multiple values + } + return annotationDescr; + } + + @Override + public TypeFieldDescr visitField(DRLParser.FieldContext ctx) { + TypeFieldDescr typeFieldDescr = new TypeFieldDescr(); + typeFieldDescr.setFieldName(ctx.label().IDENTIFIER().getText()); + typeFieldDescr.setPattern(new PatternDescr(ctx.type().getText())); + if (ctx.ASSIGN() != null) { + typeFieldDescr.setInitExpr(getTextPreservingWhitespace(ctx.initExpr)); + } + ctx.drlAnnotation().stream() + .map(this::visitDrlAnnotation) + .forEach(typeFieldDescr::addAnnotation); + return typeFieldDescr; + } + + private void visitDrlElementValuePairs(DRLParser.DrlElementValuePairsContext ctx, AnnotationDescr annotationDescr) { + ctx.drlElementValuePair().forEach(pairCtx -> { + String key = pairCtx.key.getText(); + String value = getTextPreservingWhitespace(pairCtx.value); + annotationDescr.setKeyValue(key, value); + }); + } + + @Override + public AttributeDescr visitExpressionAttribute(DRLParser.ExpressionAttributeContext ctx) { + AttributeDescr attributeDescr = new AttributeDescr(ctx.name.getText()); + attributeDescr.setValue(getTextPreservingWhitespace(ctx.conditionalOrExpression())); + attributeDescr.setType(AttributeDescr.Type.EXPRESSION); + return attributeDescr; + } + + @Override + public AttributeDescr visitBooleanAttribute(DRLParser.BooleanAttributeContext ctx) { + AttributeDescr attributeDescr = new AttributeDescr(ctx.name.getText()); + attributeDescr.setValue(ctx.BOOL_LITERAL() != null ? ctx.BOOL_LITERAL().getText() : "true"); + attributeDescr.setType(AttributeDescr.Type.BOOLEAN); + return attributeDescr; + } + + @Override + public AttributeDescr visitStringAttribute(DRLParser.StringAttributeContext ctx) { + AttributeDescr attributeDescr = new AttributeDescr(ctx.name.getText()); + attributeDescr.setValue(unescapeJava(safeStripStringDelimiters(ctx.DRL_STRING_LITERAL().getText()))); + attributeDescr.setType(AttributeDescr.Type.STRING); + return attributeDescr; + } + + @Override + public AttributeDescr visitStringListAttribute(DRLParser.StringListAttributeContext ctx) { + AttributeDescr attributeDescr = new AttributeDescr(ctx.name.getText()); + List valueList = ctx.DRL_STRING_LITERAL().stream() + .map(ParseTree::getText) + .collect(Collectors.toList()); + attributeDescr.setValue(createStringList(valueList)); + attributeDescr.setType(AttributeDescr.Type.LIST); + return attributeDescr; + } + + private static String createStringList(List valueList) { + StringBuilder sb = new StringBuilder(); + sb.append("[ "); + for (int i = 0; i < valueList.size(); i++) { + sb.append(valueList.get(i)); + if (i < valueList.size() - 1) { + sb.append(", "); + } + } + sb.append(" ]"); + return sb.toString(); + } + + @Override + public AttributeDescr visitIntOrChunkAttribute(DRLParser.IntOrChunkAttributeContext ctx) { + AttributeDescr attributeDescr = new AttributeDescr(ctx.name.getText()); + if (ctx.DECIMAL_LITERAL() != null) { + attributeDescr.setValue(ctx.DECIMAL_LITERAL().getText()); + attributeDescr.setType(AttributeDescr.Type.NUMBER); + } else { + attributeDescr.setValue(getTextPreservingWhitespace(ctx.chunk())); + attributeDescr.setType(AttributeDescr.Type.EXPRESSION); + } + return attributeDescr; + } + + @Override + public AttributeDescr visitDurationAttribute(DRLParser.DurationAttributeContext ctx) { + AttributeDescr attributeDescr = new AttributeDescr(ctx.name.getText()); + if (ctx.DECIMAL_LITERAL() != null) { + attributeDescr.setValue(ctx.DECIMAL_LITERAL().getText()); + attributeDescr.setType(AttributeDescr.Type.NUMBER); + } else { + attributeDescr.setValue(unescapeJava(safeStripStringDelimiters(ctx.TIME_INTERVAL().getText()))); + attributeDescr.setType(AttributeDescr.Type.EXPRESSION); + } + return attributeDescr; + } + + /** + * entry point for LHS + */ + @Override + public List visitLhs(DRLParser.LhsContext ctx) { + if (ctx.lhsExpression() != null) { + return visitDescrChildren(ctx); + } else { + return new ArrayList<>(); + } + } + + @Override + public BaseDescr visitLhsPatternBind(DRLParser.LhsPatternBindContext ctx) { + if (ctx.lhsPattern().size() == 1) { + return getSinglePatternDescr(ctx); + } else if (ctx.lhsPattern().size() > 1) { + return getOrDescrWithMultiplePatternDescr(ctx); + } else { + throw new IllegalStateException("ctx.lhsPattern().size() == 0 : " + ctx.getText()); + } + } + + private PatternDescr getSinglePatternDescr(DRLParser.LhsPatternBindContext ctx) { + List patternDescrList = visitDescrChildren(ctx); + if (patternDescrList.isEmpty() || !(patternDescrList.get(0) instanceof PatternDescr)) { + throw new IllegalStateException("lhsPatternBind must have at least one lhsPattern : " + ctx.getText()); + } + PatternDescr patternDescr = (PatternDescr)patternDescrList.get(0); + + if (ctx.label() != null) { + patternDescr.setIdentifier(ctx.label().IDENTIFIER().getText()); + } else if (ctx.unif() != null) { + patternDescr.setIdentifier(ctx.unif().IDENTIFIER().getText()); + patternDescr.setUnification(true); + } + return patternDescr; + } + + private OrDescr getOrDescrWithMultiplePatternDescr(DRLParser.LhsPatternBindContext ctx) { + OrDescr orDescr = new OrDescr(); + List descrList = visitDescrChildren(ctx); + descrList.stream() + .filter(PatternDescr.class::isInstance) + .map(PatternDescr.class::cast) + .forEach(patternDescr -> { + if (ctx.label() != null) { + patternDescr.setIdentifier(ctx.label().IDENTIFIER().getText()); + } + orDescr.addDescr(patternDescr); + }); + + return orDescr; + } + + /** + * entry point for a Pattern + */ + @Override + public PatternDescr visitLhsPattern(DRLParser.LhsPatternContext ctx) { + PatternDescr patternDescr = new PatternDescr(ctx.objectType.getText()); + if (ctx.QUESTION() != null) { + patternDescr.setQuery(true); + } + if (ctx.patternFilter() != null) { + patternDescr.addBehavior(visitPatternFilter(ctx.patternFilter())); + } + if (ctx.patternSource() != null) { + PatternSourceDescr patternSourceDescr = (PatternSourceDescr) visitPatternSource(ctx.patternSource()); + patternSourceDescr.setResource(patternDescr.getResource()); + patternDescr.setSource(patternSourceDescr); + } + List constraintDescrList = visitConstraints(ctx.positionalConstraints(), ctx.constraints()); + constraintDescrList.forEach(descr -> addToPatternDescr(patternDescr, descr)); + return patternDescr; + } + + private void addToPatternDescr(PatternDescr patternDescr, ExprConstraintDescr exprConstraintDescr) { + exprConstraintDescr.setResource(patternDescr.getResource()); + patternDescr.addConstraint(exprConstraintDescr); + } + + @Override + public ForallDescr visitLhsForall(DRLParser.LhsForallContext ctx) { + ForallDescr forallDescr = new ForallDescr(); + visitDescrChildren(ctx).forEach(forallDescr::addDescr); + return forallDescr; + } + + @Override + public PatternDescr visitLhsAccumulate(DRLParser.LhsAccumulateContext ctx) { + AccumulateDescr accumulateDescr = new AccumulateDescr(); + accumulateDescr.setInput(visitLhsAndDef(ctx.lhsAndDef())); + + // accumulate function + for (DRLParser.AccumulateFunctionContext accumulateFunctionContext : ctx.accumulateFunction()) { + accumulateDescr.addFunction(visitAccumulateFunction(accumulateFunctionContext)); + } + + PatternDescr patternDescr = new PatternDescr("Object"); + patternDescr.setSource(accumulateDescr); + List constraintDescrList = visitConstraints(ctx.constraints()); + constraintDescrList.forEach(patternDescr::addConstraint); + return patternDescr; + } + + @Override + public BehaviorDescr visitPatternFilter(DRLParser.PatternFilterContext ctx) { + BehaviorDescr behaviorDescr = new BehaviorDescr(); + behaviorDescr.setType(ctx.DRL_WINDOW().getText()); + behaviorDescr.setSubType(ctx.IDENTIFIER().getText()); + List drlExpressionContexts = ctx.expressionList().drlExpression(); + List parameters = drlExpressionContexts.stream().map(ParserStringUtils::getTextPreservingWhitespace).collect(Collectors.toList()); + behaviorDescr.setParameters(parameters); + return behaviorDescr; + } + + @Override + public FromDescr visitFromExpression(DRLParser.FromExpressionContext ctx) { + FromDescr fromDescr = new FromDescr(); + fromDescr.setDataSource(new MVELExprDescr(ctx.getText())); + return fromDescr; + } + + @Override + public CollectDescr visitFromCollect(DRLParser.FromCollectContext ctx) { + CollectDescr collectDescr = new CollectDescr(); + collectDescr.setInputPattern((PatternDescr) visitLhsPatternBind(ctx.lhsPatternBind())); + return collectDescr; + } + + @Override + public AccumulateDescr visitFromAccumulate(DRLParser.FromAccumulateContext ctx) { + AccumulateDescr accumulateDescr = new AccumulateDescr(); + accumulateDescr.setInput(visitLhsAndDef(ctx.lhsAndDef())); + if (ctx.DRL_INIT() != null) { + // inline custom accumulate + accumulateDescr.setInitCode(getTextPreservingWhitespace(ctx.initBlockStatements)); + accumulateDescr.setActionCode(getTextPreservingWhitespace(ctx.actionBlockStatements)); + if (ctx.DRL_REVERSE() != null) { + accumulateDescr.setReverseCode(getTextPreservingWhitespace(ctx.reverseBlockStatements)); + } + accumulateDescr.setResultCode(ctx.expression().getText()); + } else { + // accumulate function + accumulateDescr.addFunction(visitAccumulateFunction(ctx.accumulateFunction())); + } + return accumulateDescr; + } + + @Override + public AccumulateDescr.AccumulateFunctionCallDescr visitAccumulateFunction(DRLParser.AccumulateFunctionContext ctx) { + String function = ctx.IDENTIFIER().getText(); + String bind = ctx.label() == null ? null : ctx.label().IDENTIFIER().getText(); + String[] params = new String[]{getTextPreservingWhitespace(ctx.drlExpression())}; + return new AccumulateDescr.AccumulateFunctionCallDescr(function, bind, false, params); + } + + @Override + public EntryPointDescr visitFromEntryPoint(DRLParser.FromEntryPointContext ctx) { + return new EntryPointDescr(safeStripStringDelimiters(ctx.stringId().getText())); + } + + /** + * Collect constraints in a Pattern + */ + @Override + public List visitConstraints(DRLParser.ConstraintsContext ctx) { + List exprConstraintDescrList = new ArrayList<>(); + populateExprConstraintDescrList(ctx, exprConstraintDescrList); + return exprConstraintDescrList; + } + + /** + * Collect constraints in a Pattern. Positional constraints comes first with semicolon. + */ + private List visitConstraints(DRLParser.PositionalConstraintsContext positionalCtx, DRLParser.ConstraintsContext ctx) { + List exprConstraintDescrList = new ArrayList<>(); + populateExprConstraintDescrList(positionalCtx, exprConstraintDescrList); + populateExprConstraintDescrList(ctx, exprConstraintDescrList); + return exprConstraintDescrList; + } + + private void populateExprConstraintDescrList(ParserRuleContext ctx, List exprConstraintDescrList) { + if (ctx == null) { + return; + } + List descrList = visitDescrChildren(ctx); + for (BaseDescr descr : descrList) { + if (descr instanceof ExprConstraintDescr) { + ExprConstraintDescr exprConstraintDescr = (ExprConstraintDescr) descr; + exprConstraintDescr.setType(ctx instanceof DRLParser.PositionalConstraintsContext ? ExprConstraintDescr.Type.POSITIONAL : ExprConstraintDescr.Type.NAMED); + exprConstraintDescr.setPosition(exprConstraintDescrList.size()); + exprConstraintDescrList.add(exprConstraintDescr); + } + } + } + + /** + * Takes one constraint as String and create ExprConstraintDescr + */ + @Override + public ExprConstraintDescr visitConstraint(DRLParser.ConstraintContext ctx) { + String constraint = visitConstraintChildren(ctx); + if (!constraint.isEmpty()) { + ExprConstraintDescr constraintDescr = new ExprConstraintDescr(constraint); + constraintDescr.setType(ExprConstraintDescr.Type.NAMED); + return constraintDescr; + } + return null; + } + + @Override + public String visitDrlIdentifier(DRLParser.DrlIdentifierContext ctx) { + return ctx.getText(); + } + + @Override + public ExistsDescr visitLhsExists(DRLParser.LhsExistsContext ctx) { + ExistsDescr existsDescr = new ExistsDescr(); + if (ctx.lhsExpression() != null) { + // exists( A() or B() ) + List baseDescrs = visitDescrChildren(ctx); + if (baseDescrs.size() == 1) { + existsDescr.addDescr(baseDescrs.get(0)); + } else { + throw new IllegalStateException("'exists()' children descr size must be 1 : " + ctx.getText()); + } + } else { + // exists A() + BaseDescr descr = visitLhsPatternBind(ctx.lhsPatternBind()); + existsDescr.addDescr(descr); + } + return existsDescr; + } + + @Override + public NotDescr visitLhsNot(DRLParser.LhsNotContext ctx) { + NotDescr notDescr = new NotDescr(); + if (ctx.lhsExpression() != null) { + // not ( A() or B() ) + List baseDescrs = visitDescrChildren(ctx); + if (baseDescrs.size() == 1) { + notDescr.addDescr(baseDescrs.get(0)); + } else { + throw new IllegalStateException("'not()' children descr size must be 1 : " + ctx.getText()); + } + } else { + // not A() + BaseDescr descr = visitLhsPatternBind(ctx.lhsPatternBind()); + notDescr.addDescr(descr); + } + return notDescr; + } + + @Override + public EvalDescr visitLhsEval(DRLParser.LhsEvalContext ctx) { + return new EvalDescr(getTextPreservingWhitespace(ctx.conditionalOrExpression())); + } + + @Override + public BaseDescr visitLhsExpressionEnclosed(DRLParser.LhsExpressionEnclosedContext ctx) { + // enclosed expression is simply stripped because Descr itself is encapsulated + return (BaseDescr) visit(ctx.lhsExpression()); + } + + @Override + public BaseDescr visitLhsOr(DRLParser.LhsOrContext ctx) { + // For flatten nested OrDescr logic, we call visitDescrChildrenForDescrNodePair instead of usual visitDescrChildren + List descrList = visitDescrChildrenForDescrNodePair(ctx); + if (descrList.size() == 1) { + // Avoid nested OrDescr + return descrList.get(0).getDescr(); + } else { + OrDescr orDescr = new OrDescr(); + // For example, in case of A() or B() or C(), + // Parser creates AST like this: + // lhsOr + // / \ + // A() lhsOr + // / \ + // B() C() + // So, we need to flatten it so that OrDescr has A(), B() and C() as children. + List flattenedDescrs = flattenOrDescr(descrList); + flattenedDescrs.forEach(orDescr::addDescr); + return orDescr; + } + } + + private List flattenOrDescr(List descrList) { + List flattenedDescrs = new ArrayList<>(); + for (DescrNodePair descrNodePair : descrList) { + BaseDescr descr = descrNodePair.getDescr(); + ParseTree node = descrNodePair.getNode(); // parser node corresponding to the descr + if (descr instanceof OrDescr && !(node instanceof DRLParser.LhsExpressionEnclosedContext)) { + // sibling OrDescr should be flattened unless it's explicitly enclosed by parenthesis + flattenedDescrs.addAll(((OrDescr) descr).getDescrs()); + } else { + flattenedDescrs.add(descr); + } + } + return flattenedDescrs; + } + + @Override + public BaseDescr visitLhsAnd(DRLParser.LhsAndContext ctx) { + return createAndDescr(ctx); + } + + private BaseDescr createAndDescr(ParserRuleContext ctx) { + // For flatten nested AndDescr logic, we call visitDescrChildrenForDescrNodePair instead of usual visitDescrChildren + List descrList = visitDescrChildrenForDescrNodePair(ctx); + if (descrList.size() == 1) { + // Avoid nested AndDescr + return descrList.get(0).getDescr(); + } else { + AndDescr andDescr = new AndDescr(); + // For example, in case of A() and B() and C(), + // Parser creates AST like this: + // lhsAnd + // / \ + // A() lhsAnd + // / \ + // B() C() + // So, we need to flatten it so that AndDescr has A(), B() and C() as children. + List flattenedDescrs = flattenAndDescr(descrList); + flattenedDescrs.forEach(andDescr::addDescr); + return andDescr; + } + } + + private List flattenAndDescr(List descrList) { + List flattenedDescrs = new ArrayList<>(); + for (DescrNodePair descrNodePair : descrList) { + BaseDescr descr = descrNodePair.getDescr(); + ParseTree node = descrNodePair.getNode(); // parser node corresponding to the descr + if (descr instanceof AndDescr && !(node instanceof DRLParser.LhsExpressionEnclosedContext)) { + // sibling AndDescr should be flattened unless it's explicitly enclosed by parenthesis + flattenedDescrs.addAll(((AndDescr) descr).getDescrs()); + } else { + flattenedDescrs.add(descr); + } + } + return flattenedDescrs; + } + + @Override + public BaseDescr visitLhsAndDef(DRLParser.LhsAndDefContext ctx) { + return createAndDescr(ctx); + } + + @Override + public BaseDescr visitLhsUnary(DRLParser.LhsUnaryContext ctx) { + return visitDescrChildren(ctx).get(0); // lhsUnary has only one child + } + + private void populateStartEnd(BaseDescr 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()); + } + + /** + * This is a special version of visitChildren(). + * This collects children BaseDescr objects and returns them as a list. + */ + private List visitDescrChildren(RuleNode node) { + List aggregator = new ArrayList<>(); + int n = node.getChildCount(); + + for (int i = 0; i < n && this.shouldVisitNextChild(node, aggregator); ++i) { + ParseTree c = node.getChild(i); + Object childResult = c.accept(this); + if (childResult instanceof BaseDescr) { + aggregator.add((BaseDescr) childResult); + } + } + return aggregator; + } + + // This method is used when the parent descr requires children parser node information for composing the descr. + // Ideally, we should use visitDescrChildren as possible and use the returned Descr object to compose the parent descr. + // However, for example, in flatten OrDescr/AndDescr logic, + // enhancing the returned Descr object of visitLhsExpressionEnclosed() (e.g. adding 'enclosed' flag to OrDescr/AndDescr) could be intrusive just for composing the parent descr. + private List visitDescrChildrenForDescrNodePair(RuleNode node) { + List aggregator = new ArrayList<>(); + int n = node.getChildCount(); + + for (int i = 0; i < n && this.shouldVisitNextChild(node, aggregator); ++i) { + ParseTree c = node.getChild(i); + Object childResult = c.accept(this); + if (childResult instanceof BaseDescr) { + aggregator.add(new DescrNodePair((BaseDescr) childResult, c)); // pairing the returned Descr and the parser node + } + } + return aggregator; + } + + private static class DescrNodePair { + private final BaseDescr descr; // returned Descr object + private final ParseTree node; // parser node corresponding to the descr. This is used for composing the parent descr. + + private DescrNodePair(BaseDescr descr, ParseTree node) { + this.descr = descr; + this.node = node; + } + + public BaseDescr getDescr() { + return descr; + } + + public ParseTree getNode() { + return node; + } + } + + /** + * Return the text of constraint as-is + */ + private String visitConstraintChildren(ParserRuleContext ctx) { + return getTokenTextPreservingWhitespace(ctx, tokenStream); + } +} \ No newline at end of file diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/Developer_Notes.md b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/Developer_Notes.md new file mode 100644 index 00000000000..c521ee6d14f --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/Developer_Notes.md @@ -0,0 +1,38 @@ +## drools-parser + +This module is a reimplementation of the DRL (Drools Rule Language) parser based on ANTLR4. + +The current [DRL6Parser](https://github.com/apache/incubator-kie-drools/blob/main/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL6Parser.java) is based on ANTLR3 and contains a lot of custom modifications, which is hard to maintain. This new module should keep the separation between the parser syntax (`DRLParser.g4`) and the Descr generation (`DRLVisitorImpl.java`). + +This module started with a part of LSP to develop DRL editors, but it is not limited to that. This module will also replace DRL6Parser in the drools code base. + +### How is this developed? + +1. The starting point is [DRL6Parser](https://github.com/apache/incubator-kie-drools/blob/main/drools-drl/drools-drl-parser/src/main/java/org/drools/drl/parser/lang/DRL6Parser.java). While it contains lots of customizations, we can map its javadoc (e.g. `packageStatement := PACKAGE qualifiedIdentifier SEMICOLON?`) to `DRLParser.g4` (e.g. `packagedef : PACKAGE name=drlQualifiedName SEMI? ;`). +2. `DRLLexer.g4` is written to define tokens for DRL. +3. `DRLLexer.g4` imports `JavaLexer.g4` to reuse Java tokens. `DRLParser.g4` imports `JavaParser.g4` to reuse Java grammar. These Java parser files are distributed by ANTLR4 under BSD license. +4. In `DRLLexer.g4`, basically define tokens with a prefix "DRL_" to clarify they are DRL keywords. +5. In `DRLParser.g4`, define parser rules with a prefix "drl" if the rule name conflicts with `JavaParser.g4`. Sometimes we need to do that, because such a rule may contain DRL keywords. +6. (As of 2023/10/31) this parser doesn't deeply parse rule RHS (just multiple `RHS_CHUNK`s), because Drools passes RHS text to drools-compiler as-is. In case of developing DRL editors, we may need to integrate another Java LSP to support RHS code completion, etc. +7. LHS constraint (e.g. `age > 30`) is also handled as text. Further processing will be done in the later compiler phase. +8. `DRLParser` processes a DRL text and produces an AST(abstract syntax tree). Then apply `DRLVisitorImpl` to generate PackageDescr following the visitor pattern. So the main work would be implementing `DRLParser.g4` and `DRLVisitorImpl`. +9. Errors are handled by `DRLErrorListener` +10. (As of 2023/10/31) We have 2 test classes. `DRLParserTest` is a very basic test to check if the parser can parse DRL. `MiscDRLParserTest` contains various DRL syntax to check if the parser generates correct Descr objects. `MiscDRLParserTest` was ported from [RuleParserTest](https://github.com/apache/incubator-kie-drools/blob/main/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/RuleParserTest.java) so that we can ensure the compatibility of generated Descr objects between the current implementation and the new one. +11. As `DRL6Parser` contains hard-coded customizations, sometimes we need to read and understand the `DRL6Parser` source codes to meet the compatibility. +12. (As of 2023/10/31) `MiscDRLParserTest` still has several test cases with `@Disabled` which are relatively lower priority or edge cases. They need to be resolved at some point in the future. To fix the issues, file a JIRA, remove the `@Disabled` annotation, and fix the implementation to pass the test case. + +### Next steps + +1. Create a feature branch in drools repo and replace `DRL6Parser` with this new parser. +2. We will detect issues in the new parser by running the existing tests in drools repo. If we find any issues, we will fix them in the new parser and add new tests to cover them. Such tests would be more or less Descr comparison tests, so we would add a new test class which is similar to `MiscDRLParserTest`. + +### Refactoring candidates +- `DRLParserHelper` and `DRLParserWrapper` have some duplicated code and purpose. We can merge them into one class. +- `MiscDRLParserTest` can be cleaner and fixed to align with SonarLint suggestions. +- Constraint related parser rules after `conditionalOrExpression` are written in antlr3 style. They could be refactored to antlr4 style (like `lhsExpression`). + +### Development tips +- IntelliJ IDEA has an ANTLR4 plugin, which "ANTLR Preview" window displays a parse tree. It is very useful to debug the parser rules. + +### Resources +[The Definitive ANTLR 4 Reference](https://pragprog.com/titles/tpantlr2/the-definitive-antlr-4-reference/) \ No newline at end of file diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/Drl10ExprParser.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/Drl10ExprParser.java new file mode 100644 index 00000000000..19ec4735363 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/Drl10ExprParser.java @@ -0,0 +1,87 @@ +/** + * 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.drl10.parser; + +import java.util.Collections; +import java.util.List; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.RecognitionException; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.descr.ConstraintConnectiveDescr; +import org.drools.drl.parser.DrlExprParser; +import org.drools.drl.parser.DroolsParserException; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * This is a helper class that provides helper methods to parse expressions + * using both the DRLExpressions parser and the DRLExprTree parser. + */ +public class Drl10ExprParser implements DrlExprParser { + + private ParserHelper helper = null; + + private final LanguageLevelOption languageLevel; + + public Drl10ExprParser(LanguageLevelOption languageLevel) { + this.languageLevel = languageLevel; + } + + /** Parse an expression from text */ + public ConstraintConnectiveDescr parse(final String text) { + ConstraintConnectiveDescr constraint = null; + try { + DRLLexer lexer = new DRLLexer(CharStreams.fromString(text)); + CommonTokenStream input = new CommonTokenStream(lexer); + helper = new ParserHelper(input, null, languageLevel); + DRLExpressions parser = new DRL6Expressions(input, helper); + parser.setBuildDescr(true); + parser.setLeftMostExpr(null); // setting initial value just in case + BaseDescr expr = parser.conditionalOrExpressionDescr(); + if (expr != null && !parser.hasErrors()) { + constraint = ConstraintConnectiveDescr.newAnd(); + constraint.addOrMerge(expr); + } + } catch (RecognitionException e) { + helper.reportError(e); + } + return constraint; + } + + public String getLeftMostExpr() { + return helper != null ? helper.getLeftMostExpr() : null; + } + + /** + * @return true if there were parser errors. + */ + public boolean hasErrors() { + return helper != null && helper.hasErrors(); + } + + /** + * @return a list of errors found while parsing. + */ + @SuppressWarnings("unchecked") + public List getErrors() { + return helper != null ? helper.getErrors() : Collections.EMPTY_LIST; + } + +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DroolsParserExceptionFactory.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DroolsParserExceptionFactory.java new file mode 100644 index 00000000000..abf586c55d0 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/DroolsParserExceptionFactory.java @@ -0,0 +1,250 @@ +/** + * 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.drl10.parser; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.antlr.v4.runtime.FailedPredicateException; +import org.antlr.v4.runtime.NoViableAltException; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Token; +import org.drools.drl.parser.DRLFactory; +import org.drools.drl.parser.DroolsParserException; +import org.drools.drl.parser.lang.DroolsParaphraseTypes; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * Helper class that generates DroolsParserException with user friendly error + * messages. + * + * @see DroolsParserException + */ +public class DroolsParserExceptionFactory { + public final static String NO_VIABLE_ALT_MESSAGE = "Line %1$d:%2$d no viable alternative at input '%3$s'%4$s"; + public final static String FAILED_PREDICATE_MESSAGE = "Line %1$d:%2$d rule '%3$s' failed predicate: {%4$s}?%5$s"; + public final static String TRAILING_SEMI_COLON_NOT_ALLOWED_MESSAGE = "Line %1$d:%2$d trailing semi-colon not allowed%3$s"; + public final static String PARSER_LOCATION_MESSAGE_COMPLETE = " in %1$s %2$s"; + public final static String PARSER_LOCATION_MESSAGE_PART = " in %1$s"; + public final static String UNEXPECTED_EXCEPTION = "Line %1$d:%2$d unexpected exception at input '%3$s'. Exception: %4$s. Stack trace:\n %5$s"; + + private final Collection> paraphrases; + + // TODO: need to deal with this array + private String[] tokenNames = null; + + private final LanguageLevelOption languageLevel; + + /** + * DroolsParserErrorMessages constructor. + * + * @param tokenNames + * tokenNames generated by ANTLR + * @param paraphrases + * paraphrases parser structure + */ + public DroolsParserExceptionFactory(Collection> paraphrases, + LanguageLevelOption languageLevel) { + this.paraphrases = paraphrases; + this.languageLevel = languageLevel; + } + + /** + * This method creates a DroolsParserException for trailing semicolon + * exception, full of information. + * + * @param line + * line number + * @param column + * column position + * @param offset + * char offset + * @return DroolsParserException filled. + */ + public DroolsParserException createTrailingSemicolonException( int line, + int column, + int offset ) { + String message = String + .format( + TRAILING_SEMI_COLON_NOT_ALLOWED_MESSAGE, + line, + column, + formatParserLocation() ); + + return new DroolsParserException( "ERR 104", + message, + line, + column, + offset, + null ); + } + + /** + * This method creates a DroolsParserException full of information. + * + * @param e + * original exception + * @return DroolsParserException filled. + */ + public DroolsParserException createDroolsException( RecognitionException e ) { + List codeAndMessage = createErrorMessage( e ); + return new DroolsParserException( codeAndMessage.get( 1 ), + codeAndMessage + .get( 0 ), + // TODO verify this is correct + e.getOffendingToken().getLine(), + e.getOffendingToken().getCharPositionInLine(), + e.getOffendingToken().getStartIndex(), + e ); + } + + /** + * This will take a RecognitionException, and create a sensible error + * message out of it + */ + private List createErrorMessage( RecognitionException e ) { + List codeAndMessage = new ArrayList<>( 2 ); + String message; + if ( e instanceof NoViableAltException ) { + // NoViableAltException nvae = (NoViableAltException) e; + message = String.format( + NO_VIABLE_ALT_MESSAGE, + // TODO verify this is correct + e.getOffendingToken().getLine(), + e.getOffendingToken().getCharPositionInLine(), + getBetterToken( e.getOffendingToken() ), + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 101" ); + } else if ( e instanceof FailedPredicateException ) { + FailedPredicateException fpe = (FailedPredicateException) e; + String ruleName = fpe.getRecognizer().getRuleNames()[fpe.getRuleIndex()]; + message = String.format( + FAILED_PREDICATE_MESSAGE, + // TODO verify this is correct + e.getOffendingToken().getLine(), + e.getOffendingToken().getCharPositionInLine(), + ruleName, + fpe.getPredicate(), + formatParserLocation() ); + codeAndMessage.add( message ); + codeAndMessage.add( "ERR 103" ); + } + if ( codeAndMessage.get( 0 ).length() == 0 ) { + codeAndMessage.add( "?????" ); + } + return codeAndMessage; + } + + public DroolsParserException createDroolsException( Exception e, + Token token ) { + StringWriter sw = new StringWriter(); + e.printStackTrace( new PrintWriter(sw) ); + return new DroolsParserException( String.format( + DroolsParserExceptionFactory.UNEXPECTED_EXCEPTION, + token.getLine(), + token.getCharPositionInLine(), + getBetterToken( token ), + e.toString(), + sw.toString() ), + e ); + + } + + /** + * This will take Paraphrases stack, and create a sensible location + */ + private String formatParserLocation() { + StringBuilder sb = new StringBuilder(); + if ( paraphrases != null ) { + for ( Map map : paraphrases ) { + for ( Entry activeEntry : map.entrySet() ) { + if ( activeEntry.getValue().length() == 0 ) { + String kStr = getLocationName( activeEntry.getKey() ); + if( kStr.length() > 0 ){ + sb.append( String.format( PARSER_LOCATION_MESSAGE_PART, kStr ) ); + } + } else { + sb.append( String.format( PARSER_LOCATION_MESSAGE_COMPLETE, + getLocationName( activeEntry.getKey() ), + activeEntry.getValue() ) ); + } + } + } + } + return sb.toString(); + } + + /** + * Returns a string based on Paraphrase Type + * + * @param type + * Paraphrase Type + * @return a string representing the + */ + private String getLocationName( DroolsParaphraseTypes type ) { + switch ( type ) { + case PACKAGE : + return "package"; + case IMPORT : + return "import"; + case FUNCTION_IMPORT : + return "function import"; + case ACCUMULATE_IMPORT : + return "accumulate import"; + case GLOBAL : + return "global"; + case FUNCTION : + return "function"; + case QUERY : + return "query"; + case TEMPLATE : + return "template"; + case RULE : + return "rule"; + case RULE_ATTRIBUTE : + return "rule attribute"; + case PATTERN : + return "pattern"; + case EVAL : + return "eval"; + default : + return ""; + } + } + + /** + * Helper method that creates a user friendly token definition + * + * @param token + * token + * @return user friendly token definition + */ + private String getBetterToken( Token token ) { + if ( token == null ) { + return ""; + } + return DRLFactory.getBetterToken(token.getType(), token.getText(), languageLevel); + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/ParserHelper.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/ParserHelper.java new file mode 100644 index 00000000000..f729115b7ca --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/ParserHelper.java @@ -0,0 +1,678 @@ +/** + * 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. + */ +/* + * Licensed 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.drl10.parser; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.antlr.runtime.RecognizerSharedState; +import org.antlr.v4.runtime.CommonToken; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.drools.drl.ast.descr.AttributeDescr; +import org.drools.drl.ast.descr.BaseDescr; +import org.drools.drl.ast.dsl.AbstractClassTypeDeclarationBuilder; +import org.drools.drl.ast.dsl.AccumulateDescrBuilder; +import org.drools.drl.ast.dsl.AccumulateImportDescrBuilder; +import org.drools.drl.ast.dsl.AttributeDescrBuilder; +import org.drools.drl.ast.dsl.AttributeSupportBuilder; +import org.drools.drl.ast.dsl.BehaviorDescrBuilder; +import org.drools.drl.ast.dsl.CEDescrBuilder; +import org.drools.drl.ast.dsl.CollectDescrBuilder; +import org.drools.drl.ast.dsl.ConditionalBranchDescrBuilder; +import org.drools.drl.ast.dsl.DeclareDescrBuilder; +import org.drools.drl.ast.dsl.DescrBuilder; +import org.drools.drl.ast.dsl.DescrFactory; +import org.drools.drl.ast.dsl.EntryPointDeclarationDescrBuilder; +import org.drools.drl.ast.dsl.EnumDeclarationDescrBuilder; +import org.drools.drl.ast.dsl.EnumLiteralDescrBuilder; +import org.drools.drl.ast.dsl.EvalDescrBuilder; +import org.drools.drl.ast.dsl.FieldDescrBuilder; +import org.drools.drl.ast.dsl.ForallDescrBuilder; +import org.drools.drl.ast.dsl.FunctionDescrBuilder; +import org.drools.drl.ast.dsl.GlobalDescrBuilder; +import org.drools.drl.ast.dsl.GroupByDescrBuilder; +import org.drools.drl.ast.dsl.ImportDescrBuilder; +import org.drools.drl.ast.dsl.NamedConsequenceDescrBuilder; +import org.drools.drl.ast.dsl.PackageDescrBuilder; +import org.drools.drl.ast.dsl.PatternContainerDescrBuilder; +import org.drools.drl.ast.dsl.PatternDescrBuilder; +import org.drools.drl.ast.dsl.QueryDescrBuilder; +import org.drools.drl.ast.dsl.RuleDescrBuilder; +import org.drools.drl.ast.dsl.TypeDeclarationDescrBuilder; +import org.drools.drl.ast.dsl.UnitDescrBuilder; +import org.drools.drl.ast.dsl.WindowDeclarationDescrBuilder; +import org.drools.drl.parser.DroolsParserException; +import org.drools.drl.parser.lang.DroolsEditorType; +import org.drools.drl.parser.lang.DroolsParaphraseTypes; +import org.drools.drl.parser.lang.DroolsSentence; +import org.drools.drl.parser.lang.DroolsSentenceType; +import org.drools.drl.parser.lang.DroolsSoftKeywords; +import org.drools.drl.parser.lang.DroolsToken; +import org.kie.internal.builder.conf.LanguageLevelOption; + +/** + * This is a class to hold all the helper functions/methods used + * by the DRL parser + */ +public class ParserHelper { + public final String[] statementKeywords = new String[]{ + DroolsSoftKeywords.PACKAGE, + DroolsSoftKeywords.UNIT, + DroolsSoftKeywords.IMPORT, + DroolsSoftKeywords.GLOBAL, + DroolsSoftKeywords.DECLARE, + DroolsSoftKeywords.FUNCTION, + DroolsSoftKeywords.RULE, + DroolsSoftKeywords.QUERY + }; + + public List errors = new ArrayList<>(); + public LinkedList editorInterface = null; + public boolean isEditorInterfaceEnabled = false; + private Deque> paraphrases = new ArrayDeque<>(); + + // parameters from parser + private DroolsParserExceptionFactory errorMessageFactory = null; + private TokenStream input = null; + private RecognizerSharedState state = null; + + private String leftMostExpr = null; + + // helper attribute + private boolean hasOperator = false; + + private final LanguageLevelOption languageLevel; + + public ParserHelper(TokenStream input, + RecognizerSharedState state, + LanguageLevelOption languageLevel) { + this.errorMessageFactory = new DroolsParserExceptionFactory( paraphrases, languageLevel ); + this.input = input; + this.state = state; + this.languageLevel = languageLevel; + } + + public LinkedList getEditorInterface() { + return editorInterface; + } + + public void setLeftMostExpr( String value ) { + this.leftMostExpr = value; + } + + public String getLeftMostExpr() { + return this.leftMostExpr; + } + + public void enableEditorInterface() { + isEditorInterfaceEnabled = true; + } + + public void disableEditorInterface() { + isEditorInterfaceEnabled = false; + } + + public void setHasOperator( boolean hasOperator ) { + this.hasOperator = hasOperator; + } + + public boolean getHasOperator() { + return hasOperator; + } + + public void beginSentence( DroolsSentenceType sentenceType ) { + if ( isEditorInterfaceEnabled ) { + if ( null == editorInterface ) { + editorInterface = new LinkedList<>(); + } + if (editorInterface.isEmpty()){ + DroolsSentence sentence = new DroolsSentence(); + sentence.setType( sentenceType ); + editorInterface.add( sentence ); + } + } + } + + public DroolsSentence getActiveSentence() { + return editorInterface.getLast(); + } + + public void emit( List< ? > tokens, + DroolsEditorType editorType ) { + if ( isEditorInterfaceEnabled && tokens != null ) { + for ( Object activeObject : tokens ) { + emit( (Token) activeObject, + editorType ); + } + } + } + + public void emit( Token token, + DroolsEditorType editorType ) { + if ( isEditorInterfaceEnabled && token != null && editorType != null ) { + ((DroolsToken) token).setEditorType( editorType ); + getActiveSentence().addContent( (DroolsToken) token ); + } + } + + public void emit( int activeContext ) { + if ( isEditorInterfaceEnabled ) { + getActiveSentence().addContent( activeContext ); + } + } + + public DroolsToken getLastTokenOnList( LinkedList< ? > list ) { + DroolsToken lastToken = null; + for ( Object object : list ) { + if ( object instanceof DroolsToken ) { + lastToken = (DroolsToken) object; + } + } + return lastToken; + } + + public String retrieveLT( int LTNumber ) { + if ( null == input ) return null; + if ( null == input.LT( LTNumber ) ) return null; + if ( null == input.LT( LTNumber ).getText() ) return null; + + return input.LT( LTNumber ).getText(); + } + + public boolean validateLT( int LTNumber, + String text ) { + String text2Validate = retrieveLT( LTNumber ); + return validateText( text, text2Validate ); + } + + private boolean validateText( String text, String text2Validate ) { + return text2Validate != null && text2Validate.equals( text ); + } + + public boolean isPluggableEvaluator( int offset, + boolean negated ) { + String text2Validate = retrieveLT( offset ); + return text2Validate != null && DroolsSoftKeywords.isOperator(text2Validate, negated); + } + + public boolean isPluggableEvaluator( boolean negated ) { + return isPluggableEvaluator( 1, + negated ); + } + + public boolean validateIdentifierKey( String text ) { + return validateLT( 1, + text ); + } + + public boolean validateCEKeyword( int index ) { + String text2Validate = retrieveLT( index ); + return validateText( text2Validate, + DroolsSoftKeywords.NOT ) || + validateText( text2Validate, + DroolsSoftKeywords.EXISTS ) || + validateText( text2Validate, + DroolsSoftKeywords.FORALL ) || + validateText( text2Validate, + DroolsSoftKeywords.AND ) || + validateText( text2Validate, + DroolsSoftKeywords.OR ) || + validateText( text2Validate, + DroolsSoftKeywords.COLLECT ) || + validateText( text2Validate, + DroolsSoftKeywords.FROM ) || + validateText( text2Validate, + DroolsSoftKeywords.END ) || + validateText( text2Validate, + DroolsSoftKeywords.EVAL ) || + validateText( text2Validate, + DroolsSoftKeywords.OVER ) || + validateText( text2Validate, + DroolsSoftKeywords.THEN ); + } + + public boolean validateStatement( int index ) { + boolean ret = false; + String text2Validate = retrieveLT( index ); + for ( String st : statementKeywords ) { + if ( validateText( text2Validate, + st ) ) { + ret = true; + break; + } + } + return ret || validateAttribute( index ); + } + + public boolean validateAttribute( int index ) { + String text2Validate = retrieveLT( index ); + return validateText( text2Validate, + DroolsSoftKeywords.SALIENCE ) || + validateText( text2Validate, + DroolsSoftKeywords.ENABLED ) || + (validateText( text2Validate, + DroolsSoftKeywords.NO ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.LOOP )) || + (validateText( text2Validate, + DroolsSoftKeywords.AUTO ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.FOCUS )) || + (validateText( text2Validate, + DroolsSoftKeywords.LOCK ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.ON ) && + validateLT( index + 3, + "-" ) && + validateLT( index + 4, + DroolsSoftKeywords.ACTIVE )) || + (validateText( text2Validate, + DroolsSoftKeywords.AGENDA ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.GROUP )) || + (validateText( text2Validate, + DroolsSoftKeywords.ACTIVATION ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.GROUP )) || + (validateText( text2Validate, + DroolsSoftKeywords.RULEFLOW ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.GROUP )) || + (validateText( text2Validate, + DroolsSoftKeywords.DATE ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.EFFECTIVE )) || + (validateText( text2Validate, + DroolsSoftKeywords.DATE ) && + validateLT( index + 1, + "-" ) && + validateLT( index + 2, + DroolsSoftKeywords.EXPIRES )) || + validateText( text2Validate, + DroolsSoftKeywords.DIALECT ) || + validateText( text2Validate, + DroolsSoftKeywords.CALENDARS ) || + validateText( text2Validate, + DroolsSoftKeywords.TIMER ) || + validateText( text2Validate, + DroolsSoftKeywords.DURATION ) || + validateText( text2Validate, + DroolsSoftKeywords.REFRACT ) || + validateText( text2Validate, + DroolsSoftKeywords.DIRECT ); + } + + public void reportError( RecognitionException ex ) { + // if we've already reported an error and have not matched a token + // yet successfully, don't report any errors. + if ( state.errorRecovery ) { + return; + } + state.errorRecovery = true; + + errors.add( errorMessageFactory.createDroolsException( ex ) ); + } + + public void reportError( Exception e ) { + try { + errors.add( errorMessageFactory.createDroolsException( e, + input.LT( 1 ) ) ); + } catch (Exception ignored) { + errors.add(new DroolsParserException( "Unexpected error: " + e.getMessage(), e )); + } + } + + /** return the raw DroolsParserException errors */ + public List getErrors() { + return errors; + } + + /** Return a list of pretty strings summarising the errors */ + public List getErrorMessages() { + List messages = new ArrayList<>( errors.size() ); + + for ( DroolsParserException activeException : errors ) { + messages.add( activeException.getMessage() ); + } + + return messages; + } + + /** return true if any parser errors were accumulated */ + public boolean hasErrors() { + return !errors.isEmpty(); + } + + /** + * Method that adds a paraphrase type into paraphrases stack. + * + * @param type + * paraphrase type + */ + public void pushParaphrases( DroolsParaphraseTypes type ) { + Map activeMap = new HashMap<>(); + activeMap.put( type, + "" ); + paraphrases.push( activeMap ); + } + + public Map popParaphrases() { + return paraphrases.pop(); + } + + /** + * Method that sets paraphrase value for a type into paraphrases stack. + * + * @param type + * paraphrase type + * @param value + * paraphrase value + */ + public void setParaphrasesValue( DroolsParaphraseTypes type, + String value ) { + paraphrases.peek().put( type, + value ); + } + + void setStart( DescrBuilder< ? , ? > db ) { + setStart( db, + input.LT( 1 ) ); + } + + void setStart( DescrBuilder< ? , ? > db, + Token first ) { + if ( db != null && first != null ) { + db.startCharacter( ((CommonToken) first).getStartIndex() ).startLocation( first.getLine(), + first.getCharPositionInLine() ); + } + } + + void setStart( BaseDescr descr, + Token first ) { + if ( descr != null && first != null ) { + descr.setLocation( first.getLine(), + first.getCharPositionInLine() ); + descr.setStartCharacter( ((CommonToken) first).getStartIndex() ); + } + } + + void setEnd( BaseDescr descr ) { + Token last = input.LT( -1 ); + if ( descr != null && last != null ) { + int endLocation = last.getText() != null ? last.getCharPositionInLine() + last.getText().length() - 1 : last.getCharPositionInLine(); + descr.setEndCharacter( ((CommonToken) last).getStopIndex() + 1 ); + descr.setEndLocation( last.getLine(), + endLocation ); + } + } + + void setEnd( DescrBuilder< ? , ? > db ) { + Token last = input.LT( -1 ); + if ( db != null && last != null ) { + int endLocation = last.getText() != null ? last.getCharPositionInLine() + last.getText().length() - 1 : last.getCharPositionInLine(); + db.endCharacter( ((CommonToken) last).getStopIndex() + 1 ).endLocation( last.getLine(), + endLocation ); + } + } + + @SuppressWarnings("unchecked") + public > T start( DescrBuilder< ? , ? > ctxBuilder, + Class clazz, + String param ) { + if ( state.backtracking == 0 ) { + if ( PackageDescrBuilder.class.isAssignableFrom( clazz ) ) { + pushParaphrases( DroolsParaphraseTypes.PACKAGE ); + beginSentence( DroolsSentenceType.PACKAGE ); + setStart( ctxBuilder ); + } else if ( ImportDescrBuilder.class.isAssignableFrom( clazz ) ) { + ImportDescrBuilder imp; + if ( validateLT( 2, + DroolsSoftKeywords.FUNCTION ) || + validateLT( 2, + DroolsSoftKeywords.STATIC ) ) { + imp = ctxBuilder == null ? + DescrFactory.newPackage().newFunctionImport() : + ((PackageDescrBuilder) ctxBuilder).newFunctionImport(); + } else { + imp = ctxBuilder == null ? + DescrFactory.newPackage().newImport() : + ((PackageDescrBuilder) ctxBuilder).newImport(); + } + pushParaphrases( DroolsParaphraseTypes.IMPORT ); + beginSentence( DroolsSentenceType.IMPORT_STATEMENT ); + setStart( imp ); + return (T) imp; + } else if ( UnitDescrBuilder.class.isAssignableFrom( clazz ) ) { + UnitDescrBuilder imp = ctxBuilder == null ? + DescrFactory.newPackage().newUnit() : + ((PackageDescrBuilder) ctxBuilder).newUnit(); + pushParaphrases( DroolsParaphraseTypes.UNIT ); + beginSentence( DroolsSentenceType.UNIT ); + setStart( imp ); + return (T) imp; + } else if ( AccumulateImportDescrBuilder.class.isAssignableFrom( clazz ) ) { + AccumulateImportDescrBuilder imp = ctxBuilder == null ? + DescrFactory.newPackage().newAccumulateImport() : + ((PackageDescrBuilder) ctxBuilder).newAccumulateImport(); + pushParaphrases( DroolsParaphraseTypes.ACCUMULATE_IMPORT ); + beginSentence( DroolsSentenceType.ACCUMULATE_IMPORT_STATEMENT ); + setStart( imp ); + return (T) imp; + } else if ( GlobalDescrBuilder.class.isAssignableFrom( clazz ) ) { + GlobalDescrBuilder global = ctxBuilder == null ? + DescrFactory.newPackage().newGlobal() : + ((PackageDescrBuilder) ctxBuilder).newGlobal(); + pushParaphrases( DroolsParaphraseTypes.GLOBAL ); + beginSentence( DroolsSentenceType.GLOBAL ); + setStart( global ); + return (T) global; + } else if ( DeclareDescrBuilder.class.isAssignableFrom( clazz ) ) { + DeclareDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare() : + ((PackageDescrBuilder) ctxBuilder).newDeclare(); + return (T) declare; + } else if ( TypeDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + TypeDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().type() : + ((DeclareDescrBuilder) ctxBuilder).type(); + pushParaphrases( DroolsParaphraseTypes.TYPE_DECLARE ); + beginSentence( DroolsSentenceType.TYPE_DECLARATION ); + setStart( declare ); + return (T) declare; + } else if ( EnumDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + EnumDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().enumerative() : + ((DeclareDescrBuilder) ctxBuilder).enumerative(); + pushParaphrases( DroolsParaphraseTypes.ENUM_DECLARE ); + beginSentence( DroolsSentenceType.ENUM_DECLARATION ); + setStart( declare ); + return (T) declare; + }else if ( EntryPointDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + EntryPointDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().entryPoint() : + ((DeclareDescrBuilder) ctxBuilder).entryPoint(); + pushParaphrases( DroolsParaphraseTypes.ENTRYPOINT_DECLARE ); + beginSentence( DroolsSentenceType.ENTRYPOINT_DECLARATION ); + setStart( declare ); + return (T) declare; + } else if ( WindowDeclarationDescrBuilder.class.isAssignableFrom( clazz ) ) { + WindowDeclarationDescrBuilder declare = ctxBuilder == null ? + DescrFactory.newPackage().newDeclare().window() : + ((DeclareDescrBuilder) ctxBuilder).window(); + pushParaphrases( DroolsParaphraseTypes.WINDOW_DECLARE ); + beginSentence( DroolsSentenceType.WINDOW_DECLARATION ); + setStart( declare ); + return (T) declare; + } else if ( FieldDescrBuilder.class.isAssignableFrom( clazz ) ) { + FieldDescrBuilder field = ((AbstractClassTypeDeclarationBuilder) ctxBuilder).newField( param ); + setStart( field ); + return (T) field; + } else if ( EnumLiteralDescrBuilder.class.isAssignableFrom( clazz ) ) { + EnumLiteralDescrBuilder literal = ((EnumDeclarationDescrBuilder) ctxBuilder).newEnumLiteral( param ); + setStart( literal ); + return (T) literal; + } else if ( FunctionDescrBuilder.class.isAssignableFrom( clazz ) ) { + FunctionDescrBuilder function; + if ( ctxBuilder == null ) { + function = DescrFactory.newPackage().newFunction(); + } else { + PackageDescrBuilder pkg = (PackageDescrBuilder) ctxBuilder; + function = pkg.newFunction().namespace( pkg.getDescr().getName() ); + AttributeDescr attribute = pkg.getDescr().getAttribute( "dialect" ); + if ( attribute != null ) { + function.dialect( attribute.getValue() ); + } + } + pushParaphrases( DroolsParaphraseTypes.FUNCTION ); + beginSentence( DroolsSentenceType.FUNCTION ); + setStart( function ); + return (T) function; + } else if ( RuleDescrBuilder.class.isAssignableFrom( clazz ) ) { + RuleDescrBuilder rule = ctxBuilder == null ? + DescrFactory.newPackage().newRule() : + ((PackageDescrBuilder) ctxBuilder).newRule(); + pushParaphrases( DroolsParaphraseTypes.RULE ); + beginSentence( DroolsSentenceType.RULE ); + setStart( rule ); + return (T) rule; + } else if ( QueryDescrBuilder.class.isAssignableFrom( clazz ) ) { + QueryDescrBuilder query = ctxBuilder == null ? + DescrFactory.newPackage().newQuery() : + ((PackageDescrBuilder) ctxBuilder).newQuery(); + pushParaphrases( DroolsParaphraseTypes.QUERY ); + beginSentence( DroolsSentenceType.QUERY ); + setStart( query ); + return (T) query; + } else if ( AttributeDescrBuilder.class.isAssignableFrom( clazz ) ) { + AttributeDescrBuilder< ? > attribute = ((AttributeSupportBuilder< ? >) ctxBuilder).attribute(param); + setStart( attribute ); + return (T) attribute; + } else if ( EvalDescrBuilder.class.isAssignableFrom( clazz ) ) { + EvalDescrBuilder< ? > eval = ((CEDescrBuilder< ? , ? >) ctxBuilder).eval(); + pushParaphrases( DroolsParaphraseTypes.EVAL ); + beginSentence( DroolsSentenceType.EVAL ); + setStart( eval ); + return (T) eval; + } else if ( ForallDescrBuilder.class.isAssignableFrom( clazz ) ) { + ForallDescrBuilder< ? > forall = ((CEDescrBuilder< ? , ? >) ctxBuilder).forall(); + setStart( forall ); + return (T) forall; + } else if ( CEDescrBuilder.class.isAssignableFrom( clazz ) ) { + setStart( ctxBuilder ); + return (T) ctxBuilder; + } else if ( PatternDescrBuilder.class.isAssignableFrom( clazz ) ) { + PatternDescrBuilder< ? > pattern = ((PatternContainerDescrBuilder< ? , ? >) ctxBuilder).pattern(); + pushParaphrases( DroolsParaphraseTypes.PATTERN ); + setStart( pattern ); + return (T) pattern; + } else if ( CollectDescrBuilder.class.isAssignableFrom( clazz ) ) { + CollectDescrBuilder< ? > collect = ((PatternDescrBuilder< ? >) ctxBuilder).from().collect(); + setStart( collect ); + return (T) collect; + } else if ( GroupByDescrBuilder.class.isAssignableFrom(clazz) ) { + // GroupBy extends Accumulate and thus need to be before it + GroupByDescrBuilder< ? > groupBy = ((PatternDescrBuilder< ? >) ctxBuilder).from().groupBy(); + setStart( groupBy ); + return (T) groupBy; + } else if ( AccumulateDescrBuilder.class.isAssignableFrom( clazz ) ) { + AccumulateDescrBuilder< ? > accumulate = ((PatternDescrBuilder< ? >) ctxBuilder).from().accumulate(); + setStart( accumulate ); + return (T) accumulate; + } else if ( BehaviorDescrBuilder.class.isAssignableFrom( clazz ) ) { + BehaviorDescrBuilder< ? > behavior = ((PatternDescrBuilder< ? >) ctxBuilder).behavior(); + setStart( behavior ); + return (T) behavior; + } else if ( NamedConsequenceDescrBuilder.class.isAssignableFrom( clazz ) ) { + NamedConsequenceDescrBuilder< ? > namedConsequence = ((CEDescrBuilder< ? , ? >) ctxBuilder).namedConsequence(); + setStart( namedConsequence ); + return (T) namedConsequence; + } else if ( ConditionalBranchDescrBuilder.class.isAssignableFrom( clazz ) ) { + ConditionalBranchDescrBuilder< ? > conditionalBranch = ((CEDescrBuilder< ? , ? >) ctxBuilder).conditionalBranch(); + setStart( conditionalBranch ); + return (T) conditionalBranch; + } + } + return null; + } + + @SuppressWarnings("unchecked") + public > T end( Class clazz, + DescrBuilder< ? , ? > builder ) { + if ( state.backtracking == 0 ) { + if ( !(FieldDescrBuilder.class.isAssignableFrom( clazz ) || + AttributeDescrBuilder.class.isAssignableFrom( clazz ) || + CEDescrBuilder.class.isAssignableFrom( clazz ) || + CollectDescrBuilder.class.isAssignableFrom( clazz ) || + AccumulateDescrBuilder.class.isAssignableFrom( clazz ) || + ForallDescrBuilder.class.isAssignableFrom( clazz ) || + BehaviorDescrBuilder.class.isAssignableFrom( clazz ) || + ConditionalBranchDescrBuilder.class.isAssignableFrom( clazz ) || + NamedConsequenceDescrBuilder.class.isAssignableFrom( clazz )) ) { + popParaphrases(); + } + + if (RuleDescrBuilder.class.isAssignableFrom(clazz)) { + RuleDescrBuilder ruleDescrBuilder = (RuleDescrBuilder)builder; + ruleDescrBuilder.end().getDescr().afterRuleAdded(ruleDescrBuilder.getDescr()); + } + + setEnd( builder ); + return (T) builder; + } + return null; + } + + public String[] getStatementKeywords() { + return statementKeywords; + } +} diff --git a/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/ParserStringUtils.java b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/ParserStringUtils.java new file mode 100644 index 00000000000..ef22da2dce3 --- /dev/null +++ b/drools-drl/drools-drl-parser/src/main/java/org/drools/drl10/parser/ParserStringUtils.java @@ -0,0 +1,81 @@ +/** + * 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.drl10.parser; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.misc.Interval; + +/** + * Collection of String utilities used by DRLParser. + * This may be merged in drools-util + */ +public class ParserStringUtils { + + private ParserStringUtils() { + } + + /** + * Strip string delimiters (e.g. "foo" -> foo) + */ + public static String safeStripStringDelimiters(String value) { + if (value != null) { + value = value.trim(); + if (value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) { + value = value.substring(1, value.length() - 1); + } + } + return value; + } + + /** + * Get text from ParserRuleContext's CharStream without trimming whitespace + */ + public static String getTextPreservingWhitespace(ParserRuleContext ctx) { + // Using raw CharStream + int startIndex = ctx.start.getStartIndex(); + int stopIndex = ctx.stop.getStopIndex(); + if (startIndex > stopIndex) { + // no text + return ""; + } + Interval interval = new Interval(startIndex, stopIndex); + return ctx.start.getTokenSource().getInputStream().getText(interval); + } + + /** + * Get text from ParserRuleContext's CharStream without trimming whitespace + * tokenStream is required to get hidden channel token (e.g. whitespace). + * Unlike getTextPreservingWhitespace, this method reflects Lexer normalizeString + */ + public static String getTokenTextPreservingWhitespace(ParserRuleContext ctx, TokenStream tokenStream) { + return tokenStream.getText(ctx.start, ctx.stop); + } + + /** + * Just remove leading "then" + */ + public static String trimThen(String rhs) { + if (rhs.startsWith("then")) { + return rhs.substring("then".length()); + } else { + throw new DRLParserException("rhs has to start with 'then' : rhs = " + rhs); + } + } +} diff --git a/drools-drl/pom.xml b/drools-drl/pom.xml index 91f12f3d758..2fb61bb3006 100644 --- a/drools-drl/pom.xml +++ b/drools-drl/pom.xml @@ -39,6 +39,7 @@ drools-drl-ast drools-drl-extensions drools-drl-parser + drools-drl-parser-tests diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DRLExprParserTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DRLExprParserTest.java index 08eeb33edae..c7357aad4de 100644 --- a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DRLExprParserTest.java +++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DRLExprParserTest.java @@ -25,6 +25,7 @@ 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.junit.After; import org.junit.Before; import org.junit.Test; @@ -42,7 +43,7 @@ public class DRLExprParserTest { @Before public void setUp() throws Exception { new EvaluatorRegistry(); - this.parser = new DrlExprParser(LanguageLevelOption.DRL6); + this.parser = DrlExprParserFactory.getDrlExrParser(LanguageLevelOption.DRL6); } @After diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DescrDumperTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DescrDumperTest.java index 649b43c37fe..5eb52e62830 100644 --- a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DescrDumperTest.java +++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/compiler/lang/DescrDumperTest.java @@ -24,6 +24,7 @@ import org.drools.drl.ast.descr.AtomicExprDescr; import org.drools.drl.ast.descr.BindingDescr; import org.drools.drl.ast.descr.ConstraintConnectiveDescr; +import org.drools.drl.parser.DrlExprParserFactory; import org.drools.mvel.evaluators.MatchesEvaluatorsDefinition; import org.drools.mvel.evaluators.SetEvaluatorsDefinition; import org.junit.Before; @@ -358,7 +359,7 @@ public void testProcessImplicitConstraints() throws Exception { } public ConstraintConnectiveDescr parse( final String constraint ) { - DrlExprParser parser = new DrlExprParser(LanguageLevelOption.DRL6); + DrlExprParser parser = DrlExprParserFactory.getDrlExrParser(LanguageLevelOption.DRL6); ConstraintConnectiveDescr result = parser.parse( constraint ); assertThat(parser.hasErrors()).as(parser.getErrors().toString()).isFalse(); diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/AlphaNodeTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/AlphaNodeTest.java index d4089c34601..6d14f2355c6 100644 --- a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/AlphaNodeTest.java +++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/AlphaNodeTest.java @@ -48,6 +48,8 @@ public static Collection getParameters() { @Test public void testAlpha() { + + String str = "import " + Person.class.getCanonicalName() + "\n" + "rule R1 when\n" + diff --git a/drools-verifier/drools-verifier-drl/src/main/java/org/drools/verifier/visitor/ExprConstraintDescrVisitor.java b/drools-verifier/drools-verifier-drl/src/main/java/org/drools/verifier/visitor/ExprConstraintDescrVisitor.java index ba67c653c3e..6e6ef96a82a 100644 --- a/drools-verifier/drools-verifier-drl/src/main/java/org/drools/verifier/visitor/ExprConstraintDescrVisitor.java +++ b/drools-verifier/drools-verifier-drl/src/main/java/org/drools/verifier/visitor/ExprConstraintDescrVisitor.java @@ -20,6 +20,7 @@ import java.util.List; +import org.drools.drl.parser.DrlExprParserFactory; import org.drools.drl.parser.impl.Operator; import org.drools.drl.parser.DrlExprParser; import org.drools.drl.ast.descr.AtomicExprDescr; @@ -57,7 +58,7 @@ public ExprConstraintDescrVisitor(Pattern pattern, VerifierData data, OrderNumbe public void visit(ExprConstraintDescr descr) { - DrlExprParser drlExprParser = new DrlExprParser(LanguageLevelOption.DRL5); + DrlExprParser drlExprParser = DrlExprParserFactory.getDrlExrParser(LanguageLevelOption.DRL5); ConstraintConnectiveDescr constraintConnectiveDescr = drlExprParser.parse(descr.getExpression()); visit(constraintConnectiveDescr.getDescrs()); diff --git a/kie-internal/src/main/java/org/kie/internal/builder/conf/LanguageLevelOption.java b/kie-internal/src/main/java/org/kie/internal/builder/conf/LanguageLevelOption.java index ca8b96ee91a..86266a0f2d3 100644 --- a/kie-internal/src/main/java/org/kie/internal/builder/conf/LanguageLevelOption.java +++ b/kie-internal/src/main/java/org/kie/internal/builder/conf/LanguageLevelOption.java @@ -25,7 +25,7 @@ */ public enum LanguageLevelOption implements SingleValueRuleBuilderOption { - DRL5(false), DRL6(false), DRL6_STRICT(true); + DRL5(false), DRL6(false), DRL6_STRICT(true), DRL10(false); private final boolean useJavaAnnotations; diff --git a/pom.xml b/pom.xml index 5fae74ed325..40f4d9d7240 100644 --- a/pom.xml +++ b/pom.xml @@ -310,6 +310,28 @@ + + + DRL10 + + + DRL10 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + DRL10 + + + + + +