Грако «код» поколения

Я пытаюсь понять, как можно воссоздать документ, проанализированный парсером, созданным grako .

После того, как я глубоко погрузился в исходный код grako, я считаю, что, наконец, понял, как один возвращается из AST в сгенерированный документ. Может кто-нибудь, пожалуйста, проверьте, что мое следующее понимание правильное, и дайте мне знать, если есть более прямой метод?

  1. Один создает грамматику ПЭГ, которую хочет разбор. Грако создает на нем класс парсера и класс семантики.
  2. Один создает (вручную) модуль python, содержащий (более или менее) отдельный класс (подкласс grako.model.Node ) для каждого правила в своей грамматике. Каждый класс должен иметь хотя бы конструктор с параметрами для каждого именованного элемента в соответствующем правиле и хранить его значения в свойстве класса.
  3. Один из подклассов (вручную) сгенерированный семантический класс для замены аш для каждого правила соответствующим классом, созданным на шаге 2.
  4. Один создает (вручную) модуль python подкласса grako.codegen.ModelRenderer определяющий шаблон для генерации кода для (более или менее) каждого правила в своей грамматике.
  5. Один из них grako.codegen.CodeGenerator().render(...) AST, состоящий из подклассов Node и модуля python, содержащего шаблоны grako.codegen.CodeGenerator().render(...) для создания вывода.

Может ли это быть правильно? Это не кажется интуитивным вообще.

  • Почему можно было бы предпринять значительные усилия на шаге 2 и 3, чтобы не делать ничего, кроме хранения информации, которая уже содержится в AST?
  • В чем преимущество этого подхода, а не работа непосредственно из АСТ?
  • Есть ли способ автоматизировать или обойти шаги 2 и 3, если только нужно воссоздать документ в оригинальной грамматике?
  • Учитывая определение грамматики ПЭГ, теоретически возможно автоматически создать «генератор генератора кода» так же, как создать «генератор парсера»?

Если вы посмотрите, как сам Grako анализирует грамматики, вы заметите, что классы 2-го этапа создаются синтетически ModelBuilderSemantics потомка ModelBuilderSemantics :

 # from grako/semantics.py class GrakoSemantics(ModelBuilderSemantics): def __init__(self, grammar_name): super(GrakoSemantics, self).__init__( baseType=grammars.Model, types=grammars.Model.classes() ) self.grammar_name = grammar_name self.rules = OrderedDict() ... 

Классы синтезируются, если они не присутствуют в type types= paramenter. Все, что требуется ModelBuilderSemantics состоит в том, что каждое правило грамматики содержит параметр, который дает имя класса для соответствующего Node :

 module::Module = .... ; 

или,

 module(Module) = ... ; 

Шаг 3 неизбежен, потому что перевод должен быть указан «где-то». Способ Grako позволяет использовать шаблоны str указанные в CodeGenerator , с диспетчеризацией, выполняемой CodeGenerator , что является моим предпочтительным способом перевода. Но я использую grako.model.DepthFirstNodeWalker когда мне просто нужно извлечь информацию из модели, например, при создании таблицы символов или вычислений.

Шаг 3 не может быть автоматизирован, поскольку сопоставление семантики исходного языка с семантикой целевого языка требует умственной силы, даже если исходный и целевой объекты одинаковы.

Можно также обойтись с JSON-подобной структурой Python, которая parse() или grako.model.Node.asjson() генерирует (AST), как вы предлагаете, но код обработки будет заполнен if-then-elseif различать один словарь от другого или один список от другого. С моделями каждый dict в иерархии имеет класс Python как тип.

В конце концов, Грако не накладывает способ создать модель разобранного текста и не перевести его на что-то другое. В своей основной форме Grako предоставляет только синтаксическое дерево (CST) или абстрактное дерево синтаксиса (AST), если наименования элементов используются с умом. Все остальное производится конкретным классом семантики, который может быть любым желаемым.