You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:18:33