Посетитель ANTLR AST, который возвращает различные типы данных

#java #types #antlr #abstract-syntax-tree

Вопрос:

Я закончил преобразование CST ANTLR в AST и создал специальный Visitorlt;Tgt; интерфейс, который позволяет мне посещать все мои узлы AST, но основная проблема, с которой я сталкиваюсь, заключается в том, что некоторые посещения должны возвращать разные типы данных, и я не уверен, как это сделать. Например, для простых арифметических операций я хочу вернуть двойник из их соответствующих методов посещения; но для другой строковой операции потребуется, чтобы их соответствующие узлы возвращали строку. Поскольку все мои методы посещения требуют универсального типа T , я попытался создать класс с именем Result и два дочерних класса DoubleResult и StringResult чтобы мой посетитель мог возвращать либо строку, либо двойную во время посещений, но это кажется плохим и полным проверок приведения и ввода. Есть ли лучший способ делать такие вещи, как это ?

Вот пример кода :

 public class ExpressionVisitor implements Visitorlt;Resultgt; {  ...  public Result visit(BinaryExpression node) {  // here the node's left or right can be StringResults  // So i'd have to do instance and type checks  }   public Result visit(StringExpression node) {  // here i'd return a StringResult specifically  } }  

Редактировать: Цель состоит в том, чтобы иметь возможность выполнять строковую арифметику, например, как, например, в python 2*"hello"

Но тогда метод посещения двоичного выражения должен был бы проверить, какой операнд является строкой (левый или правый), а затем большинству других методов посещения необходимо было бы проверить и обработать тип DoubleResult или StringResult соответственно. Есть ли более чистый способ достичь того же самого ?

Ответ №1:

Как вы, несомненно, обнаружили, класс посетителя, который создает для вас ANTLR, является универсальным классом, в котором вам необходимо определить ожидаемый тип возврата и который должен возвращать этот тип:

Есть несколько способов решить вашу проблему. (Это определенно встречается на динамически типизированных языках).

Для простого языка, который я собрал для учебника, я только что определил Value класс, который может содержать любой из динамических типов, которые я разрешил. У меня были геттеры/сеттеры для каждого типа, а также is*() метод для каждого типа. Посетитель моего выражения только что вернул этот динамический тип.

Примечание: перед выполнением я использовал прослушиватель семантической проверки, который использовал подход, основанный на стеке, при котором я извлекал типы выражений из стека (которые были помещены в стек, когда я посещал детей) при выходе из выражения, проверял совместимость типов этих значений, а затем помещал полученный тип в стек (литералы и переменные просто помещали свой тип в стек). Любые проблемы, с которыми я сталкивался, выводили сообщение об ошибке перед выполнением. Таким образом, я знал, что при выполнении типа у меня всегда будет правильный тип. (То же самое можно было сделать во время выполнения; это было просто решение, которое я выбрал.)

Еще один потенциально удобный подход к пониманию того, что, поскольку вы контролируете навигацию по дереву при использовании посетителей, нечего говорить о том, что у вас должен быть один посетитель, которого вы используете для всего дерева. У вас может быть несколько посетителей (каждый со своим типом), и вы можете выбрать, какого посетителя использовать для посещения ваших дочерних узлов. Например, я посетил узлы операторов с Посетителем, который вернул NULL тип, но посетил все узлы выражений с Посетителем, который вернул мой динамический Value тип.

Нет никакой реальной причины, по которой у вас не может быть столько Посетителей, сколько вы хотите, чтобы у каждого был свой тип. Нередко посетители могут быть лицами без гражданства, поэтому вы можете просто повторно использовать их, переключаясь между подходящими типами для посещения каждого ребенка. (Конечно, это предполагает, что с родительского узла вы можете определить, какой тип посетителя следует использовать для посещения каждого дочернего узла.)

Мелкая дрянь… ANTLR4 создает деревья синтаксического анализа (не AST)

Комментарии:

1. Да, я в основном использовал ANTLR для создания дерева синтаксического анализа, а затем построил свой собственный AST, посещая дерево CST/синтаксического анализа ANTLR. Поэтому, если я не понял, мои варианты: 1. используйте класс a struct -like, который содержит все возможные типы данных (а также имеет посетителя семантического анализатора для проверки типов). 2. или использовать столько посетителей, сколько типов данных я хотел бы обработать (если я смогу определить, какого посетителя использовать на основе родительского правила) ? Позвольте мне попробовать взвесить эти варианты и посмотреть, сработает ли это для меня. Я изо всех сил пытался найти для этого какие-либо хорошие материалы, поэтому любая ссылка будет очень признательна!

2. На этом этапе вы проходите мимо роли ANTLR в создании вашего языка. Тем не менее, он предоставляет классы Слушателя и посетителя, чтобы упростить использование дерева синтаксического анализа. Что касается хорошего чтения, Шаблоны реализации языка (от прагматичных программистов) написаны «Парнем из ANTLR» Терренсом Парром, так что это хороший «следующий шаг» (просто имейте в виду, что конкретные части ANTLR немного устарели).