diff --git a/main/command/src/main/scala/sbt/BasicCommands.scala b/main/command/src/main/scala/sbt/BasicCommands.scala index 329b04eb9..205b53a65 100644 --- a/main/command/src/main/scala/sbt/BasicCommands.scala +++ b/main/command/src/main/scala/sbt/BasicCommands.scala @@ -84,8 +84,8 @@ object BasicCommands val parentLoader = getClass.getClassLoader state.log.info("Applying State transformations " + args.mkString(", ") + (if(cp.isEmpty) "" else " from " + cp.mkString(File.pathSeparator))) val loader = if(cp.isEmpty) parentLoader else toLoader(cp.map(f => new File(f)), parentLoader) - val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader)) - (state /: loaded) { case (s, obj: (State => State)) => obj(s) } + val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader).asInstanceOf[State => State]) + (state /: loaded)((s, obj) => obj(s)) } def callParser: Parser[(Seq[String], Seq[String])] = token(Space) ~> ((classpathOptionParser ?? Nil) ~ rep1sep(className, token(Space))) private[this] def className: Parser[String] = diff --git a/main/settings/src/main/scala/sbt/Def.scala b/main/settings/src/main/scala/sbt/Def.scala index 08d3a448f..3e984c030 100644 --- a/main/settings/src/main/scala/sbt/Def.scala +++ b/main/settings/src/main/scala/sbt/Def.scala @@ -1,5 +1,6 @@ package sbt + import Types.const import complete.Parser import java.io.File @@ -42,9 +43,14 @@ object Def extends Init[Scope] with TaskMacroExtra /** Lifts the result of a setting initialization into a Task. */ def toITask[T](i: Initialize[T]): Initialize[Task[T]] = map(i)(std.TaskExtra.inlineTask) + def toSParser[T](p: Parser[T]): State => Parser[T] = const(p) + def toISParser[T](p: Initialize[Parser[T]]): Initialize[State => Parser[T]] = p(toSParser) + def toIParser[T](p: Initialize[InputTask[T]]): Initialize[State => Parser[Task[T]]] = p(_.parser) + import language.experimental.macros - import std.TaskMacro.{InitParserInput, inputTaskMacroImpl, inputTaskDynMacroImpl, MacroValue, taskDynMacroImpl, taskMacroImpl, StateParserInput} + import std.TaskMacro.{inputTaskMacroImpl, inputTaskDynMacroImpl, taskDynMacroImpl, taskMacroImpl} import std.SettingMacro.{settingDynMacroImpl,settingMacroImpl} + import std.{MacroValue, ParserInput} def task[T](t: T): Def.Initialize[Task[T]] = macro taskMacroImpl[T] def taskDyn[T](t: Def.Initialize[Task[T]]): Def.Initialize[Task[T]] = macro taskDynMacroImpl[T] @@ -58,11 +64,12 @@ object Def extends Init[Scope] with TaskMacroExtra implicit def macroValueI[T](in: Initialize[T]): MacroValue[T] = ??? implicit def macroValueIT[T](in: Initialize[Task[T]]): MacroValue[T] = ??? + implicit def macroValueIInT[T](in: Initialize[InputTask[T]]): MacroValue[T] = ??? // The following conversions enable the types Parser[T], Initialize[Parser[T]], and Initialize[State => Parser[T]] to // be used in the inputTask macro as an input with an ultimate result of type T - implicit def parserInitToInput[T](p: Initialize[Parser[T]]): InitParserInput[T] = ??? - implicit def parserInitStateToInput[T](p: Initialize[State => Parser[T]]): StateParserInput[T] = ??? + implicit def parserInitToInput[T](p: Initialize[Parser[T]]): ParserInput[T] = ??? + implicit def parserInitStateToInput[T](p: Initialize[State => Parser[T]]): ParserInput[T] = ??? import language.experimental.macros def settingKey[T](description: String): SettingKey[T] = macro std.KeyMacro.settingKeyImpl[T] @@ -72,6 +79,8 @@ object Def extends Init[Scope] with TaskMacroExtra // these need to be mixed into the sbt package object because the target doesn't involve Initialize or anything in Def trait TaskMacroExtra { - implicit def macroValueT[T](in: Task[T]): std.TaskMacro.MacroValue[T] = ??? - implicit def parserToInput[T](in: Parser[T]): std.TaskMacro.RawParserInput[T] = ??? + implicit def macroValueT[T](in: Task[T]): std.MacroValue[T] = ??? + implicit def macroValueIn[T](in: InputTask[T]): std.MacroValue[T] = ??? + implicit def parserToInput[T](in: Parser[T]): std.ParserInput[T] = ??? + implicit def stateParserToInput[T](in: State => Parser[T]): std.ParserInput[T] = ??? } \ No newline at end of file diff --git a/main/settings/src/main/scala/sbt/InputTask.scala b/main/settings/src/main/scala/sbt/InputTask.scala index fe5541dab..73dae4af5 100644 --- a/main/settings/src/main/scala/sbt/InputTask.scala +++ b/main/settings/src/main/scala/sbt/InputTask.scala @@ -15,6 +15,9 @@ final class InputTask[T] private(val parser: State => Parser[Task[T]]) object InputTask { + implicit def inputTaskParsed[T](in: InputTask[T]): std.ParserInputTask[T] = ??? + implicit def inputTaskInitParsed[T](in: Initialize[InputTask[T]]): std.ParserInputTask[T] = ??? + def make[T](p: State => Parser[Task[T]]): InputTask[T] = new InputTask[T](p) def static[T](p: Parser[Task[T]]): InputTask[T] = free(_ => p) @@ -35,15 +38,6 @@ object InputTask def createFree[T](action: Initialize[Task[T]]): Initialize[InputTask[T]] = action { tsk => free(emptyParser)( const(tsk) ) } - /** Constructs an InputTask from: - * a) a Parser constructed using other Settings, but not Tasks - * b) an action constructed using Settings, Tasks, and the result of parsing */ - def create[I,T](p: Initialize[State => Parser[I]])(action: Initialize[Task[I => T]]): Initialize[InputTask[T]] = - { - val act = action { (tf: Task[I => T]) => (i: I) => tf.map(f => f(i)) } - separate(p)(act) - } - /** Constructs an InputTask from: * a) a Parser constructed using other Settings, but not Tasks * b) a dynamically constructed Task that uses Settings, Tasks, and the result of parsing. */ diff --git a/main/settings/src/main/scala/sbt/std/InputConvert.scala b/main/settings/src/main/scala/sbt/std/InputConvert.scala new file mode 100644 index 000000000..ee0a7a35b --- /dev/null +++ b/main/settings/src/main/scala/sbt/std/InputConvert.scala @@ -0,0 +1,86 @@ +package sbt +package std + + import language.experimental.macros + import scala.reflect._ + import reflect.macros._ + + import Def.Initialize + import complete.Parser + import appmacro.{Convert, Converted} + + +object InputInitConvert extends Convert +{ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] = + if(nme == InputWrapper.WrapInitName) + Converted.Success(in) + else if(nme == InputWrapper.WrapInitTaskName) + Converted.Failure(in.pos, "Internal sbt error: initialize+task wrapper not split") + else + Converted.NotApplicable +} + +/** Converts an input `Tree` of type `Parser[T]` or `State => Parser[T]` into a `Tree` of type `State => Parser[T]`.*/ +object ParserConvert extends Convert +{ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] = + { + if(nme == ParserInput.WrapName) + Converted.Success(in) + else if(nme == ParserInput.WrapInitName) + Converted.Failure(in.pos, "Internal sbt error: initialize+parser wrapper not split") + else + Converted.NotApplicable + } +} + +/** Convert instance for plain `Task`s not within the settings system. */ +object TaskConvert extends Convert +{ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] = + if(nme == InputWrapper.WrapTaskName) + Converted.Success(in) + else + Converted.NotApplicable +} + +/** 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 +{ + import InputWrapper._ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] = + if(nme == WrapInitTaskName) + Converted.Success(in) + else if(nme == WrapInitName) + { + val i = c.Expr[Initialize[T]](in) + val t = c.universe.reify( Def.toITask(i.splice) ).tree + Converted.Success(t) + } + else if(nme == WrapTaskName) + { + val i = c.Expr[Task[T]](in) + val t = c.universe.reify( Def.valueStrict[Task[T]](i.splice) ).tree + Converted.Success(t) + } + else + Converted.NotApplicable +} + +/** Converts an input `Tree` of type `State => Parser[T]` or `Initialize[State => Parser[T]]` +* into a `Tree` of type `Initialize[State => Parser[T]]`.*/ +object InitParserConvert extends Convert +{ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] = + if(nme == ParserInput.WrapName) { + val e = c.Expr[State => Parser[T]](in) + val t = c.universe.reify { Def.valueStrict[State => Parser[T]](e.splice) } + Converted.Success(t.tree) + } + else if(nme == ParserInput.WrapInitName) + Converted.Success(in) + else + Converted.NotApplicable +} + diff --git a/main/settings/src/main/scala/sbt/std/InputWrapper.scala b/main/settings/src/main/scala/sbt/std/InputWrapper.scala new file mode 100644 index 000000000..c1629e6b7 --- /dev/null +++ b/main/settings/src/main/scala/sbt/std/InputWrapper.scala @@ -0,0 +1,164 @@ +package sbt +package std + + import language.experimental.macros + import scala.reflect._ + import reflect.macros._ + import reflect.internal.annotations.compileTimeOnly + + import Def.Initialize + import appmacro.ContextUtil + import complete.Parser + +/** Implementation detail. The wrap methods temporarily hold inputs (as a Tree, at compile time) until a task or setting macro processes it. */ +object InputWrapper +{ + /* The names of the wrapper methods 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 macro system.*/ + + private[std] final val WrapTaskName = "wrapTask_\u2603\u2603" + private[std] final val WrapInitName = "wrapInit_\u2603\u2603" + private[std] final val WrapInitTaskName = "wrapInitTask_\u2603\u2603" + private[std] final val WrapInitInputName = "wrapInitInputTask_\u2603\u2603" + private[std] final val WrapInputName = "wrapInputTask_\u2603\u2603" + + @compileTimeOnly("`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.") + def wrapTask_\u2603\u2603[T](in: Any): T = implDetailError + + @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") + def wrapInit_\u2603\u2603[T](in: Any): T = implDetailError + + @compileTimeOnly("`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task.") + def wrapInitTask_\u2603\u2603[T](in: Any): T = implDetailError + + @compileTimeOnly("`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.") + def wrapInputTask_\u2603\u2603[T](in: Any): T = implDetailError + + @compileTimeOnly("`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask.") + def wrapInitInputTask_\u2603\u2603[T](in: Any): T = implDetailError + + private[this] def implDetailError = error("This method is an implementation detail and should not be referenced.") + + private[std] def wrapTask[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapTaskName)(ts, pos) + private[std] def wrapInit[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapInitName)(ts, pos) + private[std] def wrapInitTask[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapInitTaskName)(ts, pos) + + private[std] def wrapInitInputTask[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapInitInputName)(ts, pos) + private[std] def wrapInputTask[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapInputName)(ts, pos) + + /** Wraps an arbitrary Tree in a call to the `.` method of this module for later processing by an enclosing macro. + * The resulting Tree is the manually constructed version of: + * + * `c.universe.reify { .[T](ts.splice) }` + */ + def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](c: Context, s: S, wrapName: String)(ts: c.Expr[Any], pos: c.Position)(implicit it: c.TypeTag[s.type]): c.Expr[T] = + { + import c.universe.{Apply=>ApplyTree,_} + val util = new ContextUtil[c.type](c) + val iw = util.singleton(s) + val tpe = c.weakTypeOf[T] + val nme = newTermName(wrapName).encoded + val sel = Select(Ident(iw), nme) + sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks + val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil) + tree.setPos(ts.tree.pos) + c.Expr[T](tree) + } + + def valueMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[T] = + ContextUtil.selectMacroImpl[T](c) { (ts,pos) => + val tpe = ts.tree.tpe + if(tpe <:< c.weakTypeOf[Initialize[Task[T]]]) + InputWrapper.wrapInitTask[T](c)(ts,pos) + else if(tpe <:< c.weakTypeOf[Initialize[T]]) + InputWrapper.wrapInit[T](c)(ts,pos) + else if(tpe <:< c.weakTypeOf[Task[T]]) + InputWrapper.wrapTask[T](c)(ts,pos) + else if(tpe <:< c.weakTypeOf[InputTask[T]]) + InputWrapper.wrapInputTask[T](c)(ts,pos) + else if(tpe <:< c.weakTypeOf[Initialize[InputTask[T]]]) + InputWrapper.wrapInitInputTask[T](c)(ts,pos) + + else + c.abort(pos, s"Internal sbt error. Unexpected type $tpe") + } +} + +sealed abstract class MacroValue[T] { + @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") + def value: T = macro InputWrapper.valueMacroImpl[T] +} +sealed abstract class ParserInput[T] { + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") + def parsed: T = macro ParserInput.parsedMacroImpl[T] +} +sealed abstract class ParserInputTask[T] { + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") + def parsed: Task[T] = macro ParserInput.parsedInputMacroImpl[T] +} + +/** Implementation detail. The wrap 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.*/ + private[std] val WrapName = "parser_\u2603\u2603" + private[std] val WrapInitName = "initParser_\u2603\u2603" + + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") + def parser_\u2603\u2603[T](i: Any): T = error("This method is an implementation detail and should not be referenced.") + + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") + def initParser_\u2603\u2603[T](i: Any): T = error("This method is an implementation detail and should not be referenced.") + + private[std] def wrap[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + InputWrapper.wrapImpl[T,ParserInput.type](c, ParserInput, WrapName)(ts, pos) + private[std] def wrapInit[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + InputWrapper.wrapImpl[T,ParserInput.type](c, ParserInput, WrapInitName)(ts, pos) + + private[std] def inputParser[T: c.WeakTypeTag](c: Context)(t: c.Expr[InputTask[T]]): c.Expr[State => Parser[Task[T]]] = c.universe.reify(t.splice.parser) + + def parsedInputMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[Task[T]] = + ContextUtil.selectMacroImpl[Task[T]](c) { (p,pos) => + import c.universe.reify + val tpe = p.tree.tpe + if(tpe <:< c.weakTypeOf[InputTask[T]]) { + val e = c.Expr[InputTask[T]](p.tree) + wrap[Task[T]](c)( inputParser(c)(e), pos ) + } + else if(tpe <:< c.weakTypeOf[Initialize[InputTask[T]]]) { + val e = c.Expr[Initialize[InputTask[T]]](p.tree) + wrapInit[Task[T]](c)( reify { Def.toIParser(e.splice) }, pos ) + } + else + c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.normalize} in parsedInputMacroImpl.") + } + + /** Implements `Parser[T].parsed` by wrapping the Parser with the ParserInput wrapper.*/ + def parsedMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[T] = + ContextUtil.selectMacroImpl[T](c) { (p,pos) => + import c.universe.reify + val tpe = p.tree.tpe + if(tpe <:< c.weakTypeOf[Parser[T]]) { + val e = c.Expr[Parser[T]](p.tree) + wrap[T](c)( reify { Def.toSParser(e.splice) }, pos) + } + else if(tpe <:< c.weakTypeOf[State => Parser[T]]) + wrap[T](c)( p, pos) + else if(tpe <:< c.weakTypeOf[Initialize[Parser[T]]]) { + val e = c.Expr[Initialize[Parser[T]]](p.tree) + val es = reify { Def.toISParser(e.splice) } + wrapInit[T](c)(es, pos) + } + else if(tpe <:< c.weakTypeOf[Initialize[State => Parser[T]]]) + wrapInit[T](c)(p,pos) + else + c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.normalize} in parsedMacroImpl") + } +} diff --git a/main/settings/src/main/scala/sbt/std/SettingMacro.scala b/main/settings/src/main/scala/sbt/std/SettingMacro.scala index 28da3fee0..e87cd8b65 100644 --- a/main/settings/src/main/scala/sbt/std/SettingMacro.scala +++ b/main/settings/src/main/scala/sbt/std/SettingMacro.scala @@ -3,7 +3,7 @@ package std import Def.{Initialize,Setting} import Types.{idFun,Id} - import appmacro.{Convert, Instance, MixedBuilder, MonadInstance} + import appmacro.{Convert, Converted, Instance, MixedBuilder, MonadInstance} object InitializeInstance extends MonadInstance { @@ -13,31 +13,31 @@ object InitializeInstance extends MonadInstance def flatten[T](in: Initialize[Initialize[T]]): Initialize[T] = Def.bind(in)(idFun[Initialize[T]]) def pure[T](t: () => T): Initialize[T] = Def.pure(t) } -object InitializeConvert extends Convert -{ - def apply[T: c.WeakTypeTag](c: reflect.macros.Context)(in: c.Tree): c.Tree = - { - if(in.tpe <:< c.weakTypeOf[Initialize[Task[T]]] || in.tpe <:< c.weakTypeOf[Task[T]]) - c.abort(in.pos, "A setting cannot depend on a task") - else if(in.tpe <:< c.weakTypeOf[Initialize[T]]) - { - val i = c.Expr[Initialize[T]](in) - c.universe.reify( i.splice ).tree - } - else - c.abort(in.pos, "Unknown input type: " + in.tpe) - } -} import language.experimental.macros import scala.reflect._ import reflect.macros._ +object InitializeConvert extends Convert +{ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] = + if(nme == InputWrapper.WrapInitName) + { + val i = c.Expr[Initialize[T]](in) + val t = c.universe.reify( i.splice ).tree + Converted.Success(t) + } + else if(nme == InputWrapper.WrapTaskName || nme == InputWrapper.WrapInitTaskName) + Converted.Failure(in.pos, "A setting cannot depend on a task") + else + Converted.NotApplicable +} + object SettingMacro { def settingMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[T]] = - Instance.contImpl[T](c, InitializeInstance, InitializeConvert, MixedBuilder)(Left(t)) + Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(Left(t), Instance.idTransform[c.type]) def settingDynMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Initialize[T]]): c.Expr[Initialize[T]] = - Instance.contImpl[T](c, InitializeInstance, InitializeConvert, MixedBuilder)(Right(t)) + Instance.contImpl[T, Id](c, InitializeInstance, InitializeConvert, MixedBuilder)(Right(t), Instance.idTransform[c.type]) } diff --git a/main/settings/src/main/scala/sbt/std/TaskMacro.scala b/main/settings/src/main/scala/sbt/std/TaskMacro.scala index 47a61bf93..e004c3f6b 100644 --- a/main/settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main/settings/src/main/scala/sbt/std/TaskMacro.scala @@ -2,10 +2,11 @@ package sbt package std import Def.{Initialize,Setting} - import Types.{idFun,Id} + import Types.{const, idFun,Id} import TaskExtra.allM - import appmacro.{ContextUtil, Convert, InputWrapper, Instance, MixedBuilder, MonadInstance} - import complete.Parser + import appmacro.{ContextUtil, Convert, Converted, Instance, MixedBuilder, MonadInstance} + import Instance.Transform + import complete.{DefaultParsers,Parser} import language.experimental.macros import scala.reflect._ @@ -23,6 +24,20 @@ object TaskInstance extends MonadInstance def flatten[T](in: Task[Task[T]]): Task[T] = in flatMap idFun[Task[T]] def pure[T](t: () => T): Task[T] = toTask(t) } +object ParserInstance extends Instance +{ + import sbt.Classes.Applicative + private[this] implicit val parserApplicative: Applicative[M] = new Applicative[M]{ + def apply[S,T](f: M[S => T], v: M[S]): M[T] = s => (f(s) ~ v(s)) map { case (a,b) => a(b) } + def pure[S](s: => S) = const(Parser.success(s)) + def map[S, T](f: S => T, v: M[S]) = s => v(s).map(f) + } + + final type M[x] = State => Parser[x] + def app[K[L[x]], Z](in: K[M], f: K[Id] => Z)(implicit a: AList[K]): M[Z] = a.apply(in,f) + def map[S,T](in: M[S], f: S => T): M[T] = s => in(s) map f + def pure[T](t: () => T): State => Parser[T] = const(DefaultParsers.success(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 @@ -49,25 +64,6 @@ object FullInstance extends Instance.Composed[Initialize, Task](InitializeInstan } } } -/** 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 = - if(in.tpe <:< c.weakTypeOf[Initialize[Task[T]]]) - in - else if(in.tpe <:< c.weakTypeOf[Initialize[T]]) - { - val i = c.Expr[Initialize[T]](in) - c.universe.reify( Def.toITask(i.splice) ).tree - } - else if(in.tpe <:< c.weakTypeOf[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 { @@ -75,15 +71,14 @@ object TaskMacro final val Append1InitName = "append1" final val AppendNInitName = "appendN" final val TransformInitName = "transform" - final val InputTaskCreateName = "create" final val InputTaskCreateDynName = "createDyn" final val InputTaskCreateFreeName = "createFree" 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)) + Instance.contImpl[T,Id](c, FullInstance, FullConvert, MixedBuilder)(Left(t), Instance.idTransform[c.type]) 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)) + Instance.contImpl[T,Id](c, FullInstance, FullConvert, MixedBuilder)(Right(t), Instance.idTransform[c.type]) /** Implementation of := macro for settings. */ def settingAssignMacroImpl[T: c.WeakTypeTag](c: Context)(v: c.Expr[T]): c.Expr[Setting[T]] = @@ -220,75 +215,83 @@ object TaskMacro c.Expr[T](Literal(Constant(t))) } - sealed abstract class MacroValue[T] { - @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") - def value: T = macro std.TaskMacro.valueMacroImpl[T] - } - - def valueMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[T] = - ContextUtil.selectMacroImpl[T,Any](c)( (ts,pos) => InputWrapper.wrapKey[T](c)(ts,pos) ) - - sealed abstract class RawParserInput[T] { - @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") - def parsed: T = macro std.TaskMacro.rawParserMacro[T] - } - sealed abstract class InitParserInput[T] { - @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") - def parsed: T = macro std.TaskMacro.initParserMacro[T] - } - sealed abstract class StateParserInput[T] { - @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") - 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,pos) => - ParserInput.wrap[T](c)(c.universe.reify { InputTask.parserAsInput(p.splice) }, pos) - } - - /** 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,pos) => - ParserInput.wrap[T](c)(c.universe.reify { InputTask.initParserAsInput(t.splice) }, pos) - } - - /** 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,pos) => - ParserInput.wrap[T](c)(t, pos) - } - - /** 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" - - @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") - def parser_\u2603\u2603[T](i: Initialize[State => Parser[T]]): T = sys.error("This method is an implementation detail and should not be referenced.") - - def wrap[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = - InputWrapper.wrapImpl[T,ParserInput.type](c, ParserInput, WrapName)(ts, pos) - } def inputTaskMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = - inputTaskMacro0[T](c)(Left(t)) + inputTaskMacro0[T](c)(t) def inputTaskDynMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] = - inputTaskMacro0[T](c)(Right(t)) + inputTaskDynMacro0[T](c)(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]]] = + private[this] def inputTaskMacro0[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = + iInitializeMacro(c)(t) { et => + val pt = iParserMacro(c)(et) { pt => + iTaskMacro(c)(pt) + } + c.universe.reify { InputTask.make(pt.splice) } + } + + private[this] def iInitializeMacro[M[_], T](c: Context)(t: c.Expr[T])(f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T], mt: c.WeakTypeTag[M[T]]): c.Expr[Initialize[M[T]]] = + { + val inner: Transform[c.type,M] = new Transform[c.type,M] { def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree } + val cond = c.Expr[T](conditionInputTaskTree(c)(t.tree)) + Instance.contImpl[T,M](c, InitializeInstance, InputInitConvert, MixedBuilder)(Left(cond), inner) + } + private[this] def conditionInputTaskTree(c: Context)(t: c.Tree): c.Tree = + { + import c.universe._ + import InputWrapper._ + def wrapInitTask[T: c.WeakTypeTag](tree: Tree) = + { + val e = c.Expr[Initialize[Task[T]]](tree) + wrapTask[T](c)( wrapInit[Task[T]](c)(e, tree.pos), tree.pos).tree + } + def wrapInitParser[T: c.WeakTypeTag](tree: Tree) = + { + val e = c.Expr[Initialize[State => Parser[T]]](tree) + ParserInput.wrap[T](c)( wrapInit[State => Parser[T]](c)(e, tree.pos), tree.pos).tree + } + def wrapInitInput[T: c.WeakTypeTag](tree: Tree) = + { + val e = c.Expr[Initialize[InputTask[T]]](tree) + wrapInput[T]( wrapInit[InputTask[T]](c)(e, tree.pos).tree ) + } + def wrapInput[T: c.WeakTypeTag](tree: Tree) = + { + val e = c.Expr[InputTask[T]](tree) + val p = ParserInput.wrap[Task[T]](c)( ParserInput.inputParser(c)(e), tree.pos ) + wrapTask[T](c)(p, tree.pos).tree + } + + def expand(nme: String, tpe: Type, tree: Tree): Converted[c.type] = nme match { + case WrapInitTaskName => Converted.Success(wrapInitTask(tree)(c.WeakTypeTag(tpe))) + case ParserInput.WrapInitName => Converted.Success(wrapInitParser(tree)(c.WeakTypeTag(tpe))) + case WrapInitInputName => Converted.Success(wrapInitInput(tree)(c.WeakTypeTag(tpe))) + case WrapInputName => Converted.Success(wrapInput(tree)(c.WeakTypeTag(tpe))) + case _ => Converted.NotApplicable + } + val util = ContextUtil[c.type](c) + util.transformWrappers(t, (nme,tpe,tree) => expand(nme,tpe,tree)) + } + + private[this] def iParserMacro[M[_], T](c: Context)(t: c.Expr[T])(f: c.Expr[T] => c.Expr[M[T]])(implicit tt: c.WeakTypeTag[T], mt: c.WeakTypeTag[M[T]]): c.Expr[State => Parser[M[T]]] = + { + val inner: Transform[c.type,M] = new Transform[c.type,M] { def apply(in: c.Tree): c.Tree = f(c.Expr[T](in)).tree } + Instance.contImpl[T,M](c, ParserInstance, ParserConvert, MixedBuilder)(Left(t), inner) + } + + private[this] def iTaskMacro[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Task[T]] = + Instance.contImpl[T,Id](c, TaskInstance, TaskConvert, MixedBuilder)(Left(t), Instance.idTransform) + + private[this] def inputTaskDynMacro0[T: c.WeakTypeTag](c: Context)(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 isParserWrapper = InitParserConvert.asPredicate(c) + val isTaskWrapper = FullConvert.asPredicate(c) + val isAnyWrapper = (n: String, tpe: Type, tr: Tree) => isParserWrapper(n,tpe,tr) || isTaskWrapper(n,tpe,tr) + val ttree = t.tree val defs = util.collectDefs(ttree, isAnyWrapper) val checkQual = util.checkReferences(defs, isAnyWrapper) @@ -297,7 +300,7 @@ object TaskMacro def subWrapper(tpe: Type, qual: Tree): Tree = if(result.isDefined) { - c.error(qual.pos, "An InputTask can only have a single input parser.") + c.error(qual.pos, "Implementation restriction: a dynamic InputTask can only have a single input parser.") EmptyTree } else @@ -332,44 +335,37 @@ object TaskMacro taskMacroImpl[I](c)( c.Expr[I](tx) ) def wrapTag[I: WeakTypeTag]: WeakTypeTag[Initialize[Task[I]]] = weakTypeTag - val tx = util.transformWrappers(ttree, isParserWrapper, (tpe,tree) => subWrapper(tpe,tree)) + def sub(name: String, tpe: Type, qual: Tree): Converted[c.type] = + { + val tag = c.WeakTypeTag[T](tpe) + InitParserConvert(c)(name, qual)(tag) transform { tree => + subWrapper(tpe, tree) + } + } + + val tx = util.transformWrappers(ttree, (n,tpe,tree) => sub(n,tpe,tree)) result match { case Some((p, tpe, param)) => val fCore = Function(param :: Nil, tx) - val bodyTpe = if(t.isRight) wrapTag(tag).tpe else tag.tpe + val bodyTpe = wrapTag(tag).tpe val fTpe = util.functionType(tpe :: Nil, bodyTpe) val fTag = c.WeakTypeTag[Any](fTpe) // don't know the actual type yet, so use Any val fInit = c.resetLocalAttrs( expandTask(false, fCore)(fTag).tree ) - inputTaskCreate(if(t.isRight) InputTaskCreateDynName else InputTaskCreateName, tpe, tag.tpe, p, fInit) + inputTaskCreate(InputTaskCreateDynName, tpe, tag.tpe, p, fInit) case None => - val init = c.resetLocalAttrs( expandTask[T](t.isRight, tx).tree ) + val init = c.resetLocalAttrs( expandTask[T](true, tx).tree ) inputTaskCreateFree(tag.tpe, init) } } } - -/** 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 <:< c.weakTypeOf[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)) + Instance.contImpl[T,Id](c, TaskInstance, TaskConvert, MixedBuilder)(Left(t), Instance.idTransform[c.type]) 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)) + Instance.contImpl[T,Id](c, TaskInstance, TaskConvert, MixedBuilder)(Right(t), Instance.idTransform[c.type]) } diff --git a/main/settings/src/test/scala/UsageTest.scala b/main/settings/src/test/scala/UsageTest.scala index 7ae25d39c..16ca199ee 100644 --- a/main/settings/src/test/scala/UsageTest.scala +++ b/main/settings/src/test/scala/UsageTest.scala @@ -38,7 +38,8 @@ object Assign val dummyt = taskKey[complete.Parser[String]]("dummyt") val dummys = settingKey[complete.Parser[String]]("dummys") val dummy3 = settingKey[complete.Parser[(String,Int)]]("dummy3") - val tsk: complete.Parser[TaskKey[String]] = ??? + val tsk: complete.Parser[Task[String]] = ??? + val itsk: Initialize[InputTask[Int]] = ??? /* def azy = sk.value @@ -59,9 +60,8 @@ object Assign mk := 3, name := "asdf", tk := (math.random*1000).toInt, - isk := tsk.parsed.value, // ParserInput.wrap[TaskKey[String]](tsk).value -// isk := dummys.value.parsed , // should not compile: cannot use a task to define the parser - ik := { if( tsk.parsed.value == "blue") tk.value else mk.value } + isk := dummys.value.parsed // should not compile: cannot use a task to define the parser +// ik := { if( tsk.parsed.value == "blue") tk.value else mk.value } ) val it1 = Def.inputTask { @@ -70,12 +70,12 @@ object Assign val it2 = Def.inputTask { "lit" } - // should not compile because getting the value from a parser involves getting the value from a task -/* val it3: Initialize[InputTask[String]] = Def.inputTask[String] { - tsk.parsed.value - }*/ -/* // should not compile: cannot use a task to define the parser - val it4 = Def.inputTask { + + val it3: Initialize[InputTask[String]] = Def.inputTask[String] { + tsk.parsed.value + itsk.parsed.value.toString + isk.value + } + // should not compile: cannot use a task to define the parser +/* val it4 = Def.inputTask { dummyt.value.parsed }*/ // should compile: can use a setting to define the parser @@ -83,12 +83,17 @@ object Assign dummys.parsed } val it6 = Def.inputTaskDyn { - val (x,i) = dummy3.parsed + val d3 = dummy3.parsed + val x = d3._1 + val i = d3._2 Def.task { tk.value + i} } + val it7 = Def.inputTask { + it5.parsed + } -/* def bool: Initialize[Boolean] = Def.setting { true } + def bool: Initialize[Boolean] = Def.setting { true } def enabledOnly[T](key: Initialize[T]): Initialize[Seq[T]] = Def.setting { val keys: Seq[T] = forallIn(key).value val enabled: Seq[Boolean] = forallIn(bool).value @@ -96,5 +101,5 @@ object Assign } def forallIn[T](key: Initialize[T]): Initialize[Seq[T]] = Def.setting { key.value :: Nil - }*/ + } } \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index b9fe29388..48dd32466 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -20,11 +20,11 @@ object ContextUtil { * * Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro that uses this * method, the result of this method is `f()`. */ - def selectMacroImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: Context)(f: (c.Expr[S], c.Position) => c.Expr[T]): c.Expr[T] = + def selectMacroImpl[T: c.WeakTypeTag](c: Context)(f: (c.Expr[Any], c.Position) => c.Expr[T]): c.Expr[T] = { import c.universe._ c.macroApplication match { - case s @ Select(Apply(_, t :: Nil), tp) => f( c.Expr[S](t), s.pos ) + case s @ Select(Apply(_, t :: Nil), tp) => f( c.Expr[Any](t), s.pos ) case x => unexpectedTree(x) } } @@ -61,24 +61,18 @@ final class ContextUtil[C <: Context](val ctx: C) vd } - /* Tests whether a Tree is a Select on `methodName`. */ - def isWrapper(methodName: String): Tree => Boolean = { - case Select(_, nme) => nme.decoded == methodName - case _ => false - } - lazy val parameterModifiers = Modifiers(Flag.PARAM) /** Collects all definitions in the tree for use in checkReferences. * This excludes definitions in wrapped expressions because checkReferences won't allow nested dereferencing anyway. */ - def collectDefs(tree: Tree, isWrapper: Tree => Boolean): collection.Set[Symbol] = + def collectDefs(tree: Tree, isWrapper: (String, Type, Tree) => Boolean): collection.Set[Symbol] = { val defs = new collection.mutable.HashSet[Symbol] // adds the symbols for all non-Ident subtrees to `defs`. val process = new Traverser { override def traverse(t: Tree) = t match { case _: Ident => () - case ApplyTree(TypeApply(fun, tpe :: Nil), qual :: Nil) if isWrapper(fun) => () + case ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) if isWrapper(nme.decoded, tpe.tpe, qual) => () case tree => if(tree.symbol ne null) defs += tree.symbol; super.traverse(tree) @@ -95,8 +89,9 @@ final class ContextUtil[C <: Context](val ctx: C) /** A function that checks the provided tree for illegal references to M instances defined in the * expression passed to the macro and for illegal dereferencing of M instances. */ - def checkReferences(defs: collection.Set[Symbol], isWrapper: Tree => Boolean): Tree => Unit = { - case s @ ApplyTree(TypeApply(fun, tpe :: Nil), qual :: Nil) => if(isWrapper(fun)) ctx.error(s.pos, DynamicDependencyError) + def checkReferences(defs: collection.Set[Symbol], isWrapper: (String, Type, Tree) => Boolean): Tree => Unit = { + case s @ ApplyTree(TypeApply(Select(_, nme), tpe :: Nil), qual :: Nil) => + if(isWrapper(nme.decoded, tpe.tpe, qual)) ctx.error(s.pos, DynamicDependencyError) case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name) case _ => () } @@ -189,10 +184,10 @@ final class ContextUtil[C <: Context](val ctx: C) } /** Substitutes wrappers in tree `t` with the result of `subWrapper`. - * A wrapper is a Tree of the form `f[T](v)` for which isWrapper() returns true. + * A wrapper is a Tree of the form `f[T](v)` for which isWrapper(, , .target) returns true. * Typically, `f` is a `Select` or `Ident`. * The wrapper is replaced with the result of `subWrapper(, )` */ - def transformWrappers(t: Tree, isWrapper: Tree => Boolean, subWrapper: (Type, Tree) => Tree): Tree = + def transformWrappers(t: Tree, subWrapper: (String, Type, Tree) => Converted[ctx.type]): Tree = { // the main tree transformer that replaces calls to InputWrapper.wrap(x) with // plain Idents that reference the actual input value @@ -201,9 +196,11 @@ final class ContextUtil[C <: Context](val ctx: C) override def transform(tree: Tree): Tree = tree match { - case ApplyTree(TypeApply(fun, targ :: Nil), qual :: Nil) if isWrapper(fun) => - assert(qual.tpe != null, "Internal error: null type for wrapped tree with " + qual.getClass + "\n\t" + qual + "\n in " + t) - subWrapper(targ.tpe, qual) + case ApplyTree(TypeApply(Select(_, nme), targ :: Nil), qual :: Nil) => subWrapper(nme.decoded, targ.tpe, qual) match { + case Converted.Success(t, finalTx) => finalTx(t) + case Converted.Failure(p,m) => ctx.abort(p, m) + case _: Converted.NotApplicable[_] => super.transform(tree) + } case _ => super.transform(tree) } } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala b/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala new file mode 100644 index 000000000..6dedf776b --- /dev/null +++ b/util/appmacro/src/main/scala/sbt/appmacro/Convert.scala @@ -0,0 +1,38 @@ +package sbt +package appmacro + + import scala.reflect._ + import macros._ + import Types.idFun + +abstract class Convert +{ + def apply[T: c.WeakTypeTag](c: Context)(nme: String, in: c.Tree): Converted[c.type] + def asPredicate(c: Context): (String, c.Type, c.Tree) => Boolean = + (n,tpe,tree) => { + val tag = c.WeakTypeTag(tpe) + apply(c)(n,tree)(tag).isSuccess + } +} +sealed trait Converted[C <: Context with Singleton] { + def isSuccess: Boolean + def transform(f: C#Tree => C#Tree): Converted[C] +} +object Converted { + def NotApplicable[C <: Context with Singleton] = new NotApplicable[C] + final case class Failure[C <: Context with Singleton](position: C#Position, message: String) extends Converted[C] { + def isSuccess = false + def transform(f: C#Tree => C#Tree): Converted[C] = new Failure(position, message) + } + final class NotApplicable[C <: Context with Singleton] extends Converted[C] { + def isSuccess = false + def transform(f: C#Tree => C#Tree): Converted[C] = this + } + final case class Success[C <: Context with Singleton](tree: C#Tree, finalTransform: C#Tree => C#Tree) extends Converted[C] { + def isSuccess = true + def transform(f: C#Tree => C#Tree): Converted[C] = Success(f(tree), finalTransform) + } + object Success { + def apply[C <: Context with Singleton](tree: C#Tree): Success[C] = Success(tree, idFun) + } +} \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index d86fb2a4c..3e8b45cf0 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -16,10 +16,7 @@ trait Instance def map[S,T](in: M[S], f: S => T): M[T] def pure[T](t: () => T): M[T] } -trait Convert -{ - def apply[T: c.WeakTypeTag](c: scala.reflect.macros.Context)(in: c.Tree): c.Tree -} + trait MonadInstance extends Instance { def flatten[T](in: M[M[T]]): M[T] @@ -29,39 +26,6 @@ trait MonadInstance extends Instance import macros._ import reflect.internal.annotations.compileTimeOnly -// This needs to be moved to main/settings -object InputWrapper -{ - /** 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 macro system.*/ - final val WrapName = "wrap_\u2603\u2603" - - @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") - def wrap_\u2603\u2603[T](in: Any): T = sys.error("This method is an implementation detail and should not be referenced.") - - def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapName)(ts, pos) - - /** Wraps an arbitrary Tree in a call to the `.` method of this module for later processing by an enclosing macro. - * The resulting Tree is the manually constructed version of: - * - * `c.universe.reify { .[T](ts.splice) }` - */ - def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](c: Context, s: S, wrapName: String)(ts: c.Expr[Any], pos: c.Position)(implicit it: c.TypeTag[s.type]): c.Expr[T] = - { - import c.universe.{Apply=>ApplyTree,_} - val util = new ContextUtil[c.type](c) - val iw = util.singleton(s) - val tpe = c.weakTypeOf[T] - val nme = newTermName(wrapName).encoded - val sel = util.select(Ident(iw), nme) - sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks - val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil) - tree.setPos(ts.tree.pos) - c.Expr[T](tree) - } -} - object Instance { final val ApplyName = "app" @@ -71,11 +35,17 @@ object Instance final val InstanceTCName = "M" final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef) + trait Transform[C <: Context with Singleton, N[_]] { + def apply(in: C#Tree): C#Tree + } + def idTransform[C <: Context with Singleton]: Transform[C,Id] = new Transform[C,Id] { + def apply(in: C#Tree): C#Tree = in + } /** Implementation of a macro that provides a direct syntax for applicative functors and monads. * It is intended to be used in conjunction with another macro that conditions the inputs. * - * This method processes the Tree `t` to find inputs of the form `InputWrapper.wrap[T]( input )` + * This method processes the Tree `t` to find inputs of the form `wrap[T]( input )` * This form is typically constructed by another macro that pretends to be able to get a value of type `T` * from a value convertible to `M[T]`. This `wrap(input)` form has two main purposes. * First, it identifies the inputs that should be transformed. @@ -85,7 +55,7 @@ object Instance * allowing the original `Tree` and `Type` to be hidden behind the raw `T` type. This method will remove the call to `wrap` * so that it is not actually called at runtime. * - * Each `input` in each expression of the form `InputWrapper.wrap[T]( input )` is transformed by `convert`. + * Each `input` in each expression of the form `wrap[T]( input )` is transformed by `convert`. * This transformation converts the input Tree to a Tree of type `M[T]`. * The original wrapped expression `wrap(input)` is replaced by a reference to a new local `val $x: T`, where `$x` is a fresh name. * These converted inputs are passed to `builder` as well as the list of these synthetic `ValDef`s. @@ -107,18 +77,18 @@ object Instance * If this is for multi-input flatMap (app followed by flatMap), * this should be the argument wrapped in Right. */ - def contImpl[T](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]])( - implicit tt: c.WeakTypeTag[T], it: c.TypeTag[i.type]): c.Expr[i.M[T]] = + def contImpl[T,N[_]](c: Context, i: Instance with Singleton, convert: Convert, builder: TupleBuilder)(t: Either[c.Expr[T], c.Expr[i.M[T]]], inner: Transform[c.type,N])( + implicit tt: c.WeakTypeTag[T], nt: c.WeakTypeTag[N[T]], it: c.TypeTag[i.type]): c.Expr[i.M[N[T]]] = { import c.universe.{Apply=>ApplyTree,_} val util = ContextUtil[c.type](c) val mTC: Type = util.extractTC(i, InstanceTCName) - val mttpe: Type = appliedType(mTC, tt.tpe :: Nil).normalize + val mttpe: Type = appliedType(mTC, nt.tpe :: Nil).normalize // the tree for the macro argument val (tree, treeType) = t match { - case Left(l) => (l.tree, tt.tpe.normalize) + case Left(l) => (l.tree, nt.tpe.normalize) case Right(r) => (r.tree, mttpe) } @@ -126,7 +96,7 @@ object Instance // A Tree that references the statically accessible Instance that provides the actual implementations of map, flatMap, ... val instance = Ident(instanceSym) - val isWrapper: Tree => Boolean = util.isWrapper(InputWrapper.WrapName) + val isWrapper: (String, Type, Tree) => Boolean = convert.asPredicate(c) type In = Input[c.universe.type] var inputs = List[In]() @@ -194,16 +164,19 @@ object Instance inputs ::= new Input(tpe, qual, vd) util.refVal(vd) } - def sub(tpe: Type, qual: Tree): Tree = + def sub(name: String, tpe: Type, qual: Tree): Converted[c.type] = { - val tag = c.WeakTypeTag(tpe) - addType(tpe, convert(c)(qual)(tag) ) + val tag = c.WeakTypeTag[T](tpe) + convert[T](c)(name, qual)(tag) transform { tree => + addType(tpe, tree) + } } // applies the transformation + val tx = util.transformWrappers(tree, (n,tpe,t) => sub(n,tpe,t)) // resetting attributes must be: a) local b) done here and not wider or else there are obscure errors - val tr = makeApp( c.resetLocalAttrs( util.transformWrappers(tree, isWrapper, (tpe, tr) => sub(tpe, tr)) ) ) - c.Expr[i.M[T]](tr) + val tr = makeApp( c.resetLocalAttrs( inner(tx) ) ) + c.Expr[i.M[N[T]]](tr) } import Types._