【问题标题】:Pyparsing recursive type definition in DSLPyparsing DSL 中的递归类型定义
【发布时间】:2020-12-23 18:54:21
【问题描述】:

我正在解析一种具有多种类型定义的领域特定语言。原始类型的解析相对简单,但转向 DSL 中的复杂类型已被证明更具挑战性。我正在寻找一种策略来递归匹配可以包含对自身的引用的定义。

例如,地图数据类型在高层次上可能如下所示:

map<primitive_type, complex_type>

可以作为递归定义出现在输入中:

map<string, map<int, map<string, ...>>>=

这张地图理论上可以是任意深度的。

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

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

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

【问题讨论】:

    标签: python parsing dsl pyparsing


    【解决方案1】:

    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 将在该表达式中给出错误。

    此时我们有了复杂和简单的类型,因此我们可以使用&lt;&lt;= 运算符将它们“注入”到先前定义的 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']]]]]]]]]
    

    【讨论】:

    • 理想情况下,我应该从写出 BNF 开始,我一直强烈鼓励这样做。我可能还会更明确地分解有效的映射 key_type 和映射 value_type,然后将 map_type 声明为 MAP + LANGLE - map_key_type + COMMA + map_value_type + RANGLE,因为 '...' 似乎是映射值类型的特殊类型,但不是在其他任何地方使用。
    • 对于造成的混淆,我深表歉意,并为任何未来的读者指出这一点:'...' 不是可以遇到的文字值。我在我的示例中使用它只是为了参考这样一个事实,即地图可以在地图类型定义的复杂部分中不断深入。 ... 只是为了在我的示例中说明递归本身。我可能应该进行编辑以明确这一点。
    • 继续并保留它 - 毕竟它是您的 DSL,因此您可以随心所欲地放入其中,它展示了如何解决地图中使用但无处使用的类型的特殊情况否则(另外,如果你改变问题,我将不得不改变我的答案,我今天处于懒惰模式)。
    猜你喜欢
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多