Last active
November 6, 2017 05:47
-
-
Save lihaoyi/51e4b50e550d32154a62c3e7cd401efd to your computer and use it in GitHub Desktop.
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
package fasterparser | |
import scala.collection.mutable | |
object Parse { | |
def main(args: Array[String]): Unit = { | |
def hello[_:Ctx] = P( "hello" ) | |
def world[_:Ctx] = P( "world" ) | |
def helloWorld[_:Ctx] = P( hello.! ~ (" " | ",").rep ~ world.! ) | |
println(hello("hello ")) // Parsed.Success(()) | |
println(helloWorld("hello ")) // Parsed.Failure(6) | |
println(helloWorld("hello world"))// Parsed.Success((hello,world)) | |
println(helloWorld("hello world"))// Parsed.Success((hello,world)) | |
println(helloWorld("hello, world"))// Parsed.Success((hello,world)) | |
println(Json.jsonExpr("31337")) | |
println(Json.jsonExpr("31337")) | |
println(Json.jsonExpr("\"31337\"")) | |
println(Json.jsonExpr("[true, 123]")) | |
println(Json.jsonExpr("""{"hello": [true, 123], "world": {"foo": {"bar": "baz"}}}""")) | |
} | |
type P[+T] = Parsed[T] | |
def P[T](t: => Parsed[T])(implicit ctx: Ctx[_], name: sourcecode.Name): Parsed[T] = { | |
ctx.stack.append(name.value) | |
val res = t | |
ctx.stack.remove(ctx.stack.length - 1) | |
res | |
} | |
implicit def strToParsed(s: String)(implicit ctx: Ctx[_]): Parsed[Unit] = { | |
if (ctx.input.startsWith(s, ctx.position)) { | |
ctx.position = ctx.position + s.length | |
ctx.isSuccess = true | |
val res = ctx.success.asInstanceOf[Parsed.Success[Unit]] | |
res.value = () | |
res | |
}else{ | |
ctx.isSuccess = false | |
ctx.failure.index = ctx.position | |
ctx.failure | |
} | |
} | |
implicit class EagerOps[S, T](parse0: S)(implicit conv: S => Parsed[T]){ | |
def ~/[V, R](other: => Parsed[V])(implicit s: Implicits.Sequencer[T, V, R]): Parsed[R] = { | |
this ~ other | |
} | |
def ~/ : Parsed[T] = { | |
conv(parse0) | |
} | |
def ~[V, R](other: => Parsed[V])(implicit s: Implicits.Sequencer[T, V, R]): Parsed[R] = { | |
conv(parse0) match{ | |
case f: Parsed.Failure => f | |
case p: Parsed.Success[T] => | |
val pValue = p.value | |
other match{ | |
case f: Parsed.Failure => f | |
case p: Parsed.Success[V] => | |
val r = p.asInstanceOf[Parsed.Success[R]] | |
val applied = s.apply(pValue, p.value) | |
r.value = applied | |
r | |
} | |
} | |
} | |
} | |
implicit class ByNameOps[S, T](parse0: => S)(implicit conv: S => Parsed[T], ctx: Ctx[_]){ | |
def rep[V](implicit repeater: Implicits.Repeater[T, V]): Parsed[V] = rep(sep=null) | |
def rep[V](sep: => Parsed[_] = null)(implicit repeater: Implicits.Repeater[T, V]): Parsed[V] = { | |
val acc = repeater.initial | |
def end() = { | |
val res = ctx.success.asInstanceOf[Parsed.Success[V]] | |
res.value = repeater.result(acc) | |
res | |
} | |
def rec(): Parsed[V] = { | |
conv(parse0) match{ | |
case f: Parsed.Failure => end() | |
case s: Parsed.Success[T] => | |
repeater.accumulate(s.value, acc) | |
val sep1 = sep | |
if (sep1 == null) rec() | |
else sep1 match{ | |
case s: Parsed.Success[_] => rec() | |
case f: Parsed.Failure => end() | |
} | |
} | |
} | |
rec() | |
} | |
def log()(implicit name: sourcecode.Name): Parsed[T] = { | |
println("Starting " + name.value + " " + ctx.position) | |
val res = conv(parse0) | |
println("Ending " + name.value + " " + ctx.position + " " + ctx.isSuccess) | |
res | |
} | |
def ! : Parsed[String] = { | |
val startPos = ctx.position | |
conv(parse0) match{ | |
case f: Parsed.Failure => f | |
case s: Parsed.Success[_] => | |
val endPos= ctx.position | |
val ret = s.asInstanceOf[Parsed.Success[String]] | |
ret.value = ctx.input.substring(startPos, endPos) | |
ret | |
} | |
} | |
def ?[V](implicit optioner: Implicits.Optioner[T, V]): Parsed[V] = { | |
val startPos = ctx.position | |
val res = ctx.success.asInstanceOf[Parsed.Success[V]] | |
conv(parse0) match{ | |
case f: Parsed.Failure => | |
res.value = optioner.none | |
ctx.position = startPos | |
res | |
case s: Parsed.Success[T] => | |
res.value = optioner.some(s.value) | |
res | |
} | |
} | |
def |[V >: T](other: => Parsed[V]): Parsed[V] = { | |
val startPos = ctx.position | |
conv(parse0) match { | |
case p: Parsed.Success[T] => p | |
case f: Parsed.Failure => | |
other match{ | |
case p: Parsed.Success[V] => p | |
case f: Parsed.Failure => | |
f.index = startPos | |
f | |
} | |
} | |
} | |
} | |
def CharsWhileIn(s: String)(implicit ctx: Ctx[_]) = CharsWhile(s.toSet) | |
def CharIn(s: CharSequence*)(implicit ctx: Ctx[_]) = { | |
val set = s.flatMap(_.toString).toSet | |
def currentCharMatches = ctx.input.lift(ctx.position).exists(set) | |
if (!currentCharMatches) { | |
ctx.isSuccess = false | |
ctx.failure | |
}else{ | |
ctx.position += 1 | |
val res = ctx.success.asInstanceOf[Parsed.Success[Unit]] | |
res.value = () | |
res | |
} | |
} | |
def CharsWhile(p: Char => Boolean)(implicit ctx: Ctx[_]) = { | |
def currentCharMatches = ctx.input.lift(ctx.position).exists(p) | |
if (!currentCharMatches) { | |
ctx.isSuccess = false | |
ctx.failure | |
}else{ | |
while(currentCharMatches) ctx.position += 1 | |
val res = ctx.success.asInstanceOf[Parsed.Success[Unit]] | |
res.value = () | |
res | |
} | |
} | |
implicit def parseInputCtx(s: String): Ctx[_] = Ctx(s) | |
} | |
class Ctx[+_](val input: String, | |
var position: Int, | |
val stack: mutable.Buffer[String], | |
val success: Parsed.Success[_], | |
val failure: Parsed.Failure, | |
var isSuccess: Boolean) | |
object Ctx{ | |
def apply(input: String) = { | |
new Ctx(input, 0, mutable.Buffer.empty, new Parsed.Success, new Parsed.Failure, true) | |
} | |
} | |
abstract class Parsed[+T](val isSuccess: Boolean){ | |
def map[V](f: T => V): Parsed[V] | |
} | |
object Parsed{ | |
class Success[T] extends Parsed[T](true){ | |
var value: T = null.asInstanceOf[T] | |
def map[V](f: T => V) = { | |
val this2 = this.asInstanceOf[Success[V]] | |
this2.value = f(value) | |
this2 | |
} | |
override def toString() = s"Parsed.Success($value)" | |
} | |
class Failure extends Parsed[Nothing](false){ | |
var index = 0 | |
override def toString() = s"Parsed.Failure($index)" | |
def map[V](f: Nothing => V) = this | |
} | |
} | |
object Js { | |
sealed trait Val extends Any { | |
def value: Any | |
def apply(i: Int): Val = this.asInstanceOf[Arr].value(i) | |
def apply(s: java.lang.String): Val = | |
this.asInstanceOf[Obj].value.find(_._1 == s).get._2 | |
} | |
case class Str(value: java.lang.String) extends AnyVal with Val | |
case class Obj(value: (java.lang.String, Val)*) extends AnyVal with Val | |
case class Arr(value: Val*) extends AnyVal with Val | |
case class Num(value: Double) extends AnyVal with Val | |
case object False extends Val{ | |
def value = false | |
} | |
case object True extends Val{ | |
def value = true | |
} | |
case object Null extends Val{ | |
def value = null | |
} | |
} | |
case class NamedFunction[T, V](f: T => V, name: String) extends (T => V){ | |
def apply(t: T) = f(t) | |
override def toString() = name | |
} | |
object Json{ | |
import Parse._ | |
def StringChars[_:Ctx] = NamedFunction(!"\"\\".contains(_: Char), "StringChars") | |
def space[_:Ctx] = P( CharsWhileIn(" \r\n").? ) | |
def digits[_:Ctx] = P( CharsWhileIn("0123456789")) | |
def exponent[_:Ctx] = P( CharIn("eE") ~ CharIn("+-").? ~ digits ) | |
def fractional[_:Ctx] = P( "." ~ digits ) | |
def integral[_:Ctx] = P( "0" | CharIn('1' to '9') ~ digits.? ) | |
def number[_:Ctx] = P( CharIn("+-").? ~ integral ~ fractional.? ~ exponent.? ).!.map( | |
x => Js.Num(x.toDouble) | |
) | |
def `null`[_:Ctx] = P( "null" ).map(_ => Js.Null) | |
def `false`[_:Ctx] = P( "false" ).map(_ => Js.False) | |
def `true`[_:Ctx] = P( "true" ).map(_ => Js.True) | |
def hexDigit[_:Ctx] = P( CharIn('0'to'9', 'a'to'f', 'A'to'F') ) | |
def unicodeEscape[_:Ctx] = P( "u" ~ hexDigit ~ hexDigit ~ hexDigit ~ hexDigit ) | |
def escape[_:Ctx] = P( "\\" ~ (CharIn("\"/\\bfnrt") | unicodeEscape) ) | |
def strChars[_:Ctx] = P( CharsWhile(StringChars) ) | |
def string[_:Ctx] = | |
P( space ~ "\"" ~/ (strChars | escape).rep.! ~ "\"").map(Js.Str) | |
def array[_:Ctx] = | |
P( "[" ~/ jsonExpr.rep(sep=",".~/) ~ space ~ "]").map(Js.Arr(_:_*)) | |
def pair[_:Ctx] = P( string.map(_.value) ~/ ":" ~/ jsonExpr ) | |
def obj[_:Ctx] = | |
P( "{" ~/ pair.rep(sep=",".~/) ~ space ~ "}").map(Js.Obj(_:_*)) | |
def jsonExpr[_:Ctx]: P[Js.Val] = P( | |
space ~ (obj | array | string | `true` | `false` | `null` | number) ~ space | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Full repo, including build files and codegen https://github.com/lihaoyi/fasterparser