diff --git a/Dsl/.js/build.sbt b/Dsl/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/Dsl/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/Dsl/.jvm/build.sbt b/Dsl/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/Dsl/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/Dsl/build.sbt b/Dsl/build.sbt deleted file mode 100644 index e2a23844..00000000 --- a/Dsl/build.sbt +++ /dev/null @@ -1,32 +0,0 @@ -scalacOptions ++= { - scalaBinaryVersion.value match { - case "2.11" => - Some("-Xexperimental") - case _ => - None - } -} - - -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } -} - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } -} - -libraryDependencies += "com.thoughtworks.enableIf" %% "enableif" % "1.1.6" - -// Improve backward compatibility for Scala 2.11 -scalacOptions += "-Ydelambdafy:method" diff --git a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala b/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala deleted file mode 100644 index 39ed2c1c..00000000 --- a/Dsl/src/main/scala/com/thoughtworks/dsl/Dsl.scala +++ /dev/null @@ -1,265 +0,0 @@ -package com.thoughtworks.dsl - -import com.thoughtworks.dsl.Dsl.!! -import com.thoughtworks.enableMembersIf - -import scala.annotation._ -import scala.collection._ -import scala.collection.mutable.Builder -import scala.concurrent.Future -import scala.language.higherKinds -import scala.util.{Failure, Success, Try} -import scala.util.control.{NonFatal, TailCalls} -import scala.util.control.TailCalls.TailRec - -/** The domain-specific interpreter for `Keyword` in `Domain`, - * which is a dependent type type class that registers an asynchronous callback function, - * to handle the `Value` inside `Keyword`. - * - * @tparam Value The value held inside `Keyword`. - * @author 杨博 (Yang Bo) - * @example Creating a collaborative DSL in [[https://github.com/ThoughtWorksInc/Dsl.scala Dsl.scala]] is easy. - * Only two steps are required: - * - * - Defining their domain-specific [[com.thoughtworks.dsl.Dsl.Keyword Keyword]]. - * - Implementing this [[Dsl]] type class, which is an interpreter for an [[com.thoughtworks.dsl.Dsl.Keyword Keyword]]. - * - */ -@implicitNotFound("The keyword ${Keyword} is not supported inside a function that returns ${Domain}.") -trait Dsl[-Keyword, Domain, +Value] { - - /** Registers an asynchronous callback `handler` on `keyword`, to handle the `Value`. */ - def cpsApply(keyword: Keyword, handler: Value => Domain): Domain - -} - -private[dsl] trait LowPriorityDsl2 { - - import Dsl._ - import Scala211Or212._ - import Scala213._ - - implicit def nothingCollectionDsl[Keyword, Element, Collection[_]]( - implicit factory: Factory[Element, Collection[Element]], - restDsl: Dsl[Keyword, Element, Nothing] - ): Dsl[Keyword, Collection[Element], Nothing] = { (keyword, handler) => - singleton(resetDomain(keyword)) - } -} - -private[dsl] trait LowPriorityDsl1 extends LowPriorityDsl2 { - - implicit def derivedFunction1Dsl[Keyword, State, Domain, Value]( - implicit restDsl: Dsl[Keyword, Domain, Value] - ): Dsl[Keyword, State => Domain, Value] = { (keyword, handler) => - val restDsl1 = restDsl - locally { state: State => - val handler1 = handler - restDsl1.cpsApply(keyword, handler1(_)(state)) - } - - } - -} - -private[dsl] trait LowPriorityDsl0 extends LowPriorityDsl1 { - -// // FIXME: Shift -// implicit def continuationDsl[Keyword, LeftDomain, RightDomain, Value]( -// implicit restDsl: Dsl[Keyword, LeftDomain, Value], -// shiftDsl2: Dsl[Shift[LeftDomain, RightDomain], LeftDomain, RightDomain] -// ): Dsl[Keyword, LeftDomain !! RightDomain, Value] = { -// new Dsl[Keyword, LeftDomain !! RightDomain, Value] { -// def cpsApply(keyword: Keyword, handler: Value => LeftDomain !! RightDomain): LeftDomain !! RightDomain = { -// (continue: RightDomain => LeftDomain) => -// restDsl.cpsApply(keyword, { a => -// restDsl2.cpsApply(handler(a), continue) -// }) -// } -// } -// } - - implicit def throwableContinuationDsl[Keyword, LeftDomain, Value]( - implicit restDsl: Dsl[Keyword, LeftDomain, Value] - ): Dsl[Keyword, LeftDomain !! Throwable, Value] = { (keyword, handler) => continue => - restDsl.cpsApply( - keyword, - new (Value => LeftDomain) { - def apply(value: Value): LeftDomain = { - val protectedContinuation = try { - handler(value) - } catch { - case NonFatal(e) => - return continue(e) - } - // FIXME: Shift[Domain, Throwable] - protectedContinuation(continue) - } - } - ) - } - -} - -object Dsl extends LowPriorityDsl0 { - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$""")) - private[dsl] object Scala211Or212 { - type Factory[-A, +C] = scala.collection.generic.CanBuildFrom[Nothing, A, C] - - @inline - def singleton[A, C](a: A)(implicit factory: Factory[A, C]): C = { - val builder = factory() - builder.sizeHint(1) - builder += a - builder.result() - } - - } - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.13\..*$""")) - private[dsl] object Scala213 { - - @inline - def singleton[A, C](a: A)(implicit factory: Factory[A, C]): C = { - factory.fromSpecific(a :: Nil) - } - - } - - @inline - private[dsl] def resetDomain[Keyword, Domain](keyword: Keyword)( - implicit dsl: Dsl[Keyword, Domain, Domain]): Domain = { - dsl.cpsApply(keyword, implicitly) - } - - implicit def nothingContinuationDsl[Keyword, LeftDomain, RightDomain]( - implicit restDsl: Dsl[Keyword, RightDomain, Nothing]): Dsl[Keyword, LeftDomain !! RightDomain, Nothing] = { - (keyword, handler) => - _(resetDomain(keyword)) - } - - implicit def nothingFutureDsl[Keyword, Domain]( - implicit restDsl: Dsl[Keyword, Domain, Nothing]): Dsl[Keyword, Future[Domain], Nothing] = { (keyword, handler) => - Future.successful(resetDomain(keyword)) - } - - implicit def derivedTailRecDsl[Keyword, Domain, Value]( - implicit restDsl: Dsl[Keyword, Domain, Value]): Dsl[Keyword, TailRec[Domain], Value] = { (keyword, handler) => - TailCalls.done { - restDsl.cpsApply(keyword, { value => - handler(value).result - }) - } - } - - implicit def derivedThrowableTailRecDsl[Keyword, LeftDomain, Value]( - implicit restDsl: Dsl[Keyword, LeftDomain !! Throwable, Value]) - : Dsl[Keyword, TailRec[LeftDomain] !! Throwable, Value] = { (keyword, handler) => tailRecFailureHandler => - TailCalls.done(restDsl.cpsApply(keyword, { value => failureHandler => - handler(value) { e => - TailCalls.done(failureHandler(e)) - }.result - }) { e => - tailRecFailureHandler(e).result - }) - } - - type Continuation[R, +A] = (A => R @reset) => R - - object Continuation { - @inline - def now[R, A](a: A): R !! A = _(a) - - @inline - def empty[R, A](r: R): R !! A = Function.const(r) - - @inline - def delay[R, A](a: () => A): R !! A = _(a()) - - @inline - def apply[R, A](a: => A): (R !! A) @reset = delay(a _) - - def toTryContinuation[LeftDomain, Value](task: LeftDomain !! Throwable !! Value)( - handler: Try[Value] => LeftDomain): LeftDomain = { - task { a => failureHandler => - handler(Success(a)) - } { e => - handler(Failure(e)) - } - } - - def fromTryContinuation[LeftDomain, Value](continuation: LeftDomain !! Try[Value])( - successHandler: Value => LeftDomain !! Throwable)(failureHandler: Throwable => LeftDomain): LeftDomain = { - continuation( - new (Try[Value] => LeftDomain) { - def apply(result: Try[Value]): LeftDomain = { - result match { - case Success(a) => - val protectedContinuation = try { - successHandler(a) - } catch { - case NonFatal(e) => - return failureHandler(e) - } - protectedContinuation(failureHandler) - case Failure(e) => - failureHandler(e) - } - } - } - ) - } - - } - - type !![R, +A] = Continuation[R, A] - val !! = Continuation - - private[dsl] /* sealed */ trait ResetAnnotation extends Annotation with StaticAnnotation - private[dsl] final class nonTypeConstraintReset extends ResetAnnotation with StaticAnnotation - - /** An annotation to explicitly perform reset control operator on a code block. - * - * @note This annotation can be automatically added - * if [[compilerplugins.ResetEverywhere ResetEverywhere]] compiler plug-in is enabled. - */ - final class reset extends ResetAnnotation with StaticAnnotation with TypeConstraint - - /** An annotation to mark a method is a shift control operator. */ - final class shift extends StaticAnnotation - - def apply[Keyword, Domain, Value](implicit typeClass: Dsl[Keyword, Domain, Value]): Dsl[Keyword, Domain, Value] = - typeClass - - /** - * - * @tparam Self the self type - * @see [[https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern Curiously recurring template pattern]] - * for the reason why we need the `Self` type parameter - */ - trait Keyword[Self, Value] extends Any { this: Self => - - @shift - @compileTimeOnly( - """This method requires the compiler plugin: `addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "latest.release")` and must only be called inside a code block annotated as `@reset`.""") - final def unary_! : Value = { - throw new IllegalAccessException( - """This method requires the compiler plugin: `addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "latest.release")` and must only be called inside a code block annotated as `@reset`.""" - ) - } - - @inline - final def cpsApply[Domain](handler: Value => Domain)(implicit dsl: Dsl[Self, Domain, Value]): Domain = { - dsl.cpsApply(this, handler) - } - - /** An alias to [[cpsApply]]. */ - @inline - final def apply[Domain](handler: Value => Domain)(implicit dsl: Dsl[Self, Domain, Value]): Domain = { - cpsApply(handler) - } - - } - -} diff --git a/build.sbt b/build.sbt index 595f4864..29d77d9b 100644 --- a/build.sbt +++ b/build.sbt @@ -1,333 +1,13 @@ // shadow sbt-scalajs' crossProject(JSPlatform, JVMPlatform) and CrossType from Scala.js 0.6.x import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} -lazy val `compilerplugins-BangNotation` = project - .dependsOn(DslJVM % Test, DslJVM % Provided) - .settings( - scalacOptions in Test += raw"""-Xplugin:${(packageBin in Compile).value}""", - scalacOptions in Test += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - -lazy val `compilerplugins-ResetEverywhere` = project.dependsOn(DslJVM % Test, DslJVM % Provided) - -lazy val Dsl = - crossProject(JSPlatform, JVMPlatform).crossType(CrossType.Pure).build() -lazy val DslJS = Dsl.js -lazy val DslJVM = Dsl.jvm - -lazy val `domains-task` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(`keywords-Shift`, - `keywords-Each` % Test, - `keywords-Fork` % Test, - `keywords-Using` % Test, - `keywords-Yield` % Test, - `comprehension` % Test) -lazy val `domains-taskJS` = `domains-task`.js -lazy val `domains-taskJVM` = `domains-task`.jvm - -lazy val `keywords-Fork` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Shift`, `keywords-Catch`, `keywords-Continue`, `keywords-ForEach`) -lazy val `keywords-ForkJS` = `keywords-Fork`.js -lazy val `keywords-ForkJVM` = `keywords-Fork`.jvm - -lazy val `keywords-Return` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl) - -lazy val `keywords-ReturnJS` = `keywords-Return`.js -lazy val `keywords-ReturnJVM` = `keywords-Return`.jvm - -lazy val `keywords-Continue` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Each` % Test) - -lazy val `keywords-ContinueJS` = `keywords-Continue`.js -lazy val `keywords-ContinueJVM` = `keywords-Continue`.jvm - -lazy val `keywords-Get` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl) - -lazy val `keywords-GetJS` = `keywords-Get`.js -lazy val `keywords-GetJVM` = `keywords-Get`.jvm - -lazy val `keywords-Put` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Get` % Test, `keywords-Yield` % Test, `keywords-Return` % Test) -lazy val `keywords-PutJS` = `keywords-Put`.js -lazy val `keywords-PutJVM` = `keywords-Put`.jvm - -lazy val `keywords-AsynchronousIo` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(`keywords-Shift`, `keywords-Each` % Test, `keywords-Using` % Test, `comprehension` % Test, `domains-task` % Test) -lazy val `keywords-AsynchronousIoJS` = `keywords-AsynchronousIo`.js -lazy val `keywords-AsynchronousIoJVM` = `keywords-AsynchronousIo`.jvm - -lazy val `keywords-Shift` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl) -lazy val `keywords-ShiftJS` = `keywords-Shift`.js -lazy val `keywords-ShiftJVM` = `keywords-Shift`.jvm - -lazy val `keywords-Using` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Shift`, `keywords-Catch`) -lazy val `keywords-UsingJS` = `keywords-Using`.js -lazy val `keywords-UsingJVM` = `keywords-Using`.jvm - -lazy val `keywords-Catch` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Shift`, `keywords-Yield` % Test) -lazy val `keywords-CatchJS` = `keywords-Catch`.js -lazy val `keywords-CatchJVM` = `keywords-Catch`.jvm - -lazy val `keywords-Map` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl) -lazy val `keywords-MapJS` = `keywords-Map`.js -lazy val `keywords-MapJVM` = `keywords-Map`.jvm - -lazy val `keywords-FlatMap` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl) -lazy val `keywords-FlatMapJS` = `keywords-FlatMap`.js -lazy val `keywords-FlatMapJVM` = `keywords-FlatMap`.jvm - -lazy val `keywords-WithFilter` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(`keywords-Continue`) -lazy val `keywords-WithFilterJS` = `keywords-WithFilter`.js -lazy val `keywords-WithFilterJVM` = `keywords-WithFilter`.jvm - -lazy val `keywords-NoneSafe` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Return`) -lazy val `keywords-NoneSafeJS` = `keywords-NoneSafe`.js -lazy val `keywords-NoneSafeJVM` = `keywords-NoneSafe`.jvm - -lazy val `keywords-NullSafe` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl) -lazy val `keywords-NullSafeJS` = `keywords-NullSafe`.js -lazy val `keywords-NullSafeJVM` = `keywords-NullSafe`.jvm - -lazy val `keywords-ForEach` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Each` % Test) -lazy val `keywords-ForEachJS` = `keywords-ForEach`.js -lazy val `keywords-ForEachJVM` = `keywords-ForEach`.jvm - -lazy val `keywords-Each` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Shift`) -lazy val `keywords-EachJS` = `keywords-Each`.js -lazy val `keywords-EachJVM` = `keywords-Each`.jvm - -lazy val `keywords-Await` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, - `domains-task` % Test, - `keywords-Catch` % Test, - `keywords-Get` % Test, - `keywords-Return` % Test, - `keywords-Yield` % Test, - ) -lazy val `keywords-AwaitJS` = `keywords-Await`.js -lazy val `keywords-AwaitJVM` = `keywords-Await`.jvm - -lazy val `keywords-Yield` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, `keywords-Shift` % Test, `keywords-Each` % Test, `keywords-Continue` % Test) -lazy val `keywords-YieldJS` = `keywords-Yield`.js -lazy val `keywords-YieldJVM` = `keywords-Yield`.jvm - -lazy val `keywords-Monadic` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .dependsOn(Dsl) -lazy val `keywords-MonadicJS` = `keywords-Monadic`.js -lazy val `keywords-MonadicJVM` = `keywords-Monadic`.jvm - -lazy val `domains-scalaz` = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, - `keywords-Catch`, - `keywords-Monadic`, - `keywords-Return`, - `keywords-Shift` % Test, - `keywords-Yield` % Test) -lazy val `domains-scalazJS` = `domains-scalaz`.js -lazy val `domains-scalazJVM` = `domains-scalaz`.jvm - lazy val `domains-cats` = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn(Dsl, - `keywords-Catch`, - `keywords-Monadic`, - `keywords-Return`, - `keywords-Shift` % Test, - `keywords-Yield` % Test) lazy val `domains-catsJVM` = `domains-cats`.jvm lazy val `domains-catsJS` = `domains-cats`.js -lazy val comprehension = - crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn( - `keywords-Map`, - `keywords-FlatMap`, - `keywords-WithFilter`, - `keywords-Return`, - `keywords-Each` % Test, - `keywords-Yield` % Test, - `keywords-Using` % Test, - `keywords-Continue` % Test, - ) -lazy val comprehensionJS = comprehension.js -lazy val comprehensionJVM = comprehension.jvm - -lazy val `package` = project - .settings( - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-BangNotation` in Compile).value}""", - scalacOptions += raw"""-Xplugin:${(packageBin in `compilerplugins-ResetEverywhere` in Compile).value}""" - ) - .dependsOn( - `domains-catsJVM`, - `domains-scalazJVM`, - `keywords-GetJVM`, - `keywords-PutJVM`, - `keywords-ContinueJVM`, - `keywords-ReturnJVM`, - `keywords-ShiftJVM`, - `keywords-ForEachJVM`, - `keywords-EachJVM`, - `keywords-YieldJVM`, - `keywords-ForkJVM`, - `keywords-NoneSafeJVM`, - `keywords-NullSafeJVM`, - `keywords-AwaitJVM`, - `keywords-AsynchronousIoJVM`, - `keywords-UsingJVM`, - `keywords-MapJVM`, - `keywords-FlatMapJVM`, - `keywords-WithFilterJVM`, - `comprehensionJVM`, - `domains-taskJVM`, - DslJVM - ) - organization in ThisBuild := "com.thoughtworks.dsl" scalacOptions in ThisBuild ++= { @@ -338,52 +18,6 @@ scalacOptions in ThisBuild ++= { } } -lazy val unidoc = - project - .enablePlugins(ScalaUnidocPlugin) - .settings( - publishArtifact := false, - unidocProjectFilter in ScalaUnidoc in BaseUnidocPlugin.autoImport.unidoc := { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - // Workaround for https://github.com/scala/bug/issues/11045 - ( - inDependencies(`package`) || - inDependencies(`compilerplugins-BangNotation`) || - inDependencies(`compilerplugins-ResetEverywhere`) - ) -- - inProjects(DslJVM, - `keywords-ContinueJVM`, - `keywords-YieldJVM`, - `domains-taskJVM`, - `keywords-EachJVM`, - `keywords-ForkJVM`) - } else { - inDependencies(`package`) || - inDependencies(`compilerplugins-BangNotation`) || - inDependencies(`compilerplugins-ResetEverywhere`) - } - }, - addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7"), - scalacOptions += "-Xexperimental", - scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } - }, - libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } - } - ) - skip in publish := true parallelExecution in Global := { diff --git a/compilerplugins-BangNotation/build.sbt b/compilerplugins-BangNotation/build.sbt deleted file mode 100644 index 13684a0d..00000000 --- a/compilerplugins-BangNotation/build.sbt +++ /dev/null @@ -1,11 +0,0 @@ -libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided - -scalacOptions ++= { - if (scalaBinaryVersion.value == "2.11") { - Seq("–Yexperimental") - } else { - Seq() - } -} - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/compilerplugins-BangNotation/src/main/resources/scalac-plugin.xml b/compilerplugins-BangNotation/src/main/resources/scalac-plugin.xml deleted file mode 100644 index d2126ac2..00000000 --- a/compilerplugins-BangNotation/src/main/resources/scalac-plugin.xml +++ /dev/null @@ -1,4 +0,0 @@ - - BangNotation - com.thoughtworks.dsl.compilerplugins.BangNotation - diff --git a/compilerplugins-BangNotation/src/main/scala/com/thoughtworks/dsl/compilerplugins/BangNotation.scala b/compilerplugins-BangNotation/src/main/scala/com/thoughtworks/dsl/compilerplugins/BangNotation.scala deleted file mode 100644 index 27012e4f..00000000 --- a/compilerplugins-BangNotation/src/main/scala/com/thoughtworks/dsl/compilerplugins/BangNotation.scala +++ /dev/null @@ -1,534 +0,0 @@ -package com.thoughtworks.dsl -package compilerplugins - -import com.thoughtworks.dsl.Dsl.{ResetAnnotation, nonTypeConstraintReset, shift} -import com.thoughtworks.dsl.compilerplugins.BangNotation.HasReturn - -import scala.annotation.tailrec -import scala.tools.nsc.plugins.{Plugin, PluginComponent} -import scala.tools.nsc.transform.Transform -import scala.tools.nsc.typechecker.ContextMode -import scala.tools.nsc.{Global, Mode, Phase} -private object BangNotation { - sealed trait HasReturn - object HasReturn { - case object Yes extends HasReturn - case object No extends HasReturn - } - -} - -/** The Scala compiler plug-in to convert ordinary Scala control flows to continuation-passing style, - * which will then be interpreted by [[Dsl]]. - * - * = Usage = - * - * `
-  * // In your build.sbt
-  * addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "latest.release")
-  * 
` - * - * @author 杨博 (Yang Bo) - */ -final class BangNotation(override val global: Global) extends Plugin { - import global._ - import global.analyzer._ - - private val hasScalaJsPlugin = - global.settings.plugin.value.exists(_.matches("""^.*scalajs-compiler_.*\.jar$""")) - - private var active = true - private def deactAnalyzerPlugins[A](run: => A): A = { - synchronized { - active = false - try { - run - } finally { - active = true - } - } - } - - private type CpsAttachment = (Tree => Tree) => Tree - - private trait Deactable extends AnalyzerPlugin { - override def isActive(): Boolean = { - active && phase.id < currentRun.picklerPhase.id - } - } - - /** An [[AnalyzerPlugin]] that replaces trees annatated as [[ResetAnnotation]] to its cps transformed trees */ - private trait TreeResetter extends AnalyzerPlugin with AnnotationSymbols { - override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = { - super.canAdaptAnnotations(tree, typer, mode, pt) || { - mode.inExprMode && tree.tpe.hasAnnotation(resetAnnotationSymbol) && tree.hasAttachment[CpsAttachment] - } - } - - /** Avoid [[UnApply]] in `tree` to suppress compiler crash due to `unexpected UnApply xxx`. - * - * @see https://github.com/scala/bug/issues/8825 - */ - private def scalaBug8825Workaround(tree: Tree): Tree = { - val transformer = new Transformer { - override def transform(tree: global.Tree): global.Tree = { - tree match { - case UnApply( - Apply(Select(prefix, termNames.unapply | termNames.unapplySeq), List(Ident(termNames.SELECTOR_DUMMY))), - args) => - pq"$prefix(..${transformTrees(args)})" - case _ => - super.transform(tree) - } - } - } - transformer.transform(tree) - } - - override def adaptAnnotations(tree0: Tree, typer: Typer, mode: Mode, pt: Type): Tree = { - val tree = super.adaptAnnotations(tree0, typer, mode, pt) - tree.tpe.annotations.collectFirst { - case annotation if annotation.matches(resetAnnotationSymbol) => - val Some(attachment) = tree.attachments.get[CpsAttachment] - val cpsTree = scalaBug8825Workaround(resetAttrs(attachment(identity))) -// reporter.info(tree.pos, s"Translating to continuation-passing style: $cpsTree", true) - deactAnalyzerPlugins { - typer.context.withMode(ContextMode.ReTyping) { - typer.typed(cpsTree, Mode.EXPRmode) - } - } - } match { - case Some(typedCpsTree) => -// reporter.info(tree.pos, s"Translating to continuation-passing style: $typedCpsTree", true) - typedCpsTree.modifyType(_.filterAnnotations(!_.matches(resetAnnotationSymbol))) - case None => - tree - } - - } - - } - - private trait BangNotationTransformer extends AnalyzerPlugin with AnnotationSymbols { - - private def cpsAttachment(tree: Tree)(continue: Tree => Tree): Tree = { - tree.attachments.get[CpsAttachment] match { - case Some(attachment) => attachment(continue) - case None => continue(tree) - } - } - private def cpsParameter(parameters: List[Tree])(continue: List[Tree] => Tree): Tree = { - parameters match { - case Nil => - continue(Nil) - case head :: tail => - cpsAttachment(head) { headValue => - cpsParameter(tail) { tailValues => - continue(headValue :: tailValues) - } - } - } - } - private def cpsParameterList(parameterLists: List[List[Tree]])(continue: List[List[Tree]] => Tree): Tree = { - parameterLists match { - case Nil => - continue(Nil) - case headList :: tailList => - cpsParameter(headList) { headValue => - cpsParameterList(tailList) { tailValues => - continue(headValue :: tailValues) - } - } - } - } - - private def isCpsTree(tree: Tree) = { - def hasCpsAttachment(child: Any): Boolean = { - child match { - case list: List[_] => list.exists(hasCpsAttachment) - case TypeApply(fun, args) => hasCpsAttachment(fun) - case Apply(fun, args) => hasCpsAttachment(fun) || args.exists(hasCpsAttachment) - case CaseDef(pat, guard, body) => hasCpsAttachment(body) - case ValDef(mods, name, tpt, rhs) => hasCpsAttachment(rhs) - case childTree: Tree => childTree.hasAttachment[CpsAttachment] - case _ => false - } - } - tree.productIterator.exists(hasCpsAttachment) - } - - private lazy val catchIdent: Tree = { - try { - Ident(rootMirror.staticModule("_root_.com.thoughtworks.dsl.keywords.Catch")) - } catch { - case e: ScalaReflectionException => - abort("""The BangNotation compiler plug-in requires the runtime library `keywords-catch` to enable !-notation in `try` / `catch` / `finally` expressions: - libraryDependencies += "com.thoughtworks.dsl" %% "keywords-catch" % "latest.release" -""") - } - } - - private val whileName = currentUnit.freshTermName("while") - private val whileDef = { - val domainName = currentUnit.freshTypeName("Domain") - val conditionName = currentUnit.freshTermName("condition") - val conditionValueName = currentUnit.freshTermName("conditionValue") - val bodyName = currentUnit.freshTermName("body") - val endWhileName = currentUnit.freshTermName("endWhile") - q""" - @${definitions.ScalaInlineClass} def $whileName[$domainName]($endWhileName: => $domainName)( - $bodyName: (=> $domainName) => $domainName, - $conditionName: (_root_.scala.Boolean => $domainName) => $domainName): $domainName = { - $conditionName { $conditionValueName: ${TypeTree()} => - if ($conditionValueName) { - $bodyName { - $whileName[$domainName]($endWhileName)($bodyName, $conditionName) - } - } else { - $endWhileName - } - } - } - """ - } - private val doWhileName = currentUnit.freshTermName("doWhile") - - private val doWhileDef = { - val domainName = currentUnit.freshTypeName("Domain") - val conditionName = currentUnit.freshTermName("condition") - val conditionValueName = currentUnit.freshTermName("conditionValue") - val bodyName = currentUnit.freshTermName("body") - val endDoWhileName = currentUnit.freshTermName("endDoWhile") - q""" - @${definitions.ScalaInlineClass} def $doWhileName[$domainName]($endDoWhileName: => $domainName)( - $bodyName: (=> $domainName) => $domainName, - $conditionName: (_root_.scala.Boolean => $domainName) => $domainName): $domainName = { - $bodyName { - $conditionName { $conditionValueName: ${TypeTree()} => - if ($conditionValueName) { - $doWhileName[$domainName]($endDoWhileName)($bodyName, $conditionName) - } else { - $endDoWhileName - } - } - } - } - """ - } - private def notPure(head: Tree): List[Tree] = { - head match { - case (_: Ident) | (_: Literal) => - Nil - case _ => - head :: Nil - } - } - - private def toFunction1(continue: Tree => Tree, parameterTypes: Type) = { - val parameterName = currentUnit.freshTermName("a") - val id = q"$parameterName" - continue(id) match { - case q"${f: Ident}.apply(${`id`})" => - f - case transformed => - q""" - { ($parameterName: $parameterTypes) => - ${transformed} - } - """ - } - } - - private def hasReturnTree(tree: Tree): Boolean = { - tree.attachments.get[HasReturn] match { - case Some(HasReturn.Yes) => - true - case Some(HasReturn.No) => - false - - case _ => - tree match { - case _: Return => - true - case valDef: ValDef => - hasReturnTree(valDef.rhs) - case _: MemberDef => - false - case _ => - tree.children.exists(hasReturnTree) - } - } - - } - - override def pluginsTyped(tpe0: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = { - val tpe = super.pluginsTyped(tpe0, typer, tree, mode, pt) - - def cps(continue: Tree => Tree): Tree = atPos(tree.pos) { - tree match { - case q"$prefix.$methodName[..$typeParameters](...$parameterLists)" => - cpsAttachment(prefix) { prefixValue => - cpsParameterList(parameterLists) { parameterListsValues => - continue(atPos(tree.pos) { - q"$prefixValue.$methodName[..$typeParameters](...$parameterListsValues)" - }) - } - } - case q"${method: Ident}[..$typeParameters](...$parameterLists)" => - cpsParameterList(parameterLists) { parameterListsValues => - continue(atPos(tree.pos) { - q"$method[..$typeParameters](...$parameterListsValues)" - }) - } - case q"new $constructor[..$typeParameters](...$parameterLists)" => - cpsParameterList(parameterLists) { parameterListsValues => - continue(atPos(tree.pos) { - q"new $constructor[..$typeParameters](...$parameterListsValues)" - }) - } - case Typed(expr, tpt) => - cpsAttachment(expr) { exprValue => - atPos(tree.pos) { - if (tpt.tpe.hasAnnotation(resetAnnotationSymbol)) { - continue(exprValue) - } else { - tpt match { - case tpt: TypeTree => - // Create a new TypeTree with a null original, as a workaround of https://github.com/ThoughtWorksInc/Dsl.scala/issues/114 - val noOriginal = TypeTree(tpt.tpe).setPos(tpt.pos) - continue(Typed(exprValue, noOriginal)) - case _ => - continue(Typed(exprValue, tpt)) - } - } - } - } - case Block(stats, expr) => - def loop(stats: List[Tree]): Tree = { - stats match { - case Nil => - cpsAttachment(expr) { - case block: Block => - continue(block) - case notBlock => - continue(treeCopy.Block(tree, Nil, notBlock)) - } - case (valDef @ ValDef(mods, name, tpt, rhs)) :: tail => - // TODO: lazy val - cpsAttachment(rhs) { rhsValue => - atPos(valDef.pos) { - q"${treeCopy.ValDef(valDef, mods, name, tpt, rhsValue)}; ${loop(tail)}" - } - } - case head :: tail => - cpsAttachment(head) { headValue => - q"..${notPure(headValue)}; ${loop(tail)}" - } - } - } - loop(stats) - case If(cond, thenp, elsep) => - val endIfName = currentUnit.freshTermName("endIf") - - q""" - ${ - val endIfBody = toFunction1(continue, tpe) - if (hasReturnTree(endIfBody)) { - q""" - val $endIfName = $endIfBody - """.updateAttachment(HasReturn.Yes) - } else { - q""" - @${definitions.ScalaInlineClass} def $endIfName = $endIfBody - """.updateAttachment(HasReturn.No) - } - } - - ${cpsAttachment(cond) { condValue => - atPos(tree.pos) { - q""" - if ($condValue) ${cpsAttachment(thenp) { result => - q"$endIfName.apply($result)" - }} else ${cpsAttachment(elsep) { result => - q"$endIfName.apply($result)" - }} - """ - } - }} - """ - case Match(selector, cases) => - val endMatchName = currentUnit.freshTermName("endMatch") - q""" - ${ - val endMatchBody = toFunction1(continue, tpe) - if (hasReturnTree(endMatchBody)) { - q""" - val $endMatchName = $endMatchBody - """.updateAttachment(HasReturn.Yes) - } else { - q""" - @${definitions.ScalaInlineClass} def $endMatchName = $endMatchBody - """.updateAttachment(HasReturn.No) - } - } - - ${cpsAttachment(selector) { selectorValue => - atPos(tree.pos) { - Match( - selectorValue, - cases.map { - case caseDef @ CaseDef(pat, guard, body) => - treeCopy.CaseDef(caseDef, pat, guard, cpsAttachment(body) { bodyValue => - q"$endMatchName.apply($bodyValue)" - }) - } - ) - } - }} - """ - case _: CaseDef => - // This CaseDef tree contains some bang notations, and will be translated by enclosing Try or Match tree, not here - EmptyTree - case Try(block, catches, finalizer) => - val finalizerName = currentUnit.freshTermName("finalizer") - val resultName = currentUnit.freshTermName("result") - - q""" - $catchIdent.tryCatch { ($resultName: $tpe) => ${{ - cpsAttachment(finalizer) { finalizerValue => - q""" - ..${notPure(finalizerValue)} - ${continue(q"$resultName")} - """ - } - }}}.apply( - { $finalizerName: ${TypeTree()} => ${cpsAttachment(block) { blockValue => - q"$finalizerName.apply($blockValue)" - }}}, - { - case ..${catches.map { caseDef => - atPos(caseDef.pos) { - treeCopy.CaseDef( - caseDef, - caseDef.pat, - caseDef.guard, - q"""{ $finalizerName: ${TypeTree()} => ${{ - cpsAttachment(caseDef.body) { bodyValue => - q"$finalizerName.apply($bodyValue)" - } - }}}""" - ) - } - }}} - ) - """ - case Assign(lhs, rhs) => - cpsAttachment(rhs) { rhsValue => - continue(treeCopy.Assign(tree, lhs, rhsValue)) - } - case q"while($condition) $body" => - // TODO: Trampoline - val continueName = currentUnit.freshTermName("continue") - val conditionHandlerName = currentUnit.freshTermName("conditionHandler") - q""" - $whileDef - $whileName({ - ${continue(q"()")} - })({ $continueName: ${TypeTree()} => ${cpsAttachment(body) { bodyValue => - q""" - ..${notPure(bodyValue)} - $continueName - """ - }}}, - { $conditionHandlerName: ${TypeTree()} => ${cpsAttachment(condition) { conditionValue => - q"$conditionHandlerName.apply($conditionValue)" - }}}) - """ - case q"do $body while($condition)" => - // TODO: Trampoline - val continueName = currentUnit.freshTermName("continue") - val conditionHandlerName = currentUnit.freshTermName("conditionHandler") - q""" - $doWhileDef - - $doWhileName({ - ${continue(q"()")} - })({ $continueName: ${TypeTree()} => ${cpsAttachment(body) { bodyValue => - q""" - ..${notPure(bodyValue)} - $continueName - """ - }}}, - { $conditionHandlerName: ${TypeTree()} => ${cpsAttachment(condition) { conditionValue => - q"$conditionHandlerName.apply($conditionValue)" - }}}) - """ - case Throw(expr) => - cpsAttachment(expr) { exprValue => - continue(treeCopy.Throw(tree, exprValue)) - } - case Return(expr) => - cpsAttachment(expr) { exprValue => - continue(treeCopy.Return(tree, exprValue)) - } - case tree => - reporter.info(tree.pos, "CPS-transformation is skipped for this tree", force = false) - continue(tree) - } - } - - if (mode.inExprMode) { - val symbol = tree.symbol - tree match { - case q"$shiftOps.$shiftMethod" if symbol != null && symbol.hasAnnotation(shiftSymbol) => - def attachment: CpsAttachment = { continue: (Tree => Tree) => - // FIXME: tpe is a by-name type. I don't know why. - cpsAttachment(shiftOps) { shiftOpsValue => - atPos(tree.pos) { - q""" - $shiftOpsValue.cpsApply(${toFunction1(continue, tpe)}) - """ - } - } - } - tree.updateAttachment[CpsAttachment](attachment) - case _ => - if (isCpsTree(tree)) { - tree.updateAttachment[CpsAttachment](cps) - } - } - } - - tpe - } - - } - - private trait AnnotationSymbols { - private[BangNotation] lazy val resetAnnotationSymbol = symbolOf[ResetAnnotation] - private[BangNotation] lazy val shiftSymbol = symbolOf[shift] - } - - val name: String = "BangNotation" - - override def init(options: List[String], error: String => Unit): Boolean = { - super.init(options, error) && { - try { - global.analyzer.addAnalyzerPlugin(new Deactable with TreeResetter with BangNotationTransformer) - true - } catch { - case e: ScalaReflectionException => - error("""The BangNotation compiler plug-in requires the runtime library: - libraryDependencies += "com.thoughtworks.dsl" %% "dsl" % "latest.release" -""") - false - } - } - } - - val description: String = - "A compiler plugin that converts native imperative syntax to monadic expressions or continuation-passing style expressions" - - val components = Nil - -} diff --git a/compilerplugins-BangNotation/src/test/scala/com/thoughtworks/dsl/compilerplugin/BangNotationSpec.scala b/compilerplugins-BangNotation/src/test/scala/com/thoughtworks/dsl/compilerplugin/BangNotationSpec.scala deleted file mode 100644 index 41729a5f..00000000 --- a/compilerplugins-BangNotation/src/test/scala/com/thoughtworks/dsl/compilerplugin/BangNotationSpec.scala +++ /dev/null @@ -1,34 +0,0 @@ -package com.thoughtworks.dsl.compilerplugin - -import com.thoughtworks.dsl.Dsl.shift -import org.scalatest.{FreeSpec, Matchers} - -/** - * @author 杨博 (Yang Bo) - */ -class BangNotationSpec extends FreeSpec with Matchers { - - "printf problem" in { - - object IntPlaceholder { - @shift def unary_! : String = ??? - def cpsApply[Domain](f: String => Domain): Int => Domain = { i: Int => - f(i.toString) - } - } - - object StringPlaceholder { - @shift def unary_! : String = ??? - def cpsApply[Domain](f: String => Domain): String => Domain = f - } - - def f1 = "Hello World!" - def f2 = "Hello " + !StringPlaceholder + "!" - def f3 = "The value of " + !StringPlaceholder + " is " + !IntPlaceholder + "." - - f1 should be("Hello World!") - f2.asInstanceOf[String => String]("World") should be("Hello World!") - f3.asInstanceOf[String => Int => String]("x")(3) should be("The value of x is 3.") - } - -} diff --git a/compilerplugins-ResetEverywhere/build.sbt b/compilerplugins-ResetEverywhere/build.sbt deleted file mode 100644 index 02a6401f..00000000 --- a/compilerplugins-ResetEverywhere/build.sbt +++ /dev/null @@ -1,9 +0,0 @@ -libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided - -scalacOptions ++= { - if (scalaBinaryVersion.value == "2.11") { - Seq("–Yexperimental") - } else { - Seq() - } -} diff --git a/compilerplugins-ResetEverywhere/src/main/resources/scalac-plugin.xml b/compilerplugins-ResetEverywhere/src/main/resources/scalac-plugin.xml deleted file mode 100644 index 622eca89..00000000 --- a/compilerplugins-ResetEverywhere/src/main/resources/scalac-plugin.xml +++ /dev/null @@ -1,4 +0,0 @@ - - ResetEverywhere - com.thoughtworks.dsl.compilerplugins.ResetEverywhere - diff --git a/compilerplugins-ResetEverywhere/src/main/scala/com/thoughtworks/dsl/compilerplugins/ResetEverywhere.scala b/compilerplugins-ResetEverywhere/src/main/scala/com/thoughtworks/dsl/compilerplugins/ResetEverywhere.scala deleted file mode 100644 index 4b265aa8..00000000 --- a/compilerplugins-ResetEverywhere/src/main/scala/com/thoughtworks/dsl/compilerplugins/ResetEverywhere.scala +++ /dev/null @@ -1,154 +0,0 @@ -package com.thoughtworks.dsl.compilerplugins - -import com.thoughtworks.dsl.Dsl.{ResetAnnotation, nonTypeConstraintReset, shift} - -import scala.reflect.internal.FatalError -import scala.tools.nsc.plugins.{Plugin, PluginComponent} -import scala.tools.nsc.transform.Transform -import scala.tools.nsc.typechecker.ContextMode -import scala.tools.nsc.{Global, Mode, Phase} - -/** A Scala compiler plug-in to enable [[Dsl.Keyword#unary_$bang !-notation]] for every methods and functions. - * - * Add the following setting in your `build.sbt` to enable this plug-in. - * - * `
-  * // build.sbt
-  * addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-reseteverywhere" % "latest.release")
-  * 
` - * - * @note Once this [[ResetEverywhere]] plug-in is enabled, - * the `@[[Dsl.reset reset]]` annotations are added to class fields, every methods and every functions automatically. - * Some other macros or compiler plug-ins may conflict with those `@[[Dsl.reset reset]]` annotations. - * - * @author 杨博 (Yang Bo) - */ -final class ResetEverywhere(override val global: Global) extends Plugin { - import global._ - import global.analyzer._ - - private var active = true - private def deactAnalyzerPlugins[A](run: => A): A = { - synchronized { - active = false - try { - run - } finally { - active = true - } - } - } - - private type CpsAttachment = (Tree => Tree) => Tree - - private var nonTypeConstraintResetSymbol: Symbol = _ // = symbolOf[nonTypeConstraintReset] - - override def init(options: List[String], error: String => Unit): Boolean = { - super.init(options, error) && { - try { - nonTypeConstraintResetSymbol = symbolOf[nonTypeConstraintReset] - true - } catch { - case e: ScalaReflectionException => - error("""This compiler plug-in requires the runtime library: - libraryDependencies += "com.thoughtworks.dsl" %% "dsl" % "latest.release" -""") - false - } - } - } - val name: String = "ResetEverywhere" - - private final class ResetAnnotationCreator extends PluginComponent with Transform { - - val global: ResetEverywhere.this.global.type = ResetEverywhere.this.global - val phaseName: String = "ResetAnnotationCreator" - val runsAfter = "parser" :: Nil - override val runsBefore = "namer" :: Nil - - protected def newTransformer(unit: CompilationUnit): Transformer = new Transformer { - - private def annotatedReset(tree: Tree) = { - if (tree.isEmpty) { - tree - } else { - Annotated(q"new $nonTypeConstraintResetSymbol()", transform(tree)) - } - } - - private def typedReset(tree: Tree, typeTree: Tree) = { - if (tree.isEmpty) { - tree - } else if (typeTree.isEmpty) { - Annotated(q"new $nonTypeConstraintResetSymbol()", transform(tree)) - } else { - Annotated(q"new $nonTypeConstraintResetSymbol()", Typed(transform(tree), typeTree)) - } - } - - private def transformRootValDef(tree: ValDef) = { - val ValDef(mods, name, tpt, rhs) = tree - treeCopy.ValDef(tree, mods, name, tpt, typedReset(rhs, tpt)) - } - - override def transformTemplate(tree: Template): Template = { - val Template(parents, self, body) = tree - treeCopy.Template( - tree, - parents, - self, - body.mapConserve { - case valDef: ValDef if !valDef.mods.isParamAccessor => - transformRootValDef(valDef) - case initializer: TermTree => - annotatedReset(initializer) - case stat => - transform(stat) - } - ) - } - - private def annotateArgsAsReset(tree: Tree): Tree = { - tree match { - case tree: Apply => - treeCopy.Apply(tree, annotateArgsAsReset(tree.fun), tree.args.mapConserve(annotatedReset)) - case fun => - fun - } - } - - override def transform(tree: global.Tree): global.Tree = { - tree match { - case tree: TypeTree => - tree - case Typed(expr, tpt) => - treeCopy.Typed(tree, transform(expr), tpt) - case Function(vparams, body) => - treeCopy.Function(tree, vparams, annotatedReset(body)) - case DefDef(mods, name, tparams, vparamss, tpt, rhs) - if name != termNames.CONSTRUCTOR && name != termNames.MIXIN_CONSTRUCTOR && rhs.nonEmpty && !mods - .hasAnnotationNamed(definitions.TailrecClass.name) => - treeCopy.DefDef(tree, mods, name, tparams, transformValDefss(vparamss), tpt, typedReset(rhs, tpt)) - case valDef: ValDef if valDef.mods.hasDefault => - transformRootValDef(valDef) - case Match(EmptyTree, cases) => - treeCopy.Match(tree, EmptyTree, cases.mapConserve { - case caseDef @ CaseDef(pat, guard, body) => - treeCopy.CaseDef(caseDef, pat, guard, annotatedReset(body)) - }) - case q"${Ident(termNames.CONSTRUCTOR)}(...$argss)" => - annotateArgsAsReset(tree) - case q"super.${termNames.CONSTRUCTOR}(...$argss)" => - annotateArgsAsReset(tree) - case _ => - super.transform(tree) - } - } - } - } - - val description: String = "Add @reset annotation for every methods and functions automatically" - - val components: List[PluginComponent] = List(new ResetAnnotationCreator) - -} diff --git a/comprehension/.js/build.sbt b/comprehension/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/comprehension/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/comprehension/.jvm/build.sbt b/comprehension/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/comprehension/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/comprehension/build.sbt b/comprehension/build.sbt deleted file mode 100644 index ff892bd9..00000000 --- a/comprehension/build.sbt +++ /dev/null @@ -1,18 +0,0 @@ -enablePlugins(Example) - -import meta._ -exampleSuperTypes := exampleSuperTypes.value.map { - case ctor"_root_.org.scalatest.FreeSpec" => - ctor"_root_.org.scalatest.AsyncFreeSpec" - case otherTrait => - otherTrait -} - -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Some("–Xexperimental") - case _ => None - } -} - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/comprehension/src/main/scala/com/thoughtworks/dsl/comprehension.scala b/comprehension/src/main/scala/com/thoughtworks/dsl/comprehension.scala deleted file mode 100644 index b0c021a7..00000000 --- a/comprehension/src/main/scala/com/thoughtworks/dsl/comprehension.scala +++ /dev/null @@ -1,158 +0,0 @@ -package com.thoughtworks.dsl - -import scala.language.higherKinds -import scala.language.implicitConversions -import com.thoughtworks.dsl.keywords._ - -private[dsl] sealed trait LowPriorityComprehension0 { - import com.thoughtworks.dsl.comprehension._ - - implicit def comprehensionOps[From, Keyword, Value](from: From)( - implicit asKeyword: From => Dsl.Keyword[Keyword, Value] with Keyword): ComprehensionOps[Keyword, Value] = - new ComprehensionOps[Keyword, Value](from) -} - -/** Provides utilities to perform `for`-comprehension on [[Dsl.Keyword]]. - * - * - * Add the following import statement to enable `for`-comprehension on keywords: - * - * {{{ - * import com.thoughtworks.dsl.comprehension._ - * }}} - * - * @example `for` / `yield` expressions can be used on keywords. - * - * {{{ - * import com.thoughtworks.dsl.keywords._ - * - * def cartesianProduct = for { - * i <- Each(Array(1, 2, 3)) - * j <- Each(Vector(1, 10, 100, 1000)) - * } yield i * j - * }}} - * - * The results of `for` / `yield` expressions are also keywords. - * - * {{{ - * cartesianProduct should be(a[Dsl.Keyword[_, _]]) - * }}} - * - * You can use !-notation extract the value from the produced keyword. - * - * {{{ - * def resultAsList = List(!cartesianProduct) - * resultAsList should be(List(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000)) - * - * def resultAsSet: Set[Int] = !Return(!cartesianProduct) - * resultAsSet should be(Set(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000)) - * }}} - * - * Alternatively, [[comprehension.ComprehensionOps.to]] can be used to convert the result of a keyword to other types of values as well. - * - * {{{ - * cartesianProduct.to[List] should be(List(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000)) - * }}} - * @example This example implements the same feature as the example on Scaladoc of [[keywords.Yield]], - * except this example use `for`-comprehension instead of !-notation. - * - * {{{ - * import com.thoughtworks.dsl.Dsl - * import com.thoughtworks.dsl.keywords._ - * import com.thoughtworks.dsl.comprehension._ - * - * def gccFlagBuilder(sourceFile: String, includes: String*) = { - * for { - * _ <- Yield("gcc") - * _ <- Yield("-c") - * _ <- Yield(sourceFile) - * include <- Each(includes) - * _ <- Yield("-I") - * _ <- Yield(include) - * r <- Continue - * } yield r: String - * } - * - * gccFlagBuilder("main.c", "lib1/include", "lib2/include").to[Stream] should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include")) - * }}} - * - * Alternatively, you can use Scala native `yield` keyword to produce the last value. - * - * {{{ - * def gccFlagBuilder2(sourceFile: String, includes: String*) = { - * for { - * _ <- Yield("gcc") - * _ <- Yield("-c") - * _ <- Yield(sourceFile) - * include <- Each(includes) - * _ <- Yield("-I") - * } yield include - * } - * gccFlagBuilder2("main.c", "lib1/include", "lib2/include").to[Stream] should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include")) - * }}} - * - * You can also omit the explicit constructor of [[keywords.Yield]] with the help of implicit conversion [[keywords.Yield.implicitYield]]. - * - * {{{ - * import com.thoughtworks.dsl.keywords.Yield.implicitYield - * - * def augmentString = () - * def wrapString = () - * }}} - * - * Note that [[scala.Predef.augmentString]] and [[scala.Predef.wrapString]] must be disabled in order to use `flatMap` for [[keywords.Yield]]. - * - * {{{ - * def gccFlagBuilder3(sourceFile: String, includes: String*) = { - * for { - * _ <- "gcc" - * _ <- "-c" - * _ <- sourceFile - * include <- Each(includes) - * _ <- "-I" - * } yield include - * } - * gccFlagBuilder3("main.c", "lib1/include", "lib2/include").to[Stream] should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include")) - * }}} - * - */ -object comprehension extends LowPriorityComprehension0 { - - @inline - private def resetDomain[Keyword, Domain](keyword: Keyword)(implicit dsl: Dsl[Keyword, Domain, Domain]): Domain = { - dsl.cpsApply(keyword, implicitly) - } - - final class ComprehensionOps[Keyword, Value] private[dsl] (private val keyword: Keyword) extends AnyVal { - - import keywords._ - - def map[MapResult](mapper: Value => MapResult): Map[Keyword, Value, MapResult] = Map(keyword, mapper) - - def flatMap[MapResult, NestedKeyword, NestedValue](mapper: Value => MapResult)( - implicit asKeywordMapper: ( - Value => MapResult) => Value => Dsl.Keyword[NestedKeyword, NestedValue] with NestedKeyword) - : FlatMap[Keyword, Value, NestedKeyword, NestedValue] = FlatMap(keyword, mapper) - - def withFilter(condition: Value => Boolean): WithFilter[Keyword, Value] = WithFilter(keyword, condition) - - def to[Output[_]](implicit dsl: Dsl[Keyword, Output[Value], Value], - returnDsl: Dsl[Return[Value], Output[Value], Nothing]): Output[Value] = { - dsl.cpsApply(keyword, { value: Value => - resetDomain(Return(value)) - }) - } - def as[Domain](implicit dsl: Dsl[Keyword, Domain, Value], - returnDsl: Dsl[Return[Value], Domain, Nothing]): Domain = { - dsl.cpsApply(keyword, { value: Value => - resetDomain(Return(value)) - }) - } - - } - - implicit def nothingComprehensionOps[From, Keyword](from: From)( - implicit asKeyword: From => Dsl.Keyword[Keyword, Nothing] with Keyword): ComprehensionOps[Keyword, Nothing] = - new ComprehensionOps[Keyword, Nothing](from) - -} diff --git a/domains-cats/build.sbt b/domains-cats/build.sbt index b2856764..f5760155 100644 --- a/domains-cats/build.sbt +++ b/domains-cats/build.sbt @@ -6,6 +6,21 @@ libraryDependencies += "org.typelevel" %%% "cats-core" % "1.4.0" libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test +libraryDependencies += "com.thoughtworks.dsl" %%% "keywords-catch" % "1.3.1" + +libraryDependencies += "com.thoughtworks.dsl" %%% "keywords-monadic" % "1.3.1" + +libraryDependencies += "com.thoughtworks.dsl" %%% "keywords-return" % "1.3.1" + +libraryDependencies += "com.thoughtworks.dsl" %%% "keywords-shift" % "1.3.1" % Optional + +libraryDependencies += "com.thoughtworks.dsl" %%% "keywords-yield" % "1.3.1" % Optional + +addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "1.3.1") + +addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-reseteverywhere" % "1.3.1") + + scalacOptions ++= { import Ordering.Implicits._ if (VersionNumber(scalaVersion.value).numbers < Seq(2L, 12L)) { diff --git a/domains-scalaz/.js/build.sbt b/domains-scalaz/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/domains-scalaz/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/domains-scalaz/.jvm/build.sbt b/domains-scalaz/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/domains-scalaz/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/domains-scalaz/.jvm/jvm.sbt b/domains-scalaz/.jvm/jvm.sbt deleted file mode 100644 index 2e36c246..00000000 --- a/domains-scalaz/.jvm/jvm.sbt +++ /dev/null @@ -1,23 +0,0 @@ -enablePlugins(Example) - - -sourceGenerators in Test := { - (sourceGenerators in Test).value.filterNot { sourceGenerator => - import Ordering.Implicits._ - VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L) && - sourceGenerator.info - .get(taskDefinitionKey) - .exists { scopedKey: ScopedKey[_] => - scopedKey.key == generateExample.key - } - } -} - -import scala.meta._ -exampleSuperTypes += ctor"_root_.org.scalatest.Inside" - -libraryDependencies += "org.scalaz" %% "scalaz-concurrent" % "7.2.26" % Test - -libraryDependencies += "com.thoughtworks.tryt" %% "invariant" % "2.1.0" % Test - -libraryDependencies += "com.thoughtworks.tryt" %% "invariant" % "2.1.0" % Optional // For scaladoc diff --git a/domains-scalaz/.jvm/src/test/scala/com/thoughtworks/dsl/domains/scalazSpec.scala b/domains-scalaz/.jvm/src/test/scala/com/thoughtworks/dsl/domains/scalazSpec.scala deleted file mode 100644 index 5b157e89..00000000 --- a/domains-scalaz/.jvm/src/test/scala/com/thoughtworks/dsl/domains/scalazSpec.scala +++ /dev/null @@ -1,114 +0,0 @@ -package com.thoughtworks.dsl.domains - -import com.thoughtworks.dsl.Dsl.!! -import org.scalatest.{FreeSpec, Matchers} -import _root_.scalaz.OptionT -import _root_.scalaz.concurrent.Task -import _root_.scalaz.std.stream._ -import com.thoughtworks.dsl.domains.scalaz._ -import com.thoughtworks.dsl.keywords.{Monadic, Shift, Yield} - -/** - * @author 杨博 (Yang Bo) - */ -class scalazSpec extends FreeSpec with Matchers { - - "MonadError" in { - def task: Task[Int] = Task.now { - import com.thoughtworks.dsl.keywords.Monadic._ - try { - 0 / 0 - } catch { - case e: ArithmeticException => - 42 - } finally { - !Task.now(()) - } - } - task.unsafePerformSync should be(42) - } - - "Given a continuation that uses Yield and Monadic expressions" - { - - def asyncFunction: Stream[String] !! Unit = _ { - !Yield("Entering asyncFunction") - val subThreadId = !Monadic(Stream(0, 1)) - !Yield(s"Fork sub-thread $subThreadId") - !Yield("Leaving asyncFunction") - } - - "When create a generator that contains Yield, Shift, and Monadic expressions" - { - - def generator: Stream[String] = { - !Yield("Entering generator") - val threadId = !Monadic(Stream(0, 1)) - !Yield(s"Fork thread $threadId") - !Shift(asyncFunction) - Stream("Leaving generator") - } - - "Then the generator should contains yield values" in { - generator should be( - Seq( - /**/ "Entering generator", - /****/ "Fork thread 0", - /******/ "Entering asyncFunction", - /********/ "Fork sub-thread 0", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator", - /********/ "Fork sub-thread 1", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator", - /****/ "Fork thread 1", - /******/ "Entering asyncFunction", - /********/ "Fork sub-thread 0", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator", - /********/ "Fork sub-thread 1", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator" - )) - } - - } - - } - - "Given a monadic expression that contains a Scalaz OptionT" - { - def myOptionalList: OptionT[Stream, String] = { - // TODO: Is it possible to have `Yield` expressions here? - val threadId = !Monadic(Stream(0, 1, 2)) - val subThreadId = !Monadic(OptionT(Stream(Some(10), None, Some(30)))) - val subSubThreadId = !Monadic(OptionT(Stream(Some(100), Some(200), None))) - OptionT[Stream, String](Stream(Some(s"Fork thread $threadId-$subThreadId-$subSubThreadId"))) - } - - "Then it should skips those elements that contains a None" in { - myOptionalList.run should be( - Seq( - Some("Fork thread 0-10-100"), - Some("Fork thread 0-10-200"), - None, - None, - Some("Fork thread 0-30-100"), - Some("Fork thread 0-30-200"), - None, - Some("Fork thread 1-10-100"), - Some("Fork thread 1-10-200"), - None, - None, - Some("Fork thread 1-30-100"), - Some("Fork thread 1-30-200"), - None, - Some("Fork thread 2-10-100"), - Some("Fork thread 2-10-200"), - None, - None, - Some("Fork thread 2-30-100"), - Some("Fork thread 2-30-200"), - None - )) - } - - } -} diff --git a/domains-scalaz/build.sbt b/domains-scalaz/build.sbt deleted file mode 100644 index 4eb5cae7..00000000 --- a/domains-scalaz/build.sbt +++ /dev/null @@ -1,12 +0,0 @@ -libraryDependencies += "org.scalaz" %%% "scalaz-core" % "7.2.26" - -addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7") - -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Seq("–Xexperimental") - case _ => Seq.empty - } -} - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/domains-scalaz/src/main/scala/com/thoughtworks/dsl/domains/scalaz.scala b/domains-scalaz/src/main/scala/com/thoughtworks/dsl/domains/scalaz.scala deleted file mode 100644 index 44945b63..00000000 --- a/domains-scalaz/src/main/scala/com/thoughtworks/dsl/domains/scalaz.scala +++ /dev/null @@ -1,193 +0,0 @@ -package com.thoughtworks.dsl.domains - -import com.thoughtworks.Extractor._ -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Keyword} - -import scala.language.higherKinds -import scala.language.implicitConversions -import _root_.scalaz.{Applicative, Bind, Monad, MonadError, MonadTrans, Unapply} -import com.thoughtworks.dsl.keywords.Catch.CatchDsl -import com.thoughtworks.dsl.keywords.{Catch, Monadic, Return} - -import scala.util.control.Exception.Catcher -import scala.util.control.{ControlThrowable, NonFatal} - -/** Contains interpreters to enable [[Dsl.Keyword#unary_$bang !-notation]] - * for [[keywords.Monadic Monadic]] and other keywords - * in code blocks whose type support [[scalaz.Bind]], [[scalaz.MonadError]] and [[scalaz.MonadTrans]]. - * - * @example [[scalaz.Free.Trampoline]] is a monadic data type that performs tail call optimization. - * It can be built from a `@[[Dsl.reset reset]]` code block within some [[Dsl.Keyword#unary_$bang !-notation]], - * similar to the [[com.thoughtworks.each.Monadic.EachOps#each each]] method in - * [[https://github.com/ThoughtWorksInc/each ThoughtWorks Each]]. - * - * {{{ - * import _root_.scalaz.Trampoline - * import _root_.scalaz.Free.Trampoline - * import com.thoughtworks.dsl.keywords.Monadic._ - * import com.thoughtworks.dsl.domains.scalaz._ - * import com.thoughtworks.dsl.Dsl.reset - * - * val trampoline3 = Trampoline.done(3) - * - * def dslSquare = Trampoline.delay { - * s"This string is produced by a trampoline: ${!trampoline3 * !trampoline3}" - * }: @reset - * - * dslSquare.run should be("This string is produced by a trampoline: 9") - * }}} - * - * `!trampoline3` is a shortcut of `!Monadic(trampoline3)`, - * which will be converted to `flatMap` calls by our DSL interpreter. - * Thus, the method `dslSquare` is equivalent to the following code in [[scalaz.syntax]]: - * - * {{{ - * - * def scalazSyntaxSquare = trampoline3.flatMap { tmp1 => - * trampoline3.flatMap { tmp2 => - * Trampoline.delay { - * s"This string is produced by a trampoline: ${tmp1 * tmp2}" - * } - * } - * } - * - * scalazSyntaxSquare.run should be("This string is produced by a trampoline: 9") - * }}} - * - *
- * - * A `@[[Dsl.reset reset]]` code block can contain `try` / `catch` / `finally` - * if the monadic data type supports [[scalaz.MonadError]]. - * - * [[https://github.com/ThoughtWorksInc/tryt.scala tryt.scala]] is a monad transformer that provides - * [[scalaz.MonadError]], - * therefore `try` / `catch` / `finally` expressions can be used inside a `@[[Dsl.reset reset]]` code block - * whose return type is `TryT[Trampoline, ?]`. - * - * {{{ - * import com.thoughtworks.tryt.invariant.TryT, TryT._ - * import scala.util.{Try, Success} - * type TryTTransfomredTrampoline[A] = TryT[Trampoline, A] - * - * val trampolineSuccess0: TryTTransfomredTrampoline[Int] = TryT(Trampoline.done(Try(0))) - * - * def dslTryCatch: TryTTransfomredTrampoline[String] = TryT(Trampoline.delay(Try { - * try { - * s"Division result: ${!trampoline3 / !trampolineSuccess0}" - * } catch { - * case e: ArithmeticException => - * s"Cannot divide ${!trampoline3} by ${!trampolineSuccess0}" - * } - * })): @reset - * - * inside(dslTryCatch) { - * case TryT(trampoline) => - * trampoline.run should be(Success("Cannot divide 3 by 0")) - * } - * }}} - * - * Note that [[Dsl.Keyword#unary_$bang !-notation]] can be used on - * both `trampoline3` and `trampolineSuccess0` even when they are different types, - * i.e. `trampoline3` is a vanilla [[scalaz.Free.Trampoline Trampoline]], - * while `trampolineSuccess0` is a [[com.thoughtworks.tryt.invariant.TryT TryT]]-transfomred - * [[scalaz.Free.Trampoline Trampoline]]. - * It is possible because the interpreters of the [[keywords.Monadic]] invoke - * [[scalaz.MonadTrans.liftM]] automatically. - * - * The above `dslTryCatch` method is equivalent to the following code in [[scalaz.syntax]]: - * - * {{{ - * def scalazSyntaxTryCatch: TryTTransfomredTrampoline[String] = { - * import _root_.scalaz.syntax.monadError._ - * trampoline3.liftM[TryT].flatMap { tmp0 => - * trampolineSuccess0.flatMap { tmp1 => - * TryT(Trampoline.delay(Try(s"Division result: ${tmp0 / tmp1}"))) - * } - * }.handleError { - * case e: ArithmeticException => - * trampoline3.liftM[TryT].flatMap { tmp2 => - * trampolineSuccess0.flatMap { tmp3 => - * TryT(Trampoline.delay(Try(s"Cannot divide ${tmp2} by ${tmp3}"))) - * } - * } - * case e => - * e.raiseError[TryTTransfomredTrampoline, String] - * } - * } - * - * inside(scalazSyntaxTryCatch) { - * case TryT(trampoline) => - * trampoline.run should be(Success("Cannot divide 3 by 0")) - * } - * }}} - * - * @author 杨博 (Yang Bo) - */ -object scalaz { - - protected type MonadThrowable[F[_]] = MonadError[F, Throwable] - - implicit def scalazCatchDsl[F[_], A, B](implicit monadError: MonadThrowable[F]): CatchDsl[F[A], F[B], A] = - new CatchDsl[F[A], F[B], A] { - def tryCatch(block: F[A] !! A, catcher: Catcher[F[A] !! A], handler: A => F[B]): F[B] = { - import _root_.scalaz.syntax.all._ - val fa = monadError.bind(monadError.pure(block))(_(monadError.pure(_))) - val protectedFa = monadError.handleError(fa) { - case catcher.extract(recovered) => - recovered(monadError.pure(_)) - case e => - monadError.raiseError[A](e) - } - monadError.bind(protectedFa)(handler) - } - } - - implicit def scalazReturnDsl[F[_], A, B](implicit applicative: Applicative[F], - restReturnDsl: Dsl[Return[A], B, Nothing]) = - new Dsl[Return[A], F[B], Nothing] { - def cpsApply(keyword: Return[A], handler: Nothing => F[B]): F[B] = { - applicative.pure(restReturnDsl.cpsApply(keyword, identity)) - } - } - - implicit def scalazMonadTransformerDsl1[F[_[_], _], H[_], G[_], A, B]( - implicit monadTrans: MonadTrans[F], - rest: ScalazTransformerDsl[H, G, A, B]): ScalazTransformerDsl[H, F[G, ?], A, B] = - new ScalazTransformerDsl[H, F[G, ?], A, B] { - - def monad: Monad[F[G, ?]] = monadTrans(rest.monad) - - def lift(fa: H[A]): F[G, A] = monadTrans.liftM(rest.lift(fa))(rest.monad) - - } - - implicit def scalazMonadTransformerDsl0[F[_[_], _], G[_], A, B]( - implicit monadTrans: MonadTrans[F], - monad0: Monad[G]): ScalazTransformerDsl[G, F[G, ?], A, B] = - new ScalazTransformerDsl[G, F[G, ?], A, B] { - def monad = monadTrans(monad0) - - def lift(fa: G[A]): F[G, A] = monadTrans.liftM(fa) - - } - - implicit def scalazMonadicDsl[F[_], A, B](implicit bind: Bind[F]): Dsl[Monadic[F, A], F[B], A] = - new Dsl[Monadic[F, A], F[B], A] { - def cpsApply(keyword: Monadic[F, A], handler: A => F[B]): F[B] = { - bind.bind(keyword.fa)(handler) - } - } - - abstract class ScalazTransformerDsl[F[_], G[_], A, B] extends Dsl[Monadic[F, A], G[B], A] { - def monad: Monad[G] - - def lift(fa: F[A]): G[A] - - final def cpsApply(keyword: Monadic[F, A], handler: A => G[B]): G[B] = { - monad.bind(lift(keyword.fa))(handler) - } - - } - -} diff --git a/domains-task/.js/build.sbt b/domains-task/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/domains-task/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/domains-task/.jvm/build.sbt b/domains-task/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/domains-task/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/domains-task/.jvm/jvm.sbt b/domains-task/.jvm/jvm.sbt deleted file mode 100644 index 155e6c6c..00000000 --- a/domains-task/.jvm/jvm.sbt +++ /dev/null @@ -1,9 +0,0 @@ -enablePlugins(Example) - -import scala.meta._ -exampleSuperTypes := exampleSuperTypes.value.map { - case ctor"_root_.org.scalatest.FreeSpec" => - ctor"_root_.org.scalatest.AsyncFreeSpec" - case otherTrait => - otherTrait -} diff --git a/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/RaiiSpec.scala b/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/RaiiSpec.scala deleted file mode 100644 index 5735af55..00000000 --- a/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/RaiiSpec.scala +++ /dev/null @@ -1,258 +0,0 @@ -package com.thoughtworks.dsl -package domains - -import com.thoughtworks.dsl.Dsl.{!!, Continuation, reset} -import com.thoughtworks.dsl.keywords.{Catch, Shift, Yield} -import org.scalatest.{FreeSpec, Matchers} -import com.thoughtworks.dsl.domains.task._ - -import scala.util.control.NonFatal - -/** - * @author 杨博 (Yang Bo) - */ -final class RaiiSpec extends FreeSpec with Matchers { - - @inline - private def jvmCatch[Domain](eh: => Domain !! Throwable)(failureHandler: Throwable => Domain)( - implicit shiftDsl: Dsl[Shift[Domain, Throwable], Domain, Throwable]): Domain = { - val protectedContinuation: Domain !! Throwable = try { - eh - } catch { - case NonFatal(e) => - return failureHandler(e) - } - shiftDsl.cpsApply(protectedContinuation, failureHandler) - } - - /** - * Exit the current scope then hang up - */ - def successContinuation[LeftDomain](domain: LeftDomain): (LeftDomain !! Throwable) @reset = Continuation.empty(domain) - - def failureContinuation[LeftDomain](throwable: Throwable): (LeftDomain !! Throwable) @reset = Continuation.now(throwable) - - "Given a continuation that throws an exception" - { - object MyException extends Exception - - "Given a generator" - { - - object MyException extends Exception - def generator: Stream[Int] !! Throwable = { - !Yield(1) - throw { - !Yield(2) - MyException - } - !Yield(3) - successContinuation(Stream.empty) - } - - "When catching exception thrown from the generator" - { - val catching = generator { e: Throwable => - e should be(MyException) - Stream(100) - } - "Then it should contain all elements before throwing exception and the element when catching the exception" in { - catching should be(Seq(1, 2, 100)) - } - } - - } - } - "try/catch" - { - "yield and catch" in { - def continuation: Stream[Int] !! Throwable !! String = _ { - val tryResult = try { - 0 / 0 - } catch { - case e: ArithmeticException => - !Yield(3) - "catch" - } - "returns " + tryResult - } - continuation { result: String => - result should be("returns catch") - successContinuation(Stream.empty) - } { e => - Stream.empty - } should be(Seq(3)) - } - - "simple catch" in { - - object MyException extends Exception - def generator: Stream[String] !! Throwable !! Int = { continue => - try { - !Yield("before catch") - continue(43) - } catch { - case e: Throwable => - e should be(MyException) - !Yield("catch") - continue(42) - } - } - - object MyException2 extends Exception - - def generator2: Stream[String] !! Throwable = { - import Throwable._ - val i = !Shift(generator) - !Yield(i.toString) - i should be(43) - failureContinuation(MyException) - } - - generator2 { e => - e should be(MyException) - - Stream("end") - } should be(Stream("before catch", "43", "end")) - } - - "issue 2" in { - def continuation: Stream[Int] !! Throwable !! String = { continue => - !Yield(1) - try {} catch { - case e: ArithmeticException => - !Yield(2) - } - !Yield(3) - failureContinuation(new ArithmeticException) - } - - continuation { result: String => - failureContinuation(new AssertionError()) - } { e => - e should be(a[ArithmeticException]) - Stream.empty - } should be(Stream(1, 3)) - - } - - "empty finally" in { - def continuation: Stream[Int] !! Throwable !! String = _ { - val tryResult = try { - 0 / 0 - !Yield(-1) - } finally {} - "returns " + tryResult - } - - jvmCatch(continuation { result: String => - failureContinuation(new AssertionError()) - }) { e => - e should be(a[ArithmeticException]) - Stream.empty - } should be(Seq()) - } - "rethrow" in { - def continuation: Stream[Int] !! Throwable !! String = _ { - val tryResult = try { - 0 / 0 - } catch { - case e: ArithmeticException => - !Yield(42) - throw e - } - "returns " + tryResult - } - continuation { result: String => - failureContinuation(new AssertionError()) - } { e => - e should be(a[ArithmeticException]) - Stream.empty - } should be(Seq(42)) - } - - "complex" in { - def continuation: Stream[Int] !! Throwable !! String = _ { - !Yield(0) - val tryResult = try { - !Yield(1) - try {} catch { - case e: ArithmeticException => - !Yield(2) - } - !Yield(3) - - 0 / 0 - !Yield(4) - "try" - } catch { - case e: ArithmeticException => - !Yield(5) - "catch" - } finally { - !Yield(6) - - def ignoredFinalResult = "finally" - ignoredFinalResult - } - !Yield(7) - "returns " + tryResult - } - - continuation { result: String => - result should be("returns catch") - successContinuation(Stream.empty) - } { e => - Stream.empty - } should be(Seq(0, 1, 3, 5, 6, 7)) - } - } - // -// "Given a continuation that throws an exception" - { -// object MyException extends Exception -// -// "Given a generator" - { -// -// object MyException extends Exception -// def generator: Throwable[Stream[Int]] = { -// !Yield(1) -// throw { -// !Yield(2) -// MyException -// } -// !Yield(3) -// Throwable.success(Stream.empty) -// } -// -// "When catching exception thrown from the generator" - { -// val catching = generator.apply { e: Throwable => -// e should be(MyException) -// Stream(100) -// } -// "Then it should contain all elements before throwing exception and the element when catching the exception" in { -// catching should be(Seq(1, 2, 100)) -// } -// } -// -// } -// } -// -// "try/catch" - { -// "yield and catch" in { -// def continuation: Continuation[Throwable[Stream[Int]], String] = _ { -// val tryResult = try { -// 0 / 0 -// } catch { -// case e: ArithmeticException => -// !Yield(3) -// "catch" -// } -// "returns " + tryResult -// } -// -// continuation.onComplete { r => -// r.get should be("returns catch") -// -// Stream(5) -// -// } should be(Stream(3, 5)) -// } -// } - -} diff --git a/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/UsingSpec.scala b/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/UsingSpec.scala deleted file mode 100644 index a74536ef..00000000 --- a/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/UsingSpec.scala +++ /dev/null @@ -1,53 +0,0 @@ -package com.thoughtworks.dsl -package domains - -import com.thoughtworks.dsl.Dsl.{!!, Continuation, reset} -import com.thoughtworks.dsl.keywords.{Using, Yield} -import org.scalatest.{Assertion, FreeSpec, Matchers} - -/** - * @author 杨博 (Yang Bo) - */ -class UsingSpec extends FreeSpec with Matchers { - - def successContinuation[Domain](domain: Domain): (Domain !! Throwable) @reset = Continuation.empty(domain) - - "AutoCloseable" - { - - "scope" - { - - "arm" in { - var isOpen = false - - def raii: Stream[Int] !! Throwable !! Assertion = Continuation.apply { - !Yield(1) - isOpen should be(false) - val a = !Using { - !Yield(2) - new AutoCloseable { - isOpen should be(false) - isOpen = true - - def close(): Unit = { - isOpen should be(true) - isOpen = false - } - } - } - !Yield(3) - isOpen should be(true) - } - - isOpen should be(false) - - val myException = new Exception - - val stream = raii(_ => _ => Stream.empty)(throw _) - - stream should be(Stream(1, 2, 3)) - isOpen should be(false) - } - } - - } -} diff --git a/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala b/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala deleted file mode 100644 index 16d30716..00000000 --- a/domains-task/.jvm/src/test/scala/com/thoughtworks/dsl/domains/taskSpec.scala +++ /dev/null @@ -1,207 +0,0 @@ -package com.thoughtworks.dsl -package domains - -import com.thoughtworks.dsl.Dsl.{!!, reset} -import org.scalatest.{Assertion, AsyncFreeSpec, Matchers} - -import com.thoughtworks.dsl.keywords.{Using, Each, Fork} -import com.thoughtworks.dsl.domains.task._ -import com.thoughtworks.dsl.keywords.Shift.implicitShift - -import scala.collection.mutable.ArrayBuffer -import scala.util.control.TailCalls -import scala.util.{Failure, Success} - -/** - * @author 杨博 (Yang Bo) - */ -final class taskSpec extends AsyncFreeSpec with Matchers { - - "tailRecurision" in Task.toFuture(Task.apply { - def loop(i: Int = 0, accumulator: Int = 0): task.Task[Int] = _ { - if (i < 1000) { - !loop(i + 1, accumulator + i) - } else { - accumulator - } - } - - val result = !loop() - result should be(499500) - }) - - "taskToFuture" in Task.toFuture(Task.apply { - succeed - }) - - "loop" in Task.toFuture(Task.apply { - - val task1: Task[Int] = Task.now(1) - - val ts = Task.join { - !Fork(0 until 10) + !task1 - } - - !ts should be(1 until 11) - - }) - - "try" in Task.toFuture(Task.apply { - class MyException extends Exception - val task1: Task[Int] = Task.apply { - throw new MyException - } - - val task2 = Task.apply { - val v = try { - !task1 - "no exception" - } catch { - case myException: MyException => - "my exception" - } - - s"try: $v" - } - - !task2 should be("try: my exception") - }) - - "empty try" in { - val logs = ArrayBuffer.empty[String] - - class MyException extends Exception { - logs += "MyException" - } - val task1: Task[String] = _ { - throw new MyException - } - - val task2: Task[String] = _ { - try { - "no exception" - } catch { - case myException: MyException => - "my exception" - } - !task1 - } - - Task.onComplete(task2) { - case Success(s) => - logs += s - throw new AssertionError() - case Failure(e) => - e should be(a[MyException]) - logs += "uncaught MyException" - } - logs should be(ArrayBuffer("MyException", "uncaught MyException")) - } - - "autoClose" in { - val logs = ArrayBuffer.empty[Int] - - val task: Task[Unit] = Task.apply { - - logs += 0 - - !Using(new AutoCloseable { - logs += 10 - def close(): Unit = { - logs += 20 - } - }) - !Using(new AutoCloseable { - logs += 11 - def close(): Unit = { - logs += 21 - } - }) - !Using(new AutoCloseable { - logs += 12 - def close(): Unit = { - logs += 22 - } - }) - - !Task.apply { - logs += 3 - - !Using(new AutoCloseable { - logs += 40 - def close(): Unit = { - logs += 50 - } - }) - !Using(new AutoCloseable { - logs += 41 - def close(): Unit = { - logs += 51 - } - }) - !Using(new AutoCloseable { - logs += 42 - def close(): Unit = { - logs += 52 - } - }) - - logs += 6 - } - - logs += 7 - - } - - Task.toFuture(task).map { _ => - logs should be(ArrayBuffer(0, 10, 11, 12, 3, 40, 41, 42, 6, 52, 51, 50, 7, 22, 21, 20)) - } - - } - - "nested seq of task" in { - - def composeTask(t0: Task[Seq[Task[Seq[Task[Seq[Task[Seq[Float]]]]]]]]): Task[Seq[Seq[Seq[Seq[Float]]]]] = { - // TODO: remove explicit type parameters when https://github.com/scala/bug/issues/11068 is fixed - Task.join[Seq[Seq[Seq[Float]]], Seq[Seq[Seq[Seq[Float]]]]] { - val t1 = !Each(!t0) - !Task.join[Seq[Seq[Float]], Seq[Seq[Seq[Float]]]] { - val t2 = !Each(!t1) - !Task.join[Seq[Float], Seq[Seq[Float]]] { - val t3 = !Each(!t2) - !t3 - } - } - } - } - - Task - .toFuture( - composeTask(Task.now( - 1 to 2 map { i => - Task.now(1 to 3 map { j => - Task.now(1 to 4 map { k => - Task.now(1 to 5 map { l => - (i * 1000 + j * 100 + k * 10 + l).toFloat - }) - }) - }) - } - ))) - .map { s => - s should be( - 1 to 2 map { i => - 1 to 3 map { j => - 1 to 4 map { k => - 1 to 5 map { l => - (i * 1000 + j * 100 + k * 10 + l).toFloat - } - } - } - } - ) - - } - - } -} diff --git a/domains-task/build.sbt b/domains-task/build.sbt deleted file mode 100644 index 5e4c92b9..00000000 --- a/domains-task/build.sbt +++ /dev/null @@ -1,30 +0,0 @@ -libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Optional - -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Some("–Xexperimental") - case _ => None - } -} - -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } -} - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } -} - -libraryDependencies += "com.thoughtworks.enableIf" %% "enableif" % "1.1.6" \ No newline at end of file diff --git a/domains-task/src/main/scala/com/thoughtworks/dsl/domains/task.scala b/domains-task/src/main/scala/com/thoughtworks/dsl/domains/task.scala deleted file mode 100644 index a35f15d7..00000000 --- a/domains-task/src/main/scala/com/thoughtworks/dsl/domains/task.scala +++ /dev/null @@ -1,191 +0,0 @@ -package com.thoughtworks.dsl -package domains - -import com.thoughtworks.dsl.Dsl.{!!, Continuation, reset} -import com.thoughtworks.dsl.keywords.Shift -import com.thoughtworks.{enableIf, enableMembersIf} - -import scala.collection._ -import scala.collection.generic.CanBuildFrom -import scala.collection.mutable.Builder -import scala.concurrent.duration.Duration -import scala.concurrent.{ExecutionContext, Future, Promise, SyncVar} -import scala.util.control.{NonFatal, TailCalls} -import scala.util.{Failure, Success, Try} -import scala.util.control.TailCalls.TailRec - -/** - * @author 杨博 (Yang Bo) - */ -object task { - - type TaskDomain = TailRec[Unit] !! Throwable - - /** The asynchronous task that supports exception handling, resource management, and is stack-safe. - * - * @template - * @example A [[Task]] can be created from `for`-comprehension, - * where [[keywords.Each]] and [[keywords.Fork]] can be used together to asynchronously iterate collections. - * - * For example, the above `concatenateRemoteData` downloads and concatenates data from multiple URLs. - * - * {{{ - * import com.thoughtworks.dsl.comprehension._ - * import com.thoughtworks.dsl.keywords._ - * import com.thoughtworks.dsl.keywords.Shift._ - * import com.thoughtworks.dsl.domains.task.Task - * import java.net.URL - * def concatenateRemoteData(urls: List[URL], downloader: URL => Task[Vector[Byte]]): Task[Vector[Byte]] = { - * for { - * url <- Fork(urls) - * data <- downloader(url) - * byte <- Each(data) - * } yield byte - * }.as[Task[Vector[Byte]]] - * }}} - * - * A [[Task]] can be also created from [[Task.apply]] - * - * {{{ - * def mockDownloader(url: URL) = Task { - * "mock data\n".getBytes.toVector - * } - * }}} - * - * A [[Task]] can be then converted to [[scala.concurrent.Future]] via [[Task.toFuture]], - * in order to integrate into other frameworks. - * - * In this example, it's a `Future[Assertion]` required by [[org.scalatest.AsyncFreeSpec]]. - * - * {{{ - * val mockUrls = List(new URL("http://example.com/file1"), new URL("http://example.com/file2")) - * - * import org.scalatest.Assertion - * def assertion: Task[Assertion] = Task { - * !concatenateRemoteData(mockUrls, mockDownloader) should be("mock data\nmock data\n".getBytes.toVector) - * } - * - * Task.toFuture(assertion) - * }}} - */ - type Task[+A] = TaskDomain !! A - - object Task { - - @inline - def now[A](a: A): Task[A] = _(a) - - @inline - def delay[A](f: () => A): Task[A] = _(f()) - - @inline - def apply[A](a: => A): Task[A] @reset = delay(a _) - - /** Returns a task that does nothing but let the succeeding tasks run on `executionContext` - * - * @example All the code after a `!switchExecutionContext` should be executed on `executionContext` - * {{{ - * import com.thoughtworks.dsl.domains.task.Task - * import org.scalatest.Assertion - * import scala.concurrent.ExecutionContext - * import com.thoughtworks.dsl.keywords.Shift.implicitShift - * def myTask: Task[Assertion] = _ { - * val originalThread = Thread.currentThread - * !Task.switchExecutionContext(ExecutionContext.global) - * Thread.currentThread should not be originalThread - * } - * - * Task.toFuture(myTask) - * - * }}} - */ - @inline - def switchExecutionContext(executionContext: ExecutionContext): Task[Unit] = { continue => failureHandler => - executionContext.execute(new Runnable { - - @inline - private def stackSafeRun(): TailRec[Unit] = { - val protectedContinuation = try { - continue(()) - } catch { - case NonFatal(e) => - return failureHandler(e) - } - protectedContinuation(failureHandler) - } - - def run(): Unit = stackSafeRun().result - - }) - TailCalls.done(()) - } - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$""")) - private[task] object Scala211Or212 { - type Factory[-A, +C] = scala.collection.generic.CanBuildFrom[Nothing, A, C] - - @inline - def newBuilder[A, C](implicit factory: Factory[A, C]): Builder[A, C] = { - factory() - } - - } - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.13\..*$""")) - private[task] object Scala213 { - - @inline - def newBuilder[A, C](implicit factory: Factory[A, C]): Builder[A, C] = { - factory.newBuilder - } - - } - - import Scala211Or212._ - import Scala213._ - - def join[Element, That](element: Element)(implicit factory: Factory[Element, That]): Task[That] @reset = now { - (newBuilder[Element, That] += element).result() - } - - def onComplete[A](task: Task[A])(continue: Try[A] => Unit) = { - Continuation - .toTryContinuation(task) { result => - TailCalls.done(continue(result)) - } - .result - } - - @enableIf(c => !c.compilerSettings.exists(_.matches("""^-Xplugin:.*scalajs-compiler_.*\.jar$"""))) - def blockingAwait[A](task: Task[A], timeout: Duration = Duration.Inf): A = { - val syncVar = new SyncVar[Try[A]] - Continuation - .toTryContinuation(task) { result => - syncVar.put(result) - TailCalls.done(()) - } - .result - - if (timeout.isFinite()) { - syncVar.take(timeout.toMillis).get - } else { - syncVar.take.get - } - } - - /** Converts a [[Task]] to a [[scala.concurrent.Future]]. - * - * @see [[keywords.Await]] for converting a [[scala.concurrent.Future]] to a [[Task]]. - */ - def toFuture[A](task: Task[A]): Future[A] = { - val promise = Promise[A]() - Continuation - .toTryContinuation(task) { tryResult => - promise.complete(tryResult) - TailCalls.done(()) - } - .result - promise.future - } - } - -} diff --git a/keywords-AsynchronousIo/.js/build.sbt b/keywords-AsynchronousIo/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-AsynchronousIo/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-AsynchronousIo/.jvm/build.sbt b/keywords-AsynchronousIo/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-AsynchronousIo/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-AsynchronousIo/.jvm/jvm.sbt b/keywords-AsynchronousIo/.jvm/jvm.sbt deleted file mode 100644 index 3912b18c..00000000 --- a/keywords-AsynchronousIo/.jvm/jvm.sbt +++ /dev/null @@ -1,11 +0,0 @@ -enablePlugins(Example) - -import meta._ -exampleSuperTypes := exampleSuperTypes.value.map { - case ctor"_root_.org.scalatest.FreeSpec" => - ctor"_root_.org.scalatest.AsyncFreeSpec" - case otherTrait => - otherTrait -} - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/keywords-AsynchronousIo/build.sbt b/keywords-AsynchronousIo/build.sbt deleted file mode 100644 index 38ad40bd..00000000 --- a/keywords-AsynchronousIo/build.sbt +++ /dev/null @@ -1,9 +0,0 @@ -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers < Seq(2L, 12L)) { - // Enable SAM types for Scala 2.11 - Some("-Xexperimental") - } else { - None - } -} diff --git a/keywords-AsynchronousIo/src/main/scala/com/thoughtworks/dsl/keywords/AsynchronousIo.scala b/keywords-AsynchronousIo/src/main/scala/com/thoughtworks/dsl/keywords/AsynchronousIo.scala deleted file mode 100644 index a32b7931..00000000 --- a/keywords-AsynchronousIo/src/main/scala/com/thoughtworks/dsl/keywords/AsynchronousIo.scala +++ /dev/null @@ -1,153 +0,0 @@ -package com.thoughtworks.dsl -package keywords -import java.net.SocketAddress -import java.nio.ByteBuffer -import java.nio.channels._ - -import com.thoughtworks.dsl.Dsl.{!!, Keyword} - -import scala.util.control.NonFatal - -/** The base keyword to perform asynchronous IO in [[domains.task.Task]]s. - * - * @example The following `readAll` is a [[com.thoughtworks.dsl.domains.task.Task Task]] to read file content - * with the help of [[AsynchronousIo.ReadFile]] - * - * {{{ - * import java.nio._, file._, channels._ - * import com.thoughtworks.dsl.domains.task.Task - * import com.thoughtworks.dsl.keywords._ - * import com.thoughtworks.dsl.keywords.Shift._ - * import com.thoughtworks.dsl.keywords.AsynchronousIo.ReadFile - * import scala.collection.mutable.ArrayBuffer - * import scala.io.Codec - * def readAll(channel: AsynchronousFileChannel, temporaryBufferSize: Int = 4096): Task[ArrayBuffer[CharBuffer]] = Task { - * val charBuffers = ArrayBuffer.empty[CharBuffer] - * val decoder = Codec.UTF8.decoder - * val byteBuffer = ByteBuffer.allocate(temporaryBufferSize) - * var position: Long = 0L - * while (!ReadFile(channel, byteBuffer, position) != -1) { - * position += byteBuffer.position() - * byteBuffer.flip() - * charBuffers += decoder.decode(byteBuffer) - * byteBuffer.clear() - * } - * charBuffers - * } - * }}} - * - * `Task`s created from !-notation can be used in `for`-comprehension, - * and other keywords can be used together in the same `for` block. - * - * For example, the following `cat` function contains a single `for` block to concatenate file contents. - * It asynchronously iterates elements `Seq`, `ArrayBuffer` and `String` with the help of [[keywords.Each]], - * managed native resources with the help of [[keywords.Using]], - * performs previously created `readAll` task with the help of [[keywords.Shift]], - * and finally converts the return type [[comprehension.ComprehensionOps.as as]] a `Task[Vector[Char]]`. - * - * {{{ - * import com.thoughtworks.dsl.comprehension._ - * import com.thoughtworks.dsl.keywords._ - * import com.thoughtworks.dsl.keywords.Shift._ - * import com.thoughtworks.dsl.domains.task.Task - * import java.net.URL - * def cat(paths: Path*) = { - * for { - * path <- Each(paths) - * channel <- Using(AsynchronousFileChannel.open(path)) - * charBuffers <- readAll(channel) - * charBuffer <- Each(charBuffers) - * char <- Each(charBuffer.toString) - * } yield char - * }.as[Task[Vector[Char]]] - * }}} - * - * Then the `cat` function is used to concatenate files from this project, as shown below: - * - * {{{ - * Task.toFuture(Task { - * (!cat(Paths.get(".sbtopts"), Paths.get(".scalafmt.conf"))).mkString should be( - * "-J-XX:MaxMetaspaceSize=512M\n-J-Xmx5G\n-J-Xss6M\nversion = \"1.5.1\"\nmaxColumn = 120" - * ) - * }) - * }}} - */ -trait AsynchronousIo[Value] extends Any with Keyword[AsynchronousIo[Value], Value] { - - /** Starts the asynchronous operations */ - protected def start[Attachment](attachment: Attachment, handler: CompletionHandler[Value, _ >: Attachment]): Unit -} - -object AsynchronousIo { - - final case class Connect(socket: AsynchronousSocketChannel, remote: SocketAddress) extends AsynchronousIo[Void] { - protected def start[Attachment](attachment: Attachment, handler: CompletionHandler[Void, _ >: Attachment]): Unit = { - socket.connect(remote, attachment, handler) - } - } - - final case class Accept(socket: AsynchronousServerSocketChannel) - extends AnyVal - with AsynchronousIo[AsynchronousSocketChannel] { - protected def start[Attachment](attachment: Attachment, - handler: CompletionHandler[AsynchronousSocketChannel, _ >: Attachment]): Unit = { - socket.accept(attachment, handler) - } - } - - final case class ReadFile(channel: AsynchronousFileChannel, destination: ByteBuffer, position: Long) - extends AsynchronousIo[Integer] { - protected def start[Attachment](attachment: Attachment, - handler: CompletionHandler[Integer, _ >: Attachment]): Unit = { - channel.read(destination, position, attachment, handler) - } - } - - final case class WriteFile(channel: AsynchronousFileChannel, source: ByteBuffer, position: Long) - extends AsynchronousIo[Integer] { - protected def start[Attachment](attachment: Attachment, - handler: CompletionHandler[Integer, _ >: Attachment]): Unit = { - channel.write(source, position, attachment, handler) - } - } - - final case class Read(channel: AsynchronousByteChannel, destination: ByteBuffer) extends AsynchronousIo[Integer] { - protected def start[Attachment](attachment: Attachment, - handler: CompletionHandler[Integer, _ >: Attachment]): Unit = { - channel.read(destination, attachment, handler) - } - } - - final case class Write(channel: AsynchronousByteChannel, source: ByteBuffer) extends AsynchronousIo[Integer] { - private[dsl] def destination = source - - protected def start[Attachment](attachment: Attachment, - handler: CompletionHandler[Integer, _ >: Attachment]): Unit = { - channel.write(source, attachment, handler) - } - } - - private def completionHandler[Value](successHandler: Value => (Unit !! Throwable)) = { - new CompletionHandler[Value, Throwable => Unit] { - def failed(exc: Throwable, failureHandler: Throwable => Unit): Unit = { - failureHandler(exc) - } - - def completed(result: Value, failureHandler: Throwable => Unit): Unit = { - val protectedContinuation = try { - successHandler(result) - } catch { - case NonFatal(e) => - val () = failureHandler(e) - return - } - protectedContinuation(failureHandler) - } - } - } - - implicit def asynchronousIoDsl[Value]: Dsl[AsynchronousIo[Value], Unit !! Throwable, Value] = { (keyword, handler) => - keyword.start(_, completionHandler(handler)) - } - -} diff --git a/keywords-Await/.js/build.sbt b/keywords-Await/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Await/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Await/.jvm/build.sbt b/keywords-Await/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Await/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Await/.jvm/jvm.sbt b/keywords-Await/.jvm/jvm.sbt deleted file mode 100644 index b0e5027b..00000000 --- a/keywords-Await/.jvm/jvm.sbt +++ /dev/null @@ -1,22 +0,0 @@ -import Ordering.Implicits._ - -libraryDependencies ++= { - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq( - "com.typesafe.akka" %% "akka-actor" % "2.5.14" % Test, - "com.typesafe.akka" %% "akka-stream" % "2.5.14" % Test, - "com.typesafe.akka" %% "akka-http" % "10.1.3" % Test - ) - } -} - -// Skip tests in Scala 2.13 because Akka does not support Scala 2.13 yet -unmanagedSourceDirectories in Test --= { - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq((scalaSource in Test).value) - } else { - Nil - } -} diff --git a/keywords-Await/.jvm/src/test/scala/com/thoughtworks/dsl/keywords/AwaitSpec.scala b/keywords-Await/.jvm/src/test/scala/com/thoughtworks/dsl/keywords/AwaitSpec.scala deleted file mode 100644 index 2c179913..00000000 --- a/keywords-Await/.jvm/src/test/scala/com/thoughtworks/dsl/keywords/AwaitSpec.scala +++ /dev/null @@ -1,114 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.Uri.Path -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server._ -import akka.stream.ActorMaterializer -import akka.util.ByteString -import com.thoughtworks.dsl.Dsl.reset -import org.scalatest.{AsyncFreeSpec, BeforeAndAfterAll, Matchers} -//import org.scalamock.scalatest.MockFactory - -import scala.concurrent.Future -import scala.concurrent.duration._ - -/** - * @author 杨博 (Yang Bo) - */ -final class AwaitSpec extends AsyncFreeSpec with Matchers with BeforeAndAfterAll with Directives { - implicit val system = ActorSystem() - - implicit val materializer = ActorMaterializer() - - def downloadTwoPages(): Future[(ByteString, ByteString)] = Future { - val response1 = !Await(Http().singleRequest(HttpRequest(HttpMethods.GET, !Await(pingUri)))) - val content1 = !Await(response1.entity.toStrict(timeout = 5.seconds)) - val response2 = !Await(Http().singleRequest(HttpRequest(HttpMethods.GET, !Await(pongUri)))) - val content2 = !Await(response2.entity.toStrict(timeout = 5.seconds)) - (content1.data, content2.data) - } - - private val mockServer = { - val route = - get { - path("ping") { - complete("PING!") - } ~ path("pong") { - complete("PONG!") - } - } - Http().bindAndHandle(route, "localhost", 0) - } - override protected def afterAll(): Unit = { - val _ = Future { - val binding = !Await(mockServer) - !Await(binding.unbind()) - system.terminate() - }: @reset - } - - "download two pages" in { - downloadTwoPages().map { - case (bytes1, bytes2) => - bytes1.decodeString(io.Codec.UTF8.charSet) should be("PING!") - bytes2.decodeString(io.Codec.UTF8.charSet) should be("PONG!") - } - } - - private def pingUri = Future { - Uri.from(scheme = "http", - host = (!Await(mockServer)).localAddress.getHostName, - port = (!Await(mockServer)).localAddress.getPort, - path = "/ping") - } - - private def pongUri = Future { - Uri.from(scheme = "http", - host = (!Await(mockServer)).localAddress.getHostName, - port = (!Await(mockServer)).localAddress.getPort, - path = "/pong") - } - - "http get" in ({ - val response = !Await(Http().singleRequest(HttpRequest(uri = !Await(pingUri)))) - response.status should be(StatusCodes.OK) - }: @reset) - - "Downloading two web pages as an asynchronous generator, in the style of !-notation" in ({ - def downloadTwoPagesGenerator(): Stream[Future[ByteString]] = { - val response1 = !Await(Http().singleRequest(HttpRequest(HttpMethods.GET, !Await(pingUri)))) - val content1 = !Await(response1.entity.toStrict(timeout = 5.seconds)) - !Yield(content1.data) - val response2 = !Await(Http().singleRequest(HttpRequest(HttpMethods.GET, !Await(pongUri)))) - val content2 = !Await(response2.entity.toStrict(timeout = 5.seconds)) - !Yield(content2.data) - - Stream.empty[Future[ByteString]] - } - - val stream = downloadTwoPagesGenerator() - (!Await(stream(0))).decodeString(io.Codec.UTF8.charSet) should be("PING!") - (!Await(stream(1))).decodeString(io.Codec.UTF8.charSet) should be("PONG!") - }: @reset) - - "multiple http" in ({ - - def createAsynchronousStream(): Stream[Future[Int]] = { - val response1 = !Await(Http().singleRequest(HttpRequest(uri = !Await(pingUri)))) - !Yield(response1.status.intValue()) - response1.discardEntityBytes() - val response2 = !Await(Http().singleRequest(HttpRequest(uri = !Await(pongUri)))) - !Yield(response2.status.intValue()) - response2.discardEntityBytes() - Stream.empty[Future[Int]] - } - - val asynchronousStream = createAsynchronousStream() - !Await(asynchronousStream(0)) should be(StatusCodes.OK.intValue) - !Await(asynchronousStream(1)) should be(StatusCodes.OK.intValue) - - }: @reset) - -} diff --git a/keywords-Await/build.sbt b/keywords-Await/build.sbt deleted file mode 100644 index e98b04fa..00000000 --- a/keywords-Await/build.sbt +++ /dev/null @@ -1,11 +0,0 @@ -enablePlugins(Example) - -import scala.meta._ -exampleSuperTypes := exampleSuperTypes.value.map { - case ctor"_root_.org.scalatest.FreeSpec" => - ctor"_root_.org.scalatest.AsyncFreeSpec" - case otherTrait => - otherTrait -} - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/keywords-Await/src/main/scala/com/thoughtworks/dsl/keywords/Await.scala b/keywords-Await/src/main/scala/com/thoughtworks/dsl/keywords/Await.scala deleted file mode 100644 index b88206ec..00000000 --- a/keywords-Await/src/main/scala/com/thoughtworks/dsl/keywords/Await.scala +++ /dev/null @@ -1,135 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Keyword} - -import scala.concurrent.Await.result -import scala.concurrent.duration.Duration -import scala.concurrent.{ExecutionContext, Future} -import scala.language.implicitConversions - -/** [[Await]] is a [[Dsl.Keyword Keyword]] to extract value from a [[scala.concurrent.Future]]. - * - * This keyword is available in functions whose return types are - * [[scala.concurrent.Future Future]], [[domains.task.Task]], - * or any exception aware continuations as `(_ !! Throwable !! _)`. - * - * @example Given a [[scala.concurrent.Future Future]]: - * {{{ - * import scala.concurrent.Future - * val myFuture40 = Future { - * 40 - * } - * }}} - * - * You can [[Await]] the [[scala.concurrent.Future Future]] in another [[scala.concurrent.Future Future]] - * - * {{{ - * def myFuture42 = Future { - * !Await(myFuture40) + 2 - * } - * }}} - * - * A [[scala.concurrent.Future Future]] can be converted to a [[domains.task.Task]] - * with the help of [[Await]]. - * - * {{{ - * import com.thoughtworks.dsl.domains.task.Task - * import com.thoughtworks.dsl.keywords.Await - * val myTask = Task { - * !Await(myFuture42) - * } - * }}} - * - * Then a [[domains.task.Task]] can be converted back to a [[scala.concurrent.Future]] - * via [[domains.task.Task.toFuture]]. - * - * {{{ - * val myAssertionTask = Task { - * !Shift(myTask) should be(42) - * } - * Task.toFuture(myAssertionTask) - * }}} - * @example `!Await` can be used together with `try` / `catch` / `finally`. - * {{{ - * import scala.concurrent.Future - * val buffer = new StringBuffer - * def recoverFuture = Future { - * buffer.append("Oh") - * } - * def exceptionalFuture = Future[StringBuffer] { - * throw new IllegalStateException("No") - * } - * def myFuture = Future { - * try { - * !Await(exceptionalFuture) - * } catch { - * case e: IllegalStateException => - * !Await(recoverFuture) - * buffer.append(' ') - * buffer.append(e.getMessage) - * } finally { - * buffer.append("!") - * } - * } - * myFuture.map(_.toString should be("Oh No!")) - * }}} - * @example Other keywords, including [[Return]] or [[Get]], can be used together with [[Await]] - * {{{ - * import scala.concurrent.Future - * import com.thoughtworks.dsl.keywords.{Get, Return} - * val buffer = new StringBuffer - * def recoverFuture = Future { - * buffer.append("Oh") - * } - * def exceptionalFuture = Future[StringBuffer] { - * throw new IllegalStateException("No") - * } - * def myFuture: Char => Future[StringBuffer] = !Return { - * try { - * !Await(exceptionalFuture) - * } catch { - * case e: IllegalStateException => - * !Await(recoverFuture) - * buffer.append(!Get[Char]) - * buffer.append(e.getMessage) - * } finally { - * buffer.append("!") - * } - * } - * myFuture(' ').map(_.toString should be("Oh No!")) - * }}} - * @author 杨博 (Yang Bo) - */ -final case class Await[Value](future: Future[Value]) extends AnyVal with Keyword[Await[Value], Value] - -object Await { - - implicit def implicitAwait[Value](future: Future[Value]): Await[Value] = Await[Value](future) - - implicit def streamAwaitDsl[Value, That]( - implicit executionContext: ExecutionContext): Dsl[Await[Value], Stream[Future[That]], Value] = - new Dsl[Await[Value], Stream[Future[That]], Value] { - def cpsApply(keyword: Await[Value], handler: Value => Stream[Future[That]]): Stream[Future[That]] = { - import keyword.future - val futureOfStream = future.map(handler) - new Stream.Cons(futureOfStream.flatMap(_.head), result(futureOfStream, Duration.Inf).tail) - } - } - - implicit def awaitDsl[Value, That]( - implicit executionContext: ExecutionContext): Dsl[Await[Value], Future[That], Value] = - new Dsl[Await[Value], Future[That], Value] { - def cpsApply(keyword: Await[Value], handler: Value => Future[That]): Future[That] = { - keyword.future.flatMap(handler) - } - } - - implicit def continuationAwaitDsl[Value]( - implicit executionContext: ExecutionContext): Dsl[Await[Value], Unit !! Throwable, Value] = - new Dsl[Await[Value], Unit !! Throwable, Value] { - def cpsApply(keyword: Await[Value], handler: Value => Unit !! Throwable): Unit !! Throwable = - !!.fromTryContinuation(keyword.future.onComplete)(handler) - } - -} diff --git a/keywords-Catch/.js/build.sbt b/keywords-Catch/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Catch/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Catch/.jvm/build.sbt b/keywords-Catch/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Catch/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Catch/build.sbt b/keywords-Catch/build.sbt deleted file mode 100644 index a368460f..00000000 --- a/keywords-Catch/build.sbt +++ /dev/null @@ -1,12 +0,0 @@ -libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test - -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Seq("–Xexperimental") - case _ => Seq.empty - } -} - -libraryDependencies += "com.thoughtworks.extractor" %%% "extractor" % "2.1.1" diff --git a/keywords-Catch/src/main/scala/com/thoughtworks/dsl/keywords/Catch.scala b/keywords-Catch/src/main/scala/com/thoughtworks/dsl/keywords/Catch.scala deleted file mode 100644 index dbaa93b3..00000000 --- a/keywords-Catch/src/main/scala/com/thoughtworks/dsl/keywords/Catch.scala +++ /dev/null @@ -1,206 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.Extractor._ -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Continuation, Keyword} -import com.thoughtworks.dsl.keywords.Catch.{CatchDsl, DslCatch} - -import scala.annotation.implicitNotFound -import scala.concurrent.{ExecutionContext, Future} -import scala.util.control.Exception.Catcher -import scala.util.control.NonFatal - -/** - * @author 杨博 (Yang Bo) - */ -final case class Catch[Domain, Value](block: Domain !! Value, catcher: Catcher[Domain !! Value]) - extends Keyword[Catch[Domain, Value], Value] - -private[keywords] trait LowPriorityCatch1 { this: Catch.type => - @deprecated("Use Dsl[Catch[...], ...] as implicit parameters instead of CatchDsl[...]", "Dsl.scala 1.2.0") - private[keywords] def liftFunction1CatchDsl[InnerDomain, OuterDomain, State, Value]( - implicit leftCatchDsl: CatchDsl[InnerDomain, OuterDomain, Value]) - : CatchDsl[State => InnerDomain, State => OuterDomain, Value] = { - new LiftFunction1CatchDsl - } - - implicit def liftFunction1CatchDsl[InnerDomain, OuterDomain, State, Value]( - implicit leftCatchDsl: DslCatch[InnerDomain, OuterDomain, Value]) - : DslCatch[State => InnerDomain, State => OuterDomain, Value] = { - new LiftFunction1CatchDsl - } -} - -private[keywords] trait LowPriorityCatch0 extends LowPriorityCatch1 { this: Catch.type => - - implicit def liftContinuationCatchDsl[LeftDomain, RightDomain, Value]( - implicit leftCatchDsl: DslCatch[LeftDomain, LeftDomain, Value]) - : CatchDsl[LeftDomain !! Value, LeftDomain !! RightDomain, Value] = { - new LiftContinuationCatchDsl - } - - @deprecated("Use Dsl[Catch[...], ...] as implicit parameters instead of CatchDsl[...]", "Dsl.scala 1.2.0") - private[keywords] def liftContinuationCatchDsl[LeftDomain, RightDomain, Value]( - implicit leftCatchDsl: CatchDsl[LeftDomain, LeftDomain, Value]) - : CatchDsl[LeftDomain !! Value, LeftDomain !! Nothing, Value] = { - new LiftContinuationCatchDsl - } -} - -object Catch extends LowPriorityCatch0 { - - type DslCatch[InnerDomain, OuterDomain, Value] = Dsl[Catch[InnerDomain, Value], OuterDomain, Value] - - @implicitNotFound("Use Dsl[Catch[...], ...] as implicit parameters instead of CatchDsl[...]") - private[dsl] trait CatchDsl[InnerDomain, OuterDomain, Value] extends DslCatch[InnerDomain, OuterDomain, Value] { - - def tryCatch(block: InnerDomain !! Value, - catcher: Catcher[InnerDomain !! Value], - handler: Value => OuterDomain): OuterDomain - - @inline final def cpsApply(keyword: Catch[InnerDomain, Value], handler: Value => OuterDomain): OuterDomain = { - tryCatch(keyword.block, keyword.catcher, handler) - } - } - - @inline - def tryCatch[InnerDomain, OuterDomain, Value](finalizer: Value => OuterDomain)( - implicit catchDsl: DslCatch[InnerDomain, OuterDomain, Value]) - : (InnerDomain !! Value, Catcher[InnerDomain !! Value]) => OuterDomain = { - (block: InnerDomain !! Value, catcher: Catcher[InnerDomain !! Value]) => - catchDsl.cpsApply(Catch(block, catcher), finalizer) - } - - @deprecated("Use Dsl[Catch[...], ...] as implicit parameters instead of CatchDsl[...]", "Dsl.scala 1.2.0") - private[Catch] def tryCatch[InnerDomain, OuterDomain, Value](finalizer: Value => OuterDomain)( - implicit catchDsl: CatchDsl[InnerDomain, OuterDomain, Value]) = { - (block: InnerDomain !! Value, catcher: Catcher[InnerDomain !! Value]) => - catchDsl.tryCatch(block, catcher, finalizer) - } - - implicit def futureCatchDsl[InnerValue, OuterValue]( - implicit executionContext: ExecutionContext): CatchDsl[Future[InnerValue], Future[OuterValue], InnerValue] = - new CatchDsl[Future[InnerValue], Future[OuterValue], InnerValue] { - def tryCatch(block: Future[InnerValue] !! InnerValue, - catcher: Catcher[Future[InnerValue] !! InnerValue], - handler: InnerValue => Future[OuterValue]): Future[OuterValue] = { - val fa = Future(block).flatMap(_(Future.successful)) - - val protectedFa = fa.recoverWith(catcher.andThen { recovered => - try { - recovered(Future.successful) - } catch { - case NonFatal(e) => - Future.failed(e) - } - }) - protectedFa.flatMap { outerValue => - try { - handler(outerValue) - } catch { - case NonFatal(e) => - Future.failed(e) - } - } - } - } - - implicit def throwableCatchDsl[LeftDomain, Value]( - implicit shiftDsl: Dsl[Shift[LeftDomain, Throwable], LeftDomain, Throwable]) - : CatchDsl[LeftDomain !! Throwable, LeftDomain !! Throwable, Value] = - new CatchDsl[LeftDomain !! Throwable, LeftDomain !! Throwable, Value] { - @inline - def tryCatch(block: LeftDomain !! Throwable !! Value, - catcher: Catcher[LeftDomain !! Throwable !! Value], - handler: Value => LeftDomain !! Throwable): LeftDomain !! Throwable = { - new (LeftDomain !! Throwable) { - def apply(outerFailureHandler: Throwable => LeftDomain): LeftDomain = { - - def recover(e: Throwable): LeftDomain = { - e match { - case catcher.extract(recovered) => - val outerContinuation = try { - recovered(handler) - } catch { - case NonFatal(e) => - return outerFailureHandler(e) - } - outerContinuation(outerFailureHandler) - case e => - outerFailureHandler(e) - } - } - - val protectedContinuation = try { - block { value => - new (LeftDomain !! Throwable) { - def apply(ignored: Throwable => LeftDomain): LeftDomain = { - val rest = try { - handler(value) - } catch { - case NonFatal(e) => - return outerFailureHandler(e) - } - rest(outerFailureHandler) - } - } - } - } catch { - case NonFatal(e) => - return recover(e) - } - shiftDsl.cpsApply(protectedContinuation, recover) - } - - } - - } - } - - private[keywords] class LiftContinuationCatchDsl[LeftDomain, RightDomain, Value]( - implicit leftCatchDsl: DslCatch[LeftDomain, LeftDomain, Value]) - extends CatchDsl[LeftDomain !! Value, LeftDomain !! RightDomain, Value] { - def tryCatch(block: LeftDomain !! Value !! Value, - catcher: Catcher[LeftDomain !! Value !! Value], - handler: Value => LeftDomain !! RightDomain): LeftDomain !! RightDomain = { outerHandler => - leftCatchDsl.cpsApply( - Catch( - block = block(Continuation.now), - catcher = { - case catcher.extract(recoveredValueContinuation) => - recoveredValueContinuation(Continuation.now) - } - ), - handler = { value: Value => - handler(value)(outerHandler) - } - ) - } - } - - private[keywords] class LiftFunction1CatchDsl[InnerDomain, OuterDomain, State, Value]( - implicit leftCatchDsl: DslCatch[InnerDomain, OuterDomain, Value]) - extends CatchDsl[State => InnerDomain, State => OuterDomain, Value] { - def tryCatch(block: (State => InnerDomain) !! Value, - catcher: Catcher[(State => InnerDomain) !! Value], - handler: Value => State => OuterDomain): State => OuterDomain = { state => - leftCatchDsl.cpsApply( - Catch( - block = { (continue: Value => InnerDomain) => - block { value: Value => _ => - continue(value) - }(state) - }, - catcher = { - case catcher.extract(recoveredValueContinuation) => - continue => - recoveredValueContinuation { value: Value => _ => - continue(value) - }(state) - } - ), - handler = handler(_)(state) - ) - } - } -} diff --git a/keywords-Continue/.js/build.sbt b/keywords-Continue/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Continue/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Continue/.jvm/build.sbt b/keywords-Continue/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Continue/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Continue/build.sbt b/keywords-Continue/build.sbt deleted file mode 100644 index a72b81d7..00000000 --- a/keywords-Continue/build.sbt +++ /dev/null @@ -1,21 +0,0 @@ -enablePlugins(Example) - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test - -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } -} - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } -} diff --git a/keywords-Continue/src/main/scala/com/thoughtworks/dsl/keywords/Continue.scala b/keywords-Continue/src/main/scala/com/thoughtworks/dsl/keywords/Continue.scala deleted file mode 100644 index c0eb6b29..00000000 --- a/keywords-Continue/src/main/scala/com/thoughtworks/dsl/keywords/Continue.scala +++ /dev/null @@ -1,90 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Keyword} -import com.thoughtworks.enableMembersIf - -import scala.language.implicitConversions -import scala.language.higherKinds -import scala.util.control.TailCalls -import scala.util.control.TailCalls.TailRec -import scala.collection._ - -/** The base type of [[Continue$ Continue]] keyword. - * - * @see The [[Continue$ Continue]] object, which is the only instance of this [[Continue]] class. - */ -sealed class Continue extends Keyword[Continue, Nothing] - -/** A keyword to skip the current iteration in a collection comprehension block. - * - * @note This [[Continue$ Continue]] keyword is usually used with [[Each]], to skip an element in the loop. - * @see [[Each]] for creating collection comprehensions. - * @example [[Each]] and [[Continue$ Continue]] can be used to calculate composite numbers and prime numbers. - * - * {{{ - * def compositeNumbersBelow(maxNumber: Int) = collection.immutable.HashSet { - * val factor = !Each(2 until math.ceil(math.sqrt(maxNumber)).toInt) - * !Each(2 * factor until maxNumber by factor) - * } - * - * compositeNumbersBelow(13) should be(Set(4, 6, 8, 9, 10, 12)) - * - * def primeNumbersBelow(maxNumber: Int) = Seq { - * val compositeNumbers = compositeNumbersBelow(maxNumber) - * val i = !Each(2 until maxNumber) - * if (compositeNumbers(i)) !Continue - * i - * } - * - * primeNumbersBelow(13) should be(Array(2, 3, 5, 7, 11)) - * }}} - * @author 杨博 (Yang Bo) - */ -case object Continue extends Continue with Keyword[Continue, Nothing] { - - implicit def continueUnitDsl[Value]: Dsl[Continue, Unit, Value] = new Dsl[Continue, Unit, Value] { - def cpsApply(keyword: Continue, handler: Value => Unit): Unit = () - } - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$""")) - private[dsl] object Scala211Or212 { - type Factory[-A, +C] = scala.collection.generic.CanBuildFrom[Nothing, A, C] - - @inline - def empty[A, C](implicit factory: Factory[A, C]): C = { - factory().result() - } - - } - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.13\..*$""")) - private[dsl] object Scala213 { - - @inline - def empty[A, C](implicit factory: Factory[A, C]): C = { - factory.newBuilder.result() - } - - } - - import Scala211Or212._ - import Scala213._ - - implicit def collectionContinueDsl[Value, Element, Collection[_]]( - implicit factory: Factory[Element, Collection[Element]]): Dsl[Continue, Collection[Element], Value] = - new Dsl[Continue, Collection[Element], Value] { - def cpsApply(keyword: Continue, handler: Value => Collection[Element]): Collection[Element] = { - empty[Element, Collection[Element]] - } - } - - implicit def OptionContinueDsl[Value, Element]( - implicit factory: Factory[Element, Option[Element]]): Dsl[Continue, Option[Element], Value] = - new Dsl[Continue, Option[Element], Value] { - def cpsApply(keyword: Continue, handler: Value => Option[Element]): Option[Element] = { - None - } - } - -} diff --git a/keywords-Each/.js/build.sbt b/keywords-Each/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Each/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Each/.jvm/build.sbt b/keywords-Each/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Each/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Each/build.sbt b/keywords-Each/build.sbt deleted file mode 100644 index 6a73059a..00000000 --- a/keywords-Each/build.sbt +++ /dev/null @@ -1,33 +0,0 @@ -enablePlugins(Example) - -addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7") - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test - -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Seq("–Xexperimental") - case _ => Seq.empty - } -} - -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } -} - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } -} - -libraryDependencies += "com.thoughtworks.enableIf" %% "enableif" % "1.1.6" - diff --git a/keywords-Each/src/main/scala/com/thoughtworks/dsl/keywords/Each.scala b/keywords-Each/src/main/scala/com/thoughtworks/dsl/keywords/Each.scala deleted file mode 100644 index 5657e1ca..00000000 --- a/keywords-Each/src/main/scala/com/thoughtworks/dsl/keywords/Each.scala +++ /dev/null @@ -1,111 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.enableMembersIf -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Keyword} - -import scala.collection._ -import scala.language.implicitConversions -import Shift.implicitShift - -import scala.collection.mutable.Builder - -/** - * Iterates though each element in [[elements]]. - * @author 杨博 (Yang Bo) - * - * @example [[Each]] keywords can be used to calculate cartesian product. - * - * {{{ - * def cartesianProduct = List(!Each(Array(1, 2, 3)) * !Each(Vector(1, 10, 100, 1000))) - * cartesianProduct should be(List(1, 10, 100, 1000, 2, 20, 200, 2000, 3, 30, 300, 3000)) - * }}} - * @see [[comprehension]] if you want to use traditional `for` comprehension instead of !-notation. - */ -final case class Each[Element](elements: Traversable[Element]) extends Keyword[Each[Element], Element] -object Each { - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$""")) - private[Each] object Scala211Or212 { - type Factory[-A, +C] = scala.collection.generic.CanBuildFrom[Nothing, A, C] - - @inline - def flatMapBreakOut[Element, Domain, DomainElement]( - fa: Traversable[Element], - f: Element => GenTraversableOnce[DomainElement])(implicit factory: Factory[DomainElement, Domain]): Domain = { - fa.flatMap(f)(collection.breakOut(factory)) - } - - @inline - def newBuilder[A, C](implicit factory: Factory[A, C]): Builder[A, C] = { - factory() - } - - } - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.13\..*$""")) - private[Each] object Scala213 { - - @inline - def flatMapBreakOut[Element, Domain, DomainElement]( - fa: Traversable[Element], - f: Element => GenTraversableOnce[DomainElement])(implicit factory: Factory[DomainElement, Domain]): Domain = { - factory.fromSpecific(new View.FlatMap(fa, f)) - } - - @inline - def newBuilder[A, C](implicit factory: Factory[A, C]): Builder[A, C] = { - factory.newBuilder - } - - } - - import Scala211Or212._ - import Scala213._ - - implicit def implicitEach[Element](elements: Traversable[Element]): Each[Element] = Each[Element](elements) - - implicit def eachDsl[Element, Domain, DomainElement]( - implicit thatIsTraversableOnce: (Element => Domain) => (Element => GenTraversableOnce[DomainElement]), - factory: Factory[DomainElement, Domain] - ): Dsl[Each[Element], Domain, Element] = - new Dsl[Each[Element], Domain, Element] { - def cpsApply(keyword: Each[Element], handler: Element => Domain): Domain = { - flatMapBreakOut(keyword.elements, handler) - } - } - - private[dsl] def foreachDsl[Element]: Dsl[Each[Element], Unit, Element] = - new Dsl[Each[Element], Unit, Element] { - def cpsApply(keyword: Each[Element], handler: Element => Unit): Unit = { - keyword.elements.foreach(handler) - } - } - - implicit def continuationEachDsl[Element, LeftDomain, RightDomain, DomainElement]( - implicit rightDomainIsTraversableOnce: (Element => LeftDomain !! RightDomain) => ( - Element => LeftDomain !! TraversableOnce[DomainElement]), - factory: Factory[DomainElement, RightDomain], - shiftDsl: Dsl[Shift[LeftDomain, TraversableOnce[DomainElement]], LeftDomain, TraversableOnce[DomainElement]] - ): Dsl[Each[Element], LeftDomain !! RightDomain, Element] = { - new Dsl[Each[Element], LeftDomain !! RightDomain, Element] { - def cpsApply(keyword: Each[Element], - handler0: Element => LeftDomain !! RightDomain): LeftDomain !! RightDomain = { - val i = keyword.elements.toIterator - val builder = newBuilder[DomainElement, RightDomain] - val handler = rightDomainIsTraversableOnce(handler0) - @inline - def loop(continue: RightDomain => LeftDomain): LeftDomain = { - if (i.hasNext) { - builder ++= !handler(i.next()) - loop(continue) - } else { - continue(builder.result()) - } - } - loop - } - } - } - -} diff --git a/keywords-Each/src/test/scala/com/thoughtworks/dsl/keywords/EachSpec.scala b/keywords-Each/src/test/scala/com/thoughtworks/dsl/keywords/EachSpec.scala deleted file mode 100644 index 73e04fc9..00000000 --- a/keywords-Each/src/test/scala/com/thoughtworks/dsl/keywords/EachSpec.scala +++ /dev/null @@ -1,154 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import org.scalatest.{FreeSpec, Matchers} -import com.thoughtworks.dsl.Dsl.{!!, reset} - -/** - * @author 杨博 (Yang Bo) - */ -class EachSpec extends FreeSpec with Matchers { - - "reset helper" - { - def resetReturnValue[A](a: A): A @reset = a - def forceParameter[A](a: => A @reset): A = a - - "@reset parameter" ignore { - val seq = 1 to 10 - def run(): Seq[Int] = Seq { - val plus100 = forceParameter(Seq { - !Each(seq) + 100 - }) - plus100.length should be(10) - !Each(plus100) - } - - val result = run() - result.length should be(10) - result.last should be(110) - } - - "@reset block" in { - val seq = 1 to 10 - def run(): Seq[Int] = Seq { - val plus100 = resetReturnValue { - Seq(!Each(seq) + 100) - } - plus100.length should be(10) - !Each(plus100) - } - - val result = run() - result.length should be(10) - result.last should be(110) - } - - "@reset result value" ignore { - val seq = 1 to 10 - def run(): Seq[Int] = Seq { - val plus100 = { - val element = !Each(seq) - resetReturnValue { - Seq(element + 100) - } - } - plus100.length should be(1) - !Each(plus100) - } - - val result = run() - result.length should be(10) - result.last should be(110) - } - - } - - "nested" - { - - "each" - { - "explicit @reset" in { - val seq = 1 to 10 - - def run(): Seq[Int] = Seq { - val plus100: Seq[Int] @reset = Seq { - !Each(seq) + 100 - } - plus100.length should be(1) - !Each(plus100) - } - - val result = run() - result.length should be(10) - result.last should be(110) - } - - "val" in { - val seq = 1 to 10 - - def run(): Seq[Int] = Seq { - val plus100 = Seq { - !Each(seq) + 100 - } - plus100.length should be(1) - !Each(plus100) - } - - val result = run() - result.length should be(10) - result.last should be(110) - } - - "def" in { - val seq = 1 to 10 - - def run(): Seq[Int] = Seq { - def plus100 = Seq { - !Each(seq) + 100 - } - plus100.length should be(10) - !Each(plus100) - } - - val result = run() - result.length should be(10) - result.last should be(110) - } - } - } - - "default parameter" in { - - def foo(s: Seq[Int] = Seq { - !Each(Seq(1, 2, 3)) + 100 - }) = s - - foo() should be(Seq(101, 102, 103)) - - } - - "val in class" in { - class C { - val ascii: Set[Int] = Set( - !Each(Seq(1, 2, 3, 2)) + 100 - ) - } - - (new C).ascii should be(Set(101, 102, 103)) - } - - "pattern matching" - { - "val" in { - def foo: Seq[String] = - Seq { - // OK - val s0 = !Each(Seq("a")) - - // not OK - val (s1, s2) = !Each(Seq(("b", "c"))) - s1 - }: @reset - - foo should be(Seq("b")) - } - } - -} diff --git a/keywords-FlatMap/.js/build.sbt b/keywords-FlatMap/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-FlatMap/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-FlatMap/.jvm/build.sbt b/keywords-FlatMap/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-FlatMap/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-FlatMap/build.sbt b/keywords-FlatMap/build.sbt deleted file mode 100644 index 128585bd..00000000 --- a/keywords-FlatMap/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Some("–Xexperimental") - case _ => None - } -} diff --git a/keywords-FlatMap/src/main/scala/com/thoughtworks/dsl/keywords/FlatMap.scala b/keywords-FlatMap/src/main/scala/com/thoughtworks/dsl/keywords/FlatMap.scala deleted file mode 100644 index 775202d4..00000000 --- a/keywords-FlatMap/src/main/scala/com/thoughtworks/dsl/keywords/FlatMap.scala +++ /dev/null @@ -1,24 +0,0 @@ -package com.thoughtworks.dsl.keywords -import com.thoughtworks.dsl.Dsl - -final case class FlatMap[UpstreamKeyword, UpstreamValue, NestedKeyword, NestedValue]( - upstream: UpstreamKeyword, - flatMapper: UpstreamValue => NestedKeyword) - extends Dsl.Keyword[FlatMap[UpstreamKeyword, UpstreamValue, NestedKeyword, NestedValue], NestedValue] - -object FlatMap { - implicit def flatMapDsl[UpstreamKeyword, UpstreamValue, Domain, NestedKeyword, NestedValue]( - implicit - upstreamDsl: Dsl[UpstreamKeyword, Domain, UpstreamValue], - nestedDsl: Dsl[NestedKeyword, Domain, NestedValue] - ): Dsl[FlatMap[UpstreamKeyword, UpstreamValue, NestedKeyword, NestedValue], Domain, NestedValue] = - new Dsl[FlatMap[UpstreamKeyword, UpstreamValue, NestedKeyword, NestedValue], Domain, NestedValue] { - def cpsApply(keyword: FlatMap[UpstreamKeyword, UpstreamValue, NestedKeyword, NestedValue], - handler: NestedValue => Domain) = { - val FlatMap(upstream, flatMapper) = keyword - upstreamDsl.cpsApply(upstream, { upstreamValue => - nestedDsl.cpsApply(flatMapper(upstreamValue), handler) - }) - } - } -} diff --git a/keywords-ForEach/.js/build.sbt b/keywords-ForEach/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-ForEach/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-ForEach/.jvm/build.sbt b/keywords-ForEach/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-ForEach/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-ForEach/build.sbt b/keywords-ForEach/build.sbt deleted file mode 100644 index 699fb9a6..00000000 --- a/keywords-ForEach/build.sbt +++ /dev/null @@ -1,12 +0,0 @@ -enablePlugins(Example) - -addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7") - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test - -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Seq("–Xexperimental") - case _ => Seq.empty - } -} diff --git a/keywords-ForEach/src/main/scala/com/thoughtworks/dsl/keywords/ForEach.scala b/keywords-ForEach/src/main/scala/com/thoughtworks/dsl/keywords/ForEach.scala deleted file mode 100644 index 164ff4d3..00000000 --- a/keywords-ForEach/src/main/scala/com/thoughtworks/dsl/keywords/ForEach.scala +++ /dev/null @@ -1,24 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Keyword} - -import scala.collection._ -import scala.language.implicitConversions - -import scala.collection.mutable.Builder - -/** Iterates though each element in [[elements]]. */ -final case class ForEach[Element](elements: Traversable[Element]) extends Keyword[ForEach[Element], Element] -object ForEach { - - implicit def implicitForEach[Element](elements: Traversable[Element]): ForEach[Element] = ForEach[Element](elements) - - implicit def foreachDsl[Element]: Dsl[ForEach[Element], Unit, Element] = - new Dsl[ForEach[Element], Unit, Element] { - def cpsApply(keyword: ForEach[Element], handler: Element => Unit): Unit = { - keyword.elements.foreach(handler) - } - } - -} diff --git a/keywords-ForEach/src/test/scala/com/thoughtworks/dsl/keywords/ForEachSpec.scala b/keywords-ForEach/src/test/scala/com/thoughtworks/dsl/keywords/ForEachSpec.scala deleted file mode 100644 index 9fec0034..00000000 --- a/keywords-ForEach/src/test/scala/com/thoughtworks/dsl/keywords/ForEachSpec.scala +++ /dev/null @@ -1,40 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import org.scalatest.{FreeSpec, Matchers} -import com.thoughtworks.dsl.Dsl.{!!, reset} - -/** - * @author 杨博 (Yang Bo) - */ -class ForEachSpec extends FreeSpec with Matchers { - - "foreach" - { - - "val" in { - val seq = 1 to 10 - - def run(): Unit = { - val plus100 = Seq { - !ForEach(seq) + 100 - } - plus100.length should be(1) - !ForEach(plus100) - } - - run() - } - "def" in { - val seq = 1 to 10 - - def run(): Unit = { - def plus100 = Seq { - !Each(seq) + 100 - } - plus100.length should be(10) - !ForEach(plus100) - } - - run() - } - } -} diff --git a/keywords-Fork/.js/build.sbt b/keywords-Fork/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Fork/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Fork/.jvm/build.sbt b/keywords-Fork/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Fork/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Fork/build.sbt b/keywords-Fork/build.sbt deleted file mode 100644 index 107cbaa8..00000000 --- a/keywords-Fork/build.sbt +++ /dev/null @@ -1,19 +0,0 @@ -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } -} - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } -} - -libraryDependencies += "com.thoughtworks.enableIf" %% "enableif" % "1.1.6" diff --git a/keywords-Fork/src/main/scala/com/thoughtworks/dsl/keywords/Fork.scala b/keywords-Fork/src/main/scala/com/thoughtworks/dsl/keywords/Fork.scala deleted file mode 100644 index 1013ac25..00000000 --- a/keywords-Fork/src/main/scala/com/thoughtworks/dsl/keywords/Fork.scala +++ /dev/null @@ -1,170 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import java.io.{PrintStream, PrintWriter} -import java.util.concurrent.atomic.AtomicInteger - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Keyword} -import com.thoughtworks.dsl.keywords.Catch.{CatchDsl, DslCatch} -import com.thoughtworks.enableMembersIf - -import scala.collection._ -import scala.collection.generic.CanBuildFrom -import scala.collection.mutable.Builder -import scala.language.implicitConversions - -final case class Fork[Element](elements: Traversable[Element]) extends AnyVal with Keyword[Fork[Element], Element] - -object Fork { - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$""")) - private[Fork] object Scala211Or212 { - type Factory[-A, +C] = scala.collection.generic.CanBuildFrom[Nothing, A, C] - - @inline - def flatMapBreakOut[Element, Domain, DomainElement]( - fa: Traversable[Element], - f: Element => GenTraversableOnce[DomainElement])(implicit factory: Factory[DomainElement, Domain]): Domain = { - fa.flatMap(f)(collection.breakOut(factory)) - } - - @inline - def newBuilder[A, C](implicit factory: Factory[A, C]): Builder[A, C] = { - factory() - } - - } - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.13\..*$""")) - private[Fork] object Scala213 { - - @inline - def flatMapBreakOut[Element, Domain, DomainElement]( - fa: Traversable[Element], - f: Element => GenTraversableOnce[DomainElement])(implicit factory: Factory[DomainElement, Domain]): Domain = { - factory.fromSpecific(new View.FlatMap(fa, f)) - } - - @inline - def newBuilder[A, C](implicit factory: Factory[A, C]): Builder[A, C] = { - factory.newBuilder - } - - } - - import Scala211Or212._ - import Scala213._ - - implicit def implicitFork[Element](elements: Traversable[Element]): Fork[Element] = Fork[Element](elements) - - final case class MultipleException(throwableSet: Set[Throwable]) - extends RuntimeException("Multiple exceptions found") { - override def toString: String = throwableSet.mkString("\n") - - override def printStackTrace(): Unit = { - for (throwable <- throwableSet) { - throwable.printStackTrace() - } - } - - override def printStackTrace(s: PrintStream): Unit = { - for (throwable <- throwableSet) { - throwable.printStackTrace(s) - } - } - - override def printStackTrace(s: PrintWriter): Unit = { - for (throwable <- throwableSet) { - throwable.printStackTrace(s) - } - } - - override def getStackTrace: Array[StackTraceElement] = synchronized { - super.getStackTrace match { - case null => - setStackTrace(throwableSet.view.flatMap(_.getStackTrace).toArray) - super.getStackTrace - case stackTrace => - stackTrace - } - } - - override def fillInStackTrace(): this.type = { - this - } - - } - - implicit def forkContinuationDsl[NarrowElement, LeftDomain, WidenElement, RightDomain]( - implicit eachDsl: Dsl[ForEach[NarrowElement], LeftDomain, NarrowElement], - booleanEachDsl: Dsl[ForEach[Boolean], LeftDomain, Boolean], - isTraversableOnce: RightDomain => TraversableOnce[WidenElement], - canBuildFrom: Factory[WidenElement, RightDomain], - continueDsl: Dsl[Continue, LeftDomain, Nothing], - catchDsl: DslCatch[LeftDomain, LeftDomain, Unit] - ): Dsl[Fork[NarrowElement], LeftDomain !! RightDomain, NarrowElement] = - new Dsl[Fork[NarrowElement], LeftDomain !! RightDomain, NarrowElement] { - def cpsApply(fork: Fork[NarrowElement], - mapper: NarrowElement => LeftDomain !! RightDomain): LeftDomain !! RightDomain = _ { - val builder: mutable.Builder[WidenElement, RightDomain] = newBuilder[WidenElement, RightDomain] - val exceptionBuilder = Set.newBuilder[Throwable] - val counter = new AtomicInteger(1) - if (!ForEach(Seq(true, false))) { - val element = !ForEach(fork.elements) - counter.incrementAndGet() - try { - builder ++= !Shift(mapper(element)); - () - } catch { - case MultipleException(throwableSet) => - exceptionBuilder ++= throwableSet; - () - case e: Throwable => - exceptionBuilder += e; - () - } finally { - if (counter.decrementAndGet() > 0) { - !Continue - } - } - } else { - if (counter.decrementAndGet() > 0) { - !Continue - } - } - - val exceptions = exceptionBuilder.result() - if (exceptions.isEmpty) { - builder.result() - } else { - val i = exceptions.iterator - val firstException = i.next() - if (i.hasNext) { - throw MultipleException(exceptions) - } else { - throw firstException - } - } - } - } - - @deprecated("Use Dsl[Catch[...], ...] as implicit parameters instead of CatchDsl[...]", "Dsl.scala 1.2.0") - private[Fork] def forkContinuationDsl[NarrowElement, LeftDomain, WidenElement, RightDomain]( - implicit eachDsl: Dsl[ForEach[NarrowElement], LeftDomain, NarrowElement], - booleanEachDsl: Dsl[ForEach[Boolean], LeftDomain, Boolean], - isTraversableOnce: RightDomain => TraversableOnce[WidenElement], - canBuildFrom: Factory[WidenElement, RightDomain], - continueDsl: Dsl[Continue, LeftDomain, Nothing], - catchDsl: CatchDsl[LeftDomain, LeftDomain, Unit] - ): Dsl[Fork[NarrowElement], LeftDomain !! RightDomain, NarrowElement] = { - forkContinuationDsl( - eachDsl: Dsl[ForEach[NarrowElement], LeftDomain, NarrowElement], - booleanEachDsl: Dsl[ForEach[Boolean], LeftDomain, Boolean], - isTraversableOnce: RightDomain => TraversableOnce[WidenElement], - canBuildFrom: Factory[WidenElement, RightDomain], - continueDsl: Dsl[Continue, LeftDomain, Nothing], - catchDsl: DslCatch[LeftDomain, LeftDomain, Unit] - ) - } - -} diff --git a/keywords-Get/src/main/scala/com/thoughtworks/dsl/keywords/Get.scala b/keywords-Get/src/main/scala/com/thoughtworks/dsl/keywords/Get.scala deleted file mode 100644 index fda75807..00000000 --- a/keywords-Get/src/main/scala/com/thoughtworks/dsl/keywords/Get.scala +++ /dev/null @@ -1,19 +0,0 @@ -package com.thoughtworks.dsl.keywords -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.Keyword - -/** - * @see [[Put]] - * @author 杨博 (Yang Bo) - */ -final case class Get[S]() extends Keyword[Get[S], S] - -object Get { - - implicit def getDsl[S0, S <: S0, A] = new Dsl[Get[S0], S => A, S0] { - def cpsApply(keyword: Get[S0], handler: S0 => S => A): S => A = { b => - handler(b)(b) - } - } - -} diff --git a/keywords-Map/.js/build.sbt b/keywords-Map/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Map/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Map/.jvm/build.sbt b/keywords-Map/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Map/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Map/build.sbt b/keywords-Map/build.sbt deleted file mode 100644 index 128585bd..00000000 --- a/keywords-Map/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Some("–Xexperimental") - case _ => None - } -} diff --git a/keywords-Map/src/main/scala/com/thoughtworks/dsl/keywords/Map.scala b/keywords-Map/src/main/scala/com/thoughtworks/dsl/keywords/Map.scala deleted file mode 100644 index 8e9f35bf..00000000 --- a/keywords-Map/src/main/scala/com/thoughtworks/dsl/keywords/Map.scala +++ /dev/null @@ -1,17 +0,0 @@ -package com.thoughtworks.dsl.keywords -import com.thoughtworks.dsl.Dsl - -final case class Map[UpstreamKeyword, UpstreamValue, Value](upstream: UpstreamKeyword, mapper: UpstreamValue => Value) - extends Dsl.Keyword[Map[UpstreamKeyword, UpstreamValue, Value], Value] - -object Map { - implicit def mapDsl[UpstreamKeyword, UpstreamValue, Domain, Value]( - implicit upstreamDsl: Dsl[UpstreamKeyword, Domain, UpstreamValue] - ): Dsl[Map[UpstreamKeyword, UpstreamValue, Value], Domain, Value] = - new Dsl[Map[UpstreamKeyword, UpstreamValue, Value], Domain, Value] { - def cpsApply(keyword: Map[UpstreamKeyword, UpstreamValue, Value], handler: Value => Domain) = { - val Map(upstream, mapper) = keyword - upstreamDsl.cpsApply(upstream, mapper.andThen(handler)) - } - } -} diff --git a/keywords-Monadic/.js/build.sbt b/keywords-Monadic/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Monadic/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Monadic/.jvm/build.sbt b/keywords-Monadic/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Monadic/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Monadic/build.sbt b/keywords-Monadic/build.sbt deleted file mode 100644 index e69de29b..00000000 diff --git a/keywords-Monadic/src/main/scala/com/thoughtworks/dsl/keywords/Monadic.scala b/keywords-Monadic/src/main/scala/com/thoughtworks/dsl/keywords/Monadic.scala deleted file mode 100644 index 41625bc5..00000000 --- a/keywords-Monadic/src/main/scala/com/thoughtworks/dsl/keywords/Monadic.scala +++ /dev/null @@ -1,17 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl.Keyword -import scala.language.higherKinds -import scala.language.implicitConversions - -/** A keyword for extracting monadic value from the monadic expression [[fa]]. - * - * @see [[com.thoughtworks.dsl.domains.cats]] for using this [[Monadic]] keyword with [[cats.Monad]]. - * @see [[com.thoughtworks.dsl.domains.scalaz]] for using this [[Monadic]] keyword with [[scalaz.Monad]]. - * @todo [[Monadic]] should be a [[scala.AnyVal]] after [[https://github.com/scala/bug/issues/10595]] is resolved. - */ -final case class Monadic[F[_], A](fa: F[A]) extends Keyword[Monadic[F, A], A] - -object Monadic { - implicit def implicitMonadic[F[_], A](fa: F[A]): Monadic[F, A] = Monadic(fa) -} diff --git a/keywords-NoneSafe/src/main/scala/com/thoughtworks/dsl/keywords/NoneSafe.scala b/keywords-NoneSafe/src/main/scala/com/thoughtworks/dsl/keywords/NoneSafe.scala deleted file mode 100644 index 93389a2e..00000000 --- a/keywords-NoneSafe/src/main/scala/com/thoughtworks/dsl/keywords/NoneSafe.scala +++ /dev/null @@ -1,25 +0,0 @@ -package com.thoughtworks.dsl -package keywords -import com.thoughtworks.dsl.Dsl.Keyword -import scala.language.implicitConversions - -final case class NoneSafe[A](option: Option[A]) extends AnyVal with Keyword[NoneSafe[A], A] - -object NoneSafe { - - implicit def noneSafeDsl[A, Domain]( - implicit continueDsl: Dsl[Return[None.type], Domain, Nothing]): Dsl[NoneSafe[A], Domain, A] = - new Dsl[NoneSafe[A], Domain, A] { - def cpsApply(keyword: NoneSafe[A], handler: A => Domain): Domain = { - keyword.option match { - case None => - continueDsl.cpsApply(Return(None), identity) - case Some(a) => - handler(a) - } - } - } - - implicit def implicitNoneSafe[A](option: Option[A]): NoneSafe[A] = NoneSafe(option) - -} diff --git a/keywords-NullSafe/.jvm/build.sbt b/keywords-NullSafe/.jvm/build.sbt deleted file mode 100644 index 0391fa23..00000000 --- a/keywords-NullSafe/.jvm/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ -enablePlugins(Example) - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/keywords-NullSafe/src/main/scala/com/thoughtworks/dsl/keywords/NullSafe.scala b/keywords-NullSafe/src/main/scala/com/thoughtworks/dsl/keywords/NullSafe.scala deleted file mode 100644 index b7f865bd..00000000 --- a/keywords-NullSafe/src/main/scala/com/thoughtworks/dsl/keywords/NullSafe.scala +++ /dev/null @@ -1,177 +0,0 @@ -package com.thoughtworks.dsl.keywords -import com.thoughtworks.dsl.Dsl.{reset, shift} -import com.thoughtworks.dsl.keywords.NullSafe.? -import com.thoughtworks.dsl.keywords.NullSafe.NotNull - -import scala.language.implicitConversions -import scala.language.higherKinds -import scala.annotation.compileTimeOnly - -/** [[NullSafe]] is a keyword to perform `null` check. - * - * @example You can use [[NullSafe$.? ?]] annotation to represent a nullable value. - * - * {{{ - * import com.thoughtworks.dsl.keywords.NullSafe._ - * - * case class Tree(left: Tree @ $qmark = null, right: Tree @ $qmark = null, value: String @ $qmark = null) - * - * val root: Tree @ $qmark = Tree( - * left = Tree( - * left = Tree(value = "left-left"), - * right = Tree(value = "left-right") - * ), - * right = Tree(value = "right") - * ) - * }}} - * - * A normal `.` is not null safe, when selecting `left`, `right` or `value` on a `null` value. - * - * {{{ - * a[NullPointerException] should be thrownBy { - * root.right.left.right.value - * } - * }}} - * - * The above code throws an exception because `root.right.left` is `null`. - * - * The exception can be avoided by using [[?]] on a nullable value: - * - * {{{ - * root.?.right.?.left.?.right.?.value should be(null) - * }}} - * - * The entire expression will be `null` if one of [[?]] is performed on a `null` value. - * - *
- * - * The boundary of a null safe operator [[?]] is the nearest enclosing expression - * whose type is annotated as `@ ?`. - * - * {{{ - * ("Hello " + ("world " + root.?.right.?.left.?.value)) should be("Hello world null") - * ("Hello " + (("world " + root.?.right.?.left.?.value.?): @ $qmark)) should be("Hello null") - * (("Hello " + ("world " + root.?.right.?.left.?.value.?)): @ $qmark) should be(null) - * }}} - * - * @example The [[?]] operator usually works with Java libraries that may produce `null`. - * - * {{{ - * import com.thoughtworks.dsl.keywords.NullSafe._ - * - * val myMap = new java.util.HashMap[String, String](); - * ((myMap.get("key1").? + myMap.get("key2").?): @ $qmark) should be(null) - * }}} - * - * @note The [[?]] operator is only available on nullable values. - * - * A type is considered as nullable if it is a reference type, - * no matter it is annotated as `@ ?` or not. - * - * {{{ - * import com.thoughtworks.dsl.keywords.NullSafe._ - * - * val explicitNullable: String @ $qmark = null - * ((explicitNullable.? + " Doe") : @ $qmark) should be(null) - * }}} - * - * {{{ - * val implicitNullable: String = null - * ((implicitNullable.? + " Doe") : @ $qmark) should be(null) - * }}} - * - * A type is considered as not nullable if it is a value type. - * - * {{{ - * val implicitNotNullable: Int = 0 - * "(implicitNotNullable.? + 42) : @ $qmark" shouldNot compile - * }}} - * - * Alternatively, a type can be considered as not nullable - * by explicitly converting it to [[com.thoughtworks.dsl.keywords.NullSafe.NotNull[A]* NotNull]]. - * - * {{{ - * val explicitNotNullable: NotNull[String] = NotNull("John") - * """(explicitNotNullable.? + " Doe") : @ $qmark""" shouldNot compile - * }}} - * - * @see [[NoneSafe]] for similar checks on [[scala.Option]]s. - * @author 杨博 (Yang Bo) - * - * @define qmark ? - * - */ -final case class NullSafe[A <: AnyRef](nullable: A @ ?) extends AnyVal { - - @inline - final def cpsApply[Domain >: Null](handler: NotNull[A] => Domain @ ?): Domain @ ? = { - if (nullable == null) { - null - } else { - handler(NullSafe.OpaqueTypes.toNotNull(nullable)) - } - } - - @shift - @compileTimeOnly( - """This method requires the compiler plugin: `addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "latest.release")` and must only be called inside a code block annotated as `@reset`.""") - final def ? : NotNull[A] = { - throw new IllegalAccessException( - """This method requires the compiler plugin: `addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "latest.release")` and must only be called inside a code block annotated as `@reset`.""" - ) - } - -} - -object NullSafe { - - private[NullSafe] trait OpaqueTypes { - type NotNull[+A] <: A - - private[NullSafe] def toNotNull[A](a: A): NotNull[A] - } - - private[NullSafe] val OpaqueTypes: OpaqueTypes = new OpaqueTypes { - type NotNull[+A] = A - private[NullSafe] def toNotNull[A](a: A) = a - } - - /** - * @usecase type NotNull[+A] <: A - */ - type NotNull[+A] = OpaqueTypes.NotNull[A] - - /** Returns `a` if `a` is not `null`. - * - * @return `a` if `a` is not `null`. - * - * {{{ - * import com.thoughtworks.dsl.keywords.NullSafe._ - * - * val o = new AnyRef - * NotNull(o) should be(o) - * }}} - * - * @throws java.lang.NullPointerException if `a` is `null`. - * - * {{{ - * import com.thoughtworks.dsl.keywords.NullSafe._ - * a[NullPointerException] should be thrownBy { - * NotNull(null) - * } - * }}} - */ - def NotNull[A](a: A): NotNull[A] = { - if (a == null) { - throw new NullPointerException - } else { - OpaqueTypes.toNotNull(a) - } - } - - /** @template */ - type ? = reset - - implicit def implicitNullSafe[A <: AnyRef](nullable: A @ ?) = new NullSafe[A](nullable) - -} diff --git a/keywords-Put/.js/build.sbt b/keywords-Put/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Put/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Put/.jvm/build.sbt b/keywords-Put/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Put/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Put/build.sbt b/keywords-Put/build.sbt deleted file mode 100644 index 0391fa23..00000000 --- a/keywords-Put/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ -enablePlugins(Example) - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/keywords-Put/src/main/scala/com/thoughtworks/dsl/keywords/Put.scala b/keywords-Put/src/main/scala/com/thoughtworks/dsl/keywords/Put.scala deleted file mode 100644 index 042b2663..00000000 --- a/keywords-Put/src/main/scala/com/thoughtworks/dsl/keywords/Put.scala +++ /dev/null @@ -1,66 +0,0 @@ -package com.thoughtworks.dsl -package keywords -import com.thoughtworks.dsl.Dsl.Keyword - -/** [[Put]] is a [[Dsl.Keyword Keyword]] to replace the [[value]] of the current context. - * - * Purely functional programming languages usually do not support native first-class mutable variables. - * In those languages, mutable states can be implemented in state monads. - * - * [[Put]] and [[Get]] are the [[Dsl]]-based replacements of state monads. - * - * We use unary function as the domain of mutable state. - * The parameter of the unary function can be read from the [[Get]] keyword, and changed by the [[Put]] keyword. - * - * @example The following example creates a function that accepts a string parameter - * and returns the upper-cased last character of the parameter. - * - * {{{ - * def upperCasedLastCharacter: String => Char = { - * val initialValue = !Get[String]() - * !Put(initialValue.toUpperCase) - * - * val upperCased = !Get[String]() - * Function.const(upperCased.last) - * } - * }}} - * - * For example, given a string of `foo`, the upper-cased last character should be `O`. - * - * {{{ - * // Output: O - * upperCasedLastCharacter("foo") should be('O') - * }}} - * - * @example [[Put]] and [[Get]] support multiple states. - * - * The following code creates a formatter that [[Put]] parts of content into a `Vector[Any]` of string buffers. - * - * {{{ - * def formatter: Double => Int => Vector[Any] => String = { - * !Put(!Get[Vector[Any]] :+ "x=") - * !Put(!Get[Vector[Any]] :+ !Get[Double]) - * !Put(!Get[Vector[Any]] :+ ",y=") - * !Put(!Get[Vector[Any]] :+ !Get[Int]) - * - * !Return((!Get[Vector[Any]]).mkString) - * } - * - * formatter(0.5)(42)(Vector.empty) should be("x=0.5,y=42") - * }}} - * @see [[Get]] - * @author 杨博 (Yang Bo) - */ -final case class Put[S](value: S) extends AnyVal with Keyword[Put[S], Unit] - -object Put { - - implicit def putDsl[S0, S >: S0, A] = new Dsl[Put[S0], S => A, Unit] { - def cpsApply(keyword: Put[S0], handler: Unit => S => A): S => A = { - val newValue = keyword.value; - { oldValue => - handler(())(newValue) - } - } - } -} diff --git a/keywords-Return/.js/build.sbt b/keywords-Return/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Return/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Return/.jvm/build.sbt b/keywords-Return/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Return/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Return/build.sbt b/keywords-Return/build.sbt deleted file mode 100644 index 0391fa23..00000000 --- a/keywords-Return/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ -enablePlugins(Example) - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test diff --git a/keywords-Return/src/main/scala/com/thoughtworks/dsl/keywords/Return.scala b/keywords-Return/src/main/scala/com/thoughtworks/dsl/keywords/Return.scala deleted file mode 100644 index bd06dd18..00000000 --- a/keywords-Return/src/main/scala/com/thoughtworks/dsl/keywords/Return.scala +++ /dev/null @@ -1,68 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.Keyword - -import scala.language.implicitConversions - -/** A [[Dsl.Keyword]] to early return a lifted value from the enclosing function. - * - * @author 杨博 (Yang Bo) - * @example Suppose you are generating a random integer less than 100, - * whose first digit and second digit is different. - * A solution is generating integers in an infinite loop, - * and [[Return]] from the loop when the generated integer conforms with requirements. - * - * {{{ - * import scala.util.Random - * import scala.util.control.TailCalls - * import scala.util.control.TailCalls.TailRec - * def randomInt(): TailRec[Int] = { - * while (true) { - * val r = Random.nextInt(100) - * if (r % 10 != r / 10) { - * !Return(TailCalls.done(r)) - * } - * } - * throw new AssertionError("Unreachable code"); - * } - * - * val r = randomInt().result - * r should be < 100 - * r % 10 should not be r / 10 - * }}} - * - * @example Since this [[Return]] keyword can automatically lift the return type, - * `TailCalls.done` can be omitted. - * - * {{{ - * import scala.util.Random - * import scala.util.control.TailCalls - * import scala.util.control.TailCalls.TailRec - * def randomInt(): TailRec[Int] = { - * while (true) { - * val r = Random.nextInt(100) - * if (r % 10 != r / 10) { - * !Return(r) - * } - * } - * throw new AssertionError("Unreachable code"); - * } - * - * val r = randomInt().result - * r should be < 100 - * r % 10 should not be r / 10 - * }}} - * - */ -final case class Return[ReturnValue](returnValue: ReturnValue) extends AnyVal with Keyword[Return[ReturnValue], Nothing] - -object Return { - - implicit def returnDsl[ReturnValue, Domain >: ReturnValue]: Dsl[Return[ReturnValue], Domain, Nothing] = - new Dsl[Return[ReturnValue], Domain, Nothing] { - def cpsApply(keyword: Return[ReturnValue], handler: Nothing => Domain): Domain = { - keyword.returnValue - } - } -} diff --git a/keywords-Return/src/test/scala/com/thoughtworks/dsl/keywords/ReturnSpec.scala b/keywords-Return/src/test/scala/com/thoughtworks/dsl/keywords/ReturnSpec.scala deleted file mode 100644 index 84ed63ec..00000000 --- a/keywords-Return/src/test/scala/com/thoughtworks/dsl/keywords/ReturnSpec.scala +++ /dev/null @@ -1,59 +0,0 @@ -package com.thoughtworks.dsl.keywords -import org.scalatest.{FreeSpec, Matchers} -import com.thoughtworks.dsl.Dsl.!! - -/** - * @author 杨博 (Yang Bo) - */ -final class ReturnSpec extends FreeSpec with Matchers { - - "return a Stream" in { - def stream: Stream[Int] = !Return[Int](1) - stream should be(Stream(1)) - } - - "return the left domain" in { - def continuation: Int !! String = !Return(42) - - continuation { s => - throw new AssertionError(s) - } should be(42) - } - - "return the right domain" in { - def continuation: Int !! String = !Return("right value") - - continuation { s => - s should be("right value") - 43 - } should be(43) - } - - "return the middle domain" - { - - "as the return value" in { - def continuation: Int !! Double !! String = !Return(1.23) - - continuation { s => - throw new AssertionError(s) - } { d => - d should be(1.23) - 43 - } should be(43) - } - - "then the throw expression will not be executed" in { - def continuation: Int !! Double !! String = { - throw !Return(1.23) - } - - continuation { s => - throw new AssertionError(s) - } { d => - d should be(1.23) - 43 - } should be(43) - } - } - -} diff --git a/keywords-Shift/.js/build.sbt b/keywords-Shift/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Shift/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Shift/.jvm/build.sbt b/keywords-Shift/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Shift/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Shift/build.sbt b/keywords-Shift/build.sbt deleted file mode 100644 index 128585bd..00000000 --- a/keywords-Shift/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Some("–Xexperimental") - case _ => None - } -} diff --git a/keywords-Shift/src/main/scala/com/thoughtworks/dsl/keywords/Shift.scala b/keywords-Shift/src/main/scala/com/thoughtworks/dsl/keywords/Shift.scala deleted file mode 100644 index 9d5728aa..00000000 --- a/keywords-Shift/src/main/scala/com/thoughtworks/dsl/keywords/Shift.scala +++ /dev/null @@ -1,136 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Continuation, Keyword} -import com.thoughtworks.dsl.keywords.Shift.{SameDomainStackSafeShiftDsl, StackSafeShiftDsl} - -import scala.annotation.tailrec -import scala.language.implicitConversions -import scala.util.control.{NonFatal, TailCalls} -import scala.util.control.TailCalls.TailRec - -/** - * @author 杨博 (Yang Bo) - */ -final case class Shift[Domain, Value](continuation: Domain !! Value) - extends AnyVal - with Keyword[Shift[Domain, Value], Value] - -private[keywords] trait LowPriorityShift1 { - - @inline - implicit def stackUnsafeShiftDsl[Domain, Value]: Dsl[Shift[Domain, Value], Domain, Value] = - new Dsl[Shift[Domain, Value], Domain, Value] { - def cpsApply(shift: Shift[Domain, Value], handler: Value => Domain) = - shift.continuation(handler) - } - -} - -private[keywords] trait LowPriorityShift0 extends LowPriorityShift1 { - - @inline - implicit def stackSafeShiftDsl[Domain, NewDomain, Value]( - implicit stackSafeShiftDsl: StackSafeShiftDsl[Domain, NewDomain, Value]) - : Dsl[Shift[Domain, Value], NewDomain, Value] = { - stackSafeShiftDsl - } - -} - -object Shift extends LowPriorityShift0 { - - trait StackSafeShiftDsl[Domain, NewDomain, Value] extends Dsl[Shift[Domain, Value], NewDomain, Value] - - private type SameDomainStackSafeShiftDsl[Domain, Value] = StackSafeShiftDsl[Domain, Domain, Value] - - @inline - implicit def implicitShift[Domain, Value](fa: Domain !! Value): Shift[Domain, Value] = new Shift[Domain, Value](fa) - - private def shiftTailRec[R, Value](continuation: TailRec[R] !! Value, handler: Value => TailRec[R]) = { - continuation { a => - val handler1 = handler - TailCalls.tailcall(handler1(a)) - } - } - - @inline - implicit def tailRecShiftDsl[R, Value]: SameDomainStackSafeShiftDsl[TailRec[R], Value] = - new SameDomainStackSafeShiftDsl[TailRec[R], Value] { - def cpsApply(keyword: Shift[TailRec[R], Value], handler: Value => TailRec[R]): TailRec[R] = { - shiftTailRec(keyword.continuation, handler) - } - } - - private abstract class TrampolineContinuation[LeftDomain] extends (LeftDomain !! Throwable) { - protected def step(): LeftDomain !! Throwable - - @tailrec - private final def last(): LeftDomain !! Throwable = { - step() match { - case trampoline: TrampolineContinuation[LeftDomain] => - trampoline.last() - case notTrampoline => - notTrampoline - } - } - - final def apply(handler: Throwable => LeftDomain): LeftDomain = { - val protectedContinuation: LeftDomain !! Throwable = try { - last() - } catch { - case NonFatal(e) => - return handler(e) - } - protectedContinuation(handler) - } - } - - private def suspend[LeftDomain, Value]( - continuation: LeftDomain !! Throwable !! Value, - handler: Value => LeftDomain !! Throwable): TrampolineContinuation[LeftDomain] = - new TrampolineContinuation[LeftDomain] { - protected def step() = continuation(handler) - } - - @inline - implicit def stackSafeThrowableShiftDsl[LeftDomain, Value] = - new SameDomainStackSafeShiftDsl[LeftDomain !! Throwable, Value] { - - def cpsApply(keyword: Shift[LeftDomain !! Throwable, Value], - handler: Value => LeftDomain !! Throwable): !![LeftDomain, Throwable] = - suspend(keyword.continuation, handler) - } - - private def flatMapTrampoline[LeftDomain, RightDomain, Value]( - handler: Value => LeftDomain !! Throwable !! RightDomain, - value: Value, - continue: RightDomain => LeftDomain !! Throwable): TrampolineContinuation[LeftDomain] = - new TrampolineContinuation[LeftDomain] { - protected def step() = { - handler(value)(continue) - } - } - - private def taskFlatMap[LeftDomain, RightDomain, Value]( - task: LeftDomain !! Throwable !! Value, - handler0: Value => LeftDomain !! Throwable !! RightDomain): LeftDomain !! Throwable !! RightDomain = { - continue0 => - val handler1 = handler0 - task { value => - val handler = handler1 - val continue = continue0 - flatMapTrampoline(handler, value, continue) - } - } - - @inline - implicit def taskStackSafeShiftDsl[LeftDomain, RightDomain, Value] = - new StackSafeShiftDsl[LeftDomain !! Throwable, LeftDomain !! Throwable !! RightDomain, Value] { - def cpsApply( - keyword: Shift[!![LeftDomain, Throwable], Value], - handler: Value => !![!![LeftDomain, Throwable], RightDomain]): !![!![LeftDomain, Throwable], RightDomain] = - taskFlatMap(keyword.continuation, handler) - } - -} diff --git a/keywords-Using/.js/build.sbt b/keywords-Using/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Using/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Using/.jvm/build.sbt b/keywords-Using/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Using/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Using/build.sbt b/keywords-Using/build.sbt deleted file mode 100644 index 2d4a4864..00000000 --- a/keywords-Using/build.sbt +++ /dev/null @@ -1,21 +0,0 @@ -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test - -enablePlugins(Example) - -import meta._ -exampleSuperTypes := exampleSuperTypes.value.map { - case ctor"_root_.org.scalatest.FreeSpec" => - ctor"_root_.org.scalatest.AsyncFreeSpec" - case otherTrait => - otherTrait -} - -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers < Seq(2L, 12L)) { - // Enable SAM types for Scala 2.11 - Some("-Xexperimental") - } else { - None - } -} diff --git a/keywords-Using/src/main/scala/com/thoughtworks/dsl/keywords/Using.scala b/keywords-Using/src/main/scala/com/thoughtworks/dsl/keywords/Using.scala deleted file mode 100644 index 584b839f..00000000 --- a/keywords-Using/src/main/scala/com/thoughtworks/dsl/keywords/Using.scala +++ /dev/null @@ -1,125 +0,0 @@ -package com.thoughtworks.dsl -package keywords - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.{!!, Keyword} -import com.thoughtworks.dsl.keywords.Catch.{CatchDsl, DslCatch} - -import scala.concurrent.{ExecutionContext, Future} -import scala.language.implicitConversions -import scala.util.control.NonFatal - -/** This [[Using]] keyword automatically manage resources in [[scala.concurrent.Future]], [[domains.task.Task]], - * and other asynchrounous domains derived from `Future` or `Task`. - * - * @author 杨博 (Yang Bo) - * @see [[dsl]] for usage of this [[Using]] keyword in continuations - */ -final case class Using[R <: AutoCloseable](open: () => R) extends AnyVal with Keyword[Using[R], R] - -object Using { - - implicit def implicitUsing[R <: AutoCloseable](r: => R): Using[R] = Using[R](r _) - - trait ScopeExitHandler extends AutoCloseable - - /** Returns a [[Using]] keyword to execute a [[ScopeExitHandler]] when exiting the nearest enclosing scope - * that is annotated as [[Dsl.reset @reset]], - * (or the nearest enclosing function if [[compilerplugins.ResetEverywhere]] is enabled). - * - * @note This method is similar to [[apply]], - * except the parameter type is changed from a generic `R` to the SAM type [[ScopeExitHandler]], - * which allows for function literal expressions - * in Scala 2.12+ or Scala 2.11 with `-Xexperimental` compiler option. - * - * @example The following function will perform `n *= 2` after `n += 20`: - * - * {{{ - * import scala.concurrent.Future - * import com.thoughtworks.dsl.keywords.Using.scopeExit - * var n = 1 - * def multiplicationAfterAddition = Future { - * !scopeExit { () => - * n *= 2 - * } - * n += 20 - * } - * }}} - * - * Therefore, the final value of `n` should be `(1 + 20) * 2 = 42`. - * - * {{{ - * multiplicationAfterAddition.map { _ => - * n should be(42) - * } - * }}} - * - */ - def scopeExit(r: => ScopeExitHandler) = new Using(r _) - - def apply[R <: AutoCloseable](r: => R)( - implicit dummyImplicit: DummyImplicit = DummyImplicit.dummyImplicit): Using[R] = new Using(r _) - - implicit def throwableContinuationUsingDsl[Domain, Value, R <: AutoCloseable]( - implicit catchDsl: DslCatch[Domain, Domain, Value], - shiftDsl: Dsl[Shift[Domain, Value], Domain, Value] - ): Dsl[Using[R], Domain !! Value, R] = { (keyword: Using[R], handler: R => Domain !! Value) => - _ { - val r = keyword.open() - try { - !Shift(handler(r)) - } finally { - r.close() - } - } - } - - @deprecated("Use Dsl[Catch[...], ...] as implicit parameters instead of CatchDsl[...]", "Dsl.scala 1.2.0") - private[Using] def throwableContinuationUsingDsl[Domain, Value, R <: AutoCloseable]( - implicit catchDsl: CatchDsl[Domain, Domain, Value], - shiftDsl: Dsl[Shift[Domain, Value], Domain, Value] - ): Dsl[Using[R], Domain !! Value, R] = { - throwableContinuationUsingDsl(catchDsl: DslCatch[Domain, Domain, Value], - shiftDsl: Dsl[Shift[Domain, Value], Domain, Value]) - } - - implicit def scalaFutureUsingDsl[R <: AutoCloseable, A](implicit executionContext: ExecutionContext) - : Dsl[Using[R], Future[A], R] = { (keyword: Using[R], handler: R => Future[A]) => - Future(keyword.open()).flatMap { r: R => - def onFailure(e: Throwable): Future[Nothing] = { - try { - r.close() - Future.failed(e) - } catch { - case NonFatal(e2) => - Future.failed(e2) - } - } - - def onSuccess(a: A): Future[A] = { - try { - r.close() - Future.successful(a) - } catch { - case NonFatal(e2) => - Future.failed(e2) - } - } - - def returnableBlock(): Future[A] = { - val fa: Future[A] = try { - handler(r) - } catch { - case NonFatal(e) => - return onFailure(e) - } - fa.recoverWith { - case NonFatal(e) => - onFailure(e) - } - .flatMap(onSuccess) - } - returnableBlock() - } - } -} diff --git a/keywords-WithFilter/.js/build.sbt b/keywords-WithFilter/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-WithFilter/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-WithFilter/.jvm/build.sbt b/keywords-WithFilter/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-WithFilter/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-WithFilter/build.sbt b/keywords-WithFilter/build.sbt deleted file mode 100644 index 128585bd..00000000 --- a/keywords-WithFilter/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Some("–Xexperimental") - case _ => None - } -} diff --git a/keywords-WithFilter/src/main/scala/com/thoughtworks/dsl/keywords/WithFilter.scala b/keywords-WithFilter/src/main/scala/com/thoughtworks/dsl/keywords/WithFilter.scala deleted file mode 100644 index 356a7cea..00000000 --- a/keywords-WithFilter/src/main/scala/com/thoughtworks/dsl/keywords/WithFilter.scala +++ /dev/null @@ -1,26 +0,0 @@ -package com.thoughtworks.dsl.keywords -import com.thoughtworks.dsl.Dsl - -final case class WithFilter[UpstreamKeyword, UpstreamValue](upstream: UpstreamKeyword, - condition: UpstreamValue => Boolean) - extends Dsl.Keyword[WithFilter[UpstreamKeyword, UpstreamValue], UpstreamValue] - -object WithFilter { - implicit def withFilterDsl[UpstreamKeyword, Domain, UpstreamValue]( - implicit - upstreamDsl: Dsl[UpstreamKeyword, Domain, UpstreamValue], - continueDsl: Dsl[Continue, Domain, Nothing] - ): Dsl[WithFilter[UpstreamKeyword, UpstreamValue], Domain, UpstreamValue] = - new Dsl[WithFilter[UpstreamKeyword, UpstreamValue], Domain, UpstreamValue] { - def cpsApply(keyword: WithFilter[UpstreamKeyword, UpstreamValue], handler: UpstreamValue => Domain) = { - val WithFilter(upstream, condition) = keyword - upstreamDsl.cpsApply(upstream, { upstreamValue => - if (condition(upstreamValue)) { - handler(upstreamValue) - } else { - continueDsl.cpsApply(Continue, identity) - } - }) - } - } -} diff --git a/keywords-Yield/.js/build.sbt b/keywords-Yield/.js/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Yield/.js/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Yield/.jvm/build.sbt b/keywords-Yield/.jvm/build.sbt deleted file mode 120000 index 0569694f..00000000 --- a/keywords-Yield/.jvm/build.sbt +++ /dev/null @@ -1 +0,0 @@ -../build.sbt \ No newline at end of file diff --git a/keywords-Yield/.jvm/jvm.sbt b/keywords-Yield/.jvm/jvm.sbt deleted file mode 100644 index a2c568f7..00000000 --- a/keywords-Yield/.jvm/jvm.sbt +++ /dev/null @@ -1,2 +0,0 @@ -enablePlugins(Example) - diff --git a/keywords-Yield/build.sbt b/keywords-Yield/build.sbt deleted file mode 100644 index b1e49767..00000000 --- a/keywords-Yield/build.sbt +++ /dev/null @@ -1,26 +0,0 @@ -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test - -scalacOptions in Compile ++= { - scalaBinaryVersion.value match { - case "2.11" => Seq("–Xexperimental") - case _ => Seq.empty - } -} - -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } -} - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } -} diff --git a/keywords-Yield/src/main/scala/com/thoughtworks/dsl/keywords/Yield.scala b/keywords-Yield/src/main/scala/com/thoughtworks/dsl/keywords/Yield.scala deleted file mode 100644 index 204e912d..00000000 --- a/keywords-Yield/src/main/scala/com/thoughtworks/dsl/keywords/Yield.scala +++ /dev/null @@ -1,213 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl -import com.thoughtworks.dsl.Dsl.Keyword -import com.thoughtworks.dsl.keywords.Yield.From -import com.thoughtworks.enableMembersIf - -import scala.collection._ -import scala.collection.generic.CanBuildFrom -import scala.concurrent.Future -import scala.language.implicitConversions -import scala.language.higherKinds - -/** - * @author 杨博 (Yang Bo) - * @example This `Yield` keyword must be put inside a function that returns `Seq[Element]` or `Seq[Element] !! ...`, - * or it will not compile. - * - * {{{ - * "def f(): Int = !Yield(1)" shouldNot compile - * }}} - * - * @example [[Yield]] keywords can be used together with other keywords. - * {{{ - * def gccFlagBuilder(sourceFile: String, includes: String*): Stream[String] = { - * !Yield("gcc") - * !Yield("-c") - * !Yield(sourceFile) - * val include = !Each(includes) - * !Yield("-I") - * !Yield(include) - * !Continue - * } - * - * gccFlagBuilder("main.c", "lib1/include", "lib2/include") should be(Stream("gcc", "-c", "main.c", "-I", "lib1/include", "-I", "lib2/include")) - * }}} - * @see [[comprehension]] if you want to use traditional `for` comprehension instead of !-notation. - */ -final case class Yield[Element](element: Element) extends AnyVal with Keyword[Yield[Element], Unit] - -private[keywords] trait LowPriorityYield2 { - - def apply[A](elements: A*) = { - From(elements) - } - - implicit def iteratorYieldFromDsl[A, FromCollection <: TraversableOnce[A]] - : Dsl[From[FromCollection], Iterator[A], Unit] = - new Dsl[From[FromCollection], Iterator[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Iterator[A]): Iterator[A] = { - keyword.elements.toIterator ++ generateTail(()) - } - } - - implicit def iteratorYieldDsl[A, B >: A]: Dsl[Yield[A], Iterator[B], Unit] = - new Dsl[Yield[A], Iterator[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => Iterator[B]): Iterator[B] = { - Iterator.single(keyword.element) ++ generateTail(()) - } - } - -} - -private[keywords] object YieldScalaVersions { - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$""")) - object Scala211Or212 { - - trait LowPriorityYield1 extends LowPriorityYield2 { - - implicit def seqViewYieldFromDsl[A, FromCollection <: Traversable[A], Coll1, Coll2]( - implicit canBuildFrom: CanBuildFrom[SeqView[A, Coll1], A, SeqView[A, Coll2]]) - : Dsl[From[FromCollection], SeqView[A, Coll1], Unit] = - new Dsl[From[FromCollection], SeqView[A, Coll1], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => SeqView[A, Coll1]): SeqView[A, Coll1] = { - (keyword.elements.toIterable ++: generateTail(()))(canBuildFrom).asInstanceOf[SeqView[A, Coll1]] - } - } - - implicit def seqViewYieldDsl[A, B >: A, Coll1, Coll2]( - implicit canBuildFrom: CanBuildFrom[scala.collection.SeqView[B, Coll1], B, SeqView[B, Coll2]]) - : Dsl[Yield[A], SeqView[B, Coll1], Unit] = - new Dsl[Yield[A], SeqView[B, Coll1], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => SeqView[B, Coll1]): SeqView[B, Coll1] = { - (keyword.element +: generateTail(()))(canBuildFrom).asInstanceOf[SeqView[B, Coll1]] - } - } - } - - trait LowPriorityYield0 extends LowPriorityYield1 { - - implicit def seqYieldFromDsl[A, FromCollection <: Iterable[A], Collection[X] <: SeqLike[X, Collection[X]]]( - implicit canBuildFrom: CanBuildFrom[Collection[A], A, Collection[A]]) - : Dsl[From[FromCollection], Collection[A], Unit] = - new Dsl[From[FromCollection], Collection[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Collection[A]): Collection[A] = { - keyword.elements ++: generateTail(()) - } - } - - implicit def seqYieldDsl[A, B >: A, Collection[X] <: SeqLike[X, Collection[X]]]( - implicit canBuildFrom: CanBuildFrom[Collection[B], B, Collection[B]]): Dsl[Yield[A], Collection[B], Unit] = - new Dsl[Yield[A], Collection[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => Collection[B]): Collection[B] = { - keyword.element +: generateTail(()) - } - } - - } - } - - @enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.13\..*$""")) - object Scala213 { - - trait LowPriorityYield1 extends LowPriorityYield2 { - - implicit def viewYieldFromDsl[A, FromCollection <: View.SomeIterableOps[A]] - : Dsl[From[FromCollection], View[A], Unit] = - new Dsl[From[FromCollection], View[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => View[A]): View[A] = { - new View.Concat(keyword.elements, generateTail(())) - } - } - - implicit def seqViewYieldDsl[A, B >: A]: Dsl[Yield[A], SeqView[B], Unit] = - new Dsl[Yield[A], SeqView[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => SeqView[B]): SeqView[B] = { - generateTail(()).prepended(keyword.element) - } - } - - implicit def indexedSeqViewYieldDsl[A, B >: A]: Dsl[Yield[A], IndexedSeqView[B], Unit] = - new Dsl[Yield[A], IndexedSeqView[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => IndexedSeqView[B]): IndexedSeqView[B] = { - generateTail(()).prepended(keyword.element) - } - } - - implicit def viewYieldDsl[A, B >: A]: Dsl[Yield[A], View[B], Unit] = - new Dsl[Yield[A], View[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => View[B]): View[B] = { - new View.Concat[B](new View.Single(keyword.element), generateTail(())) - } - } - } - - trait LowPriorityYield0 extends LowPriorityYield1 { - implicit def seqYieldFromDsl[A, - FromCollection <: View.SomeIterableOps[A], - Collection[X] <: SeqOps[X, Collection, Collection[X]]] - : Dsl[From[FromCollection], Collection[A], Unit] = - new Dsl[From[FromCollection], Collection[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Collection[A]): Collection[A] = { - keyword.elements.toIterable ++: generateTail(()) - } - } - - implicit def seqYieldDsl[A, B >: A, Collection[+X] <: SeqOps[X, Collection, Collection[X]]] - : Dsl[Yield[A], Collection[B], Unit] = - new Dsl[Yield[A], Collection[B], Unit] { - def cpsApply(keyword: Yield[A], generateTail: Unit => Collection[B]): Collection[B] = { - keyword.element +: generateTail(()) - } - } - - } - } - -} - -import YieldScalaVersions.Scala211Or212._ -import YieldScalaVersions.Scala213._ - -object Yield extends LowPriorityYield0 { - - final case class From[FromCollection <: TraversableOnce[_]](elements: FromCollection) - extends AnyVal - with Keyword[From[FromCollection], Unit] - - implicit def implicitYieldFrom[FromCollection <: TraversableOnce[_]](elements: FromCollection): From[FromCollection] = - From(elements) - - implicit def streamYieldFromDsl[A, FromCollection <: Iterable[A]]: Dsl[From[FromCollection], Stream[A], Unit] = - new Dsl[From[FromCollection], Stream[A], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Stream[A]): Stream[A] = { - keyword.elements.toStream.append(generateTail(())) - } - } - - implicit def futureStreamYieldFromDsl[A, FromCollection <: Iterable[A]] - : Dsl[From[FromCollection], Stream[Future[A]], Unit] = - new Dsl[From[FromCollection], Stream[Future[A]], Unit] { - def cpsApply(keyword: From[FromCollection], generateTail: Unit => Stream[Future[A]]): Stream[Future[A]] = { - keyword.elements.toStream.map(Future.successful).append(generateTail(())) - } - } - - implicit def implicitYield[Element](element: Element): Yield[Element] = Yield[Element](element) - - implicit def streamYieldDsl[Element, That >: Element]: Dsl[Yield[Element], Stream[That], Unit] = - new Dsl[Yield[Element], Stream[That], Unit] { - def cpsApply(keyword: Yield[Element], generateTail: Unit => Stream[That]): Stream[That] = { - new Stream.Cons(keyword.element, generateTail(())) - } - } - - implicit def futureStreamYieldDsl[Element, That >: Element]: Dsl[Yield[Element], Stream[Future[That]], Unit] = - new Dsl[Yield[Element], Stream[Future[That]], Unit] { - def cpsApply(keyword: Yield[Element], generateTail: Unit => Stream[Future[That]]): Stream[Future[That]] = { - new Stream.Cons(Future.successful(keyword.element), generateTail(())) - } - } -} diff --git a/keywords-Yield/src/test/scala/com/thoughtworks/dsl/keywords/YieldSpec.scala b/keywords-Yield/src/test/scala/com/thoughtworks/dsl/keywords/YieldSpec.scala deleted file mode 100644 index 915ba8ab..00000000 --- a/keywords-Yield/src/test/scala/com/thoughtworks/dsl/keywords/YieldSpec.scala +++ /dev/null @@ -1,385 +0,0 @@ -package com.thoughtworks.dsl.keywords - -import com.thoughtworks.dsl.Dsl.!! -import com.thoughtworks.enableMembersIf -import org.scalatest.{FreeSpec, Matchers} - -import scala.annotation.tailrec -import scala.collection.{LinearSeq, SeqView} -import scala.runtime.NonLocalReturnControl - -/** - * @author 杨博 (Yang Bo) - */ -class YieldSpec extends FreeSpec with Matchers { - - "Given a continuation that uses Yield and Each expressions" - { - - def asyncFunction: Stream[String] !! Unit = _ { - !Yield("Entering asyncFunction") - val subThreadId: Int = !Each(Seq(0, 1)) - !Yield(s"Fork sub-thread $subThreadId") - !Yield("Leaving asyncFunction") - } - - "When create a generator that contains Yield, Shift, and Each expressions" - { - - def generator: Stream[String] = { - !Yield("Entering generator") - val threadId = !Each(Seq(0, 1)) - !Yield(s"Fork thread $threadId") - !Shift(asyncFunction) - Stream("Leaving generator") - } - - "Then the generator should contains yield values" in { - generator should be( - Seq( - /**/ "Entering generator", - /****/ "Fork thread 0", - /******/ "Entering asyncFunction", - /********/ "Fork sub-thread 0", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator", - /********/ "Fork sub-thread 1", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator", - /****/ "Fork thread 1", - /******/ "Entering asyncFunction", - /********/ "Fork sub-thread 0", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator", - /********/ "Fork sub-thread 1", - /**********/ "Leaving asyncFunction", - /**********/ "Leaving generator" - )) - } - - } - - } - - "stream" - { - - def shouldCompile = { - !Yield("naked") - Stream.empty[String] - } - - "local method" in { - def generator: Stream[Int] = { - def id[A](a: A) = a - id(!Yield(100)) - Stream.empty[Int] - } - generator should be(Stream(100)) - } - - "yield from" in { - def generator: Stream[Int] = { - def id[A](a: A) = a - id(!Yield(100, 200)) - Stream.empty - } - generator should be(Stream(100, 200)) - } - - "local function" in { - def generator: Stream[Int] = { - def id[A](a: A) = a - (id[Unit] _)(!Yield(100)) - Stream.empty[Int] - } - generator should be(Stream(100)) - } - - "do/while" - { - "empty body" in { - def generator: Stream[Int] = { - do {} while ({ - !Yield(100) - false - }) - Stream.empty[Int] - } - generator should be(Stream(100)) - } - - "false" in { - def generator: Stream[Int] = { - do { - !Yield(100) - } while (false) - Stream.empty[Int] - } - generator should be(Stream(100)) - } - - "with var" in { - def generator: Stream[Int] = { - var i = 5 - do { - i -= { - !Yield(i) - 1 - } - } while ({ - !Yield(-i) - i > 0 - }) - Stream.empty[Int] - } - generator should be(Stream(5, -4, 4, -3, 3, -2, 2, -1, 1, 0)) - } - } - - "while" - { - "false" in { - def whileFalse: Stream[Int] = { - while (false) { - !Yield(100) - } - Stream.empty[Int] - } - - whileFalse should be(Stream.empty) - } - } - - "match/case" in { - - def loop(i: Int): Stream[Int] = { - i match { - case 100 => - Stream.empty - case _ => - !Yield(i) - loop(i + 1) - } - } - - loop(90) should be(Stream(90, 91, 92, 93, 94, 95, 96, 97, 98, 99)) - - } - - "recursive" in { - def loop(i: Int): Stream[Int] = { - if (i < 100) { - !Yield(i) - loop(i + 1) - } else { - Stream.empty - } - } - - loop(90) should be(Stream(90, 91, 92, 93, 94, 95, 96, 97, 98, 99)) - - } - - "Given a generator that contains conditional Yield" - { - def generator = { - if (false) { - !Yield(0) - } - if (true) { - !Yield(1) - } - if ({ !Yield(2); false }) { - !Yield(3) - } else { - !Yield(4) - } - Stream.empty[Int] - } - - "Then the generator should contains values in selected branches" in { - generator should be(Seq(1, 2, 4)) - } - - } - - "Given a continuation that uses Yield" - { - - def yield4243: Stream[Int] !! Unit = _ { - !Yield(42) - !Yield(43) - } - - "when create a generator that contains multiple Yield expression followed by a bang notation and a Stream.empty" - { - - def generator: Stream[Int] = { - !Yield(0) - !Shift(yield4243) - !Yield(1) - Stream.empty[Int] - } - - "Then the generator should contains yield values" in { - generator should be(Seq(0, 42, 43, 1)) - } - - } - - } - - "apply" in { - def generator: Stream[Int] = { - val f = { - !Yield(1) - - { (x: Int) => - -x - } - } - - val result = f({ - !Yield(2) - 42 - }) - Stream(result) - } - generator should be(Stream(1, 2, -42)) - } - - "return" in { - def generator: Stream[Int] = { - if (true) { - return { - !Yield(100) - Stream(42) - } - } - Stream.empty[Int] - } - - a[NonLocalReturnControl[Stream[Int]]] should be thrownBy generator.last - } - "partial function" - { - "empty" in { - Seq.empty[Any].flatMap { - case i: Int => - !Yield(100) - Stream(42) - } should be(empty) - } - - "flatMap" in { - Seq(100, 200).flatMap { - case i: Int => - !Yield(100) - Stream(42 + i) - } should be(Seq(100, 142, 100, 242)) - } - } - - "nested function call" - { - "call by value" in { - def nested() = { - "foo" +: !Yield("bar") +: Stream.empty[Any] - } - nested() should be(Stream("bar", "foo", ())) - } - "call by name" in { - def nested() = { - "foo" #:: !Yield("bar") #:: Stream.empty[Any] - } - nested() should be(Stream("bar", "foo", ())) - } - } - - } - - "view" - { - - def shouldCompile = { - !Yield("naked") - Vector.empty[String].view - } - - "local method" in { - def generator = { - def id[A](a: A) = a - id(!Yield(100)) - Seq.empty[Int].view - } - generator.toList should be(List(100)) - } - - @enableMembersIf(scala.util.Properties.versionNumberString.startsWith("2.11.")) - object Scala211 { - def ignoreInScala211(title: String)(f: => Any) = { - title ignore f - } - } - @enableMembersIf(!scala.util.Properties.versionNumberString.startsWith("2.11.")) - object Scala212And213 { - def ignoreInScala211(title: String)(f: => Any) = { - title in f - } - } - import Scala212And213._ - import Scala211._ - - ignoreInScala211("yield from") { - def generator = { - def id[A](a: A) = a - id(!Yield(100, 200)) - Seq.empty[Int].view ++ Nil - } - generator.toList should be(List(100, 200)) - } - - } - - "iterator" - { - - def shouldCompile: Iterator[String] = { - !Yield("naked") - Iterator.empty - } - - "local method" in { - def generator: Iterator[Int] = { - def id[A](a: A) = a - id(!Yield(100)) - Iterator.empty - } - generator.toList should be(List(100)) - } - - "yield from" in { - def generator: Iterator[Int] = { - def id[A](a: A) = a - id(!Yield(100, 200)) - Iterator.empty - } - generator.toList should be(List(100, 200)) - } - } - - "seq" - { - - def shouldCompile: LinearSeq[String] = { - !Yield("naked") - LinearSeq.empty[String] - } - - "local method" in { - def generator: LinearSeq[Int] = { - def id[A](a: A) = a - id(!Yield(100)) - LinearSeq.empty - } - generator should be(LinearSeq(100)) - } - - "yield from" in { - def generator: LinearSeq[Int] = { - def id[A](a: A) = a - id(!Yield(100, 200)) - LinearSeq.empty - } - generator should be(LinearSeq(100, 200)) - } - } -} diff --git a/package/build.sbt b/package/build.sbt deleted file mode 100644 index 76206fa3..00000000 --- a/package/build.sbt +++ /dev/null @@ -1,74 +0,0 @@ -enablePlugins(Example) - -libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.6-SNAP2" % Test - -publishArtifact := false - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq( - "org.scala-lang.plugins" %% "scala-continuations-library" % "1.0.3" % Optional, - "org.scala-lang.modules" %% "scala-async" % "0.9.7" % Optional, - "com.typesafe.akka" %% "akka-actor" % "2.5.14" % Optional, - "com.twitter" %% "algebird-core" % "0.13.4" % Optional, - "com.thoughtworks.binding" %% "binding" % "11.0.1" % Optional, - "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.1" % Optional, - "org.scalacheck" %% "scalacheck" % "1.14.0" % Optional, - "com.thoughtworks.each" %% "each" % "3.3.1" % Optional, - "com.lihaoyi" %% "sourcecode" % "0.1.4" % Optional, - "io.monix" %% "monix" % "2.3.3" % Optional, - "com.typesafe.akka" %% "akka-stream" % "2.5.14" % Optional, - "com.typesafe.akka" %% "akka-http" % "10.1.3" % Optional - ) - } -} - -sourceGenerators in Test := { - (sourceGenerators in Test).value.filterNot { sourceGenerator => - import Ordering.Implicits._ - VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L) && - sourceGenerator.info - .get(taskDefinitionKey) - .exists { scopedKey: ScopedKey[_] => - scopedKey.key == generateExample.key - } - } -} - -libraryDependencies += "org.scalaz" %% "scalaz-concurrent" % "7.2.26" - -import scala.meta._ - -exampleSuperTypes := exampleSuperTypes.value.filter { - case ctor"_root_.org.scalatest.FreeSpec" => - false - case _ => - true -} - -exampleSuperTypes := ctor"_root_.org.scalatest.AsyncFreeSpec" +: exampleSuperTypes.value -exampleSuperTypes += ctor"_root_.org.scalatest.Inside" -exampleSuperTypes += ctor"_root_.com.thoughtworks.dsl.MockPingPongServer" - -addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7") - -scalacOptions ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Seq("-Ymacro-annotations") - } else { - Nil - } -} - -libraryDependencies ++= { - import Ordering.Implicits._ - if (VersionNumber(scalaVersion.value).numbers >= Seq(2L, 13L)) { - Nil - } else { - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - } -} diff --git a/package/src/main/scala/com/thoughtworks/dsl/package.scala b/package/src/main/scala/com/thoughtworks/dsl/package.scala deleted file mode 100644 index 1c6b8b80..00000000 --- a/package/src/main/scala/com/thoughtworks/dsl/package.scala +++ /dev/null @@ -1,578 +0,0 @@ -package com.thoughtworks - -/** This project, '''Dsl.scala''', is a framework to create embedded '''D'''omain-'''S'''pecific '''L'''anguages. - * - * DSLs written in '''Dsl.scala''' are collaborative with others DSLs and Scala control flows. - * DSL users can create functions that contains interleaved DSLs implemented by different vendors, - * along with ordinary Scala control flows. - * - * We also provide some built-in DSLs for asynchronous programming, collection manipulation, - * and adapters to [[scalaz.Monad]] or [[cats.Monad]]. - * Those built-in DSLs can be used as a replacement of - * [[https://docs.scala-lang.org/tour/for-comprehensions.html `for` comprehension]], - * [[https://github.com/scala/scala-continuations scala-continuations]], - * [[https://github.com/scala/scala-async scala-async]], - * [[http://monadless.io/ Monadless]], - * [[https://github.com/pelotom/effectful effectful]] - * and [[https://github.com/ThoughtWorksInc/each ThoughtWorks Each]]. - * - * = Introduction = - * - * == Reinventing control flow in DSL == - * - * Embedded DSLs usually consist of a set of domain-specific keywords, - * which can be embedded in the their hosting languages. - * - * Ideally, a domain-specific keyword should be an optional extension, - * which can be present everywhere in the ordinary control flow of the hosting language. - * However, previous embedded DSLs usually badly interoperate with hosting language control flow. - * Instead, they reinvent control flow in their own DSL. - * - * For example, the [[https://akka.io akka]] provides - * [[https://doc.akka.io/docs/akka/2.5.10/fsm.html a DSL to create finite-state machines]], - * which consists of some domain-specific keywords like [[akka.actor.FSM!.when when]], - * [[akka.actor.FSM!.goto goto]] and [[akka.actor.FSM!.stay stay]]. - * Unfortunately, you cannot embedded those keywords into your ordinary `if` / `while` / `try` control flows, - * because Akka's DSL is required to be split into small closures, - * preventing ordinary control flows from crossing the boundary of those closures. - * - * TensorFlow's [[https://www.tensorflow.org/api_guides/python/control_flow_ops control flow operations]] and - * Caolan's [[https://github.com/caolan/async async]] library are examples of reinventing control flow - * in languages other than Scala. - * - * == Monad: the generic interface of control flow == - * - * It's too trivial to reinvent the whole set of control flows for each DSL. - * A simpler approach is only implementing a minimal interface required for control flows for each domain, - * while the syntax of other control flow operations are derived from the interface, shared between different domains. - * - * Since [[https://www.sciencedirect.com/science/article/pii/0890540191900524 computation can be represented as monads]], - * some libraries use monad as the interface of control flow, - * including [[scalaz.Monad]], [[cats.Monad]] and [[com.twitter.algebird.Monad]]. - * A DSL author only have to implement two abstract method in [[scalaz.Monad]], - * and all the derived control flow operations - * like [[scalaz.syntax.MonadOps.whileM]], [[scalaz.syntax.BindOps.ifM]] are available. - * In addition, those monadic data type can be created and composed - * from Scala's built-in [[https://docs.scala-lang.org/tour/for-comprehensions.html `for` comprehension]]. - * - * For example, you can use the same [[scalaz.syntax syntax]] or `for` comprehension - * to create [[org.scalacheck.Gen random value generators]] - * and [[com.thoughtworks.binding.Binding data-binding expressions]], - * as long as there are [[scalaz.Monad Monad]] instances - * for [[org.scalacheck.Gen]] and [[com.thoughtworks.binding.Binding]] respectively. - * - * Although the effort of creating a DSL is minimized with the help of monads, - * the syntax is still unsatisfactory. - * Methods in `MonadOps` still seem like a duplicate of ordinary control flow, - * and `for` comprehension supports only a limited set of functionality in comparison to ordinary control flows. - * `if` / `while` / `try` and other block expressions cannot appear in the enumerator clause of `for` comprehension. - * - * == Enabling ordinary control flows in DSL via macros == - * - * An idea to avoid inconsistency between domain-specific control flow and ordinary control flow is - * converting ordinary control flow to domain-specific control flow at compiler time. - * - * For example, [[https://github.com/scala/scala-async scala.async]] provides a macro - * to generate asynchronous control flow. - * The users just wrap normal synchronous code in a [[scala.async.Async.async async]] block, - * and it runs asynchronously. - * - * This approach can be generalized to any monadic data types. - * [[https://github.com/ThoughtWorksInc/each ThoughtWorks Each]], [[http://monadless.io/ Monadless]] - * and [[https://github.com/pelotom/effectful effectful]] are macros - * that convert ordinary control flow to monadic control flow. - * - * For example, with the help of [[https://github.com/ThoughtWorksInc/each ThoughtWorks Each]], - * [[https://github.com/ThoughtWorksInc/Binding.scala Binding.scala]] is used to create reactive HTML templating - * from ordinary Scala control flow. - * - * == Delimited continuations == - * - * Another generic interface of control flow is continuation, - * which is known as - * [[https://www.schoolofhaskell.com/user/dpiponi/the-mother-of-all-monads the mother of all monads]], - * where control flows in specific domain can be supported by specific final result types of continuations. - * - * [[https://github.com/scala/scala-continuations scala-continuations]] - * and [[https://github.com/qifun/stateless-future stateless-future]] - * are two delimited continuation implementations. - * Both projects can convert ordinary control flow to continuation-passing style closure chains at compiler time. - * - * For example, [[https://github.com/qifun/stateless-future-akka stateless-future-akka]], - * based on `stateless-future`, - * provides a special final result type for akka actors. - * Unlike [[akka.actor.FSM]]'s inconsistent control flows, users can create complex finite-state machines - * from simple ordinary control flows along with `stateless-future-akka`'s domain-specific keyword `nextMessage`. - * - * == Collaborative DSLs == - * - * All the above approaches lack of the ability to collaborate with other DSLs. - * Each of the above DSLs can be only exclusively enabled in a code block. - * For example, - * [[https://github.com/scala/scala-continuations scala-continuations]] - * enables calls to `@cps` method in [[scala.util.continuations.reset]] blocks, - * and [[https://github.com/ThoughtWorksInc/each ThoughtWorks Each]] - * enables the magic `each` method for [[scalaz.Monad]] in [[com.thoughtworks.each.Monadic.monadic]] blocks. - * It is impossible to enable both DSLs in one function. - * - * This [[https://github.com/ThoughtWorksInc/Dsl.scala Dsl.scala]] project resolves this problem. - * - * We also provide adapters to all the above kinds of DSLs. - * Instead of switching different DSLs between different functions, - * DSL users can use multiple DSLs together in one function, - * by simply adding [[com.thoughtworks.dsl.compilerplugins.BangNotation our Scala compiler plug-in]]. - * - * @example Suppose you want to create an [[https://en.wikipedia.org/wiki/Xorshift Xorshift]] random number generator. - * - * The generated numbers should be stored in a lazily evaluated infinite [[scala.collection.immutable.Stream Stream]], - * which can be implemented as a recursive function that produce the next random number in each iteration, - * with the help of our built-in domain-specific keyword [[com.thoughtworks.dsl.keywords.Yield Yield]]. - * - * {{{ - * import com.thoughtworks.dsl.Dsl.reset - * import com.thoughtworks.dsl.keywords.Yield - * - * def xorshiftRandomGenerator(seed: Int): Stream[Int] = { - * val tmp1 = seed ^ (seed << 13) - * val tmp2 = tmp1 ^ (tmp1 >>> 17) - * val tmp3 = tmp2 ^ (tmp2 << 5) - * !Yield(tmp3) - * xorshiftRandomGenerator(tmp3) - * }: @reset - * - * val myGenerator = xorshiftRandomGenerator(seed = 123) - * - * myGenerator(0) should be(31682556) - * myGenerator(1) should be(-276305998) - * myGenerator(2) should be(2101636938) - * }}} - * - * [[com.thoughtworks.dsl.keywords.Yield Yield]] is an keyword to produce a value - * for a lazily evaluated [[scala.collection.immutable.Stream Stream]]. - * That is to say, [[scala.collection.immutable.Stream Stream]] is the domain - * where the DSL [[com.thoughtworks.dsl.keywords.Yield Yield]] can be used, - * which was interpreted like the `yield` keyword in C#, JavaScript or Python. - * - * Note that the body of `xorshiftRandomGenerator` is annotated as `@[[Dsl.reset reset]]`, - * which enables the [[Dsl.Keyword#unary_$bang !-notation]] in the code block. - * - * Alternatively, you can also use the - * [[com.thoughtworks.dsl.compilerplugins.ResetEverywhere ResetEverywhere]] compiler plug-in, - * which enable [[Dsl.Keyword#unary_$bang !-notation]] for every methods and functions. - * @example [[com.thoughtworks.dsl.keywords.Yield Yield]] and [[scala.collection.immutable.Stream Stream]] - * can be also used for logging. - * - * Suppose you have a function to parse an JSON file, - * you can append log records to a [[scala.collection.immutable.Stream Stream]] during parsing. - * - * {{{ - * import com.thoughtworks.dsl.keywords.Yield - * import com.thoughtworks.dsl.Dsl.!! - * import scala.util.parsing.json._ - * def parseAndLog1(jsonContent: String, defaultValue: JSONType): Stream[String] !! JSONType = { (callback: JSONType => Stream[String]) => - * !Yield(s"I am going to parse the JSON text $jsonContent...") - * JSON.parseRaw(jsonContent) match { - * case Some(json) => - * !Yield(s"Succeeded to parse $jsonContent") - * callback(json) - * case None => - * !Yield(s"Failed to parse $jsonContent") - * callback(defaultValue) - * } - * } - * }}} - * - * Since the function produces both a [[scala.util.parsing.json.JSONType JSONType]] - * and a [[scala.collection.immutable.Stream Stream]] of logs, - * the return type is now `Stream[String] !! JSONType`, - * where [[com.thoughtworks.dsl.Dsl.$bang$bang !!]] is - * `(JSONType => Stream[String]) => Stream[String]`, - * an alias of continuation-passing style function, - * indicating it produces both a [[scala.util.parsing.json.JSONType JSONType]] and a [[scala.Stream Stream]] of logs. - * - * {{{ - * val logs = parseAndLog1(""" { "key": "value" } """, JSONArray(Nil)) { json => - * json should be(JSONObject(Map("key" -> "value"))) - * Stream("done") - * } - * - * logs should be(Stream("I am going to parse the JSON text { \"key\": \"value\" } ...", - * "Succeeded to parse { \"key\": \"value\" } ", - * "done")) - * }}} - * @example The closure in the previous example can be simplified with the help of Scala's placeholder syntax: - * - * {{{ - * import com.thoughtworks.dsl.keywords.Yield - * import com.thoughtworks.dsl.Dsl.!! - * import scala.util.parsing.json._ - * def parseAndLog2(jsonContent: String, defaultValue: JSONType): Stream[String] !! JSONType = _ { - * !Yield(s"I am going to parse the JSON text $jsonContent...") - * JSON.parseRaw(jsonContent) match { - * case Some(json) => - * !Yield(s"Succeeded to parse $jsonContent") - * json - * case None => - * !Yield(s"Failed to parse $jsonContent") - * defaultValue - * } - * } - * - * val logs = parseAndLog2(""" { "key": "value" } """, JSONArray(Nil)) { json => - * json should be(JSONObject(Map("key" -> "value"))) - * Stream("done") - * } - * - * logs should be(Stream("I am going to parse the JSON text { \"key\": \"value\" } ...", - * "Succeeded to parse { \"key\": \"value\" } ", - * "done")) - * }}} - * - * Note that `parseAndLog2` is equivelent to `parseAndLog1`. - * The code block after underscore is still inside a function whose return type is `Stream[String]`. - * @example Instead of manually create the continuation-passing style function, - * you can also create the function from a [[com.thoughtworks.dsl.Dsl.$bang$bang !!]] block. - * - * {{{ - * import com.thoughtworks.dsl.keywords.Yield - * import com.thoughtworks.dsl.Dsl.!! - * import scala.util.parsing.json._ - * def parseAndLog3(jsonContent: String, defaultValue: JSONType): Stream[String] !! JSONType = !! { - * !Yield(s"I am going to parse the JSON text $jsonContent...") - * JSON.parseRaw(jsonContent) match { - * case Some(json) => - * !Yield(s"Succeeded to parse $jsonContent") - * json - * case None => - * !Yield(s"Failed to parse $jsonContent") - * defaultValue - * } - * } - * - * val logs = parseAndLog3(""" { "key": "value" } """, JSONArray(Nil)) { json => - * json should be(JSONObject(Map("key" -> "value"))) - * Stream("done") - * } - * - * logs should be(Stream("I am going to parse the JSON text { \"key\": \"value\" } ...", - * "Succeeded to parse { \"key\": \"value\" } ", - * "done")) - * }}} - * - * Unlike the `parseAndLog2` example, The code inside a `!!` block is not in an anonymous function. - * Instead, they are directly inside `parseAndLog3`, whose return type is `Stream[String] !! JSONType`. - * - * That is to say, - * the domain of those [[com.thoughtworks.dsl.keywords.Yield Yield]] keywords in `parseAndLog3` - * is not `Stream[String]` any more, the domain is `Stream[String] !! JSONType` now, - * which supports more keywords, which you will learnt from the next examples, - * than the `Stream[String]` domain. - * @example [[com.thoughtworks.dsl.Dsl.$bang$bang !!]], or [[com.thoughtworks.dsl.Dsl.Continuation Continuation]], - * is the preferred approach to enable multiple domains in one function. - * - * For example, you can create a function that - * lazily read each line of a [[java.io.BufferedReader BufferedReader]] to a [[scala.Stream Stream]], - * automatically close the [[java.io.BufferedReader BufferedReader]] after reading the last line, - * and finally return the total number of lines in the `Stream[String] !! Throwable !! Int` domain. - * - * {{{ - * import com.thoughtworks.dsl.Dsl.!! - * import com.thoughtworks.dsl.keywords.Using - * import com.thoughtworks.dsl.keywords.Yield - * import com.thoughtworks.dsl.keywords.Shift._ - * import java.io._ - * - * def readerToStream(createReader: () => BufferedReader): Stream[String] !! Throwable !! Int = !! { - * val reader = !Using(createReader()) - * - * def loop(lineNumber: Int): Stream[String] !! Throwable !! Int = _ { - * reader.readLine() match { - * case null => - * lineNumber - * case line => - * !Yield(line) - * !loop(lineNumber + 1) - * } - * } - * - * !loop(0) - * } - * }}} - * - * `!loop(0)` is a shortcut of `!Shift(loop(0))`, - * because there is [[keywords.Shift.implicitShift an implicit conversion]] - * from `Stream[String] !! Throwable !! Int` to a [[keywords.Shift]] case class, - * which is similar to the `await` keyword in JavaScript, Python or C#. - * - * A type like `A !! B !! C` means a domain-specific value of type `C` in the domain of `A` and `B`. - * When `B` is [[scala.Throwable Throwable]], the [[keywords.Using]] - * is available, which will close a resource when exiting the current function. - * - * {{{ - * import scala.util.Success - * - * var isClosed = false - * def createReader() = { - * new BufferedReader(new StringReader("line1\nline2\nline3")) { - * override def close() = { - * isClosed = true - * } - * } - * } - * - * val stream = readerToStream(createReader _) { numberOfLines: Int => - * numberOfLines should be(3) - * - * Function.const(Stream.empty)(_) - * } { e: Throwable => - * throw new AssertionError("Unexpected exception during readerToStream", e) - * } - * - * isClosed should be(false) - * stream should be(Stream("line1", "line2", "line3")) - * isClosed should be(true) - * }}} - * @example If you don't need to collaborate to [[scala.Stream Stream]] or other domains, - * you can use `TailRec[Unit] !! Throwable !! A` - * or the alias [[domains.task.Task]] as the return type, - * which can be used as an asynchronous task that support RAII, - * as a higher-performance replacement of - * [[scala.concurrent.Future]], [[scalaz.concurrent.Task]] or [[monix.eval.Task]]. - * - * Also, there are some keywords in [[keywords.AsynchronousIo]] - * to asynchronously perform Java NIO.2 IO operations in a [[domains.task.Task]] domain. - * For example, you can implement an HTTP client from those keywords. - * - * {{{ - * import com.thoughtworks.dsl.domains.task._ - * import com.thoughtworks.dsl.keywords._ - * import com.thoughtworks.dsl.keywords.Shift.implicitShift - * import com.thoughtworks.dsl.keywords.AsynchronousIo._ - * import java.io._ - * import java.net._ - * import java.nio._, channels._ - * - * def readAll(channel: AsynchronousByteChannel, destination: ByteBuffer): Task[Unit] = _ { - * if (destination.remaining > 0) { - * val numberOfBytesRead: Int = !Read(channel, destination) - * numberOfBytesRead match { - * case -1 => - * case _ => !readAll(channel, destination) - * } - * } else { - * throw new IOException("The response is too big to read.") - * } - * } - * - * def writeAll[Domain](channel: AsynchronousByteChannel, destination: ByteBuffer): Task[Unit] = _ { - * while (destination.remaining > 0) { - * !Write(channel, destination) - * } - * } - * - * def httpClient(url: URL): Task[String] = _ { - * val socket = AsynchronousSocketChannel.open() - * try { - * val port = if (url.getPort == -1) 80 else url.getPort - * val address = new InetSocketAddress(url.getHost, port) - * !AsynchronousIo.Connect(socket, address) - * val request = ByteBuffer.wrap(s"GET ${url.getPath} HTTP/1.1\r\nHost:${url.getHost}\r\nConnection:Close\r\n\r\n".getBytes) - * !writeAll(socket, request) - * val response = ByteBuffer.allocate(100000) - * !readAll(socket, response) - * response.flip() - * io.Codec.UTF8.decoder.decode(response).toString - * } finally { - * socket.close() - * } - * } - * }}} - * - * The usage of `Task` is similar to previous examples. - * You can check the result or exception in asynchronous handlers. - * - * But we also provides [[com.thoughtworks.dsl.domains.task.Task.blockingAwait blockingAwait]] and some other utilities - * at [[domains.task.Task]]. - * - * {{{ - * import com.thoughtworks.dsl.domains.task.Task.blockingAwait - * - * val url = new URL("http://localhost:4001/ping") - * val fileContent = blockingAwait(httpClient(url)) - * fileContent should startWith("HTTP/1.1 200 OK") - * }}} - * - * Another useful keyword for asynchronous programming is [[com.thoughtworks.dsl.keywords.Fork Fork]], - * which duplicate the current control flow, and the child control flows are executed in parallel, - * similar to the POSIX `fork` system call. - * - * [[com.thoughtworks.dsl.keywords.Fork Fork]] should be used inside - * a [[com.thoughtworks.dsl.domains.task.Task#join]] block, which collects the result of each forked control flow. - * - * {{{ - * import com.thoughtworks.dsl.keywords.Fork - * import com.thoughtworks.dsl.keywords.Return - * val Urls = Seq( - * new URL("http://localhost:4001/ping"), - * new URL("http://localhost:4001/pong") - * ) - * def parallelTask: Task[Seq[String]] = { - * val url = !Fork(Urls) - * !Return(!httpClient(url)) - * } - * - * inside(blockingAwait(parallelTask)) { - * case Seq(fileContent0, fileContent1) => - * fileContent0 should startWith("HTTP/1.1 200 OK") - * fileContent1 should startWith("HTTP/1.1 200 OK") - * } - * }}} - * @example The built-in [[keywords.Monadic]] can be used as an adaptor - * to [[scalaz.Monad]] and [[scalaz.MonadTrans]], - * to create monadic code from imperative syntax, - * similar to the !-notation in Idris. - * - * For example, suppose you are creating a program that counts lines of code under a directory. - * You want to store the result in a [[scala.Stream Stream]] of line count of each file. - * - * {{{ - * import java.io.File - * import com.thoughtworks.dsl.keywords.Monadic - * import com.thoughtworks.dsl.domains.scalaz._ - * import scalaz.std.stream._ - * - * def countMonadic(file: File): Stream[Int] = Stream { - * if (file.isDirectory) { - * file.listFiles() match { - * case null => - * // Unable to open `file` - * !Monadic(Stream.empty[Int]) - * case children => - * // Import this implicit conversion to omit the Monadic keyword - * import com.thoughtworks.dsl.keywords.Monadic.implicitMonadic - * - * val child: File = !children.toStream - * !countMonadic(child) - * } - * } else { - * scala.io.Source.fromFile(file).getLines.size - * } - * } - * - * - * val countCurrentSourceFile = countMonadic(new File(sourcecode.File())) - * - * inside(countCurrentSourceFile) { - * case Stream(lineCount) => - * lineCount should be > 0 - * } - * - * }}} - * @example The previous code requires a `toStream` conversion on `children`, - * because `children`'s type `Array[File]` does not fit the `F` type parameter in [[scalaz.Monad.bind]]. - * - * The conversion can be avoided if using [[scala.collection.generic.CanBuildFrom CanBuildFrom]] type class - * instead of monads. - * - * We provide a [[com.thoughtworks.dsl.keywords.Each Each]] - * keyword to extract each element in a Scala collection. - * The behavior is similar to monad, except the collection type can vary. - * - * For example, you can extract each element from an [[scala.Array Array]], - * even when the return type (or the domain) is a [[scala.collection.immutable.Stream Stream]]. - * - * - * {{{ - * import java.io.File - * import com.thoughtworks.dsl.keywords.Monadic, Monadic.implicitMonadic - * import com.thoughtworks.dsl.keywords.Each - * import com.thoughtworks.dsl.domains.scalaz._ - * import scalaz.std.stream._ - * - * def countEach(file: File): Stream[Int] = Stream { - * if (file.isDirectory) { - * file.listFiles() match { - * case null => - * // Unable to open `file` - * !Stream.empty[Int] - * case children => - * val child: File = !Each(children) - * !countEach(child) - * } - * } else { - * scala.io.Source.fromFile(file).getLines.size - * } - * } - * - * - * val countCurrentSourceFile = countEach(new File(sourcecode.File())) - * - * inside(countCurrentSourceFile) { - * case Stream(lineCount) => - * lineCount should be > 0 - * } - * - * }}} - * - * Unlike Haskell's do-notation or Idris's !-notation, - * Dsl.scala allows non-monadic keywords like [[com.thoughtworks.dsl.keywords.Each Each]] works along with - * monads. - * @example Dsl.scala also supports [[scalaz.MonadTrans]]. - * - * Considering the line counter implemented in previous example may be failed for some files, - * due to permission issue or other IO problem, - * you can use [[scalaz.OptionT]] monad transformer to mark those failed file as a [[scala.None None]]. - * - * {{{ - * import scalaz._ - * import java.io.File - * import com.thoughtworks.dsl.keywords.Monadic, Monadic.implicitMonadic - * import com.thoughtworks.dsl.domains.scalaz._ - * import scalaz.std.stream._ - * - * def countLift(file: File): OptionT[Stream, Int] = OptionT.some { - * if (file.isDirectory) { - * file.listFiles() match { - * case null => - * // Unable to open `file` - * !OptionT.none[Stream, Int] - * case children => - * val child: File = !Stream(children: _*) - * !countLift(child) - * } - * } else { - * scala.io.Source.fromFile(file).getLines.size - * } - * } - * - * - * val countCurrentSourceFile = countLift(new File(sourcecode.File())).run - * - * inside(countCurrentSourceFile) { - * case Stream(Some(lineCount)) => - * lineCount should be > 0 - * } - * }}} - * - * - * Note that our keywords are adaptive to the domain it belongs to. - * Thus, instead of explicit `!Monadic(OptionT.optionTMonadTrans.liftM(Stream(children: _*)))`, - * you can simply have `!Stream(children: _*)`. - * The implicit lifting feature looks like Idris's effect monads, - * though the mechanisms is different from `implicit lift` in Idris. - * @see [[Dsl]] for the guideline to create your custom DSL. - * @see [[domains.scalaz]] for using [[Dsl.Keyword#unary_$bang !-notation]] with [[scalaz]]. - * @see [[domains.cats]] for using [[Dsl.Keyword#unary_$bang !-notation]] with [[cats]]. - * - * - */ -package object dsl - -package dsl { - - /** Contains built-in domain-specific [[com.thoughtworks.dsl.Dsl.Keyword Keyword]]s and their corresponding interpreters. - * - * - */ - package object keywords -} diff --git a/package/src/test/scala/com/thoughtworks/dsl/MockPingPongServer.scala b/package/src/test/scala/com/thoughtworks/dsl/MockPingPongServer.scala deleted file mode 100644 index 4737b427..00000000 --- a/package/src/test/scala/com/thoughtworks/dsl/MockPingPongServer.scala +++ /dev/null @@ -1,39 +0,0 @@ -package com.thoughtworks.dsl -import com.thoughtworks.enableMembersIf -import org.scalatest.{AsyncTestSuite, BeforeAndAfterAll, Suite} - -import scala.concurrent.ExecutionContext -import scala.concurrent.duration.Duration - -/** - * @author 杨博 (Yang Bo) - */ -@enableMembersIf(scala.util.Properties.versionNumberString.matches("""^2\.1(1|2)\..*$""")) -trait MockPingPongServer extends BeforeAndAfterAll { this: Suite => - - implicit def executionContext: ExecutionContext - - protected implicit val system = akka.actor.ActorSystem() - - protected implicit val materializer = akka.stream.ActorMaterializer() - - protected val mockServer = { - import akka.http.scaladsl.server.Directives._ - val route = - get { - path("ping") { - complete("PING!") - } ~ path("pong") { - complete("PONG!") - } - } - concurrent.Await.result(akka.http.scaladsl.Http().bindAndHandle(route, "localhost", 4001), Duration.Inf) - } - - override protected def afterAll(): Unit = { - mockServer - .unbind() - .onComplete(_ => system.terminate()) - } - -}