Python DSL中递归类型定义的解析

Python DSL中递归类型定义的解析,python,parsing,dsl,pyparsing,Python,Parsing,Dsl,Pyparsing,我正在解析一种具有多个类型定义的领域特定语言。原语类型的解析相对来说比较简单,但在DSL中,进一步分析复杂类型更具挑战性。我正在寻找一种策略来递归地匹配一个定义,该定义可以包含对自身的引用 例如,地图数据类型在较高级别上可以如下所示: map<primitive_type, complex_type> map 它可以作为递归定义出现在输入中: map<string, map<int, map<string, ...>>>= map= 理论上

我正在解析一种具有多个类型定义的领域特定语言。原语类型的解析相对来说比较简单,但在DSL中,进一步分析复杂类型更具挑战性。我正在寻找一种策略来递归地匹配一个定义,该定义可以包含对自身的引用

例如,地图数据类型在较高级别上可以如下所示:

map<primitive_type, complex_type>
map
它可以作为递归定义出现在输入中:

map<string, map<int, map<string, ...>>>=
map=
理论上,这张地图可以是任意深度的

为了使事情更加复杂,这些嵌套的复杂类型也可以是其他复杂类型

map<string, map<int, map<string, array<array<string>>>>>
map

我正在寻找一种处理解析的策略,以便得到一个包含解析出的嵌套映射类型的准确类型。我考虑过只使用正则表达式,但这对我来说并不理想。我也知道递归语法存在向前的问题,但我还没有弄清楚如何将其应用到这个场景中。如果这种类型的解析有任何好的模式或参考,我将非常感谢任何指导

前进是实现这一点的方法。由于类型声明本身可能包含类型声明,这可能是前进方向的一个好选择:

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("...")
现在是两种复杂类型,
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中,理想情况下,我应该从编写一个BNF开始,我一直强烈鼓励这样做。我可能还会更明确地指出有效的映射键类型和映射值类型,然后将
map\u类型
声明为
map+LANGLE-map\u键类型+逗号+map\u值类型+RANGLE
,因为“…”似乎是映射值类型的一种特殊类型,但没有在其他任何地方使用。我为混淆道歉,对于未来的读者来说:“…”不是一个可以遇到的文本值。我在我的示例中使用它只是为了说明这样一个事实,即映射可以在映射类型定义的复杂部分中不断深入。这个只是为了在我的例子中说明递归本身。我可能应该做一个编辑来澄清这一点。继续吧,把它留在里面——毕竟它是你的DSL,所以你可以把你想要的任何东西放进去,它展示了如何处理一种特殊情况,这种类型在地图中使用,但在其他地方没有(另外,如果你改变问题,我将不得不改变我的答案,我今天处于懒惰模式)。
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)
type_decl <<= (complex_type | simple_type)
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']]]]]]]]]