我在OCaml中创建了一个编译器,其语法如下:
type expr =
| Cons of const
| Var of string
| List of ( expr list )
| Sum of ( expr * expr )
| Less_than of ( expr * expr )
| Conditional of ( expr * expr * expr )
| Array_literal of ( expr )
| Array_read of ( expr * expr )
AST的节点如下所示:
type 'a astNode =
{
data: 'a;
metadata: Metadata;
}
,元数据模块如下所示:
module Metadata = struct
type loc = Lexing.position
type loc_range = loc * loc
and metadata = ?
end
元数据的语法应该是什么?在and metadata = ?
行后我的代码会是什么样子?
基本上,我何时需要使用元数据信息更新AST。我应该如何构建我的AST以包含元数据信息?
我目前的元数据意味着它的位置,如行号,文件名等。这包含在Lexing.position模块中。
答案 0 :(得分:2)
这更像是一个软件设计问题,并且有一些常见的解决方案。我会尽力掩盖他们。
最常见的解决方案是将表达式类型包装到除表达式有效内容之外还包含一些元数据的记录中。元数据的类型可以是抽象的或具体的,也是品味的问题。元数据的抽象类型使以后更容易扩展。经典方法在OCaml编译器本身中实现,参考Locations模块,它将告诉您如何在经典的OCaml Yacc / Menhir解析器中获取位置信息。
稍微不同的方法是索引树并将标识符附加到每个AST节点。然后,您可以使用外部映射(如任何关联容器)将任意元数据添加到AST。这种方法在BAP Primus Lisp Parser中使用。好处是这种方法可以很容易地将它与哈希值相结合。它还使您可以与节点关联的属性集很容易扩展,而映射现在是部分成本。 (或记录映射之间的共同选择)。
您可以选择一些特定的方法来编号节点(例如,DFS编号),而不是直接在节点中存储索引。使用这种方法,您不需要修改AST类型,如果您不控制它,这尤其好。这种方法的一个示例是来自Janestreet的parsexp
库的Positions模块,该模块基于某些遍历(不是DFS编号)实现紧凑的位置集。不幸的是,它们的实现不够通用,不能重复使用不同的AST,但这种方法很通用。