@@ -24,5 +24,52 @@ object UnsafeExpr {
24
24
expr.unseal.underlyingArgument.seal.asInstanceOf [Expr [T ]]
25
25
}
26
26
27
+ // TODO generalize for any function arity (see Expr.betaReduce)
28
+ /** Allows inspection or transformation of the body of the expression of function.
29
+ * This body may have references to the arguments of the function which should be closed
30
+ * over if the expression will be spliced.
31
+ *
32
+ * ```
33
+ * val f: Expr[T => R] = ...
34
+ * UnsafeExpr.open(f) { (body, close) =>
35
+ * val newParam: Expr[T] = ...
36
+ * ...
37
+ * close(body)(newParam) // body or part of the body
38
+ * }
39
+ * ```
40
+ */
41
+ def open [T1 , R , X ](f : Expr [T1 => R ])(content : (Expr [R ], [t] => Expr [t] => Expr [T1 ] => Expr [t]) => X )(given qctx : QuoteContext ): X = {
42
+ import qctx .tasty .{given , _ }
43
+ val (params, bodyExpr) = paramsAndBody(f)
44
+ content(bodyExpr, [t] => (e : Expr [t]) => (v : Expr [T1 ]) => bodyFn[t](e.unseal, params, List (v.unseal)).seal.asInstanceOf [Expr [t]])
45
+ }
27
46
47
+ def open [T1 , T2 , R , X ](f : Expr [(T1 , T2 ) => R ])(content : (Expr [R ], [t] => Expr [t] => (Expr [T1 ], Expr [T2 ]) => Expr [t]) => X )(given qctx : QuoteContext )(given DummyImplicit ): X = {
48
+ import qctx .tasty .{given , _ }
49
+ val (params, bodyExpr) = paramsAndBody(f)
50
+ content(bodyExpr, [t] => (e : Expr [t]) => (v1 : Expr [T1 ], v2 : Expr [T2 ]) => bodyFn[t](e.unseal, params, List (v1.unseal, v2.unseal)).seal.asInstanceOf [Expr [t]])
51
+ }
52
+
53
+ def open [T1 , T2 , T3 , R , X ](f : Expr [(T1 , T2 , T3 ) => R ])(content : (Expr [R ], [t] => Expr [t] => (Expr [T1 ], Expr [T2 ], Expr [T3 ]) => Expr [t]) => X )(given qctx : QuoteContext )(given DummyImplicit , DummyImplicit ): X = {
54
+ import qctx .tasty .{given , _ }
55
+ val (params, bodyExpr) = paramsAndBody(f)
56
+ content(bodyExpr, [t] => (e : Expr [t]) => (v1 : Expr [T1 ], v2 : Expr [T2 ], v3 : Expr [T3 ]) => bodyFn[t](e.unseal, params, List (v1.unseal, v2.unseal, v3.unseal)).seal.asInstanceOf [Expr [t]])
57
+ }
58
+
59
+ private def paramsAndBody [R ](given qctx : QuoteContext )(f : Expr [Any ]) = {
60
+ import qctx .tasty .{given , _ }
61
+ val Block (List (DefDef (" $anonfun" , Nil , List (params), _, Some (body))), Closure (Ident (" $anonfun" ), None )) = f.unseal.etaExpand
62
+ (params, body.seal.asInstanceOf [Expr [R ]])
63
+ }
64
+
65
+ private def bodyFn [t](given qctx : QuoteContext )(e : qctx.tasty.Term , params : List [qctx.tasty.ValDef ], args : List [qctx.tasty.Term ]): qctx.tasty.Term = {
66
+ import qctx .tasty .{given , _ }
67
+ val map = params.map(_.symbol).zip(args).toMap
68
+ new TreeMap {
69
+ override def transformTerm (tree : Term )(given ctx : Context ): Term =
70
+ super .transformTerm(tree) match
71
+ case tree : Ident => map.getOrElse(tree.symbol, tree)
72
+ case tree => tree
73
+ }.transformTerm(e)
74
+ }
28
75
}
0 commit comments