在 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 对回答这个问题很有用。
没有测试。
当我仅引用以下代码中所示的名称时,我收到 "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 对回答这个问题很有用。
没有测试。