我正在尝试在基于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 "") }
| ...
但我对如何定义利用此功能的Parser感到困惑。我有一个解析器定义为:
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子类上使用扩展的正确方法是什么?
答案 0 :(得分:5)
如果你真的想使用基于令牌的解析,并重用StdLexical,我建议更新“TargetFolder”的语法,以便等号后面的值是一个正确的字符串文字。或者换句话说,使路径应该用引号括起来。从那时起,您不再需要扩展StdLexical。
然后出现了将正则表达式转换为解析器的问题。 Scala已经为此RegexParsers
(隐式将正则表达式转换为Parser[String]
),但不幸的是,这不是您想要的,因为它适用于Char
(type Elem = Char
的流在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"
^
请注意,这里也修复了你的“目标文件夹”regexp,你在两个替代方案中缺少了parens,加上不需要的空格。
答案 1 :(得分:0)
如果您希望从regex
获得Parser[String]
,请致电您的函数Regex
:
def p: Parser[String] = regex("".r)
或者让regex
隐式让编译器自动为你调用它:
implicit def regex(r: Regex): Parser[String] = ...
// =>
def p: Parser[String] = "".r