元 Lua 抽象语法树 |
|
树在下面用一些 Metalua 语法糖书写,这提高了它们的易读性。 反引号符号引入一个 `tag`,即存储在表 `"tag"` 字段中的字符串
`Foo{ 1, 2, 3 }
是 {tag="Foo", 1, 2, 3}
的简写;
`Foo
是 {tag="Foo"}
的简写;
`Foo 123
是 `Foo{ 123 }
的简写,因此是 {tag="Foo", 123 }
; 标签后的表达式必须是文字数字或字符串:接受表会导致歧义。
在使用 Metalua 解释器或编译器时,反引号语法得到支持,可以直接使用。 Metalua 的美化打印助手也尝试在适用时使用反引号语法。
树元素主要分为语句 stat
、表达式 expr
和语句列表 block
。 辅助定义包括函数应用/方法调用 apply
,既是有效的语句,也是表达式,在赋值语句 lhs
左侧可接受的表达式。
block: { stat* } stat: `Do{ stat* } | `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2... | `While{ expr block } -- while e do b end | `Repeat{ block expr } -- repeat b until e | `If{ (expr block)+ block? } -- if e1 then b1 [elseif e2 then b2] ... [else bn] end | `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end | `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end | `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2... | `Localrec{ ident expr } -- only used for 'local function' | `Goto{ <string> } -- goto str | `Label{ <string> } -- ::str:: | `Return{ <expr*> } -- return e1, e2... | `Break -- break | apply expr: `Nil | `Dots | `True | `False | `Number{ <number> } | `String{ <string> } | `Function{ { ident* `Dots? } block } | `Table{ ( `Pair{ expr expr } | expr )* } | `Op{ opid expr expr? } | `Paren{ expr } -- significant to cut multiple values returns | apply | lhs apply: `Call{ expr expr* } | `Invoke{ expr `String{ <string> } expr* } ident: `Id{ <string> } lhs: ident | `Index{ expr expr } opid: 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'pow' | 'concat'| 'eq' | 'lt' | 'le' | 'and' | 'or' | 'not' | 'len'
AST 还嵌入了一些元数据,允许将它们映射到它们的源表示。 这些信息存储在每个树节点的 "lineinfo"
字段中,该字段指向表示它的源字符串中的字符范围,以及出现在该节点之前或之后的任何注释的内容。
Lineinfo 对象有两个字段,"first"
和 "last"
,分别描述子树在源代码中的开始和结束。 例如,由解析 [[return 123]]
生成的子节点 `Number{123}
将具有描述偏移量 8 的 lineinfo.first
和描述偏移量 10 的 lineinfo.last
> mlc = require 'metalua.compiler'.new() > ast = mlc :src_to_ast "return 123 -- comment" > print(ast[1][1].lineinfo) <?|L1|C8-10|K8-10|C> >
Lineinfo 跟踪相对于源字符串/文件开头(上面的“K8-10”)的字符偏移量、行号(上面的 L1;跨越多行的 lineinfo 将读取类似“L1-10”的内容)、列,即行内的偏移量(上面的“C8-10”),以及如果有的话,文件名(上面的“?”标记表示我们没有文件名,因为 AST 来自字符串)。 最终的“|C>”表示节点之后有一个注释; 最初的“<C|”表示节点之前有一个注释。
位置表示标记的结束和标记间空格的开始("last"
字段),或者标记的开始和标记间空格的结束("first"
字段)。标记间空格可能为空。它们也可以包含注释,这些注释可能有助于与周围的标记和 AST 子树关联。
位置与其“对偶”位置相连:标记间空格开始处的 位置在其 "facing"
字段中保留对该标记间空格结束处的 位置的引用,反之亦然,标记间空格结束处的 位置跟踪标记间空格的开始,也在 "facing"
中。标记间空格可以为空,例如在 "2+2"
中,在这种情况下 lineinfo==lineinfo.facing
。
注释也保存在 "comments"
字段中。如果存在,该字段包含一个注释列表,其中包含一个 "lineinfo"
字段,描述第一个和最后一个注释之间的跨度。每个注释由一个字符串列表表示,其中包含一个 "lineinfo"
,描述仅此注释的跨度。连续的 --
注释行被视为一个注释:"-- foo\n-- bar\n"
解析为一个注释,其文本为 "foo\nbar"
,而 "-- foo\n\n-- bar\n"
解析为两个注释 "foo"
和 "bar"
。
例如,如果 f
是函数的 AST,并且我想检索函数之前的注释,我会执行以下操作
f_comment = f.lineinfo.first.comments[1][1]
lineinfo 位置中的信息,即每个 "first"
和 "last"
字段中的信息,保存在以下字段中
"source"
文件名(可选);
"offset"
相对于字符串/文件开头的基于 1 的偏移量;
"line"
基于 1 的行号;
"column"
行内基于 1 的偏移量;
"facing"
标记间空格另一端的 位置。
"comments"
与关联的标记间空格中的注释(可选)。
"id"
一个任意数字,它在给定的标记流中唯一标识一个标记间空格。