Skip to content

Commit

Permalink
S-Expression implementation updates (#7)
Browse files Browse the repository at this point in the history
* Updating main (#7)

* Further work on SExpr (#3)

* Working through s-expression things

* Mirror/sexpr scala2 (#1)

adding rest of files + scala2-ify

Co-authored-by: Damian Reeves <[email protected]>

* PROM-12590: adding JavaTimeSpec test (#2)

* Adding additional decoders and support (#3)

* Added additional decoder support

* added primitives

* adding decoders for java.time and some others

* decoder - adding operators and more tests

* uncommenting and fixing decoder tests in JavaTimeSpec

Co-authored-by: Damian Reeves <[email protected]>

Co-authored-by: Michelle <[email protected]>
Co-authored-by: Dmitry Kozinets <[email protected]>
Co-authored-by: Dmitry Kozinets <[email protected]>

* Ir work (#4)

* Partial definition of Value AST

* Add additional Value defs

* Added vfile

* vfile additions

* Minor ir changes

* Fix errors

* Mainly VFile and VFile path work (#5)

* Fix deprecation warnings

* Introducing zio-morphir-io

* VFile related stuff

* Moving towards ZIO over implicits

* VFilePath changes

* Move core to more-ir

* Execution changes (#6)

* Fix deprecation warnings

* Introducing zio-morphir-io

* VFile related stuff

* Moving towards ZIO over implicits

* VFilePath changes

* Move core to more-ir

* Move io project into core

* Delete io module as its replaced by core

Co-authored-by: Damian Reeves <[email protected]>
Co-authored-by: Dmitry Kozinets <[email protected]>
Co-authored-by: Dmitry Kozinets <[email protected]>

* fixing cherry picks

* fixing cherry picks

* PROM-12572: updating AST (#6)

Co-authored-by: Chan, Michelle <[email protected]>

* warning fixes (#8)

* Changes to segment off native stuff

Co-authored-by: Michelle <[email protected]>
Co-authored-by: Dmitry Kozinets <[email protected]>
Co-authored-by: Dmitry Kozinets <[email protected]>
Co-authored-by: Chan, Michelle <[email protected]>
  • Loading branch information
5 people authored Jan 21, 2022
1 parent 4cb8016 commit 1521070
Show file tree
Hide file tree
Showing 9 changed files with 413 additions and 128 deletions.
31 changes: 31 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,37 @@ lazy val sexpr = crossProject(JSPlatform, JVMPlatform, NativePlatform)
|}""".stripMargin
)
Seq(file)
}.taskValue,
Compile / sourceGenerators += Def.task {
val dir = (Compile / sourceManaged).value
val file = dir / "zio" / "morphir" / "sexpr" / "GeneratedTupleEncoders.scala"
val encoders = (1 to 22).map { i =>
val tparams = (1 to i).map(p => s"A$p").mkString(", ")
val implicits = (1 to i).map(p => s"A$p: SExprEncoder[A$p]").mkString(", ")
val work = (1 to i)
.map(p => s"A$p.unsafeEncode(t._$p, indent, out)")
.mkString("\n if (indent.isEmpty) out.write(',') else out.write(\", \")\n ")

s"""implicit def tuple$i[$tparams](implicit $implicits): SExprEncoder[Tuple$i[$tparams]] =
| new SExprEncoder[Tuple$i[$tparams]] {
| def unsafeEncode(t: Tuple$i[$tparams], indent: Option[Int], out: internal.Write): Unit = {
| out.write('[')
| $work
| out.write(']')
| }
| }""".stripMargin
}
IO.write(
file,
s"""package zio.morphir.sexpr
|
|import zio.morphir.sexpr.internal._
|
|private[sexpr] trait GeneratedTupleEncoders { this: SExprEncoder.type =>
| ${encoders.mkString("\n\n ")}
|}""".stripMargin
)
Seq(file)
}.taskValue
)
.enablePlugins(BuildInfoPlugin)
Expand Down
7 changes: 6 additions & 1 deletion project/BuildHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,15 @@ object BuildHelper {
case _ => Seq.empty
}

def platformList(platform: String): List[String] = (platform.toLowerCase match {
case "native" => Set("shared", platform)
case _ => Set("shared", platform, "shared-not-native")
}).toList

def platformSpecificSources(platform: String, conf: String, baseDirectory: File)(
versions: String*
) = for {
platform <- List("shared", platform)
platform <- platformList(platform)
version <- "scala" :: versions.toList.map("scala-" + _)
result = baseDirectory.getParentFile / platform.toLowerCase / "src" / conf / version
if result.exists
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package zio.morphir.sexpr

import zio.morphir.sexpr.Gens._
import zio.morphir.sexpr._
import zio.morphir.testing.ZioBaseSpec
import zio.test._
import zio.test.Assertion._
import zio.test.TestAspect._

import java.time._

object JavaTimeRoundtripSpec extends ZioBaseSpec {
def spec = suite("JavaTimeRoundtripSpec")(
suite("java.time")(
test("Duration") {
check(Gen.finiteDuration)(assertRoundtrips)
} @@ samples(1000),
test("Instant") {
check(Gen.instant)(assertRoundtrips)
} @@ samples(1000),
test("LocalDate") {
check(Gen.localDate)(assertRoundtrips)
} @@ samples(1000),
test("LocalDateTime") {
check(Gen.localDateTime)(assertRoundtrips)
} @@ samples(1000),
test("LocalTime") {
check(Gen.localTime)(assertRoundtrips)
} @@ samples(1000),
test("Month") {
check(Gen.month)(assertRoundtrips)
} @@ samples(1000),
test("MonthDay") {
check(Gen.monthDay)(assertRoundtrips)
} @@ samples(1000),
test("OffsetDateTime") {
check(Gen.offsetDateTime)(assertRoundtrips)
} @@ samples(1000),
test("OffsetTime") {
check(Gen.offsetTime)(assertRoundtrips)
} @@ samples(1000),
test("Period") {
check(Gen.period)(assertRoundtrips)
} @@ samples(1000),
test("Year") {
check(Gen.year)(assertRoundtrips)
} @@ samples(1000),
test("YearMonth") {
check(Gen.yearMonth)(assertRoundtrips)
} @@ samples(1000),
test("ZoneId") {
check(Gen.zoneId)(assertRoundtrips[ZoneId])
},
test("ZoneOffset") {
check(Gen.zoneOffset)(assertRoundtrips[ZoneOffset])
},
test("ZonedDateTime") {
check(Gen.zonedDateTime)(assertRoundtrips)
} @@ samples(1000)
)
)

private def assertRoundtrips[A: SExprEncoder: SExprDecoder](a: A) =
assert(a.toSExpr.fromSExpr[A])(isRight(equalTo(a))) &&
assert(a.toSExprPretty.fromSExpr[A])(isRight(equalTo(a)))
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
package zio.morphir.sexpr.ast

import zio.Chunk
import zio.morphir.sexpr.internal.*
import zio.morphir.sexpr.SExprEncoder
import zio.morphir.sexpr.{SExprDecoder, SExprEncoder}

sealed trait SExpr { self =>
import SExpr.*
import SExprCase.*
import SExprCase._
def $case: SExprCase[SExpr]

def fold[Z](f: SExprCase[Z] => Z): Z = self.$case match {
case c @ BoolCase(_) => f(c)
case c @ StrCase(_) => f(c)
case c @ NumCase(_) => f(c)
case NilCase => f(NilCase)
case ConsCase(car, cdr) => f(ConsCase(car.fold(f), cdr.fold(f)))
case QuotedCase(value) => f(QuotedCase(value.fold(f)))
case VectorCase(items) => f(VectorCase(items.map(_.fold(f))))
case c @ BoolCase(_) => f(c)
case c @ StrCase(_) => f(c)
case c @ NumCase(_) => f(c)
case c @ SymbolCase(_) => f(c)
case MapCase(items) =>
f(MapCase(items.map { case (k, v) =>
(k.fold(f), v.fold(f))
}))
case NilCase => f(NilCase)
case ConsCase(head, tail) => f(ConsCase(head.fold(f), tail.fold(f)))
case QuotedCase(get) => f(QuotedCase(get.fold(f)))
case VectorCase(items) => f(VectorCase(items.map(_.fold(f))))
}

final def widen: SExpr = this
}

object SExpr {
import SExprCase.*
import SExprCase._

implicit val decoder: SExprDecoder[SExpr] = ???

implicit val encoder: SExprEncoder[SExpr] = SExprEncoder.fromFunction {
case (sexpr: Bool, indent, out) => ???
Expand All @@ -43,6 +48,19 @@ object SExpr {
}
}

final case class SMap private[sexpr] ($case: VectorCase[SExpr]) extends SExpr
object SMap {
def apply(items: Map[SExpr, SExpr]): SMap = SMap(items)
def unapply(arg: SExpr): Option[Map[SExpr, SExpr]] = arg.$case match {
case MapCase(items: Map[SExpr, SExpr]) => Some(items)
case _ => None
}
}

case object Nil extends SExpr {
val $case = NilCase
}

final case class Num private[ast] ($case: NumCase) extends SExpr
object Num {
def apply(value: java.math.BigDecimal): Num = Num(NumCase(value))
Expand Down Expand Up @@ -71,34 +89,42 @@ object SExpr {
}
}

sealed trait SExprCase[+A] { self =>
import SExprCase.*
def map[B](f: A => B): SExprCase[B] = self match {
case BoolCase(value) => BoolCase(value)
case ConsCase(car, cdr) => ConsCase(f(car), f(cdr))
case StrCase(value) => StrCase(value)
case NilCase => NilCase
case NumCase(value) => NumCase(value)
case QuotedCase(value) => QuotedCase(f(value))
case VectorCase(items) => VectorCase(items.map(f))
sealed trait SExprCase[+Self] { self =>
import SExprCase._

def map[B](f: Self => B): SExprCase[B] = self match {
case BoolCase(value) => BoolCase(value)
case ConsCase(head, tail) => ConsCase(f(head), f(tail))
case StrCase(value) => StrCase(value)
case SymbolCase(value) => SymbolCase(value)
case MapCase(items) =>
MapCase(items.map { case (k, v) =>
(f(k), f(v))
})
case NilCase => NilCase
case NumCase(value) => NumCase(value)
case QuotedCase(get) => QuotedCase(f(get))
case VectorCase(items) => VectorCase(items.map(f))
}

}
object SExprCase {
sealed trait AtomCase[+A] extends SExprCase[A]
sealed trait CollectionCase[+A] extends SExprCase[A]
sealed trait ListCase[+A] extends CollectionCase[A]
sealed trait SymbolCase[+A] extends AtomCase[A]
sealed trait AtomCase[+Self] extends SExprCase[Self]
sealed trait CollectionCase[+Self] extends SExprCase[Self]
sealed trait ListCase[+Self] extends CollectionCase[Self]
sealed trait SymbolBaseCase[+Self] extends AtomCase[Self]

// Leaf Cases
final case class BoolCase(value: Boolean) extends SymbolCase[Nothing]
final case class BoolCase(value: Boolean) extends SymbolBaseCase[Nothing]
final case class StrCase(value: String) extends AtomCase[Nothing]
final case class NumCase(value: java.math.BigDecimal) extends AtomCase[Nothing]
case object NilCase extends ListCase[Nothing]
final case class SymbolCase(value: String) extends SymbolBaseCase[Nothing]

// Recursive Cases
final case class ConsCase[+A](car: A, cdr: A) extends ListCase[A]
final case class QuotedCase[+A](value: A) extends SExprCase[A]
final case class VectorCase[+A](items: Chunk[A]) extends CollectionCase[A]
final case class ConsCase[+Self](head: Self, tail: Self) extends ListCase[Self]
final case class MapCase[Self](items: Map[Self, Self]) extends CollectionCase[Self]
final case class QuotedCase[+Self](get: Self) extends SExprCase[Self]
final case class VectorCase[+Self](items: Chunk[Self]) extends CollectionCase[Self]

}
Original file line number Diff line number Diff line change
Expand Up @@ -270,40 +270,39 @@ object SExprDecoder extends GeneratedTupleDecoders with DecoderLowPriority1 {
//
// If alternative behaviour is desired, e.g. pass null to the underlying, then
// use a newtype wrapper.
implicit def option[A](implicit A: SExprDecoder[A]): SExprDecoder[Option[A]] =
new SExprDecoder[Option[A]] { self =>
private[this] val ull: Array[Char] = "ull".toCharArray
implicit def option[A](implicit A: SExprDecoder[A]): SExprDecoder[Option[A]] = new SExprDecoder[Option[A]] { self =>
private[this] val il: Array[Char] = "il".toCharArray

override def unsafeDecodeMissing(trace: List[SExprError]): Option[A] = Option.empty
override def unsafeDecodeMissing(trace: List[SExprError]): Option[A] = Option.empty

def unsafeDecode(trace: List[SExprError], in: RetractReader): Option[A] =
(in.nextNonWhitespace(): @switch) match {
case 'n' =>
Lexer.readChars(trace, in, ull, "null")
None
case _ =>
in.retract()
Some(A.unsafeDecode(trace, in))
}
def unsafeDecode(trace: List[SExprError], in: RetractReader): Option[A] =
(in.nextNonWhitespace(): @switch) match {
case 'n' =>
Lexer.readChars(trace, in, il, "nil")
None
case _ =>
in.retract()
Some(A.unsafeDecode(trace, in))
}

// overridden here to pass `None` to the new Decoder instead of throwing
// when called from a derived decoder
override def map[B](f: Option[A] => B): SExprDecoder[B] = new SExprDecoder[B] {
override def unsafeDecodeMissing(trace: List[SExprError]): B =
f(None)
// overridden here to pass `None` to the new Decoder instead of throwing
// when called from a derived decoder
override def map[B](f: Option[A] => B): SExprDecoder[B] = new SExprDecoder[B] {
override def unsafeDecodeMissing(trace: List[SExprError]): B =
f(None)

def unsafeDecode(trace: List[SExprError], in: RetractReader): B =
f(self.unsafeDecode(trace, in))
def unsafeDecode(trace: List[SExprError], in: RetractReader): B =
f(self.unsafeDecode(trace, in))

override final def fromAST(sexpr: SExpr): Either[String, B] =
self.fromAST(sexpr).map(f)
}
override final def fromAST(sexpr: SExpr): Either[String, B] =
self.fromAST(sexpr).map(f)
}

override final def fromAST(sexpr: SExpr): Either[String, Option[A]] = sexpr match {
// case SExpr.Null => Right(None) // TODO need to decide what to do for null
case _ => A.fromAST(sexpr).map(Some.apply)
}
override final def fromAST(sexpr: SExpr): Either[String, Option[A]] = sexpr match {
case SExpr.Nil => Right(None)
case _ => A.fromAST(sexpr).map(Some.apply)
}
}
/* TO DO need to figure out how to do Either
// supports multiple representations for compatibility with other libraries,
// but does not support the "discriminator field" encoding with a field named
Expand Down
Loading

0 comments on commit 1521070

Please sign in to comment.