aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Pretty <jon.pretty@propensive.com>2017-11-03 14:15:07 +0100
committerJon Pretty <jon.pretty@propensive.com>2017-11-03 14:15:07 +0100
commit4d957fd5a35f1d7eed8643b22132b65696c80c8a (patch)
tree9cb14d42927b4f30f7d976f22be96d258b01fd61
parent25fb02b13b721004269f9bd9a4e5d22c2bb219c4 (diff)
downloadmagnolia-4d957fd5a35f1d7eed8643b22132b65696c80c8a.tar.gz
magnolia-4d957fd5a35f1d7eed8643b22132b65696c80c8a.tar.bz2
magnolia-4d957fd5a35f1d7eed8643b22132b65696c80c8a.zip
Test fixes and other miscellaneous changes
-rw-r--r--build.sbt16
-rw-r--r--core/src/main/scala/magnolia.scala56
-rw-r--r--examples/src/main/scala/typeclasses.scala29
-rw-r--r--tests/src/main/scala/adt.scala7
-rw-r--r--tests/src/main/scala/tests.scala10
5 files changed, 90 insertions, 28 deletions
diff --git a/build.sbt b/build.sbt
index 9fc7552..6ae8750 100644
--- a/build.sbt
+++ b/build.sbt
@@ -45,9 +45,23 @@ lazy val testsJVM = tests.jvm
lazy val testsJS = tests.js
lazy val testsNative = tests.native
+lazy val benchmarks = crossProject(JSPlatform, JVMPlatform, NativePlatform)
+ .crossType(CrossType.Pure)
+ .in(file("benchmarks"))
+ .settings(buildSettings: _*)
+ .settings(noPublishSettings: _*)
+ .settings(moduleName := "magnolia-benchmarks")
+ .settings(quasiQuotesDependencies)
+ .nativeSettings(nativeSettings)
+ .dependsOn(examples)
+
+lazy val benchmarksJVM = benchmarks.jvm
+lazy val benchmarksJS = benchmarks.js
+lazy val benchmarksNative = benchmarks.native
+
lazy val buildSettings = Seq(
organization := "com.propensive",
- scalaVersion := "2.12.2",
+ scalaVersion := "2.12.4",
name := "magnolia",
version := "0.2.0",
scalacOptions ++= Seq("-deprecation", "-feature", "-Ywarn-value-discard", "-Ywarn-dead-code", "-Ywarn-nullary-unit", "-Ywarn-numeric-widen", "-Ywarn-inaccessible", "-Ywarn-adapted-args"),
diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala
index cabe88e..4d3d3d3 100644
--- a/core/src/main/scala/magnolia.scala
+++ b/core/src/main/scala/magnolia.scala
@@ -45,7 +45,7 @@ object Magnolia {
recursionStack = recursionStack.updated(
c.enclosingPosition,
recursionStack.get(c.enclosingPosition).map(_.push(path, key, value)).getOrElse(
- Stack(List(Frame(path, key, value)), Nil))
+ Stack(Map(), List(Frame(path, key, value)), Nil))
)
try Some(fn) catch { case e: Exception => None } finally {
@@ -73,17 +73,21 @@ object Magnolia {
val methodAsString = methodName.encodedName.toString
q"_root_.magnolia.Deferred.apply[$searchType]($methodAsString)"
}.orElse {
- scala.util.Try {
- val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase
- val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass"))
- recurse(ChainedImplicit(genericType.toString), genericType, assignedName) {
- val inferredImplicit = c.inferImplicitValue(searchType, false, false)
- q"""{
- def $assignedName: $searchType = $inferredImplicit
- $assignedName
- }"""
- }.get
- }.toOption.orElse(directInferImplicit(genericType, typeConstructor))
+ val (inferredImplicit, newStack) = recursionStack(c.enclosingPosition).lookup(c)(searchType) {
+ scala.util.Try {
+ val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase
+ val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass"))
+ recurse(ChainedImplicit(genericType.toString), genericType, assignedName) {
+ val inferredImplicit = c.inferImplicitValue(searchType, false, false)
+ q"""{
+ def $assignedName: $searchType = $inferredImplicit
+ $assignedName
+ }"""
+ }.get
+ }.toOption.orElse(directInferImplicit(genericType, typeConstructor))
+ }
+ recursionStack = recursionStack.updated(c.enclosingPosition, newStack)
+ inferredImplicit
}.getOrElse {
val currentStack: Stack = recursionStack(c.enclosingPosition)
@@ -111,8 +115,6 @@ object Magnolia {
val resultType = appliedType(typeConstructor, genericType)
- println(s"Deriving $genericType")
-
// FIXME: Handle AnyVals
if(isCaseObject) {
val termSym = genericType.typeSymbol.companionSymbol
@@ -162,6 +164,7 @@ object Magnolia {
def label: _root_.java.lang.String = $label
def dereference(param: ${genericType}): ${paramType} = param.${TermName(label)}
}"""
+
}
val constructor = q"""new $genericType(..${callables.zip(implicits).map { case (call, imp) =>
@@ -236,7 +239,7 @@ object Magnolia {
val genericType: Type = weakTypeOf[T]
val currentStack: Stack =
- recursionStack.get(c.enclosingPosition).getOrElse(Stack(List(), List()))
+ recursionStack.get(c.enclosingPosition).getOrElse(Stack(Map(), List(), List()))
val directlyReentrant = Some(genericType) == currentStack.frames.headOption.map(_.genericType)
@@ -268,10 +271,11 @@ object Magnolia {
if(currentStack.frames.isEmpty) recursionStack = ListMap()
result.map { tree =>
- val out = if(currentStack.frames.isEmpty) c.untypecheck(removeDeferred.transform(tree))
- else tree
- println(out)
- out
+ if(currentStack.frames.isEmpty) {
+ val out = c.untypecheck(removeDeferred.transform(tree))
+ //println(out)
+ out
+ } else tree
}.getOrElse {
c.abort(c.enclosingPosition, s"magnolia: could not infer typeclass for type $genericType")
}
@@ -297,13 +301,21 @@ private[magnolia] object CompileTimeState {
case class ImplicitNotFound(genericType: String, path: List[TypePath])
- case class Stack(frames: List[Frame], errors: List[ImplicitNotFound]) {
+ case class Stack(cache: Map[whitebox.Context#Type, Option[whitebox.Context#Tree]], frames: List[Frame], errors: List[ImplicitNotFound]) {
+ def lookup(c: whitebox.Context)(t: c.Type)(orElse: => Option[c.Tree]): (Option[c.Tree], Stack) =
+ if(cache.contains(t)) {
+ (cache(t).asInstanceOf[Option[c.Tree]], this)
+ } else {
+ val value = orElse
+ (value, copy(cache.updated(t, value)))
+ }
+
def push(path: TypePath, key: whitebox.Context#Type,
value: whitebox.Context#TermName): Stack =
- Stack(Frame(path, key, value) :: frames, errors)
+ Stack(cache, Frame(path, key, value) :: frames, errors)
- def pop(): Stack = Stack(frames.tail, errors)
+ def pop(): Stack = Stack(cache, frames.tail, errors)
}
case class Frame(path: TypePath, genericType: whitebox.Context#Type,
diff --git a/examples/src/main/scala/typeclasses.scala b/examples/src/main/scala/typeclasses.scala
index 440c5bc..b4af6d0 100644
--- a/examples/src/main/scala/typeclasses.scala
+++ b/examples/src/main/scala/typeclasses.scala
@@ -90,3 +90,32 @@ sealed trait Entity
case class Company(name: String) extends Entity
case class Person(name: String, age: Int) extends Entity
case class Address(line1: String, occupant: Person)
+
+
+
+sealed trait Alphabet
+
+case class Greek(άλφα: Letter, βήτα: Letter, γάμα: Letter, δέλτα: Letter, έψιλον: Letter, ζήτα: Letter, ήτα: Letter, θήτα: Letter) extends Alphabet
+case class Cyrillic(б: Letter, в: Letter, г: Letter, д: Letter, ж: Letter, з: Letter) extends Alphabet
+case class Latin(a: Letter, b: Letter, c: Letter, d: Letter, e: Letter, f: Letter, g: Letter, h: Letter, i: Letter, j: Letter, k: Letter, l: Letter, m: Letter) extends Alphabet
+
+case class Letter(name: String, phonetic: String)
+//case class Country(name: String, language: Language, leader: Person)
+case class Language(name: String, code: String, alphabet: Alphabet)
+//case class Person(name: String, dateOfBirth: Date)
+case class Date(year: Int, month: Month, day: Int)
+
+sealed trait Month
+case object Jan extends Month
+case object Feb extends Month
+case object Mar extends Month
+case object Apr extends Month
+case object May extends Month
+case object Jun extends Month
+case object Jul extends Month
+case object Aug extends Month
+case object Sep extends Month
+case object Oct extends Month
+case object Nov extends Month
+case object Dec extends Month
+
diff --git a/tests/src/main/scala/adt.scala b/tests/src/main/scala/adt.scala
new file mode 100644
index 0000000..75316aa
--- /dev/null
+++ b/tests/src/main/scala/adt.scala
@@ -0,0 +1,7 @@
+package adt
+
+import magnolia._, examples._
+
+object Gen {
+ Eq.generic[Alphabet]
+}
diff --git a/tests/src/main/scala/tests.scala b/tests/src/main/scala/tests.scala
index aea6cc3..64d6594 100644
--- a/tests/src/main/scala/tests.scala
+++ b/tests/src/main/scala/tests.scala
@@ -28,7 +28,7 @@ object Tests extends TestApp {
test("serialize a Branch") {
import magnolia.examples._
implicitly[Show[String, Branch[String]]].show(Branch(Leaf("LHS"), Leaf("RHS")))
- }.assert(_ == "Branch(left=Leaf(value=LHS),right=Leaf(value=RHS))")
+ }.assert(_ == "Branch[String](left=Leaf[String](value=LHS),right=Leaf[String](value=RHS))")
test("test equality false") {
import examples._
@@ -52,9 +52,9 @@ object Tests extends TestApp {
test("construction of Show instance for Leaf") {
scalac"""
import magnolia.examples._
- implicitly[Show[String, Leaf[String]]]
+ implicitly[Show[String, Leaf[java.lang.String]]]
"""
- }.assert(_ == (Returns(fqt"magnolia.examples.Show[String,magnolia.examples.Leaf[java.lang.String]]"): Compilation))
+ }.assert(_ == (Returns(fqt"magnolia.examples.Show[String,magnolia.examples.Leaf[String]]"): Compilation))
test("construction of Show instance for Tree") {
scalac"""
@@ -65,11 +65,11 @@ object Tests extends TestApp {
test("serialize a Leaf") {
implicitly[Show[String, Leaf[String]]].show(Leaf("testing"))
- }.assert(_ == "Leaf(value=testing)")
+ }.assert(_ == "Leaf[String](value=testing)")
test("serialize a Branch as a Tree") {
implicitly[Show[String, Tree[String]]].show(Branch(Leaf("LHS"), Leaf("RHS")))
- }.assert(_ == "Branch(left=Leaf(value=LHS),right=Leaf(value=RHS))")
+ }.assert(_ == "Branch[String](left=Leaf[String](value=LHS),right=Leaf[String](value=RHS))")
test("show error stack") {
scalac"""