Ste*_*eve 16 regex perl parsing
我正在尝试使用perl正则表达式来编写一个简单的语法(请注意,这不是用于生产用途,只是用于提供编辑器提示/完成的快速分析).例如,
my $GRAMMAR = qr{(?(DEFINE)
(?<expr> \( (?&expr) \) | (?&number) | (?&var) | (?&expr) (?&op) (?&expr) )
(?<number> \d++ )
(?<var> [a-z]++ )
(?<op> [-+*/] )
)}x;
Run Code Online (Sandbox Code Playgroud)
我希望能够像这样运行
$expr =~ /$GRAMMAR(?&expr)/;
Run Code Online (Sandbox Code Playgroud)
然后访问所有变量名称.然而,根据perlre,
请注意,在递归返回后,无法访问在递归内部匹配的捕获组,因此需要额外的捕获组层.因此,即使$ + {NAME},也不会定义$ + {NAME_PAT}.
显然这是不可能的.我可以尝试使用(?{ code })块来将变量名称保存到散列中,但这并不尊重回溯(即,即使变量已经过回溯,分配的副作用仍然存在).
是否有任何方法可以获取给定命名捕获组捕获的所有内容,包括递归匹配?或者我是否需要手动挖掘各个部分(从而复制所有模式)?
必须添加捕获和回溯机制的必要性是Regexp :: Grammars解决的缺点之一.
但是,你的问题中的语法是左递归的,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;
};
Run Code Online (Sandbox Code Playgroud)
请注意,这个简单的语法为所有运算符提供了相同的优先级,而不是请原谅我亲爱的阿姨Sally.
您想要提取所有变量名称,因此您可以按原样使用AST
sub 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 ];
}
Run Code Online (Sandbox Code Playgroud)
并打印结果
if ("(a + (b - c))" =~ $EXPR) {
print "[$_]\n" for sort +all_variables \%/;
}
else {
print "no match\n";
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是为Var规则安装自动操作,以便在成功解析变量时记录变量的名称.
package JustTheVarsMaam;
sub new { bless {}, shift }
sub Var {
my($self,$result) = @_;
++$self->{VARS}{$result};
$result;
}
sub all_variables { keys %{ $_[0]->{VARS} } }
1;
Run Code Online (Sandbox Code Playgroud)
称之为
my $vars = JustTheVarsMaam->new;
if ("(a + (b - c))" =~ $EXPR->with_actions($vars)) {
print "[$_]\n" for sort $vars->all_variables;
}
else {
print "no match\n";
}
Run Code Online (Sandbox Code Playgroud)
无论哪种方式,输出都是
[a] [b] [c]
小智 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
Run Code Online (Sandbox Code Playgroud)
输出是:
a, b, c
Run Code Online (Sandbox Code Playgroud)
你的问题只是如何获取变量名,所以在这个答案中没有运算符关联性的概念.请注意,如果需要,Marpa对此没有任何问题.