我正在编写一种语言的前端(ocamllex
和ocamlyacc
)。
所以frond-end可以从程序中构建Abstract Syntax Tree (AST)
。然后我们经常写一个漂亮的打印机,它需要一个AST并打印一个程序。如果以后我们只是想编译或分析AST,大部分时间,我们都不需要打印的程序完全与原始程序相同,就白色间距而言。但是,这一次,我想写一个漂亮的打印机,在白色间距方面打印完全与原始程序完全相同的程序。
因此,我的问题是在尝试不修改过多的AST类型时,处理白色间距的最佳做法是什么。我真的不想在AST中为每种类型添加一些(白色空格)。
例如,这就是我目前在lexer.mll
中处理(即跳过)白色间距的方式:
rule token = parse
...
| [' ' '\t'] { token lexbuf } (* skip blanks *)
| eof { EOF }
是否有人知道如何更改此设置以及前端的其他部分以正确考虑白色间距以便以后打印?
答案 0 :(得分:1)
保留每个令牌的源文件位置信息非常常见。例如,此信息可以提供更准确的错误。
执行此操作的最常用方法是保留每个标记的开始和结束行号和列位置,这总共是四个数字。如果很容易从其值和起始位置计算令牌的结束位置,那么可以将其减少到两个数字,但代价是额外的代码复杂性。
Bison具有一些功能,可以简化记住位置对象的簿记工作; ocamlyacc可能包含类似的功能,但我在文档中没有看到任何内容。在任何情况下,都可以直接维护与每个输入令牌关联的位置对象。
使用该信息,只要将标记分隔为空白,就可以轻松地重新创建两个相邻标记之间的空白。评论是另一个问题。
它是一个判断调用,是否比仅仅将前面的空格(甚至注释)附加到每个令牌上更简单。
答案 1 :(得分:0)
您可以使用匹配语句打印不同数量的空格,具体取决于您正在处理的令牌。如果令牌是:id,num,define语句,assign(=)
,我通常会打印1个空格如果令牌是算术表达式,我会先打印一个空格,然后打印一个空格。
如果你正在处理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...
总之,我会通过在递归函数中单独匹配每个标记来处理空格,并打印出适当数量的空格。