This is an automated email from the ASF dual-hosted git repository. mariofusco pushed a commit to branch dev-new-parser in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git
commit 97a13f11460edbb51dbcde0cbff02bf210f270f7 Author: mariofusco <[email protected]> AuthorDate: Fri Jan 14 17:19:41 2022 +0100 add antlr4 based drl parser --- drools-drl/drools-drl10-parser/.gitignore | 17 +++++ drools-drl/drools-drl10-parser/pom.xml | 76 ++++++++++++++++++++++ .../src/main/antlr4/org/drools/parser/DRL.g4 | 64 ++++++++++++++++++ .../java/org/drools/parser/DRLParserHelper.java | 64 ++++++++++++++++++ .../java/org/drools/parser/DRLVisitorImpl.java | 40 ++++++++++++ .../test/java/org/drools/parser/DRLParserTest.java | 59 +++++++++++++++++ 6 files changed, 320 insertions(+) diff --git a/drools-drl/drools-drl10-parser/.gitignore b/drools-drl/drools-drl10-parser/.gitignore new file mode 100644 index 0000000000..372e03f026 --- /dev/null +++ b/drools-drl/drools-drl10-parser/.gitignore @@ -0,0 +1,17 @@ +target/ +local/ + +# Eclipse, Netbeans and IntelliJ files +.* +!.gitignore +!.github +nbproject +*.ipr +*.iws +*.iml + +# generated files +dependency-reduced-pom.xml + +#CI +!.ci diff --git a/drools-drl/drools-drl10-parser/pom.xml b/drools-drl/drools-drl10-parser/pom.xml new file mode 100644 index 0000000000..624344d268 --- /dev/null +++ b/drools-drl/drools-drl10-parser/pom.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.drools</groupId> + <artifactId>drools-lsp</artifactId> + <version>1.0.0-SNAPSHOT</version> + </parent> + + <groupId>org.drools</groupId> + <artifactId>drools-parser</artifactId> + <version>1.0.0-SNAPSHOT</version> + + <name>Drools :: Parser</name> + + <dependencies> + <dependency> + <groupId>org.drools</groupId> + <artifactId>drools-drl-ast</artifactId> + <version>8.16.0-SNAPSHOT</version> + </dependency> + + <!-- External dependencies --> + <dependency> + <groupId>org.antlr</groupId> + <artifactId>antlr4-runtime</artifactId> + <version>${version.org.antlr4}</version> + </dependency> + <dependency> + <groupId>org.antlr</groupId> + <artifactId>antlr4</artifactId> + <version>${version.org.antlr4}</version> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.glassfish</groupId> + <artifactId>javax.json</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- Tests --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${version.junit}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.antlr</groupId> + <artifactId>antlr4-maven-plugin</artifactId> + <version>${version.org.antlr4}</version> + <executions> + <execution> + <goals> + <goal>antlr4</goal> + </goals> + <configuration> + <visitor>true</visitor> + <listener>false</listener> + </configuration> + </execution> + </executions> + </plugin> + + </plugins> + </build> + +</project> diff --git a/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRL.g4 b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRL.g4 new file mode 100644 index 0000000000..e83e84dd5f --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/antlr4/org/drools/parser/DRL.g4 @@ -0,0 +1,64 @@ +grammar DRL; + +// KEYWORDS + +PACKAGE : 'package'; +IMPORT : 'import'; +RULE : 'rule'; +WHEN : 'when'; +THEN : 'then'; +END : 'end'; + +// PARSER + +compilationunit : packagedef? importdef* ruledef* ; + +packagedef : PACKAGE FQNAME SEMICOLON? ; + +importdef : IMPORT FQNAME (DOT STAR)? SEMICOLON? ; + +ruledef : RULE IDENTIFIER WHEN lhs THEN rhs END ; + +lhs : TEXT ; + +rhs : TEXT ; + +// LITERALS + +fragment DIGIT : [0-9] ; +NUMBER : DIGIT+ ([.,] DIGIT+)? ; + +fragment LOWERCASE : [a-z] ; +fragment UPPERCASE : [A-Z] ; +LETTER : (LOWERCASE | UPPERCASE | '_' | '$') ; + +IDENTIFIER : LETTER (LETTER | DIGIT)* ; +FQNAME : IDENTIFIER (DOT IDENTIFIER)* ; + +// SEPARATORS + +LPAREN : '('; +RPAREN : ')'; +LBRACE : '{'; +RBRACE : '}'; +LBRACK : '['; +RBRACK : ']'; +COMMA : ','; +ELIPSIS : '..'; +DOT : '.'; +COLON : ':'; +SEMICOLON : ';'; +STAR : '*'; + +// OPERATORS + +EQUAL : '='; +GT : '>'; +LT : '<'; +LE : '<='; +GE : '>='; +NOTEQUAL : '!='; + +WS : [ \t\r\n\u000C\u00A0]+ -> skip ; + +fragment TEXT : .+ ; diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserHelper.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserHelper.java new file mode 100644 index 0000000000..826517a0c2 --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserHelper.java @@ -0,0 +1,64 @@ +package org.drools.parser; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.drools.drl.ast.descr.PackageDescr; + +public class DRLParserHelper { + + public static PackageDescr parse(String drl) { + return parseTree2PackageDescr(createParseTree(drl)); + } + + public static ParseTree createParseTree(String drl) { + CharStream inputStream = CharStreams.fromString(drl); + DRLLexer drlLexer = new DRLLexer(inputStream); + CommonTokenStream commonTokenStream = new CommonTokenStream(drlLexer); + DRLParser drlParser = new DRLParser(commonTokenStream); + return drlParser.compilationunit(); + } + + public static PackageDescr parseTree2PackageDescr(ParseTree parseTree) { + DRLVisitorImpl visitor = new DRLVisitorImpl(); + visitor.visit(parseTree); + return visitor.getPackageDescr(); + } + + public static ParseTree findNodeAtPosition(ParseTree root, int row, int col) { + for (int i = 0; i < root.getChildCount(); i++) { + ParseTree child = root.getChild(i); + Token stopToken = child instanceof TerminalNode ? ((TerminalNode)child).getSymbol() : ((ParserRuleContext)child).getStop(); + + if (endsAfter(stopToken, row, col)) { + return findNodeAtPosition(child, row, col); + } + } + return root; + } + + private static boolean endsAfter(Token token, int row, int col) { + if (token.getLine() != row) { + return token.getLine() > row; + } + int tokenLength = (token.getStopIndex() - token.getStartIndex()) + 1; + int lastTokenPosition = token.getCharPositionInLine() + tokenLength; + return lastTokenPosition >= col; + } + + public static boolean hasParentOfType(ParseTree leaf, int type) { + return findParentOfType(leaf, type) != null; + } + + public static ParseTree findParentOfType(ParseTree leaf, int type) { + if (leaf == null || (leaf instanceof RuleContext && ((RuleContext) leaf).getRuleIndex() == type)) { + return leaf; + } + return findParentOfType(leaf.getParent(), type); + } +} 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 new file mode 100644 index 0000000000..780076b9ab --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLVisitorImpl.java @@ -0,0 +1,40 @@ +package org.drools.parser; + +import org.drools.drl.ast.descr.ImportDescr; +import org.drools.drl.ast.descr.PackageDescr; +import org.drools.drl.ast.descr.RuleDescr; + +public class DRLVisitorImpl extends DRLBaseVisitor<Object> { + + private final PackageDescr packageDescr = new PackageDescr(); + + @Override + public Object visitCompilationunit(DRLParser.CompilationunitContext ctx) { + return super.visitCompilationunit(ctx); + } + + @Override + public Object visitPackagedef(DRLParser.PackagedefContext ctx) { + packageDescr.setName(ctx.FQNAME().getText()); + return super.visitPackagedef(ctx); + } + + @Override + public Object visitImportdef(DRLParser.ImportdefContext ctx) { + String imp = ctx.FQNAME().getText() + (ctx.STAR() != null ? ".*" : ""); + packageDescr.addImport(new ImportDescr(imp)); + return super.visitImportdef(ctx); + } + + @Override + public Object visitRuledef(DRLParser.RuledefContext ctx) { + RuleDescr rule = new RuleDescr(ctx.IDENTIFIER().getText()); + rule.setConsequence(ctx.rhs().getText()); + packageDescr.addRule(rule); + return super.visitRuledef(ctx); + } + + public PackageDescr getPackageDescr() { + return packageDescr; + } +} diff --git a/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/DRLParserTest.java b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/DRLParserTest.java new file mode 100644 index 0000000000..4e4e27e8d7 --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/test/java/org/drools/parser/DRLParserTest.java @@ -0,0 +1,59 @@ +package org.drools.parser; + +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.tree.ParseTree; +import org.drools.drl.ast.descr.PackageDescr; +import org.junit.Test; + +import static org.drools.parser.DRLParserHelper.createParseTree; +import static org.drools.parser.DRLParserHelper.findNodeAtPosition; +import static org.drools.parser.DRLParserHelper.findParentOfType; +import static org.drools.parser.DRLParserHelper.parse; +import static org.junit.Assert.assertEquals; + +public class DRLParserTest { + + @Test + public void testParse() { + String drl = + "package org.test;\n" + + "import org.test.model.Person;\n" + + "rule TestRule when\n" + + " $p:Person()\n" + + "then\n" + + " System.out.println($p.getName());\n" + + "end\n"; + + PackageDescr packageDescr = parse(drl); + assertEquals("org.test", packageDescr.getName()); + + assertEquals(1, packageDescr.getImports().size()); + assertEquals("org.test.model.Person", packageDescr.getImports().get(0).getTarget()); + + assertEquals(1, packageDescr.getRules().size()); + assertEquals("TestRule", packageDescr.getRules().get(0).getName()); + assertEquals("System.out.println($p.getName());", packageDescr.getRules().get(0).getConsequence()); + } + + @Test + public void testCursorPosition() { + String drl = + "package org.test;\n" + + "import org.test.model.Person;\n" + + "rule TestRule when\n" + + " $p:Person()\n" + + "then\n" + + " System.out.println($p.getName());\n" + + "end\n"; + + int row = 4; + int col = 7; + + ParseTree parseTree = createParseTree(drl); + ParseTree node = findNodeAtPosition(parseTree, row, col); + assertEquals("Person", node.getText()); + ParseTree lhs = findParentOfType(node, DRLParser.RULE_lhs); + assertEquals(DRLParser.RULE_lhs, ((RuleContext) lhs).getRuleIndex()); + assertEquals("$p:Person()", lhs.getText()); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
