Как я могу определить правило, которое используется ANTLR 4 для анализа выражения? - PullRequest
0 голосов
/ 06 мая 2020

Я пытаюсь создать класс Java, который строит AST (абстрактное синтаксическое дерево в моей пользовательской иерархии классов) из дерева синтаксического анализа, созданного ANTLR 4. Я делаю это для языка логики первого порядка c https://github.com/antlr/grammars-v4/blob/master/fol/fol.g4

В частности, я ищу правила для formula:

 formula
   : formula bin_connective formula 
   | NOT formula bin_connective formula
   | NOT formula 
   | FORALL LPAREN variable RPAREN formula 
   | EXISTS LPAREN variable RPAREN formula
   | pred_constant LPAREN term (separator term)* RPAREN
   | term EQUAL term
   ;

ANTLR 4 сгенерировал следующий класс для formula, который я представляю здесь только частично (для краткости убираю реализацию - она ​​стандартная, генерируется ANTLR 4 и просто вызывает какие-то технические приемы):

public static class FormulaContext extends ParserRuleContext {
        public TerminalNode NOT() { ... }
        public List<FormulaContext> formula() { ... }
        public FormulaContext formula(int i) { ... }
        public Bin_connectiveContext bin_connective() { ... }
        public TerminalNode FORALL() { ... }
        public TerminalNode LPAREN() { ... }
        public VariableContext variable() { ... }
        public TerminalNode RPAREN() { ... }
        public TerminalNode EXISTS() { ... }
        public Pred_constantContext pred_constant() { ... }
        public List<TermContext> term() { ... }
        public TermContext term(int i) { ...  }
        public List<SeparatorContext> separator() { ... }
        public SeparatorContext separator(int i) { ... }
        public TerminalNode EQUAL() { ... }
        public FormulaContext(ParserRuleContext parent, int invokingState) {
            super(parent, invokingState);
        }
        @Override public int getRuleIndex() { return RULE_formula; }
        @Override
        public void enterRule(ParseTreeListener listener) {
            if ( listener instanceof FOLListener ) ((FOLListener)listener).enterFormula(this);
        }
        @Override
        public void exitRule(ParseTreeListener listener) {
            if ( listener instanceof FOLListener ) ((FOLListener)listener).exitFormula(this);
        }
    } 

Итак - видно: есть ли правило , который содержит NOT, затем есть метод TerminalNode NOT, есть ли один или несколько методов, содержащих одну или несколько формул, тогда есть 2 метода - один возвращает FormulaContext, а другой возвращает ArrayList<FormulaContext>. Более ценных методов нет.

Предположим, у меня есть экземпляр FormulaContext, и я хотел бы изучить его дальше. Что мне делать? Я ожидаю знать правило, в соответствии с которым построен этот экземпляр FormulaContext, а затем я знаю, какие методы (и formula(0); bin_connective(); formula(1); для первого правила) я могу вызвать, и я можно безопасно вызывать эти методы.

Проблема в том, что я не могу найти методы, которые определяют правило, которое используется для построения, является ли это нетерминальным? Конечно, я могу попытаться создать тестовый метод для каждого правила (например, testRule1, testRule2, ...), который вызывает упомянутые методы и проверяет доступность формулы, NOT, bin_connective и всех этих дочерних терминов, а затем может сделать вывод, какое правило используется, а затем, соответственно, продолжить обработку рассматриваемого экземпляра.

Но являются ли такие методы тестирования правильным подходом? Не могу поверить, что все это настолько грубо. Кроме того, такие методы тестирования могут быть автоматически сгенерированы ANTLR 4, для них есть вся информация, но все же - ANTLR не имеет такой функции.

Итак - как лучше всего вывести правило для Non Класс терминала?

1 Ответ

3 голосов
/ 06 мая 2020

Вы можете пометить свои альтернативы с помощью оператора # следующим образом:

formula
    : formula bin_connective formula #BinaryFormula
    | NOT formula                    #Negation
    | ...
    ;

Это создаст классы NegationContext et c. наследуются от FormulaContext. Таким образом, вы можете определить, какая альтернатива была выбрана, исходя из того, какой у вас класс. В посетителях и слушателях теперь можно перегружать visitNegation(NegationContext) et c. для посещения указанного c типа формулы.

...