package sbt package std import Def.{Initialize,Setting} import Types.{idFun,Id} import TaskExtra.allM import appmacro.{ContextUtil, Convert, InputWrapper, Instance, MixedBuilder, MonadInstance} import complete.Parser import language.experimental.macros import scala.reflect._ import reflect.macros._ /** Instance for the monad/applicative functor for plain Tasks. */ object TaskInstance extends MonadInstance { import TaskExtra._ final type M[x] = Task[x] def app[K[L[x]], Z](in: K[Task], f: K[Id] => Z)(implicit a: AList[K]): Task[Z] = new Mapped[Z,K](in, f compose allM, a) def map[S,T](in: Task[S], f: S => T): Task[T] = in map f def flatten[T](in: Task[Task[T]]): Task[T] = in flatMap idFun[Task[T]] def pure[T](t: () => T): Task[T] = toTask(t) } /** Composes the Task and Initialize Instances to provide an Instance for [T] Initialize[Task[T]].*/ object FullInstance extends Instance.Composed[Initialize, Task](InitializeInstance, TaskInstance) with MonadInstance { type SS = sbt.Settings[Scope] val settingsData = TaskKey[SS]("settings-data", "Provides access to the project data for the build.", KeyRanks.DTask) def flatten[T](in: Initialize[Task[Initialize[Task[T]]]]): Initialize[Task[T]] = { import Scoped._ (in,settingsData, Def.capturedTransformations) apply{ (a: Task[Initialize[Task[T]]], data: Task[SS], f) => import TaskExtra.multT2Task (a, data) flatMap { case (a,d) => f(a) evaluate d } } } } /** Converts an input `Tree` of type `Initialize[T]`, `Initialize[Task[T]]`, or `Task[T]` into a `Tree` of type `Initialize[Task[T]]`.*/ object FullConvert extends Convert { def apply[T: c.WeakTypeTag](c: Context)(in: c.Tree): c.Tree = { val util = appmacro.ContextUtil[c.type](c) if(in.tpe <:< util.atypeOf[Initialize[Task[T]]]) in else if(in.tpe <:< util.atypeOf[Initialize[T]]) { val i = c.Expr[Initialize[T]](in) c.universe.reify( Def.toITask(i.splice) ).tree } else if(in.tpe <:< util.atypeOf[Task[T]]) { val i = c.Expr[Task[T]](in) c.universe.reify( Def.valueStrict[Task[T]](i.splice) ).tree } else c.abort(in.pos, "Unknown input type: " + in.tpe) } } object TaskMacro { final val AssignInitName = "<<=" final val Append1InitName = "<+=" final val AppendNInitName = "<++=" final val InputTaskCreateName = "create" def taskMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[Task[T]]] = Instance.contImpl[T](c, FullInstance, FullConvert, MixedBuilder)(Left(t)) def taskDynMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[Task[T]]] = Instance.contImpl[T](c, FullInstance, FullConvert, MixedBuilder)(Right(t)) /** Implementation of := macro for settings. */ def settingAssignMacroImpl[T: c.WeakTypeTag](c: Context)(v: c.Expr[T]): c.Expr[Setting[T]] = { val init = SettingMacro.settingMacroImpl[T](c)(v) val assign = transformMacroImpl(c)( init.tree )( AssignInitName ) c.Expr[Setting[T]]( assign ) } /** Implementation of := macro for tasks. */ def taskAssignMacroImpl[T: c.WeakTypeTag](c: Context)(v: c.Expr[T]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[T](c)(v) val assign = transformMacroImpl(c)( init.tree )( AssignInitName ) c.Expr[Setting[Task[T]]]( assign ) } /** Implementation of := macro for tasks. */ def inputTaskAssignMacroImpl[T: c.WeakTypeTag](c: Context)(v: c.Expr[T]): c.Expr[Setting[InputTask[T]]] = { val init = inputTaskMacroImpl[T](c)(v) val assign = transformMacroImpl(c)( init.tree )( AssignInitName ) c.Expr[Setting[InputTask[T]]]( assign ) } /** Implementation of += macro for tasks. */ def taskAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context)(v: c.Expr[U])(a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[U](c)(v) val assign = appendMacroImpl(c)( init.tree, a.tree )( Append1InitName ) c.Expr[Setting[Task[T]]]( assign ) } /** Implementation of += macro for settings. */ def settingAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context)(v: c.Expr[U])(a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[T]] = { val init = SettingMacro.settingMacroImpl[U](c)(v) val assign = appendMacroImpl(c)( init.tree, a.tree )( Append1InitName ) c.Expr[Setting[T]]( assign ) } /** Implementation of ++= macro for tasks. */ def taskAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context)(vs: c.Expr[U])(a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[Task[T]]] = { val init = taskMacroImpl[U](c)(vs) val assign = appendMacroImpl(c)( init.tree, a.tree )( AppendNInitName ) c.Expr[Setting[Task[T]]]( assign ) } /** Implementation of ++= macro for settings. */ def settingAppendNImpl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context)(vs: c.Expr[U])(a: c.Expr[Append.Values[T, U]]): c.Expr[Setting[T]] = { val init = SettingMacro.settingMacroImpl[U](c)(vs) val assign = appendMacroImpl(c)( init.tree, a.tree )( AppendNInitName ) c.Expr[Setting[T]]( assign ) } private[this] def appendMacroImpl(c: Context)(init: c.Tree, append: c.Tree)(newName: String): c.Tree = { import c.universe.{Apply,ApplyTag,newTermName,Select,SelectTag,TypeApply,TypeApplyTag} c.macroApplication match { case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), a) => Apply(Apply(TypeApply(Select(preT, newTermName(newName).encodedName), targs), init :: Nil), a) case x => ContextUtil.unexpectedTree(x) } } private[this] def transformMacroImpl(c: Context)(init: c.Tree)(newName: String): c.Tree = { import c.universe.{Apply,ApplyTag,newTermName,Select,SelectTag} val target = c.macroApplication match { case Apply(Select(prefix, _), _) => prefix case x => ContextUtil.unexpectedTree(x) } Apply.apply(Select(target, newTermName(newName).encodedName), init :: Nil) } sealed abstract class MacroValue[T] { def value: T = macro std.TaskMacro.valueMacroImpl[T] } def valueMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[T] = ContextUtil.selectMacroImpl[T,Any](c)( ts => InputWrapper.wrapKey[T](c)(ts) ) sealed abstract class RawParserInput[T] { def parsed: T = macro std.TaskMacro.rawParserMacro[T] } sealed abstract class InitParserInput[T] { def parsed: T = macro std.TaskMacro.initParserMacro[T] } sealed abstract class StateParserInput[T] { def parsed: T = macro std.TaskMacro.stateParserMacro[T] } /** Implements `Parser[T].parsed` by wrapping the Parser with the ParserInput wrapper.*/ def rawParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] = ContextUtil.selectMacroImpl[T, Parser[T]](c) { p => c.universe.reify { ParserInput.parser_\u2603\u2603[T](InputTask.parserAsInput(p.splice)) }} /** Implements the `Initialize[Parser[T]].parsed` macro by wrapping the input with the ParserInput wrapper.*/ def initParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] = ContextUtil.selectMacroImpl[T, Initialize[Parser[T]]](c) { t => c.universe.reify { ParserInput.parser_\u2603\u2603[T](InputTask.initParserAsInput(t.splice)) }} /** Implements the `Initialize[State => Parser[T]].parsed` macro by wrapping the input with the ParserInput wrapper.*/ def stateParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] = ContextUtil.selectMacroImpl[T, Initialize[State => Parser[T]]](c) { t => c.universe.reify { ParserInput.parser_\u2603\u2603[T](t.splice) }} /** Implementation detail. The method temporarily holds the input parser (as a Tree, at compile time) until the input task macro processes it. */ object ParserInput { /** The name of the wrapper method should be obscure. * Wrapper checking is based solely on this name, so it must not conflict with a user method name. * The user should never see this method because it is compile-time only and only used internally by the task macros.*/ val WrapName = "parser_\u2603\u2603" // This method should be annotated as compile-time only when that feature is implemented def parser_\u2603\u2603[T](i: Initialize[State => Parser[T]]): T = error("This method is an implementation detail and should not be referenced.") } def inputTaskMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = inputTaskMacro0[T](c)(Left(t)) def inputTaskDynMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] = inputTaskMacro0[T](c)(Right(t)) private[this] def inputTaskMacro0[T: c.WeakTypeTag](c: Context)(t: Either[c.Expr[T], c.Expr[Initialize[Task[T]]]]): c.Expr[Initialize[InputTask[T]]] = { import c.universe.{Apply=>ApplyTree,_} val tag = implicitly[c.WeakTypeTag[T]] val util = ContextUtil[c.type](c) val it = Ident(util.singleton(InputTask)) val isParserWrapper = util.isWrapper(ParserInput.WrapName) val isTaskWrapper = util.isWrapper(InputWrapper.WrapName) val isAnyWrapper = (tr: Tree) => isParserWrapper(tr) || isTaskWrapper(tr) val ttree = t match { case Left(l) => l.tree; case Right(r) => r.tree } val defs = util.collectDefs(ttree, isAnyWrapper) val checkQual = util.checkReferences(defs, isAnyWrapper) val unitTask = c.typeOf[TaskKey[Unit]] val taskKeyC = unitTask.typeConstructor var result: Option[(Tree, Type, ValDef)] = None def subWrapper(tpe: Type, qual: Tree): Tree = if(result.isDefined) { c.error(qual.pos, "An InputTask can only have a single input parser.") EmptyTree } else { qual.foreach(checkQual) val keyType = appliedType(taskKeyC, tpe :: Nil) // TaskKey[] val vd = util.freshValDef(keyType, qual.symbol) // val $x: TaskKey[] result = Some( (qual, tpe, vd) ) val tree = util.refVal(vd) // $x tree.setPos(qual.pos) // position needs to be set so that wrapKey passes the position onto the wrapper assert(tree.tpe != null, "Null type: " + tree) val wrapped = InputWrapper.wrapKey(c)( c.Expr[Any](tree) )( c.WeakTypeTag(tpe) ) wrapped.tree.setType(tpe) } // Tree for InputTask.create[, ](arg1)(arg2) def inputTaskApply(tpeA: Type, tpeB: Type, arg1: Tree, arg2: Tree) = { val typedApp = TypeApply(Select(it, InputTaskCreateName), TypeTree(tpeA) :: TypeTree(tpeB) :: Nil) val app = ApplyTree( ApplyTree(typedApp, arg1 :: Nil), arg2 :: Nil) c.Expr[Initialize[InputTask[T]]](app) } def expandTask(dyn: Boolean, tx: Tree): c.Expr[Initialize[Task[T]]] = if(dyn) taskDynMacroImpl[T](c)( c.Expr[Initialize[Task[T]]](tx) ) else taskMacroImpl[T](c)( c.Expr[T](tx) ) val tx = util.transformWrappers(ttree, isParserWrapper, (tpe,tree) => subWrapper(tpe,tree)) val body = c.resetLocalAttrs( expandTask(t.isRight, tx).tree ) result match { case Some((p, tpe, param)) => val f = Function(param :: Nil, body) inputTaskApply(tpe, tag.tpe, p, f) case None => // SI-6591 prevents the more direct version using reify: // reify { InputTask[Unit,T].create(TaskMacro.emptyParser)(Types.const(body.splice)) } val initType = c.weakTypeOf[Initialize[Task[T]]] val tt = Ident(util.singleton(Types)) val f = ApplyTree(TypeApply(Select(tt, "const"), TypeTree(unitTask) :: TypeTree(initType) :: Nil), body :: Nil) val p = reify { InputTask.emptyParser } inputTaskApply(c.typeOf[Unit], tag.tpe, p.tree, f) } } } /** Convert instance for plain `Task`s not within the settings system. * This is not used for the main task/setting macros, but could be used when manipulating plain Tasks.*/ object TaskConvert extends Convert { def apply[T: c.WeakTypeTag](c: Context)(in: c.Tree): c.Tree = { val u = appmacro.ContextUtil[c.type](c) if(in.tpe <:< u.atypeOf[Task[T]]) in else c.abort(in.pos, "Unknown input type: " + in.tpe) } } object PlainTaskMacro { def task[T](t: T): Task[T] = macro taskImpl[T] def taskImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Task[T]] = Instance.contImpl[T](c, TaskInstance, TaskConvert, MixedBuilder)(Left(t)) def taskDyn[T](t: Task[T]): Task[T] = macro taskDynImpl[T] def taskDynImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Task[T]]): c.Expr[Task[T]] = Instance.contImpl[T](c, TaskInstance, TaskConvert, MixedBuilder)(Right(t)) }