Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
Comment on lines +287 to +303
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added ZipUnsafe into existing algebra for demo.
Correct implementation is to have Grammar.GADT compiled into another low level algebra, which in turn will be used to produce parser/printer functions.


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
23 changes: 13 additions & 10 deletions src/test/scala/org/spartanz/parserz/compare/ParserzJsonTest.scala
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -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) }
Expand Down Expand Up @@ -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)
Expand Down
103 changes: 103 additions & 0 deletions src/test/scala/org/spartanz/parserz/prototype/P2Module.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
}
54 changes: 54 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,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()
Comment on lines +31 to +52
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running yields same results for all cases

(,Right((List(),((1,true),blah))))
(,Right((List(),Thing(1,true,blah))))
(,Right((List(),Thing(1,true,blah))))

(,Right(List(2, T)))
(,Right(List(2, T)))
(,Right(List(2, T)))

<g1> ::= <one> <two> <thr>
<g2> ::= <one> <two> <thr>
<g3> ::= <one> <two> <thr>

}
}