Skip to content

Instantly share code, notes, and snippets.

@jasonjckn
Created May 20, 2014 01:41
Show Gist options
  • Save jasonjckn/5cfa1e2c7768f2039154 to your computer and use it in GitHub Desktop.
Save jasonjckn/5cfa1e2c7768f2039154 to your computer and use it in GitHub Desktop.
basic combinator parser in scala
abstract class Result[T]
case class Failed[T](desc: String) extends Result[T]
case class Consumed[T](value: T, rest: String) extends Result[T]
trait Parser[T] extends ((String) => Result[T]) {
def flatMap[A](v2m: ((T) => Parser[A])): Parser[A] = {
new Parser[A] {
def apply(input: String) = {
Parser.this(input) match {
case Consumed(v, rest) => v2m(v)(rest)
case Failed(desc) => Failed[A](desc)
}
}
}
}
def map[A](v2v: (T => A)): Parser[A] = flatMap(v2v andThen value)
def value[A](v: A): Parser[A] = new Parser[A] {
def apply(input: String) = Consumed(v, input)
}
def rescue(p: Parser[T]) : Parser[T] = {
new Parser[T] {
def apply(input: String) : Result[T] = {
Parser.this(input) match {
case x@Consumed(_, _) => x
case Failed(desc) => p(input)
}
}
}
}
}
object Parsers {
def stringP(s: String) = new Parser[String] {
def apply(input: String) = {
if(input.startsWith(s)) Consumed(s, input.substring(s.length))
else Failed[String]("Could not match on " + s)
}
}
def digit() = new Parser[String] {
def apply(input: String) = {
if(input.length > 0 && input.charAt(0) >= '0' && input.charAt(0) <= '9')
Consumed(input.substring(0,1), input.substring(1))
else
Failed[String]("Could not match on digit")
}
}
def many1[A](p : Parser[A]) : Parser[List[A]] = p flatMap ((a) => (many1(p) rescue p.value(List.empty)) map ((lst) => List(a) ::: lst))
def sepBy[A, B](sep: Parser[A], p: Parser[B]) = many1(p flatMap (v => sep map (_ => v))) flatMap (lst => p map ((a) => List(a) ::: lst))
def number = many1(digit()) map (_.reduce(_ ++ _)) map Integer.parseInt
def parens[A](p: Parser[A]) = stringP("(") flatMap (x => p) flatMap (v => stringP(")") map (_ => v))
def comma = stringP(",")
}
object Main {
def main(args : Array[String]) {
import Parsers._
// parse numbers seperated by comma and then sum it.
val parser = parens(sepBy(comma, number) map (_.sum))
val input = "(1000,100,10,1)"
println(parser(input))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment