Это можно сделать возвращение Failure
, который будет проходить мяч последующей альтернативе (не путать Failure
с failure
. Второй остановит синтаксический)
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))
}
Здесь разобраны имена. Сначала Parser пытается идентифицировать идентификатор. Он семантически проверяет, известен ли идентификатор в контексте как функция или пакет. Если это не так, оно возвращается к простому синтаксическому анализатору. Это глупо, но это то, о чем я просил. Демонстрационная парсинг
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
используется как простой идентификатор, поскольку он не является определенным пакетом.