使用ppx_driver(register_transformation_using_ocaml_current_ast)进行AST转换的任何良好用法示例?

时间:2018-01-15 02:44:30

标签: ocaml abstract-syntax-tree ppx

tl; dr我正试图使用​​AST_mapperppx_driver创建源转换二进制文件。我无法弄清楚如何在AST_mapper文档中使用ppx_driver文档。有没有关于如何使用Ppx_driver.register_transformation_using_ocaml_current_ast的好例子?

我尝试移植示例AST_mapper in the docs以与ppx_driver兼容。具体来说,我希望创建一个以源为输入的二进制文件,使用此测试映射器转换源,然后输出转换后的源。不幸的是,default main provided by Ast_mapper只接受Ocaml AST作为输入(并且可能将其作为输出产生)。这是不可取的,因为我不想通过ocamlc-dsource来运行此操作来获取输出。

这是我最好的尝试移植它:

test_mapper.ml

open Asttypes
open Parsetree
open Ast_mapper

let test_mapper argv =
  { default_mapper with
    expr = fun mapper expr ->
      Pprintast.expression Format.std_formatter expr;
      match expr with
      | { pexp_desc = Pexp_extension ({ txt = "test" }, PStr [])} ->
        Ast_helper.Exp.constant (Ast_helper.Const.int 42)
      | other -> default_mapper.expr mapper other; }

let test_transformation ast =
  let mapper = (test_mapper ast) in
    mapper.structure mapper ast

let () =
  Ppx_driver.register_transformation_using_ocaml_current_ast
    ~impl:test_transformation
    "test_transformation"

有几点需要注意:

  • 文档中的示例没有开箱即用(在介绍ppx_driver之前):Const_int 42必须替换为Ast_helper.Const.int 42
  • 由于某种原因,test_mapperParsetree.structure -> mapper。 (我不清楚为什么递归变换需要结构来创建映射器,但无论如何。)但是,这种类型并不是Ppx_driver.register_transformation_using_ocaml_current_ast所期望的。所以我写了一个草率的包装器test_transformation来使typechecker高兴(这是基于how Ast_mapper.apply_lazy appears to apply a mapper to an AST松散的,所以理论上应该有效)

不幸的是,在将其编译成二进制文件之后:

ocamlfind ocamlc -predicates ppx_driver -o test_mapper test_mapper.ml -linkpkg -package ppx_driver.runner

在示例文件上运行它:

sample.ml

let x _ = [%test]

以下内容:

./test_mapper sample.ml

我没有看到任何转换发生(示例文件逐字回流)。更重要的是,我在代码中留下的日志记录Pprintast.expression并没有打印任何内容,这表明我的映射器从不访问任何内容。

我在野外找到的所有例子都是Jane Street(他写ppx_*)开源的,似乎要么没有记录他们的转变(也许还有一些?魔法检测正在进行中,或者如果他们they use Ppx_driver.register_transformation ~rules使用Ppx_core.ContextFree(这似乎并不完整并且不会为我工作真实的用例 - 但是出于这个问题的目的,我试图保持一般的适用性。)

有没有很好的例子说明如何正确地做到这一点?为什么没有ppx_driver使用我的转换?

1 个答案:

答案 0 :(得分:1)

如果您想使用单个模块制作独立的重写器,则需要添加

let () = Ppx_driver.standalone ()

运行重写器和链接ppx_driver而非ppx_driver.runnerppx_driver.runner在加载后立即运行驱动程序,因此在注册转换之前。另请注意,您至少应指定一个特定的Ast版本并使用Ppx_driver.register_transformation而不是Ppx_driver.register_transformation_using_current_ocaml_ast,否则使用ppx_driver而不是使用{{1}手动解析}和compiler-libs模块。