Skip to content

Commit

Permalink
Enhance test/grammar coverage. function import, global, exists, not (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
tkobayas authored and rgdoliveira committed Oct 24, 2024
1 parent 919cb1a commit a6cc3aa
Show file tree
Hide file tree
Showing 9 changed files with 462 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ WHEN : 'when';
THEN : 'then';
END : 'end';

EXISTS : 'exists';
NOT : 'not';
IN : 'in';
FROM : 'from';

SALIENCE : 'salience';
ENABLED : 'enabled';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,19 @@ lhsUnary : ( lhsExists namedConsequence?
) SEMI? ;
*/

lhsUnary : lhsPatternBind ;
lhsUnary : (
lhsExists
| lhsNot
| lhsPatternBind
) ;
lhsPatternBind : label? ( LPAREN lhsPattern (OR lhsPattern)* RPAREN | lhsPattern ) ;

/*
lhsPattern : xpathPrimary (OVER patternFilter)? |
( QUESTION? qualifiedIdentifier LPAREN positionalConstraints? constraints? RPAREN (OVER patternFilter)? (FROM patternSource)? ) ;
*/

lhsPattern : QUESTION? objectType=qualifiedName LPAREN positionalConstraints? constraints? RPAREN ;
lhsPattern : QUESTION? objectType=qualifiedName LPAREN (positionalConstraints? constraints? ) RPAREN (FROM patternSource)? ;
positionalConstraints : constraint (COMMA constraint)* SEMI ;
constraints : constraint (COMMA constraint)* ;
constraint : label? ( nestedConstraint | conditionalOrExpression ) ;
Expand All @@ -60,6 +64,34 @@ drlExpression : conditionalExpression ( op=assignmentOperator right=drlExpressio
conditionalExpression : left=conditionalOrExpression ternaryExpression? ;
ternaryExpression : QUESTION ts=drlExpression COLON fs=drlExpression ;

/*
patternSource := FROM
( fromAccumulate
| fromCollect
| fromEntryPoint
| fromWindow
| fromExpression )
*/
patternSource : fromExpression ;
fromExpression : conditionalOrExpression ;

/*
lhsExists := EXISTS
( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or
| LEFT_PAREN lhsOr RIGHT_PAREN
| lhsPatternBind
)
*/
lhsExists : EXISTS lhsPatternBind ;
/*
lhsNot := NOT
( (LEFT_PAREN (or_key|and_key))=> lhsOr // prevents '((' for prefixed and/or
| LEFT_PAREN lhsOr RIGHT_PAREN
| lhsPatternBind
)
*/
lhsNot : NOT lhsPatternBind ;

rhs : blockStatement+ ;

stringId : ( IDENTIFIER | STRING_LITERAL ) ;
Expand All @@ -76,7 +108,7 @@ drlAnnotation : AT name=qualifiedName drlArguments? ;

attributes : attribute ( COMMA? attribute )* ;
attribute : ( 'salience' DECIMAL_LITERAL )
| ( 'enabled' | 'no-loop' | 'auto-focus' | 'lock-on-active' | 'refract' | 'direct' ) BOOLEAN?
| ( 'enabled' | 'no-loop' | 'auto-focus' | 'lock-on-active' | 'refract' | 'direct' ) BOOL_LITERAL?
| ( 'agenda-group' | 'activation-group' | 'ruleflow-group' | 'date-effective' | 'date-expires' | 'dialect' ) STRING_LITERAL
| 'calendars' STRING_LITERAL ( COMMA STRING_LITERAL )*
| 'timer' ( DECIMAL_LITERAL | TEXT )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
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;

public class DRLParserHelper {
Expand Down Expand Up @@ -46,4 +49,27 @@ else if (token.getLine() == row && start < col && stop >= col)
}
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.antlr.v4.runtime.tree.ParseTree;
import org.drools.drl.ast.descr.PackageDescr;
Expand Down Expand Up @@ -39,6 +40,10 @@ public List<DRLParserError> getErrors() {
return errors;
}

public List<String> getErrorMessages() {
return errors.stream().map(DRLParserError::getMessage).collect(Collectors.toList());
}

public boolean hasErrors() {
return !errors.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package org.drools.parser;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Objects;
import java.util.stream.Collectors;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.drools.drl.ast.descr.*;

import static org.drools.parser.DRLParserHelper.getTextWithoutErrorNode;
import static org.drools.parser.StringUtils.safeStripStringDelimiters;

public class DRLVisitorImpl extends DRLParserBaseVisitor<Object> {

private final PackageDescr packageDescr = new PackageDescr();

private RuleDescr currentRule;
private PatternDescr currentPattern;
private Deque<ConditionalElementDescr> currentConstructStack = new ArrayDeque<>(); // e.g. whole LHS, not, exist

@Override
public Object visitCompilationUnit(DRLParser.CompilationUnitContext ctx) {
Expand All @@ -21,7 +28,7 @@ public Object visitCompilationUnit(DRLParser.CompilationUnitContext ctx) {

@Override
public Object visitPackagedef(DRLParser.PackagedefContext ctx) {
packageDescr.setName(ctx.name.getText());
packageDescr.setName(getTextWithoutErrorNode(ctx.name));
return super.visitPackagedef(ctx);
}

Expand All @@ -33,20 +40,32 @@ public Object visitUnitdef(DRLParser.UnitdefContext ctx) {

@Override
public Object visitGlobaldef(DRLParser.GlobaldefContext ctx) {
packageDescr.addGlobal(new GlobalDescr(ctx.IDENTIFIER().getText(), ctx.type().getText()));
GlobalDescr globalDescr = new GlobalDescr(ctx.IDENTIFIER().getText(), ctx.type().getText());
populateStartEnd(globalDescr, ctx);
packageDescr.addGlobal(globalDescr);
return super.visitGlobaldef(ctx);
}

@Override
public Object visitImportdef(DRLParser.ImportdefContext ctx) {
String imp = ctx.qualifiedName().getText() + (ctx.MUL() != null ? ".*" : "");
packageDescr.addImport(new ImportDescr(imp));
String target = ctx.qualifiedName().getText() + (ctx.MUL() != null ? ".*" : "");
if (ctx.FUNCTION() != null || ctx.STATIC() != null) {
FunctionImportDescr functionImportDescr = new FunctionImportDescr();
functionImportDescr.setTarget(target);
populateStartEnd(functionImportDescr, ctx);
packageDescr.addFunctionImport(functionImportDescr);
} else {
ImportDescr importDescr = new ImportDescr();
importDescr.setTarget(target);
populateStartEnd(importDescr, ctx);
packageDescr.addImport(importDescr);
}
return super.visitImportdef(ctx);
}

@Override
public Object visitRuledef(DRLParser.RuledefContext ctx) {
currentRule = new RuleDescr(ctx.name.getText());
currentRule = new RuleDescr(safeStripStringDelimiters(ctx.name.getText()));
currentRule.setConsequence(ctx.rhs().getText());
packageDescr.addRule(currentRule);

Expand All @@ -55,14 +74,33 @@ public Object visitRuledef(DRLParser.RuledefContext ctx) {
return result;
}

@Override
public Object visitLhs(DRLParser.LhsContext ctx) {
currentConstructStack.push(currentRule.getLhs());
try {
return super.visitLhs(ctx);
} finally {
currentConstructStack.pop();
}
}

@Override
public Object visitLhsPatternBind(DRLParser.LhsPatternBindContext ctx) {
// TODO: this logic will likely be split to visitLhsPattern
if (ctx.lhsPattern().size() == 1) {
currentPattern = new PatternDescr(ctx.lhsPattern(0).objectType.getText());
DRLParser.LhsPatternContext lhsPatternCtx = ctx.lhsPattern(0);
currentPattern = new PatternDescr(lhsPatternCtx.objectType.getText());
if (ctx.label() != null) {
currentPattern.setIdentifier(ctx.label().IDENTIFIER().getText());
}
currentRule.getLhs().addDescr(currentPattern);
if (lhsPatternCtx.patternSource() != null) {
String expression = lhsPatternCtx.patternSource().getText();
FromDescr from = new FromDescr();
from.setDataSource(new MVELExprDescr(expression));
from.setResource(currentPattern.getResource());
currentPattern.setSource(from);
}
currentConstructStack.peek().addDescr(currentPattern);
}
Object result = super.visitLhsPatternBind(ctx);
currentPattern = null;
Expand All @@ -72,9 +110,11 @@ public Object visitLhsPatternBind(DRLParser.LhsPatternBindContext ctx) {
@Override
public Object visitConstraint(DRLParser.ConstraintContext ctx) {
Object constraint = super.visitConstraint(ctx);
ExprConstraintDescr constr = new ExprConstraintDescr( constraint.toString() );
constr.setType( ExprConstraintDescr.Type.NAMED );
currentPattern.addConstraint( constr );
if (constraint != null) {
ExprConstraintDescr constr = new ExprConstraintDescr(constraint.toString());
constr.setType(ExprConstraintDescr.Type.NAMED);
currentPattern.addConstraint(constr);
}
return null;
}

Expand All @@ -89,7 +129,11 @@ public Object visitExpression(DRLParser.ExpressionContext ctx) {

@Override
public Object visitIdentifier(DRLParser.IdentifierContext ctx) {
return ctx.IDENTIFIER().getText();
if (ctx.IDENTIFIER() == null) {
return "";
} else {
return ctx.IDENTIFIER().getText();
}
}

@Override
Expand Down Expand Up @@ -124,7 +168,38 @@ public Object visitAttribute(DRLParser.AttributeContext ctx) {
return super.visitAttribute(ctx);
}

@Override
public Object visitLhsExists(DRLParser.LhsExistsContext ctx) {
ExistsDescr existsDescr = new ExistsDescr();
currentConstructStack.peek().addDescr(existsDescr);
currentConstructStack.push(existsDescr);
try {
return super.visitLhsExists(ctx);
} finally {
currentConstructStack.pop();
}
}

@Override
public Object visitLhsNot(DRLParser.LhsNotContext ctx) {
NotDescr notDescr = new NotDescr();
currentConstructStack.peek().addDescr(notDescr);
currentConstructStack.push(notDescr);
try {
return super.visitLhsNot(ctx);
} finally {
currentConstructStack.pop();
}
}

public PackageDescr getPackageDescr() {
return packageDescr;
}

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());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.drools.parser;

/**
* will be merged in drools-util
*/
public class StringUtils {

private StringUtils() {
}

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;
}
}
Loading

0 comments on commit a6cc3aa

Please sign in to comment.