Skip to main content

hara_native/core/
form.rs

1fn metadata_value(form: &Form) -> Result<MetadataValue, String> {
2    match form {
3        Form::Nil => Ok(MetadataValue::Nil),
4        Form::Bool(value) => Ok(MetadataValue::Boolean(*value)),
5        Form::Number(value) => Ok(MetadataValue::Number(*value)),
6        Form::Float(value) => Ok(MetadataValue::Float(crate::numeric::finite_float(*value)?)),
7        Form::BigInteger(value) => Ok(MetadataValue::BigInteger(value.clone())),
8        Form::Character(value) => Ok(MetadataValue::Character(*value)),
9        Form::Regex(value) => Ok(MetadataValue::Regex(value.clone())),
10        Form::Tagged(tag, value) => Ok(MetadataValue::Tagged(
11            tag.clone(),
12            Box::new(metadata_value(value)?),
13        )),
14        Form::Metadata(_, value) => metadata_value(value),
15        Form::Symbol(value) => Ok(MetadataValue::Symbol(Symbol::from(value.clone()))),
16        Form::Keyword(value) => Ok(MetadataValue::Keyword(Keyword::from(value.clone()))),
17        Form::String(value) => Ok(MetadataValue::String(value.clone())),
18        Form::Vector(values) => Ok(MetadataValue::Vector(
19            values
20                .iter()
21                .map(metadata_value)
22                .collect::<Result<_, _>>()?,
23        )),
24        Form::List(values) => Ok(MetadataValue::List(
25            values
26                .iter()
27                .map(metadata_value)
28                .collect::<Result<_, _>>()?,
29        )),
30        Form::Set(values) => Ok(MetadataValue::Set(
31            values
32                .iter()
33                .map(metadata_value)
34                .collect::<Result<_, _>>()?,
35        )),
36        Form::Map(values) => Ok(MetadataValue::Map(
37            values
38                .iter()
39                .map(|(key, value)| Ok((metadata_value(key)?, metadata_value(value)?)))
40                .collect::<Result<_, String>>()?,
41        )),
42    }
43}
44
45pub(crate) fn metadata_from_form(form: &Form) -> Result<Rc<Metadata>, String> {
46    let MetadataValue::Map(entries) = metadata_value(form)? else {
47        return Err("reader metadata must be a map".into());
48    };
49    Ok(Metadata::new(entries))
50}
51
52fn merge_metadata(
53    existing: Option<Rc<Metadata>>,
54    overlay: Option<Rc<Metadata>>,
55) -> Option<Rc<Metadata>> {
56    match (existing, overlay) {
57        (None, None) => None,
58        (Some(metadata), None) | (None, Some(metadata)) => Some(metadata),
59        (Some(existing), Some(overlay)) => {
60            let mut entries = existing.entries().to_vec();
61            for (key, value) in overlay.entries() {
62                entries.retain(|(candidate, _)| candidate != key);
63                entries.push((key.clone(), value.clone()));
64            }
65            Some(Metadata::new(entries))
66        }
67    }
68}
69
70fn assoc_metadata(
71    metadata: Option<Rc<Metadata>>,
72    key: &str,
73    value: MetadataValue,
74) -> Option<Rc<Metadata>> {
75    merge_metadata(
76        metadata,
77        Some(Metadata::new(vec![(
78            MetadataValue::Keyword(Keyword::from(key)),
79            value,
80        )])),
81    )
82}
83
84pub(crate) fn definition_metadata(
85    mut metadata: Option<Rc<Metadata>>,
86    forms: &[Form],
87    private: bool,
88    macro_form: bool,
89) -> Result<(Option<Rc<Metadata>>, &[Form]), String> {
90    let mut rest = forms;
91    if let Some(Form::String(doc)) = rest.first().map(form_without_metadata) {
92        metadata = assoc_metadata(metadata, "doc", MetadataValue::String(doc.clone()));
93        rest = &rest[1..];
94    }
95    if let Some(Form::Map(_)) = rest.first().map(form_without_metadata) {
96        metadata = merge_metadata(
97            metadata,
98            Some(metadata_from_form(form_without_metadata(&rest[0]))?),
99        );
100        rest = &rest[1..];
101    }
102    if rest.is_empty() {
103        return Ok((metadata, rest));
104    }
105    let arglists = if matches!(
106        rest.first().map(form_without_metadata),
107        Some(Form::Vector(_))
108    ) {
109        vec![metadata_value(form_without_metadata(&rest[0]))?]
110    } else {
111        rest.iter()
112            .map(|clause| match form_without_metadata(clause) {
113                Form::List(parts) if !parts.is_empty() => {
114                    metadata_value(form_without_metadata(&parts[0]))
115                }
116                _ => Err(format!(
117                    "function arity must be a list beginning with parameters: {clause:?}"
118                )),
119            })
120            .collect::<Result<Vec<_>, String>>()?
121    };
122    metadata = assoc_metadata(metadata, "arglists", MetadataValue::Vector(arglists));
123    if metadata.as_ref().is_some_and(|value| value.flag("inline")) {
124        // Transparent wrappers receive an `inline-target` for direct call
125        // lowering. Composed source wrappers may still opt into inline
126        // metadata; they remain ordinary source functions when no single
127        // forwarding target can be derived.
128        if let Some(target) = inline_forward_target(rest) {
129            metadata = assoc_metadata(
130                metadata,
131                "inline-target",
132                MetadataValue::Symbol(Symbol::from(target)),
133            );
134        }
135    }
136    if private {
137        metadata = assoc_metadata(metadata, "private", MetadataValue::Boolean(true));
138    }
139    if macro_form {
140        metadata = assoc_metadata(metadata, "macro", MetadataValue::Boolean(true));
141    }
142    Ok((metadata, rest))
143}
144
145fn inline_forward_target(forms: &[Form]) -> Option<String> {
146    fn clause_target(params: &Form, body: &[Form]) -> Option<String> {
147        if body.len() != 1 {
148            return None;
149        }
150        let Form::Vector(params) = form_without_metadata(params) else {
151            return None;
152        };
153        let Form::List(call) = form_without_metadata(&body[0]) else {
154            return None;
155        };
156        let parameter_names = params
157            .iter()
158            .map(form_without_metadata)
159            .map(|form| match form {
160                Form::Symbol(name) => Some(name.as_str()),
161                _ => None,
162            })
163            .collect::<Option<Vec<_>>>()?;
164        if let [ampersand, rest] = parameter_names.as_slice() {
165            if *ampersand == "&" {
166                return match call.as_slice() {
167                    [Form::Symbol(apply), Form::Symbol(target), Form::Symbol(argument)]
168                        if apply == "apply" && argument == rest =>
169                    {
170                        Some(target.clone())
171                    }
172                    _ => None,
173                };
174            }
175        }
176        let Form::Symbol(target) = call.first()? else {
177            return None;
178        };
179        let arguments = call[1..]
180            .iter()
181            .map(form_without_metadata)
182            .map(|form| match form {
183                Form::Symbol(name) => Some(name.as_str()),
184                _ => None,
185            })
186            .collect::<Option<Vec<_>>>()?;
187        (arguments == parameter_names).then(|| target.clone())
188    }
189
190    if matches!(
191        forms.first().map(form_without_metadata),
192        Some(Form::Vector(_))
193    ) {
194        return clause_target(forms.first()?, &forms[1..]);
195    }
196    let mut target = None;
197    for clause in forms {
198        let Form::List(parts) = form_without_metadata(clause) else {
199            return None;
200        };
201        let candidate = clause_target(parts.first()?, &parts[1..])?;
202        if target.as_ref().is_some_and(|value| value != &candidate) {
203            return None;
204        }
205        target = Some(candidate);
206    }
207    target
208}
209
210pub(crate) fn schema_var_reference(metadata: Option<&Metadata>) -> Option<&Symbol> {
211    let MetadataValue::List(reference) = metadata?.get_keyword("schema")? else {
212        return None;
213    };
214    match reference.as_slice() {
215        [MetadataValue::Symbol(operator), MetadataValue::Symbol(target)]
216            if operator.get_namespace().is_none() && operator.get_name() == "var" =>
217        {
218            Some(target)
219        }
220        _ => None,
221    }
222}
223
224fn attach_optional_metadata(value: Value, metadata: Option<Rc<Metadata>>) -> Result<Value, String> {
225    Ok(match value {
226        Value::Symbol(value) => Value::Symbol(value.with_meta(metadata.clone())),
227        Value::Pointer(value) => Value::Pointer(value.with_meta(metadata.clone())),
228        Value::Tuple(value) => Value::Tuple(Box::new(value.with_meta(metadata.clone()))),
229        Value::Vector(value) => Value::Vector(value.with_meta(metadata.clone())),
230        Value::MapEntry(value) => Value::MapEntry(Box::new(value.with_meta(metadata.clone()))),
231        Value::List(value) => Value::List(value.with_meta(metadata.clone())),
232        Value::Cons(value) => Value::Cons(Box::new(value.with_meta(metadata.clone()))),
233        Value::Queue(value) => Value::Queue(Box::new(value.with_meta(metadata.clone()))),
234        Value::Deque(value) => Value::Deque(Box::new(value.with_meta(metadata.clone()))),
235        Value::Map(value) => Value::Map(value.with_meta(metadata.clone())),
236        Value::OrderedMap(value) => Value::OrderedMap(Box::new(value.with_meta(metadata.clone()))),
237        Value::SortedMap(value) => Value::SortedMap(Box::new(value.with_meta(metadata.clone()))),
238        Value::Trie(value) => Value::Trie(Box::new(value.with_meta(metadata.clone()))),
239        Value::PriorityMap(value) => {
240            Value::PriorityMap(Box::new(value.with_meta(metadata.clone())))
241        }
242        Value::Set(value) => Value::Set(value.with_meta(metadata.clone())),
243        Value::OrderedSet(value) => Value::OrderedSet(Box::new(value.with_meta(metadata.clone()))),
244        Value::SortedSet(value) => Value::SortedSet(Box::new(value.with_meta(metadata.clone()))),
245        Value::Seq(value) => Value::Seq(Box::new(value.with_meta(metadata.clone()))),
246        Value::Var(value) => {
247            value.set_hara_metadata(metadata);
248            Value::Var(value)
249        }
250        Value::Function(value) => Value::Function(Rc::new(Function {
251            metadata,
252            ..value.as_ref().clone()
253        })),
254        Value::Struct(value) => Value::Struct(Rc::new(StructValue {
255            ty: value.ty.clone(),
256            values: value.values.clone(),
257            metadata,
258        })),
259        Value::Mutable(value) => Value::Mutable(Rc::new(MutableValue {
260            ty: value.ty.clone(),
261            values: value.values.clone(),
262            metadata,
263        })),
264        Value::NativeType(value) => Value::NativeType(Rc::new(NativeType {
265            name: value.name.clone(),
266            methods: value.methods.clone(),
267            availability: value.availability,
268            capability: value.capability.clone(),
269            metadata,
270        })),
271        Value::Keyword(value) => Value::Keyword(value),
272        _ => return Err("metadata can only be applied to object values".into()),
273    })
274}
275
276fn attach_metadata(value: Value, metadata: Rc<Metadata>) -> Result<Value, String> {
277    attach_optional_metadata(value, Some(metadata))
278}
279
280fn collection_constructor_values(name: &str, values: Vec<Value>) -> Result<Value, String> {
281    match name {
282        "hash-map" | "ordered-map" | "priority-map" | "sorted-map" | "trie" => {
283            if values.len() % 2 != 0 {
284                return Err(format!(
285                    "{name} expects an even number of key/value arguments"
286                ));
287            }
288            let entries = values
289                .chunks_exact(2)
290                .map(|pair| (pair[0].clone(), pair[1].clone()));
291            Ok(match name {
292                "hash-map" => Value::Map(PMap::from_iter(entries)),
293                "ordered-map" => Value::OrderedMap(Box::new(POrderedMap::from_iter(entries))),
294                "priority-map" => Value::PriorityMap(Box::new(PPriorityMap::from_iter(entries))),
295                "sorted-map" => Value::SortedMap(Box::new(PSortedMap::from_iter(entries))),
296                "trie" => {
297                    let mut trie = PTrie::new();
298                    for (key, value) in entries {
299                        let Value::String(key) = key else {
300                            return Err("trie expects string keys".into());
301                        };
302                        trie = trie.assoc_value(key, value);
303                    }
304                    Value::Trie(Box::new(trie))
305                }
306                _ => unreachable!("guarded map constructor"),
307            })
308        }
309        "hash-set" => Ok(Value::Set(values.into_iter().collect())),
310        "ordered-set" => Ok(Value::OrderedSet(Box::new(values.into_iter().collect()))),
311        "sorted-set" => Ok(Value::SortedSet(Box::new(values.into_iter().collect()))),
312        "deque" => Ok(Value::Deque(Box::new(values.into_iter().collect()))),
313        "queue" => Ok(Value::Queue(Box::new(values.into_iter().collect()))),
314        _ => unreachable!("guarded collection constructor"),
315    }
316}
317
318fn vector_literal(values: Vec<Value>) -> Result<Value, String> {
319    if values.len() <= 8 {
320        Ok(Value::Tuple(Box::new(PTuple::from_values(values)?)))
321    } else {
322        Ok(Value::Vector(values.into()))
323    }
324}
325
326pub(crate) fn vm_build_vector(values: Vec<Value>) -> Result<Value, String> {
327    vector_literal(values)
328}
329
330pub(crate) fn vm_build_map(values: Vec<Value>) -> Result<Value, String> {
331    if values.len() % 2 != 0 {
332        return Err("map construction requires key/value pairs".into());
333    }
334    Ok(Value::Map(PMap::from_iter(
335        values
336            .chunks_exact(2)
337            .map(|pair| (pair[0].clone(), pair[1].clone())),
338    )))
339}
340
341pub(crate) fn vm_build_set(values: Vec<Value>) -> Result<Value, String> {
342    Ok(Value::OrderedSet(Box::new(POrderedSet::from_iter(values))))
343}
344
345pub(crate) fn vm_build_list(values: Vec<Value>) -> Value {
346    Value::List(values.into())
347}
348
349pub(crate) fn vm_concat_list(values: Vec<Value>) -> Result<Value, String> {
350    let mut output = Vec::new();
351    for value in values {
352        output.extend(iterator_values(value)?);
353    }
354    Ok(Value::List(output.into()))
355}
356
357pub(crate) fn vm_to_vector(value: Value) -> Result<Value, String> {
358    vector_literal(iterator_values(value)?)
359}
360
361fn literal_value(form: &Form) -> Result<Value, String> {
362    match form {
363        Form::Nil => Ok(Value::Nil),
364        Form::Bool(value) => Ok(Value::Bool(*value)),
365        Form::Character(value) => Ok(Value::Character(*value)),
366        Form::Float(value) => Ok(Value::Float(crate::numeric::finite_float(*value)?)),
367        Form::BigInteger(value) => Ok(crate::numeric::compact_integer(value.clone())),
368        Form::Regex(value) => Ok(Value::Regex(value.clone())),
369        Form::Tagged(tag, value) if tag == "ptr" => pointer_from_descriptor(literal_value(value)?),
370        Form::Tagged(tag, value) if tag == "uuid" => {
371            crate::core::uuid_tag_value(literal_value(value)?)
372        }
373        Form::Tagged(tag, value) if tag == "ex" => exception_literal_value(value),
374        Form::Tagged(tag, value) if tag == "result" => result_literal_value(value),
375        Form::Tagged(tag, value) if tag == "arr" => {
376            let Form::Vector(values) = value.as_ref() else {
377                return Err("#arr expects a vector literal".into());
378            };
379            Ok(Value::Array(Rc::new(RefCell::new(
380                values
381                    .iter()
382                    .map(literal_value)
383                    .collect::<Result<Vec<_>, _>>()?,
384            ))))
385        }
386        Form::Tagged(tag, value) if tag == "obj" => {
387            let Form::Map(entries) = value.as_ref() else {
388                return Err("#obj expects a map literal".into());
389            };
390            Ok(Value::Object(Rc::new(RefCell::new(
391                entries
392                    .iter()
393                    .map(|(key, value)| {
394                        Ok((
395                            marker_key(&literal_value(key)?, "#obj")?,
396                            literal_value(value)?,
397                        ))
398                    })
399                    .collect::<Result<Vec<_>, String>>()?,
400            ))))
401        }
402        Form::Tagged(tag, value) => Ok(Value::Tagged(Box::new(PTaggedLiteral::new(
403            Symbol::parse(tag),
404            literal_value(value)?,
405        )))),
406        Form::Metadata(metadata, value) => {
407            attach_metadata(literal_value(value)?, metadata_from_form(metadata)?)
408        }
409        Form::Number(v) => Ok(Value::Number(*v)),
410        Form::String(v) => Ok(Value::String(v.clone())),
411        Form::Keyword(v) => Ok(Value::Keyword(v.clone().into())),
412        Form::Symbol(v) => Ok(Value::Symbol(v.clone().into())),
413        Form::Vector(values) => {
414            vector_literal(values.iter().map(literal_value).collect::<Result<_, _>>()?)
415        }
416        Form::Set(values) => Ok(Value::OrderedSet(Box::new(
417            unique_values(values.iter().map(literal_value).collect::<Result<_, _>>()?)
418                .into_iter()
419                .collect(),
420        ))),
421        Form::List(values) => Ok(Value::List(
422            values.iter().map(literal_value).collect::<Result<_, _>>()?,
423        )),
424        Form::Map(values) => Ok(Value::Map(
425            values
426                .iter()
427                .map(|(k, v)| Ok((literal_value(k)?, literal_value(v)?)))
428                .collect::<Result<_, String>>()?,
429        )),
430    }
431}
432
433fn exception_literal_value(form: &Form) -> Result<Value, String> {
434    let Form::Vector(values) = form else {
435        return Err("#ex expects a [message data] vector".into());
436    };
437    let [message, data] = values.as_slice() else {
438        return Err("#ex expects exactly a message and data value".into());
439    };
440    let Value::String(message) = literal_value(message)? else {
441        return Err("#ex message must be a string".into());
442    };
443    let data = literal_value(data)?;
444    let cause = map_entries(&data)
445        .and_then(|entries| {
446            entries.into_iter().find_map(|(key, value)| {
447                matches!(key, Value::Keyword(name) if name.as_str() == "ex/cause")
448                    .then_some(value)
449            })
450        });
451    let cause = match cause {
452        Some(Value::ExceptionInfo(value)) => Some(Box::new(Value::ExceptionInfo(value))),
453        Some(_) => return Err("#ex :ex/cause must be an Exception".into()),
454        None => None,
455    };
456    Ok(Value::ExceptionInfo(Rc::new(ExceptionInfo {
457        message,
458        data: Box::new(data),
459        cause,
460        provenance: Rc::new(RefCell::new(ExceptionProvenance::default())),
461    })))
462}
463
464fn result_literal_value(form: &Form) -> Result<Value, String> {
465    let Form::Vector(values) = form else {
466        return Err("#result expects a [status data error context] vector".into());
467    };
468    let [status, data, error, context] = values.as_slice() else {
469        return Err("#result expects exactly status, data, error, and context".into());
470    };
471    let Value::Keyword(status) = literal_value(status)? else {
472        return Err("#result status must be :success or :error".into());
473    };
474    let data = literal_value(data)?;
475    let error = literal_value(error)?;
476    let context = literal_value(context)?;
477    match status.as_str() {
478        "success" => {
479            if !matches!(error, Value::Nil) {
480                return Err("#result success must have nil error".into());
481            }
482            Ok(Value::Result(Rc::new(ResultValue::success(data, context)?)))
483        }
484        "error" => {
485            if !matches!(data, Value::Nil) {
486                return Err("#result error must have nil data".into());
487            }
488            if !matches!(error, Value::ExceptionInfo(_)) {
489                return Err("#result error must be an Exception".into());
490            }
491            Ok(Value::Result(Rc::new(ResultValue::error(error, context)?)))
492        }
493        _ => Err("#result status must be :success or :error".into()),
494    }
495}
496
497#[cfg(test)]
498mod literal_tests {
499    use super::*;
500
501    #[test]
502    fn short_exception_and_result_literals_round_trip_without_ex_info() {
503        let exception = literal_value(&Form::Tagged(
504            "ex".into(),
505            Box::new(Form::Vector(vec![
506                Form::String("boom".into()),
507                Form::Map(vec![(
508                    Form::Keyword("kind".into()),
509                    Form::Keyword("fixture".into()),
510                )]),
511            ])),
512        ))
513        .expect("#ex literal");
514        assert_eq!(exception.display(), "#ex[\"boom\" {:kind :fixture}]");
515        let forms = crate::kernel::parse_forms(&exception.display()).expect("printed #ex parses");
516        let Value::ExceptionInfo(round_trip) = literal_value(&forms[0]).expect("printed #ex reads") else {
517            panic!("#ex must read as an Exception");
518        };
519        assert_eq!(round_trip.message, "boom");
520        assert_eq!(round_trip.data.display(), "{:kind :fixture}");
521        assert!(
522            !exception_function_values()
523                .iter()
524                .any(|(name, _)| *name == "ex-info"),
525            "ex-info must not enter the Foundation prelude"
526        );
527
528        let result = Value::Result(Rc::new(
529            ResultValue::success(
530                Value::Number(42),
531                Value::Map(PMap::from_iter([(
532                    Value::Keyword("trace".into()),
533                    Value::Number(8),
534                )])),
535            )
536            .expect("Result value"),
537        ));
538        assert_eq!(result.display(), "#result[:success 42 nil {:trace 8}]");
539        let forms = crate::kernel::parse_forms(&result.display()).expect("printed #result parses");
540        assert_eq!(literal_value(&forms[0]).expect("printed #result reads"), result);
541    }
542}
543
544fn function_parts(
545    form: &Form,
546) -> Result<(Vec<String>, Option<String>, Vec<Form>, Option<Form>), String> {
547    let list = match form_without_metadata(form) {
548        Form::Vector(values) => values,
549        _ => return Err("function parameters must be a vector".into()),
550    };
551    let mut params = Vec::new();
552    let mut variadic = None;
553    let mut patterns = Vec::new();
554    let mut variadic_pattern = None;
555    let mut index = 0;
556    while index < list.len() {
557        match form_without_metadata(&list[index]) {
558            Form::Symbol(name) if name == "&" => {
559                if variadic.is_some() || index + 1 >= list.len() || index + 2 != list.len() {
560                    return Err("variadic marker must precede the final parameter".into());
561                }
562                let pattern = form_without_metadata(&list[index + 1]).clone();
563                variadic = Some(match &pattern {
564                    Form::Symbol(name) => name.clone(),
565                    _ => format!("__rest_{}", params.len()),
566                });
567                variadic_pattern = Some(pattern);
568                index += 2;
569            }
570            pattern @ (Form::Symbol(_) | Form::Vector(_) | Form::Map(_)) => {
571                params.push(match pattern {
572                    Form::Symbol(name) => name.clone(),
573                    _ => format!("__arg_{}", params.len()),
574                });
575                patterns.push(pattern.clone());
576                index += 1;
577            }
578            _ => return Err("function parameters must be binding patterns".into()),
579        }
580    }
581    Ok((params, variadic, patterns, variadic_pattern))
582}
583
584fn collect_capture_names(form: &Form, names: &mut std::collections::HashSet<String>) {
585    match form {
586        Form::Symbol(name) => {
587            names.insert(name.clone());
588        }
589        Form::List(values) | Form::Vector(values) | Form::Set(values) => {
590            for value in values {
591                collect_capture_names(value, names);
592            }
593        }
594        Form::Map(entries) => {
595            for (key, value) in entries {
596                collect_capture_names(key, names);
597                collect_capture_names(value, names);
598            }
599        }
600        Form::Metadata(metadata, value) => {
601            collect_capture_names(metadata, names);
602            collect_capture_names(value, names);
603        }
604        Form::Tagged(_, value) => collect_capture_names(value, names),
605        Form::Nil
606        | Form::Bool(_)
607        | Form::Number(_)
608        | Form::Float(_)
609        | Form::BigInteger(_)
610        | Form::Character(_)
611        | Form::Regex(_)
612        | Form::String(_)
613        | Form::Keyword(_) => {}
614    }
615}
616
617fn capture_environment(forms: &[Form], env: &HashMap<String, Value>) -> HashMap<String, Value> {
618    let mut names = std::collections::HashSet::new();
619    for form in forms {
620        collect_capture_names(form, &mut names);
621    }
622    names
623        .into_iter()
624        .filter_map(|name| env.get(&name).cloned().map(|value| (name, value)))
625        .collect()
626}
627
628fn destructuring_default<'a>(defaults: Option<&'a Form>, name: &str) -> Option<&'a Form> {
629    let Form::Map(entries) = defaults? else {
630        return None;
631    };
632    entries.iter().find_map(|(key, value)| {
633        matches!(key, Form::Symbol(candidate) if candidate == name).then_some(value)
634    })
635}
636
637pub(crate) fn bind_pattern(
638    pattern: &Form,
639    value: Value,
640    env: &mut HashMap<String, Value>,
641    bound: &mut Vec<String>,
642    defaults: Option<&Form>,
643) -> Result<(), String> {
644    match pattern {
645        Form::Symbol(name) => {
646            if name == "_" {
647                return Ok(());
648            }
649            if name.contains('/') || bound.iter().any(|candidate| candidate == name) {
650                return Err(format!("invalid or duplicate binding: {name}"));
651            }
652            let value = if matches!(value, Value::Nil) {
653                match destructuring_default(defaults, name) {
654                    Some(default) => eval(default, env)?,
655                    None => value,
656                }
657            } else {
658                value
659            };
660            env.insert(name.clone(), value);
661            bound.push(name.clone());
662            Ok(())
663        }
664        Form::Vector(patterns) => {
665            let original = value.clone();
666            let values = if matches!(value, Value::Nil) {
667                Vec::new()
668            } else {
669                iterator_values(value)
670                    .map_err(|_| "cannot destructure non-sequential value".to_owned())?
671            };
672            let mut index = 0;
673            let mut position = 0;
674            while index < patterns.len() {
675                match &patterns[index] {
676                    Form::Symbol(marker) if marker == "&" => {
677                        if index + 1 >= patterns.len() {
678                            return Err("& in a destructuring vector requires a binding".into());
679                        }
680                        bind_pattern(
681                            &patterns[index + 1],
682                            Value::Vector(values.iter().skip(position).cloned().collect()),
683                            env,
684                            bound,
685                            defaults,
686                        )?;
687                        index += 2;
688                    }
689                    Form::Keyword(marker) if marker.as_str() == "as" => {
690                        if index + 2 != patterns.len() {
691                            return Err(
692                                ":as in a destructuring vector must precede its final binding"
693                                    .into(),
694                            );
695                        }
696                        bind_pattern(&patterns[index + 1], original, env, bound, defaults)?;
697                        return Ok(());
698                    }
699                    nested => {
700                        bind_pattern(
701                            nested,
702                            values.get(position).cloned().unwrap_or(Value::Nil),
703                            env,
704                            bound,
705                            defaults,
706                        )?;
707                        position += 1;
708                        index += 1;
709                    }
710                }
711            }
712            Ok(())
713        }
714        Form::Map(entries) => {
715            if !matches!(value, Value::Nil | Value::Struct(_)) && map_entries(&value).is_none() {
716                return Err("cannot destructure non-map value".into());
717            }
718            let defaults = entries.iter().find_map(|(key, value)| {
719                matches!(key, Form::Keyword(keyword) if keyword.as_str() == "or").then_some(value)
720            });
721            for (binding, key) in entries {
722                match binding {
723                    Form::Keyword(keyword) if keyword.as_str() == "or" => {}
724                    Form::Keyword(keyword) if keyword.as_str() == "as" => {
725                        bind_pattern(key, value.clone(), env, bound, defaults)?;
726                    }
727                    Form::Keyword(keyword)
728                        if ["keys", "strs", "syms"].contains(&keyword.as_str()) =>
729                    {
730                        let Form::Vector(names) = key else {
731                            return Err(format!(
732                                ":{} destructuring expects a vector of symbols",
733                                keyword.as_str()
734                            ));
735                        };
736                        for name in names {
737                            let Form::Symbol(name) = name else {
738                                return Err(format!(
739                                    ":{} destructuring expects symbols",
740                                    keyword.as_str()
741                                ));
742                            };
743                            let lookup = match keyword.as_str() {
744                                "keys" => Value::Keyword(name.clone().into()),
745                                "strs" => Value::String(name.clone()),
746                                "syms" => Value::Symbol(name.clone().into()),
747                                _ => unreachable!(),
748                            };
749                            bind_pattern(
750                                &Form::Symbol(name.clone()),
751                                collection_get(&value, &lookup, Value::Nil)?,
752                                env,
753                                bound,
754                                defaults,
755                            )?;
756                        }
757                    }
758                    binding => {
759                        let lookup = literal_value(key)?;
760                        bind_pattern(
761                            binding,
762                            collection_get(&value, &lookup, Value::Nil)?,
763                            env,
764                            bound,
765                            defaults,
766                        )?;
767                    }
768                }
769            }
770            Ok(())
771        }
772        _ => Err("unsupported binding pattern".into()),
773    }
774}
775
776fn select_clause(functions: &[Rc<Function>], argument_count: usize) -> Option<Rc<Function>> {
777    functions
778        .iter()
779        .find(|function| function.variadic.is_none() && function.params.len() == argument_count)
780        .or_else(|| {
781            functions
782                .iter()
783                .filter(|function| {
784                    function.variadic.is_some() && argument_count >= function.params.len()
785                })
786                .max_by_key(|function| function.params.len())
787        })
788        .cloned()
789}
790
791fn multi_arity_function(
792    name: &str,
793    clauses: &[Form],
794    captured: &HashMap<String, Value>,
795    is_macro: bool,
796) -> Result<Value, String> {
797    let mut functions = Vec::with_capacity(clauses.len());
798    for clause in clauses {
799        let parts = match form_without_metadata(clause) {
800            Form::List(parts) if parts.len() >= 2 => parts,
801            _ => return Err("defn arity must contain parameters and a body".into()),
802        };
803        let (params, variadic, patterns, variadic_pattern) = function_parts(&parts[0])?;
804        functions.push(Rc::new(Function {
805            params,
806            variadic,
807            patterns,
808            variadic_pattern,
809            body: parts[1..].to_vec(),
810            captured: Rc::new(RefCell::new(capture_environment(&parts[1..], captured))),
811            name: Some(name.into()),
812            namespace: function_definition_namespace(),
813            native: None,
814            fiber_native: None,
815            clauses: Vec::new(),
816            metadata: None,
817            is_macro,
818        }));
819    }
820    if functions.is_empty() {
821        return Err("defn expects at least one arity".into());
822    }
823    Ok(arity_dispatcher(name, functions, is_macro))
824}
825
826/// Builds the multi-arity dispatcher shared by the evaluator's defn and
827/// the bytecode VM's `MakeMultiArity` (issue #223): exact fixed-arity
828/// match first, then the variadic clause with the most parameters.
829pub(crate) fn arity_dispatcher(name: &str, functions: Vec<Rc<Function>>, is_macro: bool) -> Value {
830    let dispatch_name = name.to_owned();
831    let clauses = functions.clone();
832    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
833    let fiber_functions = functions.clone();
834    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
835    let fiber_dispatch_name = dispatch_name.clone();
836    Value::Function(Rc::new(Function {
837        params: Vec::new(),
838        variadic: Some("arguments".into()),
839        patterns: Vec::new(),
840        variadic_pattern: None,
841        body: Vec::new(),
842        captured: Rc::new(RefCell::new(HashMap::new())),
843        name: Some(dispatch_name.clone()),
844        namespace: function_definition_namespace(),
845        clauses,
846        native: Some(Rc::new(move |arguments| {
847            let function = select_clause(&functions, arguments.len()).ok_or_else(|| {
848                format!(
849                    "{dispatch_name} has no arity accepting {} arguments",
850                    arguments.len()
851                )
852            })?;
853            call_function(&function, arguments)
854        })),
855        #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
856        fiber_native: Some(Rc::new(move |arguments, continuation| {
857            let function = select_clause(&fiber_functions, arguments.len()).ok_or_else(|| {
858                format!(
859                    "{fiber_dispatch_name} has no arity accepting {} arguments",
860                    arguments.len()
861                )
862            });
863            match function {
864                Ok(function) => crate::core::call_direct_native_fiber(
865                    Value::Function(function),
866                    arguments,
867                    continuation,
868                )
869                .unwrap_or_else(|error| Step::Done(Err(error))),
870                Err(error) => Step::Done(Err(error)),
871            }
872        })),
873        #[cfg(not(all(feature = "direct-native", not(target_arch = "wasm32"))))]
874        fiber_native: None,
875        metadata: None,
876        is_macro,
877    }))
878}
879
880fn deref_binding_value(name: &str, value: Value) -> Value {
881    match value {
882        Value::Var(var)
883            if name.starts_with("std.native.")
884                || name.starts_with("std.protocol.")
885                || var.symbol().get_name() == Symbol::parse(name).get_name() =>
886        {
887            var.deref_value()
888        }
889        value => value,
890    }
891}
892
893fn binding_value(env: &HashMap<String, Value>, name: &str) -> Option<Value> {
894    env.get(name)
895        .cloned()
896        .map(|value| deref_binding_value(name, value))
897        .or_else(|| {
898            let registry = namespace_registry().ok()?;
899            registry
900                .resolve(&crate::lang::data::Symbol::parse(name))
901                .or_else(|| {
902                    crate::core::canonical_intrinsic_symbol(name).and_then(|canonical| {
903                        registry.resolve(&crate::lang::data::Symbol::parse(&canonical))
904                    })
905                })
906                .map(|var| var.deref_value())
907        })
908        .or_else(|| {
909            let registry = namespace_registry().ok()?;
910            let local = crate::lang::data::Symbol::parse(name);
911            if name.contains('/') || !registry.current().foundation_visible(&local) {
912                return None;
913            }
914            registry
915                .find("std.foundation")
916                .and_then(|foundation| foundation.resolve(&local))
917                .map(|var| var.deref_value())
918        })
919        .or_else(|| {
920            let (qualifier, local) = name.rsplit_once('/')?;
921            let registry = namespace_registry().ok()?;
922            (registry.current().name().as_str() == qualifier)
923                .then(|| {
924                    env.get(local)
925                        .cloned()
926                        .map(|value| deref_binding_value(local, value))
927                })
928                .flatten()
929        })
930}
931
932fn foundation_fallback_omitted(env: &HashMap<String, Value>, name: &str) -> bool {
933    // Runtime-native helpers are resolved through their qualified native type
934    // symbols, not through an unqualified Foundation fallback.
935    const INTRINSIC_FORMS: &[&str] = &[
936        "ns-alias-state",
937        "ns-loaded?",
938        "ns-state",
939        "resolve",
940    ];
941    if name.contains('/')
942        || env.contains_key(name)
943        || syntax_symbol(name)
944        || INTRINSIC_FORMS.contains(&name)
945    {
946        return false;
947    }
948    let Ok(registry) = namespace_registry() else {
949        return false;
950    };
951    let local = crate::lang::data::Symbol::parse(name);
952    registry.resolve(&local).is_none()
953        && registry
954            .find("std.foundation")
955            .and_then(|foundation| foundation.resolve(&local))
956            .is_some()
957}
958
959fn binding_var(env: &mut HashMap<String, Value>, name: &str) -> Option<KernelVar<Value>> {
960    match env.get(name) {
961        Some(Value::Var(var)) => Some(var.clone()),
962        Some(value) => {
963            let var = KernelVar::new(name, value.clone());
964            env.insert(name.to_string(), Value::Var(var.clone()));
965            Some(var)
966        }
967        None => {
968            if let Some(local) = name.strip_prefix("-/") {
969                if let Some(Value::Var(var)) = env.get(local) {
970                    return Some(var.clone());
971                }
972            }
973            namespace_registry()
974                .ok()?
975                .resolve(&crate::lang::data::Symbol::parse(name))
976        }
977    }
978}
979
980pub(crate) fn call_value(callable: Value, arguments: Vec<Value>) -> Result<Value, String> {
981    let lookup =
982        |target: &Value, key: &Value, fallback: Value| collection_get(target, key, fallback);
983    match callable {
984        Value::Function(function) => call_function(&function, arguments),
985        Value::Namespace(namespace) => namespace
986            .resolve(&crate::lang::data::Symbol::parse("run"))
987            .map(|var| var.deref_value())
988            .ok_or_else(|| format!("namespace is not callable: {}", namespace.name().as_str()))
989            .and_then(|function| call_value(function, arguments)),
990        Value::StructType(ty) => Ok(Value::Struct(Rc::new(StructValue::from_values(
991            ty, arguments, None,
992        )?))),
993        Value::MutableType(ty) => Ok(Value::Mutable(Rc::new(MutableValue::from_values(
994            ty, arguments, None,
995        )?))),
996        value @ (Value::Struct(_) | Value::Mutable(_)) => {
997            let mut protocol_arguments = Vec::with_capacity(arguments.len() + 1);
998            protocol_arguments.push(value);
999            protocol_arguments.extend(arguments);
1000            protocol_call("std.protocol.ifn.IFn", "invoke", &protocol_arguments)
1001        }
1002        Value::Pointer(pointer) => pointer_invoke(&pointer, pointer_default(&pointer)?, &arguments),
1003        Value::Keyword(keyword) => match arguments.as_slice() {
1004            [target] => lookup(target, &Value::Keyword(keyword), Value::Nil),
1005            [target, fallback] => lookup(target, &Value::Keyword(keyword), fallback.clone()),
1006            _ => Err("keyword invocation expects one or two arguments".into()),
1007        },
1008        value @ (Value::Map(_)
1009        | Value::OrderedMap(_)
1010        | Value::SortedMap(_)
1011        | Value::Trie(_)
1012        | Value::PriorityMap(_)) => match arguments.as_slice() {
1013            [key] => Ok(map_value(&value, key).cloned().unwrap_or(Value::Nil)),
1014            [key, fallback] => Ok(map_value(&value, key)
1015                .cloned()
1016                .unwrap_or_else(|| fallback.clone())),
1017            _ => Err("map invocation expects one or two arguments".into()),
1018        },
1019        value @ (Value::Set(_) | Value::OrderedSet(_) | Value::SortedSet(_)) => {
1020            match arguments.as_slice() {
1021                [key] => Ok(set_find(&value, key).unwrap_or(Value::Nil)),
1022                [key, fallback] => Ok(set_find(&value, key).unwrap_or_else(|| fallback.clone())),
1023                _ => Err("set invocation expects one or two arguments".into()),
1024            }
1025        }
1026        _ => Err("value is not callable".into()),
1027    }
1028}
1029
1030/// Invokes a runtime callable with already-decoded values.
1031///
1032/// Embedding hosts use this prepare-once/call-many boundary to avoid routing
1033/// native values back through source text. Namespace, protocol, and host-call
1034/// contexts remain controlled by the caller.
1035pub fn invoke_callable(callable: Value, arguments: Vec<Value>) -> Result<Value, String> {
1036    call_value(callable, arguments)
1037}
1038
1039/// Invokes a callable without permitting an evaluator-backed Hara function to
1040/// cross the direct-native boundary.
1041///
1042/// Native functions and structural callable values (keywords, maps, sets,
1043/// pointers, and named value types) retain their ordinary semantics. A Hara
1044/// function whose body still belongs to the tree evaluator is rejected rather
1045/// than silently falling back to interpretation.
1046#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
1047pub(crate) fn call_direct_native_value(
1048    callable: Value,
1049    arguments: Vec<Value>,
1050) -> Result<Value, String> {
1051    match &callable {
1052        Value::Function(function) if is_direct_native_function(function) => {
1053            call_function(function, arguments)
1054        }
1055        Value::Function(function) => Err(format!(
1056            "direct-native cannot call an evaluator-backed Hara function {}; compile the callee first",
1057            function
1058                .origin_symbol()
1059                .map(|symbol| symbol.as_str().to_owned())
1060                .unwrap_or_else(|| "<anonymous>".into())
1061        )),
1062        _ => call_value(callable, arguments),
1063    }
1064}
1065
1066#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
1067pub(crate) fn call_direct_native_fiber(
1068    callable: Value,
1069    arguments: Vec<Value>,
1070    continuation: Cont,
1071) -> Result<Step, String> {
1072    match callable {
1073        Value::Function(function) if is_direct_native_function(&function) => {
1074            if let Some(fiber_native) = &function.fiber_native {
1075                return Ok(fiber_native(arguments, continuation));
1076            }
1077            Ok(Step::Done(call_direct_native_value(
1078                Value::Function(function),
1079                arguments,
1080            )))
1081        }
1082        Value::Function(function) => Err(format!(
1083            "direct-native cannot call an evaluator-backed Hara function {}; compile the callee first",
1084            function
1085                .origin_symbol()
1086                .map(|symbol| symbol.as_str().to_owned())
1087                .unwrap_or_else(|| "<anonymous>".into())
1088        )),
1089        Value::Namespace(namespace) => {
1090            let run = namespace
1091                .resolve(&crate::lang::data::Symbol::parse("run"))
1092                .map(|var| var.deref_value())
1093                .ok_or_else(|| {
1094                    format!("namespace is not callable: {}", namespace.name().as_str())
1095                })?;
1096            call_direct_native_fiber(run, arguments, continuation)
1097        }
1098        value => Ok(Step::Done(call_direct_native_value(value, arguments))),
1099    }
1100}
1101
1102#[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
1103pub(crate) fn is_direct_native_function(function: &Function) -> bool {
1104    // A direct-native closure also carries a fiber callback so that the
1105    // portable coroutine boundary can retain and resume its native frame.
1106    // The presence of `native` still distinguishes it from a tree-evaluator
1107    // function; fiber-backed native primitives are likewise safe because
1108    // their synchronous callback never enters the evaluator.
1109    function.native.is_some()
1110}
1111
1112pub(crate) fn call_function(function: &Function, arguments: Vec<Value>) -> Result<Value, String> {
1113    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
1114    if direct_native_execution() && !is_direct_native_function(function) {
1115        return Err(format!(
1116            "direct-native cannot call an evaluator- or fiber-backed Hara function {}; compile the callee first",
1117            function
1118                .origin_symbol()
1119                .map(|symbol| symbol.as_str().to_owned())
1120            .unwrap_or_else(|| "<anonymous>".into())
1121        ));
1122    }
1123    #[cfg(all(feature = "direct-native", not(target_arch = "wasm32")))]
1124    if let Some(symbol) = function.origin_symbol() {
1125        crate::direct_native::record_native_target(symbol.as_str());
1126    }
1127    #[cfg(feature = "evaluation-journal")]
1128    let operation = evaluation_journal_enter(function, &arguments);
1129    if let Some(native) = &function.native {
1130        if function.variadic.is_none() && function.params.len() != arguments.len() {
1131            #[cfg(feature = "evaluation-journal")]
1132            evaluation_journal_exit(operation, function, None);
1133            if function.name.as_deref() == Some("type") {
1134                return Err("type expects one value".into());
1135            }
1136            return Err(format!(
1137                "function expects {} arguments",
1138                function.params.len()
1139            ));
1140        }
1141        let result = native(arguments);
1142        #[cfg(feature = "evaluation-journal")]
1143        evaluation_journal_exit(operation, function, result.as_ref().ok());
1144        return result;
1145    }
1146    let tracing = tracing_enabled();
1147    if tracing {
1148        TRACE_STACK.with(|stack| {
1149            stack.borrow_mut().push(trace_frame(
1150                function
1151                    .name
1152                    .clone()
1153                    .unwrap_or_else(|| "<anonymous>".into()),
1154                function.namespace.clone(),
1155                current_exception_site(),
1156            ))
1157        });
1158    }
1159    let caller_scoped_foundation = function.namespace.as_deref() == Some("std.foundation")
1160        && (function.is_macro
1161            || matches!(
1162                function.name.as_deref(),
1163                Some(
1164                    "macroexpand"
1165                        | "macroexpand-1"
1166                        | "ns-current"
1167                        | "ns-alias-state"
1168                        | "eval"
1169                        | "eval-in-ns"
1170                        | "env-snapshot"
1171                        | "ns-vars"
1172                        | "ns-list"
1173                        | "ns-info"
1174                        | "env-module"
1175                )
1176            ));
1177    let namespace_scope = namespace_registry().ok().and_then(|registry| {
1178        (!caller_scoped_foundation)
1179            .then_some(())
1180            .and_then(|_| function.namespace.as_ref())
1181            .map(|namespace| {
1182            let previous = registry.current().name().as_str().to_owned();
1183            registry.set_current(namespace);
1184            (registry, previous)
1185        })
1186    });
1187    let result = (|| {
1188        if function.variadic.is_none() && function.params.len() != arguments.len() {
1189            if function.namespace.as_deref() == Some("std.foundation")
1190                && function.name.as_deref() == Some("type")
1191            {
1192                return Err("type expects one value".into());
1193            }
1194            return Err(format!(
1195                "function expects {} arguments",
1196                function.params.len()
1197            ));
1198        }
1199        if arguments.len() < function.params.len() {
1200            return Err(format!(
1201                "function expects at least {} arguments",
1202                function.params.len()
1203            ));
1204        }
1205        let mut env = function.captured.borrow().clone();
1206        for (name, value) in function
1207            .params
1208            .iter()
1209            .zip(arguments.iter().take(function.params.len()))
1210        {
1211            env.insert(name.clone(), value.clone());
1212        }
1213        let mut bound = Vec::new();
1214        for (pattern, value) in function
1215            .patterns
1216            .iter()
1217            .zip(arguments.iter().take(function.params.len()))
1218        {
1219            bind_pattern(pattern, value.clone(), &mut env, &mut bound, None)?;
1220        }
1221        if let Some(name) = &function.variadic {
1222            let rest = Value::List(arguments.into_iter().skip(function.params.len()).collect());
1223            env.insert(name.clone(), rest.clone());
1224            if let Some(pattern) = &function.variadic_pattern {
1225                bind_pattern(pattern, rest, &mut env, &mut bound, None)?;
1226            }
1227        }
1228        let mut result = Value::Nil;
1229        for form in &function.body {
1230            result = eval(form, &mut env)?;
1231            if matches!(result, Value::Recur(_)) {
1232                return Err("recur must be inside loop".into());
1233            }
1234        }
1235        Ok(result)
1236    })();
1237    if let Some((registry, previous)) = namespace_scope {
1238        registry.set_current(previous);
1239    }
1240    let result = result.map_err(append_trace);
1241    #[cfg(feature = "evaluation-journal")]
1242    evaluation_journal_exit(operation, function, result.as_ref().ok());
1243    if tracing {
1244        TRACE_STACK.with(|stack| {
1245            stack.borrow_mut().pop();
1246        });
1247    }
1248    result
1249}
1250
1251/// Runs one evaluator operation with a bounded evaluation journal. This is
1252/// intentionally separate from the legacy stack-trace flag above.
1253#[cfg(feature = "evaluation-journal")]
1254pub fn with_evaluation_journal<T>(
1255    journal_id: crate::journal::JournalId,
1256    limits: crate::journal::JournalLimits,
1257    evaluate: impl FnOnce() -> Result<T, String>,
1258    preview: impl FnOnce(&T, &crate::journal::JournalCollector) -> crate::journal::ValuePreview,
1259) -> (Result<T, String>, crate::journal::Journal) {
1260    EVALUATION_JOURNAL_STACK.with(|stack| stack.borrow_mut().clear());
1261    let previous = EVALUATION_JOURNAL.with(|active| {
1262        active.replace(Some(crate::journal::JournalCollector::new(
1263            journal_id, limits,
1264        )))
1265    });
1266    assert!(
1267        previous.is_none(),
1268        "nested evaluation journals are not supported yet"
1269    );
1270    EVALUATION_JOURNAL.with(|active| {
1271        active
1272            .borrow_mut()
1273            .as_mut()
1274            .expect("evaluation journal must be active")
1275            .record(crate::journal::JournalEvent::new(
1276                crate::journal::JournalEventKind::EvaluationStart,
1277            ));
1278    });
1279    let result = evaluate();
1280    let collector = EVALUATION_JOURNAL.with(|active| {
1281        active
1282            .replace(previous)
1283            .expect("evaluation journal must be active")
1284    });
1285    EVALUATION_JOURNAL_STACK.with(|stack| stack.borrow_mut().clear());
1286    let trace = match &result {
1287        Ok(value) => {
1288            let result = preview(value, &collector);
1289            collector.finish(result)
1290        }
1291        Err(error) => collector.fail(error.clone()),
1292    };
1293    (result, trace)
1294}
1295
1296pub(crate) fn binding_symbol(
1297    form: &Form,
1298    context: &str,
1299) -> Result<(String, Option<Rc<Metadata>>), String> {
1300    match form {
1301        Form::Symbol(name) => Ok((name.clone(), None)),
1302        Form::Metadata(metadata, value) => match value.as_ref() {
1303            Form::Symbol(name) => Ok((name.clone(), Some(metadata_from_form(metadata)?))),
1304            _ => Err(format!("{context} must be a symbol")),
1305        },
1306        _ => Err(format!("{context} must be a symbol")),
1307    }
1308}
1309
1310fn syntax_quote_collection(
1311    values: &[Form],
1312    vector: bool,
1313    env: &mut HashMap<String, Value>,
1314) -> Result<Value, String> {
1315    let mut output = Vec::new();
1316    for value in values {
1317        match value {
1318            Form::List(parts)
1319                if !parts.is_empty()
1320                    && matches!(&parts[0], Form::Symbol(name) if name == "unquote") =>
1321            {
1322                if parts.len() != 2 {
1323                    return Err("unquote expects one argument".into());
1324                }
1325                output.push(eval(&parts[1], env)?);
1326            }
1327            Form::List(parts)
1328                if !parts.is_empty()
1329                    && matches!(&parts[0], Form::Symbol(name) if name == "unquote-splicing") =>
1330            {
1331                if parts.len() != 2 {
1332                    return Err("unquote-splicing expects one argument".into());
1333                }
1334                output.extend(iterator_values(eval(&parts[1], env)?)?);
1335            }
1336            value => output.push(syntax_quote_value(value, env)?),
1337        }
1338    }
1339    if vector {
1340        vector_literal(output)
1341    } else {
1342        Ok(Value::List(output.into()))
1343    }
1344}
1345
1346fn syntax_quote_value(form: &Form, env: &mut HashMap<String, Value>) -> Result<Value, String> {
1347    match form {
1348        Form::Symbol(_) => literal_value(form),
1349        Form::List(values)
1350            if values.len() == 2
1351                && matches!(&values[0], Form::Symbol(name) if name == "unquote") =>
1352        {
1353            eval(&values[1], env)
1354        }
1355        Form::List(values) => syntax_quote_collection(values, false, env),
1356        Form::Vector(values) => syntax_quote_collection(values, true, env),
1357        Form::Map(values) => Ok(Value::Map(
1358            values
1359                .iter()
1360                .map(|(key, value)| {
1361                    Ok((
1362                        syntax_quote_value(key, env)?,
1363                        syntax_quote_value(value, env)?,
1364                    ))
1365                })
1366                .collect::<Result<Vec<_>, String>>()?
1367                .into_iter()
1368                .collect(),
1369        )),
1370        _ => literal_value(form),
1371    }
1372}