gpt4 book ai didi

perl - 在 Marpa::R2::Scanless 中防止天真的最长标记匹配

转载 作者:行者123 更新时间:2023-12-04 23:23:34 27 4
gpt4 key购买 nike

在当前实现中 Scanless Interface (SLIF) 在 Marpa 解析器中,词法分析器似乎以下列方式进行最长标记匹配 (LTM):

  • 尝试在输入中的当前位置匹配所有终结符。
  • 除了最长的匹配项之外的所有匹配项都被丢弃。
  • 这些最长的 token 被提供给解析器,解析器可能接受也可能不接受它们。
  • 如果没有接受 token ,则解析失败。

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

    #!/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 token 必须遵循。在这个位置进行词法分析时, value token 匹配最长的子串 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"]

    这是预期的行为。

    最佳答案

    请注意,自版本 2.079_015 , Marpa 支持 Longest Acceptable Tokens Matching 的概念,这意味着只需添加:

    lexeme default = forgiving => 1

    到您的语法将产生预期的输出。 IE。:
    #!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"]

    关于perl - 在 Marpa::R2::Scanless 中防止天真的最长标记匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17773976/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com