【问题标题】:Does the Marpa parser library support error recovery?Marpa 解析器库是否支持错误恢复?
【发布时间】:2014-10-31 03:11:00
【问题描述】:

我知道Perl's "Marpa" Earley parser 有很好的错误报告

但我无法在其文档中或通过谷歌搜索找到它是否具有错误恢复

例如,大多数 C/C++ 编译器都有错误恢复功能,它们用于报告多个语法错误,而其他编译器通常会在第一个错误处停止。

我实际上是在解析自然语言,想知道是否有办法在输入的一部分失败后重新同步和恢复解析。


例如,对于那些可以理解它的人:

我正在解析老挝语的音节。在老挝语中,一些元音是变音符号,它们被编码为单独的字符并呈现在前一个辅音之上。在解析老挝维基百科的随机文章时,我遇到了一些这样的元音加倍的文本。这在老挝语正字法中是不允许的,所以一定是错字。但我知道,在几个字符内,文本又好了。

无论如何,这是一个真实的例子,它激起了我对错误恢复或与令牌流重新同步的普遍兴趣。

【问题讨论】:

  • 您可能希望给出一个您想到的错误和恢复示例。 Marpa 允许以多种方式做事,这样可以更轻松地针对您的问题提供特定的答案。
  • @JeffreyKegler:从简单的地方开始,假设有一个无关的标记,并且在跳过它之后,标记流再次符合语法。但是,如果有一个在线错误恢复技术的一般介绍,那将是最好的,因为即使我已经使用解析器多年,但我以前从未接触过错误恢复。我曾经解析编程语言,现在我是自然语言。
  • 这可能允许使用我在对 amon 答案的评论中描述的错误规则技术进行相当简单的解决方案,例如:“vowel ::= good_vowel | bad_vowel bad_vowel ::= vowel vowel”
  • 在阅读了其他答案和 cmets 之后,我可以说我遇到的情况不是我所期望的,所以它不应该有一个特殊的情况处理程序。只是试图跳过垃圾直到再次解析工作将是正确的方法。但我也对其他方法感兴趣,因为我对整个领域感兴趣,除了我目前的项目(-:

标签: perl parsing error-recovery marpa earley-parser


【解决方案1】:

在 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-&gt;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 词位来实现相同的效果,但在我们的客户端代码中进行跳过可以让我们在必须进行太多伪造时中止解析。

【讨论】:

  • 阿蒙++。另一种技术是错误规则——诸如“bad_widget ::= widget_prefix gadget_suffix”之类的规则,用于识别(非法)尝试在小部件中包含小工具的情况。或“bad_if ::= if '(' assignment ')' block”,如果您想在 if 语句的条件中捕获顶级分配。传统的解析器不使用错误规则,因为它们几乎没有足够的能力来解析正确的输入。玛尔巴有余力。
猜你喜欢
  • 1970-01-01
  • 2014-02-07
  • 1970-01-01
  • 2016-11-06
  • 1970-01-01
  • 2018-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多