Python pyparsing部分匹配或递归限制命中

Python pyparsing部分匹配或递归限制命中,python,python-3.x,pyparsing,Python,Python 3.x,Pyparsing,使用pyparsing,我似乎很难理解为什么我的语法要么部分匹配,要么达到递归限制 lparens = Suppress("(") rparens = Suppress(")") name = Word(alphanums, exact=1) expression = Forward() function = Group(Literal("λ") + OneOrMore(name) + Literal(".").suppress() + expression) application = Gro

使用pyparsing,我似乎很难理解为什么我的语法要么部分匹配,要么达到递归限制

lparens = Suppress("(")
rparens = Suppress(")")
name = Word(alphanums, exact=1)
expression = Forward()
function = Group(Literal("λ") + OneOrMore(name) + Literal(".").suppress() + expression)
application = Group(expression + expression) #<-- the problem *I think*
exp1 = ( name | function | application )
exp2 = (lparens + exp1 + rparens) | exp1
expression << exp2
在第一个示例中,为什么它在“a”处停止,而不应用应用程序步骤,并运行到与第二个示例中相同的无限外观中

在第二个示例中,它与“(”匹配,因此需要“)”来完成exp1。因此,它应该解析名称“a”,并且由于没有后面的“)”,它应该放弃该名称并尝试函数(失败),然后转到应用程序。然后,它解析名称“a”(第一个匹配项),并应转到名称“b”,完成名称、应用程序,然后是“match”),以完成exp1和表达式

显然,情况并非如此

编辑:忘记添加以下作品:

