2012-02-22 52 views
5

考慮例如解析器這樣的:我獲得以下錯誤解析器組合器:repsep是否允許回溯?

items: item1, item2, item3, item3, item4 
items: item2, item3, item3, item5, item4  
items: item4, item5, item6, item10  
items: item1, item2, item3 
exclude: item1 
exclude: item2 

[5.5] failure: `items:' expected but `e' found 

     exclude: item1 

    ^

object TestParser extends RegexParsers { 
    override protected val whiteSpace = """[ \t]*""".r 

    def eol = """(\r?\n)+""".r 
    def item = "[a-zA-Z][a-zA-Z0-9-]*".r 
    def list = "items:" ~> rep1sep(item,",") 
    def constraints = "exclude:" ~> item 

    def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol)) 
} 

如果我嘗試解析該輸入(不含兩行包含排除的作品OK)

問題很明顯這條線:

def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol)) 

它不起作用的原因是什麼。它跟回溯有什麼關係?我必須做些什麼才能使其發揮作用?

+0

如果有人提出更好的問題標題請讓我知道。不知道它是否有道理.. – PrimosK 2012-02-22 15:24:41

回答

5

您需要名單,而限制

(rep1sep(list, eol) <~ eol) ~ repsep(constraint,eol) 

完成答案之間的EOL:

你的語法指定EOL作爲列表之間的分隔符,而不是一個終止符。它會接受一個輸入,其中第一個exclude出現在最後一個item3之後(帶有空格,但不是新行)。

解析器到達不需要的eol後,它會尋找items,並找到excludes。這給出了顯示的錯誤信息。然後,解析器確實會回溯到前一個新行。它認爲列表部分停在那裏的可能性,並尋找排除。但是,如果找到一個EOL來代替。因此,另一種可能的錯誤信息會"excludes expected, eol found",在這種情況下,將有更多的幫助

當在語法選擇,沒有分支成功,解析器返回與最遠的位置,這是正常的錯誤正確的策略。假設你的文法允許"if""for",輸入是"if !!!"。在if分支上,錯誤類似於"(" expected, "!" found。在for分支上,消息將是"for expected, if found"。顯然,第二個標記上出現的if分支的消息比第一個標記上的for分支的消息要好,而且根本不相關。

在分離器/終止的問題,你可以考慮:

  • 分離器(;帕斯卡):(在C ;repsep(item, separator)
  • 終止:rep(item <~ terminator)
  • 靈活:repsep(item, separator) <~ separator?

最後一個將允許一個單獨的分隔符後沒有任何項目。如果這是不希望的,也許(rep1sep(item, separator) <~ separator?)?

+0

哇..太棒了!但是它以這種方式工作的原因是什麼? – PrimosK 2012-02-22 16:42:07

+0

我想是因爲「sep」在「列表」之間,而不是在每次重複之後。所以解析器不能「離開」第一個'rep1sep',並且它在每個「eol」之後都需要一個「列表」。 – paradigmatic 2012-02-22 17:15:25

+0

很好的回答! TY – PrimosK 2012-02-22 19:22:03