about summary refs log tree commit diff stats
path: root/js/scripting-lang/docs/baba-yaga/0.0.1/lang.js.html
diff options
context:
space:
mode:
Diffstat (limited to 'js/scripting-lang/docs/baba-yaga/0.0.1/lang.js.html')
-rw-r--r--js/scripting-lang/docs/baba-yaga/0.0.1/lang.js.html3074
1 files changed, 0 insertions, 3074 deletions
diff --git a/js/scripting-lang/docs/baba-yaga/0.0.1/lang.js.html b/js/scripting-lang/docs/baba-yaga/0.0.1/lang.js.html
deleted file mode 100644
index 27fe6d6..0000000
--- a/js/scripting-lang/docs/baba-yaga/0.0.1/lang.js.html
+++ /dev/null
@@ -1,3074 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width,initial-scale=1">
-    <title>lang.js - Documentation</title>
-
-    <script src="scripts/prettify/prettify.js"></script>
-    <script src="scripts/prettify/lang-css.js"></script>
-    <!--[if lt IE 9]>
-      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
-    <![endif]-->
-    <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
-    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
-    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
-</head>
-<body>
-
-<input type="checkbox" id="nav-trigger" class="nav-trigger" />
-<label for="nav-trigger" class="navicon-button x">
-  <div class="navicon"></div>
-</label>
-
-<label for="nav-trigger" class="overlay"></label>
-
-<nav>
-    <li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Tutorials</li><li class="nav-item"><a href="tutorial-00_Introduction.html">00_Introduction</a></li><li class="nav-item"><a href="tutorial-01_Function_Calls.html">01_Function_Calls</a></li><li class="nav-item"><a href="tutorial-02_Function_Composition.html">02_Function_Composition</a></li><li class="nav-item"><a href="tutorial-03_Table_Operations.html">03_Table_Operations</a></li><li class="nav-item"><a href="tutorial-04_Currying.html">04_Currying</a></li><li class="nav-item"><a href="tutorial-05_Pattern_Matching.html">05_Pattern_Matching</a></li><li class="nav-item"><a href="tutorial-06_Immutable_Tables.html">06_Immutable_Tables</a></li><li class="nav-item"><a href="tutorial-07_Function_References.html">07_Function_References</a></li><li class="nav-item"><a href="tutorial-08_Combinators.html">08_Combinators</a></li><li class="nav-item"><a href="tutorial-09_Expression_Based.html">09_Expression_Based</a></li><li class="nav-item"><a href="tutorial-10_Tables_Deep_Dive.html">10_Tables_Deep_Dive</a></li><li class="nav-item"><a href="tutorial-11_Standard_Library.html">11_Standard_Library</a></li><li class="nav-item"><a href="tutorial-12_IO_Operations.html">12_IO_Operations</a></li><li class="nav-item"><a href="tutorial-13_Error_Handling.html">13_Error_Handling</a></li><li class="nav-item"><a href="tutorial-14_Advanced_Combinators.html">14_Advanced_Combinators</a></li><li class="nav-item"><a href="tutorial-15_Integration_Patterns.html">15_Integration_Patterns</a></li><li class="nav-item"><a href="tutorial-16_Best_Practices.html">16_Best_Practices</a></li><li class="nav-item"><a href="tutorial-README.html">README</a></li><li class="nav-heading"><a href="global.html">Globals</a></li><li class="nav-item"><span class="nav-item-type type-member">M</span><span class="nav-item-name"><a href="global.html#callStackTracker">callStackTracker</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#debugError">debugError</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#debugLog">debugLog</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#executeFile">executeFile</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#initializeStandardLibrary">initializeStandardLibrary</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#interpreter">interpreter</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#lexer">lexer</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#main">main</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#parser">parser</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#readFile">readFile</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#run">run</a></span></li>
-</nav>
-
-<div id="main">
-    
-    <h1 class="page-title">lang.js</h1>
-    
-
-    
-
-
-
-    
-    <section>
-        <article>
-            <pre class="prettyprint source linenums"><code>// Baba Yaga
-// Cross-platform scripting language implementation
-// Supports Node.js, Bun, and browser environments
-
-import { lexer, TokenType } from './lexer.js';
-import { parser } from './parser.js';
-
-// Cross-platform environment detection
-const isNode = typeof process !== 'undefined' &amp;&amp; process.versions &amp;&amp; process.versions.node;
-const isBun = typeof process !== 'undefined' &amp;&amp; process.versions &amp;&amp; process.versions.bun;
-const isBrowser = typeof window !== 'undefined' &amp;&amp; typeof document !== 'undefined';
-
-// Cross-platform debug flag
-const DEBUG = (isNode &amp;&amp; process.env.DEBUG) || (isBrowser &amp;&amp; window.DEBUG) || false;
-
-// Cross-platform IO operations
-const createReadline = () => {
-    if (isNode || isBun) {
-        const readline = require('readline');
-        return readline.createInterface({
-            input: process.stdin,
-            output: process.stdout
-        });
-    } else if (isBrowser) {
-        // Browser fallback - use prompt() for now
-        return {
-            question: (prompt, callback) => {
-                const result = window.prompt(prompt);
-                callback(result);
-            },
-            close: () => {}
-        };
-    } else {
-        // Fallback for other environments
-        return {
-            question: (prompt, callback) => {
-                callback("fallback input");
-            },
-            close: () => {}
-        };
-    }
-};
-
-const createFileSystem = () => {
-    if (isNode || isBun) {
-        return require('fs');
-    } else if (isBrowser) {
-        // Browser fallback - return a mock filesystem
-        return {
-            readFile: (path, encoding, callback) => {
-                callback(new Error('File system not available in browser'));
-            },
-            writeFile: (path, data, callback) => {
-                callback(new Error('File system not available in browser'));
-            }
-        };
-    } else {
-        // Fallback for other environments
-        return {
-            readFile: (path, encoding, callback) => {
-                callback(new Error('File system not available in this environment'));
-            },
-            writeFile: (path, data, callback) => {
-                callback(new Error('File system not available in this environment'));
-            }
-        };
-    }
-};
-
-// Cross-platform console output
-const safeConsoleLog = (message) => {
-    if (typeof console !== 'undefined') {
-        console.log(message);
-    }
-};
-
-const safeConsoleError = (message) => {
-    if (typeof console !== 'undefined') {
-        console.error(message);
-    }
-};
-
-// Cross-platform process exit
-const safeExit = (code) => {
-    if (isNode || isBun) {
-        process.exit(code);
-    } else if (isBrowser) {
-        // In browser, we can't exit, but we can throw an error or redirect
-        throw new Error(`Process would exit with code ${code}`);
-    }
-};
-
-/**
- * Environment interface for external system integration
- * 
- * @typedef {Object} Environment
- * @property {Function} getCurrentState - Returns the current state from external system
- * @property {Function} emitValue - Sends a value to the external system
- */
-
-/**
- * Initializes the standard library in the provided scope.
- * 
- * @param {Object} scope - The global scope object to inject functions into
- * @description Injects higher-order functions and combinator functions into the interpreter's global scope.
- * These functions provide functional programming utilities and implement the combinator foundation
- * that reduces parsing ambiguity by translating all operations to function calls.
- * 
- * The standard library includes:
- * - Higher-order functions (map, compose, pipe, apply, filter, reduce, fold, curry)
- * - Arithmetic combinators (add, subtract, multiply, divide, modulo, power, negate)
- * - Comparison combinators (equals, notEquals, lessThan, greaterThan, lessEqual, greaterEqual)
- * - Logical combinators (logicalAnd, logicalOr, logicalXor, logicalNot)
- * - Enhanced combinators (identity, constant, flip, on, both, either)
- * 
- * This approach ensures that user code can access these functions as if they were built-in,
- * without special syntax or reserved keywords. The combinator foundation allows the parser
- * to translate all operators to function calls, eliminating ambiguity while preserving syntax.
- * 
- * Functions are written to check argument types at runtime since the language is dynamically
- * typed and does not enforce arity or types at parse time. The combinator functions are
- * designed to work seamlessly with the parser's operator translation, providing a consistent
- * and extensible foundation for all language operations.
- * 
- * The standard library is the foundation of the combinator-based architecture. Each function
- * is designed to support partial application, enabling currying patterns and function composition.
- * This design choice enables functional programming patterns while maintaining
- * simplicity and consistency across all operations.
- * 
- * Error handling is implemented at the function level, with clear error messages that help
- * users understand what went wrong and how to fix it. This includes type checking for
- * function arguments and validation of input data.
- */
-function initializeStandardLibrary(scope) {
-
-    /**
-     * Map: Apply a function to a value or collection
-     * @param {Function} f - Function to apply
-     * @param {*} x - Value or collection to apply function to
-     * @returns {*} Result of applying f to x
-     * @throws {Error} When first argument is not a function
-     * @description The map function is a fundamental higher-order function that
-     * applies a transformation function to a value or collection. This enables
-     * functional programming patterns where data transformations are expressed
-     * as function applications rather than imperative operations.
-     * 
-     * The function implements APL-inspired element-wise operations for tables:
-     * when x is a table, map applies the function to each value while preserving
-     * the table structure and keys. This reduces the need for explicit loops
-     * and enables declarative data transformation patterns.
-     * 
-     * The function supports partial application: when called with only the function,
-     * it returns a new function that waits for the value. This enables currying
-     * patterns and function composition chains, which are essential for the
-     * combinator-based architecture where all operations are function calls.
-     * 
-     * This design choice aligns with the language's functional foundation and
-     * enables abstractions like `map @double numbers` to transform
-     * every element in a collection without explicit iteration.
-     * 
-     * The function is designed to be polymorphic, working with different data
-     * types including scalars, tables, and arrays. This flexibility enables
-     * consistent data transformation patterns across different data structures.
-     */
-    scope.map = function(f, x) {
-        if (typeof f !== 'function') {
-            throw new Error('map: first argument must be a function');
-        }
-        
-        if (x === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(x) {
-                return scope.map(f, x);
-            };
-        }
-        
-        // Handle tables (APL-style element-wise operations)
-        if (typeof x === 'object' &amp;&amp; x !== null &amp;&amp; !Array.isArray(x)) {
-            const result = {};
-            for (const [key, value] of Object.entries(x)) {
-                result[key] = f(value);
-            }
-            return result;
-        }
-        
-        // Handle arrays (future enhancement)
-        if (Array.isArray(x)) {
-            return x.map(f);
-        }
-        
-        // Default: apply to single value
-        return f(x);
-    };
-    
-    /**
-     * Compose: Combine two functions into a new function (function composition)
-     * @param {Function} f - First function (outer function)
-     * @param {Function} [g] - Second function (optional for partial application)
-     * @returns {Function} Composed function or partially applied function
-     * @throws {Error} When first argument is not a function
-     * @description The compose function is a core functional programming primitive
-     * that combines two functions into a new function. This is the foundation
-     * for the 'via' operator in the language syntax, enabling natural function
-     * composition chains like `f via g via h`.
-     * 
-     * The function implements right-associative composition, meaning that
-     * compose(f, compose(g, h)) creates a function that applies h, then g, then f.
-     * This matches mathematical function composition notation (f ∘ g ∘ h) and
-     * enables natural reading of composition chains from right to left.
-     * 
-     * The 'via' operator translates to compose calls:
-     * - f via g → compose(f, g)
-     * - f via g via h → compose(f, compose(g, h))
-     * - f via g via h via i → compose(f, compose(g, compose(h, i)))
-     * 
-     * This right-associative behavior means that composition chains read naturally
-     * from right to left, matching mathematical notation where (f ∘ g ∘ h)(x) = f(g(h(x))).
-     * 
-     * Partial application support enables currying patterns where functions can
-     * be built incrementally. This is essential for the combinator-based architecture
-     * where operations are built from simple, composable functions.
-     * 
-     * Examples:
-     * - compose(double, increment)(5) → double(increment(5)) → double(6) → 12
-     * - compose(increment, double)(5) → increment(double(5)) → increment(10) → 11
-     * - double via increment 5 → compose(double, increment)(5) → 12
-     * - increment via double via square 3 → compose(increment, compose(double, square))(3) → 19
-     */
-    scope.compose = function(f, g) {
-        if (typeof f !== 'function') {
-            throw new Error(`compose: first argument must be a function, got ${typeof f}`);
-        }
-        
-        if (g === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(g) {
-                if (typeof g !== 'function') {
-                    throw new Error(`compose: second argument must be a function, got ${typeof g}`);
-                }
-                return function(x) {
-                    return f(g(x));
-                };
-            };
-        }
-        
-        if (typeof g !== 'function') {
-            throw new Error(`compose: second argument must be a function, got ${typeof g}`);
-        }
-        
-        return function(x) {
-            return f(g(x));
-        };
-    };
-    
-    /**
-     * Curry: Apply a function to arguments (simplified currying)
-     * @param {Function} f - Function to curry
-     * @param {*} x - First argument
-     * @param {*} y - Second argument
-     * @returns {*} Result of applying f to x and y
-     * @throws {Error} When first argument is not a function
-     * @description The curry function provides a simplified currying mechanism
-     * that allows functions to be applied to arguments incrementally. When called
-     * with fewer arguments than the function expects, it returns a new function
-     * that waits for the remaining arguments.
-     * 
-     * This function is designed to work with the parser's one-by-one argument
-     * application system, where multi-argument function calls are translated to
-     * nested apply calls. The nested partial application checks ensure that
-     * functions return partially applied functions until all arguments are received.
-     */
-    scope.curry = function(f, x, y) { 
-        if (typeof f !== 'function') {
-            throw new Error('curry: first argument must be a function');
-        }
-        
-        if (x === undefined) {
-            // Partial application: return a function that waits for the remaining arguments
-            return function(x, y) {
-                if (y === undefined) {
-                    // Still partial application
-                    return function(y) {
-                        return f(x, y);
-                    };
-                }
-                return f(x, y);
-            };
-        }
-        
-        if (y === undefined) {
-            // Partial application: return a function that waits for the last argument
-            return function(y) {
-                return f(x, y);
-            };
-        }
-        
-        // Full application: apply the function to all arguments
-        return f(x, y);
-    };
-    
-    /**
-     * Apply: Apply a function to an argument (explicit function application)
-     * @param {Function} f - Function to apply
-     * @param {*} x - Argument to apply function to
-     * @returns {*} Result of applying f to x
-     * @throws {Error} When first argument is not a function
-     * @description The apply function is the fundamental mechanism for function
-     * application in the language. It enables the juxtaposition-based function
-     * application syntax (f x) by providing an explicit function application
-     * primitive. This function is called by the parser whenever function
-     * application is detected, ensuring consistent semantics across all
-     * function calls.
-     * 
-     * This function is the core mechanism that enables the parser's juxtaposition
-     * detection. When the parser encounters `f x`, it generates `apply(f, x)`,
-     * which this function handles. This design reduces the need for special
-     * syntax for function calls while maintaining clear precedence rules.
-     * 
-     * The function supports partial application: when called with only the function,
-     * it returns a new function that waits for the argument. This enables the
-     * parser to build function application chains incrementally, supporting
-     * both immediate evaluation and deferred execution patterns.
-     * 
-     * This partial application support is essential for the parser's left-associative
-     * function application model, where `f g x` becomes `apply(apply(f, g), x)`.
-     * The nested partial application ensures that each step returns a function
-     * until all arguments are provided.
-     */
-    scope.apply = function(f, x) { 
-        if (typeof f !== 'function') {
-            throw new Error('apply: first argument must be a function');
-        }
-        
-        if (x === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(x) {
-                return f(x);
-            };
-        }
-        
-        // Full application: apply the function to the argument
-        return f(x);
-    };
-    
-    /**
-     * Pipe: Compose functions in left-to-right order (opposite of compose)
-     * @param {Function} f - First function
-     * @param {Function} [g] - Second function (optional for partial application)
-     * @returns {Function} Function that applies the functions in left-to-right order
-     * @throws {Error} When first argument is not a function
-     * @description The pipe function provides an alternative to compose that
-     * applies functions in left-to-right order, which is often more intuitive
-     * for data processing pipelines. This enables functional programming patterns
-     * where data flows through a series of transformations in a natural reading order.
-     * 
-     * The function implements left-associative composition, meaning that
-     * pipe(f, pipe(g, h)) creates a function that applies f, then g, then h.
-     * This is the opposite of compose and matches the natural reading order
-     * for data transformation pipelines, making it intuitive for programmers
-     * who think in terms of data flow from left to right.
-     * 
-     * Like compose, it supports partial application for currying patterns.
-      * This enables building transformation pipelines incrementally,
- * which is essential for the combinator-based architecture where
-     * operations are built from simple, composable functions.
-     * 
-     * The left-associative design choice makes pipe ideal for data processing
-     * workflows where each step transforms the data and passes it to the next
-     * step, creating a natural pipeline that reads like a sequence of operations.
-     */
-    scope.pipe = function(f, g) {
-        if (typeof f !== 'function') {
-            throw new Error(`pipe: first argument must be a function, got ${typeof f}`);
-        }
-        
-        if (g === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(g) {
-                if (typeof g !== 'function') {
-                    throw new Error(`pipe: second argument must be a function, got ${typeof g}`);
-                }
-                return function(x) {
-                    return g(f(x));
-                };
-            };
-        }
-        
-        if (typeof g !== 'function') {
-            throw new Error(`pipe: second argument must be a function, got ${typeof g}`);
-        }
-        
-        return function(x) {
-            return g(f(x));
-        };
-    };
-    
-    /**
-     * Filter: Filter a value or collection based on a predicate
-     * @param {Function} p - Predicate function
-     * @param {*} x - Value or collection to test
-     * @returns {*|0} The value if predicate is true, filtered collection for tables, 0 otherwise
-     * @throws {Error} When first argument is not a function
-     * @description The filter function applies a predicate to a value or collection,
-     * returning the value if the predicate is true, or a filtered collection for tables.
-     * This enables functional programming patterns where data selection is expressed
-     * as predicate application rather than imperative filtering loops.
-     * 
-     * The function implements APL-inspired element-wise filtering for tables:
-     * when x is a table, filter applies the predicate to each value and returns
-     * a new table containing only the key-value pairs where the predicate returns true.
-     * This reduces the need for explicit loops and enables declarative data
-     * selection patterns.
-     * 
-     * The function supports partial application: when called with only the predicate,
-     * it returns a new function that waits for the value. This enables currying
-     * patterns and function composition chains, which are essential for the
-     * combinator-based architecture where all operations are function calls.
-     * 
-     * This design choice aligns with the language's functional foundation and
-     * enables abstractions like `filter @isEven numbers` to select
-     * elements from a collection without explicit iteration.
-     */
-    scope.filter = function(p, x) { 
-        if (typeof p !== 'function') {
-            throw new Error('filter: first argument must be a function');
-        }
-        
-        if (x === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(x) {
-                return scope.filter(p, x);
-            };
-        }
-        
-        // Handle tables (APL-style element-wise filtering)
-        if (typeof x === 'object' &amp;&amp; x !== null &amp;&amp; !Array.isArray(x)) {
-            const result = {};
-            for (const [key, value] of Object.entries(x)) {
-                if (p(value)) {
-                    result[key] = value;
-                }
-            }
-            return result;
-        }
-        
-        // Handle arrays (future enhancement)
-        if (Array.isArray(x)) {
-            return x.filter(p);
-        }
-        
-        // Default: apply predicate to single value
-        return p(x) ? x : 0;
-    };
-    
-    /**
-     * Reduce: Reduce two values using a binary function
-     * @param {Function} f - Binary function
-     * @param {*} init - Initial value
-     * @param {*} x - Second value
-     * @returns {*} Result of applying f to init and x
-     * @throws {Error} When first argument is not a function
-     * @description The reduce function applies a binary function to an initial value
-     * and a second value, returning the result. This is a simplified version of
-     * traditional reduce that works with pairs of values rather than collections.
-     * 
-     * The function supports partial application with nested checks to handle the
-     * parser's one-by-one argument application system. When called with only the
-     * function, it returns a function that waits for the initial value. When called
-     * with the function and initial value, it returns a function that waits for
-     * the second value. This enables currying patterns and incremental function
-     * application.
-     */
-    scope.reduce = function(f, init, x) { 
-        if (DEBUG) {
-            safeConsoleLog(`[DEBUG] reduce: f =`, typeof f, f);
-            safeConsoleLog(`[DEBUG] reduce: init =`, init);
-            safeConsoleLog(`[DEBUG] reduce: x =`, x);
-        }
-        
-        if (typeof f !== 'function') {
-            throw new Error('reduce: first argument must be a function');
-        }
-        
-        if (init === undefined) {
-            // Partial application: return a function that waits for the remaining arguments
-            return function(init, x) {
-                if (DEBUG) {
-                    safeConsoleLog(`[DEBUG] reduce returned function: f =`, typeof f, f);
-                    safeConsoleLog(`[DEBUG] reduce returned function: init =`, init);
-                    safeConsoleLog(`[DEBUG] reduce returned function: x =`, x);
-                }
-                if (x === undefined) {
-                    // Still partial application
-                    return function(x) {
-                        return scope.reduce(f, init, x);
-                    };
-                }
-                return scope.reduce(f, init, x);
-            };
-        }
-        
-        if (x === undefined) {
-            // Partial application: return a function that waits for the last argument
-            return function(x) {
-                return scope.reduce(f, init, x);
-            };
-        }
-        
-        // Handle tables (reduce all values in the table)
-        if (typeof x === 'object' &amp;&amp; x !== null &amp;&amp; !Array.isArray(x)) {
-            let result = init;
-            for (const [key, value] of Object.entries(x)) {
-                result = f(result, value, key);
-            }
-            return result;
-        }
-        
-        // Handle arrays (future enhancement)
-        if (Array.isArray(x)) {
-            return x.reduce(f, init);
-        }
-        
-        // Default: apply the function to init and x (original behavior)
-        return f(init, x);
-    };
-    
-    /**
-     * Fold: Same as reduce, but more explicit about the folding direction
-     * @param {Function} f - Binary function
-     * @param {*} init - Initial value
-     * @param {*} x - Second value
-     * @returns {*} Result of applying f to init and x
-     * @throws {Error} When first argument is not a function
-     */
-    scope.fold = function(f, init, x) { 
-        if (typeof f !== 'function') {
-            throw new Error('fold: first argument must be a function');
-        }
-        
-        if (init === undefined) {
-            // Partial application: return a function that waits for the remaining arguments
-            return function(init, x) {
-                if (x === undefined) {
-                    // Still partial application
-                    return function(x) {
-                        return f(init, x);
-                    };
-                }
-                return f(init, x);
-            };
-        }
-        
-        if (x === undefined) {
-            // Partial application: return a function that waits for the last argument
-            return function(x) {
-                return f(init, x);
-            };
-        }
-        
-        // Full application: apply the function to all arguments
-        return f(init, x);
-    };
-    
-    // ===== ARITHMETIC COMBINATORS =====
-    
-    /**
-     * Add: Add two numbers
-     * @param {number} x - First number
-     * @param {number} y - Second number
-     * @returns {number} Sum of x and y
-     * @description The add function is a fundamental arithmetic combinator that
-     * implements addition. This function is called by the parser when the '+'
-     * operator is encountered, translating `x + y` into `add(x, y)`.
-     * 
-     * As a combinator function, add supports partial application and can be used
-     * in function composition chains. This enables patterns like `map @add 10`
-     * to add 10 to every element in a collection, or `each @add table1 table2`
-     * for element-wise addition of corresponding table elements.
-     * 
-     * The function is designed to work seamlessly with the parser's operator
-     * translation system, providing consistent semantics for all arithmetic
-     * operations through the combinator foundation.
-     */
-    scope.add = function(x, y) {
-        if (y === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(y) {
-                return x + y;
-            };
-        }
-        return x + y;
-    };
-    
-    /**
-     * Subtract: Subtract second number from first
-     * @param {number} x - First number
-     * @param {number} y - Second number
-     * @returns {number} Difference of x and y
-     */
-    scope.subtract = function(x, y) {
-        if (y === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(y) {
-                return x - y;
-            };
-        }
-        return x - y;
-    };
-    
-    /**
-     * Multiply: Multiply two numbers
-     * @param {number} x - First number
-     * @param {number} y - Second number
-     * @returns {number} Product of x and y
-     * @description The multiply function is a fundamental arithmetic combinator that
-     * implements multiplication. This function is called by the parser when the '*'
-     * operator is encountered, translating `x * y` into `multiply(x, y)`.
-     * 
-     * As a combinator function, multiply supports partial application and can be used
-     * in function composition chains. This enables patterns like `map @multiply 2`
-     * to double every element in a collection, or `each @multiply table1 table2`
-     * for element-wise multiplication of corresponding table elements.
-     * 
-     * The function is designed to work seamlessly with the parser's operator
-     * translation system, providing consistent semantics for all arithmetic
-     * operations through the combinator foundation.
-     */
-    scope.multiply = function(x, y) {
-        if (y === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(y) {
-                return x * y;
-            };
-        }
-        return x * y;
-    };
-    
-    /**
-     * Divide: Divide first number by second
-     * @param {number} x - First number
-     * @param {number} y - Second number
-     * @returns {number} Quotient of x and y
-     * @throws {Error} When second argument is zero
-     */
-    scope.divide = function(x, y) {
-        if (y === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(y) {
-                if (y === 0) {
-                    throw new Error('Division by zero');
-                }
-                return x / y;
-            };
-        }
-        if (y === 0) {
-            throw new Error('Division by zero');
-        }
-        return x / y;
-    };
-    
-    /**
-     * Modulo: Get remainder of division
-     * @param {number} x - First number
-     * @param {number} y - Second number
-     * @returns {number} Remainder of x divided by y
-     */
-    scope.modulo = function(x, y) {
-        if (y === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(y) {
-                return x % y;
-            };
-        }
-        return x % y;
-    };
-    
-    /**
-     * Power: Raise first number to power of second
-     * @param {number} x - Base number
-     * @param {number} y - Exponent
-     * @returns {number} x raised to the power of y
-     */
-    scope.power = function(x, y) {
-        if (y === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(y) {
-                return Math.pow(x, y);
-            };
-        }
-        return Math.pow(x, y);
-    };
-    
-    /**
-     * Negate: Negate a number
-     * @param {number} x - Number to negate
-     * @returns {number} Negated value of x
-     */
-    scope.negate = function(x) {
-        return -x;
-    };
-    
-    // ===== COMPARISON COMBINATORS =====
-    
-    /**
-     * Equals: Check if two values are equal
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if x equals y
-     */
-    scope.equals = function(x, y) {
-        return x === y;
-    };
-    
-    /**
-     * NotEquals: Check if two values are not equal
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if x does not equal y
-     */
-    scope.notEquals = function(x, y) {
-        return x !== y;
-    };
-    
-    /**
-     * LessThan: Check if first value is less than second
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if x &lt; y
-     */
-    scope.lessThan = function(x, y) {
-        return x &lt; y;
-    };
-    
-    /**
-     * GreaterThan: Check if first value is greater than second
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if x > y
-     */
-    scope.greaterThan = function(x, y) {
-        return x > y;
-    };
-    
-    /**
-     * LessEqual: Check if first value is less than or equal to second
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if x &lt;= y
-     */
-    scope.lessEqual = function(x, y) {
-        return x &lt;= y;
-    };
-    
-    /**
-     * GreaterEqual: Check if first value is greater than or equal to second
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if x >= y
-     */
-    scope.greaterEqual = function(x, y) {
-        return x >= y;
-    };
-    
-    // ===== LOGICAL COMBINATORS =====
-    
-    /**
-     * LogicalAnd: Logical AND of two values
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if both x and y are truthy
-     */
-    scope.logicalAnd = function(x, y) {
-        return !!(x &amp;&amp; y);
-    };
-    
-    /**
-     * LogicalOr: Logical OR of two values
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if either x or y is truthy
-     */
-    scope.logicalOr = function(x, y) {
-        return !!(x || y);
-    };
-    
-    /**
-     * LogicalXor: Logical XOR of two values
-     * @param {*} x - First value
-     * @param {*} y - Second value
-     * @returns {boolean} True if exactly one of x or y is truthy
-     */
-    scope.logicalXor = function(x, y) {
-        return !!((x &amp;&amp; !y) || (!x &amp;&amp; y));
-    };
-    
-    /**
-     * LogicalNot: Logical NOT of a value
-     * @param {*} x - Value to negate
-     * @returns {boolean} True if x is falsy, false if x is truthy
-     */
-    scope.logicalNot = function(x) {
-        return !x;
-    };
-    
-    // ===== ASSIGNMENT COMBINATOR =====
-    
-    /**
-     * Assign: Assign a value to a variable name
-     * @param {string} name - Variable name
-     * @param {*} value - Value to assign
-     * @returns {*} The assigned value
-     * @throws {Error} When trying to reassign an immutable variable
-     * @note This function needs access to the global scope, so it will be
-     *       set up during interpreter initialization
-     */
-    // Note: assign will be set up in the interpreter with access to globalScope
-    
-    // ===== ENHANCED HIGHER-ORDER COMBINATORS =====
-    
-    /**
-     * Identity: Return the input unchanged
-     * @param {*} x - Any value
-     * @returns {*} The same value
-     */
-    scope.identity = function(x) {
-        return x;
-    };
-    
-    /**
-     * Constant: Create a function that always returns the same value
-     * @param {*} x - Value to return
-     * @param {*} [y] - Optional second argument (ignored)
-     * @returns {*} The value x, or a function if only one argument provided
-     */
-    scope.constant = function(x, y) {
-        if (arguments.length === 2) {
-            return x;
-        } else {
-            return function(y) {
-                return x;
-            };
-        }
-    };
-    
-    /**
-     * Flip: Flip the order of arguments for a binary function
-     * @param {Function} f - Binary function
-     * @param {*} [x] - Optional first argument
-     * @param {*} [y] - Optional second argument
-     * @returns {Function|*} Function with flipped argument order, or result if arguments provided
-     */
-    scope.flip = function(f, x, y) {
-        if (arguments.length === 3) {
-            return f(y, x);
-        } else {
-            return function(x, y) {
-                return f(y, x);
-            };
-        }
-    };
-    
-    /**
-     * On: Apply a function to the results of another function
-     * @param {Function} f - Outer function
-     * @param {Function} g - Inner function
-     * @returns {Function} Function that applies f to the results of g
-     */
-    scope.on = function(f, g) {
-        return function(x, y) {
-            return f(g(x), g(y));
-        };
-    };
-    
-    /**
-     * Both: Check if both predicates are true
-     * @param {Function} f - First predicate
-     * @param {Function} g - Second predicate
-     * @returns {Function} Function that returns true if both predicates are true
-     */
-    scope.both = function(f, g) {
-        return function(x) {
-            return f(x) &amp;&amp; g(x);
-        };
-    };
-    
-    /**
-     * Either: Check if either predicate is true
-     * @param {Function} f - First predicate
-     * @param {Function} g - Second predicate
-     * @returns {Function} Function that returns true if either predicate is true
-     */
-    scope.either = function(f, g) {
-        return function(x) {
-            return f(x) || g(x);
-        };
-    };
-    
-    /**
-     * Each: Multi-argument element-wise operations for tables and scalars
-     * @param {Function} f - Function to apply element-wise
-     * @param {*} x - First argument (table or scalar)
-     * @returns {Function|*} Function for partial application or result of element-wise application
-     * @throws {Error} When first argument is not a function
-     * @description The each combinator provides APL-inspired element-wise operations
-     * for multi-argument functions over table structures. This is the primary mechanism
-     * for combining multiple tables or tables with scalars in element-wise fashion.
-     * 
-     * The function is designed for multi-argument operations and aligns with the parser's
-     * apply mechanism. When x is a table, each returns a function that waits for the
-     * second argument (y), enabling the parser to build `apply(apply(each, f), x)` chains
-     * that resolve to element-wise operations when y is provided.
-     * 
-     * Key behaviors:
-     * - Table + Scalar: Applies f to each element of the table with the scalar as second argument
-     * - Table + Table: Applies f to corresponding elements from both tables
-     * - Scalar + Table: Uses map to apply f with the scalar as first argument to each table element
-     * - Scalar + Scalar: Falls back to normal function application for backward compatibility
-     * 
-     * This design choice enables multi-argument element-wise operations like
-     * `each @add table1 table2` for element-wise addition, while maintaining compatibility
-     * with the parser's two-argument apply model. The function is specifically designed
-     * for multi-argument operations, distinguishing it from map which is for single-table
-     * transformations.
-     */
-    scope.each = function(f, x) {
-        if (DEBUG) {
-            safeConsoleLog(`[DEBUG] each called with: f=${typeof f}, x=${typeof x}`);
-            safeConsoleLog(`[DEBUG] x value:`, x);
-        }
-        
-        if (typeof f !== 'function') {
-            throw new Error('each: first argument must be a function, got ' + typeof f);
-        }
-        
-        if (x === undefined) {
-            // Partial application: return a function that waits for the second argument
-            return function(x) {
-                return scope.each(f, x);
-            };
-        }
-        
-        // Check if x is a table
-        const isXTable = typeof x === 'object' &amp;&amp; x !== null &amp;&amp; !Array.isArray(x);
-        
-        if (isXTable) {
-            // x is a table - always return a function that can handle the second argument
-            return function(y) {
-                // Check if y is a table
-                const isYTable = typeof y === 'object' &amp;&amp; y !== null &amp;&amp; !Array.isArray(y);
-                
-                if (!isYTable) {
-                    // x is a table, y is not a table - apply function to each element of x with y as second argument
-                    const result = {};
-                    for (const [key, value] of Object.entries(x)) {
-                        result[key] = f(value, y);
-                    }
-                    return result;
-                }
-                
-                // Both x and y are tables - they should have the same keys
-                const result = {};
-                for (const [key, value] of Object.entries(x)) {
-                    if (y.hasOwnProperty(key)) {
-                        result[key] = f(value, y[key]);
-                    }
-                }
-                return result;
-            };
-        }
-        
-        // x is not a table, return a function that waits for the second argument
-        return function(y) {
-            // Check if y is a table
-            const isYTable = typeof y === 'object' &amp;&amp; y !== null &amp;&amp; !Array.isArray(y);
-            
-            if (!isYTable) {
-                // No tables, apply normally (backward compatibility)
-                return f(x, y);
-            }
-            
-            // x is not a table, y is a table - use map
-            return scope.map(function(val) { return f(x, val); }, y);
-        };
-    };
-    
-    // ===== TABLE OPERATIONS NAMESPACE (t.) =====
-    
-    /**
-     * Table operations namespace (t.)
-     * @description Provides immutable table operations that always return new tables,
-     * never modifying the original. This namespace implements APL-inspired element-wise
-     * operations and functional table manipulation patterns.
-     * 
-     * All operations in this namespace are designed to work with the language's
-     * immutable data philosophy, where data transformations create new structures
-     * rather than modifying existing ones. This enables functional programming
-     * patterns and reduces side effects from table operations.
-     * 
-     * The namespace provides both basic table operations (get, set, delete, merge)
-     * and higher-order operations (map, filter, reduce) that work element-wise
-     * on table values. This design choice enables data transformation
-     * patterns while maintaining the functional programming principles of the language.
-     * 
-     * Key design principles:
-     * - Immutability: All operations return new tables, never modify originals
-     * - Element-wise operations: Functions operate on table values, not structure
-     * - Partial application: All functions support currying patterns
-     * - Functional consistency: Operations work with the combinator foundation
-     */
-    scope.t = {
-        /**
-         * Map: Apply a function to each value in a table
-         * @param {Function} f - Function to apply
-         * @param {Object} table - Table to map over
-         * @returns {Object} New table with transformed values
-         * @throws {Error} When first argument is not a function or second is not a table
-         */
-        map: function(f, table) {
-            if (typeof f !== 'function') {
-                throw new Error('t.map: first argument must be a function');
-            }
-            
-            if (table === undefined) {
-                // Partial application: return a function that waits for the table
-                return function(table) {
-                    return scope.t.map(f, table);
-                };
-            }
-            
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.map: second argument must be a table');
-            }
-            
-            const result = {};
-            for (const [key, value] of Object.entries(table)) {
-                result[key] = f(value);
-            }
-            return result;
-        },
-        
-        /**
-         * Filter: Filter table values based on a predicate
-         * @param {Function} p - Predicate function
-         * @param {Object} table - Table to filter
-         * @returns {Object} New table with only values that pass the predicate
-         * @throws {Error} When first argument is not a function or second is not a table
-         */
-        filter: function(p, table) {
-            if (typeof p !== 'function') {
-                throw new Error('t.filter: first argument must be a function');
-            }
-            
-            if (table === undefined) {
-                // Partial application: return a function that waits for the table
-                return function(table) {
-                    return scope.t.filter(p, table);
-                };
-            }
-            
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.filter: second argument must be a table');
-            }
-            
-            const result = {};
-            for (const [key, value] of Object.entries(table)) {
-                if (p(value)) {
-                    result[key] = value;
-                }
-            }
-            return result;
-        },
-        
-        /**
-         * Reduce: Reduce all values in a table using a binary function
-         * @param {Function} f - Binary function
-         * @param {*} init - Initial value
-         * @param {Object} table - Table to reduce
-         * @returns {*} Result of reducing all values
-         * @throws {Error} When first argument is not a function or third is not a table
-         */
-        reduce: function(f, init, table) {
-            if (typeof f !== 'function') {
-                throw new Error('t.reduce: first argument must be a function');
-            }
-            
-            if (init === undefined) {
-                // Partial application: return a function that waits for the remaining arguments
-                return function(init, table) {
-                    if (table === undefined) {
-                        // Still partial application
-                        return function(table) {
-                            return scope.t.reduce(f, init, table);
-                        };
-                    }
-                    return scope.t.reduce(f, init, table);
-                };
-            }
-            
-            if (table === undefined) {
-                // Partial application: return a function that waits for the table
-                return function(table) {
-                    return scope.t.reduce(f, init, table);
-                };
-            }
-            
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.reduce: third argument must be a table');
-            }
-            
-            let result = init;
-            for (const [key, value] of Object.entries(table)) {
-                result = f(result, value, key);
-            }
-            return result;
-        },
-        
-        /**
-         * Set: Immutably set a key-value pair in a table
-         * @param {Object} table - Table to modify
-         * @param {*} key - Key to set
-         * @param {*} value - Value to set
-         * @returns {Object} New table with the key-value pair set
-         * @throws {Error} When first argument is not a table
-         */
-        set: function(table, key, value) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.set: first argument must be a table');
-            }
-            
-            if (key === undefined) {
-                // Partial application: return a function that waits for the remaining arguments
-                return function(key, value) {
-                    if (value === undefined) {
-                        // Still partial application
-                        return function(value) {
-                            return scope.t.set(table, key, value);
-                        };
-                    }
-                    return scope.t.set(table, key, value);
-                };
-            }
-            
-            if (value === undefined) {
-                // Partial application: return a function that waits for the value
-                return function(value) {
-                    return scope.t.set(table, key, value);
-                };
-            }
-            
-            return { ...table, [key]: value };
-        },
-        
-        /**
-         * Delete: Immutably delete a key from a table
-         * @param {Object} table - Table to modify
-         * @param {*} key - Key to delete
-         * @returns {Object} New table without the specified key
-         * @throws {Error} When first argument is not a table
-         */
-        delete: function(table, key) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.delete: first argument must be a table');
-            }
-            
-            if (key === undefined) {
-                // Partial application: return a function that waits for the key
-                return function(key) {
-                    return scope.t.delete(table, key);
-                };
-            }
-            
-            const result = { ...table };
-            delete result[key];
-            return result;
-        },
-        
-        /**
-         * Merge: Immutably merge two tables
-         * @param {Object} table1 - First table
-         * @param {Object} table2 - Second table (values override table1)
-         * @returns {Object} New merged table
-         * @throws {Error} When either argument is not a table
-         */
-        merge: function(table1, table2) {
-            if (typeof table1 !== 'object' || table1 === null) {
-                throw new Error('t.merge: first argument must be a table');
-            }
-            
-            if (table2 === undefined) {
-                // Partial application: return a function that waits for the second table
-                return function(table2) {
-                    return scope.t.merge(table1, table2);
-                };
-            }
-            
-            if (typeof table2 !== 'object' || table2 === null) {
-                throw new Error('t.merge: second argument must be a table');
-            }
-            
-            return { ...table1, ...table2 };
-        },
-        
-        /**
-         * Pairs: Get all key-value pairs from a table
-         * @param {Object} table - Table to get pairs from
-         * @returns {Array} Array of [key, value] pairs
-         * @throws {Error} When argument is not a table
-         */
-        pairs: function(table) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.pairs: argument must be a table');
-            }
-            return Object.entries(table);
-        },
-        
-        /**
-         * Keys: Get all keys from a table
-         * @param {Object} table - Table to get keys from
-         * @returns {Array} Array of keys
-         * @throws {Error} When argument is not a table
-         */
-        keys: function(table) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.keys: argument must be a table');
-            }
-            return Object.keys(table);
-        },
-        
-        /**
-         * Values: Get all values from a table
-         * @param {Object} table - Table to get values from
-         * @returns {Array} Array of values
-         * @throws {Error} When argument is not a table
-         */
-        values: function(table) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.values: argument must be a table');
-            }
-            return Object.values(table);
-        },
-        
-        /**
-         * Length: Get the number of key-value pairs in a table
-         * @param {Object} table - Table to measure
-         * @returns {number} Number of key-value pairs
-         * @throws {Error} When argument is not a table
-         */
-        length: function(table) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.length: argument must be a table');
-            }
-            return Object.keys(table).length;
-        },
-        
-        /**
-         * Has: Check if a table has a specific key
-         * @param {Object} table - Table to check
-         * @param {*} key - Key to check for
-         * @returns {boolean} True if key exists, false otherwise
-         * @throws {Error} When first argument is not a table
-         */
-        has: function(table, key) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.has: first argument must be a table');
-            }
-            
-            if (key === undefined) {
-                // Partial application: return a function that waits for the key
-                return function(key) {
-                    return scope.t.has(table, key);
-                };
-            }
-            
-            return table.hasOwnProperty(key);
-        },
-        
-        /**
-         * Get: Safely get a value from a table with optional default
-         * @param {Object} table - Table to get from
-         * @param {*} key - Key to get
-         * @param {*} defaultValue - Default value if key doesn't exist
-         * @returns {*} Value at key or default value
-         * @throws {Error} When first argument is not a table
-         */
-        get: function(table, key, defaultValue) {
-            if (typeof table !== 'object' || table === null) {
-                throw new Error('t.get: first argument must be a table');
-            }
-            
-            if (key === undefined) {
-                // Partial application: return a function that waits for the remaining arguments
-                return function(key, defaultValue) {
-                    if (defaultValue === undefined) {
-                        // Still partial application
-                        return function(defaultValue) {
-                            return scope.t.get(table, key, defaultValue);
-                        };
-                    }
-                    return scope.t.get(table, key, defaultValue);
-                };
-            }
-            
-            if (defaultValue === undefined) {
-                // Partial application: return a function that waits for the default value
-                return function(defaultValue) {
-                    return scope.t.get(table, key, defaultValue);
-                };
-            }
-            
-            return table.hasOwnProperty(key) ? table[key] : defaultValue;
-        }
-    };
-}
-
-/**
- * Interpreter: Walks the AST and evaluates each node using the combinator foundation.
- * 
- * @param {ASTNode} ast - Abstract Syntax Tree to evaluate
- * @param {Environment} [environment=null] - External environment for IO operations
- * @param {Object} [initialState={}] - Initial state for the interpreter
- * @returns {*} The result of evaluating the AST, or a Promise for async operations
- * @throws {Error} For evaluation errors like division by zero, undefined variables, etc.
- * 
- * @description Evaluates an AST by walking through each node and performing the
- * corresponding operations. Manages scope, handles function calls, and supports
- * both synchronous and asynchronous operations.
- * 
- * The interpreter implements a combinator-based architecture where all operations
- * are executed through function calls to standard library combinators. This design
- * reduces parsing ambiguity while preserving intuitive syntax. The parser translates
- * all operators (+, -, *, /, etc.) into FunctionCall nodes that reference combinator
- * functions, ensuring consistent semantics across all operations.
- * 
- * Key architectural features:
- * - Combinator Foundation: All operations are function calls to standard library combinators
- * - Scope Management: Prototypal inheritance for variable lookup and function definitions
- * - Forward Declaration: Recursive functions are supported through placeholder creation
- * - Error Handling: Comprehensive error detection and reporting with call stack tracking
- * - Debug Support: Optional debug mode for development and troubleshooting
- * - IO Operations: Support for input/output operations through environment interface
- * 
- * The interpreter processes legacy operator expressions (PlusExpression, MinusExpression, etc.)
- * for backward compatibility, but the parser now generates FunctionCall nodes for all operators,
- * which are handled by the standard library combinator functions. This ensures that all
- * operations follow the same execution model and can be extended by adding new combinator
- * functions to the standard library.
- * 
- * The interpreter uses a global scope for variable storage and function definitions.
- * Each function call creates a new scope (using prototypal inheritance) to implement 
- * lexical scoping. Immutability is enforced by preventing reassignment in the 
- * global scope.
- * 
- * The interpreter is split into three functions: evalNode (global), 
- * localEvalNodeWithScope (for function bodies), and localEvalNode (for internal 
- * recursion). This separation allows for correct scope handling and easier debugging.
- * 
- * Recursive function support is implemented using a forward declaration pattern:
- * a placeholder function is created in the global scope before evaluation, allowing
- * the function body to reference itself during evaluation.
- * 
- * The combinator foundation ensures that all operations are executed through
- * function calls, providing a consistent and extensible execution model. This
- * approach enables abstractions and reduces the need for special
- * handling of different operator types in the interpreter.
- * 
- * The interpreter supports both synchronous and asynchronous operations. IO operations
- * like input and output can return Promises, allowing for non-blocking execution
- * when interacting with external systems or user input.
- */
-function interpreter(ast, environment = null, initialState = {}) {
-    const globalScope = { ...initialState };
-    initializeStandardLibrary(globalScope);
-    
-    // Track whether any IO operations have been performed
-    let ioOperationsPerformed = false;
-    
-    // Debug: Check if combinators are available
-    if (DEBUG) {
-        safeConsoleLog('[DEBUG] Available functions in global scope:', Object.keys(globalScope));
-        safeConsoleLog('[DEBUG] add function exists:', typeof globalScope.add === 'function');
-        safeConsoleLog('[DEBUG] subtract function exists:', typeof globalScope.subtract === 'function');
-    }
-    
-    // Reset call stack tracker at the start of interpretation
-    callStackTracker.reset();
-    
-    /**
-     * Evaluates AST nodes in the global scope using the combinator foundation.
-     * 
-     * @param {ASTNode} node - AST node to evaluate
-     * @returns {*} The result of evaluating the node
-     * @throws {Error} For evaluation errors
-     * 
-     * @description Main evaluation function that handles all node types in the
-     * global scope context. This function processes the core language constructs
-     * and delegates to combinator functions for all operations.
-     * 
-     * The function implements the forward declaration pattern for recursive functions:
-     * when a function assignment is detected, a placeholder is created in the global
-     * scope before evaluation, allowing the function body to reference itself.
-     * This pattern enables natural recursive function definitions without requiring
-     * special syntax or pre-declaration.
-     * 
-     * This function is the primary entry point for AST evaluation and handles
-     * all the core language constructs including literals, operators (translated
-     * to combinator calls), function definitions, and control structures. It
-     * ensures that all operations are executed through the combinator foundation,
-     * providing consistent semantics across the language.
-     * 
-     * The function processes legacy operator expressions (PlusExpression, MinusExpression, etc.)
-     * for backward compatibility, but the parser now generates FunctionCall nodes for
-     * all operators, which are handled by the standard library combinator functions.
-     * This design ensures that all operations follow the same execution model and
-     * can be extended by adding new combinator functions to the standard library.
-     * 
-     * Key evaluation patterns:
-     * - Literals: Direct value return
-     * - FunctionCall: Delegates to standard library combinator functions
-     * - Assignment: Creates variables in global scope with forward declaration support
-     * - WhenExpression: Pattern matching with wildcard support
-     * - TableLiteral: Creates immutable table structures
-     * - TableAccess: Safe property access with error handling
-     * - IO Operations: Handles input/output through environment interface
-     * 
-     * The function maintains call stack tracking for debugging and error reporting.
-     * This enables detailed error messages that include the call chain leading to
-     * the error, making it easier to debug programs.
-     * 
-     * Error handling is comprehensive, with specific error messages for common
-     * issues like undefined variables, type mismatches, and division by zero.
-     * Each error includes context about where the error occurred and what was
-     * expected, helping users quickly identify and fix issues.
-     */
-    function evalNode(node) {
-        callStackTracker.push('evalNode', node?.type || 'unknown');
-        
-        try {
-            if (!node) {
-                return undefined;
-            }
-            switch (node.type) {
-                case 'NumberLiteral':
-                    return parseFloat(node.value);
-                case 'StringLiteral':
-                    return node.value;
-                case 'BooleanLiteral':
-                    return node.value;
-                case 'PlusExpression':
-                    return evalNode(node.left) + evalNode(node.right);
-                case 'MinusExpression':
-                    return evalNode(node.left) - evalNode(node.right);
-                case 'MultiplyExpression':
-                    return evalNode(node.left) * evalNode(node.right);
-                case 'DivideExpression':
-                    const divisor = evalNode(node.right);
-                    if (divisor === 0) {
-                        throw new Error('Division by zero');
-                    }
-                    return evalNode(node.left) / evalNode(node.right);
-                case 'ModuloExpression':
-                    return evalNode(node.left) % evalNode(node.right);
-                case 'PowerExpression':
-                    return Math.pow(evalNode(node.left), evalNode(node.right));
-                case 'EqualsExpression':
-                    return evalNode(node.left) === evalNode(node.right);
-                case 'LessThanExpression':
-                    return evalNode(node.left) &lt; evalNode(node.right);
-                case 'GreaterThanExpression':
-                    return evalNode(node.left) > evalNode(node.right);
-                case 'LessEqualExpression':
-                    return evalNode(node.left) &lt;= evalNode(node.right);
-                case 'GreaterEqualExpression':
-                    return evalNode(node.left) >= evalNode(node.right);
-                case 'NotEqualExpression':
-                    return evalNode(node.left) !== evalNode(node.right);
-                case 'AndExpression':
-                    return !!(evalNode(node.left) &amp;&amp; evalNode(node.right));
-                case 'OrExpression':
-                    return !!(evalNode(node.left) || evalNode(node.right));
-                case 'XorExpression':
-                    const leftVal = evalNode(node.left);
-                    const rightVal = evalNode(node.right);
-                    return !!((leftVal &amp;&amp; !rightVal) || (!leftVal &amp;&amp; rightVal));
-                case 'NotExpression':
-                    return !evalNode(node.operand);
-                case 'UnaryMinusExpression':
-                    return -evalNode(node.operand);
-                case 'TableLiteral':
-                    const table = {};
-                    let arrayIndex = 1;
-                    
-                    for (const entry of node.entries) {
-                        if (entry.key === null) {
-                            // Array-like entry: {1, 2, 3}
-                            table[arrayIndex] = evalNode(entry.value);
-                            arrayIndex++;
-                        } else {
-                            // Key-value entry: {name: "Alice", age: 30}
-                            let key;
-                            if (entry.key.type === 'Identifier') {
-                                // Convert identifier keys to strings
-                                key = entry.key.value;
-                            } else {
-                                // For other key types (numbers, strings), evaluate normally
-                                key = evalNode(entry.key);
-                            }
-                            // Special handling for FunctionDeclaration nodes
-                            if (DEBUG) {
-                                safeConsoleLog(`[DEBUG] TableLiteral: entry.value.type = ${entry.value.type}`);
-                            }
-                            if (entry.value.type === 'FunctionDeclaration') {
-                                // Don't evaluate the function body, just create the function
-                                const func = function(...args) {
-                                    callStackTracker.push('FunctionCall', entry.value.params.join(','));
-                                    try {
-                                        // If we have fewer arguments than parameters, return a curried function
-                                        if (args.length &lt; entry.value.params.length) {
-                                            return function(...moreArgs) {
-                                                const allArgs = [...args, ...moreArgs];
-                                                if (allArgs.length &lt; entry.value.params.length) {
-                                                    // Still not enough arguments, curry again
-                                                    return function(...evenMoreArgs) {
-                                                        const finalArgs = [...allArgs, ...evenMoreArgs];
-                                                        let localScope = Object.create(globalScope);
-                                                        for (let i = 0; i &lt; entry.value.params.length; i++) {
-                                                            localScope[entry.value.params[i]] = finalArgs[i];
-                                                        }
-                                                        return localEvalNodeWithScope(entry.value.body, localScope);
-                                                    };
-                                                } else {
-                                                    // We have enough arguments now
-                                                    let localScope = Object.create(globalScope);
-                                                    for (let i = 0; i &lt; entry.value.params.length; i++) {
-                                                        localScope[entry.value.params[i]] = allArgs[i];
-                                                    }
-                                                    return localEvalNodeWithScope(entry.value.body, localScope);
-                                                }
-                                            };
-                                        } else {
-                                            // We have enough arguments, evaluate the function
-                                            let localScope = Object.create(globalScope);
-                                            for (let i = 0; i &lt; entry.value.params.length; i++) {
-                                                localScope[entry.value.params[i]] = args[i];
-                                            }
-                                            return localEvalNodeWithScope(entry.value.body, localScope);
-                                        }
-                                    } finally {
-                                        callStackTracker.pop();
-                                    }
-                                };
-                                table[key] = func;
-                            } else {
-                                const value = evalNode(entry.value);
-                                table[key] = value;
-                            }
-                        }
-                    }
-                    
-                    return table;
-                case 'TableAccess':
-                    const tableValue = evalNode(node.table);
-                    let keyValue;
-                    
-                    // Handle different key types
-                    if (node.key.type === 'Identifier') {
-                        // For dot notation, use the identifier name as the key
-                        keyValue = node.key.value;
-                    } else {
-                        // For bracket notation, evaluate the key expression
-                        keyValue = evalNode(node.key);
-                    }
-                    
-                    if (typeof tableValue !== 'object' || tableValue === null) {
-                        throw new Error('Cannot access property of non-table value');
-                    }
-                    
-                    if (tableValue[keyValue] === undefined) {
-                        throw new Error(`Key '${keyValue}' not found in table`);
-                    }
-                    
-                    return tableValue[keyValue];
-                case 'AssignmentExpression':
-                    // Prevent reassignment of standard library functions
-                    if (globalScope.hasOwnProperty(node.name)) {
-                        throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                    }
-                    
-                    // Check if this is a function assignment for potential recursion
-                    if (node.value.type === 'FunctionDefinition' || node.value.type === 'FunctionDeclaration') {
-                        // Create a placeholder function that will be replaced
-                        let placeholder = function(...args) {
-                            // This should never be called, but if it is, it means we have a bug
-                            throw new Error(`Function ${node.name} is not yet fully defined`);
-                        };
-                        
-                        // Store the placeholder in global scope
-                        globalScope[node.name] = placeholder;
-                        
-                        // Now evaluate the function definition with access to the placeholder
-                        const actualFunction = evalNode(node.value);
-                        
-                        // Replace the placeholder with the actual function
-                        globalScope[node.name] = actualFunction;
-                        return;
-                    }
-                    
-                    const value = evalNode(node.value);
-                    globalScope[node.name] = value;
-                    return;
-                case 'Assignment':
-                    // Prevent reassignment of standard library functions
-                    if (globalScope.hasOwnProperty(node.identifier)) {
-                        throw new Error(`Cannot reassign immutable variable: ${node.identifier}`);
-                    }
-                    
-                    // Check if this is a function assignment for potential recursion
-                    if (node.value.type === 'FunctionDefinition' || node.value.type === 'FunctionDeclaration') {
-                        // Create a placeholder function that will be replaced
-                        let placeholder = function(...args) {
-                            // This should never be called, but if it is, it means we have a bug
-                            throw new Error(`Function ${node.identifier} is not yet fully defined`);
-                        };
-                        
-                        // Store the placeholder in global scope
-                        globalScope[node.identifier] = placeholder;
-                        
-                        // Now evaluate the function definition with access to the placeholder
-                        const actualFunction = evalNode(node.value);
-                        
-                        // Replace the placeholder with the actual function
-                        globalScope[node.identifier] = actualFunction;
-                        return;
-                    }
-                    
-                    const assignmentValue = evalNode(node.value);
-                    globalScope[node.identifier] = assignmentValue;
-                    return;
-                case 'Identifier':
-                    const identifierValue = globalScope[node.value];
-                    if (identifierValue === undefined) {
-                        throw new Error(`Variable ${node.value} is not defined`);
-                    }
-                    return identifierValue;
-                case 'FunctionDeclaration':
-                    // For anonymous functions, the name comes from the assignment
-                    // The function itself doesn't have a name, so we just return
-                    // The assignment will handle storing it in the global scope
-                    return function(...args) {
-                        callStackTracker.push('FunctionCall', node.params.join(','));
-                        try {
-                            // If we have fewer arguments than parameters, return a curried function
-                            if (args.length &lt; node.params.length) {
-                                return function(...moreArgs) {
-                                    const allArgs = [...args, ...moreArgs];
-                                    if (allArgs.length &lt; node.params.length) {
-                                        // Still not enough arguments, curry again
-                                        return function(...evenMoreArgs) {
-                                            const finalArgs = [...allArgs, ...evenMoreArgs];
-                                            let localScope = Object.create(globalScope);
-                                            for (let i = 0; i &lt; node.params.length; i++) {
-                                                localScope[node.params[i]] = finalArgs[i];
-                                            }
-                                            return localEvalNodeWithScope(node.body, localScope);
-                                        };
-                                    } else {
-                                        // We have enough arguments now
-                                        let localScope = Object.create(globalScope);
-                                        for (let i = 0; i &lt; node.params.length; i++) {
-                                            localScope[node.params[i]] = allArgs[i];
-                                        }
-                                        return localEvalNodeWithScope(node.body, localScope);
-                                    }
-                                };
-                            } else {
-                                // We have enough arguments, evaluate the function
-                                let localScope = Object.create(globalScope);
-                                for (let i = 0; i &lt; node.params.length; i++) {
-                                    localScope[node.params[i]] = args[i];
-                                }
-                                return localEvalNodeWithScope(node.body, localScope);
-                            }
-                        } finally {
-                            callStackTracker.pop();
-                        }
-                    };
-                case 'FunctionDefinition':
-                    // Create a function from the function definition
-                    return function(...args) {
-                        callStackTracker.push('FunctionCall', node.parameters.join(','));
-                        try {
-                            let localScope = Object.create(globalScope);
-                            for (let i = 0; i &lt; node.parameters.length; i++) {
-                                localScope[node.parameters[i]] = args[i];
-                            }
-                            return localEvalNodeWithScope(node.body, localScope);
-                        } finally {
-                            callStackTracker.pop();
-                        }
-                    };
-                case 'FunctionCall':
-                    let funcToCall;
-                    if (typeof node.name === 'string') {
-                        // Regular function call with string name
-                        funcToCall = globalScope[node.name];
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] FunctionCall: looking up function '${node.name}' in globalScope, found:`, typeof funcToCall);
-                        }
-                    } else if (node.name.type === 'Identifier') {
-                        // Function call with identifier
-                        funcToCall = globalScope[node.name.value];
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] FunctionCall: looking up function '${node.name.value}' in globalScope, found:`, typeof funcToCall);
-                        }
-                    } else {
-                        // Function call from expression (e.g., parenthesized function, higher-order)
-                        funcToCall = evalNode(node.name);
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] FunctionCall: evaluated function expression, found:`, typeof funcToCall);
-                        }
-                    }
-                    
-                    if (typeof funcToCall === 'function') {
-                        let args = node.args.map(evalNode);
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] FunctionCall: calling function with args:`, args);
-                        }
-                        return funcToCall(...args);
-                    }
-                    throw new Error(`Function is not defined or is not callable`);
-                case 'WhenExpression':
-                    // Handle both single values and arrays of values
-                    const whenValues = Array.isArray(node.value) 
-                        ? node.value.map(evalNode) 
-                        : [evalNode(node.value)];
-                    
-                    if (DEBUG) {
-                        safeConsoleLog(`[DEBUG] WhenExpression: whenValues =`, whenValues);
-                    }
-                    
-                    for (const caseItem of node.cases) {
-                        // Handle both single patterns and arrays of patterns
-                        const patterns = caseItem.pattern.map(evalNode);
-                        
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] WhenExpression: patterns =`, patterns);
-                        }
-                        
-                        // Check if patterns match the values
-                        let matches = true;
-                        if (whenValues.length !== patterns.length) {
-                            matches = false;
-                        } else {
-                            for (let i = 0; i &lt; whenValues.length; i++) {
-                                const value = whenValues[i];
-                                const pattern = patterns[i];
-                                
-                                if (DEBUG) {
-                                    safeConsoleLog(`[DEBUG] WhenExpression: comparing value ${value} with pattern ${pattern}`);
-                                }
-                                
-                                if (pattern === true) { // Wildcard pattern
-                                    // Wildcard always matches
-                                    if (DEBUG) {
-                                        safeConsoleLog(`[DEBUG] WhenExpression: wildcard matches`);
-                                    }
-                                    continue;
-                                } else if (typeof pattern === 'object' &amp;&amp; pattern.type === 'FunctionCall') {
-                                    // This is a boolean expression pattern (e.g., x &lt; 0)
-                                    // We need to substitute the current value for the pattern variable
-                                    // For now, let's assume the pattern variable is the first identifier in the function call
-                                    let patternToEvaluate = pattern;
-                                    if (pattern.args &amp;&amp; pattern.args.length > 0 &amp;&amp; pattern.args[0].type === 'Identifier') {
-                                        // Create a copy of the pattern with the current value substituted
-                                        patternToEvaluate = {
-                                            ...pattern,
-                                            args: [value, ...pattern.args.slice(1)]
-                                        };
-                                    }
-                                    const patternResult = evalNode(patternToEvaluate);
-                                    if (DEBUG) {
-                                        safeConsoleLog(`[DEBUG] WhenExpression: boolean pattern result = ${patternResult}`);
-                                    }
-                                    if (!patternResult) {
-                                        matches = false;
-                                        if (DEBUG) {
-                                            safeConsoleLog(`[DEBUG] WhenExpression: boolean pattern does not match`);
-                                        }
-                                        break;
-                                                                            } else {
-                                            if (DEBUG) {
-                                                safeConsoleLog(`[DEBUG] WhenExpression: boolean pattern matches`);
-                                            }
-                                        }
-                                } else if (typeof pattern === 'object' &amp;&amp; pattern !== null &amp;&amp; typeof value === 'object' &amp;&amp; value !== null) {
-                                    // Table pattern matching - check if all pattern properties exist in value
-                                    let tableMatches = true;
-                                    for (const key in pattern) {
-                                        if (pattern.hasOwnProperty(key) &amp;&amp; (!value.hasOwnProperty(key) || value[key] !== pattern[key])) {
-                                            tableMatches = false;
-                                            break;
-                                        }
-                                    }
-                                    if (!tableMatches) {
-                                        matches = false;
-                                        if (DEBUG) {
-                                            safeConsoleLog(`[DEBUG] WhenExpression: table pattern does not match`);
-                                        }
-                                        break;
-                                                                            } else {
-                                            if (DEBUG) {
-                                                safeConsoleLog(`[DEBUG] WhenExpression: table pattern matches`);
-                                            }
-                                        }
-                                } else if (value !== pattern) {
-                                    matches = false;
-                                    if (DEBUG) {
-                                        safeConsoleLog(`[DEBUG] WhenExpression: pattern does not match`);
-                                    }
-                                    break;
-                                } else {
-                                    if (DEBUG) {
-                                        safeConsoleLog(`[DEBUG] WhenExpression: pattern matches`);
-                                    }
-                                }
-                            }
-                        }
-                        
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] WhenExpression: case matches = ${matches}`);
-                        }
-                        
-                        if (matches) {
-                            const results = caseItem.result.map(evalNode);
-                            if (results.length === 1) {
-                                return results[0];
-                            }
-                            return results.join(' ');
-                        }
-                    }
-                    throw new Error('No matching pattern found');
-                case 'WildcardPattern':
-                    return true;
-                case 'IOInExpression':
-                    const rl = createReadline();
-                    
-                    return new Promise((resolve) => {
-                        rl.question('', (input) => {
-                            rl.close();
-                            const num = parseInt(input);
-                            resolve(isNaN(num) ? input : num);
-                        });
-                    });
-                case 'IOOutExpression':
-                    const outputValue = evalNode(node.value);
-                    safeConsoleLog(outputValue);
-                    ioOperationsPerformed = true;
-                    return outputValue;
-                case 'IOAssertExpression':
-                    const assertionValue = evalNode(node.value);
-                    if (!assertionValue) {
-                        throw new Error('Assertion failed');
-                    }
-                    return assertionValue;
-                case 'IOListenExpression':
-                    // Return current state from environment if available, otherwise placeholder
-                    if (environment &amp;&amp; typeof environment.getCurrentState === 'function') {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..listen called - returning state from environment');
-                        }
-                        return environment.getCurrentState();
-                    } else {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..listen called - returning placeholder state');
-                        }
-                        return { status: 'placeholder', message: 'State not available in standalone mode' };
-                    }
-                case 'IOEmitExpression':
-                    const emitValue = evalNode(node.value);
-                    // Send value to environment if available, otherwise log to console
-                    if (environment &amp;&amp; typeof environment.emitValue === 'function') {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..emit called - sending to environment');
-                        }
-                        environment.emitValue(emitValue);
-                    } else {
-                        safeConsoleLog('[EMIT]', emitValue);
-                    }
-                    ioOperationsPerformed = true;
-                    return emitValue;
-                case 'FunctionReference':
-                    const functionValue = globalScope[node.name];
-                    if (DEBUG) {
-                        safeConsoleLog(`[DEBUG] FunctionReference: looking up '${node.name}' in globalScope, found:`, typeof functionValue);
-                    }
-                    if (functionValue === undefined) {
-                        throw new Error(`Function ${node.name} is not defined`);
-                    }
-                    if (typeof functionValue !== 'function') {
-                        throw new Error(`${node.name} is not a function`);
-                    }
-                    return functionValue;
-                case 'ArrowExpression':
-                    // Arrow expressions are function bodies that should be evaluated
-                    return evalNode(node.body);
-                default:
-                    throw new Error(`Unknown node type: ${node.type}`);
-            }
-        } finally {
-            callStackTracker.pop();
-        }
-    }
-
-    /**
-     * Evaluates AST nodes in a local scope with access to parent scope.
-     * 
-     * @param {ASTNode} node - AST node to evaluate
-     * @param {Object} scope - Local scope object (prototypally inherits from global)
-     * @returns {*} The result of evaluating the node
-     * @throws {Error} For evaluation errors
-     * 
-     * @description Used for evaluating function bodies and other expressions
-     * that need access to both local and global variables. This function implements
-     * lexical scoping by creating a local scope that prototypally inherits from
-     * the global scope, allowing access to both local parameters and global functions.
-     * 
-     * The function handles the same node types as evalNode but uses the local scope
-     * for variable lookups. It also implements the forward declaration pattern for
-     * recursive functions, ensuring that function definitions can reference themselves
-     * during evaluation.
-     * 
-     * This separation of global and local evaluation allows for proper scope management
-     * and prevents variable name conflicts between function parameters and global variables.
-     * 
-     * The function prioritizes local scope lookups over global scope lookups, ensuring
-     * that function parameters shadow global variables with the same names. This
-     * implements proper lexical scoping semantics.
-     * 
-     * The function maintains the same call stack tracking as evalNode, enabling
-     * consistent debugging and error reporting across both global and local evaluation.
-     * This ensures that errors in function bodies can be traced back to their source
-     * with the same level of detail as global errors.
-     * 
-     * Scope management is implemented using JavaScript's prototypal inheritance,
-     * where each local scope is created as an object that inherits from the global
-     * scope. This approach provides efficient variable lookup while maintaining
-     * proper scoping semantics and enabling access to global functions and variables.
-     */
-    const localEvalNodeWithScope = (node, scope) => {
-        callStackTracker.push('localEvalNodeWithScope', node?.type || 'unknown');
-        
-        try {
-            if (!node) {
-                return undefined;
-            }
-            switch (node.type) {
-                case 'NumberLiteral':
-                    return parseFloat(node.value);
-                case 'StringLiteral':
-                    return node.value;
-                case 'BooleanLiteral':
-                    return node.value;
-                case 'PlusExpression':
-                    return localEvalNodeWithScope(node.left, scope) + localEvalNodeWithScope(node.right, scope);
-                case 'MinusExpression':
-                    return localEvalNodeWithScope(node.left, scope) - localEvalNodeWithScope(node.right, scope);
-                case 'MultiplyExpression':
-                    return localEvalNodeWithScope(node.left, scope) * localEvalNodeWithScope(node.right, scope);
-                case 'DivideExpression':
-                    const divisor = localEvalNodeWithScope(node.right, scope);
-                    if (divisor === 0) {
-                        throw new Error('Division by zero');
-                    }
-                    return localEvalNodeWithScope(node.left, scope) / localEvalNodeWithScope(node.right, scope);
-                case 'ModuloExpression':
-                    return localEvalNodeWithScope(node.left, scope) % localEvalNodeWithScope(node.right, scope);
-                case 'PowerExpression':
-                    return Math.pow(localEvalNodeWithScope(node.left, scope), localEvalNodeWithScope(node.right, scope));
-                case 'EqualsExpression':
-                    return localEvalNodeWithScope(node.left, scope) === localEvalNodeWithScope(node.right, scope);
-                case 'LessThanExpression':
-                    return localEvalNodeWithScope(node.left, scope) &lt; localEvalNodeWithScope(node.right, scope);
-                case 'GreaterThanExpression':
-                    return localEvalNodeWithScope(node.left, scope) > localEvalNodeWithScope(node.right, scope);
-                case 'LessEqualExpression':
-                    return localEvalNodeWithScope(node.left, scope) &lt;= localEvalNodeWithScope(node.right, scope);
-                case 'GreaterEqualExpression':
-                    return localEvalNodeWithScope(node.left, scope) >= localEvalNodeWithScope(node.right, scope);
-                case 'NotEqualExpression':
-                    return localEvalNodeWithScope(node.left, scope) !== localEvalNodeWithScope(node.right, scope);
-                case 'AndExpression':
-                    return !!(localEvalNodeWithScope(node.left, scope) &amp;&amp; localEvalNodeWithScope(node.right, scope));
-                case 'OrExpression':
-                    return !!(localEvalNodeWithScope(node.left, scope) || localEvalNodeWithScope(node.right, scope));
-                case 'XorExpression':
-                    const leftVal = localEvalNodeWithScope(node.left, scope);
-                    const rightVal = localEvalNodeWithScope(node.right, scope);
-                    return !!((leftVal &amp;&amp; !rightVal) || (!leftVal &amp;&amp; rightVal));
-                case 'NotExpression':
-                    return !localEvalNodeWithScope(node.operand, scope);
-                case 'UnaryMinusExpression':
-                    return -localEvalNodeWithScope(node.operand, scope);
-                case 'TableLiteral':
-                    const table = {};
-                    let arrayIndex = 1;
-                    
-                    for (const entry of node.entries) {
-                        if (entry.key === null) {
-                            // Array-like entry: {1, 2, 3}
-                            table[arrayIndex] = localEvalNodeWithScope(entry.value, scope);
-                            arrayIndex++;
-                        } else {
-                            // Key-value entry: {name: "Alice", age: 30}
-                            let key;
-                            if (entry.key.type === 'Identifier') {
-                                // Convert identifier keys to strings
-                                key = entry.key.value;
-                            } else {
-                                // For other key types (numbers, strings), evaluate normally
-                                key = localEvalNodeWithScope(entry.key, scope);
-                            }
-                            const value = localEvalNodeWithScope(entry.value, scope);
-                            table[key] = value;
-                        }
-                    }
-                    
-                    return table;
-                case 'TableAccess':
-                    const tableValue = localEvalNodeWithScope(node.table, scope);
-                    let keyValue;
-                    
-                    // Handle different key types
-                    if (node.key.type === 'Identifier') {
-                        // For dot notation, use the identifier name as the key
-                        keyValue = node.key.value;
-                    } else {
-                        // For bracket notation, evaluate the key expression
-                        keyValue = localEvalNodeWithScope(node.key, scope);
-                    }
-                    
-                    if (typeof tableValue !== 'object' || tableValue === null) {
-                        throw new Error('Cannot access property of non-table value');
-                    }
-                    
-                    if (tableValue[keyValue] === undefined) {
-                        throw new Error(`Key '${keyValue}' not found in table`);
-                    }
-                    
-                    return tableValue[keyValue];
-                case 'AssignmentExpression':
-                    // Prevent reassignment of standard library functions
-                    if (globalScope.hasOwnProperty(node.name)) {
-                        throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                    }
-                    
-                    // Check if this is a function assignment for potential recursion
-                    if (node.value.type === 'FunctionDefinition' || node.value.type === 'FunctionDeclaration') {
-                        // Create a placeholder function that will be replaced
-                        let placeholder = function(...args) {
-                            // This should never be called, but if it is, it means we have a bug
-                            throw new Error(`Function ${node.name} is not yet fully defined`);
-                        };
-                        
-                        // Store the placeholder in global scope
-                        globalScope[node.name] = placeholder;
-                        
-                        // Now evaluate the function definition with access to the placeholder
-                        const actualFunction = localEvalNodeWithScope(node.value, scope);
-                        
-                        // Replace the placeholder with the actual function
-                        globalScope[node.name] = actualFunction;
-                        return;
-                    }
-                    
-                    globalScope[node.name] = localEvalNodeWithScope(node.value, scope);
-                    return;
-                case 'Identifier':
-                    // First check local scope, then global scope
-                    if (scope &amp;&amp; scope.hasOwnProperty(node.value)) {
-                        return scope[node.value];
-                    }
-                    const identifierValue = globalScope[node.value];
-                    if (identifierValue === undefined &amp;&amp; node.value) {
-                        return node.value;
-                    }
-                    return identifierValue;
-                case 'FunctionDeclaration':
-                    // For anonymous functions, the name comes from the assignment
-                    // The function itself doesn't have a name, so we just return
-                    // The assignment will handle storing it in the global scope
-                    return function(...args) {
-                        callStackTracker.push('FunctionCall', node.params.join(','));
-                        try {
-                            let nestedScope = Object.create(globalScope);
-                            for (let i = 0; i &lt; node.params.length; i++) {
-                                nestedScope[node.params[i]] = args[i];
-                            }
-                            return localEvalNodeWithScope(node.body, nestedScope);
-                        } finally {
-                            callStackTracker.pop();
-                        }
-                    };
-                case 'FunctionDefinition':
-                    // Create a function from the function definition
-                    return function(...args) {
-                        callStackTracker.push('FunctionCall', node.parameters.join(','));
-                        try {
-                            let nestedScope = Object.create(globalScope);
-                            for (let i = 0; i &lt; node.parameters.length; i++) {
-                                nestedScope[node.parameters[i]] = args[i];
-                            }
-                            return localEvalNodeWithScope(node.body, nestedScope);
-                        } finally {
-                            callStackTracker.pop();
-                        }
-                    };
-                case 'FunctionCall':
-                    let localFunc;
-                    if (typeof node.name === 'string') {
-                        // Regular function call with string name
-                        localFunc = globalScope[node.name];
-                    } else if (node.name.type === 'Identifier') {
-                        // Function call with identifier
-                        localFunc = globalScope[node.name.value];
-                    } else {
-                        // Function call from expression (e.g., parenthesized function, higher-order)
-                        localFunc = localEvalNodeWithScope(node.name, scope);
-                    }
-                    
-                    if (localFunc instanceof Function) {
-                        let args = node.args.map(arg => localEvalNodeWithScope(arg, scope));
-                        return localFunc(...args);
-                    }
-                    throw new Error(`Function is not defined or is not callable`);
-                case 'WhenExpression':
-                    // Handle both single values and arrays of values
-                    const whenValues = Array.isArray(node.value) 
-                        ? node.value.map(val => localEvalNodeWithScope(val, scope)) 
-                        : [localEvalNodeWithScope(node.value, scope)];
-                    
-                    if (DEBUG) {
-                        safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: whenValues =`, whenValues);
-                    }
-                    
-                    for (const caseItem of node.cases) {
-                        // Handle both single patterns and arrays of patterns
-                        const patterns = caseItem.pattern.map(pat => localEvalNodeWithScope(pat, scope));
-                        
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: patterns =`, patterns);
-                        }
-                        
-                        // Check if patterns match the values
-                        let matches = true;
-                        if (whenValues.length !== patterns.length) {
-                            matches = false;
-                        } else {
-                            for (let i = 0; i &lt; whenValues.length; i++) {
-                                const value = whenValues[i];
-                                const pattern = patterns[i];
-                                
-                                if (DEBUG) {
-                                    safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: comparing value ${value} with pattern ${pattern}`);
-                                }
-                                
-                                if (pattern === true) { // Wildcard pattern
-                                    // Wildcard always matches
-                                    if (DEBUG) {
-                                        safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: wildcard matches`);
-                                    }
-                                    continue;
-                                } else if (typeof pattern === 'object' &amp;&amp; pattern !== null &amp;&amp; typeof value === 'object' &amp;&amp; value !== null) {
-                                    // Table pattern matching - check if all pattern properties exist in value
-                                    let tableMatches = true;
-                                    for (const key in pattern) {
-                                        if (pattern.hasOwnProperty(key) &amp;&amp; (!value.hasOwnProperty(key) || value[key] !== pattern[key])) {
-                                            tableMatches = false;
-                                            break;
-                                        }
-                                    }
-                                    if (!tableMatches) {
-                                        matches = false;
-                                        if (DEBUG) {
-                                            safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: table pattern does not match`);
-                                        }
-                                        break;
-                                                                            } else {
-                                            if (DEBUG) {
-                                                safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: table pattern matches`);
-                                            }
-                                        }
-                                } else if (value !== pattern) {
-                                    matches = false;
-                                    if (DEBUG) {
-                                        safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: pattern does not match`);
-                                    }
-                                    break;
-                                } else {
-                                    if (DEBUG) {
-                                        safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: pattern matches`);
-                                    }
-                                }
-                            }
-                        }
-                        
-                        if (DEBUG) {
-                            safeConsoleLog(`[DEBUG] localEvalNodeWithScope WhenExpression: case matches = ${matches}`);
-                        }
-                        
-                        if (matches) {
-                            const results = caseItem.result.map(res => localEvalNodeWithScope(res, scope));
-                            if (results.length === 1) {
-                                return results[0];
-                            }
-                            return results.join(' ');
-                        }
-                    }
-                    throw new Error('No matching pattern found');
-                case 'WildcardPattern':
-                    return true;
-                case 'IOInExpression':
-                    const rl2 = createReadline();
-                    
-                    return new Promise((resolve) => {
-                        rl2.question('', (input) => {
-                            rl2.close();
-                            const num = parseInt(input);
-                            resolve(isNaN(num) ? input : num);
-                        });
-                    });
-                case 'IOOutExpression':
-                    const localOutputValue = localEvalNodeWithScope(node.value, scope);
-                    safeConsoleLog(localOutputValue);
-                    ioOperationsPerformed = true;
-                    return localOutputValue;
-                case 'IOAssertExpression':
-                    const localAssertionValue = localEvalNodeWithScope(node.value, scope);
-                    if (!localAssertionValue) {
-                        throw new Error('Assertion failed');
-                    }
-                    return localAssertionValue;
-                case 'IOListenExpression':
-                    // Return current state from environment if available, otherwise placeholder
-                    if (environment &amp;&amp; typeof environment.getCurrentState === 'function') {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..listen called - returning state from environment');
-                        }
-                        return environment.getCurrentState();
-                    } else {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..listen called - returning placeholder state');
-                        }
-                        return { status: 'placeholder', message: 'State not available in standalone mode' };
-                    }
-                case 'IOEmitExpression':
-                    const localEmitValue = localEvalNodeWithScope(node.value, scope);
-                    // Send value to environment if available, otherwise log to console
-                    if (environment &amp;&amp; typeof environment.emitValue === 'function') {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..emit called - sending to environment');
-                        }
-                        environment.emitValue(localEmitValue);
-                    } else {
-                        safeConsoleLog('[EMIT]', localEmitValue);
-                    }
-                    ioOperationsPerformed = true;
-                    return localEmitValue;
-                case 'FunctionReference':
-                    const localFunctionValue = globalScope[node.name];
-                    if (localFunctionValue === undefined) {
-                        throw new Error(`Function ${node.name} is not defined`);
-                    }
-                    if (typeof localFunctionValue !== 'function') {
-                        throw new Error(`${node.name} is not a function`);
-                    }
-                    return localFunctionValue;
-                case 'ArrowExpression':
-                    // Arrow expressions are function bodies that should be evaluated
-                    return localEvalNodeWithScope(node.body, scope);
-                default:
-                    throw new Error(`Unknown node type: ${node.type}`);
-            }
-        } finally {
-            callStackTracker.pop();
-        }
-    };
-
-    /**
-     * Evaluates AST nodes in the global scope (internal recursion helper).
-     * 
-     * @param {Object} node - AST node to evaluate
-     * @returns {*} The result of evaluating the node
-     * @throws {Error} For evaluation errors
-     * 
-     * @description Internal helper function for recursive evaluation that
-     * always uses the global scope. This function is used to avoid circular
-     * dependencies and infinite recursion when evaluating nested expressions
-     * that need access to the global scope.
-     * 
-     * This function duplicates the logic of evalNode but without the scope
-     * parameter, ensuring that all variable lookups go through the global scope.
-     * It's primarily used for evaluating function bodies and other expressions
-     * that need to be isolated from local scope contexts.
-     * 
-     * The function also implements the forward declaration pattern for recursive
-     * functions, maintaining consistency with the other evaluation functions.
-     * 
-     * This function is essential for preventing scope pollution when evaluating
-     * nested expressions that should not inherit local scope variables, ensuring
-     * that global functions and variables are always accessible regardless of
-     * the current evaluation context.
-     */
-    const localEvalNode = (node) => {
-        callStackTracker.push('localEvalNode', node?.type || 'unknown');
-        
-        try {
-            if (!node) {
-                return undefined;
-            }
-            switch (node.type) {
-                case 'NumberLiteral':
-                    return parseFloat(node.value);
-                case 'StringLiteral':
-                    return node.value;
-                case 'BooleanLiteral':
-                    return node.value;
-                case 'PlusExpression':
-                    return localEvalNode(node.left) + localEvalNode(node.right);
-                case 'MinusExpression':
-                    return localEvalNode(node.left) - localEvalNode(node.right);
-                case 'MultiplyExpression':
-                    return localEvalNode(node.left) * localEvalNode(node.right);
-                case 'DivideExpression':
-                    const divisor = localEvalNode(node.right);
-                    if (divisor === 0) {
-                        throw new Error('Division by zero');
-                    }
-                    return localEvalNode(node.left) / localEvalNode(node.right);
-                case 'ModuloExpression':
-                    return localEvalNode(node.left) % localEvalNode(node.right);
-                case 'PowerExpression':
-                    return Math.pow(localEvalNode(node.left), localEvalNode(node.right));
-                case 'EqualsExpression':
-                    return localEvalNode(node.left) === localEvalNode(node.right);
-                case 'LessThanExpression':
-                    return localEvalNode(node.left) &lt; localEvalNode(node.right);
-                case 'GreaterThanExpression':
-                    return localEvalNode(node.left) > localEvalNode(node.right);
-                case 'LessEqualExpression':
-                    return localEvalNode(node.left) &lt;= localEvalNode(node.right);
-                case 'GreaterEqualExpression':
-                    return localEvalNode(node.left) >= localEvalNode(node.right);
-                case 'NotEqualExpression':
-                    return localEvalNode(node.left) !== localEvalNode(node.right);
-                case 'AndExpression':
-                    return !!(localEvalNode(node.left) &amp;&amp; localEvalNode(node.right));
-                case 'OrExpression':
-                    return !!(localEvalNode(node.left) || localEvalNode(node.right));
-                case 'XorExpression':
-                    const leftVal = localEvalNode(node.left);
-                    const rightVal = localEvalNode(node.right);
-                    return !!((leftVal &amp;&amp; !rightVal) || (!leftVal &amp;&amp; rightVal));
-                case 'NotExpression':
-                    return !localEvalNode(node.operand);
-                case 'UnaryMinusExpression':
-                    return -localEvalNode(node.operand);
-                case 'TableLiteral':
-                    const table = {};
-                    let arrayIndex = 1;
-                    
-                    for (const entry of node.entries) {
-                        if (entry.key === null) {
-                            // Array-like entry: {1, 2, 3}
-                            table[arrayIndex] = localEvalNode(entry.value);
-                            arrayIndex++;
-                        } else {
-                            // Key-value entry: {name: "Alice", age: 30}
-                            let key;
-                            if (entry.key.type === 'Identifier') {
-                                // Convert identifier keys to strings
-                                key = entry.key.value;
-                            } else {
-                                // For other key types (numbers, strings), evaluate normally
-                                key = localEvalNode(entry.key);
-                            }
-                            const value = localEvalNode(entry.value);
-                            table[key] = value;
-                        }
-                    }
-                    
-                    return table;
-                case 'TableAccess':
-                    const tableValue = localEvalNode(node.table);
-                    let keyValue;
-                    
-                    // Handle different key types
-                    if (node.key.type === 'Identifier') {
-                        // For dot notation, use the identifier name as the key
-                        keyValue = node.key.value;
-                    } else {
-                        // For bracket notation, evaluate the key expression
-                        keyValue = localEvalNode(node.key);
-                    }
-                    
-                    if (typeof tableValue !== 'object' || tableValue === null) {
-                        throw new Error('Cannot access property of non-table value');
-                    }
-                    
-                    if (tableValue[keyValue] === undefined) {
-                        throw new Error(`Key '${keyValue}' not found in table`);
-                    }
-                    
-                    return tableValue[keyValue];
-                case 'AssignmentExpression':
-                    // Prevent reassignment of standard library functions
-                    if (globalScope.hasOwnProperty(node.name)) {
-                        throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                    }
-                    
-                    // Check if this is a function assignment for potential recursion
-                    if (node.value.type === 'FunctionDefinition' || node.value.type === 'FunctionDeclaration') {
-                        // Create a placeholder function that will be replaced
-                        let placeholder = function(...args) {
-                            // This should never be called, but if it is, it means we have a bug
-                            throw new Error(`Function ${node.name} is not yet fully defined`);
-                        };
-                        
-                        // Store the placeholder in global scope
-                        globalScope[node.name] = placeholder;
-                        
-                        // Now evaluate the function definition with access to the placeholder
-                        const actualFunction = localEvalNode(node.value);
-                        
-                        // Replace the placeholder with the actual function
-                        globalScope[node.name] = actualFunction;
-                        return;
-                    }
-                    
-                    globalScope[node.name] = localEvalNode(node.value);
-                    return;
-                case 'Identifier':
-                    const identifierValue = globalScope[node.value];
-                    if (identifierValue === undefined &amp;&amp; node.value) {
-                        return node.value;
-                    }
-                    return identifierValue;
-                case 'FunctionDeclaration':
-                    // For anonymous functions, the name comes from the assignment
-                    // The function itself doesn't have a name, so we just return
-                    // The assignment will handle storing it in the global scope
-                    return function(...args) {
-                        callStackTracker.push('FunctionCall', node.params.join(','));
-                        try {
-                            let nestedScope = Object.create(globalScope);
-                            for (let i = 0; i &lt; node.params.length; i++) {
-                                nestedScope[node.params[i]] = args[i];
-                            }
-                            return localEvalNodeWithScope(node.body, nestedScope);
-                        } finally {
-                            callStackTracker.pop();
-                        }
-                    };
-                case 'FunctionDefinition':
-                    // Create a function from the function definition
-                    return function(...args) {
-                        callStackTracker.push('FunctionCall', node.parameters.join(','));
-                        try {
-                            let nestedScope = Object.create(globalScope);
-                            for (let i = 0; i &lt; node.parameters.length; i++) {
-                                nestedScope[node.parameters[i]] = args[i];
-                            }
-                            return localEvalNodeWithScope(node.body, nestedScope);
-                        } finally {
-                            callStackTracker.pop();
-                        }
-                    };
-                case 'FunctionCall':
-                    let localFunc;
-                    if (typeof node.name === 'string') {
-                        // Regular function call with string name
-                        localFunc = globalScope[node.name];
-                    } else if (node.name.type === 'Identifier') {
-                        // Function call with identifier
-                        localFunc = globalScope[node.name.value];
-                    } else {
-                        // Function call from expression (e.g., parenthesized function, higher-order)
-                        localFunc = localEvalNode(node.name);
-                    }
-                    
-                    if (localFunc instanceof Function) {
-                        let args = node.args.map(localEvalNode);
-                        return localFunc(...args);
-                    }
-                    throw new Error(`Function is not defined or is not callable`);
-                case 'WhenExpression':
-                    // Handle both single values and arrays of values
-                    const whenValues = Array.isArray(node.value) 
-                        ? node.value.map(localEvalNode) 
-                        : [localEvalNode(node.value)];
-                    
-                    for (const caseItem of node.cases) {
-                        // Handle both single patterns and arrays of patterns
-                        const patterns = caseItem.pattern.map(localEvalNode);
-                        
-                        // Check if patterns match the values
-                        let matches = true;
-                        if (whenValues.length !== patterns.length) {
-                            matches = false;
-                        } else {
-                            for (let i = 0; i &lt; whenValues.length; i++) {
-                                const value = whenValues[i];
-                                const pattern = patterns[i];
-                                
-                                if (pattern === true) { // Wildcard pattern
-                                    // Wildcard always matches
-                                    continue;
-                                } else if (typeof pattern === 'object' &amp;&amp; pattern !== null &amp;&amp; typeof value === 'object' &amp;&amp; value !== null) {
-                                    // Table pattern matching - check if all pattern properties exist in value
-                                    let tableMatches = true;
-                                    for (const key in pattern) {
-                                        if (pattern.hasOwnProperty(key) &amp;&amp; (!value.hasOwnProperty(key) || value[key] !== pattern[key])) {
-                                            tableMatches = false;
-                                            break;
-                                        }
-                                    }
-                                    if (!tableMatches) {
-                                        matches = false;
-                                        break;
-                                    }
-                                } else if (value !== pattern) {
-                                    matches = false;
-                                    break;
-                                }
-                            }
-                        }
-                        
-                        if (matches) {
-                            const results = caseItem.result.map(localEvalNode);
-                            if (results.length === 1) {
-                                return results[0];
-                            }
-                            return results.join(' ');
-                        }
-                    }
-                    throw new Error('No matching pattern found');
-                case 'WildcardPattern':
-                    return true;
-                case 'IOInExpression':
-                    const rl3 = createReadline();
-                    
-                    return new Promise((resolve) => {
-                        rl3.question('', (input) => {
-                            rl3.close();
-                            const num = parseInt(input);
-                            resolve(isNaN(num) ? input : num);
-                        });
-                    });
-                case 'IOOutExpression':
-                    const localOutputValue = localEvalNode(node.value);
-                    safeConsoleLog(localOutputValue);
-                    ioOperationsPerformed = true;
-                    return localOutputValue;
-                case 'IOAssertExpression':
-                    const localAssertionValue = localEvalNode(node.value);
-                    if (!localAssertionValue) {
-                        throw new Error('Assertion failed');
-                    }
-                    return localAssertionValue;
-                case 'IOListenExpression':
-                    // Return current state from environment if available, otherwise placeholder
-                    if (environment &amp;&amp; typeof environment.getCurrentState === 'function') {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..listen called - returning state from environment');
-                        }
-                        return environment.getCurrentState();
-                    } else {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..listen called - returning placeholder state');
-                        }
-                        return { status: 'placeholder', message: 'State not available in standalone mode' };
-                    }
-                case 'IOEmitExpression':
-                    const localEmitValue = localEvalNode(node.value);
-                    // Send value to environment if available, otherwise log to console
-                    if (environment &amp;&amp; typeof environment.emitValue === 'function') {
-                        if (DEBUG) {
-                            safeConsoleLog('[DEBUG] ..emit called - sending to environment');
-                        }
-                        environment.emitValue(localEmitValue);
-                    } else {
-                        safeConsoleLog('[EMIT]', localEmitValue);
-                    }
-                    ioOperationsPerformed = true;
-                    return localEmitValue;
-                case 'FunctionReference':
-                    const localFunctionValue = globalScope[node.name];
-                    if (localFunctionValue === undefined) {
-                        throw new Error(`Function ${node.name} is not defined`);
-                    }
-                    if (typeof localFunctionValue !== 'function') {
-                        throw new Error(`${node.name} is not a function`);
-                    }
-                    return localFunctionValue;
-                case 'ArrowExpression':
-                    // Arrow expressions are function bodies that should be evaluated
-                    return localEvalNode(node.body);
-                default:
-                    throw new Error(`Unknown node type: ${node.type}`);
-            }
-        } finally {
-            callStackTracker.pop();
-        }
-    };
-
-    let lastResult;
-    for (let node of ast.body) {
-        if (node) {
-            lastResult = evalNode(node);
-        }
-    }
-    
-    if (lastResult instanceof Promise) {
-        return lastResult.then(result => {
-            return { result: globalScope, ioOperationsPerformed };
-        });
-    }
-    
-    return { result: globalScope, ioOperationsPerformed };
-}
-
-/**
- * Run script with environment support for harness integration
- * 
- * @param {string} scriptContent - The script content to execute
- * @param {Object} [initialState={}] - Initial state for the interpreter
- * @param {Environment} [environment=null] - Environment for IO operations
- * @returns {*} The result of executing the script
- * @throws {Error} For parsing or evaluation errors
- * 
- * @description Parses and executes a script using the combinator-based language.
- * This function orchestrates the entire execution pipeline from source code
- * to final result.
- * 
- * The function performs the following steps:
- * 1. Tokenize the source code using the lexer
- * 2. Parse the tokens into an AST using the parser
- * 3. Evaluate the AST using the interpreter
- * 4. Return the final result
- * 
- * This is the primary interface for executing scripts in the language.
- * It handles the parsing and evaluation pipeline,
- * providing a simple interface for users to run their code.
- * 
- * The function supports both synchronous and asynchronous execution. When
- * the script contains IO operations that return Promises, the function
- * will return a Promise that resolves to the final result. This enables
- * non-blocking execution for interactive programs.
- * 
- * Error handling is comprehensive, with errors from any stage of the
- * pipeline (lexing, parsing, or evaluation) being caught and re-thrown
- * with appropriate context. This ensures that users get meaningful
- * error messages that help them identify and fix issues in their code.
- * 
- * The function is designed to be stateless, with each call creating
- * a fresh interpreter instance. This ensures that scripts don't interfere
- * with each other and enables safe concurrent execution of multiple scripts.
- */
-function run(scriptContent, initialState = {}, environment = null) {
-    // Parse the script
-    const tokens = lexer(scriptContent);
-    const ast = parser(tokens);
-    
-    // Run the interpreter with environment and initial state
-    const result = interpreter(ast, environment, initialState);
-    
-    // Return the result
-    return result.result;
-}
-
-/**
- * Debug logging utility function.
- * 
- * @param {string} message - Debug message to log
- * @param {*} [data=null] - Optional data to log with the message
- * 
- * @description Logs debug messages to console when DEBUG environment variable is set.
- * Provides verbose output during development while remaining silent in production.
- * 
- * Debug functions are gated by the DEBUG environment variable, allowing for 
- * verbose output during development and silent operation in production. This 
- * approach makes it easy to trace execution and diagnose issues without 
- * cluttering normal output.
- * 
- * This function is essential for debugging the combinator-based architecture,
- * allowing developers to trace how operators are translated to function calls
- * and how the interpreter executes these calls through the standard library.
- * 
- * The function is designed to be lightweight and safe to call frequently,
- * making it suitable for tracing execution flow through nested
- * expressions and function applications.
- */
-function debugLog(message, data = null) {
-    if (DEBUG) {
-        safeConsoleLog(`[DEBUG] ${message}`);
-        if (data) {
-            safeConsoleLog(data);
-        }
-    }
-}
-
-/**
- * Debug error logging utility function.
- * 
- * @param {string} message - Debug error message to log
- * @param {Error} [error=null] - Optional error object to log
- * 
- * @description Logs debug error messages to console when DEBUG environment variable is set.
- * Provides verbose error output during development while remaining silent in production.
- * 
- * Debug functions are gated by the DEBUG environment variable, allowing for 
- * verbose output during development and silent operation in production. This 
- * approach makes it easy to trace execution and diagnose issues without 
- * cluttering normal output.
- * 
- * This function is particularly useful for debugging parsing and evaluation errors,
- * providing detailed context about where and why errors occur in the language
- * execution pipeline.
- */
-function debugError(message, error = null) {
-    if (DEBUG) {
-        safeConsoleError(`[DEBUG ERROR] ${message}`);
-        if (error) {
-            safeConsoleError(error);
-        }
-    }
-}
-
-/**
- * Call stack tracking for debugging recursion issues.
- * 
- * @description Tracks function calls to help identify infinite recursion
- * and deep call stacks that cause stack overflow errors. This is essential
- * for debugging the interpreter's recursive evaluation of AST nodes.
- * 
- * The tracker maintains a stack of function calls with timestamps and context
- * information, counts function calls to identify hot paths, and detects
- * potential infinite recursion by monitoring stack depth.
- * 
- * This tool is particularly important for the combinator-based architecture
- * where function calls are the primary execution mechanism, and
- * nested expressions can lead to deep call stacks. The tracker helps identify
- * when the combinator translation creates unexpectedly deep call chains,
- * enabling optimization of the function composition and application patterns.
- * 
- * The tracker provides detailed statistics about function call patterns,
- * helping developers understand the execution characteristics of their code
- * and identify potential performance bottlenecks in the combinator evaluation.
- */
-const callStackTracker = {
-    stack: [],
-    maxDepth: 0,
-    callCounts: new Map(),
-    
-    /**
-     * Push a function call onto the stack
-     * @param {string} functionName - Name of the function being called
-     * @param {string} context - Context where the call is happening
-     */
-    push: function(functionName, context = '') {
-        const callInfo = { functionName, context, timestamp: Date.now() };
-        this.stack.push(callInfo);
-        
-        // Track maximum depth
-        if (this.stack.length > this.maxDepth) {
-            this.maxDepth = this.stack.length;
-        }
-        
-        // Count function calls
-        const key = `${functionName}${context ? `:${context}` : ''}`;
-        this.callCounts.set(key, (this.callCounts.get(key) || 0) + 1);
-        
-        // Check for potential infinite recursion
-        if (this.stack.length > 1000) {
-            console.error('=== POTENTIAL INFINITE RECURSION DETECTED ===');
-            console.error('Call stack depth:', this.stack.length);
-            console.error('Function call counts:', Object.fromEntries(this.callCounts));
-            console.error('Recent call stack:');
-            this.stack.slice(-10).forEach((call, i) => {
-                console.error(`  ${this.stack.length - 10 + i}: ${call.functionName}${call.context ? ` (${call.context})` : ''}`);
-            });
-            throw new Error(`Potential infinite recursion detected. Call stack depth: ${this.stack.length}`);
-        }
-        
-        if (DEBUG &amp;&amp; this.stack.length % 100 === 0) {
-            safeConsoleLog(`[DEBUG] Call stack depth: ${this.stack.length}, Max: ${this.maxDepth}`);
-        }
-    },
-    
-    /**
-     * Pop a function call from the stack
-     */
-    pop: function() {
-        return this.stack.pop();
-    },
-    
-    /**
-     * Get current stack depth
-     */
-    getDepth: function() {
-        return this.stack.length;
-    },
-    
-    /**
-     * Get call statistics
-     */
-    getStats: function() {
-        return {
-            currentDepth: this.stack.length,
-            maxDepth: this.maxDepth,
-            callCounts: Object.fromEntries(this.callCounts)
-        };
-    },
-    
-    /**
-     * Reset the tracker
-     */
-    reset: function() {
-        this.stack = [];
-        this.maxDepth = 0;
-        this.callCounts.clear();
-    }
-};
-
-/**
- * Cross-platform file I/O utility
- * 
- * @param {string} filePath - Path to the file to read
- * @returns {Promise&lt;string>} File contents as a string
- * @throws {Error} For file reading errors
- * 
- * @description Handles file reading across different platforms (Node.js, Bun, browser)
- * with appropriate fallbacks for each environment. This function is essential for
- * the language's file execution model where scripts are loaded from .txt files.
- * 
- * The function prioritizes ES modules compatibility by using dynamic import,
- * but falls back to require for older Node.js versions. Browser environments
- * are not supported for file I/O operations.
- * 
- * This cross-platform approach ensures the language can run in various JavaScript
- * environments while maintaining consistent behavior. The file reading capability
- * enables the language to execute scripts from files, supporting the development
- * workflow where tests and examples are stored as .txt files.
- */
-async function readFile(filePath) {
-    // Use cross-platform filesystem
-    const fs = createFileSystem();
-    
-    return new Promise((resolve, reject) => {
-        fs.readFile(filePath, 'utf8', (error, data) => {
-            if (error) {
-                reject(error);
-            } else {
-                resolve(data);
-            }
-        });
-    });
-}
-
-/**
- * Reads a file, tokenizes, parses, and interprets it.
- * 
- * @param {string} filePath - Path to the file to execute
- * @returns {Promise&lt;*>} The result of executing the file
- * @throws {Error} For file reading, parsing, or execution errors
- * 
- * @description Main entry point for file execution. Handles the complete language
- * pipeline: file reading, lexical analysis, parsing, and interpretation.
- * 
- * This function orchestrates the entire language execution process:
- * 1. Reads the source file using cross-platform I/O utilities
- * 2. Tokenizes the source code using the lexer
- * 3. Parses tokens into an AST using the combinator-based parser
- * 4. Interprets the AST using the combinator-based interpreter
- * 
- * The function provides comprehensive error handling and debug output at each
- * stage for transparency and troubleshooting. It also manages the call stack
- * tracker to provide execution statistics and detect potential issues.
- * 
- * Supports both synchronous and asynchronous execution, with proper
- * error handling and process exit codes. This function demonstrates the
- * complete combinator-based architecture in action, showing how source code
- * is transformed through each stage of the language pipeline.
- * 
- * The function enforces the .txt file extension requirement and provides
- * detailed error reporting with call stack statistics to help developers
- * understand execution behavior and diagnose issues.
- */
-async function executeFile(filePath) {
-    try {
-        // Validate file extension
-        if (!filePath.endsWith('.txt') &amp;&amp; !filePath.endsWith('.baba')) {
-            throw new Error('Only .txt and .baba files are supported');
-        }
-        
-        const input = await readFile(filePath);
-        
-        debugLog('Input:', input);
-        
-        const tokens = lexer(input);
-        debugLog('Tokens:', tokens);
-        
-        const ast = parser(tokens);
-        debugLog('AST:', JSON.stringify(ast, null, 2));
-        
-        const result = interpreter(ast);
-        
-        if (result instanceof Promise) {
-            result.then(finalResult => {
-                // Only output result if debug mode is enabled (no automatic final result output)
-                if (finalResult.result !== undefined &amp;&amp; DEBUG) {
-                    safeConsoleLog(finalResult.result);
-                }
-                // Print call stack statistics only in debug mode
-                if (DEBUG) {
-                    const stats = callStackTracker.getStats();
-                    safeConsoleLog('\n=== CALL STACK STATISTICS ===');
-                    safeConsoleLog('Maximum call stack depth:', stats.maxDepth);
-                    safeConsoleLog('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
-                }
-            }).catch(error => {
-                safeConsoleError(`Error executing file: ${error.message}`);
-                // Print call stack statistics on error only in debug mode
-                if (DEBUG) {
-                    const stats = callStackTracker.getStats();
-                    safeConsoleError('\n=== CALL STACK STATISTICS ON ERROR ===');
-                    safeConsoleError('Maximum call stack depth:', stats.maxDepth);
-                    safeConsoleError('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
-                }
-                safeExit(1);
-            });
-        } else {
-            // Only output result if debug mode is enabled (no automatic final result output)
-            if (result.result !== undefined &amp;&amp; DEBUG) {
-                safeConsoleLog(result.result);
-            }
-            // Print call stack statistics only in debug mode
-            if (DEBUG) {
-                const stats = callStackTracker.getStats();
-                safeConsoleLog('\n=== CALL STACK STATISTICS ===');
-                safeConsoleLog('Maximum call stack depth:', stats.maxDepth);
-                safeConsoleLog('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
-            }
-        }
-    } catch (error) {
-        safeConsoleError(`Error executing file: ${error.message}`);
-        // Print call stack statistics on error only in debug mode
-        if (DEBUG) {
-            const stats = callStackTracker.getStats();
-            safeConsoleError('\n=== CALL STACK STATISTICS ON ERROR ===');
-            safeConsoleError('Maximum call stack depth:', stats.maxDepth);
-            safeConsoleError('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
-        }
-        safeExit(1);
-    }
-}
-
-/**
- * CLI argument handling and program entry point.
- * 
- * @description Processes command line arguments and executes the specified file.
- * Provides helpful error messages for incorrect usage.
- * 
- * The language is designed for file execution only (no REPL), so the CLI 
- * enforces this usage and provides helpful error messages for incorrect invocation.
- * The function validates that exactly one file path is provided and that the
- * file has the correct .txt extension.
- * 
- * Exits with appropriate error codes for different failure scenarios.
- */
-async function main() {
-    // Only run main function in Node.js/Bun environments
-    if (!isNode &amp;&amp; !isBun) {
-        return; // Skip in browser environment
-    }
-    
-    const args = process.argv.slice(2);
-
-    if (args.length === 0) {
-        safeConsoleError('Usage: node lang.js &lt;file>');
-        safeConsoleError('  Provide a file path to execute');
-        safeExit(1);
-    } else if (args.length === 1) {
-        // Execute the file
-        const filePath = args[0];
-        await executeFile(filePath);
-    } else {
-        // Too many arguments
-        safeConsoleError('Usage: node lang.js &lt;file>');
-        safeConsoleError('  Provide exactly one file path to execute');
-        safeExit(1);
-    }
-}
-
-// Start the program only if this file is run directly in Node.js/Bun
-if ((isNode || isBun) &amp;&amp; process.argv[1] &amp;&amp; process.argv[1].endsWith('lang.js')) {
-    main().catch(error => {
-        safeConsoleError('Fatal error:', error.message);
-        safeExit(1);
-    });
-}
-
-// Export functions for harness integration
-export { run, interpreter, lexer, parser };
-
-
-</code></pre>
-        </article>
-    </section>
-
-
-
-
-</div>
-
-<br class="clear">
-
-<footer>
-    Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.4</a> on Tue Jul 29 2025 23:15:00 GMT-0400 (Eastern Daylight Time) using the Minami theme.
-</footer>
-
-<script>prettyPrint();</script>
-<script src="scripts/linenumber.js"></script>
-</body>
-</html>