Грако "код" генерация

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

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

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

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

  • Зачем предпринимать значительные усилия на шагах 2 и 3, чтобы ничего не делать, кроме как хранить информацию, которая уже содержится в AST?
  • В чем преимущество этого подхода вместо того, чтобы работать напрямую с AST?
  • Есть ли способ автоматизировать или обойти шаги 2 и 3, если нужно только воссоздать документ в исходной грамматике?
  • Учитывая определение грамматики PEG, теоретически возможно ли автоматически создать "генератор генератора кода" так же, как и "генератор парсера"?

1 ответ

Решение

Если вы посмотрите, как Grako анализирует грамматики, вы заметите, что классы шага 2 создаются синтетически 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()
...

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

module::Module = .... ;

или же,

module(Module) = ... ;

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

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

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

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

Другие вопросы по тегам