mirror of https://github.com/sbt/sbt.git
InputTask macro
Similar to task macros, the parsed value is accessed by calling `parsed` on a Parser[T], Initialize[Parser[T]], or Initialize[State => Parser[T]]. Values of tasks and settings may be accessed as usual via `value`.
This commit is contained in:
parent
79aeb7b00e
commit
49e7214fe3
|
|
@ -397,16 +397,26 @@ object Defaults extends BuildCommon
|
|||
}
|
||||
def succeededFile(dir: File) = dir / "succeeded_tests"
|
||||
|
||||
def inputTests(key: InputKey[_]): Initialize[InputTask[Unit]] =
|
||||
InputTask( loadForParser(definedTestNames)( (s, i) => testOnlyParser(s, i getOrElse Nil) ) ) { result =>
|
||||
(streams, loadedTestFrameworks, testFilter in key, testGrouping in key, testExecution in key, testLoader, resolvedScoped, result, fullClasspath in key, javaHome in key, state) flatMap {
|
||||
case (s, frameworks, filter, groups, config, loader, scoped, (selected, frameworkOptions), cp, javaHome, st) =>
|
||||
implicit val display = Project.showContextKey(st)
|
||||
val modifiedOpts = Tests.Filter(filter(selected)) +: Tests.Argument(frameworkOptions : _*) +: config.options
|
||||
val newConfig = config.copy(options = modifiedOpts)
|
||||
allTestGroupsTask(s, frameworks, loader, groups, newConfig, cp, javaHome) map (Tests.showResults(s.log, _, noTestsMessage(scoped)))
|
||||
}
|
||||
def inputTests(key: InputKey[_]): Initialize[InputTask[Unit]] = inputTests0.mapReferenced(Def.mapScope(_ in key.key))
|
||||
private[this] lazy val inputTests0: Initialize[InputTask[Unit]] =
|
||||
{
|
||||
val parser = loadForParser(definedTestNames)( (s, i) => testOnlyParser(s, i getOrElse Nil) )
|
||||
Def.inputTaskDyn {
|
||||
val (selected, frameworkOptions) = parser.parsed
|
||||
val s = streams.value
|
||||
val filter = testFilter.value
|
||||
val config = testExecution.value
|
||||
|
||||
implicit val display = Project.showContextKey(state.value)
|
||||
val modifiedOpts = Tests.Filter(filter(selected)) +: Tests.Argument(frameworkOptions : _*) +: config.options
|
||||
val newConfig = config.copy(options = modifiedOpts)
|
||||
val groupsTask = allTestGroupsTask(s, loadedTestFrameworks.value, testLoader.value, testGrouping.value, newConfig, fullClasspath.value, javaHome.value)
|
||||
val processed =
|
||||
for(out <- groupsTask) yield
|
||||
Tests.showResults(s.log, out, noTestsMessage(resolvedScoped.value))
|
||||
Def.value(processed)
|
||||
}
|
||||
}
|
||||
|
||||
def allTestGroupsTask(s: TaskStreams, frameworks: Map[TestFramework,Framework], loader: ClassLoader, groups: Seq[Tests.Group], config: Tests.Execution, cp: Classpath, javaHome: Option[File]): Task[Tests.Output] = {
|
||||
val groupTasks = groups map {
|
||||
|
|
@ -537,23 +547,25 @@ object Defaults extends BuildCommon
|
|||
IO.delete(clean)
|
||||
IO.move(mappings.map(_.swap))
|
||||
}
|
||||
def runMainTask(classpath: TaskKey[Classpath], scalaRun: TaskKey[ScalaRun]): Initialize[InputTask[Unit]] =
|
||||
def runMainTask(classpath: Initialize[Task[Classpath]], scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] =
|
||||
{
|
||||
import DefaultParsers._
|
||||
InputTask( loadForParser(discoveredMainClasses)( (s, names) => runMainParser(s, names getOrElse Nil) ) ) { result =>
|
||||
(classpath, scalaRun, streams, result) map { case (cp, runner, s, (mainClass, args)) =>
|
||||
toError(runner.run(mainClass, data(cp), args, s.log))
|
||||
}
|
||||
val parser = loadForParser(discoveredMainClasses)( (s, names) => runMainParser(s, names getOrElse Nil) )
|
||||
Def.inputTask {
|
||||
val (mainClass, args) = parser.parsed
|
||||
toError(scalaRun.value.run(mainClass, data(classpath.value), args, streams.value.log))
|
||||
}
|
||||
}
|
||||
|
||||
def runTask(classpath: TaskKey[Classpath], mainClassTask: TaskKey[Option[String]], scalaRun: TaskKey[ScalaRun]): Initialize[InputTask[Unit]] =
|
||||
inputTask { result =>
|
||||
(classpath, mainClassTask, scalaRun, streams, result) map { (cp, main, runner, s, args) =>
|
||||
val mainClass = main getOrElse error("No main class detected.")
|
||||
toError(runner.run(mainClass, data(cp), args, s.log))
|
||||
}
|
||||
def runTask(classpath: Initialize[Task[Classpath]], mainClassTask: Initialize[Task[Option[String]]], scalaRun: Initialize[Task[ScalaRun]]): Initialize[InputTask[Unit]] =
|
||||
{
|
||||
import Def.parserToInput
|
||||
val parser = Def.spaceDelimited()
|
||||
Def.inputTask {
|
||||
val mainClass = mainClassTask.value getOrElse error("No main class detected.")
|
||||
toError(scalaRun.value.run(mainClass, data(classpath.value), parser.parsed, streams.value.log))
|
||||
}
|
||||
}
|
||||
|
||||
def runnerTask = runner <<= runnerInit
|
||||
def runnerInit: Initialize[Task[ScalaRun]] =
|
||||
|
|
@ -1371,7 +1383,9 @@ trait BuildExtra extends BuildCommon
|
|||
}
|
||||
trait BuildCommon
|
||||
{
|
||||
def inputTask[T](f: TaskKey[Seq[String]] => Initialize[Task[T]]): Initialize[InputTask[T]] = InputTask(_ => complete.Parsers.spaceDelimited("<arg>"))(f)
|
||||
@deprecated("Use Def.inputTask with the `Def.spaceDelimited()` parser.", "0.13.0")
|
||||
def inputTask[T](f: TaskKey[Seq[String]] => Initialize[Task[T]]): Initialize[InputTask[T]] =
|
||||
InputTask.create(Def.value((s: State) => Def.spaceDelimited()))(f)
|
||||
|
||||
implicit def globFilter(expression: String): NameFilter = GlobFilter(expression)
|
||||
implicit def richAttributed(s: Seq[Attributed[File]]): RichAttributed = new RichAttributed(s)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package sbt
|
||||
|
||||
import complete.Parser
|
||||
import java.io.File
|
||||
|
||||
/** A concrete settings system that uses `sbt.Scope` for the scope type. */
|
||||
object Def extends Init[Scope]
|
||||
object Def extends Init[Scope] with TaskMacroExtra
|
||||
{
|
||||
type Classpath = Seq[Attributed[File]]
|
||||
|
||||
|
|
@ -35,22 +36,38 @@ object Def extends Init[Scope]
|
|||
case None => s
|
||||
}
|
||||
|
||||
/** A default Parser for splitting input into space-separated arguments.
|
||||
* `argLabel` is an optional, fixed label shown for an argument during tab completion.*/
|
||||
def spaceDelimited(argLabel: String = "<arg>"): Parser[Seq[String]] = complete.Parsers.spaceDelimited(argLabel)
|
||||
|
||||
/** Lifts the result of a setting initialization into a Task. */
|
||||
def toITask[T](i: Initialize[T]): Initialize[Task[T]] = map(i)(std.TaskExtra.inlineTask)
|
||||
|
||||
import language.experimental.macros
|
||||
import std.TaskMacro.{MacroValue, taskDynMacroImpl, taskMacroImpl}
|
||||
import std.TaskMacro.{InitParserInput, inputTaskMacroImpl, inputTaskDynMacroImpl, MacroValue, taskDynMacroImpl, taskMacroImpl, StateParserInput}
|
||||
import std.SettingMacro.{settingDynMacroImpl,settingMacroImpl}
|
||||
|
||||
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]
|
||||
def setting[T](t: T): Def.Initialize[T] = macro settingMacroImpl[T]
|
||||
def settingDyn[T](t: Def.Initialize[T]): Def.Initialize[T] = macro settingDynMacroImpl[T]
|
||||
def inputTask[T](t: T): Def.Initialize[InputTask[T]] = macro inputTaskMacroImpl[T]
|
||||
def inputTaskDyn[T](t: Def.Initialize[Task[T]]): Def.Initialize[InputTask[T]] = macro inputTaskDynMacroImpl[T]
|
||||
|
||||
// The following conversions enable the types Initialize[T], Initialize[Task[T]], and Task[T] to
|
||||
// be used in task and setting macros as inputs with an ultimate result of type T
|
||||
|
||||
implicit def macroValueI[T](in: Initialize[T]): MacroValue[T] = ???
|
||||
implicit def macroValueIT[T](in: Initialize[Task[T]]): MacroValue[T] = ???
|
||||
implicit def macroValueT[T](in: Task[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] = ???
|
||||
}
|
||||
// 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] = ???
|
||||
}
|
||||
|
|
@ -28,25 +28,38 @@ private sealed trait InputDynamic[T] extends InputTask[T]
|
|||
}
|
||||
object InputTask
|
||||
{
|
||||
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def static[T](p: Parser[Task[T]]): InputTask[T] = free(_ => p)
|
||||
|
||||
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def static[I,T](p: Parser[I])(c: I => Task[T]): InputTask[T] = static(p map c)
|
||||
|
||||
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def free[T](p: State => Parser[Task[T]]): InputTask[T] = new InputStatic[T](p)
|
||||
|
||||
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def free[I,T](p: State => Parser[I])(c: I => Task[T]): InputTask[T] = free(s => p(s) map c)
|
||||
|
||||
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def separate[I,T](p: State => Parser[I])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
|
||||
separate(Def value p)(action)
|
||||
|
||||
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def separate[I,T](p: Initialize[State => Parser[I]])(action: Initialize[I => Task[T]]): Initialize[InputTask[T]] =
|
||||
p.zipWith(action)((parser, act) => free(parser)(act))
|
||||
|
||||
|
||||
private[sbt] lazy val inputMap: Task[Map[AnyRef,Any]] = mktask { error("Internal sbt error: input map not substituted.") }
|
||||
|
||||
@deprecated("Use the non-overloaded `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def apply[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
|
||||
create(p)(action)
|
||||
|
||||
// This interface allows the Parser to be constructed using other Settings, but not Tasks (which is desired).
|
||||
// The action can be constructed using Settings and Tasks and with the parse result injected into a Task.
|
||||
// This is the ugly part, requiring hooks in Load.finalTransforms and Aggregation.applyDynamicTasks
|
||||
// to handle the dummy task for the parse result.
|
||||
// However, this results in a minimal interface to the full capabilities of an InputTask for users
|
||||
def apply[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
|
||||
def create[I,T](p: Initialize[State => Parser[I]])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
|
||||
{
|
||||
val key: TaskKey[I] = Def.parseResult.asInstanceOf[TaskKey[I]]
|
||||
(p zip Def.resolvedScoped zipWith action(key)) { case ((parserF, scoped), act) =>
|
||||
|
|
@ -59,6 +72,17 @@ object InputTask
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A dummy parser that consumes no input and produces nothing useful (unit).*/
|
||||
def emptyParser: Initialize[State => Parser[Unit]] = parserAsInput(complete.DefaultParsers.success(()))
|
||||
|
||||
/** Implementation detail that is public because it is used by a macro.*/
|
||||
def parserAsInput[T](p: Parser[T]): Initialize[State => Parser[T]] = Def.valueStrict(Types.const(p))
|
||||
|
||||
/** Implementation detail that is public because it is used y a macro.*/
|
||||
def initParserAsInput[T](i: Initialize[Parser[T]]): Initialize[State => Parser[T]] = i(Types.const)
|
||||
|
||||
@deprecated("Use `create` or the `Def.inputTask` macro.", "0.13.0")
|
||||
def apply[I,T](p: State => Parser[I])(action: TaskKey[I] => Initialize[Task[T]]): Initialize[InputTask[T]] =
|
||||
apply(Def.value(p))(action)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,15 @@ sealed trait ScopedTaskable[T] extends Scoped {
|
|||
sealed trait SettingKey[T] extends ScopedTaskable[T] with KeyedInitialize[T] with Scoped.ScopingSetting[SettingKey[T]] with Scoped.DefinableSetting[T]
|
||||
{
|
||||
val key: AttributeKey[T]
|
||||
def toTask: Initialize[Task[T]] = this apply inlineTask
|
||||
def scopedKey: ScopedKey[T] = ScopedKey(scope, key)
|
||||
def in(scope: Scope): SettingKey[T] = Scoped.scopedSetting(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
final def toTask: Initialize[Task[T]] = this apply inlineTask
|
||||
final def scopedKey: ScopedKey[T] = ScopedKey(scope, key)
|
||||
final def in(scope: Scope): SettingKey[T] = Scoped.scopedSetting(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
|
||||
def +=[U](v: U)(implicit a: Append.Value[T, U]): Setting[T] = macro std.TaskMacro.settingAppend1Impl[T,U]
|
||||
def ++=[U](vs: U)(implicit a: Append.Values[T, U]): Setting[T] = macro std.TaskMacro.settingAppendNImpl[T,U]
|
||||
def <+= [V](value: Initialize[V])(implicit a: Append.Value[T, V]): Setting[T] = make(value)(a.appendValue)
|
||||
def <++= [V](values: Initialize[V])(implicit a: Append.Values[T, V]): Setting[T] = make(values)(a.appendValues)
|
||||
final def := (v: T): Setting[T] = macro std.TaskMacro.settingAssignMacroImpl[T]
|
||||
final def +=[U](v: U)(implicit a: Append.Value[T, U]): Setting[T] = macro std.TaskMacro.settingAppend1Impl[T,U]
|
||||
final def ++=[U](vs: U)(implicit a: Append.Values[T, U]): Setting[T] = macro std.TaskMacro.settingAppendNImpl[T,U]
|
||||
final def <+= [V](value: Initialize[V])(implicit a: Append.Value[T, V]): Setting[T] = make(value)(a.appendValue)
|
||||
final def <++= [V](values: Initialize[V])(implicit a: Append.Values[T, V]): Setting[T] = make(values)(a.appendValues)
|
||||
|
||||
protected[this] def make[S](other: Initialize[S])(f: (T, S) => T): Setting[T] = this <<= (this, other)(f)
|
||||
}
|
||||
|
|
@ -72,6 +73,8 @@ sealed trait InputKey[T] extends Scoped with KeyedInitialize[InputTask[T]] with
|
|||
val key: AttributeKey[InputTask[T]]
|
||||
def scopedKey: ScopedKey[InputTask[T]] = ScopedKey(scope, key)
|
||||
def in(scope: Scope): InputKey[T] = Scoped.scopedInput(Scope.replaceThis(this.scope)(scope), this.key)
|
||||
|
||||
def :=(v: T): Setting[InputTask[T]] = macro std.TaskMacro.inputTaskAssignMacroImpl[T]
|
||||
}
|
||||
|
||||
/** Methods and types related to constructing settings, including keys, scopes, and initializations. */
|
||||
|
|
@ -103,7 +106,6 @@ object Scoped
|
|||
def scopedKey: ScopedKey[S]
|
||||
|
||||
private[sbt] final def :==(value: S): Setting[S] = setting(scopedKey, Def.valueStrict(value))
|
||||
final def := (v: S): Setting[S] = macro std.TaskMacro.settingAssignMacroImpl[S]
|
||||
final def ~= (f: S => S): Setting[S] = Def.update(scopedKey)(f)
|
||||
final def <<= (app: Initialize[S]): Setting[S] = set(app)
|
||||
final def set (app: Initialize[S]): Setting[S] = setting(scopedKey, app)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ package std
|
|||
import Def.{Initialize,Setting}
|
||||
import Types.{idFun,Id}
|
||||
import TaskExtra.allM
|
||||
import appmacro.{Convert, InputWrapper, Instance, MixedBuilder, MonadInstance}
|
||||
import appmacro.{ContextUtil, Convert, InputWrapper, Instance, MixedBuilder, MonadInstance}
|
||||
import complete.Parser
|
||||
|
||||
import language.experimental.macros
|
||||
import scala.reflect._
|
||||
|
|
@ -66,6 +67,7 @@ object TaskMacro
|
|||
final val AssignInitName = "<<="
|
||||
final val Append1InitName = "<+="
|
||||
final val AppendNInitName = "<++="
|
||||
final val InputTaskCreateName = "create"
|
||||
|
||||
def taskMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[Task[T]]] =
|
||||
Instance.contImpl[T](c, FullInstance, FullConvert, MixedBuilder)(Left(t))
|
||||
|
|
@ -87,6 +89,13 @@ object TaskMacro
|
|||
val assign = transformMacroImpl(c)( init.tree )( AssignInitName )
|
||||
c.Expr[Setting[Task[T]]]( assign )
|
||||
}
|
||||
/** Implementation of := macro for tasks. */
|
||||
def inputTaskAssignMacroImpl[T: c.WeakTypeTag](c: Context)(v: c.Expr[T]): c.Expr[Setting[InputTask[T]]] =
|
||||
{
|
||||
val init = inputTaskMacroImpl[T](c)(v)
|
||||
val assign = transformMacroImpl(c)( init.tree )( AssignInitName )
|
||||
c.Expr[Setting[InputTask[T]]]( assign )
|
||||
}
|
||||
/** Implementation of += macro for tasks. */
|
||||
def taskAppend1Impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context)(v: c.Expr[U])(a: c.Expr[Append.Value[T, U]]): c.Expr[Setting[Task[T]]] =
|
||||
{
|
||||
|
|
@ -122,7 +131,7 @@ object TaskMacro
|
|||
c.macroApplication match {
|
||||
case Apply(Apply(TypeApply(Select(preT, nmeT), targs), _), a) =>
|
||||
Apply(Apply(TypeApply(Select(preT, newTermName(newName).encodedName), targs), init :: Nil), a)
|
||||
case x => unexpectedTree(x)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
private[this] def transformMacroImpl(c: Context)(init: c.Tree)(newName: String): c.Tree =
|
||||
|
|
@ -131,31 +140,130 @@ object TaskMacro
|
|||
val target =
|
||||
c.macroApplication match {
|
||||
case Apply(Select(prefix, _), _) => prefix
|
||||
case x => unexpectedTree(x)
|
||||
case x => ContextUtil.unexpectedTree(x)
|
||||
}
|
||||
Apply.apply(Select(target, newTermName(newName).encodedName), init :: Nil)
|
||||
}
|
||||
private[this] def unexpectedTree[C <: Context](tree: C#Tree): Nothing = error("Unexpected macro application tree (" + tree.getClass + "): " + tree)
|
||||
|
||||
sealed abstract class MacroValue[T] {
|
||||
def value: T = macro std.TaskMacro.valueMacroImpl[T]
|
||||
}
|
||||
|
||||
def valueMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[T] =
|
||||
ContextUtil.selectMacroImpl[T,Any](c)( ts => InputWrapper.wrapKey[T](c)(ts) )
|
||||
|
||||
sealed abstract class RawParserInput[T] {
|
||||
def parsed: T = macro std.TaskMacro.rawParserMacro[T]
|
||||
}
|
||||
sealed abstract class InitParserInput[T] {
|
||||
def parsed: T = macro std.TaskMacro.initParserMacro[T]
|
||||
}
|
||||
sealed abstract class StateParserInput[T] {
|
||||
def parsed: T = macro std.TaskMacro.stateParserMacro[T]
|
||||
}
|
||||
|
||||
/** Implements `Parser[T].parsed` by wrapping the Parser with the ParserInput wrapper.*/
|
||||
def rawParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] =
|
||||
ContextUtil.selectMacroImpl[T, Parser[T]](c) { p => c.universe.reify {
|
||||
ParserInput.parser_\u2603\u2603[T](InputTask.parserAsInput(p.splice))
|
||||
}}
|
||||
|
||||
/** Implements the `Initialize[Parser[T]].parsed` macro by wrapping the input with the ParserInput wrapper.*/
|
||||
def initParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] =
|
||||
ContextUtil.selectMacroImpl[T, Initialize[Parser[T]]](c) { t => c.universe.reify {
|
||||
ParserInput.parser_\u2603\u2603[T](InputTask.initParserAsInput(t.splice))
|
||||
}}
|
||||
|
||||
/** Implements the `Initialize[State => Parser[T]].parsed` macro by wrapping the input with the ParserInput wrapper.*/
|
||||
def stateParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] =
|
||||
ContextUtil.selectMacroImpl[T, Initialize[State => Parser[T]]](c) { t => c.universe.reify {
|
||||
ParserInput.parser_\u2603\u2603[T](t.splice)
|
||||
}}
|
||||
|
||||
/** Implementation detail. The method temporarily holds the input parser (as a Tree, at compile time) until the input task macro processes it. */
|
||||
object ParserInput {
|
||||
/** The name of the wrapper method should be obscure.
|
||||
* Wrapper checking is based solely on this name, so it must not conflict with a user method name.
|
||||
* The user should never see this method because it is compile-time only and only used internally by the task macros.*/
|
||||
val WrapName = "parser_\u2603\u2603"
|
||||
|
||||
// This method should be annotated as compile-time only when that feature is implemented
|
||||
def parser_\u2603\u2603[T](i: Initialize[State => Parser[T]]): T = error("This method is an implementation detail and should not be referenced.")
|
||||
}
|
||||
|
||||
def inputTaskMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] =
|
||||
inputTaskMacro0[T](c)(Left(t))
|
||||
def inputTaskDynMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Initialize[Task[T]]]): c.Expr[Initialize[InputTask[T]]] =
|
||||
inputTaskMacro0[T](c)(Right(t))
|
||||
|
||||
private[this] def inputTaskMacro0[T: c.WeakTypeTag](c: Context)(t: Either[c.Expr[T], c.Expr[Initialize[Task[T]]]]): c.Expr[Initialize[InputTask[T]]] =
|
||||
{
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case Select(Apply(_, t :: Nil), _) => wrap[T](c)(t)
|
||||
case x => unexpectedTree(x)
|
||||
import c.universe.{Apply=>ApplyTree,_}
|
||||
|
||||
val tag = implicitly[c.WeakTypeTag[T]]
|
||||
val util = ContextUtil[c.type](c)
|
||||
val it = Ident(util.singleton(InputTask))
|
||||
val isParserWrapper = util.isWrapper(ParserInput.WrapName)
|
||||
val isTaskWrapper = util.isWrapper(InputWrapper.WrapName)
|
||||
val isAnyWrapper = (tr: Tree) => isParserWrapper(tr) || isTaskWrapper(tr)
|
||||
val ttree = t match { case Left(l) => l.tree; case Right(r) => r.tree }
|
||||
val defs = util.collectDefs(ttree, isAnyWrapper)
|
||||
val checkQual = util.checkReferences(defs, isAnyWrapper)
|
||||
val unitTask = c.typeOf[TaskKey[Unit]]
|
||||
val taskKeyC = unitTask.typeConstructor
|
||||
|
||||
var result: Option[(Tree, Type, ValDef)] = None
|
||||
|
||||
def subWrapper(tpe: Type, qual: Tree): Tree =
|
||||
if(result.isDefined)
|
||||
{
|
||||
c.error(qual.pos, "An InputTask can only have a single input parser.")
|
||||
EmptyTree
|
||||
}
|
||||
else
|
||||
{
|
||||
qual.foreach(checkQual)
|
||||
val keyType = appliedType(taskKeyC, tpe :: Nil) // TaskKey[<tpe>]
|
||||
val vd = util.freshValDef(keyType, qual.symbol) // val $x: TaskKey[<tpe>]
|
||||
result = Some( (qual, tpe, vd) )
|
||||
val tree = util.refVal(vd) // $x
|
||||
tree.setPos(qual.pos) // position needs to be set so that wrapKey passes the position onto the wrapper
|
||||
assert(tree.tpe != null, "Null type: " + tree)
|
||||
val wrapped = InputWrapper.wrapKey(c)( c.Expr[Any](tree) )( c.WeakTypeTag(tpe) )
|
||||
wrapped.tree.setType(tpe)
|
||||
}
|
||||
// Tree for InputTask.create[<tpeA>, <tpeB>](arg1)(arg2)
|
||||
def inputTaskApply(tpeA: Type, tpeB: Type, arg1: Tree, arg2: Tree) =
|
||||
{
|
||||
val typedApp = TypeApply(Select(it, InputTaskCreateName), TypeTree(tpeA) :: TypeTree(tpeB) :: Nil)
|
||||
val app = ApplyTree( ApplyTree(typedApp, arg1 :: Nil), arg2 :: Nil)
|
||||
c.Expr[Initialize[InputTask[T]]](app)
|
||||
}
|
||||
def expandTask(dyn: Boolean, tx: Tree): c.Expr[Initialize[Task[T]]] =
|
||||
if(dyn)
|
||||
taskDynMacroImpl[T](c)( c.Expr[Initialize[Task[T]]](tx) )
|
||||
else
|
||||
taskMacroImpl[T](c)( c.Expr[T](tx) )
|
||||
|
||||
val tx = util.transformWrappers(ttree, isParserWrapper, (tpe,tree) => subWrapper(tpe,tree))
|
||||
val body = c.resetLocalAttrs( expandTask(t.isRight, tx).tree )
|
||||
result match {
|
||||
case Some((p, tpe, param)) =>
|
||||
val f = Function(param :: Nil, body)
|
||||
inputTaskApply(tpe, tag.tpe, p, f)
|
||||
case None =>
|
||||
// SI-6591 prevents the more direct version using reify:
|
||||
// reify { InputTask[Unit,T].create(TaskMacro.emptyParser)(Types.const(body.splice)) }
|
||||
val initType = c.weakTypeOf[Initialize[Task[T]]]
|
||||
val tt = Ident(util.singleton(Types))
|
||||
val f = ApplyTree(TypeApply(Select(tt, "const"), TypeTree(unitTask) :: TypeTree(initType) :: Nil), body :: Nil)
|
||||
val p = reify { InputTask.emptyParser }
|
||||
inputTaskApply(c.typeOf[Unit], tag.tpe, p.tree, f)
|
||||
}
|
||||
}
|
||||
private[this] def wrap[T: c.WeakTypeTag](c: Context)(t: c.Tree): c.Expr[T] =
|
||||
{
|
||||
val ts = c.Expr[Any](t)
|
||||
c.universe.reify { InputWrapper.wrap[T](ts.splice) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package sbt
|
||||
package std
|
||||
|
||||
object UseTask
|
||||
import complete.{DefaultParsers, Parsers}
|
||||
|
||||
|
||||
/*object UseTask
|
||||
{
|
||||
import Def._
|
||||
|
||||
|
|
@ -14,18 +17,28 @@ object UseTask
|
|||
val a = taskDyn {
|
||||
if(y.value) z else x
|
||||
}
|
||||
}
|
||||
}*/
|
||||
object Assign
|
||||
{
|
||||
import java.io.File
|
||||
import Def.{Initialize,macroValueT}
|
||||
import UseTask.{x,y,z,a,set,plain}
|
||||
import Def.{Initialize,macroValueT,parserToInput}
|
||||
// import UseTask.{x,y,z,a,set,plain}
|
||||
|
||||
val ak = TaskKey[Int]("a")
|
||||
val bk = TaskKey[Seq[Int]]("b")
|
||||
val ck = SettingKey[File]("c")
|
||||
val sk = TaskKey[Set[_]]("s")
|
||||
|
||||
val ik = InputKey[Int]("i")
|
||||
val isk = InputKey[String]("is")
|
||||
val mk = SettingKey[Int]("m")
|
||||
val tk = TaskKey[Int]("t")
|
||||
val name = SettingKey[String]("name")
|
||||
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]] = ???
|
||||
|
||||
/* def azy = sk.value
|
||||
|
||||
def azy2 = appmacro.Debug.checkWild(Def.task{ sk.value.size })
|
||||
|
|
@ -37,7 +50,42 @@ object Assign
|
|||
bk ++= Seq(z.value)
|
||||
)*/
|
||||
|
||||
def bool: Initialize[Boolean] = Def.setting { true }
|
||||
import DefaultParsers._
|
||||
val p = Def.setting { name.value ~> Space ~> ID }
|
||||
val is = Seq(
|
||||
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 }
|
||||
)
|
||||
|
||||
val it1 = Def.inputTask {
|
||||
tsk.parsed //"as" //dummy.value.parsed
|
||||
}
|
||||
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 {
|
||||
dummyt.value.parsed
|
||||
}*/
|
||||
// should compile: can use a setting to define the parser
|
||||
val it5 = Def.inputTask {
|
||||
dummys.parsed
|
||||
}
|
||||
val it6 = Def.inputTaskDyn {
|
||||
val (x,i) = dummy3.parsed
|
||||
Def.task { tk.value + i}
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
|
|
@ -45,5 +93,5 @@ object Assign
|
|||
}
|
||||
def forallIn[T](key: Initialize[T]): Initialize[Seq[T]] = Def.setting {
|
||||
key.value :: Nil
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright 2010, 2011 Mark Harrah
|
||||
*/
|
||||
package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders
|
||||
with sbt.PathExtra with sbt.ProjectExtra with sbt.DependencyFilterExtra with sbt.BuildExtra
|
||||
with sbt.PathExtra with sbt.ProjectExtra with sbt.DependencyFilterExtra with sbt.BuildExtra with sbt.TaskMacroExtra
|
||||
{
|
||||
@deprecated("Renamed to CommandStrings.", "0.12.0")
|
||||
val CommandSupport = CommandStrings
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import sbt.complete.DefaultParsers._
|
||||
|
||||
InputKey[Unit]("check-output") <<= {
|
||||
{
|
||||
import sbt.complete.DefaultParsers._
|
||||
val parser = token(Space ~> ( ("exists" ^^^ true) | ("absent" ^^^ false) ) )
|
||||
def action(result: TaskKey[Boolean]) =
|
||||
(classDirectory in Configurations.Compile, result) map { (dir, shouldExist) =>
|
||||
if((dir / "Anon.class").exists != shouldExist)
|
||||
error("Top level class incorrect" )
|
||||
else if( (dir / "Anon$1.class").exists != shouldExist)
|
||||
error("Inner class incorrect" )
|
||||
else
|
||||
()
|
||||
}
|
||||
InputTask(s => parser)(action)
|
||||
InputKey[Unit]("check-output") := {
|
||||
val shouldExist = parser.parsed
|
||||
val dir = (classDirectory in Compile).value
|
||||
if((dir / "Anon.class").exists != shouldExist)
|
||||
error("Top level class incorrect" )
|
||||
else if( (dir / "Anon$1.class").exists != shouldExist)
|
||||
error("Inner class incorrect" )
|
||||
else
|
||||
()
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import Defaults._
|
|||
|
||||
object B extends Build {
|
||||
lazy val root = Project("root", file("."), settings = defaultSettings ++ Seq(
|
||||
libraryDependencies += "org.scalatest" % "scalatest_2.9.0" % "1.6.1" % "test",
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "1.8" % "test" cross(CrossVersion.full),
|
||||
parallelExecution in test := false
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,16 @@ object ScriptedPlugin extends Plugin {
|
|||
m.getClass.getMethod("run", classOf[File], classOf[Boolean], classOf[String], classOf[String], classOf[String], classOf[Array[String]], classOf[File], classOf[Array[String]])
|
||||
}
|
||||
|
||||
def scriptedTask: Initialize[InputTask[Unit]] = InputTask(_ => complete.Parsers.spaceDelimited("<arg>")) { result =>
|
||||
(scriptedDependencies, scriptedTests, scriptedRun, sbtTestDirectory, scriptedBufferLog, scriptedSbt, scriptedScalas, sbtLauncher, scriptedLaunchOpts, result) map {
|
||||
(deps, m, r, testdir, bufferlog, version, scriptedScalas, launcher, launchOpts, args) =>
|
||||
try { r.invoke(m, testdir, bufferlog: java.lang.Boolean, version.toString, scriptedScalas.build, scriptedScalas.versions, args.toArray, launcher, launchOpts.toArray) }
|
||||
catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
|
||||
def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask {
|
||||
val args = Def.spaceDelimited().parsed
|
||||
val prereq: Unit = scriptedDependencies.value
|
||||
try {
|
||||
scriptedRun.value.invoke(
|
||||
scriptedTests.value, sbtTestDirectory.value, scriptedBufferLog.value: java.lang.Boolean,
|
||||
scriptedSbt.value.toString, scriptedScalas.value.build, scriptedScalas.value.versions,
|
||||
args.toArray, sbtLauncher.value, scriptedLaunchOpts.value.toArray)
|
||||
}
|
||||
catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
|
||||
}
|
||||
|
||||
val scriptedSettings = Seq(
|
||||
|
|
|
|||
|
|
@ -4,11 +4,32 @@ package appmacro
|
|||
import scala.reflect._
|
||||
import macros._
|
||||
import scala.tools.nsc.Global
|
||||
import ContextUtil.{DynamicDependencyError, DynamicReferenceError}
|
||||
|
||||
object ContextUtil {
|
||||
final val DynamicDependencyError = "Illegal dynamic dependency"
|
||||
final val DynamicReferenceError = "Illegal dynamic reference"
|
||||
|
||||
/** Constructs an object with utility methods for operating in the provided macro context `c`.
|
||||
* Callers should explicitly specify the type parameter as `c.type` in order to preserve the path dependent types. */
|
||||
def apply[C <: Context with Singleton](c: C): ContextUtil[C] = new ContextUtil(c)
|
||||
|
||||
|
||||
/** Helper for implementing a no-argument macro that is introduced via an implicit.
|
||||
* This method removes the implicit conversion and evaluates the function `f` on the target of the conversion.
|
||||
*
|
||||
* 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.Expr[T]): c.Expr[T] =
|
||||
{
|
||||
import c.universe._
|
||||
c.macroApplication match {
|
||||
case Select(Apply(_, t :: Nil), _) => f( c.Expr[S](t) )
|
||||
case x => unexpectedTree(x)
|
||||
}
|
||||
}
|
||||
|
||||
def unexpectedTree[C <: Context](tree: C#Tree): Nothing = error("Unexpected macro application tree (" + tree.getClass + "): " + tree)
|
||||
}
|
||||
|
||||
/** Utility methods for macros. Several methods assume that the context's universe is a full compiler (`scala.tools.nsc.Global`).
|
||||
|
|
@ -42,6 +63,50 @@ 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] =
|
||||
{
|
||||
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 tree =>
|
||||
if(tree.symbol ne null) defs += tree.symbol;
|
||||
super.traverse(tree)
|
||||
}
|
||||
}
|
||||
process.traverse(tree)
|
||||
defs
|
||||
}
|
||||
|
||||
/** A reference is illegal if it is to an M instance defined within the scope of the macro call.
|
||||
* As an approximation, disallow referenced to any local definitions `defs`. */
|
||||
def illegalReference(defs: collection.Set[Symbol], sym: Symbol): Boolean =
|
||||
sym != null && sym != NoSymbol && defs.contains(sym)
|
||||
|
||||
/** 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)
|
||||
case id @ Ident(name) if illegalReference(defs, id.symbol) => ctx.error(id.pos, DynamicReferenceError + ": " + name)
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
/** Constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs. */
|
||||
def freshMethodParameter(tpe: Type): ValDef =
|
||||
ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree)
|
||||
|
||||
/** Constructs a ValDef with local modifiers and a unique name. */
|
||||
def localValDef(tpt: Tree, rhs: Tree): ValDef =
|
||||
ValDef(localModifiers, freshTermName("q"), tpt, rhs)
|
||||
|
|
@ -75,8 +140,18 @@ final class ContextUtil[C <: Context](val ctx: C)
|
|||
tc.setTypeSignature(PolyType(arg :: Nil, emptyTypeBounds))
|
||||
tc
|
||||
}
|
||||
/** >: Nothing <: Any */
|
||||
def emptyTypeBounds: TypeBounds = TypeBounds(definitions.NothingClass.toType, definitions.AnyClass.toType)
|
||||
|
||||
/** Create a Tree that references the `val` represented by `vd`. */
|
||||
def refVal(vd: ValDef): Tree =
|
||||
{
|
||||
val t = Ident(vd.name)
|
||||
assert(vd.tpt.tpe != null, "val type is null: " + vd + ", tpt: " + vd.tpt.tpe)
|
||||
t.setType(vd.tpt.tpe)
|
||||
t
|
||||
}
|
||||
|
||||
/** Returns the Symbol that references the statically accessible singleton `i`. */
|
||||
def singleton[T <: AnyRef with Singleton](i: T)(implicit it: ctx.TypeTag[i.type]): Symbol =
|
||||
it.tpe match {
|
||||
|
|
@ -105,4 +180,27 @@ final class ContextUtil[C <: Context](val ctx: C)
|
|||
assert(tc != NoType && tc.takesTypeArgs, "Invalid type constructor: " + tc)
|
||||
tc
|
||||
}
|
||||
|
||||
/** 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.
|
||||
* 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 =
|
||||
{
|
||||
// the main tree transformer that replaces calls to InputWrapper.wrap(x) with
|
||||
// plain Idents that reference the actual input value
|
||||
object appTransformer extends Transformer
|
||||
{
|
||||
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 _ => super.transform(tree)
|
||||
}
|
||||
}
|
||||
|
||||
appTransformer.transform(t)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,24 +24,45 @@ trait MonadInstance extends Instance
|
|||
{
|
||||
def flatten[T](in: M[M[T]]): M[T]
|
||||
}
|
||||
object InputWrapper
|
||||
{
|
||||
def wrap[T](in: Any): T = error("This method is an implementation detail and should not be referenced.")
|
||||
}
|
||||
|
||||
import scala.reflect._
|
||||
import macros._
|
||||
|
||||
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"
|
||||
|
||||
// This method should be annotated as compile-time only when that feature is implemented
|
||||
def wrap_\u2603\u2603[T](in: Any): T = error("This method is an implementation detail and should not be referenced.")
|
||||
|
||||
/** Wraps an arbitrary Tree in a call to the `wrap` method of this module for later processing by an enclosing macro.
|
||||
* The resulting Tree is the manually constructed version of:
|
||||
*
|
||||
* `c.universe.reify { InputWrapper.<WrapName>[T](ts.splice) }`
|
||||
*/
|
||||
def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any]): c.Expr[T] =
|
||||
{
|
||||
import c.universe.{Apply=>ApplyTree,_}
|
||||
val util = new ContextUtil[c.type](c)
|
||||
val iw = util.singleton(InputWrapper)
|
||||
val tpe = c.weakTypeOf[T]
|
||||
val nme = newTermName(WrapName).encoded
|
||||
val tree = ApplyTree(TypeApply(Select(Ident(iw), nme), TypeTree(tpe) :: Nil), ts.tree :: Nil)
|
||||
tree.setPos(ts.tree.pos)
|
||||
c.Expr[T](tree)
|
||||
}
|
||||
}
|
||||
|
||||
object Instance
|
||||
{
|
||||
final val DynamicDependencyError = "Illegal dynamic dependency."
|
||||
final val DynamicReferenceError = "Illegal dynamic reference."
|
||||
final val ApplyName = "app"
|
||||
final val FlattenName = "flatten"
|
||||
final val PureName = "pure"
|
||||
final val MapName = "map"
|
||||
final val InstanceTCName = "M"
|
||||
final val WrapName = "wrap"
|
||||
|
||||
final class Input[U <: Universe with Singleton](val tpe: U#Type, val expr: U#Tree, val local: U#ValDef)
|
||||
|
||||
|
|
@ -99,41 +120,14 @@ object Instance
|
|||
// A Tree that references the statically accessible Instance that provides the actual implementations of map, flatMap, ...
|
||||
val instance = Ident(instanceSym)
|
||||
|
||||
val parameterModifiers = Modifiers(Flag.PARAM)
|
||||
|
||||
val wrapperSym = util.singleton(InputWrapper)
|
||||
val wrapMethodSymbol = util.method(wrapperSym, WrapName)
|
||||
def isWrapper(fun: Tree) = fun.symbol == wrapMethodSymbol
|
||||
val isWrapper: Tree => Boolean = util.isWrapper(InputWrapper.WrapName)
|
||||
|
||||
type In = Input[c.universe.type]
|
||||
var inputs = List[In]()
|
||||
|
||||
// constructs a ValDef with a parameter modifier, a unique name, with the provided Type and with an empty rhs
|
||||
def freshMethodParameter(tpe: Type): ValDef =
|
||||
ValDef(parameterModifiers, freshTermName("p"), TypeTree(tpe), EmptyTree)
|
||||
|
||||
def freshTermName(prefix: String) = newTermName(c.fresh("$" + prefix))
|
||||
|
||||
/* Local definitions in the macro. This is used to ensure
|
||||
* references are to M instances defined outside of the macro call.*/
|
||||
val defs = new collection.mutable.HashSet[Symbol]
|
||||
|
||||
// a reference is illegal if it is to an M instance defined within the scope of the macro call
|
||||
def illegalReference(sym: Symbol): Boolean =
|
||||
sym != null && sym != NoSymbol && defs.contains(sym)
|
||||
|
||||
// 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.
|
||||
val checkQual: Tree => Unit = {
|
||||
case s @ ApplyTree(fun, qual :: Nil) => if(isWrapper(fun)) c.error(s.pos, DynamicDependencyError)
|
||||
case id @ Ident(name) if illegalReference(id.symbol) => c.error(id.pos, DynamicReferenceError)
|
||||
case _ => ()
|
||||
}
|
||||
// adds the symbols for all non-Ident subtrees to `defs`.
|
||||
val defSearch: Tree => Unit = {
|
||||
case _: Ident => ()
|
||||
case tree => if(tree.symbol ne null) defs += tree.symbol;
|
||||
}
|
||||
// Local definitions in the macro. This is used to ensure references are to M instances defined outside of the macro call.
|
||||
val defs = util.collectDefs(tree, isWrapper)
|
||||
val checkQual: Tree => Unit = util.checkReferences(defs, isWrapper)
|
||||
|
||||
// transforms the original tree into calls to the Instance functions pure, map, ...,
|
||||
// resulting in a value of type M[T]
|
||||
|
|
@ -163,7 +157,7 @@ object Instance
|
|||
def single(body: Tree, input: In): Tree =
|
||||
{
|
||||
val variable = input.local
|
||||
val param = ValDef(parameterModifiers, variable.name, variable.tpt, EmptyTree)
|
||||
val param = ValDef(util.parameterModifiers, variable.name, variable.tpt, EmptyTree)
|
||||
val typeApplied = TypeApply(Select(instance, MapName), variable.tpt :: TypeTree(treeType) :: Nil)
|
||||
val mapped = ApplyTree(typeApplied, input.expr :: Function(param :: Nil, body) :: Nil)
|
||||
if(t.isLeft) mapped else flatten(mapped)
|
||||
|
|
@ -173,7 +167,7 @@ object Instance
|
|||
def arbArity(body: Tree, inputs: List[In]): Tree =
|
||||
{
|
||||
val result = builder.make(c)(mTC, inputs)
|
||||
val param = freshMethodParameter( appliedType(result.representationC, util.idTC :: Nil) )
|
||||
val param = util.freshMethodParameter( appliedType(result.representationC, util.idTC :: Nil) )
|
||||
val bindings = result.extract(param)
|
||||
val f = Function(param :: Nil, Block(bindings, body))
|
||||
val ttt = TypeTree(treeType)
|
||||
|
|
@ -192,30 +186,17 @@ object Instance
|
|||
qual.foreach(checkQual)
|
||||
val vd = util.freshValDef(tpe, qual.symbol)
|
||||
inputs ::= new Input(tpe, qual, vd)
|
||||
Ident(vd.name)
|
||||
util.refVal(vd)
|
||||
}
|
||||
|
||||
// the main tree transformer that replaces calls to InputWrapper.wrap(x) with
|
||||
// plain Idents that reference the actual input value
|
||||
object appTransformer extends Transformer
|
||||
def sub(tpe: Type, qual: Tree): Tree =
|
||||
{
|
||||
override def transform(tree: Tree): Tree =
|
||||
tree match
|
||||
{
|
||||
case ApplyTree(TypeApply(fun, t :: Nil), qual :: Nil) if isWrapper(fun) =>
|
||||
val tag = c.WeakTypeTag(t.tpe)
|
||||
addType(t.tpe, convert(c)(qual)(tag) )
|
||||
case _ => super.transform(tree)
|
||||
}
|
||||
val tag = c.WeakTypeTag(tpe)
|
||||
addType(tpe, convert(c)(qual)(tag) )
|
||||
}
|
||||
|
||||
// collects all definitions in the tree. used for finding illegal references
|
||||
tree.foreach(defSearch)
|
||||
|
||||
// applies the transformation
|
||||
// resetting attributes: a) must be local b) must be done
|
||||
// on the transformed tree and not the wrapped tree or else there are obscure errors
|
||||
val tr = makeApp( c.resetLocalAttrs(appTransformer.transform(tree)) )
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ trait Init[Scope]
|
|||
}
|
||||
def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key) getOrElse error("Internal settings error: invalid reference to " + showFullKey(k))
|
||||
def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k)
|
||||
def mapScope(f: Scope => Scope): MapScoped = new MapScoped {
|
||||
def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope))
|
||||
}
|
||||
|
||||
def compiled(init: Seq[Setting[_]], actual: Boolean = true)(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): CompiledMap =
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue