From e4f67280d33d0f05706d67716ec5828a827d37d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sitko?= Date: Mon, 14 Mar 2016 22:54:29 +0100 Subject: [PATCH 1/4] tests added for Predicate1 --- .../com/wheaties/predicate/PredicateSpec.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala b/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala index 330bed8..0aea6ab 100644 --- a/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala +++ b/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala @@ -61,6 +61,21 @@ class PredicateSpec extends WordSpecLike val filtered = sampleList.filter(Modulo(2, 0) or (Modulo(3, 0) and LessThen(6))) filtered should equal(List(2, 3, 4, 6)) } + + "have implicit `not` method" in new SpecExamples { + val filtered = sampleList.filter(Predicate1.not.not(Modulo(2, 0))) + filtered should equal(List(3, 5)) + } + + "have accompanying Always" in new SpecExamples { + val filtered = sampleList.filter(Modulo(2, 0) or Always1) + filtered should equal(sampleList) + } + + "have accompanying Never" in new SpecExamples { + val filtered = sampleList.filter(Modulo(2, 0) and Never1) + filtered should equal(List.empty) + } } trait SpecExamples { From a024fd3fae573a686d295ec10d4989204b28d595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sitko?= Date: Mon, 14 Mar 2016 23:19:44 +0100 Subject: [PATCH 2/4] added macros subproject --- .gitignore | 10 ++++------ .../main/scala/com/wheaties/predicate/Macros.scala | 13 +++++++++++++ project/build.scala | 7 ++++++- 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 macros/src/main/scala/com/wheaties/predicate/Macros.scala diff --git a/.gitignore b/.gitignore index b5a8dd4..6d7e047 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,7 @@ /RUNNING_PID /logs/ -/project/*-shim.sbt -/project/project/ -/project/target/ -/target/ +project/*-shim.sbt +project/project/ +project/target/ +target/ /.idea/ -/choices/target/ -/predicates/target/ \ No newline at end of file diff --git a/macros/src/main/scala/com/wheaties/predicate/Macros.scala b/macros/src/main/scala/com/wheaties/predicate/Macros.scala new file mode 100644 index 0000000..1e1e56a --- /dev/null +++ b/macros/src/main/scala/com/wheaties/predicate/Macros.scala @@ -0,0 +1,13 @@ +package com.wheaties.predicate + +import scala.reflect.macros.blackbox +import scala.language.experimental.macros + +object Macros { + def hello: Unit = macro impl + + def impl(c: blackbox.Context): c.Expr[Unit] = { + import c.universe._ + c.Expr(q"""println("Hello World")""") + } +} diff --git a/project/build.scala b/project/build.scala index 59f5944..1aee12d 100644 --- a/project/build.scala +++ b/project/build.scala @@ -3,13 +3,18 @@ import sbt.Keys._ import xerial.sbt.Sonatype._ object Predicates extends Build{ + val macros = project.in(file("macros")).settings( + libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.7", + scalaVersion := "2.11.7" + ) + val predicates = Project( id = "predicates", base = file("predicates"), settings = Project.defaultSettings ++ baseSettings ++ Seq( libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.1" % "test" ) - ) + ).dependsOn(macros) val baseSettings = Seq( version := "0.1", From 74a5e86f8b874f6021021c6744c0aa3fb186e785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sitko?= Date: Tue, 15 Mar 2016 21:40:05 +0100 Subject: [PATCH 3/4] add macro paradise --- .../scala/com/wheaties/predicate/Macros.scala | 67 +++++++++++++++++-- project/build.scala | 34 +++++++--- 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/macros/src/main/scala/com/wheaties/predicate/Macros.scala b/macros/src/main/scala/com/wheaties/predicate/Macros.scala index 1e1e56a..97e9bff 100644 --- a/macros/src/main/scala/com/wheaties/predicate/Macros.scala +++ b/macros/src/main/scala/com/wheaties/predicate/Macros.scala @@ -1,13 +1,70 @@ package com.wheaties.predicate -import scala.reflect.macros.blackbox +import annotation.{compileTimeOnly, StaticAnnotation, Annotation} +import scala.reflect.macros.whitebox +import scala.reflect.macros._ import scala.language.experimental.macros object Macros { - def hello: Unit = macro impl +// def hello: Unit = macro impl +// +// def impl(c: whitebox.Context): c.Expr[Unit] = { +// import c.universe._ +// c.Expr(q"""println("Hello World")""") +// } - def impl(c: blackbox.Context): c.Expr[Unit] = { - import c.universe._ - c.Expr(q"""println("Hello World")""") + @compileTimeOnly("GeneratePredicate must be called during compilation") + class GeneratePredicate extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro GeneratePredicate.impl + } + + object GeneratePredicate { + def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { + import c.universe._ + + println("bazinga") + + def modifiedClass(classDecl: ClassDef) = { + println(s"bazinga ${classDecl.name}") + c.Expr( + q""" + trait ${classDecl.name} { + def print() = println("hello annotations") + } + """) + } + + annottees.map(_.tree) match { + case (classDecl: ClassDef) :: Nil => modifiedClass(classDecl) + case _ => c.abort(c.enclosingPosition, "Invalid annottee") + } + } + + } + + + object helloMacro { + def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { + import c.universe._ + import Flag._ + val result = { + annottees.map(_.tree).toList match { + case q"object $name extends ..$parents { ..$body }" :: Nil => + q""" + object $name extends ..$parents { + def hello: ${typeOf[String]} = "hello" + ..$body + } + """ + } + } + c.Expr[Any](result) + } + } + + @compileTimeOnly("some msg") + class hello extends StaticAnnotation { + def macroTransform(annottees: Any*) = macro helloMacro.impl } } + diff --git a/project/build.scala b/project/build.scala index 1aee12d..447569c 100644 --- a/project/build.scala +++ b/project/build.scala @@ -3,27 +3,43 @@ import sbt.Keys._ import xerial.sbt.Sonatype._ object Predicates extends Build{ - val macros = project.in(file("macros")).settings( - libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.7", - scalaVersion := "2.11.7" + + lazy val macros: Project = Project( + "macros", + file("macros"), + settings = commonSettings ++ Seq( + libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.8" + ) ) - val predicates = Project( + lazy val predicates = Project( id = "predicates", base = file("predicates"), - settings = Project.defaultSettings ++ baseSettings ++ Seq( - libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.1" % "test" + settings = baseSettings ++ Seq( + libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.1" % "test", + resolvers += Resolver.sonatypeRepo("snapshots"), + resolvers += Resolver.sonatypeRepo("releases"), + addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full) ) ).dependsOn(macros) + val commonSettings = { + val paradiseVersion = "2.1.0" + Seq( + scalaVersion := "2.11.8", + resolvers += Resolver.sonatypeRepo("snapshots"), + resolvers += Resolver.sonatypeRepo("releases"), + addCompilerPlugin("org.scalamacros" % "paradise" % paradiseVersion cross CrossVersion.full) + ) + } + val baseSettings = Seq( version := "0.1", - scalaVersion := "2.11.6", organization := "com.github.wheaties", scalacOptions := Seq("-deprecation", "-encoding", "UTF-8", "-feature", - "-language:higherKinds", + "-language:higherKinds", "-language:existentials", "-unchecked", "-Xfatal-warnings", @@ -41,7 +57,7 @@ object Predicates extends Build{ pomIncludeRepository := { x => false }, publishMavenStyle := true, publishArtifact in Test := false - ) + ) ++ commonSettings val predicatesPom = http://github.com/wheaties/Predicates From c56f4d18b7585dae37a1eaa43c98114840b4902c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sitko?= Date: Tue, 15 Mar 2016 22:33:22 +0100 Subject: [PATCH 4/4] Predicate1 implemented with macro annotation --- .../scala/com/wheaties/predicate/Macros.scala | 75 ++++++++++--------- .../com/wheaties/predicate/Predicates1.scala | 41 +--------- .../wheaties/predicate/PredicateSpec.scala | 9 ++- 3 files changed, 50 insertions(+), 75 deletions(-) diff --git a/macros/src/main/scala/com/wheaties/predicate/Macros.scala b/macros/src/main/scala/com/wheaties/predicate/Macros.scala index 97e9bff..a87b0fa 100644 --- a/macros/src/main/scala/com/wheaties/predicate/Macros.scala +++ b/macros/src/main/scala/com/wheaties/predicate/Macros.scala @@ -1,17 +1,11 @@ package com.wheaties.predicate -import annotation.{compileTimeOnly, StaticAnnotation, Annotation} +import annotation.{compileTimeOnly, StaticAnnotation} import scala.reflect.macros.whitebox import scala.reflect.macros._ import scala.language.experimental.macros object Macros { -// def hello: Unit = macro impl -// -// def impl(c: whitebox.Context): c.Expr[Unit] = { -// import c.universe._ -// c.Expr(q"""println("Hello World")""") -// } @compileTimeOnly("GeneratePredicate must be called during compilation") class GeneratePredicate extends StaticAnnotation { @@ -28,8 +22,46 @@ object Macros { println(s"bazinga ${classDecl.name}") c.Expr( q""" - trait ${classDecl.name} { - def print() = println("hello annotations") + trait ${classDecl.name}[T1] extends Function1[T1, Boolean]{ + self => + + def or[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ + def apply(arg1: TT1) = self(arg1) || that(arg1) + } + def and[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ + def apply(arg1: TT1) = self(arg1) && that(arg1) + } + def xor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ + def apply(arg1: TT1) = if(self(arg1)) !that(arg1) else that(arg1) + } + def nor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ + def apply(arg1: TT1) = !(self(arg1) || that(arg1)) + } + def nand[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ + def apply(arg1: TT1) = !(self(arg1) && that(arg1)) + } + def nxor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ + def apply(arg1: TT1) = if(self(arg1)) that(arg1) else !that(arg1) + } + def not = new Predicate1[T1]{ + def apply(arg1: T1) = !self(arg1) + } + override def toString() = "" + + } + + object ${classDecl.name.toTermName} { + object Always extends ${classDecl.name}[Any]{ + def apply(arg1: Any) = true + } + + val always = Always + + object Never extends ${classDecl.name}[Any]{ + def apply(arg1: Any) = false + } + + val never = Never } """) } @@ -41,30 +73,5 @@ object Macros { } } - - - object helloMacro { - def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { - import c.universe._ - import Flag._ - val result = { - annottees.map(_.tree).toList match { - case q"object $name extends ..$parents { ..$body }" :: Nil => - q""" - object $name extends ..$parents { - def hello: ${typeOf[String]} = "hello" - ..$body - } - """ - } - } - c.Expr[Any](result) - } - } - - @compileTimeOnly("some msg") - class hello extends StaticAnnotation { - def macroTransform(annottees: Any*) = macro helloMacro.impl - } } diff --git a/predicates/src/main/scala/com/wheaties/predicate/Predicates1.scala b/predicates/src/main/scala/com/wheaties/predicate/Predicates1.scala index 2f44cce..9c3b0b3 100644 --- a/predicates/src/main/scala/com/wheaties/predicate/Predicates1.scala +++ b/predicates/src/main/scala/com/wheaties/predicate/Predicates1.scala @@ -1,41 +1,6 @@ package com.wheaties.predicate -import com.wheaties.logical._ +import com.wheaties.predicate.Macros.GeneratePredicate -trait Predicate1[T1] extends Function1[T1, Boolean]{ - self => - - def or[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ - def apply(arg1: TT1) = self(arg1) || that(arg1) - } - def and[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ - def apply(arg1: TT1) = self(arg1) && that(arg1) - } - def xor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ - def apply(arg1: TT1) = if(self(arg1)) !that(arg1) else that(arg1) - } - def nor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ - def apply(arg1: TT1) = !(self(arg1) || that(arg1)) - } - def nand[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ - def apply(arg1: TT1) = !(self(arg1) && that(arg1)) - } - def nxor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{ - def apply(arg1: TT1) = if(self(arg1)) that(arg1) else !that(arg1) - } - override def toString() = "" - -} -object Predicate1{ - implicit def not[T1] = new Negation[Predicate1[T1]]{ - def not(pred: Predicate1[T1]) = new Predicate1[T1]{ - def apply(arg1: T1) = !pred(arg1) - } - } -} -object Always1 extends Predicate1[Any]{ - def apply(arg1: Any) = true -} -object Never1 extends Predicate1[Any]{ - def apply(arg1: Any) = false -} \ No newline at end of file +@GeneratePredicate +trait Predicate1 diff --git a/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala b/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala index 0aea6ab..6f7503a 100644 --- a/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala +++ b/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala @@ -63,17 +63,20 @@ class PredicateSpec extends WordSpecLike } "have implicit `not` method" in new SpecExamples { - val filtered = sampleList.filter(Predicate1.not.not(Modulo(2, 0))) +// val filtered = sampleList.filter(Predicate1.not.not(Modulo(2, 0))) + val filtered = sampleList.filter(Modulo(2, 0).not) filtered should equal(List(3, 5)) } "have accompanying Always" in new SpecExamples { - val filtered = sampleList.filter(Modulo(2, 0) or Always1) +// val filtered = sampleList.filter(Modulo(2, 0) or Always1) + val filtered = sampleList.filter(Modulo(2, 0) or Predicate1.always) filtered should equal(sampleList) } "have accompanying Never" in new SpecExamples { - val filtered = sampleList.filter(Modulo(2, 0) and Never1) +// val filtered = sampleList.filter(Modulo(2, 0) and Never1) + val filtered = sampleList.filter(Modulo(2, 0) and Predicate1.never) filtered should equal(List.empty) } }