[DROOLS-7306] Implement unification (#41)
* [DROOLS-7306] Implement unification
- Also [DROOLS-7307] Parse attribute agenda-group
* [DROOLS-7308] Parse attribute without value
- Also [DROOLS-7309] Parse attribute with parentheses
diff --git a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4 b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4
index cce083a..8aac818 100644
--- a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4
+++ b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLLexer.g4
@@ -124,7 +124,7 @@
/////////////////
HASH : '#';
-UNIFY : ':=' ;
+DRL_UNIFY : ':=' ;
NULL_SAFE_DOT : '!.' ;
QUESTION_DIV : '?/' ;
diff --git a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4 b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4
index 30ba845..80774cd 100644
--- a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4
+++ b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRLParser.g4
@@ -116,7 +116,7 @@
| lhsPatternBind
) SEMI? ;
-lhsPatternBind : label? ( LPAREN lhsPattern (DRL_OR lhsPattern)* RPAREN | lhsPattern ) ;
+lhsPatternBind : (label|unif)? ( LPAREN lhsPattern (DRL_OR lhsPattern)* RPAREN | lhsPattern ) ;
/*
lhsPattern : xpathPrimary (OVER patternFilter)? |
@@ -253,6 +253,7 @@
| 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
@@ -438,12 +439,15 @@
;
attributes : attribute ( COMMA? attribute )* ;
-attribute : ( 'salience' DECIMAL_LITERAL )
- | ( 'enabled' | 'no-loop' | 'auto-focus' | 'lock-on-active' | 'refract' | 'direct' ) BOOL_LITERAL?
- | ( 'agenda-group' | 'activation-group' | 'ruleflow-group' | 'date-effective' | 'date-expires' | 'dialect' ) DRL_STRING_LITERAL
- | 'calendars' DRL_STRING_LITERAL ( COMMA DRL_STRING_LITERAL )*
- | 'timer' ( DECIMAL_LITERAL | TEXT )
- | 'duration' ( DECIMAL_LITERAL | TEXT ) ;
+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
@@ -457,7 +461,7 @@
| LT LT ASSIGN ;
label : IDENTIFIER COLON ;
-unif : IDENTIFIER UNIFY ;
+unif : IDENTIFIER DRL_UNIFY ;
/* extending JavaParser variableInitializer */
drlVariableInitializer
diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java
index 2a09caf..3ca0016 100644
--- a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java
+++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java
@@ -40,6 +40,7 @@
import org.drools.drl.ast.descr.TypeFieldDescr;
import org.drools.drl.ast.descr.UnitDescr;
import org.drools.drl.ast.descr.WindowDeclarationDescr;
+import org.drools.util.StringUtils;
import static org.drools.parser.DRLParserHelper.getTextWithoutErrorNode;
import static org.drools.parser.ParserStringUtils.getTextPreservingWhitespace;
@@ -95,10 +96,9 @@
packageDescr.addWindowDeclaration((WindowDeclarationDescr) descr);
} else if (descr instanceof AttributeDescr) {
packageDescr.addAttribute((AttributeDescr) descr);
- } else if (descr instanceof RuleDescr) {
+ } else if (descr instanceof RuleDescr) { // QueryDescr extends RuleDescr
packageDescr.addRule((RuleDescr) descr);
- } else if (descr instanceof QueryDescr) {
- packageDescr.addRule((QueryDescr) descr);
+ packageDescr.afterRuleAdded((RuleDescr) descr);
}
});
}
@@ -301,12 +301,75 @@
}
@Override
- public AttributeDescr visitAttribute(DRLParser.AttributeContext ctx) {
- AttributeDescr attributeDescr = new AttributeDescr(ctx.getChild(0).getText());
- if (ctx.getChildCount() > 1) {
- // TODO : will likely split visitAttribute methods using labels (e.g. #stringAttribute)
- String value = unescapeJava(safeStripStringDelimiters(ctx.getChild(1).getText()));
- attributeDescr.setValue(value);
+ 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<String> 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<String> 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;
}
@@ -338,6 +401,9 @@
.orElseThrow(() -> new IllegalStateException("lhsPatternBind must have at least one lhsPattern : " + ctx.getText()));
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;
}
diff --git a/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java
index 9e6a976..50e7f82 100644
--- a/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java
+++ b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/MiscDRLParserTest.java
@@ -1651,7 +1651,6 @@
assertThat(pkg.getName()).isEqualTo("foo.bar");
}
- @Disabled("Priority : High | Parse attribute without value => true")
@Test
public void parse_Attributes() throws Exception {
final RuleDescr rule = parseAndGetFirstRuleDescrFromFile(
@@ -1731,7 +1730,6 @@
}
- @Disabled("Priority : High | Parse attribute without value => true")
@Test
public void parse_AttributeRefract() throws Exception {
final String source = "rule Test refract when Person() then end";
@@ -1751,7 +1749,6 @@
}
- @Disabled("Priority : High | Parse attribute with parentheses")
@Test
public void parse_EnabledExpression() throws Exception {
final RuleDescr rule = parseAndGetFirstRuleDescrFromFile(
@@ -1775,7 +1772,6 @@
assertThat(at.getValue()).isEqualTo("true");
}
- @Disabled("Priority : High | Parse attribute with parentheses")
@Test
public void parse_DurationExpression() throws Exception {
final RuleDescr rule = parseAndGetFirstRuleDescrFromFile(
@@ -1795,7 +1791,6 @@
assertThat(at.getValue()).isEqualTo("true");
}
- @Disabled("Priority : Mid | Parse calendar attribute")
@Test
public void parse_Calendars() throws Exception {
final RuleDescr rule = parseAndGetFirstRuleDescrFromFile(
@@ -1815,7 +1810,6 @@
assertThat(at.getValue()).isEqualTo("true");
}
- @Disabled("Priority : Mid | Parse calendar attribute")
@Test
public void parse_Calendars2() throws Exception {
final RuleDescr rule = parseAndGetFirstRuleDescrFromFile(
@@ -1905,7 +1899,6 @@
pat.getConstraint();
}
- @Disabled("Priority : High | Parse attribute agenda-group")
@Test
public void parse_PackageAttributes() throws Exception {
final PackageDescr pkg = parseAndGetPackageDescrFromFile(
@@ -3132,7 +3125,6 @@
}
- @Disabled("Priority : High | Implement unification")
@Test
public void parse_UnificationBinding() throws Exception {
final String text = "rule X when $p := Person( $name := name, $loc : location ) then end";