语义导向的解析器组合子



我试过这个

import scala.util.parsing.combinator._  
def name = ident ^^ {case ident => if (ident.contains("a")) ident.toUpperCase else ident 
println(parseAll(name, "aa")) // parsed: AA
println(parseAll(name, "bb")) 

带输出

[1.3] parsed: AA
[1.3] parsed: bb
[1.1] failure: `(' expected but `a' found
aa
^
[1.3] failure: end of input expected
f1(aa)
  ^

正如您所看到的,第二次解析失败。第一次生产的失败似乎阻止了人们尝试第二次生产。实际上,我希望根据第一个标识符的值来选择这个或那个解析器以继续。

可以返回Failure,这将把球传给后续的备选方案(不要混淆Failurefailure。第二个将停止解析)

def name = new Parser[String] {
  def apply(s: Input) = ident(s) match {
    case Success(ident, rem) => if (ident.contains("a")) Success(ident.toUpperCase, rem) else Failure("identifier with 'a' expected", s)
    case a => a
  }
} | ident 

这使得能够对产品进行真正的语义调度

def pkg(prefix: String): Parser[_] = "." ~> name ^^ {case arg => s"P:$prefix.$arg"}
def function(fID: String): Parser[_] = "(" ~> name <~ ")" ^^ {case arg => s"F:$fID($arg)"}
val env = Map("p1" -> pkg _, "p2" -> pkg _, "f1" -> function _)
def name = new Parser[Any] {
  def apply(s: Input) = ident(s) match {
    case Success(ident, rem) => env.get(ident) match {
        case Some(parser) => parser(ident)(rem)
        case _ => Failure(s"object $ident not found", s)
    } ; case a => a // how can we monade this identity?
  }
} | ident
// test inputs
List("aaa","f1(bb)","p1.p2.c","f1(f1(c))","f1(f1(p1.f1(bbb)))","aaa(bb(.cc(bbb)))") foreach {
    input => println(s"$input => " + parseAll(name, input))
}

此处解析名称。解析器首先尝试一个标识符。它从语义上检查标识符在上下文中是否已知为函数或包。如果没有,则返回到简单的标识符解析器。这很愚蠢,但这正是我所要求的。演示解析是

aaa => [1.4] parsed: aaa
f1(bb) => [1.7] parsed: F:f1(bb)
p1.p2.c => [1.8] parsed: P:p1.P:p2.c
f1(f1(c)) => [1.10] parsed: F:f1(F:f1(c))
f1(f1(p1.f1(bbb))) => [1.19] parsed: F:f1(F:f1(P:p1.F:f1(bbb)))
aa(b) => [1.3] failure: end of input expected
aa(b)
  ^
f1(cc.dd) => [1.6] failure: `)' expected but `.' found
f1(cc.dd)
     ^

错误是意料之中的:f1是唯一定义的函数,而aa不是其中之一。因此,它被用作标识符,而不使用(b)。类似地,cc被用作简单标识符,因为它不是一个定义的包。

最新更新