我让 treetop 将你的语言编译成一个 .rb 文件。这给了我一些可以深入研究的东西:
$ tt -o /tmp/rip.rb /tmp/rip.treetop
然后我用这个小存根重新创建循环:
require 'treetop'
load '/tmp/rip.rb'
RipParser.new.parse('')
这挂了。现在,是不是很有趣!一个空字符串可以重现行为,就像您问题中的十几行示例一样。
为了找出它挂在哪里,我使用 Emacs 键盘宏来编辑 rip.rb,在每个方法的条目中添加一个调试语句。例如:
def _nt_root
p [__LINE__, '_nt_root'] #DEBUG
start_index = index
现在我们可以看到循环的范围:
[16, "root"]
[21, "_nt_root"]
[57, "_nt_statement"]
...
[3293, "_nt_eol"]
[3335, "_nt_semicolon"]
[3204, "_nt_comment"]
[57, "_nt_statement"]
[57, "_nt_statement"]
[57, "_nt_statement"]
...
从那里进一步调试表明允许整数为空字符串:
rule integer
digit*
end
这间接允许语句为空字符串,并且顶级规则statement*
永远使用空语句。更改*
为+
修复循环,但揭示了另一个问题:
/tmp/rip.rb:777:in `_nt_object': stack level too deep (SystemStackError)
from /tmp/rip.rb:757:in `_nt_compound_object'
from /tmp/rip.rb:1726:in `_nt_range'
from /tmp/rip.rb:1671:in `_nt_special_literals'
from /tmp/rip.rb:825:in `_nt_literal_object'
from /tmp/rip.rb:787:in `_nt_object'
from /tmp/rip.rb:757:in `_nt_compound_object'
from /tmp/rip.rb:1726:in `_nt_range'
from /tmp/rip.rb:1671:in `_nt_special_literals'
... 3283 levels...
Range 是通过 special_literals、literal_object、object 和 Compound_object 间接左递归的。Treetop,当面临左递归时,会吃堆栈直到呕吐。我没有快速解决这个问题,但至少从现在开始你有一个堆栈跟踪。
此外,这不是您的直接问题,但 的定义digit
很奇怪:它可以是一位数,也可以是多个数。这导致digit*
或digit+
允许(可能)非法整数1________2
。