1
//! Quasiquote / unquote / unquote-splicing expansion.
2
//!
3
//! `expand_quasiquote` walks an expression in quasiquote context,
4
//! resolving each `,form` (Unquote) against the symbol table and
5
//! splicing each `,@form` (UnquoteSplicing) into its parent list.
6
//! The result wraps the fully resolved tree in a single outer
7
//! `Quote` so downstream callers see a constant-foldable value.
8

            
9
use crate::ast::Expr;
10
use crate::error::{Error, Result};
11
use crate::runtime::SymbolTable;
12

            
13
use super::eval::resolve_arg;
14
use super::format::format_expr;
15

            
16
6014
pub(in crate::compiler) fn expand_quasiquote(
17
6014
    symbols: &mut SymbolTable,
18
6014
    expr: &Expr,
19
6014
) -> Result<Expr> {
20
4994
    fn splice_elements(resolved: Expr) -> Result<Vec<Expr>> {
21
4994
        match resolved {
22
            Expr::Nil => Ok(Vec::new()),
23
            Expr::List(elems) => Ok(elems),
24
4994
            Expr::Quote(inner) => match *inner {
25
                Expr::Nil => Ok(Vec::new()),
26
4994
                Expr::List(elems) => Ok(elems),
27
                other => Err(Error::Compile(format!(
28
                    "unquote-splicing requires a list, got {}",
29
                    format_expr(&other)
30
                ))),
31
            },
32
            other => Err(Error::Compile(format!(
33
                "unquote-splicing requires a list, got {}",
34
                format_expr(&other)
35
            ))),
36
        }
37
4994
    }
38

            
39
32956
    fn quasiquote_data(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
40
32956
        match expr {
41
5810
            Expr::Unquote(inner) => resolve_arg(symbols, inner),
42
            Expr::UnquoteSplicing(_) => Err(Error::Compile(
43
                "unquote-splicing outside of list in quasiquote".to_string(),
44
            )),
45
10736
            Expr::List(elems) => {
46
10736
                let mut resolved: Vec<Expr> = Vec::new();
47
31936
                for elem in elems {
48
31936
                    match elem {
49
4994
                        Expr::UnquoteSplicing(inner) => {
50
4994
                            let spliced = resolve_arg(symbols, inner)?;
51
4994
                            resolved.extend(splice_elements(spliced)?);
52
                        }
53
26942
                        _ => resolved.push(quasiquote_data(symbols, elem)?),
54
                    }
55
                }
56
10736
                Ok(Expr::List(resolved))
57
            }
58
            Expr::Cons(car, cdr) => {
59
                let car = quasiquote_data(symbols, car)?;
60
                let cdr = quasiquote_data(symbols, cdr)?;
61
                Ok(Expr::cons(car, cdr))
62
            }
63
16410
            _ => Ok(expr.clone()),
64
        }
65
32956
    }
66

            
67
6014
    Ok(Expr::Quote(Box::new(quasiquote_data(symbols, expr)?)))
68
6014
}