使用ppx\U驱动程序的AST转换(使用ocaml\U current\U AST注册\U转换)有什么好的使用示例吗?

使用ppx\U驱动程序的AST转换(使用ocaml\U current\U AST注册\U转换)有什么好的使用示例吗?,ocaml,abstract-syntax-tree,ppx,Ocaml,Abstract Syntax Tree,Ppx,tl;dr我正试图使用AST\u映射器和ppx\u驱动程序来生成一个源转换二进制文件。我不知道如何在AST\u映射程序docs中获取供ppx\u驱动程序使用的示例。有没有关于如何使用Ppx驱动程序的好例子。使用ocaml\u current\u ast注册转换 我正在尝试移植该示例以与ppx\u驱动程序兼容。具体地说,我希望创建一个二进制文件,它将源代码作为输入,使用这个测试映射器转换源代码,然后输出转换后的源代码。不幸的是,只接受Ocaml AST作为输入(并可能将其作为输出生成)。这是不可取

tl;dr我正试图使用
AST\u映射器
ppx\u驱动程序
来生成一个源转换二进制文件。我不知道如何在
AST\u映射程序
docs中获取供
ppx\u驱动程序使用的示例。有没有关于如何使用Ppx驱动程序的好例子。使用ocaml\u current\u ast注册转换

我正在尝试移植该示例以与
ppx\u驱动程序
兼容。具体地说,我希望创建一个二进制文件,它将源代码作为输入,使用这个测试映射器转换源代码,然后输出转换后的源代码。不幸的是,只接受Ocaml AST作为输入(并可能将其作为输出生成)。这是不可取的,因为我不希望必须使用
-dsource
通过
ocamlc
来运行此命令才能获得输出

以下是我移植此功能的最佳尝试:

测试映射器.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"
let x _ = [%test]
需要注意的几点:

  • 文档中的示例并非开箱即用(在引入
    ppx\u驱动程序之前):
    Const\u int 42
    必须替换为
    Ast\u helper.Const.int 42
  • 出于某种原因,
    test\u映射器
    Parsetree.structure->mapper
    。(我不清楚为什么递归转换需要结构来创建映射器,但没关系。)但是,这种类型不是
    Ppx\u驱动程序所期望的类型。请使用\u ocaml\u current\u ast
    注册转换。因此,我编写了一个草率的包装器
    test\u transformation
    ,以使typechecker满意(这是一个松散的基础,因此在理论上它应该可以工作)
不幸的是,在将其编译为二进制文件后:

ocamlfind ocamlc -predicates ppx_driver -o test_mapper test_mapper.ml -linkpkg -package ppx_driver.runner
并在示例文件上运行它:

sample.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"
let x _ = [%test]
以下是:

./test_mapper sample.ml
我没有看到任何转换发生(示例文件是逐字返回的)。更重要的是,我在代码中留下的日志
ppritast.expression
没有打印任何内容,这表明我的映射程序从未访问过任何内容

我能在野外找到的所有例子都是由Jane Street(她写了
ppx.*
)开源的,似乎要么没有注册它们的转换(也许有一些神奇的检测正在我脑中进行),要么它们使用了什么(这似乎不完整,对我的实际用例也不起作用——但出于这个问题的目的,我正试图让事情保持普遍适用性)


有没有好的例子可以说明如何正确地执行此操作?为什么不使用my transformation?

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

let () = Ppx_driver.standalone ()

要运行重写器并链接到
ppx\u驱动程序
而不是
ppx\u驱动程序。runner
ppx\u驱动程序。runner
在加载驱动程序后立即运行驱动程序,因此在注册转换之前。还要注意,您至少应该指定特定的Ast版本并使用
ppx\u驱动程序。注册转换
不要使用
Ppx\u驱动程序。使用\u current\u ocaml\u ast
注册\u transformation\u,否则使用
Ppx\u驱动程序
没有什么意义,而不是使用
compiler libs
Pparse
模块手工解析。

非常感谢!这就成功了!至于指定ast版本和使用
register_transformation
,你能对此进行扩展吗?正如我在问题末尾提到的,我在研究中看到了这两个方面,但我不确定如何进行(我找不到文档,只是未注释的使用示例).
register\u转换
看起来像是使用了
~规则
,它使用了
Ppx\u核心。ContextFree
,根据其源代码中的注释(在我的答案中链接)是不完整的。您的建议是什么?可以通过打开
AST\u 406
(或任何其他版本)来指定AST版本打开AST相关模块,然后使用OMP提供的转换器来回转换为ppx_驱动程序使用的AST版本之前:
Migrate_parsetree_versions.Convert(AST_406)(AST_404).copy\u mapper
。然后,可以使用
register\u transformation
impl
参数,规则参数更受欢迎,因为规则对每个重写器的操作进行了精确描述,从而更容易编写多个重写器。(另外,请注意,OMP本身有一个驱动程序).Ah,那很方便。我肯定会走那条路,但我的特定应用程序不太适合。
register\u转换
似乎只做
structure->structure
interface->interface
(这对源代码转换很有意义)。但是,我希望做
interface->structure
(为接口创建存根),所以看起来我必须以一种无法很好地进行版本控制的方式深入研究AST内部。谢谢您的帮助!