diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserException.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserException.java new file mode 100644 index 00000000000..6bc5812a78f --- /dev/null +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserException.java @@ -0,0 +1,12 @@ +package org.drools.parser; + +public class DRLParserException extends RuntimeException { + + public DRLParserException() { + super(); + } + + public DRLParserException(String message) { + super(message); + } +} 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 index feabbe39207..9d42bf09641 100644 --- 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 @@ -3,7 +3,6 @@ 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; @@ -12,11 +11,14 @@ public class DRLParserHelper { + private DRLParserHelper() { + } + public static PackageDescr parse(String drl) { - return parseTree2PackageDescr(createParseTree(drl)); + return compilationUnitContext2PackageDescr(createParseTree(drl)); } - public static ParseTree createParseTree(String drl) { + public static DRLParser.CompilationUnitContext createParseTree(String drl) { return createDrlParser(drl).compilationUnit(); } @@ -24,28 +26,31 @@ public static DRLParser createDrlParser(String drl) { CharStream inputStream = CharStreams.fromString(drl); DRLLexer drlLexer = new DRLLexer(inputStream); CommonTokenStream commonTokenStream = new CommonTokenStream(drlLexer); - DRLParser drlParser = new DRLParser(commonTokenStream); - return drlParser; + return new DRLParser(commonTokenStream); } - public static PackageDescr parseTree2PackageDescr(ParseTree parseTree) { + public static PackageDescr compilationUnitContext2PackageDescr(DRLParser.CompilationUnitContext ctx) { DRLVisitorImpl visitor = new DRLVisitorImpl(); - visitor.visit(parseTree); - return visitor.getPackageDescr(); + Object descr = visitor.visit(ctx); + if (descr instanceof PackageDescr) { + return (PackageDescr) descr; + } else { + throw new DRLParserException("CompilationUnitContext should produce PackageDescr. descr = " + descr.getClass()); + } } - 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) + if (token.getLine() > row) { return token.getTokenIndex() - 1; - else if (token.getLine() == row && start >= col) + } else if (token.getLine() == row && start >= col) { return token.getTokenIndex() == 0 ? 0 : token.getTokenIndex() - 1; - else if (token.getLine() == row && start < col && stop >= col) + } else if (token.getLine() == row && start < col && stop >= col) { return token.getTokenIndex(); + } } return null; } diff --git a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java index 251c918190a..7bfdcd89044 100644 --- a/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java +++ b/drools-drl/drools-drl10-parser/src/main/java/org/drools/parser/DRLParserWrapper.java @@ -4,31 +4,29 @@ import java.util.List; import java.util.stream.Collectors; -import org.antlr.v4.runtime.tree.ParseTree; import org.drools.drl.ast.descr.PackageDescr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.drools.parser.DRLParserHelper.compilationUnitContext2PackageDescr; + public class DRLParserWrapper { private static final Logger LOGGER = LoggerFactory.getLogger(DRLParserWrapper.class); private final List errors = new ArrayList<>(); - public DRLParserWrapper() { - } - public PackageDescr parse(String drl) { DRLParser drlParser = DRLParserHelper.createDrlParser(drl); DRLErrorListener errorListener = new DRLErrorListener(); drlParser.addErrorListener(errorListener); - ParseTree parseTree = drlParser.compilationUnit(); + DRLParser.CompilationUnitContext cxt = drlParser.compilationUnit(); errors.addAll(errorListener.getErrors()); try { - return DRLParserHelper.parseTree2PackageDescr(parseTree); + return compilationUnitContext2PackageDescr(cxt); } catch (Exception e) { LOGGER.error("Exception while creating PackageDescr", e); errors.add(new DRLParserError(e)); 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 e9585a9ab10..236478d7fb8 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 @@ -1,19 +1,19 @@ package org.drools.parser; -import java.util.ArrayDeque; -import java.util.Deque; +import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.RuleNode; import org.antlr.v4.runtime.tree.TerminalNode; 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.ConditionalElementDescr; import org.drools.drl.ast.descr.ExistsDescr; import org.drools.drl.ast.descr.ExprConstraintDescr; import org.drools.drl.ast.descr.FromDescr; @@ -35,63 +35,74 @@ public class DRLVisitorImpl extends DRLParserBaseVisitor { - private final PackageDescr packageDescr = new PackageDescr(); - - private RuleDescr currentRule; - private PatternDescr currentPattern; - - private final Deque currentConstructStack = new ArrayDeque<>(); // e.g. whole LHS (= AndDescr), NotDescr, ExistsDescr - @Override - public Object visitCompilationUnit(DRLParser.CompilationUnitContext ctx) { - return super.visitCompilationUnit(ctx); + 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; } - @Override - public Object visitPackagedef(DRLParser.PackagedefContext ctx) { - packageDescr.setName(getTextWithoutErrorNode(ctx.name)); - return super.visitPackagedef(ctx); + private void applyChildrenDescrs(PackageDescr packageDescr, List descrList) { + 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 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 AttributeDescr) { + packageDescr.addAttribute((AttributeDescr) descr); + } else if (descr instanceof RuleDescr) { + packageDescr.addRule((RuleDescr) descr); + } + }); } @Override - public Object visitUnitdef(DRLParser.UnitdefContext ctx) { - packageDescr.setUnit(new UnitDescr(ctx.name.getText())); - return super.visitUnitdef(ctx); + public UnitDescr visitUnitdef(DRLParser.UnitdefContext ctx) { + return new UnitDescr(ctx.name.getText()); } @Override - public Object visitGlobaldef(DRLParser.GlobaldefContext ctx) { + public GlobalDescr visitGlobaldef(DRLParser.GlobaldefContext ctx) { GlobalDescr globalDescr = new GlobalDescr(ctx.drlIdentifier().getText(), ctx.type().getText()); populateStartEnd(globalDescr, ctx); - packageDescr.addGlobal(globalDescr); - return super.visitGlobaldef(ctx); + return globalDescr; } @Override - public Object visitImportdef(DRLParser.ImportdefContext ctx) { + public ImportDescr visitImportdef(DRLParser.ImportdefContext 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); - packageDescr.addFunctionImport(functionImportDescr); + return functionImportDescr; } else { ImportDescr importDescr = new ImportDescr(); importDescr.setTarget(target); populateStartEnd(importDescr, ctx); - packageDescr.addImport(importDescr); + return importDescr; } - return super.visitImportdef(ctx); } @Override - public Object visitFunctiondef(DRLParser.FunctiondefContext ctx) { + public FunctionDescr visitFunctiondef(DRLParser.FunctiondefContext ctx) { FunctionDescr functionDescr = new FunctionDescr(); - functionDescr.setNamespace(packageDescr.getNamespace()); - AttributeDescr dialect = packageDescr.getAttribute("dialect"); - if (dialect != null) { - functionDescr.setDialect(dialect.getValue()); - } if (ctx.typeTypeOrVoid() != null) { functionDescr.setReturnType(ctx.typeTypeOrVoid().getText()); } else { @@ -102,87 +113,146 @@ public Object visitFunctiondef(DRLParser.FunctiondefContext ctx) { DRLParser.FormalParameterListContext formalParameterListContext = formalParametersContext.formalParameterList(); if (formalParameterListContext != null) { List formalParameterContexts = formalParameterListContext.formalParameter(); - formalParameterContexts.stream().forEach(formalParameterContext -> { + formalParameterContexts.forEach(formalParameterContext -> { DRLParser.TypeTypeContext typeTypeContext = formalParameterContext.typeType(); DRLParser.VariableDeclaratorIdContext variableDeclaratorIdContext = formalParameterContext.variableDeclaratorId(); functionDescr.addParameter(typeTypeContext.getText(), variableDeclaratorIdContext.getText()); }); } functionDescr.setBody(ParserStringUtils.getTextPreservingWhitespace(ctx.block())); - packageDescr.addFunction(functionDescr); - return super.visitFunctiondef(ctx); + return functionDescr; + } + + @Override + public RuleDescr visitRuledef(DRLParser.RuledefContext ctx) { + RuleDescr ruleDescr = new RuleDescr(safeStripStringDelimiters(ctx.name.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(ParserStringUtils.getTextPreservingWhitespace(ctx.rhs().consequence())); + } + + return ruleDescr; + } + + private void slimLhsRootDescr(AndDescr root) { + List descrList = new ArrayList<>(root.getDescrs()); + root.getDescrs().clear(); + descrList.forEach(root::addOrMerge); // This slims down nested AndDescr } @Override - public Object visitRuledef(DRLParser.RuledefContext ctx) { - currentRule = new RuleDescr(safeStripStringDelimiters(ctx.name.getText())); - packageDescr.addRule(currentRule); + public AnnotationDescr visitDrlAnnotation(DRLParser.DrlAnnotationContext ctx) { + AnnotationDescr annotationDescr = new AnnotationDescr(ctx.name.getText()); + annotationDescr.setValue(ctx.drlArguments().drlArgument(0).getText()); + return annotationDescr; + } - Object result = super.visitRuledef(ctx); - currentRule = null; - return result; + @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); + } + return attributeDescr; } @Override - public Object visitLhs(DRLParser.LhsContext ctx) { - currentConstructStack.push(currentRule.getLhs()); - try { - return super.visitLhs(ctx); - } finally { - currentConstructStack.pop(); + public List visitLhs(DRLParser.LhsContext ctx) { + if (ctx.lhsExpression() != null) { + return visitLhsExpression(ctx.lhsExpression()); + } else { + return new ArrayList<>(); } } @Override - public Object visitLhsPatternBind(DRLParser.LhsPatternBindContext ctx) { + public List visitLhsExpression(DRLParser.LhsExpressionContext ctx) { + return visitDescrChildren(ctx); + } + + @Override + public BaseDescr visitLhsPatternBind(DRLParser.LhsPatternBindContext ctx) { if (ctx.lhsPattern().size() == 1) { - Object result = super.visitLhsPatternBind(ctx); - ConditionalElementDescr parentDescr = currentConstructStack.peek(); - PatternDescr patternDescr = (PatternDescr) parentDescr.getDescrs().get(parentDescr.getDescrs().size() - 1); - if (ctx.label() != null) { - patternDescr.setIdentifier(ctx.label().IDENTIFIER().getText()); - } - return result; + return getSinglePatternDescr(ctx); } else if (ctx.lhsPattern().size() > 1) { - OrDescr orDescr = new OrDescr(); - currentConstructStack.peek().addDescr(orDescr); - currentConstructStack.push(orDescr); - try { - Object result = super.visitLhsPatternBind(ctx); - List descrs = orDescr.getDescrs(); - for (BaseDescr descr : descrs) { - PatternDescr patternDescr = (PatternDescr) descr; - if (ctx.label() != null) { - patternDescr.setIdentifier(ctx.label().IDENTIFIER().getText()); - } - } - return result; - } finally { - currentConstructStack.pop(); - } + return getOrDescrWithMultiplePatternDescr(ctx); } else { throw new IllegalStateException("ctx.lhsPattern().size() == 0 : " + ctx.getText()); } } + private PatternDescr getSinglePatternDescr(DRLParser.LhsPatternBindContext ctx) { + Optional optPatternDescr = visitFirstDescrChild(ctx); + PatternDescr patternDescr = optPatternDescr.filter(PatternDescr.class::isInstance) + .map(PatternDescr.class::cast) + .orElseThrow(() -> new IllegalStateException("lhsPatternBind must have at least one lhsPattern : " + ctx.getText())); + if (ctx.label() != null) { + patternDescr.setIdentifier(ctx.label().IDENTIFIER().getText()); + } + 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; + } + @Override - public Object visitLhsPattern(DRLParser.LhsPatternContext ctx) { - currentPattern = new PatternDescr(ctx.objectType.getText()); + public PatternDescr visitLhsPattern(DRLParser.LhsPatternContext ctx) { + PatternDescr patternDescr = new PatternDescr(ctx.objectType.getText()); if (ctx.patternSource() != null) { String expression = ctx.patternSource().getText(); FromDescr from = new FromDescr(); from.setDataSource(new MVELExprDescr(expression)); - from.setResource(currentPattern.getResource()); - currentPattern.setSource(from); + from.setResource(patternDescr.getResource()); + patternDescr.setSource(from); } - Object result = super.visitLhsPattern(ctx); - currentConstructStack.peek().addDescr(currentPattern); - currentPattern = null; - return result; + List constraintDescrList = visitConstraints(ctx.constraints()); + constraintDescrList.forEach(patternDescr::addConstraint); + return patternDescr; + } + + @Override + public List visitConstraints(DRLParser.ConstraintsContext ctx) { + List descrList = visitDescrChildren(ctx); + return descrList.stream() + .filter(ExprConstraintDescr.class::isInstance) + .map(ExprConstraintDescr.class::cast) + .collect(Collectors.toList()); } @Override - public Object visitConstraint(DRLParser.ConstraintContext ctx) { + public ExprConstraintDescr visitConstraint(DRLParser.ConstraintContext ctx) { Object constraint = super.visitConstraint(ctx); if (constraint != null) { String constraintString = constraint.toString(); @@ -190,15 +260,15 @@ public Object visitConstraint(DRLParser.ConstraintContext ctx) { if (label != null) { constraintString = label.getText() + constraintString; } - ExprConstraintDescr constr = new ExprConstraintDescr(constraintString); - constr.setType(ExprConstraintDescr.Type.NAMED); - currentPattern.addConstraint(constr); + ExprConstraintDescr constraintDescr = new ExprConstraintDescr(constraintString); + constraintDescr.setType(ExprConstraintDescr.Type.NAMED); + return constraintDescr; } return null; } @Override - public Object visitDrlExpression(DRLParser.DrlExpressionContext ctx) { + public String visitDrlExpression(DRLParser.DrlExpressionContext ctx) { return ctx.children.stream() .map(c -> c instanceof TerminalNode ? c : c.accept(this)) .filter(Objects::nonNull) @@ -207,7 +277,7 @@ public Object visitDrlExpression(DRLParser.DrlExpressionContext ctx) { } @Override - public Object visitDrlPrimary(DRLParser.DrlPrimaryContext ctx) { + public String visitDrlPrimary(DRLParser.DrlPrimaryContext ctx) { return ctx.children.stream() .map(c -> c instanceof TerminalNode ? c : c.accept(this)) .filter(Objects::nonNull) @@ -216,97 +286,69 @@ public Object visitDrlPrimary(DRLParser.DrlPrimaryContext ctx) { } @Override - public Object visitDrlIdentifier(DRLParser.DrlIdentifierContext ctx) { + public String visitDrlIdentifier(DRLParser.DrlIdentifierContext ctx) { return ctx.getText(); } @Override - public Object visitDrlLiteral(DRLParser.DrlLiteralContext ctx) { + public String visitDrlLiteral(DRLParser.DrlLiteralContext ctx) { ParseTree node = ctx; while (true) { if (node instanceof TerminalNode) { return node.toString(); } if (node.getChildCount() != 1) { - return super.visitDrlLiteral(ctx); + return super.visitDrlLiteral(ctx).toString(); } node = node.getChild(0); } } @Override - public Object visitDrlAnnotation(DRLParser.DrlAnnotationContext ctx) { - AnnotationDescr annotationDescr = new AnnotationDescr(ctx.name.getText()); - annotationDescr.setValue(ctx.drlArguments().drlArgument(0).getText()); - currentRule.addAnnotation(annotationDescr); - return super.visitDrlAnnotation(ctx); - } - - @Override - public Object 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); - } - if (currentRule != null) { - currentRule.addAttribute(attributeDescr); - } else { - packageDescr.addAttribute(attributeDescr); - } - return super.visitAttribute(ctx); - } - - @Override - public Object visitLhsExists(DRLParser.LhsExistsContext ctx) { + public ExistsDescr visitLhsExists(DRLParser.LhsExistsContext ctx) { ExistsDescr existsDescr = new ExistsDescr(); - currentConstructStack.peek().addDescr(existsDescr); - currentConstructStack.push(existsDescr); - try { - return super.visitLhsExists(ctx); - } finally { - currentConstructStack.pop(); - } + BaseDescr descr = visitLhsPatternBind(ctx.lhsPatternBind()); + existsDescr.addDescr(descr); + return existsDescr; } @Override - public Object visitLhsNot(DRLParser.LhsNotContext ctx) { + public NotDescr visitLhsNot(DRLParser.LhsNotContext ctx) { NotDescr notDescr = new NotDescr(); - currentConstructStack.peek().addDescr(notDescr); - currentConstructStack.push(notDescr); - try { - return super.visitLhsNot(ctx); - } finally { - currentConstructStack.pop(); - } + BaseDescr descr = visitLhsPatternBind(ctx.lhsPatternBind()); + notDescr.addDescr(descr); + return notDescr; } @Override - public Object visitLhsOr(DRLParser.LhsOrContext ctx) { + public BaseDescr visitLhsOr(DRLParser.LhsOrContext ctx) { if (!ctx.DRL_OR().isEmpty()) { OrDescr orDescr = new OrDescr(); - currentConstructStack.peek().addDescr(orDescr); - currentConstructStack.push(orDescr); - try { - return super.visitLhsOr(ctx); - } finally { - currentConstructStack.pop(); - } + List descrList = visitDescrChildren(ctx); + descrList.forEach(orDescr::addDescr); + return orDescr; } else { - return super.visitLhsOr(ctx); + // No DRL_OR means only one lhsAnd + return visitLhsAnd(ctx.lhsAnd().get(0)); } } @Override - public Object visitRhs(DRLParser.RhsContext ctx) { - currentRule.setConsequenceLocation(ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine()); // location of "then" - currentRule.setConsequence(ParserStringUtils.getTextPreservingWhitespace(ctx.consequence())); - return super.visitChildren(ctx); + public BaseDescr visitLhsAnd(DRLParser.LhsAndContext ctx) { + if (!ctx.DRL_AND().isEmpty()) { + AndDescr andDescr = new AndDescr(); + List descrList = visitDescrChildren(ctx); + descrList.forEach(andDescr::addDescr); + return andDescr; + } else { + // No DRL_AND means only one lhsUnary + return visitLhsUnary(ctx.lhsUnary().get(0)); + } } - public PackageDescr getPackageDescr() { - return packageDescr; + @Override + public BaseDescr visitLhsUnary(DRLParser.LhsUnaryContext ctx) { + return (BaseDescr) visitChildren(ctx); } private void populateStartEnd(BaseDescr descr, ParserRuleContext ctx) { @@ -315,4 +357,31 @@ private void populateStartEnd(BaseDescr descr, ParserRuleContext ctx) { // I will revisit if this is the right approach. descr.setEndCharacter(ctx.getStop().getStopIndex()); } + + 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; + } + + private Optional visitFirstDescrChild(RuleNode node) { + int n = node.getChildCount(); + + for (int i = 0; i < n && this.shouldVisitNextChild(node, null); ++i) { + ParseTree c = node.getChild(i); + Object childResult = c.accept(this); + if (childResult instanceof BaseDescr) { + return Optional.of((BaseDescr) childResult); + } + } + return Optional.empty(); + } }