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

如何用PLY解析含嵌套匹配括号的最大分组令牌?

解决PLY中嵌套括号最大分组的令牌匹配问题

好问题!确实,PLY依赖的标准Python re 模块不支持递归正则,想靠单一正则直接生成嵌套括号的令牌是行不通的。不过咱们有个非常优雅的办法——不用在语法解析的p_*函数里手动写嵌套逻辑,而是靠PLY的**词法状态机(Lex States)**就能搞定:

核心思路:用状态机跟踪括号层级

PLY支持定义专属的词法状态,我们可以利用这个特性:遇到左括号时切换到“括号内部”状态,然后跟踪括号的嵌套层级——每遇到一个左括号就增加层级,遇到右括号就减少层级,当层级回到0时,就说明找到了一个完整的、包含嵌套的括号分组,此时生成对应的令牌即可。

下面是完整的代码示例:

import ply.lex as lex

# 定义词法状态:inparens是专属状态,只会处理括号内的内容
states = (
    ('inparens', 'exclusive'),
)

# 定义需要识别的令牌类型
tokens = (
    'IDENTIFIER',
    'PAREN_GROUP',
)

# -------------------------- 初始状态的规则 --------------------------
# 匹配标识符(比如例子中的'a')
def t_IDENTIFIER(t):
    r'[a-zA-Z_][a-zA-Z0-9_]*'
    return t

# 遇到左括号,切换到括号内部状态,初始化层级和文本缓存
def t_LPAREN(t):
    r'\('
    t.lexer.begin('inparens')
    t.lexer.paren_count = 1
    t.lexer.paren_text = '('

# -------------------------- 括号内部状态的规则 --------------------------
# 遇到左括号,层级+1,把字符加入缓存
def t_inparens_LPAREN(t):
    r'\('
    t.lexer.paren_count += 1
    t.lexer.paren_text += '('

# 遇到右括号,层级-1,把字符加入缓存;层级回到0时生成令牌并切回初始状态
def t_inparens_RPAREN(t):
    r'\)'
    t.lexer.paren_count -= 1
    t.lexer.paren_text += ')'
    if t.lexer.paren_count == 0:
        t.type = 'PAREN_GROUP'
        t.value = t.lexer.paren_text
        t.lexer.begin('INITIAL')
        return t

# 匹配括号内部的其他所有非括号字符(比如|、字母、数字等),直接加入缓存
def t_inparens_OTHER(t):
    r'[^()]+'
    t.lexer.paren_text += t.value

# -------------------------- 通用规则 --------------------------
# 忽略空白字符(按需调整)
t_ignore = ' \t\n'
t_inparens_ignore = ' \t\n'

# 错误处理函数
def t_error(t):
    print(f"非法字符: {t.value[0]}")
    t.lexer.skip(1)

def t_inparens_error(t):
    print(f"括号内的非法字符: {t.value[0]}")
    t.lexer.skip(1)

# -------------------------- 测试代码 --------------------------
if __name__ == '__main__':
    lexer = lex.lex()
    test_cases = ["a(b|c(d|e))", "a(b)(c)(d)"]
    for case in test_cases:
        print(f"\n测试用例: {case}")
        lexer.input(case)
        while True:
            tok = lexer.token()
            if not tok:
                break
            print(f"令牌类型: {tok.type}, 内容: {tok.value}")

效果验证

运行这段代码后,你会看到:

  • 对于a(b|c(d|e)),输出两个令牌:IDENTIFIER(a) 和 PAREN_GROUP((b|c(d|e))),正好匹配'a'之后的全部嵌套括号内容
  • 对于a(b)(c)(d),会输出四个令牌:IDENTIFIER(a)、PAREN_GROUP((b))、PAREN_GROUP((c))、PAREN_GROUP((d)),符合“独立括号不合并”的要求

这种方式完全在词法分析层面处理了嵌套逻辑,不需要在语法解析函数里手动写递归或层级判断,完美满足你的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:40:03