我正在使用TextX Python库(基于Arpeggio PEG解析器)为现有语言编写解析器
但是当我尝试使用它来解析文件时,出现了异常:
RecursionError: maximum recursion depth exceeded while calling a Python object
这是引发此异常的最小示例:
#!/usr/bin/env python
from textx import metamodel_from_str
meta_model_string = "Expr: ( Expr '+' Expr ) | INT ;"
model_string = "1 + 1"
mm = metamodel_from_str(meta_model_string, debug=True)
m = mm.model_from_str(model_string, debug=True)
我将其追溯到Arpeggio的left recursion issue,其中指出A := A B
之类的规则不受支持,应转换为没有此类递归的规则。
所以我的问题是:是否可以使用不使用左递归的方式重写上述Expr := Expr '+' Expr
规则?请注意,实际的Expr
规则很多更复杂。稍微简化一点的版本将是:
Expr: '(' Expr ')' | Expr '+' Expr | Expr '*' Expr' | '!' Expr | INT | STRING ;
答案 0 :(得分:2)
textX作者在这里。除了保罗的出色回答外,还有expression example应该为您提供一个良好的开端。
自上而下的解析器通常不会在没有此类黑客的情况下处理左递归规则。如果您的语言将变得复杂且以表达为主,那么最好尝试一些自下而上的解析器,该解析器允许左递归并提供声明性优先级和关联性规范。如果您喜欢textX,那么我建议看一下parglare,它的设计目标相似,但是使用了自下而上的解析技术(特别是LR和GLR)。快速介绍性示例是您要构建的确切语言。
在this post中,我写了一篇关于启动parglare项目的原理以及与textX / Arpeggio的区别的博客。
答案 1 :(得分:1)
通常写为:
multop: '*' | '/'
addop: '+' | '-'
Factor: INT | STRING | '(' Expr ')' ;
Term: Factor [multop Factor]... ;
Expr: Term [addop Term]... ;
现在,Expr
不会直接递归自身,直到首先匹配前导'('。您还将获得与操作优先级相对应的组。(请注意,Expr
和{{ 1}}可能会产生Term
之类的组,这是因为您可能期望['1', '+', '1', '+', '1']
,这是左递归解析器将为您提供的功能。)