Construct input tasks in multiple steps to allow input task reuse. Fixes #407.

This commit is contained in:
Mark Harrah 2013-03-08 14:23:30 -05:00
parent f61f87632c
commit d6f78db0c9
11 changed files with 481 additions and 219 deletions

View File

@ -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] =

View File

@ -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] = ???
}

View File

@ -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. */

View File

@ -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
}

View File

@ -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 `<s>.<wrapName>` method of this module for later processing by an enclosing macro.
* The resulting Tree is the manually constructed version of:
*
* `c.universe.reify { <s>.<wrapName>[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")
}
}

View File

@ -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])
}

View File

@ -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])
}

View File

@ -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
}*/
}
}

View File

@ -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(<Tree of someValue>)`. */
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(<Tree of f>) returns true.
* A wrapper is a Tree of the form `f[T](v)` for which isWrapper(<Tree of f>, <Underlying Type>, <qual>.target) returns true.
* Typically, `f` is a `Select` or `Ident`.
* The wrapper is replaced with the result of `subWrapper(<Type of T>, <Tree of v>)` */
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)
}
}

View File

@ -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)
}
}

View File

@ -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 `<s>.<wrapName>` method of this module for later processing by an enclosing macro.
* The resulting Tree is the manually constructed version of:
*
* `c.universe.reify { <s>.<wrapName>[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._