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)

现在是两个复杂类型,maparray。对于这些,我们将在可能出现复杂类型的任何地方使用 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']]]]]]]]]