【发布时间】:2018-01-17 02:04:40
【问题描述】:
我正在编写一个从mli 文件生成ml 存根的工具。我已经完成了有意义的映射(var f : int -> float 到 let f _ = 0.),但是我在推理类和模块的 AST 节点时遇到了一些麻烦,特别是 Pmty_* 和 Pcty_* 节点。这两种类型的节点(给定类型层次结构)似乎与mli 文件最相关。一些琐碎的成员——出现在mli 文件中的-dparsetrees(如Pmty_ident 和Pcty_constr)与与ml 文件最相关的节点有明显的映射关系(同样通过@987654337 的层次结构) @ 和 structure 类型,在上述示例中为 Pmod_ident 和 Pcl_constr)。然而,一些节点没有明显的平行。具体来说,我无法推理:
-
Pmty_with和Pmty_typeof- 似乎永远无法从有效的mli文件中解析这两个;它们只出现在structure上下文中,其中module_type作为其成员之一(我已经检查了我对all of OCaml's parser test files 的怀疑) -
Pcty_signature- 似乎唯一可能发生的情况是在Psig_class_type内(我已经直接映射到Pstr_class_type);我针对 OCaml 测试文件发现了一个Pcty_signature仅在此位置,但在我缺少的mli中是否还有其他有效位置? -
Pcty_arrow- 没有包含这个的测试文件,所以我不能确定它在哪里有效,但我的直觉说这也只在structure内的class_type上下文中出现(或在其他一个中)Pcty_*,在这种情况下,它的转换将由该节点从Pcty_*到Pcl_*的映射作为子节点处理);这是不正确的吗?
我对此相当深入,并不完全了解所有这些高级语言功能和代表它们的 AST 节点发生了什么,所以这里尝试对我的问题进行更简单的解释:
从Parsetree中提取的相关类型:
type module_type_desc =
(* .. snip .. *)
| Pmty_with of module_type * with_constraint list
(* MT with ... *)
| Pmty_typeof of module_expr
(* module type of ME *)
type class_type_desc =
(* .. snip .. *)
| Pcty_signature of class_signature
(* object ... end *)
| Pcty_arrow of arg_label * core_type * class_type
(* T -> CT Simple
~l:T -> CT Labelled l
?l:T -> CT Optional l
*)
- 我相信
Pmty_with和Pmty_typeof只能出现在ml文件中(而不是mli文件)。这个假设正确吗? -
Pcty_signature是否可以作为不是Psig_class_type的子节点的节点出现? -
Pcty_arrow可以出现在有效的mli文件中吗?在哪里?作为什么的孩子?
正如我之前提到的,除了这两个(模块和类)之外,我对自己的处理非常有信心。如果以上内容不清楚,这里有一个带注释的 sn-p 代码转换 Parsetree.signature -> Parsetree.structure,为简洁起见删除了所有非模块/类的东西:
(* Parsetree.signature -> Parsetree.structure *)
let rec stub signature_items =
(* Handles the module_type_desc *)
let rec stub_module_type module_type =
match module_type with
| { pmty_desc = type_; pmty_attributes = attrs; _ } ->
let expr =
match type_ with
| Pmty_ident ident -> Pmod_ident ident
| Pmty_signature signatures -> Pmod_structure (stub signatures)
| Pmty_functor (name, a, b) -> Pmod_functor (name, a, (stub_module_type b))
(* XXX: unclear if these two can occur in an mli *)
(* | Pmty_with (type_, constraints) -> _ TODO *)
(* | Pmty_typeof type_ -> _ TODO *)
| Pmty_extension ext -> Pmod_extension ext
| Pmty_alias name -> Pmod_ident name
in
make_module_expr expr attrs
in
(* The next three functions handles the module_type for single and multiple (rec) modules *)
let stub_module_decl module_decl =
match module_decl with
| { pmd_name = name; pmd_type = type_; pmd_attributes = attrs; _ } ->
make_module_binding name (stub_module_type type_) attrs
in
let stub_module module_ = Pstr_module (stub_module_decl module_)
and stub_modules modules = Pstr_recmodule (List.map stub_module_decl modules)
and stub_include include_ =
match include_ with
| { pincl_mod = module_type; pincl_attributes = attrs; _ } ->
Pstr_include (make_include_decl (stub_module_type module_type) attrs)
in
(* Handles classes (class_type) *)
let stub_classes classes =
(* Handles class_type_desc *)
let stub_class_descr descr =
let rec stub_class class_ =
let stub_class_type type_ =
match type_ with
| Pcty_constr (ident, types) -> Pcl_constr (ident, types)
| Pcty_signature class_ -> (* XXX: Is my below assumption true? *)
failwith "should be covered by Psig_class_type -> Pstr_class_type"
(* XXX: do we ever need to handle Pcty_arrow for mli files? *)
(* | Pcty_arrow (label, a, b) -> _ *)
| Pcty_extension ext -> Pcl_extension ext
| Pcty_open (override, ident, class_) ->
Pcl_open (override, ident, (stub_class class_))
in
match class_ with
| { pcty_desc = type_; pcty_attributes = attrs; _ } ->
make_class_expr (stub_class_type type_) attrs
in
match descr with
| { pci_virt = virt; pci_params = params; pci_name = name;
pci_expr = class_; pci_attributes = attrs } ->
make_class_decl virt params name (stub_class class_) attrs
in
Pstr_class (List.map stub_class_descr classes)
in
let transform_signature signature_item =
match signature_item with
| { psig_desc = signature; _ } ->
let desc =
match signature with
(* ... clip non-module/class stuff ... *)
| Psig_module module_ -> stub_module module_
| Psig_recmodule modules -> stub_modules modules
| Psig_include include_ -> stub_include include_
| Psig_class classes -> stub_classes classes
| Psig_class_type classes -> Pstr_class_type classes
in
make_str desc
in
List.map transform_signature signature_items
不幸的是模块/类的东西是相当复杂的逻辑,所以修剪下来还有很多。创建 *_desc 包装器有很多帮助,这些包装器封装了文件中的位置、属性等,但这些不应该是理解我如何处理模块和类的关键。但为了清楚起见,这里是所有助手的类型:
val make_str : Parsetree.structure_item_desc -> Parsetree.structure_item
val make_module_expr :
Parsetree.module_expr_desc -> Parsetree.attributes -> Parsetree.module_expr
val make_module_binding :
string Asttypes.loc ->
Parsetree.module_expr -> Parsetree.attributes -> Parsetree.module_binding
val make_include_decl :
'a -> Parsetree.attributes -> 'a Parsetree.include_infos
val make_class_decl :
Asttypes.virtual_flag ->
(Parsetree.core_type * Asttypes.variance) list ->
string Asttypes.loc ->
'a -> Parsetree.attributes -> 'a Parsetree.class_infos
val make_class_expr :
Parsetree.class_expr_desc -> Parsetree.attributes -> Parsetree.class_expr
相关文档:
-
parsetree.ml(比 docs 好,因为有一些 cmets)
编辑:顺便说一句,除了阅读有关这些功能的文档(没有产生任何我不知道的 AST 模式)之外,我记得编译后的接口可以从实现ocamlc -i。我在编译器中追踪了与该标志相关联的变量(称为print_types)并找到了它的所有用途,但我并没有立即看出它在哪里调用了派生mli的任何使用代码文件(也许它是通过解析逐步完成的,因为编译会产生一个cmi?)。如果有更多 OCaml 知识或更多编译器经验的人可以指出mli 文件的派生位置,那么对这些模块和类 AST 节点进行逆向工程可能更容易。
编辑 2: 我也知道How to auto-generate stubs from mli file?,但是答案是“手动执行”,这肯定与我正在尝试的冲突! (回答者还声称这样的工具是微不足道的,但在这些 AST 节点上倾注了一段时间后,我不敢苟同!)
【问题讨论】:
标签: ocaml abstract-syntax-tree