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

Lark Parser解析含特殊字符的无键布尔表达式失败问题排查

问题排查与修复:Lark解析含特殊字符的无键布尔表达式崩溃

问题描述

使用Lark库解析两类布尔表达式:

  • 带键表达式:如(A=(value1) OR B>(value2)) AND C<=(value3)
  • 无键表达式:如(A OR B) AND C

解析普通表达式正常,但解析含*等特殊字符的无键表达式(如valu* OR text)时崩溃,提示无法识别特殊字符;但将该表达式放在带键结构中(如A=(valu* OR text) AND B=(value2))时可正常解析。

原始代码

from lark import Lark, Tree, Token

rules = """
?start: expr
?expr: link_or

?link_or: (link_or "or"i)? link_and
?link_and: (link_and "and"i)? ( NAME | cond_eq | cond_gt | cond_ge | cond_lt | cond_le )

?cond_eq: KEY "=" const | "(" expr ")"
?cond_gt: KEY ">" const | "(" expr ")"
?cond_ge: KEY ">=" const | "(" expr ")"
?cond_lt: KEY "<" const | "(" expr ")"
?cond_le: KEY "<=" const | "(" expr ")"

KEY: NAME

?const: INT -> int
      | string_raw -> string

?string_raw: /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/

%import common.CNAME -> NAME
%import common.WS_INLINE
%import common.INT
%ignore WS_INLINE
        """

parser = Lark(rules)

for text in ("key1=(value1*) OR key2=(value2)", "key1 OR key2", "key1* OR key2"):
    print("text:", text)
    try:
        tree = parser.parse(text)
        print("parsed tree:", tree)
    except BaseException as e:
        print("Exception:", e)
    print()

输出结果

示例1:"key1=(value1*) OR key2=(value2)"(解析正常)

text: key1=(value1*) OR key2=(value2)
parsed tree: Tree(Token('RULE', 'link_or'), [Tree(Token('RULE', 'cond_eq'), [Token('KEY', 'key1'), Tree('string', [Token('__ANON_2', '(value1*)')])]), Tree(Token('RULE', 'cond_eq'), [Token('KEY', 'key2'), Tree('string', [Token('__ANON_2', '(value2)')])])])

示例2:"key1 OR key2"(解析正常)

text: key1 OR key2
parsed tree: Tree(Token('RULE', 'link_or'), [Token('NAME', 'key1'), Token('NAME', 'key2')])

示例3:"key1* OR key2"(解析失败)

text: key1* OR key2
Exception: No terminal matches '*' in the current parser context, at line 1 col 5

key1* OR key2
    ^
Expected one of: 
    * __ANON_1
    * OR
    * LESSTHAN
    * AND
    * MORETHAN
    * __ANON_0
    * EQUAL

问题分析

崩溃核心原因:

  1. 无键表达式的原子项依赖NAME匹配,但NAME导入的是common.CNAME,仅支持字母、数字、下划线组成的标识符,无法匹配*这类特殊字符。
  2. 带键结构中的特殊字符能正常解析,是因为被string_raw规则的正则表达式匹配(该规则允许括号内包含任意字符)。

修复方案

新增pattern规则匹配含特殊字符的无键原子项,替换link_and规则中的NAME:

修改后的代码

from lark import Lark, Tree, Token

rules = """
?start: expr
?expr: link_or

?link_or: (link_or "or"i)? link_and
?link_and: (link_and "and"i)? ( pattern | cond_eq | cond_gt | cond_ge | cond_lt | cond_le )

?cond_eq: KEY "=" const | "(" expr ")"
?cond_gt: KEY ">" const | "(" expr ")"
?cond_ge: KEY ">=" const | "(" expr ")"
?cond_lt: KEY "<" const | "(" expr ")"
?cond_le: KEY "<=" const | "(" expr ")"

KEY: NAME
# 匹配含特殊字符的无键原子项,可根据需求扩展允许的字符
?pattern: /[a-zA-Z0-9_*]+/
?const: INT -> int
      | string_raw -> string

?string_raw: /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/

%import common.CNAME -> NAME
%import common.WS_INLINE
%import common.INT
%ignore WS_INLINE
        """

parser = Lark(rules)

for text in ("key1=(value1*) OR key2=(value2)", "key1 OR key2", "key1* OR key2"):
    print("text:", text)
    try:
        tree = parser.parse(text)
        print("parsed tree:", tree)
    except BaseException as e:
        print("Exception:", e)
    print()

扩展说明

若需要支持更多特殊字符(如?、+),可修改pattern的正则表达式:

  • 宽松匹配(排除空格、括号和逻辑运算符):/[^ \t\n\r()&|]+/
  • 指定允许的字符:/[a-zA-Z0-9_*?+@-]+/

修改后三个示例均可正常解析,含特殊字符的无键表达式会被识别为pattern节点。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 12:15:38