4

Say I have three symbols A, B, C.

In ANTLR, how do I specify that in a sentence, A, B, and C can appear at most once and that they can occur in any order. (For eg., ABC, BCA are both legit)

I tried

(A | B | C)*

knowing it'd only take care of the "any order" part, but couldn't think of a way to say it can only appear at most once.

Edited: I've tried using boolean flags, which worked but seems too hairy - there has to be a simpler way, yes?

myrule;
   {
       boolean aSeen = false;
       boolean bSeen = false;
       boolean cSeen = false;
   }
   :

   (   A { if (aSeen) throw RuntimeException("alraedy seen") else aSeen = true; }
   |   B { if (bSeen) throw RuntimeException("alraedy seen") else bSeen = true; }
   |   C { if (cSeen) throw RuntimeException("alraedy seen") else cSeen = true; }
   )*
   ;
One Two Three
  • 22,327
  • 24
  • 73
  • 114
  • Have you considered simply listing all six permutations? `S := ABC | ACB | BAC | BCA | CAB | CBA` isn't glamorous but it is conceptually straightforward. If ANTLR can work with unrestricted grammars, you could do something like `S := ABC`, `AB := BA`, `AC := CA`, `BC := CB`, and then the last three backwards. The first way requires `n!` productions, the second `n^2`. – Patrick87 Jul 20 '17 at 18:50
  • No, because that'd be no less hairy than my current approach (plus, I'm only listing 3 here for simplification. In reality, it's 5, possibly more) – One Two Three Jul 20 '17 at 18:51
  • 2
    Possible duplicate of [In ANTLR, is there a shortcut notation for expressing alternation of all the permutations of some set of rules?](https://stackoverflow.com/questions/6883992/in-antlr-is-there-a-shortcut-notation-for-expressing-alternation-of-all-the-per) – Tomer Aberbach Jul 20 '17 at 22:24

1 Answers1

2

Since you mention there could be many, many permutations, I would instead opt to keep the grammar dead simple and handle this in the visitor or listener, for example:

public class ValuesListener : ValuesBaseListener
{
    bool isASeen = false;  // "seen flag here"  

    public override void ExitA(ValuesParser.AContext context)
    {
        if (isASeen) // already parsed this once
            <throw exception to stop and inform user>
        else // first time parsing this, so process and set flag so it won't happen again
        {
            isASeen = true;  // never gets reset during this tree walk
            <perform normal processing here>
        }
    }
}

Then your grammar can be something like

myrule: someothertoken myRuleOptions* ;

myRuleOptions
:    A
|    B
|    C
| ...etc. 

My reason? There are ways to do it with predicates as suggested above, but for readability and maintainability by engineers inexperienced in ANTLR4 but very experienced in the target language, I would consider this approach. In my environment, I often hand off ANTLR projects to engineers who simply follow a pattern that I establish, and who don't really understand ANTLR. This is easier for them to follow.

TomServo
  • 7,248
  • 5
  • 30
  • 47