Skip to content

Commit

Permalink
#75 - Eliminate creation of nested tuples
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-shabanau committed Mar 25, 2020
1 parent 8530761 commit 1de1146
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/main/scala/org/spartanz/parserz/ParsersModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
131 changes: 131 additions & 0 deletions src/test/scala/org/spartanz/parserz/prototype/T2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package org.spartanz.parserz.prototype

import org.spartanz.parserz.ParsersModule

object T2 {

object Module extends ParsersModule {
override type Input = List[Char]
}

import Module.Grammar._
import Module._
import Tools._

type S = String
type E = String
type G[A] = Grammar[S, S, E, A]

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((a, b, c) => Thing.apply(a, b, c), t => Thing.unapply(t))

val g1: G[((Int, Boolean), String)] = one ~ two ~ thr

val g2: G[Thing] = toZDirect(g1)
val g3: G[Thing] = toZ(g1)


object Tools {
import Hacks._

sealed trait Equiv[A, Repr]
case class Eq3[Z, A, B, C](f: (A, B, C) => Z, g: Z => Option[(A, B, C)]) extends Equiv[((A, B), C), 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 => Option[(A, B, C)]): Equiv[((A, B), C), Z] = Eq3(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 Eq3(_, _) => zippy(3)(g).map(l => GADT.ZipUnsafe(l.toArray))
}
// g1.fold {
// toZDirect(g)(equiv, ev)
// ???
// } { g2 =>
equiv match {
case eq: Eq3[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(eq.f(a, b, c))
},
z => {
eq.g(z).map { case (a, b, c) => Array(a, b, c) }.toRight("some error")
}
)
}
// }
}

// 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 eq: Eq3[Z, ta, tb, tc] => GADT.Map[S, S, E, A, Z](g,
{ case ((a: ta, b: tb), c: tc) =>
Right(eq.f(a, b, c))
},
z => {
eq.g(z).map { case (a, b, c) => val a0: A = ((a, b), c); a0 }.toRight("some error")
}
)
}
}

// 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
}
}

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()
}
}

0 comments on commit 1de1146

Please sign in to comment.