1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
package scala.reflect.makro
trait Reifiers {
self: Context =>
/** Reification prefix that refers to the standard reflexive mirror, ``scala.reflect.mirror''.
* Providing it for the ``prefix'' parameter of ``reifyTree'' or ``reifyType'' will create a tree that can be inspected at runtime.
*/
val reflectMirrorPrefix: Tree
/** Given a tree, generate a tree that when compiled and executed produces the original tree.
* The produced tree will be bound to the mirror specified by ``prefix'' (also see ``reflectMirrorPrefix'').
* For more information and examples see the documentation for ``Universe.reify''.
*
* This function is deeply connected to ``Universe.reify'', a macro that reifies arbitrary expressions into runtime trees.
* They do very similar things (``Universe.reify'' calls ``Context.reifyTree'' to implement itself), but they operate on different metalevels (see below).
*
* Let's study the differences between ``Context.reifyTree'' and ``Universe.reify'' on an example of using them inside a ``fooMacro'' macro:
*
* * Since reify itself is a macro, it will be executed when fooMacro is being compiled (metalevel -1)
* and will produce a tree that when evaluated during macro expansion of fooMacro (metalevel 0) will recreate the input tree.
*
* This provides a facility analogous to quasi-quoting. Writing "reify{ expr }" will generate an AST that represents expr.
* Afterwards this AST (or its parts) can be used to construct the return value of fooMacro.
*
* * reifyTree is evaluated during macro expansion (metalevel 0)
* and will produce a tree that when evaluated during the runtime of the program (metalevel 1) will recreate the input tree.
*
* This provides a way to retain certain trees from macro expansion time to be inspected later, in the runtime.
* For example, DSL authors may find it useful to capture DSL snippets into ASTs that are then processed at runtime in a domain-specific way.
*
* Also note the difference between universes of the runtime trees produced by two reifies:
*
* * The result of compiling and running the result of reify will be bound to the Universe that called reify.
* This is possible because it's a macro, so it can generate whatever code it wishes.
*
* * The result of compiling and running the result of reifyTree will be the ``prefix'' that needs to be passed explicitly.
* This happens because the Universe of the evaluated result is from a different metalevel than the Context the called reify.
*
* Typical usage of this function is to retain some of the trees received/created by a macro
* into the form that can be inspected (via pattern matching) or compiled/run (by a reflective ToolBox) during the runtime.
*/
def reifyTree(prefix: Tree, tree: Tree): Tree
/** Given a type, generate a tree that when compiled and executed produces the original type.
* The produced tree will be bound to the mirror specified by ``prefix'' (also see ``reflectMirrorPrefix'').
* For more information and examples see the documentation for ``Context.reifyTree'' and ``Universe.reify''.
*/
def reifyType(prefix: Tree, tpe: Type, dontSpliceAtTopLevel: Boolean = false, requireGroundTypeTag: Boolean = false): Tree
/** Undoes reification of a tree.
*
* This reversion doesn't simply restore the original tree (that would lose the context of reification),
* but does something more involved that conforms to the following laws:
*
* 1) unreifyTree(reifyTree(tree)) != tree // unreified tree is tree + saved context
* // in current implementation, the result of unreify is opaque
* // i.e. there's no possibility to inspect underlying tree/context
*
* 2) reifyTree(unreifyTree(reifyTree(tree))) == reifyTree(tree) // the result of reifying a tree in its original context equals to
* // the result of reifying a tree along with its saved context
*
* 3) compileAndEval(unreifyTree(reifyTree(tree))) ~ compileAndEval(tree) // at runtime original and unreified trees are behaviorally equivalent
*/
def unreifyTree(tree: Tree): Tree
/** Represents an error during reification
*/
type ReificationError <: Throwable
val ReificationError: ReificationErrorExtractor
abstract class ReificationErrorExtractor {
def unapply(error: ReificationError): Option[(Position, String)]
}
/** Wraps an unexpected error during reification
*/
type UnexpectedReificationError <: Throwable
val UnexpectedReificationError: UnexpectedReificationErrorExtractor
abstract class UnexpectedReificationErrorExtractor {
def unapply(error: UnexpectedReificationError): Option[(Position, String, Throwable)]
}
}
|