указание грамматического правила появления в любом порядке, но не более одного раза

Скажем, у меня есть три символа A, B, C.

В ANTLR, как мне указать, что в предложении A, B и C могут появляться не более одного раза и что они могут встречаться в любом порядке. (Например, ABC, BCA оба законны)

Я пытался

(A | B | C)*

зная, что он позаботится только о части «любого порядка», но не мог придумать, как сказать, что это может появиться только один раз.

Отредактировано: я пробовал использовать логические флаги, которые работали, но кажутся слишком сложными - должен быть более простой способ, да?

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; }
   )*
   ;

person One Two Three    schedule 20.07.2017    source источник
comment
Вы не задумывались о том, чтобы просто перечислить все шесть перестановок? S := ABC | ACB | BAC | BCA | CAB | CBA не гламурно, но концептуально прост. Если ANTLR может работать с неограниченной грамматикой, вы можете сделать что-то вроде S := ABC, AB := BA, AC := CA, BC := CB, а затем последние три в обратном порядке. Первый способ требует n! продукции, второй n^2.   -  person Patrick87    schedule 20.07.2017
comment
Нет, потому что это было бы не менее сложно, чем мой нынешний подход (плюс, я перечисляю здесь только 3 для упрощения. На самом деле их 5, а может и больше)   -  person One Two Three    schedule 20.07.2017


Ответы (1)


Поскольку вы упомянули, что может быть много, много перестановок, я бы вместо этого предпочел сохранить простую грамматику и обработать это в посетителе или слушателе, например:

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

Тогда ваша грамматика может быть чем-то вроде

myrule: someothertoken myRuleOptions* ;

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

Моя причина? Есть способы сделать это с помощью предикатов, как предложено выше, но для удобства чтения и поддержки инженерами, не имеющими опыта в ANTLR4, но очень опытными в целевом языке, я бы рассмотрел этот подход. В своей среде я часто передаю проекты ANTLR инженерам, которые просто следуют установленному мною шаблону и не понимают ANTLR. Им легче следовать.

person TomServo    schedule 21.07.2017