2009-11-25 60 views
9

我想爲下面的命令定義一個語法。斯卡拉解析器令牌分隔符問題

action = todo 
message = link todo to database 
properties = [deadline: next tuesday, context: app.model] 

當運行如下所定義的語法這個輸入時,收到以下錯誤消息::

[1.27] parsed: Command(todo,link todo to database,List()) 
[1.36] failure: string matching regex `\z' expected but `:' found 

todo link todo to database deadline: next tuesday context: app.model 
           ^

至於

object ParserWorkshop { 
    def main(args: Array[String]) = { 
     ChoiceParser("todo link todo to database") 
     ChoiceParser("todo link todo to database deadline: next tuesday context: app.model") 
    } 
} 

第二命令作爲應該標記化我可以看到它失敗了,因爲用於匹配消息的單詞的模式與屬性鍵:值對的模式幾乎相同,所以解析器無法知道消息的結束位置並開始物業。我可以堅持起始令牌被用來爲每個屬性像這樣解決這個問題:

todo link todo to database :deadline: next tuesday :context: app.model 

但我寧願保留命令接近自然語言越好。 我有兩個問題:

錯誤信息實際上是什麼意思? 而我將如何修改現有語法以適用於給定的輸入字符串?

import scala.util.parsing.combinator._ 

case class Command(action: String, message: String, properties: List[Property]) 
case class Property(name: String, value: String) 

object ChoiceParser extends JavaTokenParsers { 
    def apply(input: String) = println(parseAll(command, input)) 

    def command = action~message~properties ^^ {case a~m~p => new Command(a, m, p)} 

    def action = ident 

    def message = """[\w\d\s\.]+""".r 

    def properties = rep(property) 

    def property = propertyName~":"~propertyValue ^^ { 
     case n~":"~v => new Property(n, v) 
    } 

    def propertyName: Parser[String] = ident 

    def propertyValue: Parser[String] = """[\w\d\s\.]+""".r 
} 
+0

我認爲你應該改變你的語法是這樣的: todo「鏈接待辦事項數據庫」:截止日期:「下週二」:上下文:「應用程序。模型「 – ziggystar 2009-11-26 13:28:02

+0

這是一個我想避免的解決方案,因爲我想盡可能使Todo語法儘可能地接近自然語言。 – 2009-11-27 10:14:31

回答

21

這很簡單。當您使用~時,您必須明白,已成功完成的單個解析器沒有回溯。

因此,例如,message將所有內容都放到了冒號之前,因爲所有這些都是可接受的模式。接下來,propertiespropertyrep,它需要propertyName,但它只能找到冒號(第一個字符不會被message吞噬)。所以propertyName失敗,property失敗。現在,如上所述,propertiesrep,因此它以0次重複成功完成,然後使command成功完成。因此,回parseAllcommand解析器成功返回,消耗了冒號前的所有內容。然後它會問這個問題:我們是否在輸入的末尾(\z)?不,因爲下一個冒號。所以,它預期的結束輸入,但得到了冒號。

您必須更改正則表達式,以便它不會消耗冒號前的最後一個標識符。例如:

def message = """[\w\d\s\.]+(?![:\w])""".r 

順便說一句,當你使用def你強迫表達重新評估。換句話說,每次調用每個def都會創建一個解析器。正則表達式在每次處理它們所屬的解析器時被實例化。如果您將所有內容更改爲val,則您的性能將得到提高。

請記住,這些東西定義爲的解析器,他們不會運行它。運行解析器的是parseAll

+0

感謝Daniel,非常清晰,寫得很好的解釋 – 2009-11-25 21:11:42