2013-12-13 69 views
3

我一直試圖讓我的頭在斯卡拉的分析器組合器。看起來它們非常強大,但我似乎發現的唯一教程示例是使用數學表達式和幾乎沒有適當的真實世界解析示例,需要解析DSL並將其映射到不同的實體等。斯卡拉分析器組合器將字符列表轉換爲字符串

爲了在這個例子中,可以說我有這個BNF,我有這個實體名爲Model,它由一個字符串組成,如下所示:[model [name <name> ]]。這是一個更大的BNF的簡單例子,現實中有更多的實體。

所以我定義我自己的類Model更是把name的構造函數,然後定義我自己ModelParser對象延伸JavaTokenParsers。然後,我定義了下面的解析器,遵循BNF(我知道有些可能有一個更簡單的正則表達式匹配器,但我更願意遵循BNF準確地出於其他原因)。

def model : Parser[Model] = "[model" ~> "[name" ~> name <~ "]]" ^^ (Model(_)) 
def name : Parser[String] = (letter ~ (anyChar*)) ^^ {case text => text.toString()) 
def anyChar = letter | digit | "_".r | "-".r 
def letter = """[a-zA-Z]""".r 
def digit = """\d""".r 

ModeltoString看起來是這樣的:

override def toString : String = "[model " + name + "]" 

當我嘗試用繩子運行它像[model [name helloWorld]]我得到這個 [model [h~List(e, l, l, o, W, o, r, l, d)]]什麼,而不是我期待[model helloWorld]

如何我能讓這些個人角色重新加入他們最初的字符串嗎?

我也混淆了個別解析器和.r的使用。有時我看到他們只有以下解析器的例子(解析「hello」):

def hello = "hello" 

這不就是一個字符串嗎?它如何突然成爲可與其他解析器結合使用的解析器? .r究竟在做什麼?我已經閱讀了至少3個教程,但仍然完全失去了實際發生的事情。

回答

3

的問題是,anyChar*解析List[String](其中在這種情況下,每個字符串是單個字符)和字符串列表上調用toString的結果"List(...)",不是你想通過連接的內容獲取字符串。另外,case text =>模式在整個letter ~ (anyChar*)上匹配,而不僅僅是anyChar*部分。

這是可能的解決這兩個問題很直截了當:

case class Model(name: String) { 
    override def toString : String = "[model " + name + "]" 
} 

import scala.util.parsing.combinator._ 

object ModelParser extends RegexParsers { 
    def model: Parser[Model] = "[model" ~> "[name" ~> name <~ "]]" ^^ (Model(_)) 

    def name: Parser[String] = letter ~ (anyChar*) ^^ { 
    case first ~ rest => (first :: rest).mkString 
    } 

    def anyChar = letter | digit | "_".r | "-".r 
    def letter = """[a-zA-Z]""".r 
    def digit = """\d""".r 
} 

我們只是第一個字符串追加到其餘的名單,然後調用mkString整個列表上,這將串連內容。這按預期工作:

scala> ModelParser.parseAll(ModelParser.model, "[model [name helloWorld]]") 
res0: ModelParser.ParseResult[Model] = [1.26] parsed: [model helloWorld] 

當你注意,有可能(也可能是更清晰,更高性能的),讓正則表達式做更多的工作:

object ModelParser extends RegexParsers { 
    def model: Parser[Model] = "[model" ~> "[name" ~> name <~ "]]" ^^ (Model(_)) 

    def name: Parser[String] = """[a-zA-Z\d_-]+""".r 
} 

這個例子也說明解析組合器庫使用隱式轉換來減少編寫解析器的一些冗長的方式。正如你所說,def hello = "hello"定義字符串,"[a-zA-Z]+".r定義Regex(通過the r method on StringOps),但由於RegexParsers定義從String隱式轉換既可以作爲一個解析器(這一個名爲literal)和Regexregex)到Parser[String]

+0

http://www.artima.com/pins1ed/combinator-parsing.html#31.7 –

+0

非常感謝您的澄清,尤其是'.r'混淆以及String文字與'Parser [String ]'。 'name'分析器現在工作正常! – jbx

+0

@Travis我注意到,出於某種原因,即使'[名稱hello World]]被接受並在解析爲helloWorld後再現。我如何強制它不接受名稱部分,如果它有空格? 〜似乎允許它很好。我不想完全禁用解析器,因爲它非常有用。 – jbx

相關問題