Marpa でエラーを処理するには、2 つの可能性があります。
「Ruby Slippers」の解析
Marpa は、スキャン中に多くのコンテキストを維持します。このコンテキストを使用して、パーサーが何らかのトークンを要求できるようにすることができます。また、トークンが入力に含まれていない場合でも、トークンを Marpa に提供するかどうかを決定できます。たとえば、ステートメントをセミコロンで終了する必要があるプログラミング言語を考えてみましょう。次に、Ruby Slippers の手法を使用して、行末や右中括弧の前など、特定の場所にセミコロンを挿入できます。
use strict;
use warnings;
use Marpa::R2;
use Data::Dump 'dd';
my $grammar = Marpa::R2::Scanless::G->new({
source => \q{
:discard ~ ws
Block ::= Statement+ action => ::array
Statement ::= StatementBody (STATEMENT_TERMINATOR) action => ::first
StatementBody ::= 'statement' action => ::first
| ('{') Block ('}') action => ::first
STATEMENT_TERMINATOR ~ ';'
event ruby_slippers = predicted STATEMENT_TERMINATOR
ws ~ [\s]+
},
});
my $recce = Marpa::R2::Scanless::R->new({ grammar => $grammar });
my $input = q(
statement;
{ statement }
statement
statement
);
for (
$recce->read(\$input);
$recce->pos < length $input;
$recce->resume
) {
ruby_slippers($recce, \$input);
}
ruby_slippers($recce, \$input);
dd $recce->value;
sub ruby_slippers {
my ($recce, $input) = @_;
my %possible_tokens_by_length;
my @expected = @{ $recce->terminals_expected };
for my $token (@expected) {
pos($$input) = $recce->pos;
if ($token eq 'STATEMENT_TERMINATOR') {
# fudge a terminator at the end of a line, or before a closing brace
if ($$input =~ /\G \s*? (?: $ | [}] )/smxgc) {
push @{ $possible_tokens_by_length{0} }, [STATEMENT_TERMINATOR => ';'];
}
}
}
my $max_length = 0;
for (keys %possible_tokens_by_length) {
$max_length = $_ if $_ > $max_length;
}
if (my $longest_tokens = $possible_tokens_by_length{$max_length}) {
for my $lexeme (@$longest_tokens) {
$recce->lexeme_alternative(@$lexeme);
}
$recce->lexeme_complete($recce->pos, $max_length);
return ruby_slippers($recce, $input);
}
}
このruby_slippers
関数では、トークンを偽装する必要があった頻度をカウントすることもできます。その数がある値を超えた場合は、エラーをスローして解析を中止できます。
入力をスキップする
入力に解析不能なジャンクが含まれている可能性がある場合、語彙素が見つからない場合はスキップしてみてください。このために、$recce->resume
メソッドはオプションの位置引数を取り、通常の解析が再開されます。
use strict;
use warnings;
use Marpa::R2;
use Data::Dump 'dd';
use Try::Tiny;
my $grammar = Marpa::R2::Scanless::G->new({
source => \q{
:discard ~ ws
Sentence ::= WORD+ action => ::array
WORD ~ 'foo':i | 'bar':i | 'baz':i | 'qux':i
ws ~ [\s]+
},
});
my $recce = Marpa::R2::Scanless::R->new({ grammar => $grammar });
my $input = '1) Foo bar: baz and qux, therefore qux (foo!) implies bar.';
try { $recce->read(\$input) };
while ($recce->pos < length $input) {
# ruby_slippers($recce, \$input);
try { $recce->resume } # restart at current position
catch { try { $recce->resume($recce->pos + 1) } }; # advance the position
# if both fail, we go into a new iteration of the loop.
}
dd $recce->value;
何にでも一致する語彙素で同じ効果を達成できますが:discard
、クライアント コードでスキップを行うと、あまりにも多くのファッジを行う必要がある場合に解析を中止できます。