Как делегировать между визиторами ANTLR4 в двух отдельных грамматиках?

Вопрос или проблема

Я работаю над проектом ANTLR4, где у меня есть две отдельные грамматики:

Transformation.g4: Содержит правила для преобразования объектов в строки с операциями, такими как toString, lowercase и uppercase.

grammar Transformation;

transformation
    : toStringExpr
    | lowercaseExpr
    | uppercaseExpr
    | nestedTransformation
    ;

toStringExpr
    : 'toString' '(' object ')'
    ;
lowercaseExpr
    : 'lowercase' '(' transformation ')'
    ;

uppercaseExpr
    : 'uppercase' '(' transformation ')'
    ;

nestedTransformation
    : '(' transformation ')'
    ;

object
    : IDENTIFIER
    ;

IDENTIFIER
    : [a-zA-Z_][a-zA-Z_0-9]*
    ;

WS
    : [ \t\n\r]+ -> skip ;

Predicate.g4: Содержит правила для предикатов, которые сравнивают преобразования. Например: predicate: transformation == transformation;.

grammar Predicate;


import Transformation;

predicate
    : transformation EQUALS transformation
    ;

EQUALS
    : '==' ;

WS
    : [ \t\n\r]+ -> skip ;

Я хочу скомпилировать эти грамматики в Java и реализовать посетителей для обеих. PredicateVisitorImpl должен полагаться на TransformationVisitorImpl для оценки transformations, но я хочу сохранить грамматики раздельными.

Я попытался сделать это так:

public class PredicateVisitorImpl extends PredicateBaseVisitor<Boolean> {
    private final TransformationVisitor transformationVisitor = new TransformationVisitor();

    @Override
    public Boolean visitTransformationComparison(PredicateParser.TransformationComparisonContext ctx) {
        String left = transformationVisitor.visit(ctx.transformation(0));
        String right = transformationVisitor.visit(ctx.transformation(1));
        
        return left.equals(right);
    }
}

Однако оба вызова transformationVisitor.visit возвращают null, потому что ctx.transformation относится к контексту Predicate, а не к Transformation.

Есть ли чистые способы добиться этого? Я хотел бы сохранить грамматики раздельными. Я рассматривал возможность повторного разбора строки преобразования внутри PredicateVisitorImpl с использованием TransformationParser, но это кажется неэффективным.

Ответ или решение

Ваша задача заключается в реализации делегирования между Visitors в двух отдельных грамматиках ANTLR4, при этом сохраняя грамматики независимыми. Давайте рассмотрим, как это можно сделать.

Шаги для реализации

  1. Определение Compiler: Вам необходимо скомпилировать обе грамматики в Java, что, как правило, осуществляется с помощью команды antlr4 Transformation.g4 Predicate.g4. Убедитесь, что обе грамматики компилируются без ошибок и все необходимые классы создаются.

  2. Создание Visitor для трансформации:
    Вам нужно создать класс TransformationVisitorImpl, который будет реализовывать ваши операции трансформации. Например:

    public class TransformationVisitorImpl extends TransformationBaseVisitor<String> {
       @Override
       public String visitToStringExpr(TransformationParser.ToStringExprContext ctx) {
           return ctx.object().getText();  // Простой пример, накладывающий объект на строку
       }
    
       @Override
       public String visitLowercaseExpr(TransformationParser.LowercaseExprContext ctx) {
           String innerTransformation = visit(ctx.transformation());
           return innerTransformation.toLowerCase();
       }
    
       @Override
       public String visitUppercaseExpr(TransformationParser.UppercaseExprContext ctx) {
           String innerTransformation = visit(ctx.transformation());
           return innerTransformation.toUpperCase();
       }
    
       // и т.д. для других правил
    }
  3. Реализация Visitor для предикатов:
    В вашем классе PredicateVisitorImpl, необходимо создать экземпляр TransformationVisitorImpl, чтобы вызывать его методы для вычисления строковых трансформаций:

    public class PredicateVisitorImpl extends PredicateBaseVisitor<Boolean> {
       private final TransformationVisitorImpl transformationVisitor = new TransformationVisitorImpl();
    
       @Override
       public Boolean visitPredicate(PredicateParser.PredicateContext ctx) {
           String left = transformationVisitor.visit(ctx.transformation(0));
           String right = transformationVisitor.visit(ctx.transformation(1));
    
           return left != null && left.equals(right);
       }
    }

Примечание о контексте

Если ctx.transformation возвращает контекст, относящийся к грамматике Predicate, это может указывать на то, что контекст не настроен на использование TransformationVisitorImpl непосредственно. Чтобы обойти эту проблему, вам нужно убедиться, что PredicateParser корректно импортирует Transformation.g4, как это было указано в вашей грамматике Predicate.g4.

Обратите внимание, что в PredicateVisitorImpl использование метода visit осуществляет шаг через парсер, и если у вас возникает проблема с контекстом, убедитесь, что грамматика настроена правильно и контексты доступны.

Избежание повторного парсинга

Повторный парсинг с использованием TransformationParser для каждой операции может быть неэффективным, как вы упомянули. Вместо этого вашему PredicateVisitorImpl нужно лишь всегда использовать методы вашего TransformationVisitorImpl, что позволит избежать повторного парсинга строк.

Заключение

Таким образом, следуя приведенным выше шагам, вы сможете создать архитектуру делегирования между Visitor в ANTLR4 для управления двумя отдельными грамматиками, не подвергая их изменениям. Весь этот процесс позволит вам сохранять независимость ваших грамматик и их Visitors. Если у вас возникнут дополнительные вопросы или потребуется более детальный анализ, не стесняйтесь задавать их!

Оцените материал
Добавить комментарий

Капча загружается...