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..a87b0fa --- /dev/null +++ b/macros/src/main/scala/com/wheaties/predicate/Macros.scala @@ -0,0 +1,77 @@ +package com.wheaties.predicate + +import annotation.{compileTimeOnly, StaticAnnotation} +import scala.reflect.macros.whitebox +import scala.reflect.macros._ +import scala.language.experimental.macros + +object Macros { + + @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}[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 + } + """) + } + + annottees.map(_.tree) match { + case (classDecl: ClassDef) :: Nil => modifiedClass(classDecl) + case _ => c.abort(c.enclosingPosition, "Invalid annottee") + } + } + + } +} + 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 330bed8..6f7503a 100644 --- a/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala +++ b/predicates/src/test/scala/com/wheaties/predicate/PredicateSpec.scala @@ -61,6 +61,24 @@ 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))) + 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 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 Predicate1.never) + filtered should equal(List.empty) + } } trait SpecExamples { diff --git a/project/build.scala b/project/build.scala index 59f5944..447569c 100644 --- a/project/build.scala +++ b/project/build.scala @@ -3,22 +3,43 @@ import sbt.Keys._ import xerial.sbt.Sonatype._ object Predicates extends Build{ - val predicates = Project( + + lazy val macros: Project = Project( + "macros", + file("macros"), + settings = commonSettings ++ Seq( + libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.8" + ) + ) + + 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", @@ -36,7 +57,7 @@ object Predicates extends Build{ pomIncludeRepository := { x => false }, publishMavenStyle := true, publishArtifact in Test := false - ) + ) ++ commonSettings val predicatesPom = http://github.com/wheaties/Predicates