Skip to main content

cljrs_interp/
destructure.rs

1//! Sequential and associative destructuring for `let*`, `fn*`, and `loop*`.
2
3use cljrs_builtins::form::form_to_value;
4use cljrs_gc::GcPtr;
5use cljrs_reader::Form;
6use cljrs_reader::form::FormKind;
7use cljrs_value::{Keyword, PersistentList, PersistentVector, Symbol, Value};
8use std::sync::Arc;
9
10use cljrs_env::env::Env;
11use cljrs_env::error::{EvalError, EvalResult};
12
13/// Bind a destructuring pattern `pattern` against `val` in `env`.
14///
15/// Supports:
16/// - Plain symbol binding
17/// - `[a b]` sequential destructuring (recursive)
18pub fn bind_pattern(pattern: &Form, val: Value, env: &mut Env) -> EvalResult<()> {
19    match &pattern.kind {
20        FormKind::Symbol(s) => {
21            // Plain binding, including `_`.
22            env.bind(Arc::from(s.as_str()), val);
23            Ok(())
24        }
25        FormKind::Vector(forms) => bind_sequential(forms, &val, env),
26        FormKind::Map(forms) => bind_associative(forms, &val, env),
27        _ => Err(EvalError::Runtime(format!(
28            "unsupported binding pattern: {:?}",
29            pattern.kind
30        ))),
31    }
32}
33
34/// Bind a sequential destructuring pattern against `val`.
35///
36/// Grammar of `pattern`:
37/// ```text
38/// [sym* (& rest)? (:as alias)?]
39/// ```
40pub fn bind_sequential(pattern: &[Form], val: &Value, env: &mut Env) -> EvalResult<()> {
41    let items = value_to_seq_vec(val);
42    let mut idx = 0usize;
43    let mut i = 0usize;
44
45    while i < pattern.len() {
46        let p = &pattern[i];
47
48        // `&` introduces a rest binding.
49        if matches!(&p.kind, FormKind::Symbol(s) if s == "&") {
50            i += 1;
51            let rest_pat = pattern
52                .get(i)
53                .ok_or_else(|| EvalError::Runtime("& in destructuring requires a name".into()))?;
54            let rest_list = if idx < items.len() {
55                let rest_vals: Vec<Value> = items[idx..].to_vec();
56                Value::List(GcPtr::new(PersistentList::from_iter(rest_vals)))
57            } else {
58                Value::Nil
59            };
60            bind_pattern(rest_pat, rest_list, env)?;
61            i += 1;
62            // Skip optional `:as` after rest.
63            if i < pattern.len()
64                && let FormKind::Keyword(k) = &pattern[i].kind
65                && k == "as"
66            {
67                i += 1;
68                let alias = pattern
69                    .get(i)
70                    .ok_or_else(|| EvalError::Runtime(":as requires a name".into()))?;
71                bind_pattern(alias, val.clone(), env)?;
72            }
73            break;
74        }
75
76        // `:as` alias — must be last.
77        if let FormKind::Keyword(k) = &p.kind
78            && k == "as"
79        {
80            i += 1;
81            let alias = pattern
82                .get(i)
83                .ok_or_else(|| EvalError::Runtime(":as requires a name".into()))?;
84            bind_pattern(alias, val.clone(), env)?;
85            break;
86        }
87
88        // Normal positional binding.
89        let item = items.get(idx).cloned().unwrap_or(Value::Nil);
90        bind_pattern(p, item, env)?;
91        idx += 1;
92        i += 1;
93    }
94
95    Ok(())
96}
97
98/// Convert any sequential Value to a Vec of its elements.
99pub fn value_to_seq_vec(val: &Value) -> Vec<Value> {
100    match val {
101        Value::WithMeta(inner, _) => value_to_seq_vec(inner),
102        Value::Nil => vec![],
103        Value::LazySeq(ls) => value_to_seq_vec(&ls.get().realize()),
104        Value::Cons(c) => {
105            let mut result = vec![c.get().head.clone()];
106            let mut tail = c.get().tail.clone();
107            loop {
108                match tail {
109                    Value::Nil => break,
110                    Value::List(l) => {
111                        result.extend(l.get().iter().cloned());
112                        break;
113                    }
114                    Value::Cons(next_c) => {
115                        result.push(next_c.get().head.clone());
116                        tail = next_c.get().tail.clone();
117                    }
118                    Value::LazySeq(ls) => {
119                        tail = ls.get().realize();
120                    }
121                    _ => break,
122                }
123            }
124            result
125        }
126        Value::List(l) => l.get().iter().cloned().collect(),
127        Value::Vector(v) => v.get().iter().cloned().collect(),
128        Value::Set(s) => s.iter().cloned().collect(),
129        Value::Map(m) => {
130            let mut result = Vec::new();
131            m.for_each(|k, v| {
132                result.push(Value::Vector(GcPtr::new(PersistentVector::from_iter([
133                    k.clone(),
134                    v.clone(),
135                ]))));
136            });
137            result
138        }
139        _ => vec![],
140    }
141}
142
143// ── Associative destructuring ─────────────────────────────────────────────────
144
145/// Bind a map destructuring pattern against `val` in `env`.
146///
147/// `pattern` is a flat `[key val key val ...]` slice from `FormKind::Map`.
148///
149/// Supports:
150/// - `:keys [a b c]`   — bind symbols from keyword keys `:a`, `:b`, `:c`
151/// - `:strs [a b]`     — bind symbols from string keys `"a"`, `"b"`
152/// - `:syms [a b]`     — bind symbols from symbol keys `'a`, `'b`
153/// - `:as name`        — bind the whole value to `name`
154/// - `:or {a default}` — default value for missing keys
155/// - Regular `{sym :key}` direct bindings
156pub fn bind_associative(pattern: &[Form], val: &Value, env: &mut Env) -> EvalResult<()> {
157    // First pass: collect :or defaults.
158    let mut defaults: std::collections::HashMap<String, Value> = std::collections::HashMap::new();
159    let mut i = 0;
160    while i + 1 < pattern.len() {
161        let k = &pattern[i];
162        let v = &pattern[i + 1];
163        if let FormKind::Keyword(kw) = &k.kind
164            && kw == "or"
165        {
166            // v is a map literal {sym default ...}
167            if let FormKind::Map(or_forms) = &v.kind {
168                let mut j = 0;
169                while j + 1 < or_forms.len() {
170                    if let FormKind::Symbol(sym) = &or_forms[j].kind {
171                        defaults.insert(sym.clone(), form_to_value(&or_forms[j + 1]));
172                    }
173                    j += 2;
174                }
175            }
176        }
177        i += 2;
178    }
179
180    let get_val = |key: &Value| -> Value {
181        match val.unwrap_meta() {
182            Value::Map(m) => m.get(key).unwrap_or(Value::Nil),
183            _ => Value::Nil,
184        }
185    };
186
187    let mut i = 0;
188    while i + 1 < pattern.len() {
189        let k = &pattern[i];
190        let v = &pattern[i + 1];
191        i += 2;
192
193        match &k.kind {
194            FormKind::Keyword(kw) if kw == "keys" => {
195                if let FormKind::Vector(syms) = &v.kind {
196                    for sym_form in syms {
197                        if let FormKind::Symbol(sym) = &sym_form.kind {
198                            let key = Value::keyword(Keyword::simple(sym.as_str()));
199                            let mut bound_val = get_val(&key);
200                            if matches!(bound_val, Value::Nil)
201                                && let Some(d) = defaults.get(sym.as_str())
202                            {
203                                bound_val = d.clone();
204                            }
205                            env.bind(Arc::from(sym.as_str()), bound_val);
206                        }
207                    }
208                }
209            }
210            FormKind::Keyword(kw) if kw == "strs" => {
211                if let FormKind::Vector(syms) = &v.kind {
212                    for sym_form in syms {
213                        if let FormKind::Symbol(sym) = &sym_form.kind {
214                            let key = Value::string(sym.clone());
215                            let mut bound_val = get_val(&key);
216                            if matches!(bound_val, Value::Nil)
217                                && let Some(d) = defaults.get(sym.as_str())
218                            {
219                                bound_val = d.clone();
220                            }
221                            env.bind(Arc::from(sym.as_str()), bound_val);
222                        }
223                    }
224                }
225            }
226            FormKind::Keyword(kw) if kw == "syms" => {
227                if let FormKind::Vector(syms) = &v.kind {
228                    for sym_form in syms {
229                        if let FormKind::Symbol(sym) = &sym_form.kind {
230                            let key = Value::symbol(Symbol::simple(sym.as_str()));
231                            let mut bound_val = get_val(&key);
232                            if matches!(bound_val, Value::Nil)
233                                && let Some(d) = defaults.get(sym.as_str())
234                            {
235                                bound_val = d.clone();
236                            }
237                            env.bind(Arc::from(sym.as_str()), bound_val);
238                        }
239                    }
240                }
241            }
242            FormKind::Keyword(kw) if kw == "as" => {
243                if let FormKind::Symbol(sym) = &v.kind {
244                    env.bind(Arc::from(sym.as_str()), val.clone());
245                }
246            }
247            FormKind::Keyword(kw) if kw == "or" => {
248                // Already processed in the first pass.
249            }
250            _ => {
251                // Regular {binding-form lookup-key} pair.
252                // In Clojure map destructuring {a :x}, the key position is the
253                // binding target and the value position is the lookup key.
254                let lookup_key = form_to_value(v);
255                let mut bound_val = get_val(&lookup_key);
256                // Apply defaults for simple symbol bindings.
257                if matches!(bound_val, Value::Nil)
258                    && let FormKind::Symbol(sym) = &k.kind
259                    && let Some(d) = defaults.get(sym.as_str())
260                {
261                    bound_val = d.clone();
262                }
263                // Bind via pattern to support nested destructuring.
264                bind_pattern(k, bound_val, env)?;
265            }
266        }
267    }
268    Ok(())
269}