2013-01-09 61 views
2

我想在基於StandardTokenParsers的解析器中使用正則表達式。對於這一點,我已經子類StdLexical如下:在StandardTokenParsers中使用正則表達式

class CustomLexical extends StdLexical{ 
    def regex(r: Regex): Parser[String] = new Parser[String] { 
    def apply(in:Input) = r.findPrefixMatchOf(in.source.subSequence(in.offset, in.source.length)) match { 
     case Some(matched) => Success(in.source.subSequence(in.offset, in.offset + matched.end).toString, 
     in.drop(matched.end)) 
     case None => Failure("string matching regex `" + r + "' expected but " + in.first + " found", in) 
    } 
    } 

    override def token: Parser[Token] = 
    ( regex("[a-zA-Z]:\\\\[\\w\\\\?]* | /[\\w/]*".r)  ^^ { StringLit(_) } 
     | identChar ~ rep(identChar | digit)    ^^ { case first ~ rest => processIdent(first :: rest mkString "") } 
     | ... 

但我對我將如何定義一個分析器,它充分利用了這一點困惑。我有一個解析器定義爲:

def mTargetFolder: Parser[String] = "TargetFolder" ~> "=" ~> mFilePath 

應該用來識別有效的文件路徑。我試過了:

def mFilePath: Parser[String] = "[a-zA-Z]:\\\\[\\w\\\\?]* | /[\\w/]*".r 

但是這顯然是不正確的。我得到一個錯誤:

scala: type mismatch; 
found : scala.util.matching.Regex 
required: McfpDSL.this.Parser[String] 
    def mFilePath: Parser[String] = "[a-zA-Z]:\\\\[\\w\\\\?]* | /[\\w/]*".r 
                     ^

在我的StdLexical子類上使用擴展的正確方法是什麼?

回答

5

如果您真的想使用基於令牌的解析,並重用StdLexical,我會建議更新「TargetFolder」的語法,以便等號後面的值是合適的字符串文字。換句話說,使路徑應該用引號括起來。從這一點開始,你不需要再擴展StdLexical了。

然後出現將正則表達式轉換爲解析器的問題。斯卡拉已經有RegexParsers爲此(它隱式轉換正則表達式爲Parser[String]),但不幸的是,這不是你想要在這裏,因爲它在Chartype Elem = CharRegexParsers)的流上工作,而你正在處理一個令牌的sttream。 所以我們確實必須定義我們自己從Regex到Parser[String]的轉換(但是在句法層面而不是詞法層面,或者換句話說在令牌解析器中)。

import scala.util.parsing.combinator.syntactical._ 
import scala.util.matching.Regex 
import scala.util.parsing.input._ 

object MyParser extends StandardTokenParsers { 
    import lexical.StringLit 
    def regexStringLit(r: Regex): Parser[String] = acceptMatch( 
    "string literal matching regex " + r, 
    { case StringLit(s) if r.unapplySeq(s).isDefined => s } 
) 
    lexical.delimiters += "=" 
    lexical.reserved += "TargetFolder" 
    lazy val mTargetFolder: Parser[String] = "TargetFolder" ~> "=" ~> mFilePath 
    lazy val mFilePath: Parser[String] = regexStringLit("([a-zA-Z]:\\\\[\\w\\\\?]*)|(/[\\w/]*)".r) 
    def parseTargetFolder(s: String) = { mTargetFolder(new lexical.Scanner(s)) } 
} 

例子:

scala> MyParser.parseTargetFolder("""TargetFolder = "c:\Dir1\Dir2" """) 
res12: MyParser.ParseResult[String] = [1.31] parsed: c:\Dir1\Dir2 

scala> MyParser.parseTargetFolder("""TargetFolder = "/Dir1/Dir2" """) 
res13: MyParser.ParseResult[String] = [1.29] parsed: /Dir1/Dir2 

scala> MyParser.parseTargetFolder("""TargetFolder = "Hello world" """) 
res14: MyParser.ParseResult[String] = 
[1.16] failure: identifier matching regex ([a-zA-Z]:\\[\w\\?]*)|(/[\w/]*) expected 
TargetFolder = "Hello world" 
     ^

注意,也修正了自己的「目標文件夾」正規這裏,你已經失蹤圍繞兩個選擇,再加上不需要的空間括號。

+0

感謝您的回覆。我提出了你的建議,但似乎StdLexical正在開發和處理「Z:\ ...」,所以它處理'Z'作爲標識符 - >'標識符Z',我得到這個錯誤:「string文字匹配正則表達式([a-zA-Z]:\\ [\ w \\?] *)|(/ [\ w /] *)在位置2,26處找到。在我使用自己的實現覆蓋詞法之後,它就消失了,它在達到標識符事件之前處理正則表達式。不太確定你爲什麼沒有得到那個錯誤。 – Dan

+1

因爲我非常自由地改變語法:dir路徑必須是合適的字符串文字(帶引號)。沒有這個改變,一切都變得更加複雜。您可以將路徑標記作爲標識符(因此不需要引號),但是您需要在標識符中使用「/」,「:」和「\」(這是您顯然要做的),然後這會影響**每個**標識符(從而破壞解析)。核心問題是詞法分析器和解析器是分開的,如果你想要一個路徑或標識符(它沒有上下文),詞法分析器不知道它何時解析char流。 –

+0

避免這些問題的另一個解決方案是簡單地不使用基於標記的解析(其中有兩個單獨的部分:詞法分析器和語法解析器),而是將所有內容都放在同一級別。通過示例定義'對象MyParser使用RegexParsers擴展StdLexical'並且在那裏做所有事情。因爲沒有分離,所以你知道每個地方的上下文,作爲一個獎勵,「RegexParsers」會自動將每個正則表達式轉換爲合適的分析器。 –

0

只需調用函數regex,當你想獲得一個Parser[String]Regex

def p: Parser[String] = regex("".r) 

或者使regex隱含讓編譯器自動調用它你:

implicit def regex(r: Regex): Parser[String] = ... 
// => 
def p: Parser[String] = "".r 
+0

'p'用'regex'定義,'regex'用'p'定義。可以嗎? –

+0

'regex'沒有用'p'來定義,你爲什麼這麼認爲? – sschaef