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

关于Lark解析器优先级设置的困惑与技术咨询

Lark语法配置疑问与合理性分析

问题描述

我正在使用Python解析库Lark,它支持Earley和LALR(1)解析器,可通过自定义EBNF定义语法(EBNF即扩展巴科斯范式)。小写为规则,大写为终结符,Lark还能为大写终结符设置权重调整匹配优先级。我编写了用于KPI计算的语法,但对实现合理性存疑,尤其不理解为何需要以下配置才能正确解析SUMNAME := A+B、IF(1>0, TRUE, FALSE)这类公式:

// column names
_NAME: /\_{0,2}[a-zA-Z][a-zA-Z_0-9\%]*/
NAME.-1: _NAME

完整语法如下:

?start: expr
    | NAME ":=" expr                                            -> create_statement

?expr: expr_or

?exprif: _IF "(" expr_or "," expr_or ["," expr_or] ")"          -> if_clause

?expr_or: expr_and
    | expr_and (_OR expr_and)+                                  -> or_

?expr_and: expr_cond
    | expr_cond (_AND expr_cond)+                               -> and_

?expr_cond: sum
    | sum COMPARISON sum                                        -> condition
    | sum _IN "(" list_expr ")"                                 -> is_in
    | sum _BETWEEN sum _AND sum                                 -> between
    | _NOT expr_atom                                            -> not_


?sum: product
    | sum "+" product                                           -> add
    | sum "-" product                                           -> sub

?product: division
    | product "*" division                                      -> mul

?division: power
    | power "/" power                                           -> div
    | _DIVIDE "(" expr_or "," expr_or ")"                       -> div

?power: exprfactor
    | power "**" exprfactor                                     -> pow
    | power "^" exprfactor                                      -> pow

?exprfactor: expr_atom
    | "-" expr_atom                                             -> neg

?expr_atom: atom
    | _SUM "(" list_expr ")" [_OVER"(" list_str ")"]            -> sum
    | _MEAN "(" list_expr ")" [_OVER"(" list_str ")"]           -> mean
    | _STD "(" list_expr ")" [_OVER"(" list_str ")"]            -> std
    | _MAX "(" list_expr ")"                                    -> max
    | _MIN "(" list_expr ")"                                    -> min
    | _COALESCE "(" list_expr ")"                               -> coalesce
    | _ABS "(" expr_or ")"                                      -> abs
    | _SQRT "(" expr_or ")"                                     -> sqrt
    | _FLOAT "(" expr_or ")"                                    -> float_
    | _MOD "(" expr_or "," atom ")"                             -> mod
    | _ROUND "(" expr_or "," atom ")"                           -> round
    | _YEAR "(" atom ")"                                        -> year
    | _QUARTER "(" atom ")"                                     -> quarter
    | _MONTH "(" atom ")"                                       -> month
    | _DAY "(" atom ")"                                         -> day
    | _HOUR "(" atom ")"                                        -> hour
    | _MINUTE "(" atom ")"                                      -> minute
    | _IS_NULL "(" expr_or ")"                                  -> is_null
    | _SECOND "(" atom ")"                                      -> second
    | _SUBSTR "(" expr_or "," atom ["," atom] ")"               -> substr
    | "(" expr ")"
    | exprif


?atom: NAME                                                     -> variable
    | NUMBER                                                    -> variable
    | BOOLEAN                                                   -> variable
    | STRING                                                    -> variable
    | NAN                                                       -> variable
    | NULL                                                      -> variable
    | DATETIME                                                  -> variable
    | DATE                                                      -> variable


list_expr: expr ("," expr)*
list_str: STRING ("," STRING)*


// over
_OVER: /\_{2}OVER\_{2}/

//special values
NAN: "nan"i
NULL: "null"i

// comparison operators
COMPARISON: GREATER | GREATER_OR_EQUAL | SMALLER | SMALLER_OR_EQUAL | EQUAL | UN_EQUAL
GREATER: ">"
GREATER_OR_EQUAL: GREATER"="
SMALLER: "<"
SMALLER_OR_EQUAL: SMALLER"="
EQUAL: "=="
UN_EQUAL: "!="
_BETWEEN: "between"i
_IN: "in"i

// IF token
_IF: "if"i

// logical operators.
_NOT.1: "not"i
_AND.1: "and"i
_OR.1: "or"i
_XOR.1: "xor"i

