@@ -24,5 +24,52 @@ object UnsafeExpr {
2424 expr.unseal.underlyingArgument.seal.asInstanceOf [Expr [T ]]
2525 }
2626
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+ }
2746
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+ }
2875}
0 commit comments