Разбор строки с вложенными скобками с использованием Parse::RecDescent
Я пытаюсь использовать 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";
}
1 ответ
Решение
Сначала вы переходите от низшего приоритета к высшему приоритету.
parse : expr /\Z/
expr : list
list : unary(s?)
unary : unary '?'
| term
term : '(' expr ')'
| STRING
STRING : /\w+/
Конечно,
unary : unary '?'
| term
не работает, потому что он левый рекурсивный. Ассоциативность операторов и устранение левой рекурсии в Parse::RecDescent может помочь вам избавиться от этого. Мы получаем
unary : term unary_(s?)
unary_ : '?'
Но это не собирается построить правильное дерево для нас. Итак, давайте начнем с "(s?)
".
unary : term unary_
unary_ : '?' unary_
|
Затем мы можем использовать аргументы subrule для создания правильного дерева.
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));