3

我正在尝试在基于 StandardTokenParsers 的解析器中使用正则表达式。为此,我将 StdLexical 子类化如下:

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 子类上使用扩展的正确方法是什么?

4

2 回答 2

5

如果您真的想使用基于标记的解析并重用 StdLexical,我建议更新“TargetFolder”的语法,以便等号后面的值是正确的字符串文字。或者换句话说,使它的路径应该用引号括起来。从那时起,您不再需要扩展 StdLexical。

然后是将正则表达式转换为解析器的问题。Scala 已经RegexParsers为此提供了(将正则表达式隐式转换为 a Parser[String]),但不幸的是,这不是您想要的,因为它在您处理令牌流时处理Char( type Elem = Charin ) 流。RegexParsers所以我们确实必须定义我们自己的从 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"
           ^

请注意,这里还修复了您的“目标文件夹”正则表达式,您缺少两个替代方案周围的括号,以及不需要的空格。

于 2013-01-09T16:44:47.563 回答
0

regex当您想Parser[String]从 a中获取 a 时,只需调用您的函数Regex

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

或者regex隐式让编译器为你自动调用它:

implicit def regex(r: Regex): Parser[String] = ...
// =>
def p: Parser[String] = "".r
于 2013-01-09T15:35:18.740 回答