1
//! `DESCRIBE` and `PP` — symbol-introspection + pretty-print forms
2
//! that both produce a `String` summary.
3

            
4
use crate::ast::{Expr, WasmType};
5
use crate::compiler::context::CompileContext;
6
use crate::compiler::emit::FunctionEmitter;
7
use crate::compiler::expr::{compile_expr, eval_value, format_expr};
8
use crate::error::{Error, Result};
9
use crate::runtime::{SymbolKind, SymbolTable};
10

            
11
use super::compile_static_result_for_stack;
12

            
13
710
pub(super) fn describe(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
14
710
    if args.len() != 1 {
15
71
        return Err(Error::Arity {
16
71
            name: "DESCRIBE".to_string(),
17
71
            expected: 1,
18
71
            actual: args.len(),
19
71
        });
20
639
    }
21
639
    let name = match &args[0] {
22
213
        Expr::Symbol(s) => s.clone(),
23
426
        Expr::Quote(inner) => match inner.as_ref() {
24
426
            Expr::Symbol(s) => s.clone(),
25
            _ => {
26
                return Err(Error::Compile(
27
                    "DESCRIBE: argument must be a symbol".to_string(),
28
                ));
29
            }
30
        },
31
        _ => {
32
            return Err(Error::Compile(
33
                "DESCRIBE: argument must be a symbol".to_string(),
34
            ));
35
        }
36
    };
37
639
    let sym = symbols
38
639
        .lookup(&name)
39
639
        .ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
40

            
41
568
    let mut lines = Vec::new();
42
568
    let kind_str = match sym.kind() {
43
71
        SymbolKind::Macro => "a macro",
44
497
        _ if sym.function().is_some() => "a function",
45
142
        SymbolKind::Variable => "a variable",
46
213
        SymbolKind::Operator => "an operator",
47
        SymbolKind::Native => "a native function",
48
        SymbolKind::SpecialForm => "a special form",
49
        SymbolKind::Function => "a function",
50
    };
51
568
    lines.push(format!("{name} is {kind_str}"));
52
568
    if let Some(func) = sym.function() {
53
213
        lines.push(format!("  Lambda: {}", format_expr(func)));
54
355
    }
55
568
    if let Some(val) = sym.value() {
56
142
        let type_name = match val {
57
            Expr::Nil | Expr::Bool(false) => "Nil",
58
            Expr::Bool(true) => "Bool",
59
142
            Expr::Number(_) => "Number",
60
            Expr::String(_) => "String",
61
            Expr::Symbol(_) => "Symbol",
62
            Expr::Lambda(_, _) => "Lambda",
63
            _ => "Compound",
64
        };
65
142
        lines.push(format!("  Value: {} ({})", format_expr(val), type_name));
66
426
    }
67
568
    if let Some(doc) = sym.doc() {
68
142
        lines.push(format!("  Documentation: \"{doc}\""));
69
426
    }
70
568
    Ok(Expr::String(lines.join("\n")))
71
710
}
72

            
73
568
pub(super) fn compile_describe(
74
568
    ctx: &mut CompileContext,
75
568
    emit: &mut FunctionEmitter,
76
568
    symbols: &mut SymbolTable,
77
568
    args: &[Expr],
78
568
) -> Result<()> {
79
568
    let result = describe(symbols, args)?;
80
426
    compile_expr(ctx, emit, symbols, &result)
81
568
}
82

            
83
/// `(pp v)` → a String containing the pretty-printed textual form
84
/// of `v`. The compile-side path constant-folds via [`format_expr`],
85
/// so the call returns an `Expr::String` directly. Runtime values
86
/// (`WasmRuntime` / `WasmLocal`) print as their type descriptor —
87
/// a runtime-side printer that walks the wasm value lands when the
88
/// host needs it; today the emacs/nms clients format the
89
/// `nomi-eval` return slot themselves.
90
1141
pub(super) fn pp(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
91
1141
    if args.len() != 1 {
92
144
        return Err(Error::Arity {
93
144
            name: "PP".to_string(),
94
144
            expected: 1,
95
144
            actual: args.len(),
96
144
        });
97
997
    }
98
997
    let resolved = eval_value(symbols, &args[0])?;
99
997
    Ok(Expr::String(format_expr(&resolved)))
100
1141
}
101

            
102
497
pub(super) fn compile_pp(
103
497
    ctx: &mut CompileContext,
104
497
    emit: &mut FunctionEmitter,
105
497
    symbols: &mut SymbolTable,
106
497
    args: &[Expr],
107
497
) -> Result<()> {
108
497
    let result = pp(symbols, args)?;
109
355
    compile_expr(ctx, emit, symbols, &result)
110
497
}
111

            
112
142
pub(super) fn compile_describe_for_stack(
113
142
    ctx: &mut CompileContext,
114
142
    emit: &mut FunctionEmitter,
115
142
    symbols: &mut SymbolTable,
116
142
    args: &[Expr],
117
142
) -> Result<WasmType> {
118
142
    let result = describe(symbols, args)?;
119
142
    compile_static_result_for_stack(ctx, emit, symbols, &result)
120
142
}
121

            
122
142
pub(super) fn compile_pp_for_stack(
123
142
    ctx: &mut CompileContext,
124
142
    emit: &mut FunctionEmitter,
125
142
    symbols: &mut SymbolTable,
126
142
    args: &[Expr],
127
142
) -> Result<WasmType> {
128
142
    let result = pp(symbols, args)?;
129
142
    compile_static_result_for_stack(ctx, emit, symbols, &result)
130
142
}