这个perl6语法是否被破坏,或者是否暴露了一个bug?

pio*_*ojo 7 regex parsing perl6

以下是基于更大语法的测试用例 - 目标是解析Unity3D资产文件中使用的YAML子集.有趣的功能是键控数组匹配器.这个匹配器循环,匹配data[i]: val<array-name(index)><indexer-and-value(index, name)>.<array-name>重载,所以第一次调用时,它将匹配任何名称.后续迭代 - 当索引非零时 - 将仅匹配所看到的相同名称.

问题的关键在于当index> 0时,数组应始终有一个已知的名称,并且应该作为参数传递给匹配器.它不是 - 解释器给出以下错误:

Cannot resolve caller array-name(Match.new(...): 1, Nil, 1); none of these signatures match:
    (Prefab $: Int $ where { ... }, $prevName, Int $indent, *%_)
    (Prefab $: Int $idx, Match $ (@ (Any $prevName, *@)), Int $indent, *%_)
    (Prefab $: Int $idx, @ (Any $prevName, *@), Int $indent, *%_)
Run Code Online (Sandbox Code Playgroud)

所以索引是1,但之前没有匹配的名称.那个参数是Nil没有意义的.注意该函数中注释掉的块:#{ }.如果取消注释,测试用例将停止失败.没有基于最长匹配(|运算符或proto匹配器)的分支,因此在匹配器中添加额外的东西不应该更改解析.

测试输入包含在测试用例中.这里是:

#use Grammar::Tracer;
#use Grammar::Debugger;

grammar Prefab {
    token TOP {
        <key> ':' <value=hash-multiline(1)> \n
    }

    token key { \w+ }

    token kvpair(Int $indent=0) {
        [
        || <key> ':'  <hash-multiline($indent+1)>
        || <keyed-array($indent)>
        || <key> ': ' (\w+)
        ]
    }

    token keyed-array(Int $indent) {
        # Keys are built in to the list:
        # look for arrayname[0] first, then match subsequent lines more strictly, based on name[idx]
        :my $idx = 0;
        [
            <array-name($idx, $<array-name>, $indent)>
            <indexer-and-value($idx++, $indent)>
            #{ } # XXX this fixes it, somehow
        ] +% \n

    }
    multi token array-name(0, $prevName, Int $indent) {
        # the first element doesn't need to match indentation
        \w+
    }

    multi token array-name(Int $idx, Match $ ([$prevName, *@]), Int $indent) {
        <.indent($indent)>
        $prevName
    }
    # todo: Can I remove this overload? In testing, the parameter was sometimes an array, sometimes a Match
    multi token array-name(Int $idx, [$prevName, *@], Int $indent) {
        <.indent($indent)>
        $prevName
    }

    # arr[2]: foo
    #    ^^^^^^^^ match this
    token indexer-and-value(Int $idx, Int $indent) {
        '[' ~ ']' $idx
        [
        || ':'  <hash-multiline($indent+1)>
        || ': ' \w+
        ]
    }


    token hash-multiline(Int $indent=0) {
        # Note: the hash does not need a newline if it's over after the first (inline) kv-pair!
        # optional first line which is on the same line as the previous text:
        [
        || [<kvpair($indent)>]  [ \n <.indent($indent)> <kvpair($indent)> ]*
        ||                      [ \n <.indent($indent)> <kvpair($indent)> ]+
        ]
    }

    multi token indent(0) {
        ^^ <?>
    }
    multi token indent(Int $level) {
        ^^ ' ' ** {2*$level}
    }
}

sub MAIN() {
    say so Prefab.parse($*kv-list);
}

my $*kv-list = q:to/END/;
Renderer:
  m_Color[0]: red
END
Run Code Online (Sandbox Code Playgroud)

pio*_*ojo 7

timotimo说明了问题的IRC-匹配变量($/,$0,$1,并命名比赛)不是全局性的.匹配器开始时,匹配变量已经填充.由于性能问题,它们大部分*都没有在匹配器主体的其余部分内更新.但是,当看到代码块(甚至是空块)时,会更新匹配变量.所以"bug"解决方法实际上是一个有效的解决方案 - 包括一个空块来强制匹配变量更新.

*$0似乎已更新并立即可用.可能还有其他编号的比赛.

更新:似乎唯一的时间匹配变量不是立即可用的是当你在类似代码的上下文中使用它们而不使用块时,例如在参数列表中使用不同的匹配器.这里,匹配变量在上一次匹配后立即可用:

my regex word { \w+ };
say 'hellohello' ~~ /<word> $<word>/
Run Code Online (Sandbox Code Playgroud)

但是这个用作参数的示例失败了:

my regex repeated($x) { [$x]+ };
say 'ooxoo' ~~ / ^ <repeated('o')> . <repeated($<repeated>)> $ /
Run Code Online (Sandbox Code Playgroud)

除非您添加块以强制命名匹配变量进行更新:

my regex repeated($x) { [$x]+ };
say 'ooxoo' ~~ / ^ <repeated('o')> . {} <repeated($<repeated>)> $ /
Run Code Online (Sandbox Code Playgroud)