The temporary and the reference to it come back together, so they cannot drift

This commit is contained in:
Joseph Ferano 2026-09-12 03:44:16 +07:00
parent f7009fcd34
commit 0b8564828b

View File

@ -375,6 +375,15 @@ and bindings f (items : Form.t list) : Ast.binding list =
(* ── Destructuring ─────────────────────────────────────────────────── *)
(* The temporary every pattern binds its value to before anything reads it, so
that the value is evaluated once however many names come out of it. Returning
the reference as well as the binding is what makes the two impossible to
separate by accident. *)
and temp (p : Form.t) (v : Ast.expr) : Ast.expr * Ast.binding =
let t = fresh_temp () in
({ Ast.e = Ast.Var t; loc = p.loc },
{ Ast.bname = t; bty = None; bval = v; bloc = p.loc })
(* Clojure's destructuring, desugared here into the bindings and field accesses
the language already has. [Ast.binding] carries a name and nothing else, and
deliberately so: nothing downstream not [Load]'s renaming, not [Check], not
@ -393,12 +402,8 @@ and destructure (p : Form.t) (v : Ast.expr) : Ast.binding list =
(* The value goes into a temporary first, so it is evaluated once however
many names the pattern binds, and so that [(let [{:keys [p]} p] ...)]
reads the old [p] rather than the one it is in the middle of rebinding. *)
| Map items ->
let t = fresh_temp () in
{ Ast.bname = t; bty = None; bval = v; bloc = p.loc } :: dmap p t items
| Vec items ->
let t = fresh_temp () in
{ Ast.bname = t; bty = None; bval = v; bloc = p.loc } :: dvec p t items
| Map items -> let t, bind = temp p v in bind :: dmap p t items
| Vec items -> let t, bind = temp p v in bind :: dvec p t items
| _ ->
fail p
"expected a name or a destructuring pattern, found %s — a pattern is \
@ -410,9 +415,9 @@ and destructure (p : Form.t) (v : Ast.expr) : Ast.binding list =
the pair form is what nests, since a [:keys] entry is a name and never a
pattern. Everything else Clojure puts in this position [:as], [:or],
[:strs], [:syms] is refused by name where it is written. *)
and dmap (p : Form.t) (t : string) (items : Form.t list) : Ast.binding list =
and dmap (p : Form.t) (t : Ast.expr) (items : Form.t list) : Ast.binding list =
let ex loc e : Ast.expr = { Ast.e; loc } in
let field loc name = ex loc (Ast.Field (ex loc (Ast.Var t), name)) in
let field loc name = ex loc (Ast.Field (t, name)) in
let rec go = function
| [] -> []
| { v = Kw "keys"; _ } :: names :: rest ->
@ -460,7 +465,7 @@ and dmap (p : Form.t) (t : string) (items : Form.t list) : Ast.binding list =
else go items
(* [a b] and [a b & rest], over a fixed array. Not over a slice: see [Check]. *)
and dvec (p : Form.t) (t : string) (items : Form.t list) : Ast.binding list =
and dvec (p : Form.t) (t : Ast.expr) (items : Form.t list) : Ast.binding list =
let ex loc e : Ast.expr = { Ast.e; loc } in
let var loc n = ex loc (Ast.Var n) in
let rec split acc = function
@ -491,7 +496,7 @@ and dvec (p : Form.t) (t : string) (items : Form.t list) : Ast.binding list =
let nth i =
ex p.loc
(Ast.Call (var p.loc "destructure~nth",
[ var p.loc t;
[ t;
ex p.loc (Ast.Int (Int64.of_int i));
ex p.loc (Ast.Int (Int64.of_int n));
ex p.loc (Ast.Int exact) ]))
@ -518,10 +523,9 @@ and dvec (p : Form.t) (t : string) (items : Form.t list) : Ast.binding list =
bval =
ex r.loc
(Ast.Call (var r.loc "slice",
[ var r.loc t;
[ t;
ex r.loc (Ast.Int (Int64.of_int n));
ex r.loc (Ast.Call (var r.loc "len",
[ var r.loc t ])) ])) } ]
ex r.loc (Ast.Call (var r.loc "len", [ t ])) ])) } ]
in
each 0 elems @ rest_binding