2017-09-09 36 views
7

以下是基於一個更大的語法測試案例 - 我們的目標是分析它在Unity3D的資產文件中使用YAML的子集。有趣的功能是鍵控陣列匹配器。此匹配程序循環,匹配data[i]: val<array-name(index)><indexer-and-value(index, name)><array-name>被重載,所以第一次被調用時,它會匹配任何名字。隨後的迭代 - 索引不爲零時 - 只會匹配看到的同名。這是perl6語法破碎,還是在暴露一個錯誤?

問題的關鍵是,當索引> 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 

回答

7

timotimo上IRC-說明了問題的匹配變量($/$0$1,並命名比賽)不是全局性的。匹配器開始時,匹配變量已經填充。由於性能方面的考慮,它們大部分*並未在匹配器的其餘部分進行更新。但是,當看到一個代碼塊(即使是空白塊)時,匹配變量也會被更新。所以「錯誤」解決方法實際上是一個有效的解決方案 - 包括一個空塊來強制匹配變量進行更新。

* $0似乎立即更新和可用的。可能是其他編號的匹配。

UPDATE:這似乎是唯一的一次比賽變量不能立即使用是當你在代碼類似情況下使用,而不必使用塊,如參數列表,以不同的匹配。在這裏,匹配變量是以前的比賽結束後立即可用:

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>)> $/
相關問題