Marpa パーサーのスキャンレス インターフェイス(SLIF)の現在の実装では、レクサーは次の方法で最長トークン マッチング (LTM) を行うようです。
- すべての終端記号は、入力内の現在の位置で一致しようとします。
- 最長一致以外はすべて破棄されます。
- これらの最長のトークンはパーサーに送られますが、パーサーはそれらを受け入れる場合と受け入れない場合があります。
- トークンが受け入れられない場合、解析は失敗します。
これにより、文法に最長の部分文字列に一致するトークンが含まれているが、現在の位置では発生しない場合、イライラする解析の失敗が発生します。次のコードを検討してください。
#!/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"]
これは予想される動作です。