以下是基于更大语法的测试用例 - 目标是解析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
答案 0 :(得分:7)
$/
,$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>)> $ /