mirror of https://github.com/sbt/sbt.git
Construct input tasks in multiple steps to allow input task reuse. Fixes #407.
This commit is contained in:
parent
f61f87632c
commit
d6f78db0c9
|
|
@ -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] =
|
||||
|
|
|
|||
|
|
@ -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] = ???
|
||||
}
|
||||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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._
|
||||
|
|
|
|||
Loading…
Reference in New Issue