我正在尝试使用
pyparsing
创建一个解析器来解析以下格式的表达式:
FUNC_NAME(ARG1, ARG2, ..., ARGN, K1=V1, K2=V2, ..., KN=VN)
ARG 和 KWARG 值可以是文字(带引号的字符串、数字、不带引号的日期表达式)或其本身是具有无限嵌套的函数。
我似乎无法解决的问题是 Forward() 的递归结构。
这是我所拥有的:
import pyparsing as pp
LP, RP = map(pp.Suppress, "()")
ident = ppc.identifier("ident")
number = pp.pyparsing_common.number
string = (pp.QuotedString("\"") | pp.QuotedString("'"))
date = pp.Combine(pp.Word(pp.nums, exact=4) + "-" + pp.Word(pp.nums, exact=2) + "-" + pp.Word(pp.nums, exact=2))("date")
literal = date | number | string
body = pp.Forward()
body <<= body | (ident + pp.Suppress("=") + body) | (ident + LP + body + RP) | literal | pp.empty
expr = ident + (LP + pp.Optional(pp.Group(pp.delimitedList(body))) + RP)
运行这个表达式会给我一个递归深度错误。不知道我做错了什么。
这是一个有趣的问题,支持您开始使用递归语法。
当前的问题在于
body
内容的定义:
body <<= body | (ident + pp.Suppress("=") + body) | (ident + LP + body + RP) | literal | pp.empty
这会创建一个左递归(或“LR”)表达式,因为要解析
body
,你首先必须解析 body
,但首先你必须解析 body
,但首先......等等。其他替代术语也可以使用 body
,但作为更大表达式的一部分(在括在括号中之后,或作为赋值的一部分)。因此,解决方案可能是删除领先的 body
术语:
body <<= (ident + pp.Suppress("=") + body) | (ident + LP + body + RP) | literal | pp.empty
但是您可能希望支持将
body
括在括号中,因此更改为这也可以解决您的 LR 问题:
body <<= LP + body + RP | (ident + pp.Suppress("=") + body) | (ident + LP + body + RP) | literal | pp.empty
这些更改中的任何一个都将解决您的 LR 问题。默认情况下,pyparsing 不支持 LR 解析器。您可以启用此功能,使用 pyparsing 3.0.0 中添加的功能:
pp.ParserElement.enable_left_recursion()
但是如果采用任何建议的更改,则没有必要。
我还添加了这些行,以生成解析器的铁路图。我发现这些图表对于可视化解析器的结构非常有帮助;
pp.autoname_elements()
expr.create_diagram("lr_function_parser.html")
生成此图:
祝您进一步的 pyparsing 工作顺利!