expression.parseString("((λa.a)(λa.a))")
`#结果:([([(['λ','a',{}),(['λ','a',{})],{})

setDebug()
setName()
添加到各个元素并解析“(ab)”会产生:


我很难回答你的问题,因为我对你试图解析的东西并没有很好的理解。幸运的是,函数定义中的lambda字符足以作为提示,因此我搜索了lambda演算符号的一些背景知识,并在这里找到了一个不错的描述:

由此,我提出了一个简单的BNF:

expr ::= (name | function | '(' expr ')')+
function ::= 'λ' name+ '.' expr
name ::= a single character 'a'..'z' or 'A'..'Z'
从这里转换为pyparsing如下所示:

lparens = Suppress("(")
rparens = Suppress(")")
dot = Suppress('.')
# work around output encoding issues by displaying as '^'
λ = Literal('λ').setParseAction(replaceWith('^'))

# Forward() placeholder for recursive expression
expression = Forward()

# implement BNF bottom-up
name = oneOf(list(alphas))
function = Group(λ + Group(OneOrMore(name))("head") + dot + expression("body"))
lambda_term = name | function | lparens + expression + rparens
expression <<= Group(OneOrMore(lambda_term))
这似乎有很多额外的筑巢需要涉水而过。我们可以通过稍微调整
expression
来减少一些,仅当有2个或更多表达式时才进行分组,否则就解析单个未分组的表达式。元组乘法功能允许我们将其定义为:

expression <<= Group(lambda_term*(2,)) | lambda_term
或者更清楚地说:

[
    [
        ['^', ['a'], 'a'], 
        ['^', ['a'], 'a']
    ]
]
在发布的解析器中,您还具有“应用程序”的概念。我推测你所说的申请是引述的《傻瓜》文章所说的“解决”。要解析函数,可以对函数头中的每个名称逐个使用后续表达式,并用相应的表达式替换函数体中的名称。在解析时,我试图理解这一点,但在表达式中,函数嵌套在其他函数中,或者头部和身体定义在嵌套的括号中,替换表达式通过几个嵌套级别与函数分开,这让我很为难。我得出的结论是,解析必须在完成名称和函数的解析之后进行。pyparsing赋予结果的结构应该使遍历结果和连续解析带有表达式的名称变得简单

以下是完整的解析器代码:

"""
(from http://palmstroem.blogspot.com/2012/05/lambda-calculus-for-absolute-dummies.html)

BNF:
  expr ::= (name | function | '(' expr ')')+
  name ::= ['a'..'z' 'A'..'Z']+
  function ::= lambda name+ '.' expr
"""

lparens = Suppress("(")
rparens = Suppress(")")
dot = Suppress('.')
# work around output encoding issues by displaying as '^'
λ = Literal('λ').setParseAction(replaceWith('^'))

# Forward() placeholder for recursive expression
expression = Forward()

# implement BNF bottom-up
name = oneOf(list(alphas))
function = Group(λ + Group(OneOrMore(name))("head") + dot + expression("body"))
lambda_term = name | function | lparens + expression + rparens
#~ expression <<= Group(OneOrMore(lambda_term))
expression <<= Group(lambda_term*(2,)) | lambda_term


tests = """\
    ((λa.a)(λa.a))
    (λy.xy) ab
    λx.λy.xzy
""".splitlines()
for t in tests:
    t = t.strip()
    print(t.replace('λ','^'))
    expression.parseString(t, parseAll=True).pprint()
    print()
“”“
(来自http://palmstroem.blogspot.com/2012/05/lambda-calculus-for-absolute-dummies.html)
BNF:
expr::=(名称|函数|'('expr'))+
姓名::=['a'..'z''a'..'z']+
函数::=lambda name+'.'expr
"""
lparens=抑制(“”)
rparens=Suppress(“)”)
点=抑制('.'))
#通过显示为“^”解决输出编码问题
λ=文字('λ')。setParseAction(替换为('^'))
#递归表达式的Forward()占位符
表达式=向前()
#实现BNF自底向上
名称=其中一个(列表(字母))
函数=组(λ+组(一个或多个(名称))(“头”)+点+表达式(“体”))
lambda_term=name |函数| lparens+表达式+rparens

#~expression为了理解为什么语法无法完成任务,首先必须了解pyparsing是如何工作的。您似乎相信pyparsing会探索所有可能的路径,直到找到一条正确表示整个输入字符串的路径。这是不正确的。请参见以下示例:

exp1 = ( name | function | application )
exp1.parseString("ab") # outputs ['a']
这里,“a”是一个有效的
名称
,因此是一个有效的
exp1
。这就是你得到的结果。Pyparsing不关心额外的“b”。它找到了一个匹配的
exp1
,因此它就停止了


至于无限递归,问题是:

application = Group(expression + expression)
exp1 = ( name | function | application )
exp2 = (lparens + exp1 + rparens) | exp1
expression << exp2
application=Group(表达式+表达式)
exp1=(名称|函数|应用程序)
exp2=(lparens+exp1+rparens)| exp1
表达式中止

  • 应用第二个选项(
    功能
    ):
  • λ不匹配
  • 应用第三个选项(
    应用程序
    ):
  • 应用规则
    应用程序
    表达式+表达式
    ):
  • 应用规则
    表达式
  • 开始


  • 请参阅,了解一个有效的实现。

    如果我通过更改
    exp2=(lparens+exp1+rparens)
    强制使用括号,那么情况会更好,但是“(a)(b)”仍然只解析“a”。同样,如果我只使用像“((a)(b))”这样的括号,它将解析……因此更复杂的示例工作,因为它遵循了该示例。”(a)b'也可以工作——所以我很确定它永远不会超过'ab'中的名字——尝试添加到您的ParseRelation以跟踪正在发生的事情。我添加了它——和setName,以便它更可读一点。仍在尝试理解正在发生的事情。据我所知,问题似乎是,只要它能说它有匹配项,它就可以当它匹配满足exp1的“a”时会这样做,但exp2在rparens中失败,因此它会一直后退并尝试exp1 so名称、函数,然后是应用程序——在这里,一切都重新开始。你知道,我找到了一些其他BNF,但没有找到这个。我发现的其他BNF要么遇到相同的问题,要么导致AST不明确.顺便说一句,非常感谢“绝对傻瓜的Lambda演算”比如说,老实说,这正是这里发生的事情——我在计算Church算术时遇到了手工代换的问题,所以我打算编写一个小的逐步代换解释程序。我专注于手头的任务,第一步就绊倒了。谢谢!
    [
        [
            ['^', ['a'], 'a'], 
            ['^', ['a'], 'a']
        ]
    ]
    
    """
    (from http://palmstroem.blogspot.com/2012/05/lambda-calculus-for-absolute-dummies.html)
    
    BNF:
      expr ::= (name | function | '(' expr ')')+
      name ::= ['a'..'z' 'A'..'Z']+
      function ::= lambda name+ '.' expr
    """
    
    lparens = Suppress("(")
    rparens = Suppress(")")
    dot = Suppress('.')
    # work around output encoding issues by displaying as '^'
    λ = Literal('λ').setParseAction(replaceWith('^'))
    
    # Forward() placeholder for recursive expression
    expression = Forward()
    
    # implement BNF bottom-up
    name = oneOf(list(alphas))
    function = Group(λ + Group(OneOrMore(name))("head") + dot + expression("body"))
    lambda_term = name | function | lparens + expression + rparens
    #~ expression <<= Group(OneOrMore(lambda_term))
    expression <<= Group(lambda_term*(2,)) | lambda_term
    
    
    tests = """\
        ((λa.a)(λa.a))
        (λy.xy) ab
        λx.λy.xzy
    """.splitlines()
    for t in tests:
        t = t.strip()
        print(t.replace('λ','^'))
        expression.parseString(t, parseAll=True).pprint()
        print()
    
    exp1 = ( name | function | application )
    exp1.parseString("ab") # outputs ['a']
    
    application = Group(expression + expression)
    exp1 = ( name | function | application )
    exp2 = (lparens + exp1 + rparens) | exp1
    expression << exp2