如何修改Python Lark EBNF以正确解析PlantUML条件分支内语句?
问题描述
我需要用Python的Lark包解析类流程图的PlantUML代码,其中每个action的语法基于Verilog-HDL。现有PlantUML包含条件分支,使用给定的EBNF解析时,if分支外的语句可正常解析,但分支内的同名语句报错(提示当前上下文不匹配':')。我需要将write(reg01, 8'h01, )提取为单个expression token,请问该如何修改EBNF?
PlantUML代码
@startuml :[7:0] a; :write(reg01, 8'h01, ); if (flag == 1'b1) then (Yes) :write(reg01, 8'h01, ); endif @enduml
原EBNF规则
start: "@startuml" (statement | cond_if)* "@enduml" cond_if: "if" "(" condition ")" "then" "(" "Yes" ")" (statement)* "endif" statement: ":" expression ";" condition: expression expression: /[^;\n]+/ %import common.WS %ignore WS
错误信息
Error: No terminal matches ':' in the current parser context, at line 6 col 3 :write(reg01, 8'h01, ); ^ Expected one of: * RPAR
问题原因与修改方案
问题根源
原EBNF中expression: /[^;\n]+/是贪婪匹配规则,会匹配除分号、换行外的所有字符。在解析if (flag == 1'b1) then (Yes)时,condition对应的expression会把flag == 1'b1) then (Yes全部匹配,导致解析器认为if的条件括号尚未闭合,因此遇到分支内的:时,会错误地预期右括号RPAR,从而触发报错。
修改后的EBNF
针对这个问题,我们需要给condition单独定义更精确的匹配规则,避免它匹配到条件括号之外的内容。以下是两种可行的修改方案:
方案1:适配无嵌套括号的条件(简单场景)
如果你的条件表达式不会出现嵌套括号,可以直接限制condition只匹配到第一个右括号之前的内容:
start: "@startuml" (statement | cond_if)* "@enduml" cond_if: "if" "(" condition ")" "then" "(" "Yes" ")" (statement)* "endif" statement: ":" expression ";" # 仅匹配条件括号内的内容,直到第一个右括号 condition: /[^)]+/ # 语句表达式保留原有规则,匹配到分号/换行前的所有内容(含括号) expression: /[^;\n]+/ %import common.WS %ignore WS
方案2:支持嵌套括号的条件(通用场景)
如果条件表达式可能包含嵌套括号,可以用递归规则处理括号结构,确保解析器能正确识别括号的闭合:
start: "@startuml" (statement | cond_if)* "@enduml" cond_if: "if" "(" condition ")" "then" "(" "Yes" ")" (statement)* "endif" statement: ":" stmt_expr ";" condition: cond_expr # 语句表达式:允许文本和嵌套括号组 stmt_expr: (TEXT | PAREN_GROUP)+ # 条件表达式:同语句表达式规则,支持嵌套括号 cond_expr: (TEXT | PAREN_GROUP)+ # 定义括号组:匹配括号及内部的所有内容(支持嵌套) PAREN_GROUP: "(" (TEXT | PAREN_GROUP | WS)* ")" # 定义文本:匹配除括号、分号、换行外的所有字符 TEXT: /[^();\n]+/ %import common.WS %ignore WS
修改效果
修改后,解析器能正确识别if语句的完整结构,分支内的statement会被正常解析,write(reg01, 8'h01, )也会被提取为单个expression token。
内容的提问来源于stack exchange,提问作者Asustor
相关产品推荐
相关产品推荐

