在 Hammer 解析器组合器中,如何实现引用自身的规则?

In the Hammer parser combinator, how do you implement a rule that refers to itself?

当我仅引用以下代码中所示的名称时,我收到 "Deep recursion on subroutine" 警告。 use 5.016 … __SUB__->() 也无济于事。

构建提示:git clone; scons 绑定=perl 测试; cd build/opt/src/bindings/perl ; $EDITOR h.pl

use 5.024;
use strictures;
use blib;
use hammer qw();

# digits = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
sub digits { hammer::many hammer::in 0..9 }

# product = digits "*" digits
sub product {
    hammer::sequence digits, hammer::ch('*'), digits
}

product->parse('23*42'); # [[2, 3], '*', [4, 2]]


# mproduct = digits "*" mproduct
sub mproduct;
sub mproduct {
    hammer::sequence digits, hammer::ch('*'), mproduct
}
mproduct->parse('8*23*42');
# Deep recursion on subroutine "main::mproduct" at h.pl line 21.

您的代码有一个无限循环:mproduct 无条件调用 mproduct

你的语法也有同样的无限循环:mproduct 是根据 mproduct.

无条件定义的
mproduct ::= digits '*' mproduct

你想要的语法是

mproduct ::= digits '*' mproduct
           | digits

mproduct  ::= digits mproduct_
mproduct_ ::= '*' mproduct_
            | ε

用pseudo-BNF说的更清楚,你要的文法是

myproduct ::= digits ( '*' digits )*

hammer 提供了一个工具来实现这个:

hammer::sepBy1(digits, hammer::ch('*'))

这没有解决更普遍的问题。以下是对一般问题的回答可能有用的情况:

expr ::= sum

sum  ::= term sum_
sum_ ::= '+' sum_
       | ε

term ::= num
       | '(' expr ')'

错误的方法是:

sub num { ... }

sub term {
   hammer::choice(
      num(),
      hammer::sequence(
         hammer::ch('('),
         expr(),
         hammer::ch(')'),
      )
   )
}

sub sum { hammer::sepBy1(term, hammer::ch('+')) }

sub expr { sum() }

my $parser = expr();

解决这个问题,可以用bind_indirect.

use feature qw( state );

sub num { ... }

sub term {
   hammer::choice(
      num(),
      hammer::sequence(
         hammer::ch('('),
         expr(),
         hammer::ch(')'),
      )
   )
}

sub sum { hammer::sepBy1(term, hammer::ch('+')) }

sub _expr { sum() }
sub expr { state $expr = hammer::indirect(); $expr }
UNITCHECK { hammer::bind_indirect(expr(), _expr()); }

my $parser = expr();

事实证明 test file 对回答这个问题很有用。

没有测试。