Scalaz来救援:-)
当您处理警告时,退出解析器并不是一个好主意。您可以轻松地将解析器与 Scalaz writer monad 结合起来。使用此 monad,您可以在解析器运行期间将消息添加到部分结果。这些消息可能是信息、警告或错误。解析器完成后,您可以验证结果是否可以使用或是否包含严重问题。通过这样一个单独的验证器步骤,您通常会收到更好的错误消息。例如,您可以接受字符串末尾的任意字符,但在找到它们时会发出错误(例如“在最后一条语句后发现垃圾”)。该错误消息对用户来说可能比您在下面的示例中得到的神秘默认消息更有帮助(“字符串匹配正则表达式 `\z' 预期 [...]”)。
这是一个基于您问题中的代码的示例:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import util.parsing.combinator.RegexParsers
import scalaz._, Scalaz._
object DemoParser extends RegexParsers {
type Warning = String
case class Equation(left : String, right : String)
type PWriter = Writer[Vector[Warning], List[Equation]]
val emptyList : List[Equation] = Nil
def rep1sep2[T](p : => Parser[T], q : => Parser[Any]): Parser[List[T]] =
p ~ rep(q ~> p) ^^ {case x~y => x::y}
def name : Parser[String] = """\w+""".r
def equation : Parser[Equation] = name ~ "=" ~ name ^^ { case n ~ _ ~ v => Equation(n,v) }
def commaList : Parser[PWriter] = rep1sep(equation, ",") ^^ (_.set(Vector()))
def danglingComma : Parser[PWriter] = opt(",") ^^ (
_ map (_ => emptyList.set(Vector("Warning: Dangling comma")))
getOrElse(emptyList.set(Vector("getOrElse(emptyList.set(Vector(""))))
def danglingList : Parser[PWriter] = commaList ~ danglingComma ^^ {
case l1 ~ l2 => (l1.over ++ l2.over).set(l1.written ++ l2.written) }
def apply(input: String): PWriter = parseAll(danglingList, input) match {
case Success(result, _) => result
case failure : NoSuccess => emptyList.set(Vector(failure.msg))
}
}
// Exiting paste mode, now interpreting.
import util.parsing.combinator.RegexParsers
import scalaz._
import Scalaz._
defined module DemoParser
scala> DemoParser("a=1, b=2")
res2: DemoParser.PWriter = (Vector(),List(Equation(a,1), Equation(b,2)))
scala> DemoParser("a=1, b=2,")
res3: DemoParser.PWriter = (Vector(Warning: Dangling comma),List(Equation(a,1), Equation(b,2)))
scala> DemoParser("a=1, b=2, ")
res4: DemoParser.PWriter = (Vector(Warning: Dangling comma),List(Equation(a,1), Equation(b,2)))
scala> DemoParser("a=1, b=2, ;")
res5: DemoParser.PWriter = (Vector(string matching regex `\z' expected but `;' found),List())
scala>
如您所见,它可以很好地处理错误情况。如果要扩展示例,请为不同类型的错误添加案例类,并在消息中包含当前解析器位置。
顺便提一句。空白的问题由RegexParsers
班级处理。如果要更改空格的处理,只需覆盖字段whiteSpace
。