【问题标题】:Python parser for lambda calculus用于 lambda 演算的 Python 解析器
【发布时间】:2019-04-10 16:24:36
【问题描述】:

为了好玩,我想为无类型的 Lambda 演算编写一个解析器。这 最简单的方法可能是编写一个手写解析器,但我想知道 如果有更 Pythonic 的方式?具体来说,我想使用 翻译语言语法描述的 Python 库 进入解析器。这是该语言的 BNF 定义:

<term> ::= <var>
        |  <term> <term>
        |  λ <var> <term>

为简单起见,我省略了括号规则。申请伙伴 到左边,这样x y z 就是(x y) z

什么Python库可以取上面的语法描述,或者一些 从它派生的语法(正如书面的那样,我相信,语法是 模棱两可和左递归,因此实现起来并不简单),并且 生成解析器?我想看看它是如何使用代码完成的,所以请 不要只回答“pyparsing 可以做到”。请写代码 以下几行:

>>> G = """syntax description here..."""
>>> parser = build.the_parser(G)
>>> parser.parse("λ x. (y z)")
Abs('x', App(Id('x', Id('y'))))

最后一行是生成的抽象语法树可能是什么。绝对值 代表抽象 (lambda),App 代表应用程序,Id 代表 标识符。我认为 PEG Packrat 解析器生成器在这里可以很好地工作。

【问题讨论】:

    标签: python parsing lambda-calculus peg


    【解决方案1】:

    这个 ANTLR4 语法可以解决问题:

    grammar T;
    
    program
     : term EOF
     ;
    
    term
     : Lambda Id '.' term
     | '(' term ')'
     | term term
     | Id
     ;
    
    Lambda
     : '\u03BB'
     ;
    
    Id
     : [a-z] [a-zA-Z0-9]*
     ;
    
    Spaces
     : [ \t\r\n] -> skip
     ;
    

    将上述内容放在名为T.g4 的文件中。将ANTLR4 jar 下载到同一个文件夹并执行:

    java -cp antlr-4.7.2-complete.jar org.antlr.v4.Tool -Dlanguage=Python3 T.g4
    

    这将创建词法分析器和解析器文件。

    现在运行:

    from antlr4 import *
    from playground.TLexer import TLexer
    from playground.TParser import TParser
    
    
    tests = [
      'λ x. (y z)', 
      'x y z w'
    ]
    
    for test in tests:
        lexer = TLexer(InputStream(test))
        parser = TParser(CommonTokenStream(lexer))
        tree = parser.program()
        print("{}".format(tree.toStringTree(recog=parser)))
    

    将打印:

    (program (term λ x . (term ( (term (term y) (term z)) ))) <EOF>)
    (program (term (term (term (term x) (term y)) (term z)) (term w)) <EOF>)
    

    【讨论】:

    • 不错!但它不会解析像 x y z w 这样的术语。
    • 是的,x y z w 是一个有效术语。但是您不必逐字执行我的语法。只要它仍然可读,我就可以修改它(当然很难定义我所说的可读性)。 ANTLR 也可以工作,但不是最优的,因为它需要 Java 运行时。
    • 谢谢!我也希望有一些更轻量级的替代品。
    • 当然,没问题@BjörnLindqvist。尽管支持左递归规则的轻量级解决方案不太可能,但 IMO。当然,如果有这样的选择,我会很高兴。
    • 为什么不呢? lambda 演算的解析规则是如此琐碎,所以我不明白为什么需要一个复杂的解析器。
    【解决方案2】:

    这是一个删除左递归的替代方法。尽管访问语法树是 OP 的一项练习。

    from parsimonious import Grammar
    
    grammar = Grammar(r"""
        start = term
    
        term = ('(' _* term1 _* ')') / term1
        term1 = app / atom1
    
        atom = ('(' _* atom1 _* ')') / atom1
        atom1 = abs / id
    
        abs = 'λ' _* id _* '.' _* term
    
        app = atom _+ term
    
        id = ~"[A-Za-z]+"
    
        _ = ~"[^\S\n\r]"u
    """)
    
    print(grammar.parse("x y z w"))
    

    【讨论】:

    • 它无法解析(((x y) z) w)(λx. x x) (λx. x x),我也认为你的应用程序规则是右关联的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多