Created
May 20, 2014 01:41
-
-
Save jasonjckn/5cfa1e2c7768f2039154 to your computer and use it in GitHub Desktop.
basic combinator parser in scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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