package MyParser
import Character._
case class Parser[A](parse: List[Char] => Option[(List[Char], A)]) {
def |||(p2: => Parser[A]): Parser[A] = Parser(s => this parse s match {
case v@Some(_) => v
case None => p2 parse s
})
def >>>[B](q: => Parser[B]): Parser[B] = Parser.bindParser[A, B](this, _ => q)
}
object Parser {
def value[A](a: A): Parser[A] = Parser(s => Some(s, a))
def failed[A]: Parser[A] = Parser(s => None)
def character: Parser[Char] = Parser[Char] {
case Nil => None
case c::r => Some(r, c)
}
def mapParser[A, B](p: Parser[A], f: A => B): Parser[B] = Parser(s => p parse s match {
case Some((r, c)) => Some(r, f(c))
case None => None
})
def bindParser[A, B](p: Parser[A], f: A => Parser[B]): Parser[B] = Parser(s => p parse s match {
case Some((r, c)) => f(c) parse r
case None => None
})
def sequenceParser[A](ps: List[Parser[A]]): Parser[List[A]] = ps match {
case Nil => value(Nil)
case h::t => bindParser(h, (a: A) => mapParser(sequenceParser(t), (as: List[A]) => a :: as))
}
def thisMany[A](n: Int, p: Parser[A]): Parser[List[A]] = sequenceParser(List.make(n, p))
def list[A](k: Parser[A]): Parser[List[A]] = many1(k) ||| value(Nil)
def many1[A](k: Parser[A]): Parser[List[A]] = bindParser(k, (kx: A) => mapParser(list(k), (kkx: List[A]) => kx :: kkx))
def satisfy(p: Char => Boolean): Parser[Char] = bindParser(character, (c: Char) => if(p(c)) value(c) else failed)
def is(c: Char): Parser[Char] = satisfy(_ == c)
val digit: Parser[Char] = satisfy(isDigit(_))
val natural: Parser[Int] = mapParser(list(digit), (_: List[Char]).mkString.toInt)
val space: Parser[Char] = satisfy(isWhitespace(_))
val spaces: Parser[List[Char]] = many1(space)
val lower: Parser[Char] = satisfy(isLowerCase(_))
val upper: Parser[Char] = satisfy(isUpperCase(_))
val alpha: Parser[Char] = satisfy(isLetter(_))
val alphaNum: Parser[Char] = satisfy(isLetterOrDigit(_))
}
case class Person(age: Int, firstName: String, surname: String, gender: Char, phone: String)
object Person {
import Parser._
val ageParser: Parser[Int] = natural
val firstNameParser: Parser[String] = bindParser(upper, (c: Char) => mapParser(list(lower), (cs: List[Char]) => (c :: cs).mkString))
val surnameParser: Parser[String] = bindParser(upper, (c: Char) => bindParser(thisMany(5, lower), (cs: List[Char]) => mapParser(list(lower), (t: List[Char]) => (c :: cs ::: t).mkString)))
val genderParser: Parser[Char] = is('m') ||| is('f')
val phoneBodyParser: Parser[String] = mapParser(list(digit ||| is('.') ||| is('-')), (_: List[Char]).mkString)
val phoneParser: Parser[String] = bindParser(digit, (d: Char) => bindParser(phoneBodyParser, (z: String) => mapParser(is('#'), (_: Char) => (d :: z.toList).mkString)))
val personParser1: Parser[Person] = bindParser(ageParser, (age: Int) =>
spaces >>>
bindParser(firstNameParser, (firstName: String) =>
spaces >>>
bindParser(surnameParser, (surname: String) =>
spaces >>>
bindParser(genderParser, (gender: Char) =>
spaces >>>
bindParser(phoneParser, (phone: String) =>
value(Person(age, firstName, surname, gender, phone)))))))
implicit def PersonMonad[A](p: Parser[A]) = new {
def map[B](f: A => B) = mapParser(p, f)
def flatMap[B](f: A => Parser[B]) = bindParser(p, f)
}
val personParser2: Parser[Person] = for(age <- ageParser;
_ <- spaces;
firstName <- firstNameParser;
_ <- spaces;
surname <- surnameParser;
_ <- spaces;
gender <- genderParser;
_ <- spaces;
phone <- phoneParser)
yield Person(age, firstName, surname, gender, phone)
}