【问题标题】:Parsing string with nested parentheses using Parse::RecDescent使用 Parse::RecDescent 解析带有嵌套括号的字符串
【发布时间】:2012-07-05 19:06:00
【问题描述】:

我正在尝试使用Parse::RecDescent 制作一个可以解析括号表达式和一元运算符? 的解析器。

到目前为止,我创建解析器时失败了,因为规则 expression 是左递归的:

use strict;
use warnings;
use Parse::RecDescent;

my $test = <<END;
((foo)? bar)
END

my $grammar = q(
    parse: expression(s)
    expression: string | parend | expression(s)
    parend : "(" (string | expression) ")" /\??/
    string : /\w+/ /\??/

);
my $parser = Parse::RecDescent->new($grammar);
my $result = $parser->parse($test);
if($result){
    print $result;
}else{
    print STDERR "Invalid grammar\n";
}

【问题讨论】:

    标签: perl parsing parentheses parse-recdescent


    【解决方案1】:

    首先,从最低优先级到最高优先级。

    parse  : expr /\Z/
    
    expr   : list
    
    list   : unary(s?)
    
    unary  : unary '?'
           | term
    
    term   : '(' expr ')'
           | STRING
    
    STRING : /\w+/
    

    当然,

    unary  : unary '?'
           | term
    

    不起作用,因为它是左递归的。 Operator Associativity and Eliminating Left-Recursion in Parse::RecDescent 可以帮助你摆脱它。我们得到

    unary  : term unary_(s?)
    unary_ : '?'
    

    但这不会为我们构建正确的树。因此,让我们先将“(s?)”变平。

    unary  : term unary_
    unary_ : '?' unary_
           |
    

    然后我们可以使用 subrule args 来创建正确的树。

    unary  : term unary_[ $item[1] ]
    unary_ : '?' unary_[ [ 'postfix?' => $arg[0] ] ]
           | { $arg[0] }
    

    大家一起:

    use strict;
    use warnings;
    use Data::Dumper      qw( Dumper );
    use Parse::RecDescent qw( );
    
    my $grammar = <<'END';
       {
          use strict;
          use warnings;
       }
    
       parse  : expr /\Z/ { $item[1] }
    
       expr   : list
    
       list   : unary(s?) { [ $item[0] => @{ $item[1] } ] }
    
       unary  : term unary_[ $item[1] ]
       unary_ : '?' unary_[ [ 'postfix?' => $arg[0] ] ]
              | { $arg[0] }
    
       term   : '(' expr ')' { $item[2] }
              | STRING { [ string => $item[1] ] }
    
       STRING : /\w+/
    
    END
    
    my $parser = Parse::RecDescent->new($grammar)
       or die "Invalid grammar\n";
    my $tree = $parser->parse("((foo bar)? baz)\n")
       or die "Invalid text\n";
    print(Dumper($tree));
    

    【讨论】:

    • 哎呀,应该是/\Z//\Z/ 是为了确保你的表达后面没有垃圾。考虑输入( foo ) ) bar。如果没有/\Z/,不正确的) bar 将被忽略。
    • 我无法正确解析表达式。你能再解释一下这些动作吗?一元匹配,将术语作为参数传递给 unary_,它要么不匹配任何内容并将参数传回,要么匹配“?”并使用一个匿名数组调用一元数组,该数组由一个值散列组成... uhhmmm...
    • 如果你给unary_添加另一个一元运算符你会更好理解。目标是为foo?+ 创建[ 'postfix+' =&gt; [ 'postfix?' =&gt; [ string =&gt; 'foo' ] ] ],为foo+? 创建[ 'postfix?' =&gt; [ 'postfix+' =&gt; [ string =&gt; 'foo' ] ] ]。我建议你阅读链接的帖子。
    • 它不适用于链接输入,因为我在理解您的语法时出错。固定。
    • 添加了更多细节以完成代码。具体来说,它现在构建了一个有用的、可扩展的解析树。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-03
    • 2021-11-18
    相关资源
    最近更新 更多