4

Marpa パーサーのスキャンレス インターフェイス(SLIF)の現在の実装では、レクサーは次の方法で最長トークン マッチング (LTM) を行うようです。

  1. すべての終端記号は、入力内の現在の位置で一致しようとします。
  2. 最長一致以外はすべて破棄されます。
  3. これらの最長のトークンはパーサーに送られますが、パーサーはそれらを受け入れる場合と受け入れない場合があります。
  4. トークンが受け入れられない場合、解析は失敗します。

これにより、文法に最長の部分文字列に一致するトークンが含まれているが、現在の位置では発生しない場合、イライラする解析の失敗が発生します。次のコードを検討してください。

#!/usr/bin/env perl

use strict; use warnings; use feature qw/say/; use utf8;

use Marpa::R2;
use Data::Dump;

my @data = ('! key : value', '! key:value');

my $grammar = Marpa::R2::Scanless::G->new({
    source => \<<'END_GRAMMAR',
        :default ::= action => [values]
        :start   ::= record

        :discard  ~  ws
        ws        ~  [\s]+

        record ::= ('!') key (':') value
        key     ~  [\w]+
        value   ~  [^\s]+
END_GRAMMAR
});


for my $data (@data) {
    my $recce = Marpa::R2::Scanless::R->new({
        grammar => $grammar,
        trace_terminals => 0, # set this to "1" to see how the tokens are recognized
    });

    $recce->read(\$data);

    my $val = $recce->value // die "no parse";

    say ">> $data";
    dd $$val;
}

これにより、次の出力が生成されます。

>> ! key : value
["key", "value"]
Error in SLIF G1 read: No lexemes accepted at position 2
* Error was at end of input
* String before error: ! key:value
Marpa::R2 exception at marpa.pl line 33.

期待される出力:

>> ! key : value
["key", "value"]
>> ! key:value
["key", "value"]

!が認識された後、keyトークンが続く必要があります。この位置で字句解析を行うと、valueトークンは最長の部分文字列と一致しkey:valueますが、この位置では発生しません。したがって、解析は失敗します。

質問:手動レクサーを作成せずに期待される出力を達成することは可能ですか?

(レクサーは認識エンジンに予想されるトークンを問い合わせることができ、これらのトークンのみに一致するように制限できることは知っていますが、SLIF にこれを行うよう説得する方法はわかりません。)

perl5 v16.2でMarpa::R2 v2.064を実行しています


編集

Jeffrey Kegler のアドバイスに従って、プレーン文字列よりも長い部分文字列に常に一致するルールを実装したvalueため、推奨されます。イベントを使用しpauseて手動で解析できますが、正しいセマンティクスのためにファントム ルールを維持する必要があります。

更新された完全なコードは次のとおりです。イベント処理と更新されたテスト ケース:

#!/usr/bin/env perl

use strict; use warnings; use feature qw/say/; use utf8;

use Marpa::R2;
use Data::Dump;

my @data = ('! key : value', '! key:value', '! key :value', '! key: value');

my $grammar = Marpa::R2::Scanless::G->new({
    source => \<<'END_GRAMMAR',
        :default ::= action => [values]
        :start   ::= Record

        :discard  ~  ws
        ws        ~  [\s]+

        Record ::=
                ('!') Key (<Op colon>) Value # not directly used
            |   ('!') KeyValue
        Key     ~  key
        Value   ~  value
        KeyValue~  key <ws any> ':' <ws any> value
        :lexeme ~ KeyValue pause => before event => 'before KeyValue'
        <Op colon> ~ ':'

        key     ~  [\w]+
        value   ~  [^\s]+
        <ws any>~  [\s]*
END_GRAMMAR
});

my %events = (
    'before KeyValue' => sub {
        my ($recce, $string, $start, $length) = @_;
        my ($k, $o, $v) = split /(\s*:\s*)/, $string, 2;
        say STDERR qq(k="$k" o="$o" v="$v");
        my $pos = $start;
        $recce->lexeme_read('Key'      => $pos, length($k), $k);
        $pos += length $k;
        $recce->lexeme_read('Op colon' => $pos, length($o), $o);
        $pos += length $o;
        $recce->lexeme_read('Value'    => $pos, length($v), $v);
    },
);


for my $data (@data) {
    my $recce = Marpa::R2::Scanless::R->new({
        grammar => $grammar,
        trace_terminals => 0,
    });
    my $length = length $data;
    for (
        my $pos = $recce->read(\$data);
        $pos < $length;
        $pos = $recce->resume()
    ) {
        say STDERR "pause";
        my ($start, $length) = $recce->pause_span();
        my $str = substr $data, $start, $length;
        for my $event_data (@{ $recce->events }) {
            my ($name) = @$event_data;
            my $code = $events{$name} // die "no code for event $name";
            $recce->$code($str, $start, $length);
        }
    }

    my $val = $recce->value // die "no parse";

    say ">> $data";
    dd $$val;
}

これにより、

>> ! key : value
["key", "value"]
>> ! key:value
["key", "value"]
>> ! key :value
["key", "value"]
>> ! key: value
["key", "value"]

これは予想される動作です。

4

2 に答える 2

6

バージョン2.079_015以降、Marpa はLongest Acceptable Tokens Matchingの概念をサポートしていることに注意してください。つまり、以下を追加するだけです。

lexeme default = forgiving => 1

あなたの文法に期待される出力が生成されます。すなわち:

#!env perl -w
use strict;
use Marpa::R2;
use Data::Dump;
use feature qw/say/;

my $grammar = Marpa::R2::Scanless::G->new({source => \do {local $/; <DATA>}});
my @data = ('! key : value', '! key:value', '! key :value', '! key: value');

foreach (@data) {
    my $r = Marpa::R2::Scanless::R->new({grammar => $grammar});
    $r->read(\$_);
    my $val = $r->value;
    say ">> $_"; dd $$val;
}
__DATA__
:default ::= action => [values]
lexeme default = forgiving => 1
:start   ::= record

:discard  ~  ws
 ws        ~  [\s]+

record ::= ('!') key (':') value
key     ~  [\w]+
value   ~  [^\s]+

あげる:

>> ! key : value
["key", "value"]
>> ! key:value
["key", "value"]
>> ! key :value
["key", "value"]
>> ! key: value
["key", "value"]
于 2014-02-15T06:12:31.977 に答える
2

ロスの提案に従って、コメントからコピーされました:

スペースがなく、2 つ以上のコロンrecord ::= ('!') <complex record>が含まれる という形式のルールを作成できます。<complex record>

  1. 「前に一時停止」</a> <complex record>(pause_lexemeまたはeventsメソッドで一時停止を確認します)。
  2. キーとコロンと値を分離し、それらを手動で lex します
  3. 次にresume、レコードの後の通常の解析。
于 2013-11-30T17:11:16.643 に答える