我正在尝试用perl正则表达式编写一个简单的语法(请注意,这不是用于生产用途,只是用于提供编辑器提示/完成的快速分析)。例如,
my $GRAMMAR = qr{(?(DEFINE)
(?<expr> \( (?&expr) \) | (?&number) | (?&var) | (?&expr) (?&op) (?&expr) )
(?<number> \d++ )
(?<var> [a-z]++ )
(?<op> [-+*/] )
)}x;
我希望能够将其作为
运行$expr =~ /$GRAMMAR(?&expr)/;
然后访问所有变量名称。但是,根据perlre,
请注意,在递归返回后,无法访问在递归内部匹配的捕获组,因此需要额外的捕获组层。因此,即使$ + {NAME}为。
,也不会定义$ + {NAME_PAT}
显然这是不可能的。我可以尝试使用(?{ code })
块将变量名称保存到散列中,但这并不尊重回溯(即,即使变量已经过去了,分配的副作用仍然存在)。
有没有办法获取给定命名捕获组捕获的所有内容,包括递归匹配?或者我是否需要手动挖掘各个部分(从而复制所有模式)?
答案 0 :(得分:8)
必须添加捕获和回溯机制是Regexp::Grammars解决的缺点之一。
但是,你问题中的语法是left-recursive,Perl正则表达式和递归下降解析器都不会解析。
将语法调整为Regexp::Grammars并将左递归分解出来
my $EXPR = do {
use Regexp::Grammars;
qr{
^ <Expr> $
<rule: Expr> <Term> <ExprTail>
| <Term>
<rule: Term> <Number>
| <Var>
| \( <MATCH=Expr> \)
<rule: ExprTail> <Op> <Expr>
<token: Op> \+ | \- | \* | \/
<token: Number> \d++
<token: Var> [a-z]++
}x;
};
请注意,这个简单的语法为所有操作员提供了相同的优先权,而不是请原谅我亲爱的姨妈莎莉。
您想要提取所有变量名称,因此您可以像在
中一样遍历ASTsub all_variables {
my($root,$var) = @_;
$var ||= {};
++$var->{ $root->{Var} } if exists $root->{Var};
all_variables($_, $var) for grep ref $_, values %$root;
wantarray ? keys %$var : [ keys %$var ];
}
并使用
打印结果if ("(a + (b - c))" =~ $EXPR) {
print "[$_]\n" for sort +all_variables \%/;
}
else {
print "no match\n";
}
另一种方法是为Var
规则安装自动操作,该规则记录变量名称,因为它们已被成功解析。
package JustTheVarsMaam;
sub new { bless {}, shift }
sub Var {
my($self,$result) = @_;
++$self->{VARS}{$result};
$result;
}
sub all_variables { keys %{ $_[0]->{VARS} } }
1;
在
中调用此方法my $vars = JustTheVarsMaam->new;
if ("(a + (b - c))" =~ $EXPR->with_actions($vars)) {
print "[$_]\n" for sort $vars->all_variables;
}
else {
print "no match\n";
}
无论哪种方式,输出都是
[a] [b] [c]
答案 1 :(得分:7)
使用下面__DATA__部分中的BNF,Marpa::R2递归是原生的:
#!env perl
use strict;
use diagnostics;
use Marpa::R2;
my $input = shift || '(a + (b - c))';
my $grammar_source = do {local $/; <DATA>};
my $recognizer = Marpa::R2::Scanless::R->new
(
{
grammar => Marpa::R2::Scanless::G->new
(
{
source => \$grammar_source,
action_object => __PACKAGE__,
}
)
},
);
my %vars = ();
sub new { return bless {}, shift;}
sub varAction { ++$vars{$_[1]}};
$recognizer->read(\$input);
$recognizer->value() || die "No parse";
print join(', ', sort keys %vars) . "\n";
__DATA__
:start ::= expr
expr ::= NUMBER
| VAR action => varAction
| expr OP expr
| '(' expr ')'
NUMBER ~ [\d]+
VAR ~ [a-z]+
OP ~ [-+*/]
WS ~ [\s]+
:discard ~ WS
输出结果为:
a, b, c
您的问题只是如何获取变量名称,因此在此答案中没有运算符关联性等概念。请注意,如果需要,Marpa对此没有任何问题。