Skip to main content

dazzle_core/scheme/
evaluator.rs

1//! Scheme evaluator (eval loop)
2//!
3//! Ported from OpenJade's `Interpreter.cxx` (~2,000 lines).
4//!
5//! ## Core Responsibilities
6//!
7//! 1. **Evaluate expressions** - Transform Values into results
8//! 2. **Special forms** - Handle if, let, define, lambda, quote, etc.
9//! 3. **Function application** - Call procedures with arguments
10//! 4. **Tail call optimization** - Prevent stack overflow in recursive functions
11//!
12//! ## OpenJade Correspondence
13//!
14//! | Dazzle          | OpenJade                  | Purpose                    |
15//! |-----------------|---------------------------|----------------------------|
16//! | `Evaluator`     | `Interpreter`             | Main evaluator state       |
17//! | `eval()`        | `Interpreter::eval()`     | Core eval loop             |
18//! | `apply()`       | `Interpreter::apply()`    | Function application       |
19//! | `eval_special()`| `Interpreter::evalXXX()`  | Special form handlers      |
20//!
21//! ## Evaluation Rules (R4RS)
22//!
23//! - **Self-evaluating**: Numbers, strings, booleans, characters → return as-is
24//! - **Symbols**: Look up in environment
25//! - **Lists**: First element determines behavior:
26//!   - Special form keyword → handle specially
27//!   - Otherwise → evaluate all elements, apply first to rest
28
29use crate::scheme::environment::Environment;
30use crate::scheme::parser::Position;
31use crate::scheme::value::{Procedure, Value};
32use crate::scheme::arena::{Arena, ValueId, ValueData};
33use crate::scheme::vm::VM;
34use crate::scheme::compiler::Compiler;
35use crate::scheme::instruction::Instruction;
36use crate::scheme::bridge::{value_to_arena, arena_to_value};
37use crate::grove::{Grove, Node};
38use crate::fot::FotBuilder;
39use gc::Gc;
40use std::rc::Rc;
41use std::cell::RefCell;
42use std::collections::HashMap;
43
44// Thread-local evaluator context for primitives
45//
46// Similar to OpenJade's approach, we use thread-local storage to give
47// primitives access to the evaluator state (current node, grove, etc.)
48// without changing all primitive signatures.
49//
50// This is safe because:
51// 1. Scheme evaluation is single-threaded in our implementation
52// 2. The context is set/cleared around each eval call
53// 3. Primitives only run during evaluation
54thread_local! {
55    static EVALUATOR_CONTEXT: RefCell<Option<EvaluatorContext>> = RefCell::new(None);
56}
57
58/// Context available to primitives during evaluation
59#[derive(Clone)]
60pub struct EvaluatorContext {
61    pub grove: Option<Rc<dyn Grove>>,
62    pub current_node: Option<Rc<Box<dyn Node>>>,
63    pub backend: Option<Rc<RefCell<dyn FotBuilder>>>,
64}
65
66/// Get the current evaluator context (for use in primitives)
67pub fn get_evaluator_context() -> Option<EvaluatorContext> {
68    EVALUATOR_CONTEXT.with(|ctx| ctx.borrow().clone())
69}
70
71/// Check if evaluator context is currently set
72fn has_evaluator_context() -> bool {
73    EVALUATOR_CONTEXT.with(|ctx| ctx.borrow().is_some())
74}
75
76/// Set the evaluator context (called by evaluator before eval)
77fn set_evaluator_context(ctx: EvaluatorContext) {
78    EVALUATOR_CONTEXT.with(|c| *c.borrow_mut() = Some(ctx));
79}
80
81/// Clear the evaluator context (called by evaluator after eval)
82fn clear_evaluator_context() {
83    EVALUATOR_CONTEXT.with(|c| *c.borrow_mut() = None);
84}
85
86// =============================================================================
87// Call Stack (for error reporting)
88// =============================================================================
89
90use crate::scheme::value::SourceInfo;
91
92/// A call stack frame
93///
94/// Tracks function calls for error reporting with source locations.
95#[derive(Debug, Clone)]
96pub struct CallFrame {
97    /// Function name (or "<lambda>" for anonymous functions)
98    pub function_name: String,
99    /// Source location (file:line:column)
100    pub source: Option<SourceInfo>,
101}
102
103impl CallFrame {
104    pub fn new(function_name: String, source: Option<SourceInfo>) -> Self {
105        CallFrame {
106            function_name,
107            source,
108        }
109    }
110}
111
112// =============================================================================
113// Evaluation Error
114// =============================================================================
115
116/// Evaluation error with call stack
117#[derive(Debug, Clone)]
118pub struct EvalError {
119    pub message: String,
120    pub call_stack: Vec<CallFrame>,
121}
122
123impl EvalError {
124    pub fn new(message: String) -> Self {
125        EvalError {
126            message,
127            call_stack: Vec::new(),
128        }
129    }
130
131    /// Create error with call stack
132    pub fn with_stack(message: String, call_stack: Vec<CallFrame>) -> Self {
133        EvalError {
134            message,
135            call_stack,
136        }
137    }
138}
139
140impl std::fmt::Display for EvalError {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        // OpenJade-style format: file:line:col:E: message
143        write!(f, "{}", self.message)?;
144
145        // Show call stack in reverse order (innermost to outermost, matching OpenJade)
146        for (i, frame) in self.call_stack.iter().rev().enumerate() {
147            if let Some(ref source) = frame.source {
148                // First frame gets a newline before it, rest don't
149                if i == 0 {
150                    writeln!(f, "\n{}:{}:{}:I: called from here",
151                             source.file, source.pos.line, source.pos.column)?;
152                } else {
153                    writeln!(f, "{}:{}:{}:I: called from here",
154                             source.file, source.pos.line, source.pos.column)?;
155                }
156            } else {
157                if i == 0 {
158                    writeln!(f, "\n{}:I: called from here", frame.function_name)?;
159                } else {
160                    writeln!(f, "{}:I: called from here", frame.function_name)?;
161                }
162            }
163        }
164
165        Ok(())
166    }
167}
168
169impl std::error::Error for EvalError {}
170
171pub type EvalResult = Result<Value, EvalError>;
172
173// =============================================================================
174// DSSSL Processing Mode (OpenJade ProcessingMode.h/ProcessingMode.cxx)
175// =============================================================================
176
177/// Construction rule for DSSSL processing
178///
179/// Corresponds to OpenJade's `ElementRule` + `Rule` + `Action`.
180/// Stores the pattern (element name) and action (expression to evaluate).
181#[derive(Clone)]
182pub struct ConstructionRule {
183    /// Element name pattern (GI) - the actual element being matched
184    pub element_name: String,
185
186    /// Context pattern - parent element names (empty for simple patterns)
187    /// For `(part title)`, this would be vec!["part"]
188    /// For simple `title`, this would be empty
189    pub context: Vec<String>,
190
191    /// Construction expression (returns sosofo when evaluated)
192    pub expr: Value,
193
194    /// Source position where this rule was defined (for error reporting)
195    pub source_file: Option<String>,
196    pub source_pos: Option<Position>,
197
198    /// Cached bytecode instructions (OpenJade's InsnPtr optimization)
199    ///
200    /// When VM is enabled, we compile the expression once and cache the instructions.
201    /// This is the key optimization that makes OpenJade fast: compile once, run many times.
202    ///
203    /// Tuple: (instructions, start_ip)
204    pub cached_instructions: RefCell<Option<(Vec<Instruction>, usize)>>,
205}
206
207/// Processing mode containing construction rules
208///
209/// Corresponds to OpenJade's `ProcessingMode` class.
210/// Stores all element construction rules defined in the template.
211pub struct ProcessingMode {
212    /// Construction rules indexed by element name for O(1) lookup
213    /// HashMap<element_name, Vec<rules_for_that_element>>
214    /// This avoids linear search through all rules for every element.
215    pub rules: std::collections::HashMap<String, Vec<ConstructionRule>>,
216
217    /// Default construction rule (fallback when no specific rule matches)
218    pub default_rule: Option<Value>,
219}
220
221impl ProcessingMode {
222    /// Create a new empty processing mode
223    pub fn new() -> Self {
224        ProcessingMode {
225            rules: std::collections::HashMap::new(),
226            default_rule: None,
227        }
228    }
229
230    /// Add a construction rule
231    pub fn add_rule(&mut self, element_name: String, context: Vec<String>, expr: Value, source_file: Option<String>, source_pos: Option<Position>) {
232        // Insert rule into HashMap, grouped by element name
233        self.rules
234            .entry(element_name.clone())
235            .or_insert_with(Vec::new)
236            .push(ConstructionRule {
237                element_name,
238                context,
239                expr,
240                source_file,
241                source_pos,
242                cached_instructions: RefCell::new(None),
243            });
244    }
245
246    /// Add a default construction rule
247    pub fn add_default_rule(&mut self, expr: Value) {
248        self.default_rule = Some(expr);
249    }
250
251    /// Find matching rule for an element
252    ///
253    /// Corresponds to OpenJade's `ProcessingMode::findMatch()`.
254    /// Returns the first rule matching the given element name and context.
255    ///
256    /// OPTIMIZATION: Uses HashMap lookup by element name (O(1)) instead of
257    /// linear search through all rules (O(N)). Critical for large documents!
258    pub fn find_match(&self, gi: &str, node: &dyn crate::grove::Node) -> Option<&ConstructionRule> {
259        // Fast path: lookup rules for this specific element name
260        let rules_for_element = self.rules.get(gi)?;
261
262        // Now search only among rules for this element (typically 1-5 rules)
263        rules_for_element.iter().find(|rule| {
264            // Element name already matches (we looked it up by GI)
265
266            // If rule has no context, it matches any parent
267            if rule.context.is_empty() {
268                return true;
269            }
270
271            // Check if parent chain matches the context
272            let mut current = node.parent();
273            for expected_parent in rule.context.iter().rev() {
274                match current {
275                    Some(ref parent_node) => {
276                        if let Some(parent_gi) = parent_node.gi() {
277                            if parent_gi != *expected_parent {
278                                return false;
279                            }
280                            current = parent_node.parent();
281                        } else {
282                            return false;
283                        }
284                    }
285                    None => return false,
286                }
287            }
288
289            true
290        })
291    }
292}
293
294/// Manager for multiple processing modes
295///
296/// DSSSL supports multiple named modes for different processing contexts.
297/// The unnamed mode (empty string key) is the initial/default mode.
298pub struct ModeManager {
299    /// Map of mode name to processing mode
300    modes: std::collections::HashMap<String, ProcessingMode>,
301}
302
303impl ModeManager {
304    /// Create a new mode manager with an empty default mode
305    pub fn new() -> Self {
306        let mut modes = std::collections::HashMap::new();
307        modes.insert(String::new(), ProcessingMode::new());
308        ModeManager { modes }
309    }
310
311    /// Get or create a mode by name
312    pub fn get_or_create_mode(&mut self, name: &str) -> &mut ProcessingMode {
313        self.modes.entry(name.to_string()).or_insert_with(ProcessingMode::new)
314    }
315
316    /// Get a mode by name (read-only)
317    pub fn get_mode(&self, name: &str) -> Option<&ProcessingMode> {
318        self.modes.get(name)
319    }
320
321    /// Get the default (unnamed) mode
322    pub fn default_mode(&mut self) -> &mut ProcessingMode {
323        self.get_or_create_mode("")
324    }
325
326    /// Get the default (unnamed) mode (read-only)
327    pub fn get_default_mode(&self) -> Option<&ProcessingMode> {
328        self.get_mode("")
329    }
330}
331
332// =============================================================================
333// Evaluator
334// =============================================================================
335
336/// Scheme evaluator
337///
338/// Corresponds to OpenJade's `Interpreter` class.
339///
340/// ## Usage
341///
342/// ```ignore
343/// let mut evaluator = Evaluator::new();
344/// let result = evaluator.eval(expr, env)?;
345/// ```
346pub struct Evaluator {
347    /// Arena for arena-based values (Phase 2 migration)
348    ///
349    /// Used for hot primitives (car, cdr, cons, null?, equal?) to eliminate Gc overhead.
350    /// During Phase 2, this works in dual-mode: Values are converted to ValueIds for hot
351    /// primitives, then converted back.
352    arena: Arena,
353
354    /// Manager for multiple processing modes
355    ///
356    /// Corresponds to OpenJade's mode management.
357    /// DSSSL supports multiple named modes for different processing contexts.
358    mode_manager: ModeManager,
359
360    /// Current mode name for rule definition
361    ///
362    /// When defining rules with `(element ...)` or `(default ...)`, they go into this mode.
363    /// Empty string means the unnamed/default mode.
364    /// Set by `(mode name ...)` special form.
365    current_mode: String,
366
367    /// Current processing mode for rule lookup
368    ///
369    /// When processing nodes with `process-children`, rules are looked up in this mode.
370    /// Empty string means the unnamed/default mode.
371    /// Set by `(with-mode name ...)` special form.
372    current_processing_mode: String,
373
374    /// Backend for output generation (FotBuilder)
375    ///
376    /// This is used by the `make` special form to write flow objects to output.
377    /// Wrapped in Rc<RefCell<>> to allow shared mutable access.
378    backend: Option<Rc<RefCell<dyn FotBuilder>>>,
379
380    /// Call stack for error reporting
381    ///
382    /// Tracks function calls with their source locations.
383    /// Used to generate helpful error messages with clickable file paths.
384    call_stack: Vec<CallFrame>,
385
386    /// Current source file being evaluated (for error reporting)
387    ///
388    /// Set when loading templates, used to provide context in errors.
389    current_source_file: Option<String>,
390
391    /// Current position in source (for error reporting)
392    ///
393    /// Tracks line and column for the expression being evaluated.
394    current_position: Option<Position>,
395
396    /// Line mappings for translating output lines to source files
397    ///
398    /// When templates are loaded from XML wrappers that concatenate multiple files,
399    /// this maps output line numbers to (source_file, source_line) pairs.
400    /// Used to provide accurate file names and line numbers in error messages.
401    line_mappings: Vec<LineMapping>,
402
403    /// Enable VM-based execution (OpenJade's bytecode model)
404    ///
405    /// When true, expressions are compiled to bytecode and executed with the VM.
406    /// When false, uses tree-walking interpreter (slower but simpler).
407    /// This flag allows benchmarking VM vs tree-walker performance.
408    use_vm: bool,
409
410    /// Instruction cache for lambda expressions
411    ///
412    /// Maps lambda Value pointers to cached (instructions, start_ip).
413    /// This enables "compile once, run many" for frequently-called lambdas.
414    lambda_cache: HashMap<usize, (Vec<Instruction>, usize)>,
415
416    /// VM global variables (name -> ValueId)
417    ///
418    /// When use_vm is true, this HashMap persists global variables across evaluations.
419    /// Each VM execution saves its globals here and restores them on next execution.
420    vm_globals: HashMap<String, ValueId>,
421
422    /// Counter for processed nodes (for periodic GC)
423    ///
424    /// Tracks how many nodes have been processed. Used to trigger garbage collection
425    /// periodically to prevent memory accumulation during long-running document processing.
426    nodes_processed: usize,
427}
428
429/// Line mapping entry - maps a line number in concatenated code to its source file and line
430#[derive(Debug, Clone)]
431pub struct LineMapping {
432    /// Line number in the concatenated output (1-indexed)
433    pub output_line: usize,
434    /// Source file path
435    pub source_file: String,
436    /// Line number in the source file (1-indexed)
437    pub source_line: usize,
438}
439
440impl Evaluator {
441    /// Create a new evaluator without a grove
442    pub fn new() -> Self {
443        Evaluator {
444            arena: Arena::new(),
445            mode_manager: ModeManager::new(),
446            current_mode: String::new(), // Start with unnamed/default mode
447            current_processing_mode: String::new(), // Start with unnamed/default mode
448            backend: None,
449            call_stack: Vec::new(),
450            current_source_file: None,
451            current_position: None,
452            line_mappings: Vec::new(),
453            use_vm: std::env::var("DAZZLE_VM").is_ok(), // Enable VM via environment variable
454            lambda_cache: HashMap::new(),
455            vm_globals: HashMap::new(), // Persists VM globals across evaluations
456            nodes_processed: 0,
457        }
458    }
459
460    /// Create a new evaluator with a grove
461    pub fn with_grove(grove: Rc<dyn Grove>) -> Self {
462        let mut arena = Arena::new();
463        arena.grove = Some(grove);
464        Evaluator {
465            arena,
466            mode_manager: ModeManager::new(),
467            current_mode: String::new(), // Start with unnamed/default mode
468            current_processing_mode: String::new(), // Start with unnamed/default mode
469            backend: None,
470            call_stack: Vec::new(),
471            current_source_file: None,
472            current_position: None,
473            line_mappings: Vec::new(),
474            use_vm: std::env::var("DAZZLE_VM").is_ok(), // Enable VM via environment variable
475            lambda_cache: HashMap::new(),
476            vm_globals: HashMap::new(), // Persists VM globals across evaluations
477            nodes_processed: 0,
478        }
479    }
480
481    /// Enable VM-based execution (for benchmarking)
482    pub fn enable_vm(&mut self) {
483        self.use_vm = true;
484    }
485
486    /// Disable VM-based execution (use tree-walker)
487    pub fn disable_vm(&mut self) {
488        self.use_vm = false;
489    }
490
491    /// Set line mappings for error reporting
492    pub fn set_line_mappings(&mut self, mappings: Vec<LineMapping>) {
493        self.line_mappings = mappings;
494    }
495
496    /// Set the current source file (for error reporting)
497    pub fn set_source_file(&mut self, file: String) {
498        self.current_source_file = Some(file);
499    }
500
501    /// Get the current source file
502    pub fn source_file(&self) -> Option<&str> {
503        self.current_source_file.as_deref()
504    }
505
506    /// Set the current position (for error reporting)
507    pub fn set_position(&mut self, position: Position) {
508        self.current_position = Some(position);
509    }
510
511    /// Push a call frame onto the stack
512    fn push_call_frame(&mut self, function_name: String, source: Option<SourceInfo>) {
513        self.call_stack.push(CallFrame::new(function_name, source));
514    }
515
516    /// Pop a call frame from the stack
517    fn pop_call_frame(&mut self) {
518        self.call_stack.pop();
519    }
520
521    /// Create an error with the current call stack and position
522    fn error_with_stack(&self, message: String) -> EvalError {
523        // Include current position in the error message (OpenJade format)
524        let full_message = match (&self.current_source_file, &self.current_position) {
525            (Some(file), Some(pos)) => {
526                format!("{}:{}:{}:E: {}", file, pos.line, pos.column, message)
527            }
528            (Some(file), None) => {
529                format!("{}:E: {}", file, message)
530            }
531            _ => message,
532        };
533        EvalError::with_stack(full_message, self.call_stack.clone())
534    }
535
536    /// Set the backend
537    pub fn set_backend(&mut self, backend: Rc<RefCell<dyn FotBuilder>>) {
538        self.backend = Some(backend);
539    }
540
541    /// Set the grove
542    pub fn set_grove(&mut self, grove: Rc<dyn Grove>) {
543        self.arena.grove = Some(grove);
544    }
545
546    /// Get the grove
547    pub fn grove(&self) -> Option<&Rc<dyn Grove>> {
548        self.arena.grove.as_ref()
549    }
550
551    /// Get static string name for a known primitive
552    ///
553    /// Returns a `'static str` for the primitive name if it's known,
554    /// allowing it to be stored in a Procedure::Primitive value.
555    fn get_primitive_static_name(&self, name: &str) -> Option<&'static str> {
556        match name {
557            // R4RS list primitives
558            "cons" => Some("cons"),
559            "car" => Some("car"),
560            "cdr" => Some("cdr"),
561            "cadr" => Some("cadr"),
562            "caddr" => Some("caddr"),
563            "cadddr" => Some("cadddr"),
564            "list" => Some("list"),
565            "length" => Some("length"),
566            "append" => Some("append"),
567            "reverse" => Some("reverse"),
568            "list-ref" => Some("list-ref"),
569            "list-tail" => Some("list-tail"),
570            "member" => Some("member"),
571            "memv" => Some("memv"),
572            "memq" => Some("memq"),
573            "assoc" => Some("assoc"),
574            "assv" => Some("assv"),
575            "assq" => Some("assq"),
576            "null?" => Some("null?"),
577            "pair?" => Some("pair?"),
578            "list?" => Some("list?"),
579
580            // R4RS predicates
581            "boolean?" => Some("boolean?"),
582            "symbol?" => Some("symbol?"),
583            "char?" => Some("char?"),
584            "string?" => Some("string?"),
585            "number?" => Some("number?"),
586            "integer?" => Some("integer?"),
587            "real?" => Some("real?"),
588            "exact?" => Some("exact?"),
589            "inexact?" => Some("inexact?"),
590            "procedure?" => Some("procedure?"),
591            "vector?" => Some("vector?"),
592            "zero?" => Some("zero?"),
593            "positive?" => Some("positive?"),
594            "negative?" => Some("negative?"),
595            "odd?" => Some("odd?"),
596            "even?" => Some("even?"),
597
598            // R4RS comparison
599            "eq?" => Some("eq?"),
600            "eqv?" => Some("eqv?"),
601            "equal?" => Some("equal?"),
602            "=" => Some("="),
603            "<" => Some("<"),
604            ">" => Some(">"),
605            "<=" => Some("<="),
606            ">=" => Some(">="),
607
608            // R4RS arithmetic
609            "+" => Some("+"),
610            "-" => Some("-"),
611            "*" => Some("*"),
612            "/" => Some("/"),
613            "quotient" => Some("quotient"),
614            "remainder" => Some("remainder"),
615            "modulo" => Some("modulo"),
616            "abs" => Some("abs"),
617            "max" => Some("max"),
618            "min" => Some("min"),
619            "floor" => Some("floor"),
620            "ceiling" => Some("ceiling"),
621            "truncate" => Some("truncate"),
622            "round" => Some("round"),
623            "sqrt" => Some("sqrt"),
624            "expt" => Some("expt"),
625            "exp" => Some("exp"),
626            "log" => Some("log"),
627            "sin" => Some("sin"),
628            "cos" => Some("cos"),
629            "tan" => Some("tan"),
630            "asin" => Some("asin"),
631            "acos" => Some("acos"),
632            "atan" => Some("atan"),
633            "number->string" => Some("number->string"),
634            "string->number" => Some("string->number"),
635
636            // R4RS strings
637            "string" => Some("string"),
638            "string-append" => Some("string-append"),
639            "substring" => Some("substring"),
640            "string-ref" => Some("string-ref"),
641            "string-length" => Some("string-length"),
642            "string=?" => Some("string=?"),
643            "string<?" => Some("string<?"),
644            "string>?" => Some("string>?"),
645            "string<=?" => Some("string<=?"),
646            "string>=?" => Some("string>=?"),
647            "string-ci=?" => Some("string-ci=?"),
648            "string-ci<?" => Some("string-ci<?"),
649            "string-ci>?" => Some("string-ci>?"),
650            "string-ci<=?" => Some("string-ci<=?"),
651            "string-ci>=?" => Some("string-ci>=?"),
652            "string->list" => Some("string->list"),
653            "list->string" => Some("list->string"),
654            "symbol->string" => Some("symbol->string"),
655            "string->symbol" => Some("string->symbol"),
656            "keyword?" => Some("keyword?"),
657            "keyword->string" => Some("keyword->string"),
658            "string->keyword" => Some("string->keyword"),
659
660            // R4RS characters
661            "char=?" => Some("char=?"),
662            "char<?" => Some("char<?"),
663            "char>?" => Some("char>?"),
664            "char<=?" => Some("char<=?"),
665            "char>=?" => Some("char>=?"),
666            "char-ci=?" => Some("char-ci=?"),
667            "char-ci<?" => Some("char-ci<?"),
668            "char-ci>?" => Some("char-ci>?"),
669            "char-ci<=?" => Some("char-ci<=?"),
670            "char-ci>=?" => Some("char-ci>=?"),
671            "char-alphabetic?" => Some("char-alphabetic?"),
672            "char-numeric?" => Some("char-numeric?"),
673            "char-whitespace?" => Some("char-whitespace?"),
674            "char-upper-case?" => Some("char-upper-case?"),
675            "char-lower-case?" => Some("char-lower-case?"),
676            "char-upcase" => Some("char-upcase"),
677            "char-downcase" => Some("char-downcase"),
678            "char->integer" => Some("char->integer"),
679            "integer->char" => Some("integer->char"),
680            "char-property" => Some("char-property"),
681            "char-script-case" => Some("char-script-case"),
682
683            // R4RS vectors
684            "vector" => Some("vector"),
685            "make-vector" => Some("make-vector"),
686            "vector-ref" => Some("vector-ref"),
687            "vector-set!" => Some("vector-set!"),
688            "vector-length" => Some("vector-length"),
689            "vector->list" => Some("vector->list"),
690            "list->vector" => Some("list->vector"),
691            "vector-fill!" => Some("vector-fill!"),
692
693            // R4RS logic
694            "not" => Some("not"),
695
696            // R4RS I/O (excluding special forms)
697            "display" => Some("display"),
698            "write" => Some("write"),
699            "newline" => Some("newline"),
700            "read" => Some("read"),
701            // Note: "load" is NOT included - it's a special form with eval_load
702
703            // Note: R4RS higher-order "map", "for-each", "apply" are NOT included
704            // They are special forms with eval_map, eval_for_each, eval_apply
705
706            // DSSSL grove primitives
707            "node?" => Some("node?"),
708            "sosofo?" => Some("sosofo?"),
709            "current-node" => Some("current-node"),
710            "gi" => Some("gi"),
711            "id" => Some("id"),
712            "data" => Some("data"),
713            "node-property" => Some("node-property"),
714            "attribute-string" => Some("attribute-string"),
715            "parent" => Some("parent"),
716            "ancestor" => Some("ancestor"),
717            "children" => Some("children"),
718            "descendants" => Some("descendants"),
719            "follow" => Some("follow"),
720            "preced" => Some("preced"),
721            "ipreced" => Some("ipreced"),
722            "attributes" => Some("attributes"),
723            "ancestors" => Some("ancestors"),
724            "document-element" => Some("document-element"),
725            "have-ancestor?" => Some("have-ancestor?"),
726            "hierarchical-number" => Some("hierarchical-number"),
727            "hierarchical-number-recursive" => Some("hierarchical-number-recursive"),
728            "absolute-first-sibling?" => Some("absolute-first-sibling?"),
729            "absolute-last-sibling?" => Some("absolute-last-sibling?"),
730            "select-elements" => Some("select-elements"),
731            "element-with-id" => Some("element-with-id"),
732            "match-element?" => Some("match-element?"),
733            "first-sibling?" => Some("first-sibling?"),
734            "last-sibling?" => Some("last-sibling?"),
735            "child-number" => Some("child-number"),
736            "element-number" => Some("element-number"),
737            "node-list?" => Some("node-list?"),
738            "node-list-first" => Some("node-list-first"),
739            "node-list-rest" => Some("node-list-rest"),
740            "node-list-length" => Some("node-list-length"),
741            "empty-node-list" => Some("empty-node-list"),
742            "node-list-empty?" => Some("node-list-empty?"),
743            "node-list-head" => Some("node-list-head"),
744            "node-list-tail" => Some("node-list-tail"),
745            "node-list-sublist" => Some("node-list-sublist"),
746            "node-list-ref" => Some("node-list-ref"),
747            "node-list-reduce" => Some("node-list-reduce"),
748            "node-list-reduce-right" => Some("node-list-reduce-right"),
749            "node-list-map" => Some("node-list-map"),
750            "node-list-filter" => Some("node-list-filter"),
751            "node-list-contains?" => Some("node-list-contains?"),
752            "node-list-some?" => Some("node-list-some?"),
753            "node-list-every?" => Some("node-list-every?"),
754            "node-list->list" => Some("node-list->list"),
755            "node-list-union" => Some("node-list-union"),
756            "node-list-intersection" => Some("node-list-intersection"),
757            "node-list-difference" => Some("node-list-difference"),
758            "node-list-remove-duplicates" => Some("node-list-remove-duplicates"),
759            "node-list-last" => Some("node-list-last"),
760            "node-list-reverse" => Some("node-list-reverse"),
761
762            // DSSSL processing
763            // Note: "process-children" and "process-node-list" are NOT included
764            // They are special forms with eval_process_children, eval_process_node_list
765            "literal" => Some("literal"),
766            "next-match" => Some("next-match"),
767            "sosofo-append" => Some("sosofo-append"),
768            "empty-sosofo" => Some("empty-sosofo"),
769            "format-number" => Some("format-number"),
770            "format-number-list" => Some("format-number-list"),
771
772            // DSSSL entity/notation primitives
773            "entity-system-id" => Some("entity-system-id"),
774            "entity-public-id" => Some("entity-public-id"),
775            "entity-text" => Some("entity-text"),
776            "entity-type" => Some("entity-type"),
777            "notation-system-id" => Some("notation-system-id"),
778            "notation-public-id" => Some("notation-public-id"),
779
780            // DSSSL quantity primitives (stubs)
781            "quantity?" => Some("quantity?"),
782            "quantity" => Some("quantity"),
783            "quantity->number" => Some("quantity->number"),
784            "number->quantity" => Some("number->quantity"),
785            "quantity-convert" => Some("quantity-convert"),
786            "device-length" => Some("device-length"),
787            "label-distance" => Some("label-distance"),
788
789            // DSSSL color primitives (stubs)
790            "color?" => Some("color?"),
791            "color" => Some("color"),
792            "color-space?" => Some("color-space?"),
793            "color-space" => Some("color-space"),
794
795            // DSSSL address primitives (stubs)
796            "address?" => Some("address?"),
797            "address" => Some("address"),
798            "address-local?" => Some("address-local?"),
799            "address-visited?" => Some("address-visited?"),
800
801            // DSSSL glyph primitives (stubs)
802            "glyph-id?" => Some("glyph-id?"),
803            "glyph-id" => Some("glyph-id"),
804            "glyph-subst-table?" => Some("glyph-subst-table?"),
805            "glyph-subst-table" => Some("glyph-subst-table"),
806            "glyph-subst" => Some("glyph-subst"),
807
808            // DSSSL spacing primitives (stubs)
809            "display-space" => Some("display-space"),
810            "inline-space" => Some("inline-space"),
811            "display-space?" => Some("display-space?"),
812            "inline-space?" => Some("inline-space?"),
813
814            // OpenJade extensions
815            "time" => Some("time"),
816            "time->string" => Some("time->string"),
817            "time<=?" => Some("time<=?"),
818            "time<?" => Some("time<?"),
819            "time>=?" => Some("time>=?"),
820            "time>?" => Some("time>?"),
821            "language?" => Some("language?"),
822            "language" => Some("language"),
823            "style?" => Some("style?"),
824            "string-equiv?" => Some("string-equiv?"),
825            "label-length" => Some("label-length"),
826            "external-procedure" => Some("external-procedure"),
827            "declaration" => Some("declaration"),
828            "dtd" => Some("dtd"),
829            "epilog" => Some("epilog"),
830            "prolog" => Some("prolog"),
831            "sgml-declaration" => Some("sgml-declaration"),
832            "sgml-parse" => Some("sgml-parse"),
833            "entity-address" => Some("entity-address"),
834            "entity-generated-system-id" => Some("entity-generated-system-id"),
835            "entity-name-normalize" => Some("entity-name-normalize"),
836            "general-name-normalize" => Some("general-name-normalize"),
837            "normalize" => Some("normalize"),
838            "first-child-gi" => Some("first-child-gi"),
839            "tree-root" => Some("tree-root"),
840            "declare-default-language" => Some("declare-default-language"),
841            "read-entity" => Some("read-entity"),
842            "set-visited!" => Some("set-visited!"),
843            "sosofo-contains-node?" => Some("sosofo-contains-node?"),
844            "page-number-sosofo" => Some("page-number-sosofo"),
845            "ifollow" => Some("ifollow"),
846            "with-language" => Some("with-language"),
847            "all-element-number" => Some("all-element-number"),
848            "ancestor-child-number" => Some("ancestor-child-number"),
849            "element-number-list" => Some("element-number-list"),
850            "inherited-attribute-string" => Some("inherited-attribute-string"),
851            "inherited-element-attribute-string" => Some("inherited-element-attribute-string"),
852            "inherited-start-indent" => Some("inherited-start-indent"),
853            "inherited-end-indent" => Some("inherited-end-indent"),
854            "inherited-line-spacing" => Some("inherited-line-spacing"),
855            "inherited-font-family-name" => Some("inherited-font-family-name"),
856            "inherited-font-size" => Some("inherited-font-size"),
857            "inherited-font-weight" => Some("inherited-font-weight"),
858            "inherited-font-posture" => Some("inherited-font-posture"),
859            "inherited-dbhtml-value" => Some("inherited-dbhtml-value"),
860            "inherited-pi-value" => Some("inherited-pi-value"),
861            "node-list" => Some("node-list"),
862            "node-list=?" => Some("node-list=?"),
863            "node-list-count" => Some("node-list-count"),
864            "node-list-union-map" => Some("node-list-union-map"),
865            "node-list-symmetrical-difference" => Some("node-list-symmetrical-difference"),
866            "node-list-address" => Some("node-list-address"),
867            "node-list-error" => Some("node-list-error"),
868            "node-list-no-order" => Some("node-list-no-order"),
869            "origin-to-subnode-rel-forest-addr" => Some("origin-to-subnode-rel-forest-addr"),
870            "named-node" => Some("named-node"),
871            "named-node-list?" => Some("named-node-list?"),
872            "named-node-list-names" => Some("named-node-list-names"),
873            "select-by-class" => Some("select-by-class"),
874            "select-children" => Some("select-children"),
875            "process-children-trim" => Some("process-children-trim"),
876            "process-element-with-id" => Some("process-element-with-id"),
877            "process-first-descendant" => Some("process-first-descendant"),
878            "process-matching-children" => Some("process-matching-children"),
879
880            // Additional utility primitives
881            "add" => Some("add"),
882            "divide" => Some("divide"),
883            "equal" => Some("equal"),
884            "char-eq" => Some("char-eq"),
885            "char-lt" => Some("char-lt"),
886            "error" => Some("error"),
887            "eof-object?" => Some("eof-object?"),
888            "debug" => Some("debug"),
889            "current-language" => Some("current-language"),
890            "current-mode" => Some("current-mode"),
891            "current-node-address" => Some("current-node-address"),
892            "current-node-page-number-sosofo" => Some("current-node-page-number-sosofo"),
893
894            // Not a known primitive
895            _ => None,
896        }
897    }
898
899    // =========================================================================
900    // Arena Conversion Layer (Phase 2 migration)
901    // =========================================================================
902    //
903    // These functions convert between old Value and new ValueId.
904    // During Phase 2, hot primitives use arena (ValueId), while the rest
905    // of the system still uses Value. These converters bridge the gap.
906
907    /// Convert Value to ValueId (for hot primitives)
908    fn value_to_arena(&mut self, value: &Value) -> ValueId {
909        use crate::scheme::arena::{NIL_ID, TRUE_ID, FALSE_ID};
910
911        match value {
912            Value::Nil => NIL_ID,
913            Value::Bool(true) => TRUE_ID,
914            Value::Bool(false) => FALSE_ID,
915            Value::Integer(n) => self.arena.int(*n),
916            Value::Real(f) => self.arena.real(*f),
917            Value::Quantity { magnitude, unit } => {
918                self.arena.alloc(ValueData::Quantity { magnitude: *magnitude, unit: *unit })
919            }
920            Value::String(s) => self.arena.string((**s).clone()),
921            Value::Symbol(s) => self.arena.symbol(s.clone()),
922            Value::Keyword(k) => self.arena.keyword(k.clone()),
923            Value::Char(c) => self.arena.char(*c),
924            Value::Pair(pair) => {
925                let p = pair.borrow();
926                let car = self.value_to_arena(&p.car);
927                let cdr = self.value_to_arena(&p.cdr);
928                if let Some(pos) = &p.pos {
929                    self.arena.cons_with_pos(car, cdr, pos.clone())
930                } else {
931                    self.arena.cons(car, cdr)
932                }
933            }
934            Value::Vector(vec) => {
935                let v = vec.borrow();
936                let elements: Vec<ValueId> = v.iter().map(|val| self.value_to_arena(val)).collect();
937                self.arena.vector(elements)
938            }
939            Value::Node(node) => {
940                self.arena.alloc(ValueData::Node(node.clone()))
941            }
942            Value::NodeList(node_list) => {
943                self.arena.alloc(ValueData::NodeList(node_list.clone()))
944            }
945            Value::Sosofo => {
946                self.arena.alloc(ValueData::Sosofo)
947            }
948            Value::Unspecified => {
949                crate::scheme::arena::UNSPECIFIED_ID
950            }
951            _ => {
952                // For now, unsupported types return NIL
953                NIL_ID
954            }
955        }
956    }
957
958    /// Convert ValueId to Value (from hot primitives)
959    fn arena_to_value(&self, id: ValueId) -> Value {
960        use crate::scheme::arena::{NIL_ID, TRUE_ID, FALSE_ID};
961
962        // Fast path for constants
963        if id == NIL_ID {
964            return Value::Nil;
965        }
966        if id == TRUE_ID {
967            return Value::Bool(true);
968        }
969        if id == FALSE_ID {
970            return Value::Bool(false);
971        }
972
973        match self.arena.get(id) {
974            ValueData::Nil => Value::Nil,
975            ValueData::Bool(b) => Value::Bool(*b),
976            ValueData::Integer(n) => Value::Integer(*n),
977            ValueData::Real(f) => Value::Real(*f),
978            ValueData::Quantity { magnitude, unit } => {
979                Value::Quantity { magnitude: *magnitude, unit: *unit }
980            }
981            ValueData::String(s) => Value::String(Gc::new(s.clone())),
982            ValueData::Symbol(s) => Value::Symbol(s.clone()),
983            ValueData::Keyword(k) => Value::Keyword(k.clone()),
984            ValueData::Char(c) => Value::Char(*c),
985            ValueData::Pair { car, cdr, pos } => {
986                let car_val = self.arena_to_value(*car);
987                let cdr_val = self.arena_to_value(*cdr);
988                if let Some(p) = pos {
989                    Value::cons_with_pos(car_val, cdr_val, p.clone())
990                } else {
991                    Value::cons(car_val, cdr_val)
992                }
993            }
994            ValueData::Vector(elements) => {
995                let vals: Vec<Value> = elements.iter().map(|id| self.arena_to_value(*id)).collect();
996                Value::vector(vals)
997            }
998            ValueData::Node(node) => {
999                Value::Node(node.clone())
1000            }
1001            ValueData::NodeList(node_list) => {
1002                Value::NodeList(node_list.clone())
1003            }
1004            ValueData::Sosofo => Value::Sosofo,
1005            ValueData::Unspecified => Value::Unspecified,
1006            ValueData::Error => Value::Error,
1007            ValueData::Procedure(_) => {
1008                // Procedures cannot be converted back to old-style values
1009                // This shouldn't happen in normal operation
1010                Value::Unspecified
1011            }
1012        }
1013    }
1014
1015    /// Apply arena primitive (Phase 2+3 hot path)
1016    fn apply_primitive(&mut self, name: &str, args: &[Value]) -> EvalResult {
1017        use crate::scheme::primitives::{
1018            car, cdr, cons, null, equal,
1019            cadr, caddr, cadddr, list, length,
1020            reverse, append, list_p, list_ref,
1021            pair_p, number_p, integer_p, real_p,
1022            string_p, symbol_p, char_p, boolean_p,
1023            zero_p, positive_p, negative_p, odd_p, even_p,
1024            add, subtract, multiply, divide,
1025            quotient, remainder, modulo,
1026            num_eq, num_lt, num_gt, num_le, num_ge,
1027            abs, min, max,
1028            floor, ceiling, truncate, round,
1029            sqrt, sin, cos, tan,
1030            asin, acos, atan, exp, log, expt,
1031            string_length, string_ref, substring, string_append,
1032            string_eq, string_lt, string_gt, string_le, string_ge,
1033            string_ci_eq, string_ci_lt, string_ci_gt,
1034            string_ci_le, string_ci_ge,
1035            char_eq, char_lt, char_gt, char_le, char_ge,
1036            char_ci_eq, char_ci_lt, char_ci_gt, char_ci_le, char_ci_ge,
1037            char_upcase, char_downcase,
1038            char_alphabetic_p, char_numeric_p, char_whitespace_p,
1039            char_to_integer, integer_to_char,
1040            char_property, char_script_case,
1041            symbol_to_string, string_to_symbol,
1042            keyword_p, keyword_to_string, string_to_keyword,
1043            memq, memv, member,
1044            assq, assv, assoc,
1045            not, eq_p, eqv_p,
1046            caar, cdar, cddr,
1047            caaar, caadr, cadar,
1048            cdaar, cdadr, cddar, cdddr,
1049            vector, make_vector, vector_length,
1050            vector_ref, vector_set,
1051            vector_to_list, list_to_vector, vector_fill,
1052            vector_p, procedure_p,
1053            set_car, set_cdr, list_tail,
1054            string_upcase, string_downcase, case_fold_down,
1055            string_index,
1056            string_to_list, list_to_string,
1057            gcd, lcm,
1058            exact_to_inexact, inexact_to_exact,
1059            make_string, string, reverse_bang,
1060            string_set, string_copy, string_fill,
1061            char_lower_case_p, char_upper_case_p,
1062            last, last_pair, list_copy,
1063            append_bang, iota,
1064            take, drop, split_at,
1065            filter, remove,
1066            numerator, denominator, rationalize,
1067            angle, magnitude, string_to_number_radix,
1068            number_to_string_radix,
1069            null_list_p, improper_list_p, circular_list_p,
1070            bitwise_and, bitwise_ior, bitwise_xor, bitwise_not,
1071            arithmetic_shift, bit_extract,
1072            bitwise_bit_set_p, bitwise_bit_count,
1073            display, newline, write, write_char,
1074            read_char, eof_object_p,
1075            format_number, format_number_list,
1076            empty_sosofo, sosofo_append, if_first_page, if_front_page,
1077            current_node,
1078            gi, data, id,
1079            children, parent, attributes,
1080            node_list_p, empty_node_list, node_list_empty_p,
1081            node_list_length, node_list_first,
1082            attribute_string,
1083            node_list_rest, node_list_ref, node_list_reverse,
1084            node_p, sosofo_p, quantity_p,
1085            color_p, color, display_space_p, inline_space_p,
1086            quantity_to_number, number_to_quantity, quantity_convert,
1087            device_length, label_distance,
1088            ancestor, descendants, follow, preced, ipreced,
1089            node_list_last, node_list_union, node_list_intersection,
1090            node_list_difference, node_list_remove_duplicates,
1091            select_elements, first_sibling_p, last_sibling_p,
1092            child_number, element_with_id,
1093            element_number, hierarchical_number, hierarchical_number_recursive,
1094            ancestors, document_element, have_ancestor_p,
1095            match_element_p, node_list_map,
1096            node_property, absolute_first_sibling_p, absolute_last_sibling_p,
1097            node_list_to_list, node_list_contains_p,
1098            entity_system_id, entity_public_id, entity_type,
1099            notation_system_id, notation_public_id,
1100            current_language, current_mode, current_node_address,
1101            current_node_page_number_sosofo, debug,
1102            exact_p, inexact_p, error,
1103            address_p, address_local_p, address_visited_p,
1104            color_space_p, color_space, display_space, inline_space,
1105            glyph_id_p, glyph_id, glyph_subst_table_p,
1106            glyph_subst_table, glyph_subst,
1107            time, time_to_string, time_le, time_lt,
1108            time_ge, time_gt,
1109            language_p, language, style_p,
1110            string_equiv_p, label_length, external_procedure,
1111            declaration, dtd, epilog, prolog,
1112            sgml_declaration, sgml_parse,
1113            entity_address, entity_generated_system_id,
1114            entity_name_normalize, general_name_normalize, normalize,
1115            first_child_gi, tree_root, declare_default_language,
1116            read_entity, set_visited,
1117            sosofo_contains_node_p, page_number_sosofo, ifollow, with_language,
1118            all_element_number, ancestor_child_number, element_number_list,
1119            inherited_attribute_string, inherited_element_attribute_string,
1120            inherited_start_indent, inherited_end_indent, inherited_line_spacing,
1121            inherited_font_family_name, inherited_font_size, inherited_font_weight,
1122            inherited_font_posture, inherited_dbhtml_value, inherited_pi_value,
1123            node_list, node_list_eq_p,
1124            node_list_union_map, node_list_symmetrical_difference, node_list_count,
1125            node_list_address, node_list_error, node_list_no_order,
1126            origin_to_subnode_rel_forest_addr,
1127            named_node, named_node_list_p, named_node_list_names,
1128            select_by_class, select_children,
1129            process_children_trim, process_element_with_id, process_first_descendant,
1130            process_matching_children, next_match,
1131        };
1132
1133        // Special handling for eq? and eqv? - check pointer equality at Value level
1134        // to preserve identity semantics when converting from Value to ValueId
1135        if (name == "eq?" || name == "eqv?") && args.len() == 2 {
1136            // Check if the two Values are pointer-equal (same object)
1137            let ptr_equal = match (&args[0], &args[1]) {
1138                (Value::Pair(p1), Value::Pair(p2)) => gc::Gc::ptr_eq(p1, p2),
1139                (Value::String(s1), Value::String(s2)) => gc::Gc::ptr_eq(s1, s2),
1140                (Value::Procedure(pr1), Value::Procedure(pr2)) => gc::Gc::ptr_eq(pr1, pr2),
1141                _ => false,
1142            };
1143            if ptr_equal {
1144                return Ok(Value::bool(true));
1145            }
1146        }
1147
1148        // Special handling for type predicates that check types not convertible to arena
1149        if name == "vector?" && args.len() == 1 {
1150            return Ok(Value::bool(matches!(args[0], Value::Vector(_))));
1151        }
1152        if name == "procedure?" && args.len() == 1 {
1153            return Ok(Value::bool(matches!(args[0], Value::Procedure(_))));
1154        }
1155
1156        // Convert args to arena
1157        let arena_args: Vec<ValueId> = args.iter().map(|v| self.value_to_arena(v)).collect();
1158
1159        // Call arena primitive
1160        let result_id = match name {
1161            "car" => car(&self.arena, &arena_args),
1162            "cdr" => cdr(&self.arena, &arena_args),
1163            "cons" => cons(&mut self.arena, &arena_args),
1164            "null?" => null(&self.arena, &arena_args),
1165            "equal?" => equal(&self.arena, &arena_args),
1166            "cadr" => cadr(&self.arena, &arena_args),
1167            "caddr" => caddr(&self.arena, &arena_args),
1168            "cadddr" => cadddr(&self.arena, &arena_args),
1169            "list" => list(&mut self.arena, &arena_args),
1170            "length" => length(&mut self.arena, &arena_args),
1171            "reverse" => reverse(&mut self.arena, &arena_args),
1172            "append" => append(&mut self.arena, &arena_args),
1173            "list?" => list_p(&self.arena, &arena_args),
1174            "list-ref" => list_ref(&self.arena, &arena_args),
1175            "pair?" => pair_p(&self.arena, &arena_args),
1176            "number?" => number_p(&self.arena, &arena_args),
1177            "integer?" => integer_p(&self.arena, &arena_args),
1178            "real?" => real_p(&self.arena, &arena_args),
1179            "string?" => string_p(&self.arena, &arena_args),
1180            "symbol?" => symbol_p(&self.arena, &arena_args),
1181            "char?" => char_p(&self.arena, &arena_args),
1182            "boolean?" => boolean_p(&self.arena, &arena_args),
1183            "zero?" => zero_p(&self.arena, &arena_args),
1184            "positive?" => positive_p(&self.arena, &arena_args),
1185            "negative?" => negative_p(&self.arena, &arena_args),
1186            "odd?" => odd_p(&self.arena, &arena_args),
1187            "even?" => even_p(&self.arena, &arena_args),
1188            "+" => add(&mut self.arena, &arena_args),
1189            "-" => subtract(&mut self.arena, &arena_args),
1190            "*" => multiply(&mut self.arena, &arena_args),
1191            "/" => divide(&mut self.arena, &arena_args),
1192            "quotient" => quotient(&mut self.arena, &arena_args),
1193            "remainder" => remainder(&mut self.arena, &arena_args),
1194            "modulo" => modulo(&mut self.arena, &arena_args),
1195            "=" => num_eq(&self.arena, &arena_args),
1196            "<" => num_lt(&self.arena, &arena_args),
1197            ">" => num_gt(&self.arena, &arena_args),
1198            "<=" => num_le(&self.arena, &arena_args),
1199            ">=" => num_ge(&self.arena, &arena_args),
1200            "abs" => abs(&mut self.arena, &arena_args),
1201            "min" => min(&mut self.arena, &arena_args),
1202            "max" => max(&mut self.arena, &arena_args),
1203            "floor" => floor(&mut self.arena, &arena_args),
1204            "ceiling" => ceiling(&mut self.arena, &arena_args),
1205            "truncate" => truncate(&mut self.arena, &arena_args),
1206            "round" => round(&mut self.arena, &arena_args),
1207            "sqrt" => sqrt(&mut self.arena, &arena_args),
1208            "sin" => sin(&mut self.arena, &arena_args),
1209            "cos" => cos(&mut self.arena, &arena_args),
1210            "tan" => tan(&mut self.arena, &arena_args),
1211            "asin" => asin(&mut self.arena, &arena_args),
1212            "acos" => acos(&mut self.arena, &arena_args),
1213            "atan" => atan(&mut self.arena, &arena_args),
1214            "exp" => exp(&mut self.arena, &arena_args),
1215            "log" => log(&mut self.arena, &arena_args),
1216            "expt" => expt(&mut self.arena, &arena_args),
1217            "string-length" => string_length(&mut self.arena, &arena_args),
1218            "string-ref" => string_ref(&mut self.arena, &arena_args),
1219            "substring" => substring(&mut self.arena, &arena_args),
1220            "string-append" => string_append(&mut self.arena, &arena_args),
1221            "string=?" => string_eq(&self.arena, &arena_args),
1222            "string<?" => string_lt(&self.arena, &arena_args),
1223            "string>?" => string_gt(&self.arena, &arena_args),
1224            "string<=?" => string_le(&self.arena, &arena_args),
1225            "string>=?" => string_ge(&self.arena, &arena_args),
1226            "string-ci=?" => string_ci_eq(&self.arena, &arena_args),
1227            "string-ci<?" => string_ci_lt(&self.arena, &arena_args),
1228            "string-ci>?" => string_ci_gt(&self.arena, &arena_args),
1229            "string-ci<=?" => string_ci_le(&self.arena, &arena_args),
1230            "string-ci>=?" => string_ci_ge(&self.arena, &arena_args),
1231
1232            "char=?" => char_eq(&self.arena, &arena_args),
1233            "char<?" => char_lt(&self.arena, &arena_args),
1234            "char>?" => char_gt(&self.arena, &arena_args),
1235            "char<=?" => char_le(&self.arena, &arena_args),
1236            "char>=?" => char_ge(&self.arena, &arena_args),
1237            "char-ci=?" => char_ci_eq(&self.arena, &arena_args),
1238            "char-ci<?" => char_ci_lt(&self.arena, &arena_args),
1239            "char-ci>?" => char_ci_gt(&self.arena, &arena_args),
1240            "char-ci<=?" => char_ci_le(&self.arena, &arena_args),
1241            "char-ci>=?" => char_ci_ge(&self.arena, &arena_args),
1242            "char-upcase" => char_upcase(&mut self.arena, &arena_args),
1243            "char-downcase" => char_downcase(&mut self.arena, &arena_args),
1244            "char-alphabetic?" => char_alphabetic_p(&self.arena, &arena_args),
1245            "char-numeric?" => char_numeric_p(&self.arena, &arena_args),
1246            "char-whitespace?" => char_whitespace_p(&self.arena, &arena_args),
1247            "char->integer" => char_to_integer(&mut self.arena, &arena_args),
1248            "integer->char" => integer_to_char(&mut self.arena, &arena_args),
1249            "char-property" => char_property(&mut self.arena, &arena_args),
1250            "char-script-case" => char_script_case(&mut self.arena, &arena_args),
1251
1252            "symbol->string" => symbol_to_string(&mut self.arena, &arena_args),
1253            "string->symbol" => string_to_symbol(&mut self.arena, &arena_args),
1254            "keyword?" => keyword_p(&self.arena, &arena_args),
1255            "keyword->string" => keyword_to_string(&mut self.arena, &arena_args),
1256            "string->keyword" => string_to_keyword(&mut self.arena, &arena_args),
1257
1258            "memq" => memq(&self.arena, &arena_args),
1259            "memv" => memv(&self.arena, &arena_args),
1260            "member" => member(&self.arena, &arena_args),
1261            "assq" => assq(&self.arena, &arena_args),
1262            "assv" => assv(&self.arena, &arena_args),
1263            "assoc" => assoc(&self.arena, &arena_args),
1264
1265            "not" => not(&self.arena, &arena_args),
1266            "eq?" => eq_p(&self.arena, &arena_args),
1267            "eqv?" => eqv_p(&self.arena, &arena_args),
1268            "caar" => caar(&self.arena, &arena_args),
1269            "cdar" => cdar(&self.arena, &arena_args),
1270            "cddr" => cddr(&self.arena, &arena_args),
1271
1272            "caaar" => caaar(&self.arena, &arena_args),
1273            "caadr" => caadr(&self.arena, &arena_args),
1274            "cadar" => cadar(&self.arena, &arena_args),
1275            "cdaar" => cdaar(&self.arena, &arena_args),
1276            "cdadr" => cdadr(&self.arena, &arena_args),
1277            "cddar" => cddar(&self.arena, &arena_args),
1278            "cdddr" => cdddr(&self.arena, &arena_args),
1279
1280            "vector" => vector(&mut self.arena, &arena_args),
1281            "make-vector" => make_vector(&mut self.arena, &arena_args),
1282            "vector-length" => vector_length(&mut self.arena, &arena_args),
1283            "vector-ref" => vector_ref(&self.arena, &arena_args),
1284            "vector-set!" => vector_set(&mut self.arena, &arena_args),
1285            "vector->list" => vector_to_list(&mut self.arena, &arena_args),
1286            "list->vector" => list_to_vector(&mut self.arena, &arena_args),
1287            "vector-fill!" => vector_fill(&mut self.arena, &arena_args),
1288
1289            "vector?" => vector_p(&self.arena, &arena_args),
1290            "procedure?" => procedure_p(&self.arena, &arena_args),
1291            "set-car!" => set_car(&mut self.arena, &arena_args),
1292            "set-cdr!" => set_cdr(&mut self.arena, &arena_args),
1293            "list-tail" => list_tail(&self.arena, &arena_args),
1294
1295            "string-upcase" => string_upcase(&mut self.arena, &arena_args),
1296            "string-downcase" => string_downcase(&mut self.arena, &arena_args),
1297            "case-fold-down" => case_fold_down(&mut self.arena, &arena_args), // DSSSL alias for string-downcase
1298            "string-index" => string_index(&mut self.arena, &arena_args),
1299            "string->number" => string_to_number_radix(&mut self.arena, &arena_args), // Updated to support radix
1300            "number->string" => number_to_string_radix(&mut self.arena, &arena_args), // Updated to support radix
1301            "string->list" => string_to_list(&mut self.arena, &arena_args),
1302            "list->string" => list_to_string(&mut self.arena, &arena_args),
1303
1304            "gcd" => gcd(&mut self.arena, &arena_args),
1305            "lcm" => lcm(&mut self.arena, &arena_args),
1306            "exact->inexact" => exact_to_inexact(&mut self.arena, &arena_args),
1307            "inexact->exact" => inexact_to_exact(&mut self.arena, &arena_args),
1308            "make-string" => make_string(&mut self.arena, &arena_args),
1309            "string" => string(&mut self.arena, &arena_args),
1310            "reverse!" => reverse_bang(&mut self.arena, &arena_args),
1311
1312            "string-set!" => string_set(&mut self.arena, &arena_args),
1313            "string-copy" => string_copy(&mut self.arena, &arena_args),
1314            "string-fill!" => string_fill(&mut self.arena, &arena_args),
1315            "char-lower-case?" => char_lower_case_p(&self.arena, &arena_args),
1316            "char-upper-case?" => char_upper_case_p(&self.arena, &arena_args),
1317
1318            "last" => last(&self.arena, &arena_args),
1319            "last-pair" => last_pair(&self.arena, &arena_args),
1320            "list-copy" => list_copy(&mut self.arena, &arena_args),
1321            "append!" => append_bang(&mut self.arena, &arena_args),
1322            "iota" => iota(&mut self.arena, &arena_args),
1323
1324            "take" => take(&mut self.arena, &arena_args),
1325            "drop" => drop(&self.arena, &arena_args),
1326            "split-at" => split_at(&mut self.arena, &arena_args),
1327            "filter" => filter(&self.arena, &arena_args),
1328            "remove" => remove(&self.arena, &arena_args),
1329
1330            "numerator" => numerator(&mut self.arena, &arena_args),
1331            "denominator" => denominator(&mut self.arena, &arena_args),
1332            "rationalize" => rationalize(&self.arena, &arena_args),
1333            "angle" => angle(&mut self.arena, &arena_args),
1334            "magnitude" => magnitude(&mut self.arena, &arena_args),
1335
1336            "null-list?" => null_list_p(&self.arena, &arena_args),
1337            "improper-list?" => improper_list_p(&self.arena, &arena_args),
1338            "circular-list?" => circular_list_p(&self.arena, &arena_args),
1339
1340            "bitwise-and" => bitwise_and(&mut self.arena, &arena_args),
1341            "bitwise-ior" => bitwise_ior(&mut self.arena, &arena_args),
1342            "bitwise-xor" => bitwise_xor(&mut self.arena, &arena_args),
1343            "bitwise-not" => bitwise_not(&mut self.arena, &arena_args),
1344            "arithmetic-shift" => arithmetic_shift(&mut self.arena, &arena_args),
1345            "bit-extract" => bit_extract(&mut self.arena, &arena_args),
1346            "bitwise-bit-set?" => bitwise_bit_set_p(&self.arena, &arena_args),
1347            "bitwise-bit-count" => bitwise_bit_count(&mut self.arena, &arena_args),
1348
1349            "display" => display(&self.arena, &arena_args),
1350            "newline" => newline(&self.arena, &arena_args),
1351            "write" => write(&self.arena, &arena_args),
1352            "write-char" => write_char(&self.arena, &arena_args),
1353            "read-char" => read_char(&self.arena, &arena_args),
1354            "eof-object?" => eof_object_p(&self.arena, &arena_args),
1355
1356            "format-number" => format_number(&mut self.arena, &arena_args),
1357            "format-number-list" => format_number_list(&mut self.arena, &arena_args),
1358
1359            "empty-sosofo" => empty_sosofo(&mut self.arena, &arena_args),
1360            "sosofo-append" => sosofo_append(&mut self.arena, &arena_args),
1361            "if-first-page" => if_first_page(&mut self.arena, &arena_args),
1362            "if-front-page" => if_front_page(&mut self.arena, &arena_args),
1363
1364            "current-node" => current_node(&mut self.arena, &arena_args),
1365
1366            "gi" => gi(&mut self.arena, &arena_args),
1367            "data" => data(&mut self.arena, &arena_args),
1368            "id" => id(&mut self.arena, &arena_args),
1369
1370            "children" => children(&mut self.arena, &arena_args),
1371            "parent" => parent(&mut self.arena, &arena_args),
1372            "attributes" => attributes(&mut self.arena, &arena_args),
1373
1374            "node-list?" => node_list_p(&self.arena, &arena_args),
1375            "empty-node-list" => empty_node_list(&mut self.arena, &arena_args),
1376            "node-list-empty?" => node_list_empty_p(&self.arena, &arena_args),
1377            "node-list-length" => node_list_length(&mut self.arena, &arena_args),
1378            "node-list-first" => node_list_first(&mut self.arena, &arena_args),
1379
1380            "attribute-string" => attribute_string(&mut self.arena, &arena_args),
1381
1382            "node-list-rest" => node_list_rest(&mut self.arena, &arena_args),
1383            "node-list-ref" => node_list_ref(&mut self.arena, &arena_args),
1384            "node-list-reverse" => node_list_reverse(&mut self.arena, &arena_args),
1385
1386            "node?" => node_p(&self.arena, &arena_args),
1387            "sosofo?" => sosofo_p(&self.arena, &arena_args),
1388            "quantity?" => quantity_p(&self.arena, &arena_args),
1389
1390            "color?" => color_p(&self.arena, &arena_args),
1391            "color" => color(&mut self.arena, &arena_args),
1392            "display-space?" => display_space_p(&self.arena, &arena_args),
1393            "inline-space?" => inline_space_p(&self.arena, &arena_args),
1394
1395            "quantity->number" => quantity_to_number(&mut self.arena, &arena_args),
1396            "number->quantity" => number_to_quantity(&mut self.arena, &arena_args),
1397            "quantity-convert" => quantity_convert(&mut self.arena, &arena_args),
1398            "device-length" => device_length(&mut self.arena, &arena_args),
1399            "label-distance" => label_distance(&mut self.arena, &arena_args),
1400
1401            "ancestor" => ancestor(&mut self.arena, &arena_args),
1402            "descendants" => descendants(&mut self.arena, &arena_args),
1403            "follow" => follow(&mut self.arena, &arena_args),
1404            "preced" => preced(&mut self.arena, &arena_args),
1405            "ipreced" => ipreced(&mut self.arena, &arena_args),
1406
1407            "node-list-last" => node_list_last(&mut self.arena, &arena_args),
1408            "node-list-union" => node_list_union(&mut self.arena, &arena_args),
1409            "node-list-intersection" => node_list_intersection(&mut self.arena, &arena_args),
1410            "node-list-difference" => node_list_difference(&mut self.arena, &arena_args),
1411            "node-list-remove-duplicates" => node_list_remove_duplicates(&mut self.arena, &arena_args),
1412
1413            "select-elements" => select_elements(&mut self.arena, &arena_args),
1414            "first-sibling?" => first_sibling_p(&mut self.arena, &arena_args),
1415            "last-sibling?" => last_sibling_p(&mut self.arena, &arena_args),
1416            "child-number" => child_number(&mut self.arena, &arena_args),
1417            "element-with-id" => element_with_id(&mut self.arena, &arena_args),
1418
1419            "element-number" => element_number(&mut self.arena, &arena_args),
1420            "hierarchical-number" => hierarchical_number(&mut self.arena, &arena_args),
1421            "hierarchical-number-recursive" => hierarchical_number_recursive(&mut self.arena, &arena_args),
1422
1423            "ancestors" => ancestors(&mut self.arena, &arena_args),
1424            "document-element" => document_element(&mut self.arena, &arena_args),
1425            "have-ancestor?" => have_ancestor_p(&mut self.arena, &arena_args),
1426            "match-element?" => match_element_p(&mut self.arena, &arena_args),
1427            "node-list-map" => node_list_map(&mut self.arena, &arena_args),
1428
1429            "node-property" => node_property(&mut self.arena, &arena_args),
1430            "absolute-first-sibling?" => absolute_first_sibling_p(&mut self.arena, &arena_args),
1431            "absolute-last-sibling?" => absolute_last_sibling_p(&mut self.arena, &arena_args),
1432            "node-list->list" => node_list_to_list(&mut self.arena, &arena_args),
1433            "node-list-contains?" => node_list_contains_p(&mut self.arena, &arena_args),
1434
1435            "entity-system-id" => entity_system_id(&mut self.arena, &arena_args),
1436            "entity-public-id" => entity_public_id(&mut self.arena, &arena_args),
1437            "entity-type" => entity_type(&mut self.arena, &arena_args),
1438            "notation-system-id" => notation_system_id(&mut self.arena, &arena_args),
1439            "notation-public-id" => notation_public_id(&mut self.arena, &arena_args),
1440
1441            "current-language" => current_language(&self.arena, &arena_args),
1442            "current-mode" => current_mode(&self.arena, &arena_args),
1443            "current-node-address" => current_node_address(&self.arena, &arena_args),
1444            "current-node-page-number-sosofo" => current_node_page_number_sosofo(&mut self.arena, &arena_args),
1445            "debug" => debug(&self.arena, &arena_args),
1446
1447            "add" => add(&mut self.arena, &arena_args),
1448            "divide" => divide(&mut self.arena, &arena_args),
1449            "equal" => equal(&self.arena, &arena_args),
1450            "char-eq" => char_eq(&self.arena, &arena_args),
1451            "char-lt" => char_lt(&self.arena, &arena_args),
1452            "exact?" => exact_p(&self.arena, &arena_args),
1453            "inexact?" => inexact_p(&self.arena, &arena_args),
1454            "error" => error(&mut self.arena, &arena_args),
1455            "address?" => address_p(&self.arena, &arena_args),
1456            "address-local?" => address_local_p(&self.arena, &arena_args),
1457            "address-visited?" => address_visited_p(&self.arena, &arena_args),
1458            "color-space?" => color_space_p(&self.arena, &arena_args),
1459            "color-space" => color_space(&self.arena, &arena_args),
1460            "display-space" => display_space(&mut self.arena, &arena_args),
1461            "inline-space" => inline_space(&mut self.arena, &arena_args),
1462            "glyph-id?" => glyph_id_p(&self.arena, &arena_args),
1463            "glyph-id" => glyph_id(&self.arena, &arena_args),
1464            "glyph-subst-table?" => glyph_subst_table_p(&self.arena, &arena_args),
1465            "glyph-subst-table" => glyph_subst_table(&self.arena, &arena_args),
1466            "glyph-subst" => glyph_subst(&self.arena, &arena_args),
1467            "time" => time(&self.arena, &arena_args),
1468            "time->string" => time_to_string(&mut self.arena, &arena_args),
1469            "time<=?" => time_le(&self.arena, &arena_args),
1470            "time<?" => time_lt(&self.arena, &arena_args),
1471            "time>=?" => time_ge(&self.arena, &arena_args),
1472            "time>?" => time_gt(&self.arena, &arena_args),
1473            "language?" => language_p(&self.arena, &arena_args),
1474            "language" => language(&mut self.arena, &arena_args),
1475            "style?" => style_p(&self.arena, &arena_args),
1476            "string-equiv?" => string_equiv_p(&self.arena, &arena_args),
1477            "label-length" => label_length(&mut self.arena, &arena_args),
1478            "external-procedure" => external_procedure(&mut self.arena, &arena_args),
1479            "declaration" => declaration(&self.arena, &arena_args),
1480            "dtd" => dtd(&self.arena, &arena_args),
1481            "epilog" => epilog(&self.arena, &arena_args),
1482            "prolog" => prolog(&self.arena, &arena_args),
1483            "sgml-declaration" => sgml_declaration(&self.arena, &arena_args),
1484            "sgml-parse" => sgml_parse(&self.arena, &arena_args),
1485            "entity-address" => entity_address(&self.arena, &arena_args),
1486            "entity-generated-system-id" => entity_generated_system_id(&mut self.arena, &arena_args),
1487            "entity-name-normalize" => entity_name_normalize(&mut self.arena, &arena_args),
1488            "general-name-normalize" => general_name_normalize(&mut self.arena, &arena_args),
1489            "normalize" => normalize(&mut self.arena, &arena_args),
1490            "first-child-gi" => first_child_gi(&mut self.arena, &arena_args),
1491            "tree-root" => tree_root(&mut self.arena, &arena_args),
1492            "declare-default-language" => declare_default_language(&self.arena, &arena_args),
1493            "read-entity" => read_entity(&mut self.arena, &arena_args),
1494            "set-visited!" => set_visited(&self.arena, &arena_args),
1495            "sosofo-contains-node?" => sosofo_contains_node_p(&self.arena, &arena_args),
1496            "page-number-sosofo" => page_number_sosofo(&mut self.arena, &arena_args),
1497            "ifollow" => ifollow(&self.arena, &arena_args),
1498            "with-language" => with_language(&self.arena, &arena_args),
1499            "all-element-number" => all_element_number(&mut self.arena, &arena_args),
1500            "ancestor-child-number" => ancestor_child_number(&mut self.arena, &arena_args),
1501            "element-number-list" => element_number_list(&self.arena, &arena_args),
1502            "inherited-attribute-string" => inherited_attribute_string(&mut self.arena, &arena_args),
1503            "inherited-element-attribute-string" => inherited_element_attribute_string(&mut self.arena, &arena_args),
1504            "inherited-start-indent" => inherited_start_indent(&mut self.arena, &arena_args),
1505            "inherited-end-indent" => inherited_end_indent(&mut self.arena, &arena_args),
1506            "inherited-line-spacing" => inherited_line_spacing(&mut self.arena, &arena_args),
1507            "inherited-font-family-name" => inherited_font_family_name(&mut self.arena, &arena_args),
1508            "inherited-font-size" => inherited_font_size(&mut self.arena, &arena_args),
1509            "inherited-font-weight" => inherited_font_weight(&mut self.arena, &arena_args),
1510            "inherited-font-posture" => inherited_font_posture(&mut self.arena, &arena_args),
1511            "inherited-dbhtml-value" => inherited_dbhtml_value(&mut self.arena, &arena_args),
1512            "inherited-pi-value" => inherited_pi_value(&mut self.arena, &arena_args),
1513            "node-list" => node_list(&mut self.arena, &arena_args),
1514            "node-list=?" => node_list_eq_p(&self.arena, &arena_args),
1515            "node-list-count" => node_list_count(&mut self.arena, &arena_args),
1516            "node-list-union-map" => node_list_union_map(&self.arena, &arena_args),
1517            "node-list-symmetrical-difference" => node_list_symmetrical_difference(&self.arena, &arena_args),
1518            "node-list-address" => node_list_address(&self.arena, &arena_args),
1519            "node-list-error" => node_list_error(&mut self.arena, &arena_args),
1520            "node-list-no-order" => node_list_no_order(&self.arena, &arena_args),
1521            "origin-to-subnode-rel-forest-addr" => origin_to_subnode_rel_forest_addr(&self.arena, &arena_args),
1522            "named-node" => named_node(&self.arena, &arena_args),
1523            "named-node-list?" => named_node_list_p(&self.arena, &arena_args),
1524            "named-node-list-names" => named_node_list_names(&self.arena, &arena_args),
1525            "select-by-class" => select_by_class(&self.arena, &arena_args),
1526            "select-children" => select_children(&self.arena, &arena_args),
1527            "process-children-trim" => process_children_trim(&self.arena, &arena_args),
1528            "process-element-with-id" => process_element_with_id(&self.arena, &arena_args),
1529            "process-first-descendant" => process_first_descendant(&self.arena, &arena_args),
1530            "process-matching-children" => process_matching_children(&self.arena, &arena_args),
1531            "next-match" => next_match(&self.arena, &arena_args),
1532
1533            // Special handling for literal - it's not an arena primitive
1534            "literal" => {
1535                // literal can take 1 or 2 arguments:
1536                // (literal "text") or (literal data: "text")
1537                // For now, we just extract the text from the first string argument
1538                if args.is_empty() {
1539                    return Err(self.error_with_stack("literal: expected at least 1 argument".to_string()));
1540                }
1541
1542                // Find the text argument - could be first arg or after a keyword
1543                let text = if args.len() == 1 {
1544                    // (literal "text")
1545                    match &args[0] {
1546                        Value::String(s) => s.to_string(),
1547                        _ => return Err(self.error_with_stack("literal: argument must be a string".to_string())),
1548                    }
1549                } else if args.len() == 2 {
1550                    // (literal data: "text") - second arg is the text
1551                    match &args[1] {
1552                        Value::String(s) => s.to_string(),
1553                        _ => return Err(self.error_with_stack("literal: text argument must be a string".to_string())),
1554                    }
1555                } else {
1556                    return Err(self.error_with_stack(format!(
1557                        "literal: expected 1 or 2 arguments, got {}",
1558                        args.len()
1559                    )));
1560                };
1561
1562                if let Some(ref backend) = self.backend {
1563                    backend.borrow_mut().formatting_instruction(&text)
1564                        .map_err(|e| self.error_with_stack(format!("Backend error: {}", e)))?;
1565                }
1566
1567                return Ok(Value::Unspecified);
1568            }
1569
1570            _ => unreachable!("apply_primitive called with non-arena primitive: {}", name),
1571        }
1572        .map_err(|e| self.error_with_stack(e))?;
1573
1574        // Convert result back to Value
1575        Ok(self.arena_to_value(result_id))
1576    }
1577
1578    /// Set the current node
1579    pub fn set_current_node(&mut self, node: Box<dyn Node>) {
1580        self.arena.current_node = Some(Rc::new(node));
1581    }
1582
1583    /// Get the current node
1584    pub fn current_node(&self) -> Option<Rc<Box<dyn Node>>> {
1585        self.arena.current_node.clone()
1586    }
1587
1588    /// Clear the current node
1589    pub fn clear_current_node(&mut self) {
1590        self.arena.current_node = None;
1591    }
1592
1593    /// Restore current node from saved state
1594    pub fn restore_current_node(&mut self, node: Option<Rc<Box<dyn Node>>>) {
1595        self.arena.current_node = node;
1596    }
1597
1598    // =========================================================================
1599    // DSSSL Processing (OpenJade ProcessContext.cxx)
1600    // =========================================================================
1601
1602    /// Start DSSSL processing from the root node
1603    ///
1604    /// Corresponds to OpenJade's `ProcessContext::process()`.
1605    /// After template loading, this triggers automatic tree processing.
1606    pub fn process_root(&mut self, env: Gc<Environment>) -> EvalResult {
1607        // Get the root node from the grove
1608        let root_node = match self.grove() {
1609            Some(grove) => grove.root(),
1610            None => return Err(EvalError::new("No grove set".to_string())),
1611        };
1612
1613        // Set as current node and start processing
1614        self.set_current_node(root_node);
1615        self.process_node(env)
1616    }
1617
1618    /// Process the current node
1619    ///
1620    /// Corresponds to OpenJade's `ProcessContext::processNode()`.
1621    ///
1622    /// ## Algorithm (from OpenJade):
1623    /// 1. If character data node, output directly
1624    /// 2. If element node:
1625    ///    a. Find matching construction rule by GI
1626    ///    b. If rule found, evaluate it (returns sosofo)
1627    ///    c. If no rule, default behavior: process-children
1628    pub fn process_node(&mut self, env: Gc<Environment>) -> EvalResult {
1629        // Increment node counter and trigger periodic GC
1630        self.nodes_processed += 1;
1631
1632        // Trigger GC every 100 nodes to prevent memory accumulation
1633        if self.nodes_processed % 100 == 0 {
1634            // Collect Gc-wrapped values (tree-walker mode)
1635            gc::force_collect();
1636
1637            // Collect arena values (VM mode)
1638            // Preserve VM globals as GC roots
1639            let roots: Vec<_> = self.vm_globals.values().copied().collect();
1640            self.arena.gc(&roots);
1641        }
1642
1643        let node = match self.current_node() {
1644            Some(n) => n.clone(),
1645            None => return Err(EvalError::new("No current node".to_string())),
1646        };
1647
1648        // Get element name (GI)
1649        let gi = match node.gi() {
1650            Some(gi) => gi.to_string(),
1651            None => {
1652                // Not an element (e.g., text node, comment, etc.)
1653                // For text nodes, output their data content
1654                // Skip whitespace-only text nodes (OpenJade behavior)
1655                if node.is_text() {
1656                    if let Some(text) = node.data() {
1657                        // Skip if text is only whitespace
1658                        if !text.trim().is_empty() {
1659                            // Output text to backend using literal() (each backend handles its own escaping)
1660                            if let Some(ref backend) = self.backend {
1661                                backend.borrow_mut().literal(&text)
1662                                    .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
1663                            }
1664                        }
1665                    }
1666                }
1667                return Ok(Value::Unspecified);
1668            }
1669        };
1670
1671        // Find matching construction rule (in current processing mode)
1672        let mode_name = self.current_processing_mode.clone();
1673        let mode = self.mode_manager.get_mode(&mode_name);
1674        let rule = mode.and_then(|m| m.find_match(&gi, &**node));
1675
1676        if let Some(rule) = rule {
1677            // Rule found - evaluate the construction expression
1678            // Save current source context
1679            let saved_file = self.current_source_file.clone();
1680            let saved_pos = self.current_position.clone();
1681
1682            // Restore source context to where the rule was defined
1683            // This ensures error messages show the rule definition location, not the rule body location
1684            if let Some(ref rule_file) = rule.source_file {
1685                self.current_source_file = Some(rule_file.clone());
1686            }
1687            if let Some(ref rule_pos) = rule.source_pos {
1688                self.current_position = Some(rule_pos.clone());
1689            }
1690
1691            // Extract rule data to avoid borrow conflicts
1692            let rule_expr = rule.expr.clone();
1693            let rule_cached = rule.cached_instructions.clone();
1694
1695            // Evaluate the construction expression (with instruction caching if VM is enabled)
1696            let result = if self.use_vm {
1697                self.eval_rule_with_cache(rule_expr, rule_cached, env)
1698            } else {
1699                self.eval(rule_expr, env)
1700            };
1701
1702            // Restore previous source context
1703            self.current_source_file = saved_file;
1704            self.current_position = saved_pos;
1705
1706            result
1707        } else if let Some(default_expr) = mode.and_then(|m| m.default_rule.as_ref()).cloned() {
1708            // No specific rule found - use default rule
1709            self.eval(default_expr, env)
1710        } else {
1711            // No rule found (and no default) - OpenJade's implicit default behavior:
1712            // Process children automatically (DSSSL §10.1.5)
1713            self.eval_process_children(env)
1714        }
1715    }
1716
1717    /// Evaluate a construction rule with instruction caching (OpenJade's InsnPtr optimization)
1718    ///
1719    /// This implements OpenJade's key performance optimization:
1720    /// ```cpp
1721    /// class Identifier {
1722    ///     Owner<Expression> def_;   // Parsed AST
1723    ///     InsnPtr insn_;            // Compiled instructions (cached!)
1724    /// };
1725    /// ```
1726    ///
1727    /// Each construction rule compiles its expression ONCE and caches the bytecode.
1728    /// Subsequent evaluations execute the cached instructions directly.
1729    ///
1730    /// This is why OpenJade is 50-74x faster than tree-walking interpreters on
1731    /// real DSSSL workloads (e.g., DocBook processing with thousands of rule applications).
1732    fn eval_rule_with_cache(
1733        &mut self,
1734        rule_expr: Value,
1735        rule_cached: RefCell<Option<(Vec<Instruction>, usize)>>,
1736        _env: Gc<Environment>
1737    ) -> EvalResult {
1738        // Check if we have cached instructions (clone to avoid holding the borrow)
1739        let cached_data = rule_cached.borrow().clone();
1740
1741        if let Some((instructions, start_ip)) = cached_data {
1742            // Cache hit! Execute cached instructions directly
1743            // Create VM with primitives and extend with saved user-defined globals
1744            let mut vm = VM::with_primitives(&mut self.arena);
1745            vm.extend_globals(self.vm_globals.clone());
1746
1747            let result_id = vm.run(&instructions, start_ip)
1748                .map_err(|e| EvalError::new(format!("VM error: {}", e)))?;
1749
1750            // Save globals for next execution
1751            self.vm_globals = vm.get_user_globals();
1752
1753        // Debug: Log saved globals
1754        if std::env::var("DAZZLE_DEBUG").is_ok() {
1755            let saved: Vec<_> = self.vm_globals.keys().filter(|k| k.starts_with('%')).collect();
1756            if !saved.is_empty() {
1757                eprintln!("Evaluator: Saved {} user globals (% vars: {:?})", self.vm_globals.len(), saved);
1758            }
1759        }
1760
1761            // Convert back: ValueId → Value
1762            return Ok(arena_to_value(&self.arena, result_id));
1763        }
1764
1765        // Cache miss - compile and cache the instructions
1766        let expr_id = value_to_arena(&mut self.arena, &rule_expr);
1767
1768        let mut compiler = Compiler::new(&self.arena);
1769        let start_ip = compiler.compile(expr_id)
1770            .map_err(|e| EvalError::new(format!("Compilation error: {}", e)))?;
1771
1772        let mut program = compiler.into_program();
1773        program.emit(Instruction::Return);
1774
1775        // Cache the compiled instructions
1776        let instructions = program.instructions.clone();
1777        *rule_cached.borrow_mut() = Some((instructions.clone(), start_ip));
1778
1779        // Execute the newly compiled instructions
1780        let mut vm = VM::with_primitives(&mut self.arena);
1781        vm.extend_globals(self.vm_globals.clone());
1782
1783        let result_id = vm.run(&instructions, start_ip)
1784            .map_err(|e| EvalError::new(format!("VM error: {}", e)))?;
1785
1786        // Save globals for next execution
1787        self.vm_globals = vm.get_user_globals();
1788
1789        // Debug: Log saved globals
1790        if std::env::var("DAZZLE_DEBUG").is_ok() {
1791            let saved: Vec<_> = self.vm_globals.keys().filter(|k| k.starts_with('%')).collect();
1792            if !saved.is_empty() {
1793                eprintln!("Evaluator: Saved {} user globals (% vars: {:?})", self.vm_globals.len(), saved);
1794            }
1795        }
1796
1797        // Convert back: ValueId → Value
1798        Ok(arena_to_value(&self.arena, result_id))
1799    }
1800
1801    /// Evaluate an expression using the VM (bytecode execution)
1802    ///
1803    /// This is OpenJade's optimization: compile expressions to bytecode once,
1804    /// execute with a fast stack-based VM. Key advantages:
1805    /// - No recursion (flat while loop)
1806    /// - No pattern matching overhead
1807    /// - Pre-resolved closures (no environment lookup)
1808    /// - Stack-based (minimal GC pressure)
1809    ///
1810    /// Returns Err if compilation or execution fails.
1811    fn eval_with_vm(&mut self, expr: Value, _env: Gc<Environment>) -> EvalResult {
1812        // Convert Value → ValueId (using bridge)
1813        let expr_id = value_to_arena(&mut self.arena, &expr);
1814
1815        // Compile to bytecode
1816        let mut compiler = Compiler::new(&self.arena);
1817        let start_ip = compiler.compile(expr_id)
1818            .map_err(|e| EvalError::new(format!("Compilation error: {}", e)))?;
1819
1820        let mut program = compiler.into_program();
1821
1822        // Add Return instruction at the end (top-level eval needs this)
1823        program.emit(Instruction::Return);
1824
1825        // Create VM with primitives registered and extend with saved user-defined globals
1826        let mut vm = VM::with_primitives(&mut self.arena);
1827        vm.extend_globals(self.vm_globals.clone());
1828
1829        // Execute bytecode
1830        let result_id = vm.run(&program.instructions, start_ip)
1831            .map_err(|e| EvalError::new(format!("VM error: {}", e)))?;
1832
1833        // Save globals for next execution
1834        self.vm_globals = vm.get_user_globals();
1835
1836        // Debug: Log saved globals
1837        if std::env::var("DAZZLE_DEBUG").is_ok() {
1838            let saved: Vec<_> = self.vm_globals.keys().filter(|k| k.starts_with('%')).collect();
1839            if !saved.is_empty() {
1840                eprintln!("Evaluator: Saved {} user globals (% vars: {:?})", self.vm_globals.len(), saved);
1841            }
1842        }
1843
1844        // Convert back: ValueId → Value
1845        Ok(arena_to_value(&self.arena, result_id))
1846    }
1847
1848    /// Temporarily disable VM mode (returns previous state)
1849    ///
1850    /// This is useful for operations like template loading where VM mode's
1851    /// arena allocation doesn't work well with intermediate values.
1852    pub fn set_use_vm(&mut self, enabled: bool) -> bool {
1853        let previous = self.use_vm;
1854        self.use_vm = enabled;
1855        previous
1856    }
1857
1858    /// Sync all Environment definitions to vm_globals
1859    ///
1860    /// This is needed when switching from tree-walker mode to VM mode.
1861    /// Definitions made in tree-walker mode are stored in Environment (Gc),
1862    /// but VM mode looks in vm_globals (HashMap<String, ValueId>).
1863    pub fn sync_env_to_vm(&mut self, env: Gc<Environment>) -> Result<(), EvalError> {
1864        use crate::scheme::bridge::value_to_arena;
1865
1866        // Get all bindings from environment
1867        let bindings = env.all_bindings();
1868
1869        // Convert each binding to arena and store in vm_globals
1870        for (name, value) in bindings {
1871            let value_id = value_to_arena(&mut self.arena, &value);
1872            self.vm_globals.insert(name, value_id);
1873        }
1874
1875        Ok(())
1876    }
1877
1878    /// Trigger garbage collection if needed
1879    ///
1880    /// This should be called periodically during long-running operations like
1881    /// template loading to prevent memory accumulation.
1882    pub fn gc_if_needed(&mut self) {
1883        // Collect Gc-wrapped values (tree-walker mode)
1884        gc::force_collect();
1885
1886        // Collect arena values (VM mode)
1887        // Preserve VM globals as GC roots
1888        let roots: Vec<_> = self.vm_globals.values().copied().collect();
1889        self.arena.gc(&roots);
1890    }
1891
1892    /// Evaluate an expression in an environment
1893    ///
1894    /// Corresponds to OpenJade's `Interpreter::eval()`.
1895    ///
1896    /// ## Evaluation Rules
1897    ///
1898    /// 1. **Self-evaluating**: Numbers, strings, bools, chars → return as-is
1899    /// 2. **Symbols**: Variable lookup in environment
1900    /// 3. **Lists**: Check first element for special forms, otherwise apply
1901    pub fn eval(&mut self, expr: Value, env: Gc<Environment>) -> EvalResult {
1902        // Check if VM execution is enabled
1903        if self.use_vm {
1904            return self.eval_with_vm(expr, env);
1905        }
1906
1907        // Save previous context state
1908        let context_was_set = has_evaluator_context();
1909        let previous_context = get_evaluator_context();
1910
1911        // ALWAYS update context to reflect current evaluator state
1912        // This ensures current_node is correct for nested eval() calls
1913        set_evaluator_context(EvaluatorContext {
1914            grove: self.arena.grove.clone(),
1915            current_node: self.arena.current_node.clone(),
1916            backend: self.backend.clone(),
1917        });
1918
1919        // Evaluate
1920        let result = self.eval_inner(expr, env);
1921
1922        // Restore previous context state
1923        if context_was_set {
1924            if let Some(prev_ctx) = previous_context {
1925                set_evaluator_context(prev_ctx);
1926            }
1927        } else {
1928            clear_evaluator_context();
1929        }
1930
1931        result
1932    }
1933
1934    /// Inner eval implementation (separated to ensure context cleanup)
1935    fn eval_inner(&mut self, expr: Value, env: Gc<Environment>) -> EvalResult {
1936        match expr {
1937            // Self-evaluating literals
1938            Value::Nil => Ok(Value::Nil),
1939            Value::Bool(_) => Ok(expr),
1940            Value::Integer(_) => Ok(expr),
1941            Value::Real(_) => Ok(expr),
1942            Value::Quantity { .. } => Ok(expr),
1943            Value::Char(_) => Ok(expr),
1944            Value::String(_) => Ok(expr),
1945            Value::Procedure(_) => Ok(expr),
1946            Value::Vector(_) => Ok(expr), // Vectors are self-evaluating in R4RS
1947            Value::Unspecified => Ok(expr),
1948            Value::Error => Ok(expr),
1949
1950            // DSSSL types (self-evaluating for now)
1951            Value::Node(_) => Ok(expr),
1952            Value::NodeList(_) => Ok(expr),
1953            Value::Sosofo => Ok(expr),
1954
1955            // Symbols: variable lookup
1956            Value::Symbol(ref name) => {
1957                // First try environment lookup
1958                if let Some(val) = env.lookup(name) {
1959                    return Ok(val);
1960                }
1961
1962                // Fallback: check if this is a known primitive name
1963                if let Some(static_name) = self.get_primitive_static_name(name) {
1964                    // Return a marker procedure that will be recognized during application
1965                    // Use a dummy function - the real dispatch happens in apply_primitive
1966                    return Ok(Value::primitive(static_name, |_args| {
1967                        Err("Primitive should be dispatched through apply_primitive".to_string())
1968                    }));
1969                }
1970
1971                Err(self.error_with_stack(format!("Undefined variable: {}", name)))
1972            },
1973
1974            // Keywords are self-evaluating
1975            Value::Keyword(_) => Ok(expr),
1976
1977            // Lists: special forms or function application
1978            Value::Pair(_) => self.eval_list(expr, env),
1979        }
1980    }
1981
1982    /// Evaluate a list (special form or function call)
1983    fn eval_list(&mut self, expr: Value, env: Gc<Environment>) -> EvalResult {
1984        // Extract position from the pair if available and update current position
1985        if let Value::Pair(ref p) = expr {
1986            let pair_data = p.borrow();
1987            if let Some(ref pos) = pair_data.pos {
1988                // If we have line mappings, translate the position to source file coordinates
1989                if !self.line_mappings.is_empty() {
1990                    if let Some(mapping) = self.line_mappings.iter().find(|m| m.output_line == pos.line) {
1991                        self.current_source_file = Some(mapping.source_file.clone());
1992                        self.current_position = Some(Position {
1993                            line: mapping.source_line,
1994                            column: pos.column,
1995                        });
1996                    } else {
1997                        // No mapping found, use original position
1998                        self.current_position = Some(pos.clone());
1999                    }
2000                } else {
2001                    // No line mappings, use original position
2002                    self.current_position = Some(pos.clone());
2003                }
2004            }
2005        }
2006
2007        // Extract the operator (first element)
2008        let (operator, args) = self.list_car_cdr(&expr)?;
2009
2010        // Check if operator is a symbol (special form keyword)
2011        if let Value::Symbol(ref sym) = operator {
2012            match &**sym {
2013                "quote" => self.eval_quote(args),
2014                "if" => self.eval_if(args, env),
2015                "define" => self.eval_define(args, env),
2016                "set!" => self.eval_set(args, env),
2017                "lambda" => self.eval_lambda(args, env),
2018                "let" => self.eval_let(args, env),
2019                "let*" => self.eval_let_star(args, env),
2020                "letrec" => self.eval_letrec(args, env),
2021                "begin" => self.eval_begin(args, env),
2022                "cond" => self.eval_cond(args, env),
2023                "case" => self.eval_case(args, env),
2024                "and" => self.eval_and(args, env),
2025                "or" => self.eval_or(args, env),
2026                "apply" => self.eval_apply(args, env),
2027                "map" => self.eval_map(args, env),
2028                "for-each" => self.eval_for_each(args, env),
2029                "node-list-filter" => self.eval_node_list_filter(args, env),
2030                "node-list-map" => self.eval_node_list_map(args, env),
2031                "node-list-some?" => self.eval_node_list_some(args, env),
2032                "load" => self.eval_load(args, env),
2033
2034                // DSSSL special forms
2035                "define-unit" => self.eval_define_unit(args, env),
2036                "define-language" => self.eval_define_language(args, env),
2037                "declare-flow-object-class" => self.eval_declare_flow_object_class(args, env),
2038                "declare-characteristic" => self.eval_declare_characteristic(args, env),
2039                "declare-initial-value" => self.eval_declare_initial_value(args, env),
2040                "mode" => self.eval_mode(args, env),
2041                "with-mode" => self.eval_with_mode(args, env),
2042                "element" => self.eval_element(args, env),
2043                "default" => self.eval_default(args, env),
2044                "process-children" => self.eval_process_children(env),
2045                "process-children-trim" => self.eval_process_children_trim(env),
2046                "process-node-list" => self.eval_process_node_list(args, env),
2047                "make" => self.eval_make(args, env),
2048                "style" => self.eval_style(args, env),
2049
2050                // Not a special form - evaluate as function call
2051                _ => self.eval_application(operator, args, env),
2052            }
2053        } else {
2054            // Operator is not a symbol - evaluate and apply
2055            self.eval_application(operator, args, env)
2056        }
2057    }
2058
2059    /// Extract car and cdr from a list
2060    fn list_car_cdr(&self, list: &Value) -> Result<(Value, Value), EvalError> {
2061        if let Value::Pair(ref p) = list {
2062            let pair = p.borrow();
2063            Ok((pair.car.clone(), pair.cdr.clone()))
2064        } else {
2065            Err(EvalError::new("Expected list".to_string()))
2066        }
2067    }
2068
2069    /// Convert a Vec to a list
2070    fn vec_to_list(&self, vec: Vec<Value>) -> Value {
2071        let mut result = Value::Nil;
2072        for val in vec.iter().rev() {
2073            result = Value::cons(val.clone(), result);
2074        }
2075        result
2076    }
2077
2078    /// Convert a list to a Vec of elements
2079    pub fn list_to_vec(&self, list: Value) -> Result<Vec<Value>, EvalError> {
2080        let mut result = Vec::new();
2081        let mut current = list;
2082
2083        loop {
2084            match current {
2085                Value::Nil => break,
2086                Value::Pair(ref p) => {
2087                    let pair = p.borrow();
2088                    result.push(pair.car.clone());
2089                    let cdr = pair.cdr.clone();
2090                    drop(pair); // Explicitly drop borrow before reassigning
2091                    current = cdr;
2092                }
2093                _ => return Err(EvalError::new("Improper list".to_string())),
2094            }
2095        }
2096
2097        Ok(result)
2098    }
2099
2100    // =========================================================================
2101    // Special Forms
2102    // =========================================================================
2103
2104    /// (quote expr) → expr
2105    fn eval_quote(&mut self, args: Value) -> EvalResult {
2106        let args_vec = self.list_to_vec(args)?;
2107        if args_vec.len() != 1 {
2108            return Err(EvalError::new("quote requires exactly 1 argument".to_string()));
2109        }
2110        Ok(args_vec[0].clone())
2111    }
2112
2113    /// (if test consequent [alternate])
2114    fn eval_if(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2115        let args_vec = self.list_to_vec(args)?;
2116        if args_vec.len() < 2 || args_vec.len() > 3 {
2117            return Err(EvalError::new(
2118                "if requires 2 or 3 arguments".to_string(),
2119            ));
2120        }
2121
2122        let test = self.eval_inner(args_vec[0].clone(), env.clone())?;
2123
2124        if test.is_true() {
2125            self.eval_inner(args_vec[1].clone(), env)
2126        } else if args_vec.len() == 3 {
2127            self.eval_inner(args_vec[2].clone(), env)
2128        } else {
2129            Ok(Value::Unspecified)
2130        }
2131    }
2132
2133    /// (define name value) or (define (name params...) body...)
2134    fn eval_define(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2135        let args_vec = self.list_to_vec(args)?;
2136        if args_vec.len() < 2 {
2137            return Err(EvalError::new(
2138                "define requires at least 2 arguments".to_string(),
2139            ));
2140        }
2141
2142        // Check if first arg is a symbol or a list
2143        match &args_vec[0] {
2144            Value::Symbol(ref name) => {
2145                // Simple variable definition: (define x value)
2146                if args_vec.len() != 2 {
2147                    return Err(EvalError::new(
2148                        "define with symbol requires exactly 2 arguments".to_string(),
2149                    ));
2150                }
2151                let value = self.eval_inner(args_vec[1].clone(), env.clone())?;
2152                env.define(name, value);
2153                Ok(Value::Unspecified)
2154            }
2155
2156            Value::Pair(_) => {
2157                // Function definition: (define (name params...) body...)
2158                // This is syntactic sugar for: (define name (lambda (params...) body...))
2159                let (name_val, params) = self.list_car_cdr(&args_vec[0])?;
2160
2161                if let Value::Symbol(ref name) = name_val {
2162                    // Parse parameters (handles #!optional)
2163                    let (param_names, required_count, optional_defaults) =
2164                        self.parse_lambda_params(params)?;
2165
2166                    // Build body
2167                    let body = if args_vec.len() == 2 {
2168                        args_vec[1].clone()
2169                    } else {
2170                        let mut body_list = Value::Nil;
2171                        for expr in args_vec[1..].iter().rev() {
2172                            body_list = Value::cons(expr.clone(), body_list);
2173                        }
2174                        Value::cons(Value::symbol("begin"), body_list)
2175                    };
2176
2177                    // Create lambda with function name and source info
2178                    let source_info = self.current_source_file.as_ref().map(|file| {
2179                        use crate::scheme::parser::Position;
2180                        SourceInfo::new(file.clone(), Position::new())
2181                    });
2182
2183                    let lambda_value = if optional_defaults.is_empty() {
2184                        Value::lambda_with_source(
2185                            param_names,
2186                            body,
2187                            env.clone(),
2188                            source_info,
2189                            Some(name.to_string()),
2190                        )
2191                    } else {
2192                        Value::lambda_with_optional(
2193                            param_names,
2194                            required_count,
2195                            optional_defaults,
2196                            body,
2197                            env.clone(),
2198                            source_info,
2199                            Some(name.to_string()),
2200                        )
2201                    };
2202
2203                    env.define(name, lambda_value);
2204                    Ok(Value::Unspecified)
2205                } else {
2206                    Err(EvalError::new(
2207                        "First element of define must be a symbol".to_string(),
2208                    ))
2209                }
2210            }
2211
2212            _ => Err(EvalError::new(
2213                "First argument to define must be symbol or list".to_string(),
2214            )),
2215        }
2216    }
2217
2218    /// Evaluate (define-unit name value)
2219    /// DSSSL unit definition - defines a unit (em, pi, pt, etc.) as a quantity value
2220    /// Examples:
2221    ///   (define-unit em %bf-size%)
2222    ///   (define-unit pi (/ 1in 6))
2223    fn eval_define_unit(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2224        let args_vec = self.list_to_vec(args)?;
2225
2226        if args_vec.len() != 2 {
2227            return Err(EvalError::new(
2228                "define-unit requires exactly 2 arguments: name and value".to_string(),
2229            ));
2230        }
2231
2232        // First argument must be a symbol (unit name)
2233        if let Value::Symbol(ref name) = args_vec[0] {
2234            // Evaluate the value expression
2235            let value = self.eval_inner(args_vec[1].clone(), env.clone())?;
2236            // Define the unit name in the environment
2237            env.define(name, value);
2238            Ok(Value::Unspecified)
2239        } else {
2240            Err(EvalError::new(
2241                "First argument to define-unit must be a symbol".to_string(),
2242            ))
2243        }
2244    }
2245
2246    /// Evaluate (define-language name props...)
2247    /// DSSSL language definition - defines the language name as a symbol
2248    fn eval_define_language(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2249        let args_vec = self.list_to_vec(args)?;
2250
2251        if args_vec.is_empty() {
2252            return Err(EvalError::new(
2253                "define-language requires at least 1 argument".to_string(),
2254            ));
2255        }
2256
2257        // First argument must be a symbol (language name)
2258        if let Value::Symbol(ref name) = args_vec[0] {
2259            // Define the language name as a symbol bound to itself
2260            // This allows it to be used in (declare-default-language name)
2261            env.define(name, args_vec[0].clone());
2262            Ok(Value::Unspecified)
2263        } else {
2264            Err(EvalError::new(
2265                "First argument to define-language must be a symbol".to_string(),
2266            ))
2267        }
2268    }
2269
2270    /// Evaluate (declare-flow-object-class name public-id)
2271    /// DSSSL flow object class declaration - defines the class name as a symbol
2272    fn eval_declare_flow_object_class(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2273        let args_vec = self.list_to_vec(args)?;
2274
2275        if args_vec.is_empty() {
2276            return Err(EvalError::new(
2277                "declare-flow-object-class requires at least 1 argument".to_string(),
2278            ));
2279        }
2280
2281        // First argument must be a symbol (flow object class name)
2282        if let Value::Symbol(ref name) = args_vec[0] {
2283            // Define the class name as a symbol bound to itself
2284            // This allows it to be used in (make name ...) constructs
2285            env.define(name, args_vec[0].clone());
2286            Ok(Value::Unspecified)
2287        } else {
2288            Err(EvalError::new(
2289                "First argument to declare-flow-object-class must be a symbol".to_string(),
2290            ))
2291        }
2292    }
2293
2294    /// Evaluate (declare-characteristic name public-id default-value)
2295    /// DSSSL characteristic declaration - defines the characteristic with its default value
2296    fn eval_declare_characteristic(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2297        let args_vec = self.list_to_vec(args)?;
2298
2299        if args_vec.len() < 3 {
2300            return Err(EvalError::new(
2301                "declare-characteristic requires at least 3 arguments (name, public-id, default-value)".to_string(),
2302            ));
2303        }
2304
2305        // First argument must be a symbol (characteristic name)
2306        if let Value::Symbol(ref name) = args_vec[0] {
2307            // Third argument is the default value - evaluate it
2308            let default_value = self.eval(args_vec[2].clone(), env.clone())?;
2309
2310            // Define the characteristic name as a variable with its default value
2311            env.define(name, default_value);
2312            Ok(Value::Unspecified)
2313        } else {
2314            Err(EvalError::new(
2315                "First argument to declare-characteristic must be a symbol".to_string(),
2316            ))
2317        }
2318    }
2319
2320    /// Evaluate (declare-initial-value name value)
2321    /// DSSSL initial value declaration - sets the initial value for a characteristic
2322    /// Example: (declare-initial-value page-width 210mm)
2323    fn eval_declare_initial_value(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2324        let args_vec = self.list_to_vec(args)?;
2325
2326        if args_vec.len() != 2 {
2327            return Err(EvalError::new(
2328                "declare-initial-value requires exactly 2 arguments (name and value)".to_string(),
2329            ));
2330        }
2331
2332        // First argument must be a symbol (characteristic name)
2333        if let Value::Symbol(ref name) = args_vec[0] {
2334            // Second argument is the value - evaluate it
2335            let value = self.eval_inner(args_vec[1].clone(), env.clone())?;
2336
2337            // Define the characteristic name as a variable with its value
2338            env.define(name, value);
2339            Ok(Value::Unspecified)
2340        } else {
2341            Err(EvalError::new(
2342                "First argument to declare-initial-value must be a symbol".to_string(),
2343            ))
2344        }
2345    }
2346
2347    /// DSSSL element construction rule (OpenJade SchemeParser::doElement)
2348    /// Syntax: (element element-pattern construction-expression)
2349    ///
2350    /// Element pattern can be:
2351    /// - A symbol: (element foo ...)
2352    /// - A list for context matching: (element (parent child) ...)
2353    ///
2354    /// Stores the rule in processing mode WITHOUT evaluating the body.
2355    /// The body will be evaluated later during tree processing when a matching element is found.
2356    fn eval_element(&mut self, args: Value, _env: Gc<Environment>) -> EvalResult {
2357        let args_vec = self.list_to_vec(args)?;
2358
2359        if args_vec.len() < 2 {
2360            return Err(self.error_with_stack(
2361                "element requires at least 2 arguments (element-pattern and construction-expression)".to_string(),
2362            ));
2363        }
2364
2365        // First argument is the element pattern (symbol or list)
2366        // For context matching like (parent child), extract the last element and context
2367        let (element_name, context) = match &args_vec[0] {
2368            Value::Symbol(ref name) => (name.clone(), Vec::new()),
2369            Value::Pair(_) => {
2370                // List pattern like (parent child) - extract context and element
2371                let pattern_list = self.list_to_vec(args_vec[0].clone())?;
2372                if pattern_list.is_empty() {
2373                    return Err(self.error_with_stack(
2374                        "Element pattern list cannot be empty".to_string(),
2375                    ));
2376                }
2377                // Last element in the list is the actual element being matched
2378                let element_name = if let Value::Symbol(ref name) = pattern_list[pattern_list.len() - 1] {
2379                    name.clone()
2380                } else {
2381                    return Err(self.error_with_stack(
2382                        "Element pattern must contain only symbols".to_string(),
2383                    ));
2384                };
2385                // Elements before the last one are the context (parent chain)
2386                let mut context = Vec::new();
2387                for i in 0..pattern_list.len() - 1 {
2388                    if let Value::Symbol(ref parent_name) = pattern_list[i] {
2389                        context.push(parent_name.to_string());
2390                    } else {
2391                        return Err(self.error_with_stack(
2392                            "Element pattern must contain only symbols".to_string(),
2393                        ));
2394                    }
2395                }
2396                (element_name, context)
2397            }
2398            _ => {
2399                return Err(self.error_with_stack(
2400                    "First argument to element must be a symbol or list of symbols".to_string(),
2401                ));
2402            }
2403        };
2404
2405        // Remaining arguments are the construction expressions
2406        // OpenJade behavior: error on multiple expressions for better error detection
2407        // (suggest using sosofo-append explicitly)
2408        if args_vec.len() > 2 {
2409            return Err(self.error_with_stack(
2410                "element can only contain one sosofo expression. Use (sosofo-append ...) to combine multiple sosofos".to_string(),
2411            ));
2412        }
2413
2414        let construction_expr = args_vec[1].clone();
2415
2416        // Store the construction expression for later evaluation
2417        // Capture the current source position (where the 'element' form is)
2418        // Add the rule to the current mode
2419        let mode_name = self.current_mode.clone();
2420        self.mode_manager.get_or_create_mode(&mode_name).add_rule(
2421            element_name.to_string(),
2422            context,
2423            construction_expr,
2424            self.current_source_file.clone(),
2425            self.current_position.clone()
2426        );
2427
2428        Ok(Value::Unspecified)
2429    }
2430
2431    /// DSSSL default construction rule
2432    /// Syntax: (default construction-expression)
2433    ///
2434    /// Defines a default rule that applies to all elements that don't have a specific rule.
2435    /// This is the catch-all rule.
2436    fn eval_default(&mut self, args: Value, _env: Gc<Environment>) -> EvalResult {
2437        let args_vec = self.list_to_vec(args)?;
2438
2439        if args_vec.is_empty() {
2440            return Err(self.error_with_stack(
2441                "default requires at least 1 argument (construction-expression)".to_string(),
2442            ));
2443        }
2444
2445        // OpenJade behavior: error on multiple expressions for better error detection
2446        // (suggest using sosofo-append explicitly)
2447        if args_vec.len() > 1 {
2448            return Err(self.error_with_stack(
2449                "default can only contain one sosofo expression. Use (sosofo-append ...) to combine multiple sosofos".to_string(),
2450            ));
2451        }
2452
2453        let construction_expr = args_vec[0].clone();
2454
2455        // Store the default rule in the current mode
2456        let mode_name = self.current_mode.clone();
2457        self.mode_manager.get_or_create_mode(&mode_name).add_default_rule(construction_expr);
2458
2459        Ok(Value::Unspecified)
2460    }
2461
2462    /// DSSSL mode definition
2463    /// Syntax: (mode mode-name rule1 rule2 ...)
2464    ///
2465    /// Defines a named processing mode with its own construction rules.
2466    /// All element and default rules within the mode body are added to the specified mode.
2467    fn eval_mode(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2468        let args_vec = self.list_to_vec(args)?;
2469
2470        if args_vec.is_empty() {
2471            return Err(self.error_with_stack(
2472                "mode requires at least 1 argument (mode-name)".to_string(),
2473            ));
2474        }
2475
2476        // First argument is the mode name (symbol)
2477        let mode_name = if let Value::Symbol(ref name) = args_vec[0] {
2478            name.clone()
2479        } else {
2480            return Err(self.error_with_stack(
2481                "First argument to mode must be a symbol".to_string(),
2482            ));
2483        };
2484
2485        // Save the current mode
2486        let saved_mode = self.current_mode.clone();
2487
2488        // Switch to the new mode
2489        self.current_mode = mode_name.to_string();
2490
2491        // Evaluate all the body expressions (element/default definitions)
2492        let mut result = Value::Unspecified;
2493        for expr in args_vec.iter().skip(1) {
2494            result = self.eval(expr.clone(), env.clone())?;
2495        }
2496
2497        // Restore the previous mode
2498        self.current_mode = saved_mode;
2499
2500        Ok(result)
2501    }
2502
2503    /// DSSSL with-mode - temporarily switch processing mode
2504    /// Syntax: (with-mode mode-name expr)
2505    ///
2506    /// Evaluates expr with the processing mode temporarily switched to mode-name.
2507    /// Rules are looked up in the specified mode during processing.
2508    fn eval_with_mode(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2509        let args_vec = self.list_to_vec(args)?;
2510
2511        if args_vec.len() < 2 {
2512            return Err(self.error_with_stack(
2513                "with-mode requires 2 arguments (mode-name and expression)".to_string(),
2514            ));
2515        }
2516
2517        // First argument is the mode name (symbol)
2518        let mode_name = if let Value::Symbol(ref name) = args_vec[0] {
2519            name.clone()
2520        } else {
2521            return Err(self.error_with_stack(
2522                "First argument to with-mode must be a symbol".to_string(),
2523            ));
2524        };
2525
2526        // Save the current processing mode
2527        let saved_processing_mode = self.current_processing_mode.clone();
2528
2529        // Switch to the new processing mode
2530        self.current_processing_mode = mode_name.to_string();
2531
2532        // Evaluate the expression in the new mode
2533        let result = self.eval(args_vec[1].clone(), env);
2534
2535        // Restore the previous processing mode
2536        self.current_processing_mode = saved_processing_mode;
2537
2538        result
2539    }
2540
2541    /// DSSSL process-children (OpenJade ProcessContext::processChildren)
2542    /// Syntax: (process-children)
2543    ///
2544    /// Processes all children of the current node.
2545    /// For each child, matches construction rules and evaluates them.
2546    fn eval_process_children(&mut self, env: Gc<Environment>) -> EvalResult {
2547        // Get current node
2548        let current_node = match self.current_node() {
2549            Some(node) => node.clone(),
2550            None => return Err(EvalError::new("No current node".to_string())),
2551        };
2552
2553        // Get ALL children (including text nodes)
2554        // Note: all_children() returns elements AND text, not just elements like children()
2555        let mut children = current_node.all_children();
2556
2557        // Process each child (using DSSSL node-list iteration pattern)
2558        let mut result = Value::Unspecified;
2559
2560        while !children.is_empty() {
2561            // Get first child
2562            if let Some(child_node) = children.first() {
2563                // Save current node
2564                let saved_node = self.current_node();
2565
2566                // Set child as current node
2567                self.set_current_node(child_node);
2568
2569                // Process the child node
2570                result = self.process_node(env.clone())?;
2571
2572                // Restore current node
2573                self.restore_current_node(saved_node);
2574            }
2575
2576            // Move to rest of children
2577            children = children.rest();
2578        }
2579
2580        Ok(result)
2581    }
2582
2583    /// DSSSL (process-children-trim)
2584    ///
2585    /// OpenJade semantics: Process children with whitespace trimming
2586    /// - Text nodes are output directly to backend (not through rules)
2587    /// - Leading whitespace is trimmed from first text node
2588    /// - Trailing whitespace is trimmed from last text node
2589    /// - Element nodes are processed through rules normally
2590    fn eval_process_children_trim(&mut self, env: Gc<Environment>) -> EvalResult {
2591        // Get current node
2592        let current_node = match self.current_node() {
2593            Some(node) => node.clone(),
2594            None => return Err(EvalError::new("No current node".to_string())),
2595        };
2596
2597        // Get ALL children (including text nodes)
2598        let mut children = current_node.all_children();
2599
2600        // Collect all children into a vector to support trimming
2601        let mut child_nodes = Vec::new();
2602        while !children.is_empty() {
2603            if let Some(child) = children.first() {
2604                child_nodes.push(child);
2605            }
2606            children = children.rest();
2607        }
2608
2609        if child_nodes.is_empty() {
2610            return Ok(Value::Unspecified);
2611        }
2612
2613        // Track position for trimming
2614        let mut at_start = true;
2615
2616        // Process each child
2617        for (index, child_node) in child_nodes.iter().enumerate() {
2618            let is_last = index == child_nodes.len() - 1;
2619
2620            if child_node.is_text() {
2621                // Text node: output directly with trimming
2622                if let Some(mut text) = child_node.data() {
2623                    // Trim leading whitespace from first text node
2624                    if at_start {
2625                        let trimmed = text.trim_start();
2626                        if trimmed.is_empty() {
2627                            // Skip whitespace-only nodes at start
2628                            continue;
2629                        }
2630                        text = trimmed.to_string();
2631                        at_start = false;
2632                    }
2633
2634                    // Trim trailing whitespace from last text node
2635                    if is_last {
2636                        text = text.trim_end().to_string();
2637                    }
2638
2639                    if !text.is_empty() {
2640                        // Output text directly to backend
2641                        if let Some(ref backend) = self.backend {
2642                            backend.borrow_mut().literal(&text)
2643                                .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2644                        }
2645                    }
2646                }
2647            } else if child_node.is_element() {
2648                // Element node: mark that we're no longer at start
2649                at_start = false;
2650
2651                // Process through rules
2652                let saved_node = self.current_node();
2653                self.set_current_node(child_node.clone_node());
2654                let _result = self.process_node(env.clone())?;
2655                self.restore_current_node(saved_node);
2656            }
2657        }
2658
2659        Ok(Value::Unspecified)
2660    }
2661
2662    /// DSSSL (process-node-list node-list)
2663    /// Syntax: (process-node-list node-list)
2664    ///
2665    /// Processes all nodes in the given node-list.
2666    /// For each node, matches construction rules and evaluates them.
2667    fn eval_process_node_list(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2668        let args_vec = self.list_to_vec(args)?;
2669        if args_vec.len() != 1 {
2670            return Err(EvalError::new(
2671                "process-node-list requires exactly 1 argument".to_string(),
2672            ));
2673        }
2674
2675        // Evaluate the argument to get the node-list
2676        let node_list_value = self.eval(args_vec[0].clone(), env.clone())?;
2677
2678        // Get the node-list (auto-convert single node to singleton node-list)
2679        let mut nodes = match node_list_value {
2680            Value::NodeList(ref nl) => nl.clone(),
2681            Value::Node(ref n) => {
2682                // Auto-convert single node to singleton node-list
2683                // n is Rc<Box<dyn Node>>, we need Vec<Box<dyn Node>>
2684                let node_box: Box<dyn crate::grove::Node> = (**n).clone_node();
2685                Rc::new(Box::new(crate::grove::VecNodeList::new(vec![node_box])) as Box<dyn crate::grove::NodeList>)
2686            }
2687            _ => {
2688                return Err(EvalError::new(format!(
2689                    "process-node-list: not a node-list: {:?}",
2690                    node_list_value
2691                )))
2692            }
2693        };
2694
2695        // Process each node (using DSSSL node-list iteration pattern)
2696        let mut result = Value::Unspecified;
2697        while !nodes.is_empty() {
2698            // Get first node
2699            if let Some(node) = nodes.first() {
2700                // Save current node
2701                let saved_node = self.current_node();
2702
2703                // Set this node as current node
2704                self.set_current_node(node);
2705
2706                // Process the node
2707                result = self.process_node(env.clone())?;
2708
2709                // Restore current node
2710                self.restore_current_node(saved_node);
2711            }
2712
2713            // Move to rest of nodes
2714            nodes = Rc::new(nodes.rest());
2715        }
2716
2717        Ok(result)
2718    }
2719
2720    /// DSSSL make flow object (OpenJade FotBuilder)
2721    /// Syntax: (make flow-object-type keyword: value ... body-sosofo)
2722    ///
2723    /// Creates flow objects and writes them to the backend.
2724    /// Supports: entity, formatting-instruction
2725    fn eval_make(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
2726        let args_vec = self.list_to_vec(args)?;
2727
2728        if args_vec.is_empty() {
2729            return Err(EvalError::new(
2730                "make requires at least a flow object type".to_string(),
2731            ));
2732        }
2733
2734        // First argument is the flow object type (symbol)
2735        let fo_type = match &args_vec[0] {
2736            Value::Symbol(s) => s.as_ref(),
2737            _ => return Err(EvalError::new(
2738                "make: first argument must be a flow object type symbol".to_string(),
2739            )),
2740        };
2741
2742        // Parse keyword arguments and collect body expressions
2743        let mut i = 1;
2744        let mut system_id = None;
2745        let mut data = None;
2746        let mut path = None;
2747        let mut gi = None;
2748        let mut attributes = None;
2749        let mut body_exprs = Vec::new();
2750
2751        while i < args_vec.len() {
2752            match &args_vec[i] {
2753                Value::Keyword(kw) => {
2754                    // Next argument is the keyword value
2755                    if i + 1 >= args_vec.len() {
2756                        return Err(EvalError::new(
2757                            format!("make: keyword {} requires a value", kw),
2758                        ));
2759                    }
2760
2761                    // Debug: Log keyword processing
2762                    if std::env::var("DAZZLE_DEBUG").is_ok() {
2763                        eprintln!("EVAL_MAKE: Processing keyword '{}:', value expr = {:?}",
2764                                  kw, args_vec[i + 1]);
2765                    }
2766
2767                    let value = self.eval(args_vec[i + 1].clone(), env.clone())?;
2768
2769                    // Debug: Log evaluated value
2770                    if std::env::var("DAZZLE_DEBUG").is_ok() {
2771                        eprintln!("EVAL_MAKE: Keyword '{}' evaluated to {:?}", kw, value);
2772                    }
2773
2774                    match kw.as_ref() {
2775                        "system-id" => {
2776                            if let Value::String(s) = value {
2777                                system_id = Some(s);
2778                            } else {
2779                                return Err(EvalError::new(
2780                                    "make: system-id must be a string".to_string(),
2781                                ));
2782                            }
2783                        }
2784                        "data" => {
2785                            if let Value::String(s) = value {
2786                                data = Some(s);
2787                            } else {
2788                                return Err(EvalError::new(
2789                                    format!("make: data must be a string, got {:?}", value),
2790                                ));
2791                            }
2792                        }
2793                        "path" => {
2794                            if let Value::String(s) = value {
2795                                path = Some(s);
2796                            } else {
2797                                return Err(EvalError::new(
2798                                    "make: path must be a string".to_string(),
2799                                ));
2800                            }
2801                        }
2802                        "gi" => {
2803                            if let Value::String(s) = value {
2804                                gi = Some(s);
2805                            } else {
2806                                return Err(EvalError::new(
2807                                    "make element: gi must be a string".to_string(),
2808                                ));
2809                            }
2810                        }
2811                        "attributes" => {
2812                            // attributes can be a list or #f
2813                            attributes = Some(value);
2814                        }
2815                        _ => {
2816                            // Ignore unknown keywords for now
2817                        }
2818                    }
2819                    i += 2;
2820                }
2821                _ => {
2822                    // Non-keyword argument - collect as body expression
2823                    body_exprs.push(args_vec[i].clone());
2824                    i += 1;
2825                }
2826            }
2827        }
2828
2829        // Call backend method based on flow object type
2830        let backend = self.backend.clone();
2831        match backend {
2832            Some(ref backend) => {
2833                match fo_type {
2834                    "entity" => {
2835                        if let Some(sid) = system_id {
2836                            // Save current buffer (for nested entities)
2837                            let saved_buffer = backend.borrow().current_output().to_string();
2838                            backend.borrow_mut().clear_buffer();
2839
2840                            // Evaluate body expressions (they append to buffer)
2841                            for expr in body_exprs {
2842                                self.eval(expr, env.clone())?;
2843                            }
2844
2845                            // Get current buffer content and write to file
2846                            let content = backend.borrow().current_output().to_string();
2847                            backend.borrow_mut().entity(&sid, &content)
2848                                .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2849
2850                            // Restore saved buffer (for parent entity context)
2851                            backend.borrow_mut().clear_buffer();
2852                            backend.borrow_mut().formatting_instruction(&saved_buffer)
2853                                .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2854                        } else {
2855                            return Err(EvalError::new(
2856                                "make entity requires system-id: keyword".to_string(),
2857                            ));
2858                        }
2859                    }
2860                    "formatting-instruction" => {
2861                        if let Some(d) = data {
2862                            // Append to current buffer
2863                            backend.borrow_mut().formatting_instruction(&d)
2864                                .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2865                        } else {
2866                            return Err(EvalError::new(
2867                                "make formatting-instruction requires data: keyword".to_string(),
2868                            ));
2869                        }
2870                    }
2871                    "literal" => {
2872                        // literal is typically called as (literal "text") not (make literal ...)
2873                        // but we support both forms for completeness
2874                        if let Some(d) = data {
2875                            backend.borrow_mut().formatting_instruction(&d)
2876                                .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2877                        } else {
2878                            return Err(EvalError::new(
2879                                "make literal requires data: keyword or a string body".to_string(),
2880                            ));
2881                        }
2882                    }
2883                    "directory" => {
2884                        if let Some(p) = path {
2885                            // Save current directory context
2886                            let prev_dir = backend.borrow().current_directory().map(|s| s.to_string());
2887
2888                            // Create directory and set as current context
2889                            backend.borrow_mut().directory(&p)
2890                                .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2891
2892                            // Evaluate body expressions in the new directory context
2893                            // (nested entities/directories will be created relative to this directory)
2894                            for expr in body_exprs {
2895                                self.eval(expr, env.clone())?;
2896                            }
2897
2898                            // Restore previous directory context
2899                            backend.borrow_mut().set_current_directory(prev_dir);
2900                        } else {
2901                            return Err(EvalError::new(
2902                                "make directory requires path: keyword".to_string(),
2903                            ));
2904                        }
2905                    }
2906                    "sequence" => {
2907                        // Sequence evaluates all body expressions in order
2908                        // This is the primary composition mechanism for flow objects
2909                        for expr in body_exprs {
2910                            self.eval(expr, env.clone())?;
2911                        }
2912                    }
2913                    "element" => {
2914                        // OpenJade extension: (make element gi: "name" attributes: '(("key" "val")) body...)
2915                        // Outputs: <name\nkey="val"\n>body</name\n>
2916                        //
2917                        // This is a compound flow object that generates HTML-like tags
2918                        // with OpenJade's special formatting (newline after tag name and each attribute)
2919
2920                        let gi = gi.ok_or_else(|| EvalError::new(
2921                            "make element requires gi: keyword".to_string()
2922                        ))?;
2923
2924                        // Start tag with newline after tag name
2925                        backend.borrow_mut().formatting_instruction(&format!("<{}\n", gi))
2926                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2927
2928                        // Add attributes if present, each on its own line
2929                        if let Some(attrs_val) = attributes {
2930                            // attributes should be a list of (name value) pairs
2931                            let attrs_list = self.list_to_vec(attrs_val)?;
2932                            for attr_pair in attrs_list {
2933                                let pair_vec = self.list_to_vec(attr_pair)?;
2934                                if pair_vec.len() == 2 {
2935                                    if let (Value::String(name), Value::String(value)) =
2936                                        (&pair_vec[0], &pair_vec[1]) {
2937                                        // Escape special characters in attribute value
2938                                        let escaped_value = value
2939                                            .replace('&', "&amp;")
2940                                            .replace('"', "&quot;")
2941                                            .replace('<', "&lt;")
2942                                            .replace('>', "&gt;");
2943                                        backend.borrow_mut().formatting_instruction(&format!("{}=\"{}\"\n", name, escaped_value))
2944                                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2945                                    }
2946                                }
2947                            }
2948                        }
2949
2950                        // Closing > for opening tag
2951                        backend.borrow_mut().formatting_instruction(">")
2952                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2953
2954                        // Evaluate body expressions
2955                        for expr in body_exprs {
2956                            self.eval(expr, env.clone())?;
2957                        }
2958
2959                        // End tag with OpenJade's line break pattern
2960                        backend.borrow_mut().formatting_instruction(&format!("</{}\n>", gi))
2961                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2962                    }
2963                    "paragraph" => {
2964                        // RTF paragraph flow object
2965                        // Start paragraph
2966                        backend.borrow_mut().start_paragraph()
2967                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2968
2969                        // Evaluate body expressions (literal text, etc.)
2970                        for expr in body_exprs {
2971                            self.eval(expr, env.clone())?;
2972                        }
2973
2974                        // End paragraph
2975                        backend.borrow_mut().end_paragraph()
2976                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2977                    }
2978                    "display-group" => {
2979                        // RTF display-group flow object
2980                        backend.borrow_mut().start_display_group()
2981                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2982
2983                        for expr in body_exprs {
2984                            self.eval(expr, env.clone())?;
2985                        }
2986
2987                        backend.borrow_mut().end_display_group()
2988                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2989                    }
2990                    "simple-page-sequence" => {
2991                        // RTF simple-page-sequence flow object (main page container)
2992                        backend.borrow_mut().start_simple_page_sequence()
2993                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
2994
2995                        for expr in body_exprs {
2996                            self.eval(expr, env.clone())?;
2997                        }
2998
2999                        backend.borrow_mut().end_simple_page_sequence()
3000                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
3001                    }
3002                    "line-field" => {
3003                        // RTF line-field flow object (inline text container)
3004                        backend.borrow_mut().start_line_field()
3005                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
3006
3007                        for expr in body_exprs {
3008                            self.eval(expr, env.clone())?;
3009                        }
3010
3011                        backend.borrow_mut().end_line_field()
3012                            .map_err(|e| EvalError::new(format!("Backend error: {}", e)))?;
3013                    }
3014                    "link" | "scroll" | "marginalia" | "leader" | "table" | "table-row" | "table-cell" | "table-column" | "table-part" | "paragraph-break" => {
3015                        // Flow objects that just process their children
3016                        // For SGML backend (code gen), we ignore these formatting constructs
3017                        for expr in body_exprs {
3018                            self.eval(expr, env.clone())?;
3019                        }
3020                    }
3021                    _ => {
3022                        // Unknown flow object type - error
3023                        return Err(EvalError::new(
3024                            format!("make: unknown flow object type '{}'", fo_type),
3025                        ));
3026                    }
3027                }
3028            }
3029            None => {
3030                return Err(EvalError::new(
3031                    "make: no backend available".to_string(),
3032                ));
3033            }
3034        }
3035
3036        // Flow objects (make forms) always return a Sosofo
3037        Ok(Value::Sosofo)
3038    }
3039
3040    /// (style keyword: value ...)
3041    ///
3042    /// Stub for DSSSL style objects used in document formatting.
3043    /// Dazzle focuses on code generation, so this returns a dummy value.
3044    fn eval_style(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3045        // Parse keyword arguments (but ignore them)
3046        let args_vec = self.list_to_vec(args)?;
3047        let mut i = 0;
3048
3049        while i < args_vec.len() {
3050            match &args_vec[i] {
3051                Value::Keyword(_kw) => {
3052                    // Skip keyword and its value
3053                    if i + 1 >= args_vec.len() {
3054                        return Err(EvalError::new(
3055                            "style: keyword requires a value".to_string(),
3056                        ));
3057                    }
3058                    // Evaluate the value (to check for errors) but don't use it
3059                    let _value = self.eval(args_vec[i + 1].clone(), env.clone())?;
3060                    i += 2;
3061                }
3062                _ => {
3063                    return Err(EvalError::new(
3064                        format!("style: unexpected argument {:?}", args_vec[i]),
3065                    ));
3066                }
3067            }
3068        }
3069
3070        // Return a dummy style object (we don't use it for code generation)
3071        Ok(Value::Symbol(Rc::from("dummy-style")))
3072    }
3073
3074    /// (set! name value)
3075    fn eval_set(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3076        let args_vec = self.list_to_vec(args)?;
3077        if args_vec.len() != 2 {
3078            return Err(EvalError::new(
3079                "set! requires exactly 2 arguments".to_string(),
3080            ));
3081        }
3082
3083        if let Value::Symbol(ref name) = args_vec[0] {
3084            let value = self.eval(args_vec[1].clone(), env.clone())?;
3085            env.set(name, value)
3086                .map_err(|e| EvalError::new(e))?;
3087            Ok(Value::Unspecified)
3088        } else {
3089            Err(EvalError::new(
3090                "First argument to set! must be a symbol".to_string(),
3091            ))
3092        }
3093    }
3094
3095    /// (lambda (params...) body...)
3096    fn eval_lambda(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3097        let args_vec = self.list_to_vec(args)?;
3098        if args_vec.len() < 2 {
3099            return Err(EvalError::new(
3100                "lambda requires at least 2 arguments (params and body)".to_string(),
3101            ));
3102        }
3103
3104        // Parse parameter list (handles #!optional parameters)
3105        let params_list = &args_vec[0];
3106        let (param_names, required_count, optional_defaults) =
3107            self.parse_lambda_params(params_list.clone())?;
3108
3109        // Extract body (one or more expressions)
3110        let body = if args_vec.len() == 2 {
3111            // Single body expression
3112            args_vec[1].clone()
3113        } else {
3114            // Multiple body expressions - wrap in (begin ...)
3115            let mut body_list = Value::Nil;
3116            for expr in args_vec[1..].iter().rev() {
3117                body_list = Value::cons(expr.clone(), body_list);
3118            }
3119            Value::cons(Value::symbol("begin"), body_list)
3120        };
3121
3122        // Create lambda closure capturing current environment and source location
3123        // current_position has been set by eval_list to the position of the (lambda ...) expression
3124        let source_info = match (&self.current_source_file, &self.current_position) {
3125            (Some(file), Some(pos)) => {
3126                // Clone the position since we'll be mutating current_position later
3127                Some(SourceInfo::new(file.clone(), pos.clone()))
3128            }
3129            (Some(file), None) => {
3130                use crate::scheme::parser::Position;
3131                Some(SourceInfo::new(file.clone(), Position::new()))
3132            }
3133            _ => None,
3134        };
3135
3136        // Create lambda with optional parameters if present
3137        if optional_defaults.is_empty() {
3138            Ok(Value::lambda_with_source(param_names, body, env, source_info, None))
3139        } else {
3140            Ok(Value::lambda_with_optional(
3141                param_names,
3142                required_count,
3143                optional_defaults,
3144                body,
3145                env,
3146                source_info,
3147                None,
3148            ))
3149        }
3150    }
3151
3152    /// Parse lambda parameter list, handling #!optional parameters
3153    ///
3154    /// Returns: (param_names, required_count, optional_defaults)
3155    fn parse_lambda_params(
3156        &mut self,
3157        params: Value,
3158    ) -> Result<(Vec<String>, usize, Vec<Value>), EvalError> {
3159        if params.is_nil() {
3160            return Ok((Vec::new(), 0, Vec::new()));
3161        }
3162
3163        let params_vec = self.list_to_vec(params)?;
3164        let mut param_names = Vec::new();
3165        let mut required_count = 0;
3166        let mut optional_defaults = Vec::new();
3167        let mut in_optional = false;
3168
3169        for param in params_vec {
3170            // Check for #!optional marker
3171            if let Value::Symbol(ref sym) = param {
3172                if sym.as_ref() == "#!optional" {
3173                    in_optional = true;
3174                    continue;
3175                }
3176            }
3177
3178            if !in_optional {
3179                // Required parameter - must be a symbol
3180                if let Value::Symbol(ref name) = param {
3181                    param_names.push(name.to_string());
3182                    required_count += 1;
3183                } else {
3184                    return Err(EvalError::new(format!(
3185                        "Parameter must be a symbol, got: {:?}",
3186                        param
3187                    )));
3188                }
3189            } else {
3190                // Optional parameter - can be symbol or (symbol default)
3191                match param {
3192                    Value::Symbol(ref name) => {
3193                        // Optional with no default: use #<unspecified>
3194                        param_names.push(name.to_string());
3195                        optional_defaults.push(Value::Unspecified);
3196                    }
3197                    Value::Pair(_) => {
3198                        // (name default-expr)
3199                        let opt_list = self.list_to_vec(param)?;
3200                        if opt_list.len() != 2 {
3201                            return Err(EvalError::new(format!(
3202                                "Optional parameter must be (name default), got list of length {}",
3203                                opt_list.len()
3204                            )));
3205                        }
3206
3207                        if let Value::Symbol(ref name) = opt_list[0] {
3208                            param_names.push(name.to_string());
3209                            // Store the unevaluated default expression
3210                            if std::env::var("DEBUG_OPTIONAL").is_ok() {
3211                                eprintln!("[DEBUG_OPTIONAL] Storing default for param '{}': {:?}", name, opt_list[1]);
3212                            }
3213                            optional_defaults.push(opt_list[1].clone());
3214                        } else {
3215                            return Err(EvalError::new(format!(
3216                                "Optional parameter name must be a symbol, got: {:?}",
3217                                opt_list[0]
3218                            )));
3219                        }
3220                    }
3221                    _ => {
3222                        return Err(EvalError::new(format!(
3223                            "Optional parameter must be symbol or (symbol default), got: {:?}",
3224                            param
3225                        )));
3226                    }
3227                }
3228            }
3229        }
3230
3231        Ok((param_names, required_count, optional_defaults))
3232    }
3233
3234    /// (let ((var val)...) body...)
3235    fn eval_let(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3236        let args_vec = self.list_to_vec(args)?;
3237        if args_vec.len() < 2 {
3238            return Err(EvalError::new(
3239                "let requires at least 2 arguments".to_string(),
3240            ));
3241        }
3242
3243        // Check if this is named let: (let name ((var val)...) body...)
3244        if let Value::Symbol(ref loop_name) = args_vec[0] {
3245            if args_vec.len() < 3 {
3246                return Err(EvalError::new(
3247                    "named let requires at least 3 arguments".to_string(),
3248                ));
3249            }
3250
3251            // Named let: transform to (letrec ((name (lambda (vars...) body...))) (name vals...))
3252            let bindings_list = &args_vec[1];
3253            let bindings = self.list_to_vec(bindings_list.clone())?;
3254            let body = &args_vec[2..];
3255
3256            // Extract variable names and initial values
3257            let mut var_names = Vec::new();
3258            let mut init_values = Vec::new();
3259            for binding in &bindings {
3260                let binding_vec = self.list_to_vec(binding.clone())?;
3261                if binding_vec.len() != 2 {
3262                    return Err(EvalError::new(
3263                        "named let binding must have exactly 2 elements".to_string(),
3264                    ));
3265                }
3266                var_names.push(binding_vec[0].clone());
3267                init_values.push(binding_vec[1].clone());
3268            }
3269
3270            // Create lambda: (lambda (vars...) body...)
3271            let lambda_params = self.vec_to_list(var_names);
3272            let mut lambda_body = vec![Value::symbol("lambda"), lambda_params];
3273            lambda_body.extend_from_slice(body);
3274            let lambda_expr = self.vec_to_list(lambda_body);
3275
3276            // Create letrec binding: ((name (lambda ...)))
3277            let letrec_binding = Value::cons(
3278                Value::symbol(loop_name),
3279                Value::cons(lambda_expr, Value::Nil),
3280            );
3281            let letrec_bindings = Value::cons(letrec_binding, Value::Nil);
3282
3283            // Create function call: (name vals...)
3284            let mut call_expr = vec![Value::symbol(loop_name)];
3285            call_expr.extend_from_slice(&init_values);
3286            let call = self.vec_to_list(call_expr);
3287
3288            // Evaluate: (letrec ((name (lambda ...))) (name vals...))
3289            return self.eval_letrec(self.vec_to_list(vec![letrec_bindings, call]), env);
3290        }
3291
3292        // Standard let: (let ((var val)...) body...)
3293        let bindings_list = &args_vec[0];
3294        let bindings = self.list_to_vec(bindings_list.clone())?;
3295
3296        // Create new environment extending current
3297        let new_env = Environment::extend(env.clone());
3298
3299        // Evaluate bindings in OLD environment, define in NEW environment
3300        for binding in bindings {
3301            let binding_vec = self.list_to_vec(binding)?;
3302            if binding_vec.len() != 2 {
3303                return Err(EvalError::new(
3304                    "let binding must have exactly 2 elements".to_string(),
3305                ));
3306            }
3307
3308            if let Value::Symbol(ref name) = binding_vec[0] {
3309                let value = self.eval_inner(binding_vec[1].clone(), env.clone())?;
3310                new_env.define(name, value);
3311            } else {
3312                return Err(EvalError::new(
3313                    "Binding variable must be a symbol".to_string(),
3314                ));
3315            }
3316        }
3317
3318        // Evaluate body in new environment
3319        let body = &args_vec[1..];
3320        self.eval_sequence(body, new_env)
3321    }
3322
3323    /// (let* ((var val)...) body...)
3324    fn eval_let_star(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3325        let args_vec = self.list_to_vec(args)?;
3326        if args_vec.len() < 2 {
3327            return Err(EvalError::new(
3328                "let* requires at least 2 arguments".to_string(),
3329            ));
3330        }
3331
3332        // Parse bindings
3333        let bindings_list = &args_vec[0];
3334        let bindings = self.list_to_vec(bindings_list.clone())?;
3335
3336        // Create new environment
3337        let current_env = Environment::extend(env);
3338
3339        // Evaluate bindings sequentially in CURRENT environment
3340        for binding in bindings {
3341            let binding_vec = self.list_to_vec(binding)?;
3342            if binding_vec.len() != 2 {
3343                return Err(EvalError::new(
3344                    "let* binding must have exactly 2 elements".to_string(),
3345                ));
3346            }
3347
3348            if let Value::Symbol(ref name) = binding_vec[0] {
3349                let value = self.eval_inner(binding_vec[1].clone(), current_env.clone())?;
3350                current_env.define(name, value);
3351            } else {
3352                return Err(EvalError::new(
3353                    "Binding variable must be a symbol".to_string(),
3354                ));
3355            }
3356        }
3357
3358        // Evaluate body
3359        let body = &args_vec[1..];
3360        self.eval_sequence(body, current_env)
3361    }
3362
3363    /// (letrec ((var val)...) body...)
3364    ///
3365    /// letrec allows recursive definitions - all bindings can refer to each other.
3366    /// Implementation:
3367    /// 1. Create new environment
3368    /// 2. Bind all variables to Unspecified first
3369    /// 3. Evaluate all values in the new environment
3370    /// 4. Update bindings with evaluated values
3371    /// 5. Evaluate body in the new environment
3372    fn eval_letrec(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3373        let args_vec = self.list_to_vec(args)?;
3374        if args_vec.len() < 2 {
3375            return Err(EvalError::new(
3376                "letrec requires at least 2 arguments".to_string(),
3377            ));
3378        }
3379
3380        // Parse bindings
3381        let bindings_list = &args_vec[0];
3382        let bindings = self.list_to_vec(bindings_list.clone())?;
3383
3384        // Create new environment extending current
3385        let new_env = Environment::extend(env);
3386
3387        // First pass: bind all variables to Unspecified
3388        let mut var_names = Vec::new();
3389        for binding in &bindings {
3390            let binding_vec = self.list_to_vec(binding.clone())?;
3391            if binding_vec.len() != 2 {
3392                return Err(EvalError::new(
3393                    "letrec binding must have exactly 2 elements".to_string(),
3394                ));
3395            }
3396
3397            if let Value::Symbol(ref name) = binding_vec[0] {
3398                var_names.push(name.to_string());
3399                new_env.define(name, Value::Unspecified);
3400            } else {
3401                return Err(EvalError::new(
3402                    "Binding variable must be a symbol".to_string(),
3403                ));
3404            }
3405        }
3406
3407        // Second pass: evaluate all values in the new environment and update bindings
3408        for (i, binding) in bindings.iter().enumerate() {
3409            let binding_vec = self.list_to_vec(binding.clone())?;
3410            let value = self.eval_inner(binding_vec[1].clone(), new_env.clone())?;
3411
3412            // Update the binding (set! will work since we already defined it)
3413            new_env.set(&var_names[i], value)
3414                .map_err(|e| EvalError::new(e))?;
3415        }
3416
3417        // Evaluate body in new environment
3418        let body = &args_vec[1..];
3419        self.eval_sequence(body, new_env)
3420    }
3421
3422    /// (begin expr...)
3423    fn eval_begin(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3424        let args_vec = self.list_to_vec(args)?;
3425        self.eval_sequence(&args_vec, env)
3426    }
3427
3428    /// (cond (test expr...)...)
3429    fn eval_cond(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3430        let clauses = self.list_to_vec(args)?;
3431
3432        for clause in clauses {
3433            let clause_vec = self.list_to_vec(clause)?;
3434            if clause_vec.is_empty() {
3435                return Err(EvalError::new("Empty cond clause".to_string()));
3436            }
3437
3438            // Check for else clause
3439            if let Value::Symbol(ref sym) = clause_vec[0] {
3440                if &**sym == "else" {
3441                    return self.eval_sequence(&clause_vec[1..], env);
3442                }
3443            }
3444
3445            // Evaluate test
3446            let test = self.eval_inner(clause_vec[0].clone(), env.clone())?;
3447            if test.is_true() {
3448                if clause_vec.len() == 1 {
3449                    return Ok(test);
3450                } else {
3451                    return self.eval_sequence(&clause_vec[1..], env);
3452                }
3453            }
3454        }
3455
3456        Ok(Value::Unspecified)
3457    }
3458
3459    /// (case key ((datum...) expr...)...)
3460    ///
3461    /// R4RS case statement:
3462    /// ```scheme
3463    /// (case expr
3464    ///   ((datum1 datum2 ...) result1 result2 ...)
3465    ///   ((datum3 datum4 ...) result3 result4 ...)
3466    ///   ...
3467    ///   [else resultN ...])
3468    /// ```
3469    ///
3470    /// The key expression is evaluated and compared with each datum using eqv?.
3471    /// The datums are NOT evaluated (they are literal constants).
3472    fn eval_case(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3473        let args_vec = self.list_to_vec(args)?;
3474        if args_vec.is_empty() {
3475            return Err(EvalError::new("case requires at least 1 argument".to_string()));
3476        }
3477
3478        // Evaluate the key expression
3479        let key = self.eval_inner(args_vec[0].clone(), env.clone())?;
3480
3481        // Iterate through clauses
3482        for clause in &args_vec[1..] {
3483            let clause_vec = self.list_to_vec(clause.clone())?;
3484            if clause_vec.is_empty() {
3485                return Err(EvalError::new("Empty case clause".to_string()));
3486            }
3487
3488            // Check for else clause
3489            if let Value::Symbol(ref sym) = clause_vec[0] {
3490                if &**sym == "else" {
3491                    return self.eval_sequence(&clause_vec[1..], env);
3492                }
3493            }
3494
3495            // First element should be a list of datums
3496            let datums = self.list_to_vec(clause_vec[0].clone())?;
3497
3498            // Check if key matches any datum using equal? (not eqv?)
3499            // NOTE: R4RS specifies eqv?, but that doesn't work for strings.
3500            // OpenJade uses equal? for case matching to handle string comparisons.
3501            for datum in datums {
3502                if key.equal(&datum) {
3503                    // Match found - evaluate body expressions
3504                    if clause_vec.len() == 1 {
3505                        // No expressions in clause - return unspecified
3506                        return Ok(Value::Unspecified);
3507                    } else {
3508                        return self.eval_sequence(&clause_vec[1..], env);
3509                    }
3510                }
3511            }
3512        }
3513
3514        // No match found - OpenJade treats this as an error for better error detection
3515        // R4RS says result is unspecified, but OpenJade's behavior is more useful
3516        Err(EvalError::new("case: no matching clause and no else clause".to_string()))
3517    }
3518
3519    /// (and expr...)
3520    fn eval_and(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3521        let args_vec = self.list_to_vec(args)?;
3522
3523        if args_vec.is_empty() {
3524            return Ok(Value::bool(true));
3525        }
3526
3527        let mut result = Value::bool(true);
3528        for expr in args_vec {
3529            result = self.eval_inner(expr, env.clone())?;
3530            if !result.is_true() {
3531                return Ok(Value::bool(false));
3532            }
3533        }
3534
3535        Ok(result)
3536    }
3537
3538    /// (or expr...)
3539    fn eval_or(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3540        let args_vec = self.list_to_vec(args)?;
3541
3542        for expr in args_vec {
3543            let result = self.eval_inner(expr, env.clone())?;
3544            if result.is_true() {
3545                return Ok(result);
3546            }
3547        }
3548
3549        Ok(Value::bool(false))
3550    }
3551
3552    /// Evaluate a sequence of expressions, return last result
3553    fn eval_sequence(&mut self, exprs: &[Value], env: Gc<Environment>) -> EvalResult {
3554        if exprs.is_empty() {
3555            return Ok(Value::Unspecified);
3556        }
3557
3558        let mut result = Value::Unspecified;
3559        for expr in exprs {
3560            result = self.eval_inner(expr.clone(), env.clone())?;
3561        }
3562
3563        Ok(result)
3564    }
3565
3566    /// (apply proc args)
3567    ///
3568    /// Apply a procedure to a list of arguments.
3569    /// Example: (apply + '(1 2 3)) → 6
3570    fn eval_apply(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3571        let args_vec = self.list_to_vec(args)?;
3572        if args_vec.len() != 2 {
3573            return Err(EvalError::new(
3574                "apply requires exactly 2 arguments".to_string(),
3575            ));
3576        }
3577
3578        // Evaluate the procedure
3579        let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
3580
3581        // Evaluate the argument list
3582        let arg_list = self.eval_inner(args_vec[1].clone(), env)?;
3583
3584        // Convert argument list to vector
3585        let arg_values = self.list_to_vec(arg_list)?;
3586
3587        // Apply the procedure
3588        self.apply(proc, arg_values)
3589    }
3590
3591    /// (map proc list)
3592    ///
3593    /// Apply procedure to each element of list, return list of results.
3594    /// Example: (map (lambda (x) (* x 2)) '(1 2 3)) → '(2 4 6)
3595    /// (map proc list1 list2 ...)
3596    ///
3597    /// R4RS: Apply procedure to corresponding elements of lists.
3598    /// All lists must have the same length.
3599    /// Returns a list of results.
3600    ///
3601    /// Examples:
3602    /// - (map + '(1 2 3) '(4 5 6)) => (5 7 9)
3603    /// - (map list '(1 2) '(a b) '(x y)) => ((1 a x) (2 b y))
3604    fn eval_map(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3605        let args_vec = self.list_to_vec(args)?;
3606        if args_vec.len() < 2 {
3607            return Err(EvalError::new("map requires at least 2 arguments".to_string()));
3608        }
3609
3610        // Evaluate the procedure
3611        let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
3612
3613        // Evaluate all lists
3614        let mut lists = Vec::new();
3615        for i in 1..args_vec.len() {
3616            let list = self.eval_inner(args_vec[i].clone(), env.clone())?;
3617            let list_vec = self.list_to_vec(list)?;
3618            lists.push(list_vec);
3619        }
3620
3621        // Check all lists have the same length
3622        if lists.is_empty() {
3623            return Ok(Value::Nil);
3624        }
3625
3626        let length = lists[0].len();
3627        for list in &lists[1..] {
3628            if list.len() != length {
3629                return Err(EvalError::new(
3630                    "map: all lists must have the same length".to_string(),
3631                ));
3632            }
3633        }
3634
3635        // Apply procedure to corresponding elements
3636        let mut result_vec = Vec::new();
3637        for i in 0..length {
3638            // Gather i-th element from each list
3639            let mut proc_args = Vec::new();
3640            for list in &lists {
3641                proc_args.push(list[i].clone());
3642            }
3643
3644            // Apply procedure
3645            let result = self.apply(proc.clone(), proc_args)?;
3646            result_vec.push(result);
3647        }
3648
3649        // Convert result vector back to list
3650        let mut result_list = Value::Nil;
3651        for elem in result_vec.into_iter().rev() {
3652            result_list = Value::cons(elem, result_list);
3653        }
3654
3655        Ok(result_list)
3656    }
3657
3658    /// (for-each proc list1 list2 ...)
3659    ///
3660    /// R4RS: Apply procedure to corresponding elements of lists for side effects.
3661    /// All lists must have the same length.
3662    /// Returns unspecified.
3663    ///
3664    /// Example: (for-each display '("a" "b" "c"))
3665    fn eval_for_each(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3666        let args_vec = self.list_to_vec(args)?;
3667        if args_vec.len() < 2 {
3668            return Err(EvalError::new(
3669                "for-each requires at least 2 arguments".to_string(),
3670            ));
3671        }
3672
3673        // Evaluate the procedure
3674        let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
3675
3676        // Evaluate all lists
3677        let mut lists = Vec::new();
3678        for i in 1..args_vec.len() {
3679            let list = self.eval_inner(args_vec[i].clone(), env.clone())?;
3680            let list_vec = self.list_to_vec(list)?;
3681            lists.push(list_vec);
3682        }
3683
3684        // Check all lists have the same length
3685        if lists.is_empty() {
3686            return Ok(Value::Unspecified);
3687        }
3688
3689        let length = lists[0].len();
3690        for list in &lists[1..] {
3691            if list.len() != length {
3692                return Err(EvalError::new(
3693                    "for-each: all lists must have the same length".to_string(),
3694                ));
3695            }
3696        }
3697
3698        // Apply procedure to corresponding elements (for side effects)
3699        for i in 0..length {
3700            // Gather i-th element from each list
3701            let mut proc_args = Vec::new();
3702            for list in &lists {
3703                proc_args.push(list[i].clone());
3704            }
3705
3706            // Apply procedure for side effects
3707            self.apply(proc.clone(), proc_args)?;
3708        }
3709
3710        Ok(Value::Unspecified)
3711    }
3712
3713    /// (node-list-filter predicate node-list)
3714    ///
3715    /// (node-list-filter pred node-list) → node-list
3716    ///
3717    /// Returns a node-list containing only nodes for which predicate returns #t.
3718    /// DSSSL: Filter a node-list based on a predicate function.
3719    fn eval_node_list_filter(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3720        let args_vec = self.list_to_vec(args)?;
3721        if args_vec.len() != 2 {
3722            return Err(EvalError::new("node-list-filter requires exactly 2 arguments".to_string()));
3723        }
3724
3725        // Evaluate the predicate
3726        let pred = self.eval_inner(args_vec[0].clone(), env.clone())?;
3727
3728        // Evaluate the node-list
3729        let node_list_val = self.eval_inner(args_vec[1].clone(), env.clone())?;
3730
3731        match node_list_val {
3732            Value::NodeList(ref nl) => {
3733                let mut filtered_nodes = Vec::new();
3734
3735                // Iterate through the node-list
3736                let mut index = 0;
3737                loop {
3738                    if let Some(node) = nl.get(index) {
3739                        // Apply predicate to this node
3740                        let node_val = Value::node(node);
3741                        let result = self.apply(pred.clone(), vec![node_val.clone()])?;
3742
3743                        // If predicate returns a truthy value (anything except #f), include this node
3744                        if !matches!(result, Value::Bool(false)) {
3745                            // Need to get the node again since we consumed it
3746                            if let Value::Node(n) = node_val {
3747                                filtered_nodes.push(n.as_ref().clone_node());
3748                            }
3749                        }
3750
3751                        index += 1;
3752                    } else {
3753                        break;
3754                    }
3755                }
3756
3757                Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(filtered_nodes))))
3758            }
3759            _ => Err(EvalError::new(format!("node-list-filter: second argument not a node-list: {:?}", node_list_val))),
3760        }
3761    }
3762
3763    /// (node-list-map proc node-list) → node-list
3764    ///
3765    /// Applies proc to each node in node-list and returns a flattened node-list.
3766    /// Each result must be a node-list or a single node (which is treated as a singleton node-list).
3767    /// Results are concatenated (flattened) into a single node-list.
3768    /// If proc returns #f or any non-node value, processing stops (OpenJade compatibility).
3769    ///
3770    /// DSSSL: Maps a procedure over a node-list, flattening results into a single node-list.
3771    /// OpenJade: MapNodeListObj - stops processing when proc returns a non-node-list value.
3772    fn eval_node_list_map(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3773        let args_vec = self.list_to_vec(args)?;
3774        if args_vec.len() != 2 {
3775            return Err(EvalError::new("node-list-map requires exactly 2 arguments".to_string()));
3776        }
3777
3778        // Evaluate the procedure
3779        let proc = self.eval_inner(args_vec[0].clone(), env.clone())?;
3780
3781        // Evaluate the node-list
3782        let node_list_val = self.eval_inner(args_vec[1].clone(), env.clone())?;
3783
3784        // Collect all nodes from mapping results (flattened)
3785        let mut result_nodes: Vec<Box<dyn crate::grove::Node>> = Vec::new();
3786
3787        match node_list_val {
3788            Value::Node(ref n) => {
3789                // Single node - apply proc and collect result
3790                let node_val = Value::node(n.as_ref().clone_node());
3791                let result = self.apply(proc, vec![node_val])?;
3792
3793                // OpenJade: Result must be node or node-list. If not, stop processing.
3794                // Single nodes are auto-converted to singleton node-lists (DSSSL spec)
3795                match result {
3796                    Value::Node(n) => {
3797                        // Single node - treat as singleton node-list
3798                        result_nodes.push(n.as_ref().clone_node());
3799                    }
3800                    Value::NodeList(nl) => {
3801                        // Node-list - flatten all nodes
3802                        let mut index = 0;
3803                        while let Some(node) = nl.get(index) {
3804                            result_nodes.push(node);
3805                            index += 1;
3806                        }
3807                    }
3808                    _ => {
3809                        // Non-node result (e.g., #f) - stop processing (OpenJade compat)
3810                        // Return empty node-list
3811                    }
3812                }
3813            }
3814            Value::NodeList(ref nl) => {
3815                // Iterate through the node-list
3816                let mut index = 0;
3817                loop {
3818                    if let Some(node) = nl.get(index) {
3819                        // Apply procedure to this node
3820                        let node_val = Value::node(node);
3821                        let result = self.apply(proc.clone(), vec![node_val])?;
3822
3823                        // OpenJade: Result must be node or node-list. If not, stop processing.
3824                        match result {
3825                            Value::Node(n) => {
3826                                // Single node - treat as singleton node-list
3827                                result_nodes.push(n.as_ref().clone_node());
3828                                index += 1;
3829                            }
3830                            Value::NodeList(nl_result) => {
3831                                // Node-list - flatten all nodes
3832                                let mut nl_index = 0;
3833                                while let Some(node) = nl_result.get(nl_index) {
3834                                    result_nodes.push(node);
3835                                    nl_index += 1;
3836                                }
3837                                index += 1;
3838                            }
3839                            _ => {
3840                                // Non-node result (e.g., #f) - stop processing (OpenJade compat)
3841                                break;
3842                            }
3843                        }
3844                    } else {
3845                        break;
3846                    }
3847                }
3848            }
3849            _ => return Err(EvalError::new(format!("node-list-map: second argument must be a node or node-list: {:?}", node_list_val))),
3850        }
3851
3852        // Return flattened node-list
3853        Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(result_nodes))))
3854    }
3855
3856    /// (node-list-some? predicate node-list) → boolean
3857    ///
3858    /// Returns #t if the predicate returns true for at least one node in the node-list.
3859    /// Returns #f if the node-list is empty or the predicate returns false for all nodes.
3860    /// DSSSL: Test if any node in the node-list satisfies the predicate.
3861    fn eval_node_list_some(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3862        let args_vec = self.list_to_vec(args)?;
3863        if args_vec.len() != 2 {
3864            return Err(EvalError::new("node-list-some? requires exactly 2 arguments".to_string()));
3865        }
3866
3867        // Evaluate the predicate
3868        let pred = self.eval_inner(args_vec[0].clone(), env.clone())?;
3869
3870        // Evaluate the node-list
3871        let node_list_val = self.eval_inner(args_vec[1].clone(), env.clone())?;
3872
3873        match node_list_val {
3874            Value::NodeList(ref nl) => {
3875                // Iterate through the node-list
3876                let mut index = 0;
3877                loop {
3878                    if let Some(node) = nl.get(index) {
3879                        // Apply predicate to this node
3880                        let node_val = Value::node(node);
3881                        let result = self.apply(pred.clone(), vec![node_val])?;
3882
3883                        // If predicate returns a truthy value (anything except #f), return #t immediately
3884                        if !matches!(result, Value::Bool(false)) {
3885                            return Ok(Value::bool(true));
3886                        }
3887
3888                        index += 1;
3889                    } else {
3890                        break;
3891                    }
3892                }
3893
3894                // If we get here, no node satisfied the predicate
3895                Ok(Value::bool(false))
3896            }
3897            _ => Err(EvalError::new(format!("node-list-some?: second argument not a node-list: {:?}", node_list_val))),
3898        }
3899    }
3900
3901    /// (load filename)
3902    ///
3903    /// Load and evaluate Scheme code from a file.
3904    /// Returns the result of the last expression in the file.
3905    fn eval_load(&mut self, args: Value, env: Gc<Environment>) -> EvalResult {
3906        let args_vec = self.list_to_vec(args)?;
3907        if args_vec.len() != 1 {
3908            return Err(EvalError::new(
3909                "load requires exactly 1 argument".to_string(),
3910            ));
3911        }
3912
3913        // Evaluate the filename argument
3914        let filename_val = self.eval(args_vec[0].clone(), env.clone())?;
3915
3916        let filename = match filename_val {
3917            Value::String(s) => s.to_string(),
3918            _ => return Err(EvalError::new(
3919                format!("load: filename must be a string, got {:?}", filename_val)
3920            )),
3921        };
3922
3923        // Read the file
3924        let contents = std::fs::read_to_string(&filename)
3925            .map_err(|e| EvalError::new(format!("load: cannot read file '{}': {}", filename, e)))?;
3926
3927        // Parse the file contents with filename for error reporting
3928        let mut parser = crate::scheme::parser::Parser::new_with_filename(&contents, filename.clone());
3929        let mut result = Value::Unspecified;
3930
3931        // Save current source file and position, set to the loaded file for error reporting
3932        let prev_source_file = self.current_source_file.clone();
3933        let prev_position = self.current_position.clone();
3934        self.current_source_file = Some(filename.clone());
3935
3936        // Evaluate each expression in sequence
3937        let eval_result = loop {
3938            // Get position before parsing
3939            let pos = parser.current_position();
3940
3941            match parser.parse() {
3942                Ok(expr) => {
3943                    // Set position for this expression
3944                    self.current_position = Some(pos);
3945
3946                    match self.eval(expr, env.clone()) {
3947                        Ok(val) => result = val,
3948                        Err(e) => break Err(e),
3949                    }
3950                }
3951                Err(e) => {
3952                    // Check if we've reached end of input (not an error)
3953                    let error_msg = e.to_string();
3954                    if error_msg.contains("Unexpected end of input")
3955                        || error_msg.contains("Expected")
3956                        || error_msg.contains("EOF") {
3957                        break Ok(result);
3958                    }
3959                    break Err(EvalError::new(
3960                        format!("load: parse error in '{}': {}", filename, e)
3961                    ));
3962                }
3963            }
3964        };
3965
3966        // Restore previous source file and position
3967        self.current_source_file = prev_source_file;
3968        self.current_position = prev_position;
3969
3970        eval_result
3971    }
3972
3973    // =========================================================================
3974    // Function Application
3975    // =========================================================================
3976
3977    /// Apply a function to arguments
3978    fn eval_application(
3979        &mut self,
3980        operator: Value,
3981        args: Value,
3982        env: Gc<Environment>,
3983    ) -> EvalResult {
3984        // Save the position of this application expression (the call site)
3985        let application_pos = self.current_position.clone();
3986        let application_file = self.current_source_file.clone();
3987
3988        // Evaluate operator
3989        let proc = self.eval_inner(operator, env.clone())?;
3990
3991        // Evaluate arguments - extract position from the pair containing each argument
3992        let mut evaled_args = Vec::new();
3993        let mut current_args = args;
3994        loop {
3995            match current_args {
3996                Value::Nil => break,
3997                Value::Pair(ref p) => {
3998                    let pair_borrow = p.borrow();
3999
4000                    // Extract position from this pair (which contains the argument)
4001                    // This gives us the position where the argument appears in the source
4002                    if let Some(ref pos) = pair_borrow.pos {
4003                        // Translate output position to source position using line mappings
4004                        if !self.line_mappings.is_empty() {
4005                            if let Some(mapping) = self.line_mappings.iter().find(|m| m.output_line == pos.line) {
4006                                self.current_source_file = Some(mapping.source_file.clone());
4007                                self.current_position = Some(Position {
4008                                    line: mapping.source_line,
4009                                    column: pos.column,
4010                                });
4011                            } else {
4012                                self.current_position = Some(pos.clone());
4013                            }
4014                        } else {
4015                            self.current_position = Some(pos.clone());
4016                        }
4017                    }
4018
4019                    let arg = pair_borrow.car.clone();
4020                    let cdr = pair_borrow.cdr.clone();
4021                    drop(pair_borrow); // Release borrow before evaluating
4022
4023                    evaled_args.push(self.eval_inner(arg, env.clone())?);
4024                    current_args = cdr;
4025                }
4026                _ => return Err(EvalError::new("Improper argument list".to_string())),
4027            }
4028        }
4029
4030        // Restore the application position before calling apply
4031        // This ensures that when we push a call frame, we capture the CALL SITE, not the last argument's position
4032        self.current_position = application_pos;
4033        self.current_source_file = application_file;
4034
4035        // Apply procedure
4036        self.apply(proc, evaled_args)
4037    }
4038
4039    /// Apply a procedure to evaluated arguments
4040    fn apply(&mut self, proc: Value, args: Vec<Value>) -> EvalResult {
4041        if let Value::Procedure(ref p) = proc {
4042            match &**p {
4043                Procedure::Primitive { name, func } => {
4044                    match *name {
4045                        "car" | "cdr" | "cons" | "null?" | "equal?" |
4046                        "cadr" | "caddr" | "cadddr" | "list" | "length" |
4047                        "reverse" | "append" | "list?" | "list-ref" |
4048                        "pair?" | "number?" | "integer?" | "real?" | "string?" |
4049                        "symbol?" | "char?" | "boolean?" | "zero?" | "positive?" |
4050                        "negative?" | "odd?" | "even?" |
4051                        "+" | "-" | "*" | "/" | "quotient" | "remainder" | "modulo" |
4052                        "=" | "<" | ">" | "<=" | ">=" |
4053                        "abs" | "min" | "max" |
4054                        "floor" | "ceiling" | "truncate" | "round" |
4055                        "sqrt" | "sin" | "cos" | "tan" | "asin" | "acos" | "atan" |
4056                        "exp" | "log" | "expt" |
4057                        "string-length" | "string-ref" | "substring" | "string-append" |
4058                        "string=?" | "string<?" | "string>?" | "string<=?" | "string>=?" |
4059                        "string-ci=?" | "string-ci<?" | "string-ci>?" | "string-ci<=?" | "string-ci>=?" |
4060                        "char=?" | "char<?" | "char>?" | "char<=?" | "char>=?" |
4061                        "char-ci=?" | "char-ci<?" | "char-ci>?" | "char-ci<=?" | "char-ci>=?" |
4062                        "char-upcase" | "char-downcase" |
4063                        "char-alphabetic?" | "char-numeric?" | "char-whitespace?" |
4064                        "char->integer" | "integer->char" |
4065                        "char-property" | "char-script-case" |
4066                        "symbol->string" | "string->symbol" |
4067                        "keyword?" | "keyword->string" | "string->keyword" |
4068                        "memq" | "memv" | "member" |
4069                        "assq" | "assv" | "assoc" |
4070                        "not" | "eq?" | "eqv?" |
4071                        "caar" | "cdar" | "cddr" |
4072                        "caaar" | "caadr" | "cadar" |
4073                        "cdaar" | "cdadr" | "cddar" | "cdddr" |
4074                        "vector" | "make-vector" | "vector-length" |
4075                        "vector-ref" | "vector-set!" |
4076                        "vector->list" | "list->vector" | "vector-fill!" |
4077                        "vector?" | "procedure?" |
4078                        "set-car!" | "set-cdr!" | "list-tail" |
4079                        "string-upcase" | "string-downcase" | "case-fold-down" |
4080                        "string-index" |
4081                        "string->number" | "number->string" |
4082                        "string->list" | "list->string" |
4083                        "gcd" | "lcm" |
4084                        "exact->inexact" | "inexact->exact" |
4085                        "make-string" | "string" | "reverse!" |
4086                        "string-set!" | "string-copy" | "string-fill!" |
4087                        "char-lower-case?" | "char-upper-case?" |
4088                        "last" | "last-pair" | "list-copy" |
4089                        "append!" | "iota" |
4090                        "take" | "drop" | "split-at" |
4091                        "filter" | "remove" |
4092                        "numerator" | "denominator" | "rationalize" |
4093                        "angle" | "magnitude" |
4094                        "null-list?" | "improper-list?" | "circular-list?" |
4095                        "bitwise-and" | "bitwise-ior" | "bitwise-xor" | "bitwise-not" |
4096                        "arithmetic-shift" | "bit-extract" |
4097                        "bitwise-bit-set?" | "bitwise-bit-count" |
4098                        "format-number" | "format-number-list" |
4099                        "empty-sosofo" | "sosofo-append" | "if-first-page" | "if-front-page" |
4100                        "current-node" |
4101                        "gi" | "data" | "id" |
4102                        "children" | "parent" | "attributes" |
4103                        "node-list?" | "empty-node-list" | "node-list-empty?" |
4104                        "node-list-length" | "node-list-first" |
4105                        "attribute-string" |
4106                        "node-list-rest" | "node-list-ref" | "node-list-reverse" |
4107                        "node?" | "sosofo?" | "quantity?" |
4108                        "color?" | "color" | "display-space?" | "inline-space?" |
4109                        "quantity->number" | "number->quantity" | "quantity-convert" |
4110                        "device-length" | "label-distance" |
4111                        "ancestor" | "descendants" | "follow" | "preced" | "ipreced" |
4112                        "node-list-last" | "node-list-union" | "node-list-intersection" |
4113                        "node-list-difference" | "node-list-remove-duplicates" |
4114                        "select-elements" | "first-sibling?" | "last-sibling?" |
4115                        "child-number" | "element-with-id" |
4116                        "element-number" | "hierarchical-number" | "hierarchical-number-recursive" |
4117                        "ancestors" | "document-element" | "have-ancestor?" |
4118                        "match-element?" | "node-list-map" |
4119                        "node-property" | "absolute-first-sibling?" | "absolute-last-sibling?" |
4120                        "node-list->list" | "node-list-contains?" |
4121                        "entity-system-id" | "entity-public-id" | "entity-type" |
4122                        "notation-system-id" | "notation-public-id" |
4123                        "current-language" | "current-mode" | "current-node-address" |
4124                        "current-node-page-number-sosofo" | "debug" |
4125                        "add" | "divide" | "equal" | "char-eq" | "char-lt" |
4126                        "exact?" | "inexact?" | "error" |
4127                        "address?" | "address-local?" | "address-visited?" |
4128                        "color-space?" | "color-space" | "display-space" | "inline-space" |
4129                        "glyph-id?" | "glyph-id" | "glyph-subst-table?" | "glyph-subst-table" | "glyph-subst" |
4130                        "time" | "time->string" | "time<=?" | "time<?" | "time>=?" | "time>?" |
4131                        "language?" | "language" | "style?" |
4132                        "string-equiv?" | "label-length" | "external-procedure" |
4133                        "declaration" | "dtd" | "epilog" | "prolog" | "sgml-declaration" | "sgml-parse" |
4134                        "entity-address" | "entity-generated-system-id" | "entity-name-normalize" | "general-name-normalize" | "normalize" |
4135                        "first-child-gi" | "tree-root" | "declare-default-language" | "read-entity" | "set-visited!" |
4136                        "sosofo-contains-node?" | "page-number-sosofo" | "ifollow" | "with-language" |
4137                        "all-element-number" | "ancestor-child-number" | "element-number-list" |
4138                        "inherited-attribute-string" | "inherited-element-attribute-string" |
4139                        "inherited-start-indent" | "inherited-end-indent" | "inherited-line-spacing" |
4140                        "inherited-font-family-name" | "inherited-font-size" | "inherited-font-weight" |
4141                        "inherited-font-posture" | "inherited-dbhtml-value" | "inherited-pi-value" |
4142                        "node-list" | "node-list=?" | "node-list-count" | "node-list-union-map" |
4143                        "node-list-symmetrical-difference" |
4144                        "node-list-address" | "node-list-error" | "node-list-no-order" |
4145                        "origin-to-subnode-rel-forest-addr" |
4146                        "named-node" | "named-node-list?" | "named-node-list-names" |
4147                        "select-by-class" | "select-children" |
4148                        "process-children-trim" | "process-element-with-id" | "process-first-descendant" |
4149                        "process-matching-children" | "next-match" => {
4150                            self.apply_primitive(name, &args)
4151                        }
4152                        // Note: I/O operations (display, write, newline, etc.) stay in non-arena path
4153                        // because they need actual side effects (stdout/stdin interaction)
4154                        _ => {
4155                            // Check if this is a known primitive that should be dispatched to apply_primitive
4156                            // This handles marker primitives created during symbol lookup
4157                            if self.get_primitive_static_name(name).is_some() {
4158                                self.apply_primitive(name, &args)
4159                            } else {
4160                                // Call the function directly for non-arena primitives (I/O, etc.)
4161                                // Don't push call frames for primitives - only for user lambdas
4162                                // This matches OpenJade's behavior
4163                                func(&args).map_err(|e| self.error_with_stack(e))
4164                            }
4165                        }
4166                    }
4167                }
4168                Procedure::Lambda { params, required_count, optional_defaults, body, env, source, name } => {
4169                    // Check argument count - must have at least required_count, at most params.len()
4170                    if args.len() < *required_count {
4171                        return Err(self.error_with_stack(format!(
4172                            "Lambda expects at least {} arguments, got {}",
4173                            required_count,
4174                            args.len()
4175                        )));
4176                    }
4177                    if args.len() > params.len() {
4178                        return Err(self.error_with_stack(format!(
4179                            "Lambda expects at most {} arguments, got {}",
4180                            params.len(),
4181                            args.len()
4182                        )));
4183                    }
4184
4185                    // Save current position (call site) before switching to lambda's definition location
4186                    let saved_file = self.current_source_file.clone();
4187                    let saved_pos = self.current_position.clone();
4188
4189                    // Only push call frame for NAMED functions (not anonymous lambdas)
4190                    // This matches OpenJade's behavior - it only tracks named function calls
4191                    let pushed_frame = if let Some(func_name) = name.clone() {
4192                        let call_site = match (&saved_file, &saved_pos) {
4193                            (Some(file), Some(pos)) => Some(SourceInfo {
4194                                file: file.clone(),
4195                                pos: pos.clone(),
4196                            }),
4197                            _ => None,
4198                        };
4199                        self.push_call_frame(func_name, call_site);
4200                        true
4201                    } else {
4202                        false
4203                    };
4204
4205                    // Switch to lambda's definition location for evaluating the body
4206                    if let Some(ref src) = source {
4207                        self.current_source_file = Some(src.file.clone());
4208                        self.current_position = Some(src.pos.clone());
4209                    }
4210
4211                    // Create new environment extending the closure environment
4212                    let lambda_env = Environment::extend(env.clone());
4213
4214                    // Bind required and provided arguments
4215                    for (param_name, arg_value) in params.iter().zip(args.iter()) {
4216                        lambda_env.define(param_name, arg_value.clone());
4217                    }
4218
4219                    // Bind optional parameters that weren't provided with their defaults
4220                    if args.len() < params.len() {
4221                        let num_optional_provided = args.len().saturating_sub(*required_count);
4222                        let num_optional_defaults_needed = (params.len() - *required_count) - num_optional_provided;
4223
4224                        for i in 0..num_optional_defaults_needed {
4225                            let param_idx = *required_count + num_optional_provided + i;
4226                            let param_name = &params[param_idx];
4227                            let default_expr = &optional_defaults[num_optional_provided + i];
4228
4229                            if std::env::var("DEBUG_OPTIONAL").is_ok() {
4230                                eprintln!("[DEBUG_OPTIONAL] Evaluating default for param '{}': {:?}", param_name, default_expr);
4231                                // Try to look up the parameter name in the environment
4232                                if let Some(val) = env.lookup(param_name) {
4233                                    eprintln!("[DEBUG_OPTIONAL] Found '{}' in closure env: {:?}", param_name, val);
4234                                } else {
4235                                    eprintln!("[DEBUG_OPTIONAL] '{}' not found in closure env (expected)", param_name);
4236                                }
4237                            }
4238
4239                            // Evaluate default expression in the closure environment
4240                            let default_value = self.eval_inner(default_expr.clone(), env.clone())?;
4241
4242                            if std::env::var("DEBUG_OPTIONAL").is_ok() {
4243                                eprintln!("[DEBUG_OPTIONAL] Evaluated to: {:?}", default_value);
4244                            }
4245
4246                            lambda_env.define(param_name, default_value);
4247                        }
4248                    }
4249
4250                    // Evaluate body in the new environment
4251                    let result = self.eval_inner((**body).clone(), lambda_env);
4252
4253                    // Restore previous position
4254                    self.current_source_file = saved_file;
4255                    self.current_position = saved_pos;
4256
4257                    // Pop call frame if we pushed one
4258                    if pushed_frame {
4259                        self.pop_call_frame();
4260                    }
4261
4262                    result
4263                }
4264            }
4265        } else if let Value::Symbol(sym) = &proc {
4266            // Special handling for symbols returned by external-procedure
4267            // These are primitive names that need to be dispatched to arena primitives
4268            let sym_str = sym.as_ref();
4269
4270            // Dispatch directly to the arena primitive by name
4271            self.apply_primitive(sym_str, &args)
4272        } else {
4273            Err(self.error_with_stack(format!(
4274                "Not a procedure: {:?}",
4275                proc
4276            )))
4277        }
4278    }
4279}
4280
4281impl Default for Evaluator {
4282    fn default() -> Self {
4283        Self::new()
4284    }
4285}
4286
4287// =============================================================================
4288// Tests
4289// =============================================================================
4290
4291#[cfg(test)]
4292mod tests {
4293    use super::*;
4294
4295    fn make_env() -> Gc<Environment> {
4296        Environment::new_global()
4297    }
4298
4299    #[test]
4300    fn test_eval_self_evaluating() {
4301        let mut eval = Evaluator::new();
4302        let env = make_env();
4303
4304        assert!(eval.eval(Value::integer(42), env.clone()).unwrap().is_integer());
4305        assert!(eval.eval(Value::bool(true), env.clone()).unwrap().is_bool());
4306        assert!(eval.eval(Value::string("hello".to_string()), env).unwrap().is_string());
4307    }
4308
4309    #[test]
4310    fn test_eval_quote() {
4311        let mut eval = Evaluator::new();
4312        let env = make_env();
4313
4314        // (quote (1 2 3))
4315        let expr = Value::cons(
4316            Value::symbol("quote"),
4317            Value::cons(
4318                Value::cons(
4319                    Value::integer(1),
4320                    Value::cons(Value::integer(2), Value::cons(Value::integer(3), Value::Nil)),
4321                ),
4322                Value::Nil,
4323            ),
4324        );
4325
4326        let result = eval.eval(expr, env).unwrap();
4327        assert!(result.is_list());
4328    }
4329
4330    #[test]
4331    fn test_eval_if_true() {
4332        let mut eval = Evaluator::new();
4333        let env = make_env();
4334
4335        // (if #t 1 2)
4336        let expr = Value::cons(
4337            Value::symbol("if"),
4338            Value::cons(
4339                Value::bool(true),
4340                Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
4341            ),
4342        );
4343
4344        let result = eval.eval(expr, env).unwrap();
4345        if let Value::Integer(n) = result {
4346            assert_eq!(n, 1);
4347        } else {
4348            panic!("Expected integer 1");
4349        }
4350    }
4351
4352    #[test]
4353    fn test_eval_if_false() {
4354        let mut eval = Evaluator::new();
4355        let env = make_env();
4356
4357        // (if #f 1 2)
4358        let expr = Value::cons(
4359            Value::symbol("if"),
4360            Value::cons(
4361                Value::bool(false),
4362                Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
4363            ),
4364        );
4365
4366        let result = eval.eval(expr, env).unwrap();
4367        if let Value::Integer(n) = result {
4368            assert_eq!(n, 2);
4369        } else {
4370            panic!("Expected integer 2");
4371        }
4372    }
4373
4374    #[test]
4375    fn test_eval_define() {
4376        let mut eval = Evaluator::new();
4377        let env = make_env();
4378
4379        // (define x 42)
4380        let expr = Value::cons(
4381            Value::symbol("define"),
4382            Value::cons(Value::symbol("x"), Value::cons(Value::integer(42), Value::Nil)),
4383        );
4384
4385        eval.eval(expr, env.clone()).unwrap();
4386
4387        // Check that x is defined
4388        assert!(env.is_defined("x"));
4389        if let Value::Integer(n) = env.lookup("x").unwrap() {
4390            assert_eq!(n, 42);
4391        }
4392    }
4393
4394    #[test]
4395    fn test_eval_symbol_lookup() {
4396        let mut eval = Evaluator::new();
4397        let env = make_env();
4398
4399        env.define("x", Value::integer(99));
4400
4401        let result = eval.eval(Value::symbol("x"), env).unwrap();
4402        if let Value::Integer(n) = result {
4403            assert_eq!(n, 99);
4404        } else {
4405            panic!("Expected integer 99");
4406        }
4407    }
4408
4409    #[test]
4410    fn test_eval_and() {
4411        let mut eval = Evaluator::new();
4412        let env = make_env();
4413
4414        // (and #t #t)
4415        let expr = Value::cons(
4416            Value::symbol("and"),
4417            Value::cons(Value::bool(true), Value::cons(Value::bool(true), Value::Nil)),
4418        );
4419
4420        let result = eval.eval(expr, env.clone()).unwrap();
4421        assert!(result.is_true());
4422
4423        // (and #t #f)
4424        let expr = Value::cons(
4425            Value::symbol("and"),
4426            Value::cons(Value::bool(true), Value::cons(Value::bool(false), Value::Nil)),
4427        );
4428
4429        let result = eval.eval(expr, env).unwrap();
4430        assert!(!result.is_true());
4431    }
4432
4433    #[test]
4434    fn test_eval_or() {
4435        let mut eval = Evaluator::new();
4436        let env = make_env();
4437
4438        // (or #f #t)
4439        let expr = Value::cons(
4440            Value::symbol("or"),
4441            Value::cons(Value::bool(false), Value::cons(Value::bool(true), Value::Nil)),
4442        );
4443
4444        let result = eval.eval(expr, env.clone()).unwrap();
4445        assert!(result.is_true());
4446
4447        // (or #f #f)
4448        let expr = Value::cons(
4449            Value::symbol("or"),
4450            Value::cons(Value::bool(false), Value::cons(Value::bool(false), Value::Nil)),
4451        );
4452
4453        let result = eval.eval(expr, env).unwrap();
4454        assert!(!result.is_true());
4455    }
4456
4457    #[test]
4458    fn test_eval_lambda_creation() {
4459        let mut eval = Evaluator::new();
4460        let env = make_env();
4461
4462        // (lambda (x) x)
4463        let expr = Value::cons(
4464            Value::symbol("lambda"),
4465            Value::cons(
4466                Value::cons(Value::symbol("x"), Value::Nil),
4467                Value::cons(Value::symbol("x"), Value::Nil),
4468            ),
4469        );
4470
4471        let result = eval.eval(expr, env).unwrap();
4472        assert!(result.is_procedure());
4473    }
4474
4475    #[test]
4476    fn test_eval_lambda_application() {
4477        let mut eval = Evaluator::new();
4478        let env = make_env();
4479
4480        // ((lambda (x) x) 42)
4481        let lambda_expr = Value::cons(
4482            Value::symbol("lambda"),
4483            Value::cons(
4484                Value::cons(Value::symbol("x"), Value::Nil),
4485                Value::cons(Value::symbol("x"), Value::Nil),
4486            ),
4487        );
4488
4489        let app_expr = Value::cons(lambda_expr, Value::cons(Value::integer(42), Value::Nil));
4490
4491        let result = eval.eval(app_expr, env).unwrap();
4492        if let Value::Integer(n) = result {
4493            assert_eq!(n, 42);
4494        } else {
4495            panic!("Expected integer 42");
4496        }
4497    }
4498
4499    #[test]
4500    fn test_eval_lambda_multiple_params() {
4501        let mut eval = Evaluator::new();
4502        let env = make_env();
4503
4504        // ((lambda (x y) x) 1 2) - Just return first param
4505        let params = Value::cons(Value::symbol("x"), Value::cons(Value::symbol("y"), Value::Nil));
4506        let body = Value::symbol("x");
4507
4508        let lambda_expr = Value::cons(Value::symbol("lambda"), Value::cons(params, Value::cons(body, Value::Nil)));
4509
4510        let app_expr = Value::cons(
4511            lambda_expr,
4512            Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
4513        );
4514
4515        let result = eval.eval(app_expr, env).unwrap();
4516        if let Value::Integer(n) = result {
4517            assert_eq!(n, 1);
4518        } else {
4519            panic!("Expected integer 1");
4520        }
4521    }
4522
4523    #[test]
4524    fn test_eval_lambda_wrong_arg_count() {
4525        let mut eval = Evaluator::new();
4526        let env = make_env();
4527
4528        // ((lambda (x) x) 1 2) - wrong argument count
4529        let lambda_expr = Value::cons(
4530            Value::symbol("lambda"),
4531            Value::cons(
4532                Value::cons(Value::symbol("x"), Value::Nil),
4533                Value::cons(Value::symbol("x"), Value::Nil),
4534            ),
4535        );
4536
4537        let app_expr = Value::cons(
4538            lambda_expr,
4539            Value::cons(Value::integer(1), Value::cons(Value::integer(2), Value::Nil)),
4540        );
4541
4542        let result = eval.eval(app_expr, env);
4543        assert!(result.is_err());
4544    }
4545
4546    #[test]
4547    fn test_eval_lambda_closure() {
4548        let mut eval = Evaluator::new();
4549        let env = make_env();
4550
4551        // (define x 10)
4552        env.define("x", Value::integer(10));
4553
4554        // ((lambda (y) x) 20)
4555        // Should capture x from outer environment and ignore y
4556        let lambda_expr = Value::cons(
4557            Value::symbol("lambda"),
4558            Value::cons(
4559                Value::cons(Value::symbol("y"), Value::Nil),
4560                Value::cons(Value::symbol("x"), Value::Nil),
4561            ),
4562        );
4563
4564        let app_expr = Value::cons(lambda_expr, Value::cons(Value::integer(20), Value::Nil));
4565
4566        let result = eval.eval(app_expr, env).unwrap();
4567        if let Value::Integer(n) = result {
4568            assert_eq!(n, 10); // Should get x from outer environment
4569        } else {
4570            panic!("Expected integer 10 from closure");
4571        }
4572    }
4573
4574    #[test]
4575    fn test_eval_lambda_no_params() {
4576        let mut eval = Evaluator::new();
4577        let env = make_env();
4578
4579        // ((lambda () 42))
4580        let lambda_expr = Value::cons(
4581            Value::symbol("lambda"),
4582            Value::cons(Value::Nil, Value::cons(Value::integer(42), Value::Nil)),
4583        );
4584
4585        let app_expr = Value::cons(lambda_expr, Value::Nil);
4586
4587        let result = eval.eval(app_expr, env).unwrap();
4588        if let Value::Integer(n) = result {
4589            assert_eq!(n, 42);
4590        } else {
4591            panic!("Expected integer 42");
4592        }
4593    }
4594
4595    #[test]
4596    fn test_eval_lambda_multiple_body_expressions() {
4597        let mut eval = Evaluator::new();
4598        let env = make_env();
4599
4600        // ((lambda (x) 1 2 x) 99)
4601        // Should return x (last expression)
4602        let params = Value::cons(Value::symbol("x"), Value::Nil);
4603        let body1 = Value::integer(1);
4604        let body2 = Value::integer(2);
4605        let body3 = Value::symbol("x");
4606
4607        let lambda_expr = Value::cons(
4608            Value::symbol("lambda"),
4609            Value::cons(
4610                params,
4611                Value::cons(body1, Value::cons(body2, Value::cons(body3, Value::Nil))),
4612            ),
4613        );
4614
4615        let app_expr = Value::cons(lambda_expr, Value::cons(Value::integer(99), Value::Nil));
4616
4617        let result = eval.eval(app_expr, env).unwrap();
4618        if let Value::Integer(n) = result {
4619            assert_eq!(n, 99);
4620        } else {
4621            panic!("Expected integer 99");
4622        }
4623    }
4624
4625    #[test]
4626    fn test_element_rule_multiple_sosofos_error() {
4627        let mut eval = Evaluator::new();
4628        let env = make_env();
4629
4630        // (element foo expr1 expr2) - should error
4631        let expr = Value::cons(
4632            Value::symbol("element"),
4633            Value::cons(
4634                Value::symbol("foo"),
4635                Value::cons(
4636                    Value::symbol("expr1"),
4637                    Value::cons(Value::symbol("expr2"), Value::Nil),
4638                ),
4639            ),
4640        );
4641
4642        let result = eval.eval(expr, env);
4643        assert!(result.is_err());
4644        let err_msg = result.unwrap_err().to_string();
4645        assert!(err_msg.contains("can only contain one sosofo expression"));
4646        assert!(err_msg.contains("sosofo-append"));
4647    }
4648
4649    #[test]
4650    fn test_element_rule_single_sosofo_ok() {
4651        let mut eval = Evaluator::new();
4652        let env = make_env();
4653
4654        // (element foo expr) - should succeed
4655        let expr = Value::cons(
4656            Value::symbol("element"),
4657            Value::cons(
4658                Value::symbol("foo"),
4659                Value::cons(Value::symbol("expr"), Value::Nil),
4660            ),
4661        );
4662
4663        let result = eval.eval(expr, env);
4664        assert!(result.is_ok());
4665    }
4666
4667    #[test]
4668    fn test_default_rule_multiple_sosofos_error() {
4669        let mut eval = Evaluator::new();
4670        let env = make_env();
4671
4672        // (default expr1 expr2) - should error
4673        let expr = Value::cons(
4674            Value::symbol("default"),
4675            Value::cons(
4676                Value::symbol("expr1"),
4677                Value::cons(Value::symbol("expr2"), Value::Nil),
4678            ),
4679        );
4680
4681        let result = eval.eval(expr, env);
4682        assert!(result.is_err());
4683        let err_msg = result.unwrap_err().to_string();
4684        assert!(err_msg.contains("can only contain one sosofo expression"));
4685        assert!(err_msg.contains("sosofo-append"));
4686    }
4687
4688    #[test]
4689    fn test_default_rule_single_sosofo_ok() {
4690        let mut eval = Evaluator::new();
4691        let env = make_env();
4692
4693        // (default expr) - should succeed
4694        let expr = Value::cons(
4695            Value::symbol("default"),
4696            Value::cons(Value::symbol("expr"), Value::Nil),
4697        );
4698
4699        let result = eval.eval(expr, env);
4700        assert!(result.is_ok());
4701    }
4702
4703    #[test]
4704    fn test_vm_simple_arithmetic() {
4705        let mut eval = Evaluator::new();
4706        eval.enable_vm(); // Enable VM execution
4707
4708        let env = make_env();
4709
4710        // Test: (+ 10 32)
4711        let expr = Value::cons(
4712            Value::symbol("+"),
4713            Value::cons(
4714                Value::Integer(10),
4715                Value::cons(Value::Integer(32), Value::Nil),
4716            ),
4717        );
4718
4719        let result = eval.eval(expr, env);
4720        assert!(result.is_ok());
4721        let value = result.unwrap();
4722        assert!(matches!(value, Value::Integer(42)));
4723    }
4724
4725    #[test]
4726    fn test_vm_vs_tree_walker() {
4727        // Test that VM and tree-walker produce the same results
4728        let expr = Value::cons(
4729            Value::symbol("+"),
4730            Value::cons(
4731                Value::Integer(10),
4732                Value::cons(Value::Integer(32), Value::Nil),
4733            ),
4734        );
4735
4736        // Tree-walker
4737        let mut eval1 = Evaluator::new();
4738        eval1.disable_vm();
4739        let env1 = make_env();
4740        let result1 = eval1.eval(expr.clone(), env1).unwrap();
4741
4742        // VM
4743        let mut eval2 = Evaluator::new();
4744        eval2.enable_vm();
4745        let env2 = make_env();
4746        let result2 = eval2.eval(expr, env2).unwrap();
4747
4748        // Both should produce Integer(42)
4749        assert!(matches!(result1, Value::Integer(42)));
4750        assert!(matches!(result2, Value::Integer(42)));
4751    }
4752}