关于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,负数优先级表示比默认更低。
这个配置的核心目的是避免关键字与自定义标识符(列名)的匹配冲突:
- 语法中定义了大量关键字(如
_SUM、_IF、_AND等),这些关键字的优先级都是默认的0。如果NAME优先级和它们相同,Lark可能会把SUMNAME错误拆成_SUM+NAME,或者把IF当成NAME而非_IF。 - 给
NAME设置-1的低优先级后,Lark会优先匹配所有优先级更高的关键字,只有当输入内容不匹配任何关键字时,才会尝试用_NAME的正则去匹配成NAME终结符。这样:IF(1>0, TRUE, FALSE)中的IF会被正确识别为_IF关键字,而非NAME;SUMNAME因为前缀SUM后面还有内容,不匹配_SUM关键字,所以会被完整识别为NAME。
二、当前语法实现的合理性分析
优点
- 运算符优先级分层清晰:从
sum、product、division到power,严格遵循数学运算的优先级顺序,保证表达式解析符合常规逻辑。 - 覆盖常用计算场景:支持算术运算、逻辑判断、聚合函数、日期时间处理等KPI计算的核心需求,函数定义完整。
- 大小写不敏感处理:通过
i修饰符(如"sum"i)实现关键字的大小写不敏感,适配不同输入习惯。 - 支持变量定义语句:
start规则包含NAME ":=" expr,满足KPI定义的场景需求。
可优化点
- 冗余规则合并:
division规则中同时定义了power "/" power -> div和_DIVIDE "(" expr_or "," expr_or ")" -> div,两个规则都映射到div处理逻辑,可以合并注释说明,或统一处理方式。 - 未使用的规则清理:
TIME_SHIFT、_XOR等规则定义后未在任何表达式规则中使用,建议清理或补充对应的语法支持。 _NOT规则位置调整:当前_NOT expr_atom放在expr_cond层级,只能作用于原子表达式,无法处理NOT (A > B)这类逻辑。可以将其移到expr_and或更高层级,支持作用于任意条件表达式。- BOOLEAN定义简化:目前先定义
_TRUE、_FALSE再合并为BOOLEAN,可以直接简化为BOOLEAN: "TRUE" | "FALSE",减少冗余层级。 - 列表规则的空值支持:
list_expr和list_str当前要求至少有一个元素,如果需要支持空列表(如SUM()),可以修改为list_expr: (expr ("," expr)*)?。
内容的提问来源于stack exchange,提问作者VicVic
相关产品推荐
相关产品推荐

