Python Lark Parser转换AST时恢复行列号的更优方法问询
我正在使用Python Lark Parser的transform函数,尝试恢复语法元素的行号与列号。原生解析树中的Tree对象包含行列号信息,但将初始解析树转换为自定义AST时,所有匹配的符号都会丢失该信息。
以解析简单整数表达式的语法片段为例:
binary_int_expr: _int_expr binary_op _int_expr binary_op: "+" -> binary_add | "-" -> binary_sub | "*" -> binary_mult | "%" -> binary_mod
当我用如下处理函数时:
def binary_add(self, value): ....
参数value仅为[]。使用!binary_add可得到[+],但对应的Tree对象已消失。
我目前的解决方式是添加额外的定位规则:
binary_int_expr: _int_expr binary_op _int_expr binary_op: binary_add_loc -> binary_add | binary_sub_loc -> binary_sub | binary_mult_loc -> binary_mult | binary_mod_loc -> binary_mod binary_add_loc: "+" binary_sub_loc: "-" binary_mult_loc: "*" binary_mod_loc: "%"
对应的转换规则如下:
def Loc..... # 提取行号和列号信息 def binary_int_expr(self, value): (loc,op) = value[1] return BinaryOp(loc, op, value[0], value[2]) def binary_add(self, value): return (Loc(value[0]), BinOps.Add) def binary_sub(self, value): return (Loc(value[0]), BinOps.Sub) def binary_mult(self, value): return (Loc(value[0]), BinOps.Mult) def binary_mod(self, value): return (Loc(value[0]), BinOps.Mod)
此时binary_add被调用时,value参数包含binary_add_loc返回的Tree对象,可从中提取行列号,但这种方式会产生大量冗余的定位规则。
我尝试过多种语法规则写法:
_binary_add_loc (内联) binary_op: "+" -> binary_add !binary_op: "+" -> binary_add binary_op: !"+" -> binary_add
这些写法要么直接报错,要么仍会省略Tree对象。
理想的方式是让字面量以Tree对象形式返回,例如:
binary_op: "+" -> binary_add
调用binary_add时传入Tree对象,请问是否存在这样的实现方式?
不需要添加冗余的定位规则,Lark Transformer的处理方法中可以直接通过self.meta属性获取当前节点的位置信息(包括行号、列号),无需额外包装规则。
步骤1:保持原有语法不变
binary_int_expr: _int_expr binary_op _int_expr binary_op: "+" -> binary_add | "-" -> binary_sub | "*" -> binary_mult | "%" -> binary_mod
步骤2:修改Transformer处理函数
在binary_add等方法中,直接通过self.meta提取位置:
# 假设Loc是你定义的位置存储类 def binary_add(self, value): loc = Loc(line=self.meta.line, column=self.meta.column) return (loc, BinOps.Add) def binary_sub(self, value): loc = Loc(line=self.meta.line, column=self.meta.column) return (loc, BinOps.Sub) def binary_mult(self, value): loc = Loc(line=self.meta.line, column=self.meta.column) return (loc, BinOps.Mult) def binary_mod(self, value): loc = Loc(line=self.meta.line, column=self.meta.column) return (loc, BinOps.Mod) def binary_int_expr(self, value): (loc, op) = value[1] return BinaryOp(loc, op, value[0], value[2])
原理说明
当你使用"+" -> binary_add这样的语法规则时,Lark会生成一个名为binary_add的Tree节点,虽然它的子节点列表(即value参数)是空的,但该节点的meta属性会保留原始字面量的位置信息。Transformer的每个处理方法都可以通过self.meta访问当前节点的元数据,其中包含line(行号)、column(列号)等关键定位信息。
这种方式完全避免了冗余的定位规则,直接利用Lark原生的位置跟踪能力实现需求。
内容的提问来源于stack exchange,提问作者Ervan Darnell

