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

使用pyparsing解析嵌套函数时,exprStack为何未包含tag函数?

问题:Pyparsing解析嵌套函数时tag元组未加入exprStack

我用pyparsing解析以tag开头的嵌套函数,示例语句为:tag("tag_name_1", value_equal("proxy.province", "value", "return_value", test(1,2)))。运行代码后控制台打印出('tag', 2),但exprStack中却未包含该tag函数元组,当前exprStack内容为['tag_name_1', 'proxy.province', 'value', 'return_value', '1', '2', ('test', 2), ('value_equal', 4)],预期应追加('tag', 2),请问该问题的原因是什么?

相关代码如下:

import pyparsing as pp
from typing import Any, List, Dict


def debug(*args, **kwargs):
    print("debug"+"---"*10)
    print(*args, **kwargs)
    print("debug"+"---"*10)
    return "debug"

def insert_fn_argcount_tuple(t):
    fn = t.pop(0)
    num_args = len(t)
    print((fn, num_args))
    t.insert(0, (fn, num_args))


def push_first(toks):
    exprStack.append(toks[0])

def to_string(toks):
    pass

LPAREN, RPAREN, COMMA = map(pp.Suppress, '(),') 
ident = pp.Word(pp.alphas, pp.alphanums+"_")
integer = pp.Word(pp.nums)
string = (pp.QuotedString("'") | pp.QuotedString('"')).setParseAction()
expr = pp.Forward()
expr_list = pp.delimitedList(pp.Group(expr))
tag_fn = ("tag" + LPAREN + expr_list + RPAREN).setParseAction(insert_fn_argcount_tuple)
fn_call = (ident + LPAREN + expr_list + RPAREN).setParseAction(insert_fn_argcount_tuple)
atom = ( (fn_call | string | integer) | pp.Group(LPAREN+expr+RPAREN)).addParseAction(push_first)
expr <<= atom 

bnf = pp.Forward()
bnf <<= tag_fn

funcs = """tag
value_equal
value_contain
value_match
value
"""

# functions
def tag(tag_name: str, value:Any)->Dict:
    if not tag_name or not value:
        return {}
    return {"tag_name": tag_name, "tag_value": value}

def test(*args, **kwargs):
    return ""

def value_equal(key: str, value, default=None, test=None):
    print(f"---{value_equal}---")
    print(f"key: {key}, value: {value}, defaul: {default}, test:{test}")
    return "value-1"

fns = {
    "tag": tag,
    "value_equal": value_equal,
    "test": test
}
exprStack = []

def evaluate_stack(s:List):
    fn, arg_nums = s.pop(), 0
    if isinstance(fn, tuple):
        fn, arg_nums = fn
    if fn in fns:
        args = reversed([evaluate_stack(s) for _ in range(arg_nums)])
        return fns[fn](*args)
    else:
        return fn

test_str = """tag("tag_name_1", value_equal("proxy.province", "value", "return_value", test(1,2)))"""
p = bnf.parse_string(test_str)
print(f"\nexprStack:{exprStack}\n")
t = evaluate_stack(exprStack)
print(f"tag:{t}")

运行输出:

('test', 2)
('value_equal', 4)
('tag', 2)

exprStack:['tag_name_1', 'proxy.province', 'value', 'return_value', '1', '2', ('test', 2), ('value_equal', 4)]

原因分析

核心问题是**tag_fn规则未绑定push_first解析动作**,导致处理后的('tag',2)元组没有被加入exprStack:

  • 你定义的fn_call规则在执行insert_fn_argcount_tuple生成元组后,会通过atom规则上绑定的push_first把元组推到栈中。
  • 但tag_fn是单独定义的规则,仅绑定了insert_fn_argcount_tuple用于生成元组,却没有绑定push_first来将元组添加到exprStack。
  • 同时你把bnf直接指向tag_fn,完全绕开了atom的解析路径,进一步导致push_first没有被触发。

解决方案

有两种简单的修复方式:

方式一:让tag函数复用fn_call规则

删除单独的tag_fn定义,让tag函数被fn_call匹配,自动触发push_first:

# 删除原tag_fn定义
# tag_fn = ("tag" + LPAREN + expr_list + RPAREN).setParseAction(insert_fn_argcount_tuple)

# 修改bnf规则
bnf <<= expr

方式二:给tag_fn添加push_first动作

如果需要保留单独的tag_fn规则,直接给它绑定push_first动作:

tag_fn = ("tag" + LPAREN + expr_list + RPAREN).setParseAction(insert_fn_argcount_tuple, push_first)

修复后重新运行,exprStack末尾会出现('tag',2),evaluate_stack也能正确调用tag函数返回预期结果。


内容的提问来源于stack exchange,提问作者Burson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 15:25:18