diff --git a/src/main/scala/org/spartanz/parserz/Expr.scala b/src/main/scala/org/spartanz/parserz/Expr.scala new file mode 100644 index 0000000..c1310db --- /dev/null +++ b/src/main/scala/org/spartanz/parserz/Expr.scala @@ -0,0 +1,32 @@ +package org.spartanz.parserz + +sealed trait Expr[A] + +object Expr { + + private[parserz] case class Equals[A](a: A) extends Expr[A] + private[parserz] case class Not[A](a: A) extends Expr[A] + private[parserz] case class InSet[A](as: Set[A]) extends Expr[A] + private[parserz] case class Condition[A](f: A => Boolean) extends Expr[A] + + def === [A](a: A): Expr[A] = Equals(a) + def =!= [A](a: A): Expr[A] = Not(a) + def in[A](a1: A, an: A*): Expr[A] = InSet(Set(an: _*) + a1) + def cond[A](f: A => Boolean): Expr[A] = Condition(f) + + private[parserz] def exprFilter[A](expr: Expr[A]): A => Boolean = + expr match { + case Equals(a) => _ == a + case Not(a) => _ != a + case InSet(as) => as.contains + case Condition(f) => f + } + + private[parserz] def exprBNF[A](expr: Expr[A]): String = + expr match { + case Equals(a) => "\"" + a.toString + "\"" + case Not(a) => "- \"" + a.toString + "\"" + case InSet(as) => as.map(_.toString).toList.sorted.mkString("( \"", "\" | \"", "\" )") + case Condition(_) => "" + } +} diff --git a/src/main/scala/org/spartanz/parserz/InOut.scala b/src/main/scala/org/spartanz/parserz/InOut.scala new file mode 100644 index 0000000..900e625 --- /dev/null +++ b/src/main/scala/org/spartanz/parserz/InOut.scala @@ -0,0 +1,144 @@ +package org.spartanz.parserz + +import scala.util.control.NoStackTrace + +object InOut { + + sealed trait Error extends RuntimeException with NoStackTrace + case object NoInput extends Error + + private def unsafeCompare[A](arr1: Array[A], i1: Int, arr2: Array[A], i2: Int, len: Int): Boolean = { + var i = 0 + while (i < len && arr1(i1 + i) == arr2(i2 + i)) i += 1 + i == len + } + + + sealed trait Consumer[+E, I, +A] + object Consumer { + + sealed trait Mutable[+E, I, +A] extends Consumer[E, I, A] { + type State // the implementation of State is mutable + def create(): State + def copy(s: State): State + def needsMoreInput(s: State): Boolean + def feed(s: State, input: I, i: Int): Int + def finish(s: State): A + } + + sealed trait Simple[+E, I, +A] extends Consumer[E, I, A] { + def create(): Int + def needsMoreInput(s: Int): Boolean + def feed(s: Int, input: I, i: Int): Simple.CountAndState + def finish(s: Int): A + } + object Simple { + final type CountAndState = Long + final def create(count: Int, state: Int): CountAndState = ((0xFFFFFFFFL & count) << 32) | (0xFFFFFFFFL & state) + final def extractCount(cas: CountAndState): Int = (cas >>> 32).toInt + final def extractState(cas: CountAndState): Int = cas.toInt + } + } + + sealed trait Producer[+E, -A, O] + object Producer { + sealed trait Mutable[+E, -A, O] extends Producer[E, A, O] { + type State + def create(a: A): State + def copy(s: State): State + def needsMoreOutput(s: State): Boolean + def expel(s: State, output: O, i: Int): Int + } + + sealed trait Simple[+E, -A, O] extends Producer[E, A, O] { + def create(a: A): Int + def needsMoreOutput(s: Int): Boolean + def expel(s: Int, output: O, i: Int): Long + } + } + + case class One[+E, I, A](consumer: Consumer.Simple[E, I, A], condition: A => Boolean) + case class Many[+E, I, A](consumer: Consumer.Mutable[E, I, A]) + case class Exact[+E, I, A](consumer: Consumer.Simple[E, I, A]) + + + object Chars { + import Consumer._ + + private val single: Simple[Nothing, Array[Char], Char] = + new Simple[Nothing, Array[Char], Char] { + final val create: Int = -1 + final def needsMoreInput(s: Int): Boolean = s == -1 + + final def feed(s: Int, input: Array[Char], i: Int): Simple.CountAndState = + try { Simple.create(1, input(i).toInt) } + catch { case _: ArrayIndexOutOfBoundsException => -1L } + + final def finish(s: Int): Char = + if (s == -1) throw NoInput + else s.toChar + } + + private def multiple(p: Char => Boolean): Mutable[Nothing, Array[Char], Array[Char]] = { + class St(var acc: Array[Char], var done: Boolean) + + new Mutable[Nothing, Array[Char], Array[Char]] { + final type State = St + final def create(): State = new St(Array.emptyCharArray, false) + final def copy(s: State): State = new St(s.acc, s.done) + final def needsMoreInput(s: State): Boolean = !s.done + + final def feed(s: State, input: Array[Char], i: Int): Int = { + val last = input.indexWhere(!p(_), i) + val i2 = if (last == -1) input.length else last + val chunk = java.util.Arrays.copyOfRange(input, i, i2) + + s.acc ++= chunk + s.done = last != -1 || input.length == 0 + chunk.length + } + + final def finish(s: State): Array[Char] = + s.acc + } + } + + val one: One[Nothing, Array[Char], Char] = + One(single, null) + + def oneIf(expr: Expr[Char]): One[Nothing, Array[Char], Char] = + One(single, Expr.exprFilter(expr)) + + def manyWhile(expr: Expr[Char]): Many[Nothing, Array[Char], Array[Char]] = + Many(multiple(Expr.exprFilter(expr))) + + def exact(t: String): Exact[Nothing, Array[Char], Array[Char]] = + Exact( + new Simple[Nothing, Array[Char], Array[Char]] { + private val req = t.toCharArray + private val len = t.length + + // bit0: means "no match detected" if set + // all other bits are number of chars consumed so far + final val create: Int = 0 + final def needsMoreInput(s: Int): Boolean = (s >>> 31) == 0 && s < len + + final def feed(s: Int, input: Array[Char], i: Int): Long = { + try { Simple.create(len, if (unsafeCompare(req, 0, input, i, len)) len else 0x80000000 | len) } + catch { case _: ArrayIndexOutOfBoundsException => + // todo: consume available chars and update state, e.g. + val consumed = 0 + val matching = true + Simple.create(0, if (matching) consumed else 0x80000000 | consumed) + } + } + + final def finish(s: Int): Array[Char] = { + if ((s >>> 31) == 1) null + else if (s < len) throw NoInput + else req + } + } + ) + } +} diff --git a/src/main/scala/org/spartanz/parserz/ParsersModule2.scala b/src/main/scala/org/spartanz/parserz/ParsersModule2.scala new file mode 100644 index 0000000..0889bc4 --- /dev/null +++ b/src/main/scala/org/spartanz/parserz/ParsersModule2.scala @@ -0,0 +1,316 @@ +package org.spartanz.parserz + +import scala.annotation.tailrec +import scala.reflect.ClassTag + +trait ParsersModule2 { + type Input + + import Expr._ + + sealed abstract class Grammar[-SI, +SO, +E, A] { + self => + + import Grammar._ + import Grammar.GADT._ + + final def map[B](to: A => B, from: B => A): Grammar[SI, SO, E, B] = + Map[SI, SO, E, A, B](self, a => Right(to(a)), b => Right(from(b))) + + final def mapOption[E1 >: E, B](e: E1)(to: A => Option[B], from: B => Option[A]): Grammar[SI, SO, E1, B] = + Map[SI, SO, E1, A, B](self, asEither(e)(to), asEither(e)(from)) + + final def mapEither[E1 >: E, B](to: A => E1 \/ B, from: B => E1 \/ A): Grammar[SI, SO, E1, B] = + Map[SI, SO, E1, A, B](self, to, from) + + final def mapPartial[E1 >: E, B](e: E1)(to: A =?> B, from: B =?> A): Grammar[SI, SO, E1, B] = + Map[SI, SO, E1, A, B](self, asEither(e)(to.lift), asEither(e)(from.lift)) + + final def filter[E1 >: E](e: E1)(f: Expr[A]): Grammar[SI, SO, E1, A] = + Filter[SI, SO, E1, A](self, e, f) + + final def option: Grammar[SI, SO, E, Option[A]] = + alt(succeed(None)).map({ + case Left(v) => Some(v) + case Right(_) => None + }, { + case Some(v) => Left(v) + case None => Right(None) + }) + + final def recover(default: A): Grammar[SI, SO, E, A] = + alt(succeed(default)).map(_.merge, Left(_)) + + final def select[SI1 <: SI, SO1 >: SO, E1 >: E, B](f: A => Grammar[SI1, SO1, E1, B])( + implicit en: Enumerable[A] + ): Grammar[SI1, SO1, E1, B] = + Select[SI1, SO1, E1, A, B](self, f, en) + + final def zip[SI1 <: SI, SO1 >: SO, E1 >: E, B](that: Grammar[SI1, SO1, E1, B]): Grammar[SI1, SO1, E1, A /\ B] = + Zip(self, that) + + final def zipL[SI1 <: SI, SO1 >: SO, E1 >: E, B](that: Grammar[SI1, SO1, E1, B], b: B): Grammar[SI1, SO1, E1, A] = + ZipL(self, that, b) + + final def zipR[SI1 <: SI, SO1 >: SO, E1 >: E, B](a: A, that: Grammar[SI1, SO1, E1, B]): Grammar[SI1, SO1, E1, B] = + ZipR(self, that, a) + + final def alt[SI1 <: SI, SO1 >: SO, E1 >: E, B](that: Grammar[SI1, SO1, E1, B]): Grammar[SI1, SO1, E1, A \/ B] = + Alt(self, that) + + final def ∘ [B](to: A => B, from: B => A): Grammar[SI, SO, E, B] = map(to, from) + + final def ~ [SI1 <: SI, SO1 >: SO, E1 >: E, B](that: Grammar[SI1, SO1, E1, B]): Grammar[SI1, SO1, E1, A /\ B] = self.zip(that) + + final def <~ [SI1 <: SI, SO1 >: SO, E1 >: E, B](b: B, that: Grammar[SI1, SO1, E1, B]): Grammar[SI1, SO1, E1, A] = self.zipL(that, b) + + final def | [SI1 <: SI, SO1 >: SO, E1 >: E, B](that: Grammar[SI1, SO1, E1, B]): Grammar[SI1, SO1, E1, A \/ B] = self.alt(that) + + final def rep: Grammar[SI, SO, E, List[A]] = Rep(self) + + final def rep1: Grammar[SI, SO, E, ::[A]] = Rep1(self) + + final def separated[SI1 <: SI, SO1 >: SO, E1 >: E, S](by: Grammar[SI1, SO1, E1, S]): Grammar[SI1, SO1, E1, SeparatedBy[A, S]] = Sep(self, by) + + final def @@ (tag: String): Grammar[SI, SO, E, A] = Tag(self, tag) + + final def tag(tag: String): Grammar[SI, SO, E, A] = self @@ tag + } + + object Grammar extends GrammarSyntax { + // format: off + object GADT { + private[parserz] case class Produce[SI, SO, E, A](a: A) extends Grammar[Any, Nothing, Nothing, A] + private[parserz] case class Consume[SI, SO, E, A](consumer: InOut.Consumer.Simple[E, Input, A], condition: A => Boolean, e: E) extends Grammar[SI, SO, E, A] + private[parserz] case class ConsumeMany[SI, SO, E, A](consumer: InOut.Consumer.Mutable[E, Input, A], e: E) extends Grammar[SI, SO, E, A] + private[parserz] case class ConsumeExact[SI, SO, E, A](consumer: InOut.Consumer.Simple[E, Input, A], e: E) extends Grammar[SI, SO, E, A] + private[parserz] case class Delay[SI, SO, E, A](delayed: () => Grammar[SI, SO, E, A]) extends Grammar[SI, SO, E, A] + private[parserz] case class Tag[SI, SO, E, A](value: Grammar[SI, SO, E, A], tag: String) extends Grammar[SI, SO, E, A] + private[parserz] case class Map[SI, SO, E, A, B](value: Grammar[SI, SO, E, A], to: A => E \/ B, from: B => E \/ A) extends Grammar[SI, SO, E, B] + private[parserz] case class Filter[SI, SO, E, A](value: Grammar[SI, SO, E, A], e: E, filter: Expr[A]) extends Grammar[SI, SO, E, A] + private[parserz] case class Zip[SI, SO, E, A, B](left: Grammar[SI, SO, E, A], right: Grammar[SI, SO, E, B]) extends Grammar[SI, SO, E, A /\ B] + private[parserz] case class ZipL[SI, SO, E, A, B](left: Grammar[SI, SO, E, A], right: Grammar[SI, SO, E, B], b: B) extends Grammar[SI, SO, E, A] + private[parserz] case class ZipR[SI, SO, E, A, B](left: Grammar[SI, SO, E, A], right: Grammar[SI, SO, E, B], a: A) extends Grammar[SI, SO, E, B] + private[parserz] case class Alt[SI, SO, E, A, B](left: Grammar[SI, SO, E, A], right: Grammar[SI, SO, E, B]) extends Grammar[SI, SO, E, A \/ B] + private[parserz] case class Select[SI, SO, E, A, B](value: Grammar[SI, SO, E, A], f: A => Grammar[SI, SO, E, B], en: Enumerable[A]) extends Grammar[SI, SO, E, B] + private[parserz] case class Rep[SI, SO, E, A](value: Grammar[SI, SO, E, A]) extends Grammar[SI, SO, E, List[A]] + private[parserz] case class Rep1[SI, SO, E, A](value: Grammar[SI, SO, E, A]) extends Grammar[SI, SO, E, ::[A]] + private[parserz] case class Sep[SI, SO, E, A, S](value: Grammar[SI, SO, E, A], sep: Grammar[SI, SO, E, S]) extends Grammar[SI, SO, E, SeparatedBy[A, S]] + } + // format: on + + final val unit: Grammar[Any, Nothing, Nothing, scala.Unit] = + GADT.Produce(()) + + final def succeed[A](a: A): Grammar[Any, Nothing, Nothing, A] = + GADT.Produce(a) + + final def fail[E, A](e: E): Grammar[Any, Nothing, E, A] = + unit.mapPartial(e)(PartialFunction.empty, PartialFunction.empty) + + final def consume[E, A](e: E, codec: InOut.One[E, Input, A]): Grammar[Any, Nothing, E, A] = + GADT.Consume(codec.consumer, codec.condition, e) + + final def consume[E, A](e: E, codec: InOut.Many[E, Input, A]): Grammar[Any, Nothing, E, A] = + GADT.ConsumeMany(codec.consumer, e) + + final def consumeToken[E, A](e: E, codec: InOut.Exact[E, Input, A]): Grammar[Any, Nothing, E, A] = + GADT.ConsumeExact(codec.consumer, e) + + final def delay[SI, SO, E, A](g: => Grammar[SI, SO, E, A]): Grammar[SI, SO, E, A] = + GADT.Delay(() => g) + + private def asEither[E, A, B](e: E)(f: A => Option[B]): A => E \/ B = + f(_).map(Right(_)).getOrElse(Left(e)) + } + + trait GrammarSyntax { + + implicit final class ToStringOps1(self: String) { + + def @@ [SI, SO, E, A](g: Grammar[SI, SO, E, A]): Grammar[SI, SO, E, A] = + g @@ self + } + + implicit final class ToZipOps1[SI, SO, E, A, B](self: (Grammar[SI, SO, E, A], A)) { + + def ~> [SI1 <: SI, SO1 >: SO, E1 >: E](that: Grammar[SI1, SO1, E1, B]): Grammar[SI1, SO1, E1, B] = + self._1.zipR(self._2, that) + } + + implicit final class ToGrammarOps1[SI, SO, E, A, B](self: Grammar[SI, SO, E, List[A]]) { + + def orEmpty: Grammar[SI, SO, E, List[A]] = + self.recover(Nil) + } + + implicit final class ToGrammarOps2[SI, SO, E, A: ClassTag, B](self: Grammar[SI, SO, E, Array[A]]) { + + def orEmpty: Grammar[SI, SO, E, Array[A]] = + self.recover(Array.empty[A]) + } + + implicit final class ToGrammarOps3[SI, SO, E, A, B](self: Grammar[SI, SO, E, (A, List[B])]) { + + def foldLeft(fold: (A, B) => A, unfold: A =?> (A, B)): Grammar[SI, SO, E, A] = + self.map( + arg => { + arg._2.foldLeft(arg._1)(fold) + }, + arg => { + @tailrec + def rec(acc: List[B])(a: A): (A, List[B]) = + unfold.lift(a) match { + case None => (a, acc) + case Some((a1, b)) => rec(b :: acc)(a1) + } + rec(Nil)(arg) + } + ) + } + } + + + private class ParserState[E](val input: Input, var i: Int, var e: E) + + final def parser[S, E, A](grammar: Grammar[S, S, E, A]): Input => E \/ A = { + input => + val ps = new ParserState(input, 0, null.asInstanceOf[E]) + val a = step(grammar, ps) + if (a == null) Left(ps.e) else Right(a) + } + + private def step[S, E, A](grammar: Grammar[S, S, E, A], ps: ParserState[E]): A = + grammar match { + case Grammar.GADT.Consume(c, cond, e) => + import InOut.Consumer.Simple._ + val cas = c.feed(c.create(), ps.input, ps.i) + ps.i += extractCount(cas) + try { + val res = c.finish(extractState(cas)) + if (cond == null || cond(res)) res else { ps.e = e; null.asInstanceOf[A] } + } + catch { + case InOut.NoInput => ps.e = e; null.asInstanceOf[A] + } + + case Grammar.GADT.ConsumeExact(c, e) => + import InOut.Consumer.Simple._ + val cas = c.feed(c.create(), ps.input, ps.i) + ps.i += extractCount(cas) + try { + val res = c.finish(extractState(cas)) + if (res == null) { ps.e = e; null.asInstanceOf[A] } else res + } + catch { + case InOut.NoInput => ps.e = e; null.asInstanceOf[A] + } + + case Grammar.GADT.ConsumeMany(c, e) => + val state = c.create() + val count = c.feed(state, ps.input, ps.i) + ps.i += count + try { + c.finish(state) + } + catch { + case InOut.NoInput => ps.e = e; null.asInstanceOf[A] + } + + + case Grammar.GADT.Produce(a) => a + case Grammar.GADT.Tag(value, _) => step(value, ps) + case Grammar.GADT.Delay(delayed) => step(delayed(), ps) + + case Grammar.GADT.Map(value, to, _) => + val a = step(value, ps) + if (a == null) null.asInstanceOf[A] else to(a) match { + case Left(e) => ps.e = e; null.asInstanceOf[A] + case Right(a) => a + } + + case Grammar.GADT.Filter(value, e, expr) => + val a = step(value, ps) + if (a != null && exprFilter(expr)(a)) a + else ps.e = e; null.asInstanceOf[A] + + case zip: Grammar.GADT.Zip[S, S, E, ta, tb] => + val a: ta = step(zip.left, ps) + if (a == null) null.asInstanceOf[A] + else { + val b: tb = step(zip.right, ps) + if (b == null) null.asInstanceOf[A] + else (a, b) + } + + case zip: Grammar.GADT.ZipL[S, S, E, ta, tb] => + val a: ta = step(zip.left, ps) + if (a == null) null.asInstanceOf[A] + else { + val b: tb = step(zip.right, ps) + if (b == null) null.asInstanceOf[A] + else a + } + + case zip: Grammar.GADT.ZipR[S, S, E, ta, tb] => + val a: ta = step(zip.left, ps) + if (a == null) null.asInstanceOf[A] + else step(zip.right, ps) + + case alt: Grammar.GADT.Alt[S, S, E, ta, tb] => + val checkpoint = ps.i + val a = step(alt.left, ps) + if (a != null) Left(a) + else { + ps.i = checkpoint + val b = step(alt.right, ps) + if (b == null) null.asInstanceOf[A] + else Right(b) + } + + case sel: Grammar.GADT.Select[S, S, E, _, _] => + val v = step(sel.value, ps) + if (v == null) null.asInstanceOf[A] + else step(sel.f(v), ps) + + case rep: Grammar.GADT.Rep[S, S, E, ta] => + repeatStep(rep.value, ps, Nil).reverse + + case rep: Grammar.GADT.Rep1[S, S, E, ta] => + val a = step(rep.value, ps) + if (a == null) null.asInstanceOf[A] + else ::(a, repeatStep(rep.value, ps, Nil).reverse) + + case sep: Grammar.GADT.Sep[S, S, E, ta, ts] => + val checkpoint = ps.i + val a: ta = step(sep.value, ps) + if (a != null) repeatStep(sep.sep, sep.value, ps, SeparatedBy(a)).reverse + else { + ps.i = checkpoint + SeparatedBy() + } + } + + @tailrec + private def repeatStep[S, E, A](g: Grammar[S, S, E, A], ps: ParserState[E], as: List[A]): List[A] = { + val checkpoint = ps.i + val a: A = step(g, ps) + if (a == null) { + ps.i = checkpoint + as + } + else repeatStep(g, ps, a :: as) + } + + @tailrec + private def repeatStep[S, E, A, B](g1: Grammar[S, S, E, B], g2: Grammar[S, S, E, A], ps: ParserState[E], as: SeparatedBy1[A, B]): SeparatedBy1[A, B] = { + val checkpoint = ps.i + val b: B = step(g1, ps) + if (b == null) { ps.i = checkpoint; as } + else { + val a: A = step(g2, ps) + if (a == null) { ps.i = checkpoint; as } + else repeatStep(g1, g2, ps, as.prepend(a, b)) + } + } +} diff --git a/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala b/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala index 9ff0673..722a966 100644 --- a/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala +++ b/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala @@ -1,6 +1,8 @@ package org.spartanz.parserz.compare -import org.spartanz.parserz.{ParsersModule, \/} +import org.spartanz.parserz.{ParsersModule2, \/} +import org.spartanz.parserz.Expr._ +import org.spartanz.parserz.InOut._ object ParserzJsonTest { @@ -18,12 +20,11 @@ object ParserzJsonTest { } - object Parser extends ParsersModule { - override type Input = List[Char] + object Parser extends ParsersModule2 { + override type Input = Array[Char] } import Parser.Grammar._ - import Parser.Expr._ import Parser._ import Js._ @@ -31,14 +32,11 @@ object ParserzJsonTest { type E = String type G[A] = Grammar[Any, Nothing, E, A] - def char(c: Char): G[Char] = consume( - cs => if (cs.nonEmpty && cs.head == c) Right((cs.tail, c)) else Left("expected: " + c), - { case (cs, _) => Right(c :: cs) } - ) - def token(t: List[Char]): G[List[Char]] = consume( - cs => if (cs.startsWith(t)) Right((cs.drop(t.length), t)) else Left("expected: " + t), - { case (cs, _) => Right(t reverse_::: cs) } - ) + def char(c: Char): G[Char] = + consume(s"expected: '$c'", Chars.oneIf(===(c))) + + def token[A](t: String, v: A): G[A] = + consumeToken(s"expected: '$t'", Chars.exact(t)).map(_ => v, _ => t.toCharArray) val dot: G[Char] = char('.') val comma: G[Char] = char(',') @@ -49,40 +47,26 @@ object ParserzJsonTest { val brace1: G[Char] = char('{') val brace2: G[Char] = char('}') - val spacing: G[Unit] = consumePure( - cs => (cs.dropWhile(c => c == ' ' || c == '\n' || c == '\r'), ()), - { case (cs, _) => ' ' :: cs } - ) - - val ch: G[Char] = consume( - cs => if (cs.nonEmpty) Right((cs.tail, cs.head)) else Left("expected: char"), - { case (cs, c) => Right(c :: cs) } - ) + val spacing: G[Unit] = consume("expected: spacing", Chars.manyWhile(in(' ', '\n', '\r'))).map(_ => (), _ => Array.emptyCharArray) - def chars(cond: Char => Boolean): G[List[Char]] = consumePure({ - cs => - val out = cs.takeWhile(cond) - (cs.drop(out.length), out) - }, { - case (cs, cs1) => cs1 reverse_::: cs - }) + def chars(p: Char => Boolean): G[Array[Char]] = consume("expected: conditional", Chars.manyWhile(cond(p))) - val digits: G[List[Char]] = chars(c => '0' <= c && c <= '9') - val sign: G[Option[Char]] = ch.filter("expected: +/-")(in('+', '-')).option - val exponent: G[List[Char]] = (ch.filter("expected: E")(in('e', 'E')) ~ sign, ('E', Some('+'))) ~> digits - val fractional: G[List[Char]] = (dot, '.') ~> digits - val integral: G[List[Char]] = digits + val digits: G[Array[Char]] = chars(c => '0' <= c && c <= '9') + val sign: G[Option[Char]] = consume("expected: +/-", Chars.oneIf(in('+', '-'))).option + val exponent: G[Array[Char]] = (consume("expected: E/e", Chars.oneIf(in('e', 'E'))) ~ sign, ('E', Some('+'))) ~> digits + val fractional: G[Array[Char]] = (dot, '.') ~> digits + val integral: G[Array[Char]] = digits val num: G[Num] = (sign ~ integral ~ fractional.orEmpty ~ exponent.orEmpty).map( { case (((s, l1), l2), l3) => Num((s.mkString + l1.mkString + l2.mkString + l3.mkString).toDouble) }, { case Num(_) => ??? } ) - val `null`: G[Null.type] = token("null".toList).map( _ => Null, _ => "null".toList) - val `false`: G[False.type] = token("false".toList).map(_ => False, _ => "false".toList) - val `true`: G[True.type] = token("true".toList).map( _ => True, _ => "true".toList) + val `null`: G[Null.type] = token("null", Null) + val `false`: G[False.type] = token("false", False) + val `true`: G[True.type] = token("true", True) - val string: G[String] = ((spacing ~ quote, ((), '"')) ~> chars(c => c != '\"' && c != '\\') <~ ('"', quote)).map(_.mkString, _.toList) + val string: G[String] = ((spacing ~ quote, ((), '"')) ~> chars(c => c != '\"' && c != '\\') <~ ('"', quote)).map(_.mkString, _.toCharArray) val str: G[Str] = string.map(Str, "\"" + _.value + "\"") val arr: G[Arr] = ((bracket1, '[') ~> js.separated(comma).map(_.values, { _: List[Val] => ??? }) <~ (((), ']'), spacing ~ bracket2)).map( @@ -119,25 +103,34 @@ object ParserzJsonTest { } - val parser: (S, Input) => (S, E \/ (Input, Val)) = Parser.parser[S, E, Val](js) + val parser: Input => E \/ Val = Parser.parser[S, E, Val](js) def main(args: Array[String]): Unit = { // ((),Right((List(),Obj(List((firstName,Str(John)), (lastName,Str(Smith)), (age,Num(25.0)), (address,Obj(List((streetAddress,Str(21 2nd Street)), (city,Str(New York)), (state,Str(NY)), (postalCode,Num(10021.0))))), (phoneNumbers,Arr(List(Obj(List((type,Str(home)), (number,Str(212 555-1234)))), Obj(List((type,Str(fax)), (number,Str(646 555-4567)))))))))))) - println(parser((), value)) + println(parser(value)) val t1: Long = System.nanoTime() (1 to 1000000).foreach { _ => - parser((), value) + parser(value) } val t2: Long = System.nanoTime() - t1 println(s"\n\n Execution time = ${(t2 / 1000).toString.reverse.grouped(3).map(_.reverse).toList.reverse.mkString(",")} μs") } + // v.0.1.4 // 100,000 in 3.3 sec // 1,000,000 in 24.9 sec - val value: List[Char] = + // v.0.2.0 - pre-alpha + // 100,000 in 5.5 sec + // 1,000,000 in 49.3 sec + + // v.0.2.0 - alpha + // 100,000 in 1.9 sec + // 1,000,000 in 14.1 sec + + val value: Array[Char] = """{ | "firstName": "John", | "lastName": "Smith", @@ -158,5 +151,5 @@ object ParserzJsonTest { | "number": "646 555-4567" | } | ] - |}""".stripMargin.toList + |}""".stripMargin.toCharArray }