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

            
5
use super::super::context::CompileContext;
6
use super::super::emit::FunctionEmitter;
7
use super::super::expr::{compile_call, compile_string, eval_value, format_expr};
8
use super::binding::parse_lambda_params;
9

            
10
44
pub(super) fn compile_lambda_form(
11
44
    ctx: &mut CompileContext,
12
44
    emit: &mut FunctionEmitter,
13
44
    args: &[Expr],
14
44
) -> Result<()> {
15
44
    let result = lambda(args)?;
16
44
    compile_string(ctx, emit, &format_expr(&result));
17
44
    Ok(())
18
44
}
19

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

            
31
220
pub(super) fn compile_funcall(
32
220
    ctx: &mut CompileContext,
33
220
    emit: &mut FunctionEmitter,
34
220
    symbols: &mut SymbolTable,
35
220
    args: &[Expr],
36
220
) -> Result<()> {
37
220
    if args.is_empty() {
38
        return Err(Error::Compile(
39
            "funcall requires at least a function argument".to_string(),
40
        ));
41
220
    }
42
220
    compile_call(ctx, emit, symbols, args)
43
220
}
44

            
45
88
pub(super) fn compile_apply_form(
46
88
    ctx: &mut CompileContext,
47
88
    emit: &mut FunctionEmitter,
48
88
    symbols: &mut SymbolTable,
49
88
    args: &[Expr],
50
88
) -> Result<()> {
51
88
    if args.len() < 2 {
52
        return Err(Error::Compile(
53
            "apply requires at least a function and one argument".to_string(),
54
        ));
55
88
    }
56
88
    let leading = &args[..args.len() - 1];
57
88
    let last = eval_value(symbols, &args[args.len() - 1])?;
58

            
59
88
    let spread = match &last {
60
88
        Expr::Quote(inner) => match inner.as_ref() {
61
88
            Expr::List(elems) => elems.clone(),
62
            Expr::Nil => vec![],
63
            _ => {
64
                return Err(Error::Compile(
65
                    "apply: last argument must be a quoted list".to_string(),
66
                ));
67
            }
68
        },
69
        _ => {
70
            return Err(Error::Compile(
71
                "apply: last argument must be a quoted list".to_string(),
72
            ));
73
        }
74
    };
75

            
76
88
    let mut all_args: Vec<Expr> = Vec::with_capacity(leading.len() + spread.len());
77
88
    all_args.push(resolve_apply_designator(symbols, &leading[0])?);
78
88
    for arg in &leading[1..] {
79
88
        all_args.push(eval_value(symbols, arg)?);
80
    }
81
88
    all_args.extend(spread);
82
88
    compile_call(ctx, emit, symbols, &all_args)
83
88
}
84

            
85
440
pub(super) fn lambda(args: &[Expr]) -> Result<Expr> {
86
440
    if args.len() < 2 {
87
        return Err(Error::Compile(
88
            "LAMBDA requires a parameter list and body".to_string(),
89
        ));
90
440
    }
91
440
    let params = parse_lambda_params("LAMBDA", &args[0])?;
92
440
    let body = if args.len() == 2 {
93
440
        args[1].clone()
94
    } else {
95
        let mut forms = Vec::with_capacity(args.len());
96
        forms.push(Expr::Symbol("BEGIN".to_string()));
97
        forms.extend_from_slice(&args[1..]);
98
        Expr::List(forms)
99
    };
100
440
    Ok(Expr::Lambda(params, Box::new(body)))
101
440
}
102

            
103
44
pub(super) fn function_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
104
44
    if args.len() != 1 {
105
        return Err(Error::Arity {
106
            name: "FUNCTION".to_string(),
107
            expected: 1,
108
            actual: args.len(),
109
        });
110
44
    }
111
44
    match &args[0] {
112
44
        Expr::Symbol(name) => {
113
44
            let func = symbols
114
44
                .lookup(name)
115
44
                .and_then(|s| s.function().cloned())
116
44
                .ok_or_else(|| Error::Compile(format!("'{name}' has no function definition")))?;
117
44
            Ok(func)
118
        }
119
        Expr::List(elems) if matches!(elems.first(), Some(Expr::Symbol(s)) if s == "LAMBDA") => {
120
            lambda(&elems[1..])
121
        }
122
        _ => Err(Error::Compile(
123
            "FUNCTION: argument must be a symbol or lambda expression".to_string(),
124
        )),
125
    }
126
44
}
127

            
128
pub(super) fn funcall(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
129
    if args.is_empty() {
130
        return Err(Error::Compile(
131
            "funcall requires at least a function argument".to_string(),
132
        ));
133
    }
134
    tracing::debug!(args = args.len(), "compiling funcall");
135
    super::super::expr::call(symbols, args)
136
}
137

            
138
220
pub(super) fn apply(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
139
220
    if args.len() < 2 {
140
        return Err(Error::Compile(
141
            "apply requires at least a function and one argument".to_string(),
142
        ));
143
220
    }
144
220
    let leading = &args[..args.len() - 1];
145
220
    let last = eval_value(symbols, &args[args.len() - 1])?;
146

            
147
220
    let spread = match &last {
148
220
        Expr::Quote(inner) => match inner.as_ref() {
149
220
            Expr::List(elems) => elems.clone(),
150
            Expr::Nil => vec![],
151
            _ => {
152
                return Err(Error::Compile(
153
                    "apply: last argument must be a quoted list".to_string(),
154
                ));
155
            }
156
        },
157
        _ => {
158
            return Err(Error::Compile(
159
                "apply: last argument must be a quoted list".to_string(),
160
            ));
161
        }
162
    };
163

            
164
220
    let mut all_args: Vec<Expr> = Vec::with_capacity(leading.len() + spread.len());
165
220
    all_args.push(resolve_apply_designator(symbols, &leading[0])?);
166
660
    for arg in &leading[1..] {
167
660
        all_args.push(eval_value(symbols, arg)?);
168
    }
169
220
    all_args.extend(spread);
170
220
    super::super::expr::call(symbols, &all_args)
171
220
}
172

            
173
308
pub(super) fn resolve_apply_designator(symbols: &mut SymbolTable, arg: &Expr) -> Result<Expr> {
174
308
    match arg {
175
88
        Expr::Symbol(name) => {
176
88
            let sym = symbols
177
88
                .lookup(name)
178
88
                .ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
179
88
            if sym.value().is_some() {
180
                eval_value(symbols, arg)
181
            } else {
182
88
                Ok(Expr::Symbol(name.clone()))
183
            }
184
        }
185
220
        _ => eval_value(symbols, arg),
186
    }
187
308
}