1

我正在为一种语言编写前端(by ocamllexand ocamlyacc)。

所以前端可以Abstract Syntax Tree (AST)从一个程序构建一个。然后我们经常写一个漂亮的打印机,它接受一个 AST 并打印一个程序。如果稍后我们只想编译或分析 AST,大多数时候,我们不需要打印的程序与原始程序在空白方面完全相同。但是,这一次,我想编写一个漂亮的打印机,它打印的程序与原始程序完全相同,就白间距而言。

因此,我的问题是在尽量不修改太多 AST 类型的同时处理空白的最佳实践是什么。我真的不想为 AST 中的每种类型添加一些(空格)。

例如,这就是我目前处理(即跳过)空白的方式lexer.mll

rule token = parse
  ...
  | [' ' '\t']       { token lexbuf }     (* skip blanks *)
  | eof              { EOF }

有谁知道如何更改此以及前端的其他部分以正确考虑空白以供以后打印?

4

2 回答 2

1

为每个令牌保留源文件位置信息是很常见的。例如,此信息允许更准确的错误。

最通用的方法是保留每个标记的开始和结束行号和列位置,总共四个数字。如果很容易从令牌的值和起始位置计算令牌的结束位置,则可以减少到两个数字,但代价是额外的代码复杂性。

Bison 有一些功能可以简化记住位置对象的簿记工作;ocamlyacc 可能包含类似的功能,但我在文档中没有看到任何内容。在任何情况下,维护与每个输入令牌相关联的位置对象都是直截了当的。

有了这些信息,就很容易在两个相邻标记之间重新创建空白,只要分隔标记的是空白即可。评论是另一个问题。

这是一个判断调用,这是否比仅在每个标记上附加前面的空格(甚至是注释)更简单。

于 2016-06-01T22:52:58.103 回答
0

您可以使用 match 语句打印不同数量的空格,具体取决于您正在处理的令牌。如果令牌是一个,我通常会在之前打印 1 个空格:id、num、define 语句、assign(=)

如果标记是算术表达式,我会在它之前打印一个空格,在它之后打印一个空格。

如果您正在处理 if 或 while 语句,我会将正文缩进四个空格。

我认为最好的选择是编写一个 pretty_print 函数,例如:

let rec pretty_print pos ast =
   match ast with
    |Some_token -> String.make pos ' '; (* adds 'pos' number of spaces; pos will start off as zero. *)
                   print_string "Some_token";
    |Other_token...

总之,我将通过在递归函数中单独匹配每个标记并打印出适当数量的空格来处理空格。

于 2016-06-03T08:02:58.683 回答