Compiler construction 在OCaml中将行信息添加到我的AST

Compiler construction 在OCaml中将行信息添加到我的AST,compiler-construction,ocaml,metadata,abstract-syntax-tree,Compiler Construction,Ocaml,Metadata,Abstract Syntax Tree,我正在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 )

我正在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
元数据的语法应该是什么?我的代码在
和metadata=”行之后是什么样子的?
基本上,我什么时候需要用元数据信息更新AST。我应该如何构造AST以包含元数据信息?
元数据目前对我来说意味着它的位置,如行号、文件名等。这包含在Lexing.position模块中。

这更多是一个软件设计问题,有一些常见的解决方案。我会尽力把它们都包括进去

最常见的解决方案是将表达式类型包装到一个记录中,该记录除了表达式负载外,还包含一些元数据。元数据的类型可以是抽象的,也可以是具体的,这也是一个品味问题。元数据的抽象类型使得以后更容易扩展它。经典方法是在OCaml编译器中实现的,参考模块,它将告诉您如何在经典OCaml Yacc/Menhir解析器中获取位置信息

一种稍有不同的方法是为树编制索引,并将标识符附加到每个AST节点。然后,您可以使用外部映射(像任何关联容器一样)向AST添加任意元数据。这种方法在BAP中使用。很好的一点是,这种方法可以很容易地将其与hash consing结合起来。它还使得可以与节点关联的属性集易于扩展,而映射的成本现在是部分的。(或记录和映射之间的常见选择)


您可以选择一些特定的节点编号方法(例如DFS编号),而不是直接在节点中存储索引。使用这种方法,您不需要修改AST类型,如果您不控制它,这尤其好。这种方法的一个例子是Janestreet的
parsexp
库中的模块,该模块基于某些遍历(而不是DFS编号)实现紧凑的位置集。不幸的是,它们的实现不够通用,不能与不同的AST一起重用,但这种方法是通用的

这更多的是一个软件设计问题,有一些常见的解决方案。我会尽力把它们都包括进去

最常见的解决方案是将表达式类型包装到一个记录中,该记录除了表达式负载外,还包含一些元数据。元数据的类型可以是抽象的,也可以是具体的,这也是一个品味问题。元数据的抽象类型使得以后更容易扩展它。经典方法是在OCaml编译器中实现的,参考模块,它将告诉您如何在经典OCaml Yacc/Menhir解析器中获取位置信息

一种稍有不同的方法是为树编制索引,并将标识符附加到每个AST节点。然后,您可以使用外部映射(像任何关联容器一样)向AST添加任意元数据。这种方法在BAP中使用。很好的一点是,这种方法可以很容易地将其与hash consing结合起来。它还使得可以与节点关联的属性集易于扩展,而映射的成本现在是部分的。(或记录和映射之间的常见选择)


您可以选择一些特定的节点编号方法(例如DFS编号),而不是直接在节点中存储索引。使用这种方法,您不需要修改AST类型,如果您不控制它,这尤其好。这种方法的一个例子是Janestreet的
parsexp
库中的模块,该模块基于某些遍历(而不是DFS编号)实现紧凑的位置集。不幸的是,它们的实现不够通用,不能与不同的AST一起重用,但这种方法是通用的

您是否已经编写了解析器?如果是,是手动还是使用解析器生成器,哪一个?我强烈建议您也浏览一下Steffen Smolka的样板/示例项目-具体来说,关于您的问题,他编写了一些最小的代码来扩展lexer,并提供位置信息:(您也可以)您已经编写了解析器吗?如果是,是手动还是使用解析器生成器,哪一个?我强烈建议您也浏览一下Steffen Smolka的样板/示例项目-具体来说,关于您的问题,他编写了一些最小的代码来扩展lexer,并提供了位置信息:(您也可以)很抱歉回答得太晚。我很晚才开始实施。谢谢你的回复。你的第一种和第二种方法的轻微组合最终对我有效。我使用Locations模块并修改每个astnode以包含位置信息。这样,在解析程序时,我将行信息和其他元数据信息附加到正在生成的ast节点。这不需要任何外部映射,从而消除了另一个令人头痛的问题。很抱歉,回复太晚。我很晚才开始实施。谢谢你的回复。你的第一种和第二种方法的轻微组合最终对我有效。我使用Locations模块并修改每个astnode以包含位置信息。这样,在解析程序时,我将行信息和其他元数据信息附加到正在生成的ast节点。这不需要任何外部映射,从而消除了另一个额外的麻烦。