首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >防止Marpa中最长的令牌匹配::R2::Scanless

防止Marpa中最长的令牌匹配::R2::Scanless
EN

Stack Overflow用户
提问于 2013-07-21 15:35:04
回答 2查看 445关注 0票数 4

在Marpa解析器中无扫描接口的当前实现中,lexer似乎以以下方式执行最长令牌匹配(LTM):

  1. 所有终端符号都试图在输入的当前位置匹配。
  2. 除了最长的匹配之外,所有的匹配都被丢弃。
  3. 这些最长的令牌被输入解析器,解析器可能接受也可能不接受它们。
  4. 如果没有接受标记,则解析将失败。

当我的语法包含匹配最长子字符串但不能发生在当前位置的标记时,这会产生令人沮丧的解析失败。考虑以下代码:

代码语言:javascript
复制
#!/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;
}

这将产生输出:

代码语言:javascript
复制
>> ! 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.

预期产出:

代码语言:javascript
复制
>> ! key : value
["key", "value"]
>> ! key:value
["key", "value"]

!被识别后,必须跟着一个key令牌。在此位置的词法期间,value令牌与最长的子字符串key:value相匹配,尽管它不能发生在此位置。因此,解析失败。

问题:是否有可能实现预期的输出而无需编写手动的词法?

(我知道,lexer可以查询识别器以获得预期的令牌,并且可以将自己限制为只匹配这些标记,但我不知道如何说服幻灯片为我这样做。)

我在perl5 v16.2上运行Marpa::R2 v2.064

编辑

按照Jeffrey的建议,我实现了一条规则,它总是匹配比普通value更长的子字符串,因此是首选的。使用pause事件,我可以手动解析它,尽管为了获得正确的语义,我必须保留一个幻影规则。

这是完整的,更新的代码包括。事件处理和更新的测试用例:

代码语言:javascript
复制
#!/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;
}

这会产生

代码语言:javascript
复制
>> ! key : value
["key", "value"]
>> ! key:value
["key", "value"]
>> ! key :value
["key", "value"]
>> ! key: value
["key", "value"]

这就是人们所期望的行为。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-02-15 06:12:31

请注意,由于015版本,Marpa支持最长可接受令牌匹配的概念,这意味着添加:

代码语言:javascript
复制
lexeme default = forgiving => 1

对您的语法将产生预期的输出。即:

代码语言:javascript
复制
#!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]+

将给予:

代码语言:javascript
复制
>> ! key : value
["key", "value"]
>> ! key:value
["key", "value"]
>> ! key :value
["key", "value"]
>> ! key: value
["key", "value"]
票数 6
EN

Stack Overflow用户

发布于 2013-11-30 17:11:16

根据罗斯的建议,从评论中抄录:

您可以创建表单record ::= ('!') <complex record>的规则,其中<complex record>不包含空格和两个或多个冒号。

  1. “停顿”之前 <complex record> (检查是否与pause_lexeme方法暂停)。
  2. 将键和冒号、值和用手把它们分开。
  3. 然后在记录之后进行resume正常解析。
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17773976

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档