// boolean operators
_TRUE: "TRUE"
_FALSE: "FALSE"

// syntax tokens
_SUM: "sum"i
_MEAN: "mean"i
_STD: "std"i
_IS_NULL: "isnull"i | "is_null"i
_MAX: "max"i
_MIN: "min"i
_DIVIDE: "divide"i
_COALESCE: "coalesce"i
_SQRT: "sqrt"i
_FLOAT: "float"i
_MOD: "mod"i
_ROUND: "round"i
_ABS: "abs"i
_YEAR: "year"i
_QUARTER: "quarter"i
_MONTH: "month"i
_DAY: "day"i
_HOUR: "hour"i
_MINUTE: "minute"i
_SECOND: "second"i
_SUBSTR: "substr"i

// tokens for timeshifts
FREQUENCY: "M" | "Q" | "Y"
SHIFT: PLUS | MINUS
PYE: "PYE"
TIME_SHIFT: "T" SHIFT INT FREQUENCY | PYE

// operators
PLUS: "+"
MINUS: "-"

// time and date formats
DATETIME.1: "'" /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/ "'"
DATE.1: "'" /\d{4}-\d{2}-\d{2}/ "'"

// strings
STRING : "'" _STRING_ESC_INNER "'" | "\"" _STRING_ESC_INNER "\""



// booleans
BOOLEAN: _TRUE | _FALSE

// column names
_NAME: /\_{0,2}[a-zA-Z][a-zA-Z_0-9\%]*/
NAME.-1: _NAME

%import common.INT
%import common.NUMBER
%import common.WS_INLINE
%import common._STRING_ESC_INNER

%ignore WS_INLINE

想请教该配置的作用,以及当前语法实现是否合理。


一、NAME.-1: _NAME配置的作用

Lark中,终结符的优先级决定匹配顺序:优先级数值越高,越先被匹配;默认优先级为0,负数优先级表示比默认更低。

这个配置的核心目的是避免关键字与自定义标识符(列名)的匹配冲突:

  1. 语法中定义了大量关键字(如_SUM、_IF、_AND等),这些关键字的优先级都是默认的0。如果NAME优先级和它们相同,Lark可能会把SUMNAME错误拆成_SUM + NAME,或者把IF当成NAME而非_IF。
  2. 给NAME设置-1的低优先级后,Lark会优先匹配所有优先级更高的关键字,只有当输入内容不匹配任何关键字时,才会尝试用_NAME的正则去匹配成NAME终结符。这样:
    • IF(1>0, TRUE, FALSE)中的IF会被正确识别为_IF关键字,而非NAME;
    • SUMNAME因为前缀SUM后面还有内容,不匹配_SUM关键字,所以会被完整识别为NAME。

二、当前语法实现的合理性分析

优点

  1. 运算符优先级分层清晰:从sum、product、division到power,严格遵循数学运算的优先级顺序,保证表达式解析符合常规逻辑。
  2. 覆盖常用计算场景:支持算术运算、逻辑判断、聚合函数、日期时间处理等KPI计算的核心需求,函数定义完整。
  3. 大小写不敏感处理:通过i修饰符(如"sum"i)实现关键字的大小写不敏感,适配不同输入习惯。
  4. 支持变量定义语句:start规则包含NAME ":=" expr,满足KPI定义的场景需求。

可优化点

  1. 冗余规则合并:division规则中同时定义了power "/" power -> div和_DIVIDE "(" expr_or "," expr_or ")" -> div,两个规则都映射到div处理逻辑,可以合并注释说明,或统一处理方式。
  2. 未使用的规则清理:TIME_SHIFT、_XOR等规则定义后未在任何表达式规则中使用,建议清理或补充对应的语法支持。
  3. _NOT规则位置调整:当前_NOT expr_atom放在expr_cond层级,只能作用于原子表达式,无法处理NOT (A > B)这类逻辑。可以将其移到expr_and或更高层级,支持作用于任意条件表达式。
  4. BOOLEAN定义简化:目前先定义_TRUE、_FALSE再合并为BOOLEAN,可以直接简化为BOOLEAN: "TRUE" | "FALSE",减少冗余层级。
  5. 列表规则的空值支持:list_expr和list_str当前要求至少有一个元素,如果需要支持空列表(如SUM()),可以修改为list_expr: (expr ("," expr)*)?。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:57:03