问题
How would you do that in the parser combinators
def namedAfterPos[P, N](pos: Parser[P], nmd: Parser[N], sep: Parser[_] = ",") = ???
List("a", "a,a,a", "a,a,a=b,a=b", "a=b, a=b") map (_ parseWith namedAfterPos("a", "a=b")) map {case Success(res, _) => res}
val Failure("positional is not expected after named", pos) = "a,a=b,a" parseWith namedAfterPos("a", "a=b")
回答1:
Ok, here is mind approach
scala> def namedAfterPos[P, N](pos: Parser[P], nmd: Parser[N], sep: Parser[_] = ",") = {
// NB! http://stackoverflow.com/q/38041980/6267925 disucsses that that defining type
// aliases for result type is a bad programming practice that we use! Nevertheless,
// define result type -- first comes the list of positional params, second - named.
type Res = (List[P], List[N]); // named and positional have different types
def namedOrPositional(positional: Boolean, acc: Res): Parser[Res] = {
// once association accepted, we'll try the comma and go for next or exit
def recur(positional: Boolean, acc: Res) =
(sep flatMap {_ => namedOrPositional(positional, acc)}) | success(acc);
// named association should always be acceptable - stop accepting positionals when found
(nmd flatMap {n => recur(false, acc match {case (p,nn)=> (p, n :: nn)})}) |
// if named failed - we have positional arg
(pos flatMap {p =>
// proceed if positionals are still accepted
if (positional) recur(true, acc match {case (pp, n) => (p :: pp, n)})
// if they are not - report parsing failure
else failure("positional is not expected after named")})
};
namedOrPositional(true, (Nil, Nil)) // start collecting the args
}
defined function namedAfterPos
scala> List("a", "a,a,a", "a,a,a=b,a=b", "a=b, a=b") map (
_ p namedAfterPos("a", "a=b")) map {case Success(res, _) => res}
res67: List[(List[P], List[N])] = List((List(a), List()), (List(a, a, a), List()), (List(a, a), List(a=b, a=b)), (List(), List(a=b, a=b)))
val Failure("positional is not expected after named", pos) = "a,a=b,a" p namedAfterPos("a", "a=b")
pos: Input = scala.util.parsing.input.CharSequenceReader@1726cd4
// named param is usually has a form of identifier "=" expr and positionals are expressions
scala> def paredArgList[K,V](name: Parser[K] = identifier, value: Parser[V] = expr) =
pared(namedAfterPos(value, name ~ ("=" ~> value) map {case n~v => (n,v)}))
defined function paredArgList
scala> List("a+b-1", "b=1+1", "a,a+1", "b=3+1,c=c+1", "1,b=g+g,d=123,bd=123+1") map ("(" + _ + ")" p paredArgList()) map {case Success(res, _) => res}
res70: List[(List[P], List[N])] = List((List(a + b - 1), List()), (List(), List((b,1 + 1))), (List(a + 1, a), List()), (List(), List((c,c + 1), (b,3 + 1))), (List(1), List((bd,123 + 1), (d,123), (b,g + g))))
来源:https://stackoverflow.com/questions/38037089/positional-before-named-argument-list-parsing