1
use crate::ast::Expr;
2
use crate::error::{Error, Result};
3
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
4

            
5
use super::super::context::CompileContext;
6
use super::super::emit::FunctionEmitter;
7
use super::super::expr::{compile_expr, resolve_arg};
8
use super::binding::parse_lambda_params;
9

            
10
8888
pub(super) fn compile_defmacro_form(
11
8888
    ctx: &mut CompileContext,
12
8888
    emit: &mut FunctionEmitter,
13
8888
    symbols: &mut SymbolTable,
14
8888
    args: &[Expr],
15
8888
) -> Result<()> {
16
8888
    let result = defmacro(symbols, args)?;
17
8888
    compile_expr(ctx, emit, symbols, &result)
18
8888
}
19

            
20
88
pub(super) fn compile_macroexpand_1_form(
21
88
    ctx: &mut CompileContext,
22
88
    emit: &mut FunctionEmitter,
23
88
    symbols: &mut SymbolTable,
24
88
    args: &[Expr],
25
88
) -> Result<()> {
26
88
    let result = macroexpand_1(symbols, args)?;
27
88
    compile_expr(ctx, emit, symbols, &result)
28
88
}
29

            
30
44
pub(super) fn compile_macroexpand_form(
31
44
    ctx: &mut CompileContext,
32
44
    emit: &mut FunctionEmitter,
33
44
    symbols: &mut SymbolTable,
34
44
    args: &[Expr],
35
44
) -> Result<()> {
36
44
    let result = macroexpand(symbols, args)?;
37
44
    compile_expr(ctx, emit, symbols, &result)
38
44
}
39

            
40
18392
pub(super) fn defmacro(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
41
18392
    if args.len() < 3 {
42
        return Err(Error::Compile(
43
            "DEFMACRO requires a name, parameter list, and body".to_string(),
44
        ));
45
18392
    }
46
18392
    let name = match &args[0] {
47
18392
        Expr::Symbol(s) => s.clone(),
48
        other => {
49
            return Err(Error::Compile(format!(
50
                "DEFMACRO: expected symbol name, got {other:?}"
51
            )));
52
        }
53
    };
54
18392
    let params = parse_lambda_params("DEFMACRO", &args[1])?;
55
18392
    if !params.aux.is_empty() {
56
        return Err(Error::Compile(
57
            "DEFMACRO: &aux is not yet supported".to_string(),
58
        ));
59
18392
    }
60
18392
    let (doc, body_idx) = match args.get(2) {
61
        Some(Expr::String(s)) if args.len() > 3 => (Some(s.clone()), 3),
62
18392
        _ => (None, 2),
63
    };
64
18392
    if body_idx >= args.len() {
65
        return Err(Error::Compile("DEFMACRO: missing body".to_string()));
66
18392
    }
67
18392
    let body = if args.len() == body_idx + 1 {
68
18392
        args[body_idx].clone()
69
    } else {
70
        let mut forms = Vec::with_capacity(args.len() - body_idx + 1);
71
        forms.push(Expr::Symbol("BEGIN".to_string()));
72
        forms.extend_from_slice(&args[body_idx..]);
73
        Expr::List(forms)
74
    };
75
18392
    let lambda = Expr::Lambda(params, Box::new(body));
76

            
77
18392
    let mut sym = Symbol::new(&name, SymbolKind::Macro).with_function(lambda);
78
18392
    if let Some(d) = doc {
79
        sym = sym.with_doc(d);
80
18392
    }
81
18392
    symbols.define(sym);
82
18392
    Ok(Expr::Quote(Box::new(Expr::Symbol(name))))
83
18392
}
84

            
85
132
pub(super) fn macroexpand_1(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
86
132
    if args.len() != 1 {
87
        return Err(Error::Arity {
88
            name: "MACROEXPAND-1".to_string(),
89
            expected: 1,
90
            actual: args.len(),
91
        });
92
132
    }
93
132
    let form = resolve_arg(symbols, &args[0])?;
94
132
    macroexpand_1_impl(symbols, &form)
95
132
}
96

            
97
44
pub(super) fn macroexpand(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
98
44
    if args.len() != 1 {
99
        return Err(Error::Arity {
100
            name: "MACROEXPAND".to_string(),
101
            expected: 1,
102
            actual: args.len(),
103
        });
104
44
    }
105
44
    let mut form = resolve_arg(symbols, &args[0])?;
106
    loop {
107
132
        let expanded = macroexpand_1_impl(symbols, &form)?;
108
132
        if expanded == form {
109
44
            break;
110
88
        }
111
88
        form = expanded;
112
    }
113
44
    Ok(form)
114
44
}
115

            
116
264
fn macroexpand_1_impl(symbols: &mut SymbolTable, form: &Expr) -> Result<Expr> {
117
264
    match form {
118
264
        Expr::Quote(inner) => match inner.as_ref() {
119
264
            Expr::List(elems) if !elems.is_empty() => {
120
264
                if let Some(name) = macro_head_name(&elems[0])
121
264
                    && let Some(sym) = symbols.lookup(name)
122
264
                    && sym.kind() == SymbolKind::Macro
123
176
                    && let Some(Expr::Lambda(params, body)) = sym.function().cloned()
124
                {
125
176
                    return super::super::expr::expand_macro(symbols, &params, &body, &elems[1..]);
126
88
                }
127
88
                Ok(form.clone())
128
            }
129
            _ => Ok(form.clone()),
130
        },
131
        _ => Ok(form.clone()),
132
    }
133
264
}
134

            
135
264
fn macro_head_name(expr: &Expr) -> Option<&str> {
136
264
    match expr {
137
176
        Expr::Symbol(name) => Some(name),
138
88
        Expr::Quote(inner) => match inner.as_ref() {
139
88
            Expr::Symbol(name) => Some(name),
140
            _ => None,
141
        },
142
        _ => None,
143
    }
144
264
}