【发布时间】:2017-09-09 12:29:02
【问题描述】:
以下是一个基于更大语法的测试用例——目标是解析 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
【问题讨论】: