import scala.util.parsing.combinator1.Parsers /* The goal is to provide combinators to parse any permutation of a given set of parsers, in a type-safe way and providing results in a consistent order. These two requirements are of course related, since type safety will require that results are in a predictable order. We can achieve this, with a couple of major shortcomings. I don't know whether it's possible to eliminate the shortcomings, but I still hope that it is. The implementation is a translation of the Haskell version in this paper: http://citeseer.ist.psu.edu/451549.html Here is an example usage: [~/test]% scalac PermParsers.scala [~/test]% scala Test '(abc)' [1.6] parsed: (a,b,c) [~/test]% scala Test '(bca)' [1.6] parsed: (a,b,c) I'll show the sample code first. The implementation follows. */ import scala.util.parsing.input.CharArrayReader object Test extends Parsers with PermParsers { type Elem = Char def tup3[A,B,C] = (a:A) => (b:B) => (c:C) => (a,b,c) def phrase = '(' ~> permute(tup3 ~~ 'a' ~~ 'b' ~~ 'c') <~ ')' def parse(s: String) = println(phrase(new CharArrayReader(s.toCharArray))) def main(args: Array[String]) = parse(args(0)) } /* Now the implementation. We provide these combinators as an option that can be mixed into the standard Parsers trait. The main thing to note is that efficiency has *not* been my concern. The Haskell version will lazily unfold the permutation tree, while this version constructs it in advance. While it's probably possible to change this in a fairly non-invasive way, I'm not yet satisfied enough with the approach to investigate. The other interesting difference is that we use implicit definitions to provide syntactic sugar, since it's not possible to define infix operations in Scala other than as methods. */ trait PermParsers { this: Parsers => /* Define the permutation tree. See the original paper for discussion. Note that in Scala, we define the map functions more idiomatically as methods on the tree. Note also that the existential type, which is part of the Branch declaration in Haskell, is moved into the definition of Choice. It's very awkward to try to share an existential across multiple constructor parameters in Scala, and even so I had problems getting the existential to work when declared that way. When declared like this, it works as expected. */ trait Perms[A] { def map[B](f: A => B): Perms[B] } case class Empty[A](a: A) extends Perms[A] { def map[B](f: A => B): Perms[B] = Empty(f(a)) } case class Choice[A](chs: List[Branch[A,X] forSome {type X}]) extends Perms[A] { def map[B](f: A => B): Perms[B] = Choice(chs.map(_.map(f))) } case class Branch[A,X](sub: Perms[X => A], p: Parser[X]) { def map[B](f: A => B): Branch[B,Y] forSome { type Y } = Branch(sub.map(f.compose(_)), p) } /* We'll need this standard combinator. */ def flip[A,B,C](f: A => B => C): B => A => C = (b => (a => (f(a)(b)))) /* Add a parser to a permutation tree. Note that the type annotation on flip is necessary. Otherwise it's as in the paper. */ def add[A,B](a: Perms[A=>B], p: Parser[A]): Perms[B] = a match { case t @ Empty(a) => Choice(List(Branch(t, p))) case t @ Choice(chs) => { val first = Branch(t, p) def insert[X](br: Branch[A=>B,X]) = br match { case Branch(sub, px) => Branch(add(sub.map(flip[X,A,B]), p), px) } val others = chs.map(insert(_)) Choice(first :: others) } } /* A parser combinator corresponding to applicative functor application. I'm surprised this is not in the standard library, but it's more idiomatic in Haskell than Scala... In any case, we'll need it. */ def app[A,B](pf: Parser[A=>B], p: Parser[A]): Parser[B] = pf >> (f => p ^^ f) /* Finally, convert a permutation tree to a parser. This is the *inefficient* version from the paper. I can't be bothered to think about efficiency yet, and this version is easier to define. In any case, without introducing laziness, I don't think it matters. Again, the type annotation is necessary. */ def permute[A](ps: Perms[A]): Parser[A] = ps match { case Empty(a) => success(a) case Choice(chs) => val newchs = chs map { case Branch(sub, p) => app(permute(sub), p) } (newchs :\ (failure("no valid permutation"):Parser[A])) (_ | _) } /* The following definitions provide the syntactic sugar. Since ~~ is defined only on Perms[A=>B], it's not possible to put it directly in Perms. So we instead define implicit conversions at the appropriate types. */ class Addable[A,B](ps: Perms[A=>B]) { def ~~(p: Parser[A]): Perms[B] = add(ps, p) } implicit def functionIsAddable[A,B](f: A=>B): Addable[A,B] = new Addable(Empty(f)) implicit def permsIsAddable[A,B](ps: Perms[A=>B]): Addable[A,B] = new Addable(ps) }