Важный для анализа синтаксический анализ с Parse::RecDescent (например, HAML, Python)

Я пытаюсь проанализировать HAML ( http://haml.info/) с Parse:: RecDescent. Если вы не знаете haml, проблема в том же, что и при разборе Python - блоки синтаксиса сгруппированы по уровню отступа.

Начиная с очень простого подмножества, я попробовал несколько подходов, но думаю, что я не совсем понимаю ни жадность, ни рекурсивный порядок P::RD. Учитывая хамл:

%p
  %span foo

Простейшая грамматика, которую я считаю, должна работать (с битами, ненужными для приведенного выше фрагмента):

<autotree>

startrule           : <skip:''> block(s?)
non_space           : /[^ ]/
space               : ' '
indent              : space(s?)
indented_line       : indent line
indented_lines      : indented_line(s) <reject: do { Perl6::Junction::any(map { $_->level } @{$item[1]}) != $item[1][0]->level }>
block               : indented_line block <reject: do { $item[2]->level <= $item[1]->level }>
                    | indented_lines
line                : single_line | multiple_lines
single_line         : line_head space line_body newline | line_head space(s?) newline | plain_text newline

# ALL subsequent lines ending in | are consumed
multiple_lines      : line_head space line_body continuation_marker newline continuation_line(s)
continuation_marker : space(s) '|' space(s?)
continuation_line   : space(s?) line_body continuation_marker

newline      : "\n"
line_head    : haml_comment | html_element
haml_comment : '-#'
html_element : '%' tag

# TODO: xhtml tags technically allow unicode
tag_start_char : /[:_a-z]/i
tag_char       : /[-:_a-z.0-9]/i
tag            : tag_start_char tag_char(s?)

line_body    : /.*/
plain_text   : backslash ('%' | '!' | '.' | '#' | '-' | '/' | '=' | '&' | ':' | '~') /.*/ | /.*/
backslash    : '\\'

Проблема в block определение. Как и выше, он не захватывает текст, хотя правильно фиксирует следующее:

-# haml comment
%p a paragraph

Если я уберу второй reject строка сверху (та, что на первом block правило), тогда он действительно захватывает все, но, конечно, неправильно сгруппирован, так как первый блок будет отбрасывать все строки, независимо от отступа.

Я также пытался использовать опережающие действия для проверки $text и несколько других подходов без удачи.

Может ли кто-нибудь (а) объяснить, почему вышеприведенное не работает, и / или (б), если есть подход без использования действий / отклонений perl? Я попытался захватить количество пробелов в отступе, а затем использовать его в интерполированном предварительном условии для количества пробелов в следующей строке, но я так и не смог получить правильный синтаксис интерполяции (поскольку для этого требуется оператор со стрелкой).

1 ответ

Намного лучше делать часть работы вне PRD.

my @stack = [ -1, [{}] ];
while (<>) {
   chomp;
   s/^( *)//;
   my $indent = length($1);

   if ($indent < $stack[-1][0]) {
      pop @stack while $indent < $stack[-1][0];
      die "Indent mismatch\n" if $indent != $stack[-1][0];
   }
   elsif ($indent > $stack[-1][0]) {
      my $children = $stack[-1][1][-1]{children} = [];
      push @stack, [ $indent, $children ];
   }

   push @{ $stack[-1][1] }, $parser->parse_line($_);
}

die "Empty document\n" if !$stack[0][1][0]{children};
die "Multiple roots\n" if @{ $stack[0][1][0]{children} } > 1;

my $root = $stack[0][1][0]{children}[0];

$parser->parse_line($_) ожидается вернуть хэш

Другие вопросы по тегам