我正在尝试使用 Parse::RecDescent 解析 HAML (haml.info)。如果你不知道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 操作/拒绝?我尝试抓取缩进中的空格数,然后在内插前瞻条件下使用它来计算下一行中的空格数,但我永远无法完全正确使用插值语法(因为它需要一个箭头运算符)。
珠三角之外做一些工作。
my @stack = [ -1, [{}] ];
while (<>) {
chomp;
s/^( *)//;
my $indent = length($1);
if ($indent < $stack[-1][0]) {
pop @stack while $indent < $stack[-1][0];
die "Indent mismatchn" 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 documentn" if !$stack[0][1][0]{children};
die "Multiple rootsn" if @{ $stack[0][1][0]{children} } > 1;
my $root = $stack[0][1][0]{children}[0];
$parser->parse_line($_)
应返回哈希引用。