Как обойти типизированное абстрактное синтаксическое дерево в компиляторе OCaml


Я пытаюсь сбросить информацию о типе всех идентификаторов в проекте OCaml, в основном это то же самое, что и обход типизированного абстрактного синтаксического дерева (https://github.com/ocaml/ocaml/blob/trunk/typing/typedtree.mli ). поскольку я новичок в кодовой базе компилятора OCaml, я не уверен, предоставляет ли компилятор API, чтобы мы могли легко написать плагин для выполнения этой работы, или нам нужно взломать код компилятора? Кроме того, как это взаимодействует с OCamlbuild? Спасибо за любые советы и подсказки.

2 4

2 ответа:

Предполагая, что вы уже получили типизированный AST типа structure каким-то образом.

Классический способ - это просто написать большую рекурсивную функцию, чтобы самостоятельно пройти АСТ. Но теперь есть модуль TypedtreeIter, доступный в исходном коде компилятора OCaml, и он открыт для compiler-libs. Для простого обхода это очень удобно.

TypedtreeIter предоставляет функтор для построения собственного итератора над типизированными AST. Вот очень простой пример, чтобы напечатать все идентификаторы шаблона с их типы:

(* ocamlfind ocamlc -package compiler-libs.common -c example.ml *)
open Typedtree
open TypedtreeIter

module MyIteratorArgument = struct
  include DefaultIteratorArgument

  let enter_pattern p = match p.pat_desc with
    | Tpat_var (id, _) ->
        Format.printf "@[<2>%s@ : %a@]@."
          (Ident.name id)
          Printtyp.type_scheme p.pat_type
    | _ -> ()
end

module Iterator = TypedtreeIter.MakeIterator(MyIteratorArgument)

Тип модуля TypedtreeIter.IteratorArgument предназначен для указания того, что итератор делает для каждой конструкции AST. У вас есть две точки для выполнения задания: когда обход входит в конструкцию и когда он выходит из нее. Например, для pattern у вас есть enter_pattern и exit_pattern. Вам не нужно беспокоиться о самом рекурсивном обходе: это работа функтора MakeIterator. Давая IteratorArgument модуль, он связывает все enter_* и exit_* рекурсивно и возвращает модуль с кучей итераторы.

Обычно вы интересуетесь только какой-то частью АСТ и хотите пропустить остальные. DefaultIteratorArgument - это модуль, в котором enter_* и exit_* ничего не делают. Ваш модуль IteratorArgument должен включать в себя DefaultIteratorArgument, чтобы наследовать это поведение по умолчанию, а затем реализовать только те части, которые делают что-то особенное.

Если вы хотите не только обойти типизированные AST, но и изменить некоторую их часть, используйте TypedtreeMap вместо TypedtreeIter. Есть небольшой пример TypedtreeMap на https://bitbucket.org/camlspotter/compiler-libs-hack/src/340072a7c14cbce624b98a57bf8c4c6509c40a31/overload/mod.ml?at=default.

(я не использую ocamlbuild, поэтому я не могу помочь этому пункту.)

OCaml предоставляет свой собственный компилятор в виде библиотеки с именем compiler-libs. В нем есть все, что позволяет перейти от конкретного синтаксиса к исполняемому, со всеми промежуточными шагами под вашим контролем, включая typedtree, конечно.

Плохая новость заключается в том, что она не задокументирована. Я бы предложил вам использовать utop или merlin для изучения этой библиотеки.

Вам не нужно ничего особенного делать с ocamlbuild, чтобы использовать compiler-libs, это обычная библиотека.