'how to tell which non-terminal is missing within an expansion in ANTLR

oC_RangeLiteral
            :  '*' SP? ( oC_IntegerLiteral SP? )? ( '..' SP? ( oC_IntegerLiteral SP? )? )? ;

Given a parser tree with ctx->oC_IntegerLiteral().size() == 1, How can I tell whether the first one is missing or the second one is missing?

Maybe the question title can be improved based on this concrete question.



Solution 1:[1]

You can label rule elements with name = like this:

oC_RangeLiteral
 :  '*' SP? 
    ( first=oC_IntegerLiteral SP? )? 
    ( '..' SP? ( second=oC_IntegerLiteral SP? )? )? 
 ;

Not sure how to use them with the C++ runtime, but for, say, Java, that'd look like this:

if (ctx.first != null) {
  // 'first' exists
}

if (ctx.second != null) {
  // 'second' exists
}

EDIT

Is it possible to achieve similar without modifying the grammar?

Sure, but that makes it a lot messier. You'd need to figure out if there is a .. among the children of oC_RangeLiteral and then check if the oC_IntegerLiteral comes before or after this .. token. Something like this:

// Only need to check if 1 child is present: in case of 2 or 0 children, it is clear
if (ctx.oC_IntegerLiteral().size() == 1) {

  int indexOfIntegerLiteral = ctx.children.indexOf(ctx.oC_IntegerLiteral().get(0));

  OptionalInt indexOfDotDot = java.util.stream.IntStream
      .range(0, ctx.children.size())
      .filter(i -> ctx.children.get(i).getText().equals(".."))
      .findFirst();

  System.out.printf("indexOfIntegerLiteral=%d\n", indexOfIntegerLiteral);
  System.out.printf("indexOfDotDot=%s\n", indexOfDotDot);

  if (indexOfDotDot.isPresent() && indexOfIntegerLiteral > indexOfDotDot.getAsInt()) {
    // If there is a ".." and the single `oC_IntegerLiteral` comes after it: it's the second one
  }
  else {
    // otherwise, it's the first `oC_IntegerLiteral`
  }
} 

Solution 2:[2]

Another way is this:

Give your terminals own token names, say

STAR: '*';
DOTDOT: '..';

With that your rules becomes:

oC_RangeLiteral
            :  STAR SP? ( oC_IntegerLiteral SP? )? ( DOTDOT SP? ( oC_IntegerLiteral SP? )? )? ;

Now in your code you just check for DOTDOT:

if (ctx->oC_IntegerLiteral().size() == 1) {
    if (ctx->DOTDOT()) {
         // ctx->oC_IntegerLiteral(0) is the second integer literal
    } else {
         // ctx->oC_IntegerLiteral(0) is the first integer literal
    }
}

As a side note: ctx->oC_IntegerLiteral().size() can be 0, because the it's optional in both cases where it appears in the rule. So, just testing the existence of DOTDOT does not tell you there's at least one integer literal.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Mike Lischke