当您需要在模块中生成树并在其他模块中输入相同的树(或几乎相同的树,以某种方式优化)时,使用 ASDL。
为此,您需要具有构造功能(最好使用类型检查器),打印树的功能,以便将其可视化以确保您正确生成了它。
ASDL 将一些树作为输入,其语法几乎与代数数据类型的语法(如在 haskell 或 ml 中)或 BNF 中的语法相同,但更简化,并自动生成所有构造函数,打印以一棵树的简单描述。
例如,如果您有一个词法分析器,它必须生成具有类型的词位。您还需要查看词位的输出流(这是线性形式,因此是一个非常简单的树)。无需编写用于打印、构建词位的函数,而是将它们定义为类似的东西
lexeme=
ID(STRING)
| INT(num_integer)
| FLOAT(num_float)
attributes(int coord_x, int coord_y)
num_integer:
....
num_float:
....
你从你的词法分析器中调用构造函数 ID、INT、FLOAT 等。ASDL 将在您需要的所有函数中转换这种简单的语法,为 AST 构造节点,或打印,或任何您需要的。ASDL 不对生成的代码施加限制。
如果您添加attributes
到一个类型,例如令牌的坐标,这些属性将附加到该类型的每个构造函数的参数中。
由解析器创建的更复杂的树看起来像这样
expr: SUM(expr, expr)
|PRODUCT(expr, expr)
|number
number: num_integer
在这种情况下,asdl 将检查解析器对 SUM(__) 的调用是否将传递给使用 expr 的构造函数之一创建的 sum 节点。num_integer
是在外部定义的,可能是由词法分析器的 asdl 树定义的。
请注意,您不能定义包含正则表达式的构造函数,例如number: [0-9]+
. ASDL 比 EBNF 简单。
这些构造函数将被定义为构建您需要的内容,并且不仅如此,它们还会进行类型检查,以确保您的词法分析器/解析器/代码生成器输出符合 asdl 定义的语言的树。
要很好地理解 ASDL,您需要编写 3-4 个解析器并查看它们生成的代码中的共同点。该公共部分实际上是 ASDL,因此这是特别是解析器输出的抽象。