使用Parse :: RecDescent进行空格重要的解析(例如,HAML,Python)

时间:2016-01-20 00:17:54

标签: perl parsing grammar parse-recdescent

我正在尝试使用haml.info解析HAML(Parse::RecDescent)。如果您不了解haml,则问题与解析Python相同 - 语法块按缩进级别进行分组。

从一个非常简单的子集开始,我尝试了一些方法,但我认为我不太了解P :: RD的贪婪或递归顺序。鉴于haml:

%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以及其他一些没有运气的方法。

任何人(a)都可以解释为什么上述不起作用和/或(b)是否存在不使用perl操作/拒绝的方法?我尝试抓取缩进中的空格数,然后在插值前瞻条件中使用它来获取下一行中的空格数,但我永远无法正确获得插值语法(因为它需要箭头操作符)。 / p>

1 个答案:

答案 0 :(得分:0)

在珠三角以外做一些工作要好得多。

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($_)应返回哈希引用。