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
6248
pub(in crate::compiler) fn expand_quasiquote(
17
6248
    symbols: &mut SymbolTable,
18
6248
    expr: &Expr,
19
6248
) -> Result<Expr> {
20
5183
    fn splice_elements(resolved: Expr) -> Result<Vec<Expr>> {
21
5183
        match resolved {
22
            Expr::Nil => Ok(Vec::new()),
23
            Expr::List(elems) => Ok(elems),
24
5183
            Expr::Quote(inner) => match *inner {
25
                Expr::Nil => Ok(Vec::new()),
26
5183
                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
5183
    }
38

            
39
34222
    fn quasiquote_data(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
40
34222
        match expr {
41
6035
            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
11147
            Expr::List(elems) => {
46
11147
                let mut resolved: Vec<Expr> = Vec::new();
47
33157
                for elem in elems {
48
33157
                    match elem {
49
5183
                        Expr::UnquoteSplicing(inner) => {
50
5183
                            let spliced = resolve_arg(symbols, inner)?;
51
5183
                            resolved.extend(splice_elements(spliced)?);
52
                        }
53
27974
                        _ => resolved.push(quasiquote_data(symbols, elem)?),
54
                    }
55
                }
56
11147
                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
17040
            _ => Ok(expr.clone()),
64
        }
65
34222
    }
66

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