It can be done returning Failure
, which will pass the ball to the subsequent alternative (do not confuse Failure
with failure
. The second will stop the parsing)
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
This enables the real semantic dispatching of the productions
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))
}
Here names are parsed. Parser first tries an identifier. It semantically checks if the identifier is known in the context as function or package. If it does not, it falls back to simple identifier parser. This is stupid but this is what I asked for. The demo parsing is
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)
^
The errors are expected: f1
is the only defined function and aa
is not one of them. Therefore, it is consumed as identifier, leaving (b)
unconsumed. Similarly, cc
is consumed as simple identifier because it is not a defined package.