DSL 中的 Pyparsing 递归类型定义
Pyparsing recursive type definition in DSL
我正在解析一种具有多种类型定义的领域特定语言。原始类型的解析相对简单,但事实证明,转向 DSL 中的复杂类型更具挑战性。我正在寻找一种策略来递归匹配可以包含对自身的引用的定义。
例如,地图数据类型在较高层次上可能如下所示:
map<primitive_type, complex_type>
它可以作为递归定义出现在输入中:
map<string, map<int, map<string, ...>>>=
这张地图的地图理论上可以是任意深度。
更复杂的是,这些嵌套的复杂类型也可以是其他复杂类型。
map<string, map<int, map<string, array<array<string>>>>>
我正在寻找一种策略来处理此解析,以便获得包含解析出的嵌套地图类型的准确类型。我考虑过只使用正则表达式,但这对我来说并不是最佳选择。我也知道递归语法存在 Forward,但我还不太清楚如何将它应用于这种情况。如果对这种类型的解析有任何好的模式或参考,我将不胜感激任何指导!
转发是执行此操作的方法。由于类型声明本身可能包含类型声明,因此对于 Forward 的去向,这可能是一个不错的选择:
import pyparsing as pp
type_decl = pp.Forward()
看看你的例子,我知道我们需要一些标点符号,我们可以为这些定义一些隐藏的表达式,以便它们解析,但不会破坏解析的结果:
LANGLE, RANGLE, COMMA = map(pp.Suppress, "<>,")
# if we get an ellipsis, we want to keep it, so use Literal instead of Suppress
ELLIPSIS = pp.Literal("...")
接下来我们可以做简单的类型:
simple_type = pp.oneOf("string int float", asKeyword=True)
现在是两个复杂类型,map
和 array
。对于这些,我们将在可能出现复杂类型的任何地方使用 Forward:
MAP, ARRAY = map(pp.Keyword, ["map", "array"])
map_type = MAP - LANGLE + pp.Group(simple_type + COMMA + (type_decl | ELLIPSIS)) + RANGLE
array_type = ARRAY - LANGLE + type_decl + RANGLE
complex_type = pp.Group(map_type | array_type)
我在这里使用 '-' 运算符,因为如果在看到有效的“map”或“array”关键字后出现语法错误,pyparsing 会在该表达式中给出错误。
此时我们有了复杂类型和简单类型,因此我们可以使用 <<=
运算符将它们“注入”到先前定义的 Forward 中:
type_decl <<= (complex_type | simple_type)
检查使用 runTests
:
type_decl.runTests("""\
string
int
float
map<string, map<int, map<string, ...>>>
map<string, map<int, map<string, array<array<string>>>>>
""", fullDump=False)
给出:
string
['string']
int
['int']
float
['float']
map<string, map<int, map<string, ...>>>
[['map', ['string', ['map', ['int', ['map', ['string', '...']]]]]]]
map<string, map<int, map<string, array<array<string>>>>>
[['map', ['string', ['map', ['int', ['map', ['string', ['array', ['array', 'string']]]]]]]]]
我正在解析一种具有多种类型定义的领域特定语言。原始类型的解析相对简单,但事实证明,转向 DSL 中的复杂类型更具挑战性。我正在寻找一种策略来递归匹配可以包含对自身的引用的定义。
例如,地图数据类型在较高层次上可能如下所示:
map<primitive_type, complex_type>
它可以作为递归定义出现在输入中:
map<string, map<int, map<string, ...>>>=
这张地图的地图理论上可以是任意深度。
更复杂的是,这些嵌套的复杂类型也可以是其他复杂类型。
map<string, map<int, map<string, array<array<string>>>>>
我正在寻找一种策略来处理此解析,以便获得包含解析出的嵌套地图类型的准确类型。我考虑过只使用正则表达式,但这对我来说并不是最佳选择。我也知道递归语法存在 Forward,但我还不太清楚如何将它应用于这种情况。如果对这种类型的解析有任何好的模式或参考,我将不胜感激任何指导!
转发是执行此操作的方法。由于类型声明本身可能包含类型声明,因此对于 Forward 的去向,这可能是一个不错的选择:
import pyparsing as pp
type_decl = pp.Forward()
看看你的例子,我知道我们需要一些标点符号,我们可以为这些定义一些隐藏的表达式,以便它们解析,但不会破坏解析的结果:
LANGLE, RANGLE, COMMA = map(pp.Suppress, "<>,")
# if we get an ellipsis, we want to keep it, so use Literal instead of Suppress
ELLIPSIS = pp.Literal("...")
接下来我们可以做简单的类型:
simple_type = pp.oneOf("string int float", asKeyword=True)
现在是两个复杂类型,map
和 array
。对于这些,我们将在可能出现复杂类型的任何地方使用 Forward:
MAP, ARRAY = map(pp.Keyword, ["map", "array"])
map_type = MAP - LANGLE + pp.Group(simple_type + COMMA + (type_decl | ELLIPSIS)) + RANGLE
array_type = ARRAY - LANGLE + type_decl + RANGLE
complex_type = pp.Group(map_type | array_type)
我在这里使用 '-' 运算符,因为如果在看到有效的“map”或“array”关键字后出现语法错误,pyparsing 会在该表达式中给出错误。
此时我们有了复杂类型和简单类型,因此我们可以使用 <<=
运算符将它们“注入”到先前定义的 Forward 中:
type_decl <<= (complex_type | simple_type)
检查使用 runTests
:
type_decl.runTests("""\
string
int
float
map<string, map<int, map<string, ...>>>
map<string, map<int, map<string, array<array<string>>>>>
""", fullDump=False)
给出:
string
['string']
int
['int']
float
['float']
map<string, map<int, map<string, ...>>>
[['map', ['string', ['map', ['int', ['map', ['string', '...']]]]]]]
map<string, map<int, map<string, array<array<string>>>>>
[['map', ['string', ['map', ['int', ['map', ['string', ['array', ['array', 'string']]]]]]]]]