diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2014-02-01 00:13:53 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2014-02-14 23:51:24 +0100 |
commit | 8c29132055845181a34ed9077d30fac87c284574 (patch) | |
tree | 29c5bb2ca8be67e324ee3e0f9d78bc62bf00d8b3 /src | |
parent | 73adf2d9de441e151a117a5b33ae707ad79a9f36 (diff) | |
download | scala-8c29132055845181a34ed9077d30fac87c284574.tar.gz scala-8c29132055845181a34ed9077d30fac87c284574.tar.bz2 scala-8c29132055845181a34ed9077d30fac87c284574.zip |
adds internal.typingTransform
As per Jason’s request, this commit introduces a facility to perform
tree transformations that know how to track current lexical context
and how to typecheck trees in that lexical context.
Interestingly enough, we can’t get away with the traditional “subclass the
Transformer” approach, because the required base transformer class is
declared in scala-compiler.jar, and our API is defined in scala-reflect.jar.
This forced me to play with an idea that we’ve discussed with Denys today
(actually, it’s already yesterday) - lightweight transformers that take
contextful HOFs. This commit is a sketch in that direction. Turning
`transform` and `typingTransform` into macros would make the API more
elegant (see the comments), but I didn’t have time to experiment.
I’m running short on time, so I haven’t had a chance to actually test this API,
but I’m still submitting the pull request to ignite a discussion.
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/reflect/macros/contexts/Internals.scala | 32 | ||||
-rw-r--r-- | src/reflect/scala/reflect/macros/Internals.scala | 47 |
2 files changed, 78 insertions, 1 deletions
diff --git a/src/compiler/scala/reflect/macros/contexts/Internals.scala b/src/compiler/scala/reflect/macros/contexts/Internals.scala index e0f7824cf3..e35a8ae034 100644 --- a/src/compiler/scala/reflect/macros/contexts/Internals.scala +++ b/src/compiler/scala/reflect/macros/contexts/Internals.scala @@ -1,9 +1,39 @@ package scala.reflect.macros package contexts -trait Internals { +trait Internals extends scala.tools.nsc.transform.TypingTransformers { self: Context => + import global._ + lazy val internal: ContextInternalApi = new global.SymbolTableInternal with ContextInternalApi { + class HofTransformer(hof: (Tree, TransformApi) => Tree) extends Transformer { + val api = new TransformApi { + def recur(tree: Tree): Tree = hof(tree, this) + def default(tree: Tree): Tree = superTransform(tree) + } + def superTransform(tree: Tree) = super.transform(tree) + override def transform(tree: Tree): Tree = hof(tree, api) + } + + def transform(tree: Tree)(transformer: (Tree, TransformApi) => Tree): Tree = new HofTransformer(transformer).transform(tree) + + class HofTypingTransformer(hof: (Tree, TypingTransformApi) => Tree) extends TypingTransformer(callsiteTyper.context.unit) { self => + currentOwner = callsiteTyper.context.owner + curTree = EmptyTree + localTyper = global.analyzer.newTyper(callsiteTyper.context.make(unit = callsiteTyper.context.unit)) + + val api = new TypingTransformApi { + def recur(tree: Tree): Tree = hof(tree, this) + def default(tree: Tree): Tree = superTransform(tree) + def atOwner[T](owner: Symbol)(op: => T): T = self.atOwner(owner)(op) + def currentOwner: Symbol = self.currentOwner + def typecheck(tree: Tree): Tree = localTyper.typed(tree) + } + def superTransform(tree: Tree) = super.transform(tree) + override def transform(tree: Tree): Tree = hof(tree, api) + } + + def typingTransform(tree: Tree)(transformer: (Tree, TypingTransformApi) => Tree): Tree = new HofTypingTransformer(transformer).transform(tree) } }
\ No newline at end of file diff --git a/src/reflect/scala/reflect/macros/Internals.scala b/src/reflect/scala/reflect/macros/Internals.scala index f74f120470..415e70c36e 100644 --- a/src/reflect/scala/reflect/macros/Internals.scala +++ b/src/reflect/scala/reflect/macros/Internals.scala @@ -14,5 +14,52 @@ trait Internals { /** @see [[scala.reflect.api.Internals]] */ trait ContextInternalApi extends universe.MacroInternalApi { + /** Functions that are available during [[transform]]. + * @see [[transform]] + */ + trait TransformApi { + /** Calls the current transformer on the given tree. + * Current transformer = argument to the `transform` call. + */ + def recur(tree: Tree): Tree + + /** Calls the default transformer on the given tree. + * Default transformer = recur into tree's children and assemble the results. + */ + def default(tree: Tree): Tree + } + + /** Transforms a given tree using the provided function. + * @see [[TransformApi]] + */ + // TODO: explore a more concise notation that Denys and I discussed today + // when transformer is PartialFunction[Tree, Tree]] and TransformApi is passed magically + // also cf. https://github.com/dsl-paradise/dsl-paradise + def transform(tree: Tree)(transformer: (Tree, TransformApi) => Tree): Tree + + /** Functions that are available during [[typingTransform]]. + * @see [[typingTransform]] + */ + trait TypingTransformApi extends TransformApi { + /** Temporarily pushes the given symbol onto the owner stack, creating a new local typer, + * invoke the given operation and then rollback the changes to the owner stack. + */ + def atOwner[T](owner: Symbol)(op: => T): T + + /** Returns the symbol currently on the top of the owner stack. + * If we're not inside any `atOwner` call, then macro application's context owner will be used. + */ + def currentOwner: Symbol + + /** Typechecks the given tree using the local typer currently on the top of the owner stack. + * If we're not inside any `atOwner` call, then macro application's callsite typer will be used. + */ + def typecheck(tree: Tree): Tree + } + + /** Transforms a given tree using the provided function. + * @see [[TypingTransformApi]] + */ + def typingTransform(tree: Tree)(transformer: (Tree, TypingTransformApi) => Tree): Tree } } |