Я думаю, что понимаю цель AST, и раньше я построил пару древовидных структур, но не AST. Я в основном сбит с толку, потому что узлы - это текст, а не число, поэтому я не могу придумать хороший способ ввода токена / строки, когда я разбираю некоторый код.
Например, когда я смотрел на диаграммы AST, переменная и ее значение представляли собой листовые узлы со знаком равенства. Это имеет смысл для меня, но как бы я реализовал это? Я думаю, я могу сделать это в каждом конкретном случае, поэтому, когда я натыкаюсь на «=», я использую это как узел и добавляю значение, проанализированное перед «=» в качестве листа. Это просто кажется неправильным, потому что я, вероятно, должен был бы делать случаи для множества вещей, в зависимости от синтаксиса.
И тогда я натолкнулся на другую проблему, как проходит дерево? Должен ли я пройти весь путь вниз по высоте и вернуться назад к узлу, когда я достигну дна, и сделать то же самое для его соседа?
Я видел тонны диаграмм на AST, но я не смог найти довольно простой пример того, что в коде, который, вероятно, помог бы.
источник
Ответы:
Короткий ответ: вы используете стеки. Это хороший пример, но я применю его к AST.
К вашему сведению, это алгоритм Шунтирования-Ярда Эдсгера Дейкстры .
В этом случае я буду использовать стек операторов и стек выражений. Поскольку числа считаются выражениями в большинстве языков, я буду использовать их для хранения.
(Пожалуйста, будьте внимательны с моим кодом. Я знаю, что он не надежный; просто он должен быть псевдокодом.)
В любом случае, как видно из кода, произвольные выражения могут быть операндами других выражений. Если у вас есть следующие данные:
код, который я написал, будет производить этот AST:
И затем, когда вы хотите создать код для этого AST, вы выполняете обход дерева почтовых заказов . Когда вы посещаете листовой узел (с номером), вы генерируете константу, потому что компилятору необходимо знать значения операнда. Когда вы посещаете узел с оператором, вы генерируете соответствующую инструкцию от оператора. Например, оператор «+» дает вам инструкцию «добавить».
источник
Существует значительная разница между тем, как AST обычно изображается в тесте (дерево с числами / переменными в конечных узлах и символами во внутренних узлах), и тем, как оно на самом деле реализовано.
Типичная реализация AST (на языке OO) интенсивно использует полиморфизм. Узлы в AST обычно реализуются с помощью различных классов, все они являются производными от общего
ASTNode
класса. Для каждой синтаксической конструкции в языке, который вы обрабатываете, будет класс для представления этой конструкции в AST, такой какConstantNode
(для констант, таких как0x10
или42
),VariableNode
(для имен переменных),AssignmentNode
(для операций присваивания),ExpressionNode
(для универсального выражения) и т. д.Каждый конкретный тип узла указывает, есть ли у этого узла дочерние элементы, сколько и, возможно, какого типа. У
ConstantNode
детей, как правило, нет детей, у детейAssignmentNode
может быть два, иExpressionBlockNode
у детей может быть любое количество детей.AST создается синтаксическим анализатором, который знает, какую конструкцию он только что проанализировал, поэтому он может создать правильный тип AST Node.
При прохождении AST полиморфизм узлов действительно вступает в игру. База
ASTNode
определяет операции, которые могут быть выполнены на узлах, и каждый конкретный тип узла реализует эти операции определенным образом для этой конкретной языковой конструкции.источник
Построение AST из исходного текста - это просто анализ . Как именно это делается, зависит от разобранного формального языка и реализации. Вы можете использовать генераторы синтаксического анализатора, такие как menhir (для Ocaml) , GNU
bison
withflex
, или ANTLR и т. Д. И т. Д. Это часто делается «вручную» путем кодирования некоторого синтаксического анализатора с рекурсивным спуском (см. Этот ответ, объясняющий почему). Контекстный аспект синтаксического анализа часто выполняется в другом месте (таблицы символов, атрибуты, ....).Однако на практике AST гораздо сложнее, чем вы верите. Например, в компиляторе, таком как GCC, AST хранит информацию о местоположении источника и некоторую информацию о типизации. Прочтите об общих деревьях в GCC и загляните внутрь его gcc / tree.def . Кстати, посмотрите также внутри GCC MELT (который я разработал и внедрил), это актуально для вашего вопроса.
источник
--My comment #1 print("Hello, ".."world.")
преобразуется в `[{" type ":" - "," content ":" Мой комментарий # 1 "}, {" type ":" call "," name ":" print "," arguments ": [[{" type ":" str "," action ":" .. "," content ":" Hello, "}, {" type ":" str "," content ": "Мир." }]]}] `Я думаю, что в JS это намного проще, чем в любом другом языке!Я знаю, что этому вопросу 4+ года, но я чувствую, что должен добавить более подробный ответ.
Абстрактный синтаксис Деревья создаются не иначе, как другие деревья; более верное утверждение в этом случае состоит в том, что узлы синтаксического дерева имеют вариативное количество узлов, КАК НУЖНО.
Примером являются бинарные выражения, такие как
1 + 2
Простое выражение, подобное этому, создающее один корневой узел, содержащий правый и левый узел, который содержит данные о числах. На языке C это будет выглядеть примерно такВаш вопрос был также, как пройти? Обход в этом случае называется посещением узлов . Посещение каждого узла требует использования каждого типа узла, чтобы определить, как оценивать данные каждого узла синтаксиса.
Вот еще один пример этого в C, где я просто печатаю содержимое каждого узла:
Обратите внимание, как функция рекурсивно посещает каждый узел в зависимости от того, с каким типом узла мы имеем дело.
Давайте добавим более сложный пример,
if
конструкцию оператора! Напомним, что операторы if могут также иметь необязательное условие else. Давайте добавим оператор if-else к нашей исходной структуре узла. Помните, что сами операторы if могут также иметь операторы if, поэтому может произойти своего рода рекурсия в нашей системе узлов. Остальные операторы являются необязательными, поэтомуelsestmt
поле может иметь значение NULL, которое рекурсивная функция посетителя может игнорировать.вернувшись в нашу функцию печати посетителя узла
AST_PrintNode
, мы можем разместитьif
конструкцию AST оператора, добавив этот код C:Так просто, как, что! В заключение, синтаксическое дерево - это не что иное, как дерево тегового объединения дерева и самих его данных!
источник