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

时间:2017-09-09 12:29:03

标签: 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, *%_)

因此索引为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

1 个答案:

答案 0 :(得分:7)

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

* $0似乎已更新并立即可用。可能还有其他编号的匹配。

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

my regex word { \w+ };
say 'hellohello' ~~ /<word> $<word>/

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

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

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

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