2

Parse::RecDescentでHAML ( haml.info ) を解析しようとしています。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上記の 2 行目 (最初のルールの行)を削除すると、blockすべてがキャプチャされますが、インデントに関係なく、最初のブロックがすべての行を丸呑みするため、もちろん正しくグループ化されません。

また、先読みアクションを使用して検査$textし、他のいくつかのアプローチを試してみましたが、うまくいきませんでした。

誰でも(a)上記が機能しない理由、および/または(b)perlアクション/拒否を使用しないアプローチがあるかどうかを説明できますか? インデント内のスペースの数を取得して、それを次の行のスペース数の補間された先読み条件で使用しようとしましたが、補間構文を正しく取得することはできませんでした (矢印演算子が必要なため)。

4

1 に答える 1

0

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($_)ハッシュ参照を返すことが期待されています。

于 2016-02-16T04:15:26.213 に答える