Lines
84.54 %
Functions
22.86 %
Branches
100 %
//! `DESCRIBE` and `PP` — symbol-introspection + pretty-print forms
//! that both produce a `String` summary.
use crate::ast::{Expr, WasmType};
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::expr::{compile_expr, eval_value, format_expr};
use crate::error::{Error, Result};
use crate::runtime::{SymbolKind, SymbolTable};
use super::compile_static_result_for_stack;
pub(super) fn describe(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 1 {
return Err(Error::Arity {
name: "DESCRIBE".to_string(),
expected: 1,
actual: args.len(),
});
}
let name = match &args[0] {
Expr::Symbol(s) => s.clone(),
Expr::Quote(inner) => match inner.as_ref() {
_ => {
return Err(Error::Compile(
"DESCRIBE: argument must be a symbol".to_string(),
));
},
};
let sym = symbols
.lookup(&name)
.ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
let mut lines = Vec::new();
let kind_str = match sym.kind() {
SymbolKind::Macro => "a macro",
_ if sym.function().is_some() => "a function",
SymbolKind::Variable => "a variable",
SymbolKind::Operator => "an operator",
SymbolKind::Native => "a native function",
SymbolKind::SpecialForm => "a special form",
SymbolKind::Function => "a function",
lines.push(format!("{name} is {kind_str}"));
if let Some(func) = sym.function() {
lines.push(format!(" Lambda: {}", format_expr(func)));
if let Some(val) = sym.value() {
let type_name = match val {
Expr::Nil | Expr::Bool(false) => "Nil",
Expr::Bool(true) => "Bool",
Expr::Number(_) => "Number",
Expr::String(_) => "String",
Expr::Symbol(_) => "Symbol",
Expr::Lambda(_, _) => "Lambda",
_ => "Compound",
lines.push(format!(" Value: {} ({})", format_expr(val), type_name));
if let Some(doc) = sym.doc() {
lines.push(format!(" Documentation: \"{doc}\""));
Ok(Expr::String(lines.join("\n")))
pub(super) fn compile_describe(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
let result = describe(symbols, args)?;
compile_expr(ctx, emit, symbols, &result)
/// `(pp v)` → a String containing the pretty-printed textual form
/// of `v`. The compile-side path constant-folds via [`format_expr`],
/// so the call returns an `Expr::String` directly. Runtime values
/// (`WasmRuntime` / `WasmLocal`) print as their type descriptor —
/// a runtime-side printer that walks the wasm value lands when the
/// host needs it; today the emacs/nms clients format the
/// `nomi-eval` return slot themselves.
pub(super) fn pp(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
name: "PP".to_string(),
let resolved = eval_value(symbols, &args[0])?;
Ok(Expr::String(format_expr(&resolved)))
pub(super) fn compile_pp(
let result = pp(symbols, args)?;
pub(super) fn compile_describe_for_stack(
) -> Result<WasmType> {
compile_static_result_for_stack(ctx, emit, symbols, &result)
pub(super) fn compile_pp_for_stack(