diff --git a/src/main/scala/org/spartanz/parserz/ParsersModule.scala b/src/main/scala/org/spartanz/parserz/ParsersModule.scala index 028568d..b6e21f0 100644 --- a/src/main/scala/org/spartanz/parserz/ParsersModule.scala +++ b/src/main/scala/org/spartanz/parserz/ParsersModule.scala @@ -111,6 +111,7 @@ trait ParsersModule extends ExprModule { private[parserz] case class MapES[SI, SO, E, A, B](value: Grammar[SI, SO, E, A], fe: SI => (SO, E), to: A => Option[B], from: B => Option[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 FilterES[SI, SO, E, A](value: Grammar[SI, SO, E, A], fe: SI => (SO, E), filter: Expr[A]) extends Grammar[SI, SO, E, A] + private[parserz] case class ZipUnsafe[SI, SO, E](gs: Array[Grammar[SI, SO, E, Any]]) extends Grammar[SI, SO, E, Array[Any]] 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] @@ -283,6 +284,24 @@ trait ParsersModule extends ExprModule { } } + case Grammar.GADT.ZipUnsafe(gs) => + (s: S, in: Input) => { + val size = gs.length + val arr: Array[Any] = Array.ofDim(size) + var i = 0 + var state: S = s + var input: Input = in + var res: E \/ (Input, Array[Any]) = Right((input, arr)) + while (i < size && res.isRight) { + val (s1, res1): (S, E \/ (Input, Any)) = parser(gs(i))(state, input) + res1.foreach { case (i1, v1) => input = i1; arr(i) = v1 } + state = s1 + res = res1.map(_ => (input, arr)) + i += 1 + } + (state, res) + } + case zip: Grammar.GADT.Zip[S, S, E, ta, tb] => (s: S, i: Input) => { val (s1, res1): (S, E \/ (Input, ta)) = parser(zip.left)(s, i) @@ -439,6 +458,24 @@ trait ParsersModule extends ExprModule { } } + case Grammar.GADT.ZipUnsafe(gs) => + (s: S, in: (Input, Array[Any])) => { + val arr = in._2 + val size = arr.length + var i = 0 + var state: S = s + var input = in._1 + var res: E \/ Input = Right(input) + while (i < size && res.isRight) { + val (s1: S, res1: Either[E, Input]) = printer(gs(i))(state, (input, arr(i))) + state = s1 + res = res1 + res.foreach(input = _) + i += 1 + } + (state, res) + } + case zip: Grammar.GADT.Zip[S, S, E, ta, tb] => (s: S, in: (Input, ta /\ tb)) => { val (i, (a, b)) = in @@ -548,6 +585,7 @@ trait ParsersModule extends ExprModule { case Grammar.GADT.MapES(value, _, _, _) => tagOrExpand(value) case Grammar.GADT.Filter(v, _, expr) => Some(exprBNF(expr)).filter(_.nonEmpty).getOrElse(tagOrExpand(v)) case Grammar.GADT.FilterES(v, _, expr) => Some(exprBNF(expr)).filter(_.nonEmpty).getOrElse(tagOrExpand(v)) + case Grammar.GADT.ZipUnsafe(gs) => gs.map(tagOrExpand).mkString(" ") case Grammar.GADT.Zip(left, right) => tagOrExpand(left) + " " + tagOrExpand(right) case Grammar.GADT.ZipL(left, right, _) => tagOrExpand(left) + " " + tagOrExpand(right) case Grammar.GADT.ZipR(left, right, _) => tagOrExpand(left) + " " + tagOrExpand(right) @@ -576,6 +614,7 @@ trait ParsersModule extends ExprModule { case Grammar.GADT.MapES(value, _, _, _) => show(value) case Grammar.GADT.Filter(value, _, _) => show(value) case Grammar.GADT.FilterES(value, _, _) => show(value) + case Grammar.GADT.ZipUnsafe(gs) => gs.toList.flatMap(show) case Grammar.GADT.Zip(left, right) => show(left) ::: show(right) case Grammar.GADT.ZipL(left, right, _) => show(left) ::: show(right) case Grammar.GADT.ZipR(left, right, _) => show(left) ::: show(right) diff --git a/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala b/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala index 9ff0673..6dd61cf 100644 --- a/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala +++ b/src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala @@ -1,6 +1,7 @@ package org.spartanz.parserz.compare -import org.spartanz.parserz.{ParsersModule, \/} +import org.spartanz.parserz.\/ +import org.spartanz.parserz.prototype.P2Module object ParserzJsonTest { @@ -18,19 +19,18 @@ object ParserzJsonTest { } - object Parser extends ParsersModule { + object Parser extends P2Module { override type Input = List[Char] + override type S = Unit + override type E = String } import Parser.Grammar._ + import Parser.Tools._ import Parser.Expr._ import Parser._ import Js._ - type S = Unit - 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) } @@ -73,10 +73,13 @@ object ParserzJsonTest { val fractional: G[List[Char]] = (dot, '.') ~> digits val integral: G[List[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(_) => ??? } - ) + implicit val numEquiv: Equiv[(((Option[Char], List[Char]), List[Char]), List[Char]), Num] = + Equiv.caseClass4( + { case (s, l1, l2, l3) => Num((s.mkString + l1.mkString + l2.mkString + l3.mkString).toDouble) }, + { case Num(_) => ??? } + ) + + val num: G[Num] = toZ(sign ~ integral ~ fractional.orEmpty ~ exponent.orEmpty) val `null`: G[Null.type] = token("null".toList).map( _ => Null, _ => "null".toList) val `false`: G[False.type] = token("false".toList).map(_ => False, _ => "false".toList) diff --git a/src/test/scala/org/spartanz/parserz/prototype/P2Module.scala b/src/test/scala/org/spartanz/parserz/prototype/P2Module.scala new file mode 100644 index 0000000..57b311e --- /dev/null +++ b/src/test/scala/org/spartanz/parserz/prototype/P2Module.scala @@ -0,0 +1,103 @@ +package org.spartanz.parserz.prototype + +import org.spartanz.parserz.ParsersModule + +trait P2Module extends ParsersModule { + import Grammar._ + + type S + type E + type G[A] = Grammar[S, S, E, A] + + object Tools { + import Hacks._ + + sealed trait Equiv[A, Repr] + case class Equiv3[Z, A, B, C](f: (A, B, C) => Z, g: Z => (A, B, C)) extends Equiv[((A, B), C), Z] + case class Equiv4[Z, A, B, C, D](f: (A, B, C, D) => Z, g: Z => (A, B, C, D)) extends Equiv[(((A, B), C), D), Z] + + object Equiv { +// def caseClass1[Z, A](f: A => Z, g: Z => Option[A]): Equiv[A, Z] = ??? +// def caseClass2[Z, A, B](f: (A, B) => Z, g: Z => Option[(A, B)]): Equiv[(A, B), Z] = ??? + def caseClass3[Z, A, B, C](f: (A, B, C) => Z, g: Z => (A, B, C)): Equiv[((A, B), C), Z] = Equiv3(f, g) + def caseClass4[Z, A, B, C, D](f: (A, B, C, D) => Z, g: Z => (A, B, C, D)): Equiv[(((A, B), C), D), Z] = Equiv4(f, g) + } + + // using Equiv with prior optimization of Zip operations + def toZ[A, AA, Z](g: G[A])(implicit equiv: Equiv[AA, Z], ev: AA <:< A): G[Z] = { + val g1: Option[GADT.ZipUnsafe[S, S, E]] = equiv match { + case Equiv3(_, _) => zippy(3)(g).map(l => GADT.ZipUnsafe(l.toArray)) + case Equiv4(_, _) => zippy(4)(g).map(l => GADT.ZipUnsafe(l.toArray)) + } +// g1.fold { +// toZDirect(g)(equiv, ev) +// ??? +// } { g2 => + // place where we cast types back at runtime + equiv match { + case equiv: Equiv3[Z, ta, tb, tc] => GADT.Map[S, S, E, Array[Any], Z](g1.get, + arr => { + val Array(a: ta, b: tb, c: tc) = arr + Right(equiv.f(a, b, c)) + }, + z => { + val (a: ta, b: tb, c: tc) = equiv.g(z) + Right(Array(a, b, c)) + } + ) + case equiv: Equiv4[Z, ta, tb, tc, td] => GADT.Map[S, S, E, Array[Any], Z](g1.get, + arr => { + val Array(a: ta, b: tb, c: tc, d: td) = arr + Right(equiv.f(a, b, c, d)) + }, + z => { + val (a: ta, b: tb, c: tc, d: td) = equiv.g(z) + Right(Array(a, b, c, d)) + } + ) + } +// } + } + + // using Equiv without any optimization of execution plan + def toZDirect[A, AA, Z](g: G[A])(implicit equiv: Equiv[AA, Z], ev: AA <:< A): G[Z] = + equiv match { + case equiv: Equiv3[Z, ta, tb, tc] => GADT.Map[S, S, E, A, Z](g, + { case ((a: ta, b: tb), c: tc) => Right(equiv.f(a, b, c)) }, + z => { + val (a: ta, b: tb, c: tc) = equiv.g(z) + Right(((a, b), c)) + } + ) + case equiv: Equiv4[Z, ta, tb, tc, td] => GADT.Map[S, S, E, A, Z](g, + { case (((a: ta, b: tb), c: tc), d: td) => Right(equiv.f(a, b, c, d)) }, + z => { + val (a: ta, b: tb, c: tc, d: td) = equiv.g(z) + Right((((a, b), c), d)) + } + ) + } + } + + // place where we loose types + object Hacks { + def zippy[A](size: Int)(g: G[A]): Option[List[G[Any]]] = { + + @scala.annotation.tailrec + def step[AA](i: Int)(acc: List[G[Any]])(g: G[AA]): List[G[Any]] = + g match { +// case GADT.ZipL(left, right, b) => +// case GADT.ZipR(left, right, a) => + case GADT.Zip(l, r) if i > 0 => + step(i - 1)(r.asInstanceOf[G[Any]] :: acc)(l) + case g if i > 0 => + g.asInstanceOf[G[Any]] :: acc + case _ => + acc + } + + val list = step(size)(Nil)(g) + if (list.length == size) Some(list) else None + } + } +} diff --git a/src/test/scala/org/spartanz/parserz/prototype/T2.scala b/src/test/scala/org/spartanz/parserz/prototype/T2.scala new file mode 100644 index 0000000..dde22da --- /dev/null +++ b/src/test/scala/org/spartanz/parserz/prototype/T2.scala @@ -0,0 +1,54 @@ +package org.spartanz.parserz.prototype + +object T2 { + + object Module extends P2Module { + override type Input = List[Char] + override type S = String + override type E = String + } + + import Module.Grammar._ + import Module._ + import Tools._ + + val one: G[Int] = "one" @@ consumePure(i => i.tail -> i.head.toString.toInt, { case (i, a) => i ::: a.toString.toList }) + val two: G[Boolean] = "two" @@ consumePure(i => i.tail -> (i.head == 'T'), { case (i, a) => i ::: (if (a) List('T') else List('F')) }) + val thr: G[String] = "thr" @@ succeed("blah") + + case class Thing(idx: Int, exists: Boolean, name: String) + + implicit val thingEquiv: Equiv[((Int, Boolean), String), Thing] = + Equiv.caseClass3(Thing.apply, Thing.unapply(_).get) + + val g1: G[((Int, Boolean), String)] = one ~ two ~ thr + + val g2: G[Thing] = toZDirect(g1) + val g3: G[Thing] = toZ(g1) + + + def main(args: Array[String]): Unit = { + println(parser(g1)("", List('1', 'T'))) + println() + println(parser(g2)("", List('1', 'T'))) + println() + println(parser(g3)("", List('1', 'T'))) + println() + + println() + println(printer(g1)("", (Nil, ((2, true), "printed")))) + println() + println(printer(g2)("", (Nil, Thing(2, true, "printed")))) + println() + println(printer(g3)("", (Nil, Thing(2, true, "printed")))) + println() + + println() + println(bnf("g1" @@ g1).mkString("\n")) + println() + println(bnf("g2" @@ g2).mkString("\n")) + println() + println(bnf("g3" @@ g3).mkString("\n")) + println() + } +}