我知道Perl的"Marpa“ Earley解析器有非常好的错误报告。
但我在它的文档或谷歌搜索中找不到它是否有错误恢复。
例如,大多数C/C++编译器都有错误恢复,它们用于报告多个语法错误,而其他编译器通常在第一个错误时停止。
我实际上是在解析自然语言,并想知道是否有一种方法可以在输入的一部分失败后重新同步并恢复解析。
例如,对于那些能摸到它的人:
我在分析老挝语的音节。在老挝,一些元音是二音,编码为单独的字符,并呈现在前面的辅音之上。在分析来自老挝维基百科的随机文章时,我遇到了这样一个元音加倍的文本。这是不允许在老挝正字法,所以必须是一个错误。但我知道,在几个字符内,文字又是好的。
无论如何,这是一个真正的例子,它激发了我对错误恢复或与令牌流重新同步的一般兴趣。
发布于 2014-09-06 12:13:36
在Marpa中有两种处理错误的可能性。
“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语言来实现同样的效果,但是在客户端代码中跳过允许我们在需要做太多虚假操作的情况下中止解析。
https://stackoverflow.com/questions/25698167
复制相似问题