1
use tracing::debug;
2

            
3
use crate::ast::Expr;
4
use crate::error::{Error, Result};
5
use crate::runtime::{SymbolKind, SymbolTable};
6

            
7
use super::super::context::CompileContext;
8
use super::super::emit::FunctionEmitter;
9
use super::super::expr::{compile_expr, eval_value, format_expr, resolve_arg};
10

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

            
21
220
pub(super) fn compile_eval_form(
22
220
    ctx: &mut CompileContext,
23
220
    emit: &mut FunctionEmitter,
24
220
    symbols: &mut SymbolTable,
25
220
    args: &[Expr],
26
220
) -> Result<()> {
27
220
    if args.len() != 1 {
28
        return Err(Error::Arity {
29
            name: "eval".to_string(),
30
            expected: 1,
31
            actual: args.len(),
32
        });
33
220
    }
34
220
    let resolved = resolve_arg(symbols, &args[0])?;
35
220
    match &resolved {
36
88
        Expr::Quote(inner) => compile_expr(ctx, emit, symbols, inner),
37
132
        _ => compile_expr(ctx, emit, symbols, &resolved),
38
    }
39
220
}
40

            
41
132
pub(super) fn compile_describe(
42
132
    ctx: &mut CompileContext,
43
132
    emit: &mut FunctionEmitter,
44
132
    symbols: &mut SymbolTable,
45
132
    args: &[Expr],
46
132
) -> Result<()> {
47
132
    let result = describe(symbols, args)?;
48
132
    compile_expr(ctx, emit, symbols, &result)
49
132
}
50

            
51
44
pub(super) fn compile_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
52
44
    if args.len() != 1 {
53
        return Err(Error::Arity {
54
            name: "compile".to_string(),
55
            expected: 1,
56
            actual: args.len(),
57
        });
58
44
    }
59
44
    let name = match &args[0] {
60
44
        Expr::Quote(inner) => match inner.as_ref() {
61
44
            Expr::Symbol(s) => s,
62
            _ => {
63
                return Err(Error::Compile(
64
                    "compile: argument must be a quoted symbol".to_string(),
65
                ));
66
            }
67
        },
68
        Expr::Symbol(_) => {
69
            let resolved = resolve_arg(symbols, &args[0])?;
70
            match &resolved {
71
                Expr::Quote(inner) => match inner.as_ref() {
72
                    Expr::Symbol(s) => {
73
                        return compile_form_with_name(symbols, s);
74
                    }
75
                    _ => {
76
                        return Err(Error::Compile(
77
                            "compile: argument must be a quoted symbol".to_string(),
78
                        ));
79
                    }
80
                },
81
                _ => {
82
                    return Err(Error::Compile(
83
                        "compile: argument must be a quoted symbol".to_string(),
84
                    ));
85
                }
86
            }
87
        }
88
        _ => {
89
            return Err(Error::Compile(
90
                "compile: argument must be a quoted symbol".to_string(),
91
            ));
92
        }
93
    };
94
44
    compile_form_with_name(symbols, name)
95
44
}
96

            
97
44
pub(super) fn compile_form_with_name(symbols: &mut SymbolTable, name: &str) -> Result<Expr> {
98
44
    debug!(function = %name, "compiling compile");
99
44
    let sym = symbols
100
44
        .lookup(name)
101
44
        .ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
102
44
    if sym.function().is_none() {
103
        return Err(Error::Compile(format!(
104
            "compile: '{name}' is not a function"
105
        )));
106
44
    }
107
44
    Ok(Expr::Quote(Box::new(Expr::Symbol(name.to_string()))))
108
44
}
109

            
110
88
pub(super) fn eval_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
111
88
    if args.len() != 1 {
112
        return Err(Error::Arity {
113
            name: "eval".to_string(),
114
            expected: 1,
115
            actual: args.len(),
116
        });
117
88
    }
118
88
    let resolved = eval_value(symbols, &args[0])?;
119
88
    debug!(expr = ?resolved, "compiling eval");
120
88
    match &resolved {
121
88
        Expr::Quote(inner) => eval_value(symbols, inner),
122
        _ => eval_value(symbols, &resolved),
123
    }
124
88
}
125

            
126
132
pub(super) fn describe(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
127
132
    if args.len() != 1 {
128
        return Err(Error::Arity {
129
            name: "DESCRIBE".to_string(),
130
            expected: 1,
131
            actual: args.len(),
132
        });
133
132
    }
134
132
    let name = match &args[0] {
135
132
        Expr::Symbol(s) => s.clone(),
136
        Expr::Quote(inner) => match inner.as_ref() {
137
            Expr::Symbol(s) => s.clone(),
138
            _ => {
139
                return Err(Error::Compile(
140
                    "DESCRIBE: argument must be a symbol".to_string(),
141
                ));
142
            }
143
        },
144
        _ => {
145
            return Err(Error::Compile(
146
                "DESCRIBE: argument must be a symbol".to_string(),
147
            ));
148
        }
149
    };
150
132
    let sym = symbols
151
132
        .lookup(&name)
152
132
        .ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
153

            
154
132
    let mut lines = Vec::new();
155
132
    let kind_str = match sym.kind() {
156
44
        SymbolKind::Macro => "a macro",
157
88
        _ if sym.function().is_some() => "a function",
158
44
        SymbolKind::Variable => "a variable",
159
        SymbolKind::Operator => "an operator",
160
        SymbolKind::Native => "a native function",
161
        SymbolKind::SpecialForm => "a special form",
162
        SymbolKind::Function => "a function",
163
    };
164
132
    lines.push(format!("{name} is {kind_str}"));
165
132
    if let Some(func) = sym.function() {
166
88
        lines.push(format!("  Lambda: {}", format_expr(func)));
167
88
    }
168
132
    if let Some(val) = sym.value() {
169
44
        let type_name = match val {
170
            Expr::Nil | Expr::Bool(false) => "Nil",
171
            Expr::Bool(true) => "Bool",
172
44
            Expr::Number(_) => "Number",
173
            Expr::String(_) => "String",
174
            Expr::Symbol(_) => "Symbol",
175
            Expr::Lambda(_, _) => "Lambda",
176
            _ => "Compound",
177
        };
178
44
        lines.push(format!("  Value: {} ({})", format_expr(val), type_name));
179
88
    }
180
132
    if let Some(doc) = sym.doc() {
181
88
        lines.push(format!("  Documentation: \"{doc}\""));
182
88
    }
183
132
    Ok(Expr::String(lines.join("\n")))
184
132
}