about summary refs log tree commit diff stats
path: root/js/scripting-lang/lang.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/scripting-lang/lang.js')
-rw-r--r--js/scripting-lang/lang.js3776
1 files changed, 1550 insertions, 2226 deletions
diff --git a/js/scripting-lang/lang.js b/js/scripting-lang/lang.js
index 3ad0aa6..d3bc0b5 100644
--- a/js/scripting-lang/lang.js
+++ b/js/scripting-lang/lang.js
@@ -1,10 +1,40 @@
-// The goal here is less to make anything useful...or even something that works, but to learn what parts an interpreted languages needs to have to function.
+// Cross-platform scripting language implementation
+// Supports Node.js, Bun, and browser environments
 
-// Initialize standard library functions
+import { lexer, TokenType } from './lexer.js';
+import { parser } from './parser.js';
+
+/**
+ * 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 eliminates 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.
+ */
 function initializeStandardLibrary(scope) {
-    // Map: Apply a function to each element
+    /**
+     * Map: Apply a function to a value
+     * @param {Function} f - Function to apply
+     * @param {*} x - Value to apply function to
+     * @returns {*} Result of applying f to x
+     * @throws {Error} When first argument is not a function
+     */
     scope.map = function(f, x) { 
-        // Handle function references by calling them if they're functions
         if (typeof f === 'function') {
             return f(x);
         } else {
@@ -12,17 +42,36 @@ function initializeStandardLibrary(scope) {
         }
     };
     
-    // Compose: Compose two functions (f ∘ g)(x) = f(g(x))
+    /**
+     * Compose: Compose two functions (f ∘ g)(x) = f(g(x))
+     * @param {Function} f - Outer function
+     * @param {Function} g - Inner function  
+     * @param {*} [x] - Optional argument to apply composed function to
+     * @returns {Function|*} Either a composed function or the result of applying it
+     * @throws {Error} When first two arguments are not functions
+     */
     scope.compose = function(f, g, x) { 
         if (typeof f === 'function' && typeof g === 'function') {
-            return f(g(x));
+            if (arguments.length === 3) {
+                return f(g(x));
+            } else {
+                return function(x) {
+                    return f(g(x));
+                };
+            }
         } else {
             throw new Error('compose: first two arguments must be functions');
         }
     };
     
-    // Curry: Convert a function that takes multiple arguments into a series of functions
-    // Since our language already uses curried functions by default, this is mostly for explicit currying
+    /**
+     * 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
+     */
     scope.curry = function(f, x, y) { 
         if (typeof f === 'function') {
             return f(x, y);
@@ -31,7 +80,13 @@ function initializeStandardLibrary(scope) {
         }
     };
     
-    // Apply: Apply a function to an argument (same as function call, but more explicit)
+    /**
+     * 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
+     */
     scope.apply = function(f, x) { 
         if (typeof f === 'function') {
             return f(x);
@@ -40,18 +95,35 @@ function initializeStandardLibrary(scope) {
         }
     };
     
-    // Pipe: Compose functions in left-to-right order (opposite of compose)
-    // pipe f g x = g f x
+    /**
+     * Pipe: Compose functions in left-to-right order (opposite of compose)
+     * @param {Function} f - First function
+     * @param {Function} g - Second function
+     * @param {*} [x] - Optional argument to apply piped function to
+     * @returns {Function|*} Either a piped function or the result of applying it
+     * @throws {Error} When first two arguments are not functions
+     */
     scope.pipe = function(f, g, x) { 
         if (typeof f === 'function' && typeof g === 'function') {
-            return g(f(x));
+            if (arguments.length === 3) {
+                return g(f(x));
+            } else {
+                return function(x) {
+                    return g(f(x));
+                };
+            }
         } else {
             throw new Error('pipe: first two arguments must be functions');
         }
     };
     
-    // Filter: Filter based on a predicate
-    // For now, we'll implement it as a higher-order function
+    /**
+     * Filter: Filter a value based on a predicate
+     * @param {Function} p - Predicate function
+     * @param {*} x - Value to test
+     * @returns {*|0} The value if predicate is true, 0 otherwise
+     * @throws {Error} When first argument is not a function
+     */
     scope.filter = function(p, x) { 
         if (typeof p === 'function') {
             return p(x) ? x : 0;
@@ -60,8 +132,14 @@ function initializeStandardLibrary(scope) {
         }
     };
     
-    // Reduce: Reduce to a single value using a binary function
-    // For now, we'll implement it as a higher-order function
+    /**
+     * 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
+     */
     scope.reduce = function(f, init, x) { 
         if (typeof f === 'function') {
             return f(init, x);
@@ -70,7 +148,14 @@ function initializeStandardLibrary(scope) {
         }
     };
     
-    // Fold: Same as reduce, but more explicit about the folding direction
+    /**
+     * 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') {
             return f(init, x);
@@ -78,1924 +163,1281 @@ function initializeStandardLibrary(scope) {
             throw new Error('fold: first argument must be a function');
         }
     };
-}
-
-// Define the types of tokens
-const TokenType = {
-    NUMBER: 'NUMBER',
-    PLUS: 'PLUS',
-    MINUS: 'MINUS',
-    MULTIPLY: 'MULTIPLY',
-    DIVIDE: 'DIVIDE',
-    IDENTIFIER: 'IDENTIFIER',
-    ASSIGNMENT: 'ASSIGNMENT',
-    ARROW: 'ARROW',
-    CASE: 'CASE',
-    OF: 'OF',
-    WILDCARD: 'WILDCARD',
-    FUNCTION: 'FUNCTION',
-    LEFT_PAREN: 'LEFT_PAREN',
-    RIGHT_PAREN: 'RIGHT_PAREN',
-    LEFT_BRACE: 'LEFT_BRACE',
-    RIGHT_BRACE: 'RIGHT_BRACE',
-    SEMICOLON: 'SEMICOLON',
-    IO_IN: 'IO_IN',
-    IO_OUT: 'IO_OUT',
-    IO_ASSERT: 'IO_ASSERT',
-    EQUALS: 'EQUALS',
-    FUNCTION_REF: 'FUNCTION_REF',
-    STRING: 'STRING',
-    // New arithmetic operators
-    MODULO: 'MODULO',
-    POWER: 'POWER',
-    // New comparison operators
-    LESS_THAN: 'LESS_THAN',
-    GREATER_THAN: 'GREATER_THAN',
-    LESS_EQUAL: 'LESS_EQUAL',
-    GREATER_EQUAL: 'GREATER_EQUAL',
-    NOT_EQUAL: 'NOT_EQUAL',
-    // New logical operators
-    AND: 'AND',
-    OR: 'OR',
-    XOR: 'XOR',
-    NOT: 'NOT',
-    // Comments
-    COMMENT: 'COMMENT',
-
-};
-
-// Lexer
-function lexer(input) {
-    debugLog('Starting lexer with input', input);
-    const tokens = [];
-    let current = 0;
-
-    while (current < input.length) {
-        let char = input[current];
-
-        // Handle comments /* ... */
-        if (input.slice(current, current + 2) === '/*') {
-            let commentContent = '';
-            let commentDepth = 1; // Track nested comment depth
-            current += 2; // Skip /*
-            
-            // Find the closing */, handling nested comments
-            while (current < input.length && commentDepth > 0) {
-                if (input.slice(current, current + 2) === '/*') {
-                    commentDepth++;
-                    commentContent += input[current];
-                    commentContent += input[current + 1];
-                    current += 2;
-                } else if (input.slice(current, current + 2) === '*/') {
-                    commentDepth--;
-                    if (commentDepth > 0) {
-                        commentContent += input[current];
-                        commentContent += input[current + 1];
-                    }
-                    current += 2;
-                } else {
-                    commentContent += input[current];
-                    current++;
-                }
-            }
-            
-            if (commentDepth === 0) {
-                // Don't add comment tokens to the output - they're ignored
-                continue;
-            } else {
-                throw new Error('Unterminated comment: missing */');
-            }
-        }
-
-        if (/\d/.test(char)) {
-            let value = '';
-            while (/\d/.test(char)) {
-                value += char;
-                char = input[++current];
-            }
-            tokens.push({
-                type: TokenType.NUMBER,
-                value
-            });
-            continue;
-        }
-
-        if (char === '+') {
-            tokens.push({
-                type: TokenType.PLUS
-            });
-            current++;
-            continue;
-        }
-
-        if (input.slice(current, current + 2) === '->') {
-            tokens.push({
-                type: TokenType.ARROW
-            });
-            current += 2;
-            continue;
-        }
-
-        if (char === '-') {
-            tokens.push({
-                type: TokenType.MINUS
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '*') {
-            tokens.push({
-                type: TokenType.MULTIPLY
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '/') {
-            tokens.push({
-                type: TokenType.DIVIDE
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '%') {
-            tokens.push({
-                type: TokenType.MODULO
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '^') {
-            tokens.push({
-                type: TokenType.POWER
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '<') {
-            if (input.slice(current, current + 2) === '<=') {
-                tokens.push({
-                    type: TokenType.LESS_EQUAL
-                });
-                current += 2;
-            } else {
-                tokens.push({
-                    type: TokenType.LESS_THAN
-                });
-                current++;
-            }
-            continue;
-        }
-
-        if (char === '>') {
-            if (input.slice(current, current + 2) === '>=') {
-                tokens.push({
-                    type: TokenType.GREATER_EQUAL
-                });
-                current += 2;
-            } else {
-                tokens.push({
-                    type: TokenType.GREATER_THAN
-                });
-                current++;
-            }
-            continue;
-        }
-
-        if (input.slice(current, current + 2) === '!=') {
-            tokens.push({
-                type: TokenType.NOT_EQUAL
-            });
-            current += 2;
-            continue;
-        }
-
-        // Check for keywords before general identifiers
-        if (input.slice(current, current + 4) === 'case') {
-            tokens.push({
-                type: TokenType.CASE
-            });
-            current += 4;
-            continue;
-        }
-
-        if (input.slice(current, current + 2) === 'of') {
-            tokens.push({
-                type: TokenType.OF
-            });
-            current += 2;
-            continue;
-        }
-
-        // Check for logical keywords
-        if (input.slice(current, current + 3) === 'and') {
-            tokens.push({
-                type: TokenType.AND
-            });
-            current += 3;
-            continue;
-        }
-
-        if (input.slice(current, current + 2) === 'or') {
-            tokens.push({
-                type: TokenType.OR
-            });
-            current += 2;
-            continue;
-        }
-
-        if (input.slice(current, current + 3) === 'xor') {
-            tokens.push({
-                type: TokenType.XOR
-            });
-            current += 3;
-            continue;
-        }
-
-        if (input.slice(current, current + 3) === 'not') {
-            tokens.push({
-                type: TokenType.NOT
-            });
-            current += 3;
-            continue;
-        }
-
-
-
-        if (/[a-z]/i.test(char)) {
-            let value = '';
-            while (/[a-z0-9_]/i.test(char)) {
-                value += char;
-                char = input[++current];
-            }
-            tokens.push({
-                type: TokenType.IDENTIFIER,
-                value
-            });
-            continue;
-        }
-
-        if (char === ':') {
-            tokens.push({
-                type: TokenType.ASSIGNMENT
-            });
-            current++;
-            continue;
-        }
-
-        if (input.slice(current, current + 2) === '==') {
-            tokens.push({
-                type: TokenType.EQUALS
-            });
-            current += 2;
-            continue;
-        }
-
-        if (char === '@') {
-            tokens.push({
-                type: TokenType.FUNCTION_REF
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '=') {
-            tokens.push({
-                type: TokenType.EQUALS
-            });
-            current++;
-            continue;
-        }
-
-
-
-        if (char === '_') {
-            tokens.push({
-                type: TokenType.WILDCARD
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '(') {
-            tokens.push({
-                type: TokenType.LEFT_PAREN
-            });
-            current++;
-            continue;
-        }
-
-        if (char === ')') {
-            tokens.push({
-                type: TokenType.RIGHT_PAREN
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '{') {
-            tokens.push({
-                type: TokenType.LEFT_BRACE
-            });
-            current++;
-            continue;
-        }
-
-        if (char === '}') {
-            tokens.push({
-                type: TokenType.RIGHT_BRACE
-            });
-            current++;
-            continue;
-        }
-
-        if (input.slice(current, current + 8) === 'function') {
-            tokens.push({
-                type: TokenType.FUNCTION
-            });
-            current += 8;
-            continue;
-        }
-
-        // Check for IO tokens
-        if (input.slice(current, current + 4) === '..in') {
-            tokens.push({
-                type: TokenType.IO_IN
-            });
-            current += 4;
-            continue;
-        }
-
-        if (input.slice(current, current + 5) === '..out') {
-            tokens.push({
-                type: TokenType.IO_OUT
-            });
-            current += 5;
-            continue;
-        }
-
-        if (input.slice(current, current + 8) === '..assert') {
-            tokens.push({
-                type: TokenType.IO_ASSERT
-            });
-            current += 8;
-            continue;
-        }
-
-        // Handle string literals
-        if (char === '"') {
-            let value = '';
-            current++; // Skip opening quote
-            while (current < input.length && input[current] !== '"') {
-                value += input[current];
-                current++;
-            }
-            if (current < input.length && input[current] === '"') {
-                current++; // Skip closing quote
-                tokens.push({
-                    type: TokenType.STRING,
-                    value
-                });
-                continue;
-            } else {
-                throw new Error('Unterminated string literal');
-            }
+    
+    // ===== ARITHMETIC COMBINATORS =====
+    
+    /**
+     * Add: Add two numbers
+     * @param {number} x - First number
+     * @param {number} y - Second number
+     * @returns {number} Sum of x and y
+     */
+    scope.add = function(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) {
+        return x - y;
+    };
+    
+    /**
+     * Multiply: Multiply two numbers
+     * @param {number} x - First number
+     * @param {number} y - Second number
+     * @returns {number} Product of x and y
+     */
+    scope.multiply = function(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 === 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) {
+        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) {
+        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 < y
+     */
+    scope.lessThan = function(x, y) {
+        return x < 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 <= y
+     */
+    scope.lessEqual = function(x, y) {
+        return x <= 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 && 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 && !y) || (!x && 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;
+            };
         }
-
-        if (char === ';') {
-            tokens.push({ type: TokenType.SEMICOLON });
-            current++;
-            continue;
+    };
+    
+    /**
+     * 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);
+            };
         }
-
-        current++;
-    }
-
-    return tokens;
+    };
+    
+    /**
+     * 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) && 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);
+        };
+    };
 }
 
-// Parser
-function parser(tokens) {
-    debugLog('Starting parser with tokens', tokens);
+/**
+ * Interpreter: Walks the AST and evaluates each node.
+ * 
+ * @param {Object} ast - Abstract Syntax Tree to evaluate
+ * @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 translated to function calls to standard library combinators. This eliminates
+ * parsing ambiguity while preserving the original syntax. The parser generates
+ * FunctionCall nodes for operators (e.g., x + y becomes add(x, y)), and the
+ * interpreter executes these calls using the combinator functions in the global scope.
+ * 
+ * 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.
+ */
+function interpreter(ast) {
+    const globalScope = {};
+    initializeStandardLibrary(globalScope);
     
-
+    // Debug: Check if combinators are available
+    if (process.env.DEBUG) {
+        console.log('[DEBUG] Available functions in global scope:', Object.keys(globalScope));
+        console.log('[DEBUG] add function exists:', typeof globalScope.add === 'function');
+        console.log('[DEBUG] subtract function exists:', typeof globalScope.subtract === 'function');
+    }
     
-    let current = 0;
-
-    function walk() {
-        if (current >= tokens.length) {
-            return null; // Return null when there are no more tokens
-        }
-
-        let token = tokens[current];
-
-        // Helper function to detect ambiguous nested function calls
-        function detectAmbiguousFunctionCalls() {
-            // Look ahead to see if we have a pattern like: func1 func2 arg1 func3 arg2
-            // This indicates ambiguous nested function calls
-            let tempCurrent = current;
-            let functionCalls = [];
-            
-            while (tempCurrent < tokens.length && tokens[tempCurrent].type !== TokenType.SEMICOLON) {
-                if (tokens[tempCurrent].type === TokenType.IDENTIFIER) {
-                    // Check if this identifier is followed by arguments (function call)
-                    if (tempCurrent + 1 < tokens.length && 
-                        tokens[tempCurrent + 1].type !== TokenType.ASSIGNMENT &&
-                        tokens[tempCurrent + 1].type !== TokenType.SEMICOLON &&
-                        (tokens[tempCurrent + 1].type === TokenType.NUMBER ||
-                         tokens[tempCurrent + 1].type === TokenType.IDENTIFIER ||
-                         tokens[tempCurrent + 1].type === TokenType.LEFT_PAREN)) {
+    // Reset call stack tracker at the start of interpretation
+    callStackTracker.reset();
+    
+    /**
+     * Evaluates AST nodes in the global scope.
+     * 
+     * @param {Object} 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.
+     */
+    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) < evalNode(node.right);
+                case 'GreaterThanExpression':
+                    return evalNode(node.left) > evalNode(node.right);
+                case 'LessEqualExpression':
+                    return evalNode(node.left) <= 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) && 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 && !rightVal) || (!leftVal && 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);
+                            }
+                            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;
                         
-                        functionCalls.push(tokens[tempCurrent].value);
+                        // Now evaluate the function definition with access to the placeholder
+                        const actualFunction = evalNode(node.value);
                         
-                        // Skip the function name and its arguments
-                        tempCurrent++; // Skip function name
-                        while (tempCurrent < tokens.length && 
-                               tokens[tempCurrent].type !== TokenType.SEMICOLON &&
-                               tokens[tempCurrent].type !== TokenType.RIGHT_PAREN) {
-                            tempCurrent++;
+                        // 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 {
+                            let localScope = Object.create(globalScope);
+                            for (let i = 0; i < 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 < 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 (process.env.DEBUG) {
+                            console.log(`[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 (process.env.DEBUG) {
+                            console.log(`[DEBUG] FunctionCall: looking up function '${node.name.value}' in globalScope, found:`, typeof funcToCall);
                         }
                     } else {
-                        tempCurrent++;
+                        // Function call from expression (e.g., parenthesized function, higher-order)
+                        funcToCall = evalNode(node.name);
+                        if (process.env.DEBUG) {
+                            console.log(`[DEBUG] FunctionCall: evaluated function expression, found:`, typeof funcToCall);
+                        }
                     }
-                } else {
-                    tempCurrent++;
-                }
-            }
-            
-            // If we have more than 2 function calls in sequence, it's ambiguous
-            if (functionCalls.length > 2) {
-                throw new Error(`Ambiguous nested function calls detected: ${functionCalls.join(' ')}. Use parentheses to explicitly group function calls.`);
-            }
-        }
-
-        // Helper function to parse function calls with proper precedence
-        function parseFunctionCall() {
-            if (token.type !== TokenType.IDENTIFIER) {
-                return null;
-            }
-
-            // Check if this is a function call (identifier followed by arguments)
-            if (tokens[current + 1] && 
-                tokens[current + 1].type !== TokenType.ASSIGNMENT &&
-                tokens[current + 1].type !== TokenType.PLUS &&
-                tokens[current + 1].type !== TokenType.MINUS &&
-                tokens[current + 1].type !== TokenType.MULTIPLY &&
-                tokens[current + 1].type !== TokenType.DIVIDE &&
-                tokens[current + 1].type !== TokenType.MODULO &&
-                tokens[current + 1].type !== TokenType.POWER &&
-                tokens[current + 1].type !== TokenType.EQUALS &&
-                tokens[current + 1].type !== TokenType.LESS_THAN &&
-                tokens[current + 1].type !== TokenType.GREATER_THAN &&
-                tokens[current + 1].type !== TokenType.LESS_EQUAL &&
-                tokens[current + 1].type !== TokenType.GREATER_EQUAL &&
-                tokens[current + 1].type !== TokenType.NOT_EQUAL &&
-                tokens[current + 1].type !== TokenType.AND &&
-                tokens[current + 1].type !== TokenType.OR &&
-                tokens[current + 1].type !== TokenType.XOR &&
-                tokens[current + 1].type !== TokenType.NOT &&
-                tokens[current + 1].type !== TokenType.COMPOSE &&
-                tokens[current + 1].type !== TokenType.PIPE &&
-                tokens[current + 1].type !== TokenType.APPLY &&
-                tokens[current + 1].type !== TokenType.SEMICOLON &&
-                (tokens[current + 1].type === TokenType.NUMBER ||
-                 tokens[current + 1].type === TokenType.IDENTIFIER ||
-                 tokens[current + 1].type === TokenType.LEFT_PAREN ||
-                 tokens[current + 1].type === TokenType.FUNCTION_REF)) {
-                
-
-                
-                const funcName = token.value;
-                current++; // Skip function name
-                const args = [];
-                
-                // Collect arguments until we hit a semicolon, closing parenthesis, or end
-                while (current < tokens.length && 
-                       tokens[current].type !== TokenType.SEMICOLON &&
-                       tokens[current].type !== TokenType.RIGHT_PAREN) {
                     
-                    // Handle function references (@functionName)
-                    if (tokens[current] && tokens[current].type === TokenType.FUNCTION_REF) {
-                        current++; // Skip @
-                        if (tokens[current] && tokens[current].type === TokenType.IDENTIFIER) {
-                            args.push({
-                                type: 'FunctionReference',
-                                name: tokens[current].value,
-                            });
-                            current++;
-                        } else {
-                            throw new Error('Expected function name after @');
+                    if (funcToCall instanceof Function) {
+                        let args = node.args.map(evalNode);
+                        if (process.env.DEBUG) {
+                            console.log(`[DEBUG] FunctionCall: calling function with args:`, args);
                         }
-                    } else {
-                        // Parse arguments with proper precedence
-                        // For nested function calls, we need to parse them as complete function calls
-                        if (tokens[current] && tokens[current].type === TokenType.IDENTIFIER &&
-                            tokens[current + 1] && tokens[current + 1].type !== TokenType.ASSIGNMENT &&
-                            tokens[current + 1].type !== TokenType.PLUS &&
-                            tokens[current + 1].type !== TokenType.MINUS &&
-                            tokens[current + 1].type !== TokenType.MULTIPLY &&
-                            tokens[current + 1].type !== TokenType.DIVIDE &&
-                            tokens[current + 1].type !== TokenType.MODULO &&
-                            tokens[current + 1].type !== TokenType.POWER &&
-                            tokens[current + 1].type !== TokenType.EQUALS &&
-                            tokens[current + 1].type !== TokenType.LESS_THAN &&
-                            tokens[current + 1].type !== TokenType.GREATER_THAN &&
-                            tokens[current + 1].type !== TokenType.LESS_EQUAL &&
-                            tokens[current + 1].type !== TokenType.GREATER_EQUAL &&
-                            tokens[current + 1].type !== TokenType.NOT_EQUAL &&
-                            tokens[current + 1].type !== TokenType.AND &&
-                            tokens[current + 1].type !== TokenType.OR &&
-                            tokens[current + 1].type !== TokenType.XOR &&
-                            tokens[current + 1].type !== TokenType.NOT &&
-                            tokens[current + 1].type !== TokenType.SEMICOLON &&
-                            (tokens[current + 1].type === TokenType.NUMBER ||
-                             tokens[current + 1].type === TokenType.IDENTIFIER ||
-                             tokens[current + 1].type === TokenType.LEFT_PAREN)) {
-                            
-                            // Check for ambiguous nested function calls
-                            // Look for pattern: func1 func2 arg1 func3 arg2
-                            let tempCurrent = current;
-                            let functionNames = [];
-                            
-                            while (tempCurrent < tokens.length && 
-                                   tokens[tempCurrent].type !== TokenType.SEMICOLON &&
-                                   tokens[tempCurrent].type !== TokenType.RIGHT_PAREN) {
-                                if (tokens[tempCurrent].type === TokenType.IDENTIFIER &&
-                                    tempCurrent + 1 < tokens.length && 
-                                    tokens[tempCurrent + 1].type !== TokenType.ASSIGNMENT &&
-                                    tokens[tempCurrent + 1].type !== TokenType.PLUS &&
-                                    tokens[tempCurrent + 1].type !== TokenType.MINUS &&
-                                    tokens[tempCurrent + 1].type !== TokenType.MULTIPLY &&
-                                    tokens[tempCurrent + 1].type !== TokenType.DIVIDE &&
-                                    tokens[tempCurrent + 1].type !== TokenType.MODULO &&
-                                    tokens[tempCurrent + 1].type !== TokenType.POWER &&
-                                    tokens[tempCurrent + 1].type !== TokenType.EQUALS &&
-                                    tokens[tempCurrent + 1].type !== TokenType.LESS_THAN &&
-                                    tokens[tempCurrent + 1].type !== TokenType.GREATER_THAN &&
-                                    tokens[tempCurrent + 1].type !== TokenType.LESS_EQUAL &&
-                                    tokens[tempCurrent + 1].type !== TokenType.GREATER_EQUAL &&
-                                    tokens[tempCurrent + 1].type !== TokenType.NOT_EQUAL &&
-                                    tokens[tempCurrent + 1].type !== TokenType.AND &&
-                                    tokens[tempCurrent + 1].type !== TokenType.OR &&
-                                    tokens[tempCurrent + 1].type !== TokenType.XOR &&
-                                    tokens[tempCurrent + 1].type !== TokenType.NOT &&
-                                    tokens[tempCurrent + 1].type !== TokenType.COMPOSE &&
-                                    tokens[tempCurrent + 1].type !== TokenType.PIPE &&
-                                    tokens[tempCurrent + 1].type !== TokenType.APPLY &&
-                                    tokens[tempCurrent + 1].type !== TokenType.SEMICOLON &&
-                                    (tokens[tempCurrent + 1].type === TokenType.NUMBER ||
-                                     tokens[tempCurrent + 1].type === TokenType.IDENTIFIER ||
-                                     tokens[tempCurrent + 1].type === TokenType.LEFT_PAREN)) {
-                                    functionNames.push(tokens[tempCurrent].value);
+                        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 (process.env.DEBUG) {
+                        console.log(`[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 (process.env.DEBUG) {
+                            console.log(`[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 < whenValues.length; i++) {
+                                const value = whenValues[i];
+                                const pattern = patterns[i];
+                                
+                                if (process.env.DEBUG) {
+                                    console.log(`[DEBUG] WhenExpression: comparing value ${value} with pattern ${pattern}`);
                                 }
-                                tempCurrent++;
-                            }
-                            
-                            if (functionNames.length > 2) {
-                                throw new Error(`Ambiguous nested function calls detected: ${functionNames.join(' ')}. Use parentheses to explicitly group function calls.`);
-                            }
-                            
-                            // This is a nested function call, parse it as a complete function call
-                            const nestedFuncName = tokens[current].value;
-                            current++; // Skip function name
-                            const nestedArgs = [];
-                            
-                            // Parse nested function arguments
-                            while (current < tokens.length && 
-                                   tokens[current].type !== TokenType.SEMICOLON &&
-                                   tokens[current].type !== TokenType.RIGHT_PAREN) {
-                                const nestedArg = walk();
-                                if (nestedArg) {
-                                    nestedArgs.push(nestedArg);
+                                
+                                if (pattern === true) { // Wildcard pattern
+                                    // Wildcard always matches
+                                    if (process.env.DEBUG) {
+                                        console.log(`[DEBUG] WhenExpression: wildcard matches`);
+                                    }
+                                    continue;
+                                } else if (value !== pattern) {
+                                    matches = false;
+                                    if (process.env.DEBUG) {
+                                        console.log(`[DEBUG] WhenExpression: pattern does not match`);
+                                    }
+                                    break;
+                                } else {
+                                    if (process.env.DEBUG) {
+                                        console.log(`[DEBUG] WhenExpression: pattern matches`);
+                                    }
                                 }
                             }
-                            
-                            args.push({
-                                type: 'FunctionCall',
-                                name: nestedFuncName,
-                                args: nestedArgs,
-                            });
-                        } else {
-                            // Use walk() for other types of arguments
-                            const arg = walk();
-                            if (arg) {
-                                args.push(arg);
+                        }
+                        
+                        if (process.env.DEBUG) {
+                            console.log(`[DEBUG] WhenExpression: case matches = ${matches}`);
+                        }
+                        
+                        if (matches) {
+                            const results = caseItem.result.map(evalNode);
+                            if (results.length === 1) {
+                                return results[0];
                             }
+                            return results.join(' ');
                         }
                     }
-                }
-                
-                return {
-                    type: 'FunctionCall',
-                    name: funcName,
-                    args,
-                };
-            }
-            
-            return null;
-        }
-
-        // Handle arithmetic expressions that start with a number followed by an operator
-        if (token.type === TokenType.NUMBER && tokens[current + 1] && (
-            tokens[current + 1].type === TokenType.PLUS ||
-            tokens[current + 1].type === TokenType.MINUS ||
-            tokens[current + 1].type === TokenType.MULTIPLY ||
-            tokens[current + 1].type === TokenType.DIVIDE ||
-            tokens[current + 1].type === TokenType.MODULO ||
-            tokens[current + 1].type === TokenType.POWER
-        )) {
-            debugLog('Parsing arithmetic expression starting with number', { 
-                current: token, 
-                next: tokens[current + 1] 
-            });
-            const left = {
-                type: 'NumberLiteral',
-                value: token.value,
-            };
-            current++; // Skip the number
-            const operator = tokens[current].type;
-            current++; // Skip the operator
-            const right = walk();
-            
-            const expressionTypes = {
-                [TokenType.PLUS]: 'PlusExpression',
-                [TokenType.MINUS]: 'MinusExpression',
-                [TokenType.MULTIPLY]: 'MultiplyExpression',
-                [TokenType.DIVIDE]: 'DivideExpression',
-                [TokenType.MODULO]: 'ModuloExpression',
-                [TokenType.POWER]: 'PowerExpression',
-            };
-            
-            return {
-                type: expressionTypes[operator],
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.NUMBER) {
-            current++;
-            return {
-                type: 'NumberLiteral',
-                value: token.value,
-            };
-        }
-
-        if (token.type === TokenType.STRING) {
-            current++;
-            return {
-                type: 'StringLiteral',
-                value: token.value,
-            };
-        }
-
-        if (token.type === TokenType.WILDCARD) {
-            current++;
-            return {
-                type: 'WildcardPattern',
-            };
-        }
-
-        if (token.type === TokenType.IO_IN) {
-            current++;
-            return {
-                type: 'IOInExpression',
-            };
-        }
-
-        if (token.type === TokenType.IO_OUT) {
-            current++;
-            const value = walk();
-            return {
-                type: 'IOOutExpression',
-                value,
-            };
-        }
-
-        if (token.type === TokenType.FUNCTION_REF) {
-            current++; // Skip the @ token
-            if (tokens[current] && tokens[current].type === TokenType.IDENTIFIER) {
-                const functionName = tokens[current].value;
-                current++; // Skip the function name
-                return {
-                    type: 'FunctionReference',
-                    name: functionName,
-                };
-            } else {
-                throw new Error('Expected function name after @');
-            }
-        }
-
-        if (token.type === TokenType.IO_ASSERT) {
-            current++;
-            
-            // Parse a comparison expression (e.g., x = 5, y > 3, z != 0)
-            const left = walk();
-            
-            // Expect a comparison operator
-            if (tokens[current] && (
-                tokens[current].type === TokenType.EQUALS ||
-                tokens[current].type === TokenType.LESS_THAN ||
-                tokens[current].type === TokenType.GREATER_THAN ||
-                tokens[current].type === TokenType.LESS_EQUAL ||
-                tokens[current].type === TokenType.GREATER_EQUAL ||
-                tokens[current].type === TokenType.NOT_EQUAL
-            )) {
-                const operator = tokens[current].type;
-                current++; // Skip the operator
-                const right = walk();
-                
-                return {
-                    type: 'IOAssertExpression',
-                    left,
-                    operator,
-                    right,
-                };
-            } else {
-                throw new Error('Expected comparison operator (=, <, >, <=, >=, !=) in assertion');
-            }
-        }
-
-
-
-        if (token.type === TokenType.PLUS) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'PlusExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.MINUS) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'MinusExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.MULTIPLY) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'MultiplyExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.DIVIDE) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'DivideExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.MODULO) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'ModuloExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.POWER) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'PowerExpression',
-                left,
-                right,
-            };
-        }
-
-        // Comparison operators
-        if (token.type === TokenType.EQUALS) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'EqualsExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.LESS_THAN) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'LessThanExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.GREATER_THAN) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'GreaterThanExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.LESS_EQUAL) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'LessEqualExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.GREATER_EQUAL) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'GreaterEqualExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.NOT_EQUAL) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'NotEqualExpression',
-                left,
-                right,
-            };
-        }
-
-        // Logical operators
-        if (token.type === TokenType.AND) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'AndExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.OR) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'OrExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.XOR) {
-            current++;
-            const left = walk();
-            const right = walk();
-            return {
-                type: 'XorExpression',
-                left,
-                right,
-            };
-        }
-
-        if (token.type === TokenType.NOT) {
-            current++;
-            const operand = walk();
-            return {
-                type: 'NotExpression',
-                operand,
-            };
-        }
-
-        // Functional operators
-
-
-        if (token.type === TokenType.LEFT_PAREN) {
-            current++; // Skip opening parenthesis
-            const expression = walk();
-            
-            // Expect closing parenthesis
-            if (tokens[current] && tokens[current].type === TokenType.RIGHT_PAREN) {
-                debugLog('Found right parenthesis');
-                current++; // Skip closing parenthesis
-                
-                // Check if there's an arithmetic, logical, or functional operator after the parentheses
-                if (tokens[current] && (
-                    tokens[current].type === TokenType.PLUS ||
-                    tokens[current].type === TokenType.MINUS ||
-                    tokens[current].type === TokenType.MULTIPLY ||
-                    tokens[current].type === TokenType.DIVIDE ||
-                    tokens[current].type === TokenType.MODULO ||
-                    tokens[current].type === TokenType.POWER ||
-                    tokens[current].type === TokenType.AND ||
-                    tokens[current].type === TokenType.OR ||
-                    tokens[current].type === TokenType.XOR ||
-                    tokens[current].type === TokenType.NOT
-                )) {
-                    // This is an arithmetic expression with parentheses as left operand
-                    const operator = tokens[current].type;
-                    current++; // Skip the operator
-                    const right = walk();
-                    
-                    const expressionTypes = {
-                        [TokenType.PLUS]: 'PlusExpression',
-                        [TokenType.MINUS]: 'MinusExpression',
-                        [TokenType.MULTIPLY]: 'MultiplyExpression',
-                        [TokenType.DIVIDE]: 'DivideExpression',
-                        [TokenType.MODULO]: 'ModuloExpression',
-                        [TokenType.POWER]: 'PowerExpression',
-                        [TokenType.AND]: 'AndExpression',
-                        [TokenType.OR]: 'OrExpression',
-                        [TokenType.XOR]: 'XorExpression',
-                        [TokenType.NOT]: 'NotExpression',
-
-                    };
+                    throw new Error('No matching pattern found');
+                case 'WildcardPattern':
+                    return true;
+                case 'IOInExpression':
+                    const readline = require('readline');
+                    const rl = readline.createInterface({
+                        input: process.stdin,
+                        output: process.stdout
+                    });
                     
-                    return {
-                        type: expressionTypes[operator],
-                        left: expression,
-                        right,
-                    };
-                }
-                
-                // If not followed by an arithmetic operator, just return the parenthesized expression
-                // This handles cases like function arguments: add (3 + 2) (4 + 1)
-                return expression;
-            } else {
-                debugLog('Expected right parenthesis but found', tokens[current]);
-                throw new Error('Expected closing parenthesis');
+                    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);
+                    console.log(outputValue);
+                    return outputValue;
+                case 'IOAssertExpression':
+                    const assertionValue = evalNode(node.value);
+                    if (!assertionValue) {
+                        throw new Error('Assertion failed');
+                    }
+                    return assertionValue;
+                case 'FunctionReference':
+                    const functionValue = globalScope[node.name];
+                    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();
         }
+    }
 
-        if (token.type === TokenType.ASSIGNMENT) {
-            current++; // Skip the assignment token
-            return walk(); // Continue parsing
-        }
-
-        if (token.type === TokenType.IDENTIFIER) {
-            // First, try to parse as a function call with proper precedence
-            const functionCall = parseFunctionCall();
-            if (functionCall) {
-                return functionCall;
+    /**
+     * Evaluates AST nodes in a local scope with access to parent scope.
+     * 
+     * @param {Object} 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.
+     */
+    const localEvalNodeWithScope = (node, scope) => {
+        callStackTracker.push('localEvalNodeWithScope', node?.type || 'unknown');
+        
+        try {
+            if (!node) {
+                return undefined;
             }
-            
-            // Check if this is followed by an assignment (variable or function)
-            if (tokens[current + 1] && tokens[current + 1].type === TokenType.ASSIGNMENT) {
-                const name = token.value;
-                
-                // Check if this is a function declaration (has parameters and arrow)
-                // Look ahead to see if there are parameters followed by arrow
-                let params = [];
-                let isFunction = false;
-                let tempCurrent = current + 2; // Skip identifier and assignment
-                
-                // Collect parameters until we hit an arrow
-                while (tempCurrent < tokens.length && tokens[tempCurrent].type === TokenType.IDENTIFIER) {
-                    params.push(tokens[tempCurrent].value);
-                    tempCurrent++;
-                }
-                
-                // Check if next token is arrow
-                if (tempCurrent < tokens.length && tokens[tempCurrent].type === TokenType.ARROW) {
-                    isFunction = true;
-                }
-                
-                if (isFunction) {
-                    // This is a function declaration
-                    // Advance current to where tempCurrent left off
-                    current = tempCurrent + 1; // Skip the arrow token
+            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) < localEvalNodeWithScope(node.right, scope);
+                case 'GreaterThanExpression':
+                    return localEvalNodeWithScope(node.left, scope) > localEvalNodeWithScope(node.right, scope);
+                case 'LessEqualExpression':
+                    return localEvalNodeWithScope(node.left, scope) <= 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) && 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 && !rightVal) || (!leftVal && rightVal));
+                case 'NotExpression':
+                    return !localEvalNodeWithScope(node.operand, scope);
+                case 'UnaryMinusExpression':
+                    return -localEvalNodeWithScope(node.operand, scope);
+                case 'TableLiteral':
+                    const table = {};
+                    let arrayIndex = 1;
                     
-                    // Check if the body is a case expression
-                    if (tokens[current] && tokens[current].type === TokenType.CASE) {
-                        current++; // Skip 'case'
-                        
-                        // Parse the value being matched (e.g., "x y")
-                        const value = [];
-                        while (current < tokens.length && 
-                               tokens[current].type !== TokenType.OF) {
-                            if (tokens[current].type === TokenType.IDENTIFIER) {
-                                value.push({
-                                    type: 'Identifier',
-                                    value: tokens[current].value,
-                                });
-                            } else if (tokens[current].type === TokenType.NUMBER) {
-                                value.push({
-                                    type: 'NumberLiteral',
-                                    value: tokens[current].value,
-                                });
+                    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);
                             }
-                            current++;
+                            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`);
+                        };
                         
-                        // Expect 'of' after the value
-                        if (tokens[current] && tokens[current].type === TokenType.OF) {
-                            current++; // Skip 'of'
-                        } else {
-                            throw new Error('Expected "of" after case value');
-                        }
+                        // Store the placeholder in global scope
+                        globalScope[node.name] = placeholder;
                         
-                        const cases = [];
+                        // Now evaluate the function definition with access to the placeholder
+                        const actualFunction = localEvalNodeWithScope(node.value, scope);
                         
-                        // Parse cases until we hit a semicolon or end
-                        while (current < tokens.length) {
-                            // Check if we've reached the end of the case expression
-                            if (tokens[current] && tokens[current].type === TokenType.SEMICOLON) {
-                                debugLog('Found semicolon at start of loop, ending case parsing');
-                                current++;
-                                break; // Exit the case parsing loop
-                            }
-                            
-                            debugLog('Case parsing loop', { 
-                                current: tokens[current], 
-                                currentType: tokens[current]?.type 
-                            });
-                            // Parse pattern (e.g., "0 0" or "0 _" or "_ 0" or "_ _")
-                            debugLog('Parsing pattern', { 
-                                current: tokens[current], 
-                                currentType: tokens[current]?.type 
-                            });
-                            const pattern = [];
-                            while (current < tokens.length && 
-                                   tokens[current].type !== TokenType.ASSIGNMENT && 
-                                   tokens[current].type !== TokenType.SEMICOLON) {
-                                if (tokens[current].type === TokenType.NUMBER) {
-                                    pattern.push({
-                                        type: 'NumberLiteral',
-                                        value: tokens[current].value,
-                                    });
-                                } else if (tokens[current].type === TokenType.WILDCARD) {
-                                    pattern.push({
-                                        type: 'WildcardPattern',
-                                    });
-                                } else if (tokens[current].type === TokenType.IDENTIFIER) {
-                                    pattern.push({
-                                        type: 'Identifier',
-                                        value: tokens[current].value,
-                                    });
-                                }
-                                current++;
-                            }
-                            
-                            // Expect ':' after pattern
-                            debugLog('Looking for : after pattern', { 
-                                current: tokens[current], 
-                                currentType: tokens[current]?.type 
-                            });
-                            if (tokens[current] && tokens[current].type === TokenType.ASSIGNMENT) {
-                                current++; // Skip ':'
-                                debugLog('Found : after pattern');
-                            } else {
-                                throw new Error('Expected ":" after pattern');
-                            }
-                            
-                            // Parse result using walk() to handle complex expressions
-                            debugLog('Starting case result parsing', { 
-                                current: tokens[current], 
-                                currentType: tokens[current]?.type 
-                            });
-                            const result = [];
-                            
-                            // Parse one expression for the case result
-                            debugLog('Parsing case result node', { 
-                                current: tokens[current], 
-                                currentType: tokens[current]?.type 
-                            });
-                            const resultNode = walk();
-                            if (resultNode) {
-                                result.push(resultNode);
-                                debugLog('Added result node', resultNode);
+                        // 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 && scope.hasOwnProperty(node.value)) {
+                        return scope[node.value];
+                    }
+                    const identifierValue = globalScope[node.value];
+                    if (identifierValue === undefined && 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 < node.params.length; i++) {
+                                nestedScope[node.params[i]] = args[i];
                             }
-                            
-                            cases.push({ pattern, result });
-                            
-                            // Check if we've reached the end of the case expression
-                            if (tokens[current] && tokens[current].type === TokenType.SEMICOLON) {
-                                debugLog('Found semicolon, ending case parsing');
-                                current++;
-                                break; // Exit the case parsing loop
+                            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 < node.parameters.length; i++) {
+                                nestedScope[node.parameters[i]] = args[i];
                             }
+                            return localEvalNodeWithScope(node.body, nestedScope);
+                        } finally {
+                            callStackTracker.pop();
                         }
-                        
-                        const body = {
-                            type: 'CaseExpression',
-                            value,
-                            cases,
-                        };
-                        
-                        return {
-                            type: 'FunctionDeclaration',
-                            name,
-                            params,
-                            body,
-                        };
+                    };
+                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 {
-                        // Parse function body directly to avoid function call detection issues
-                        let body;
-                        if (tokens[current] && (
-                            tokens[current].type === TokenType.PLUS ||
-                            tokens[current].type === TokenType.MINUS ||
-                            tokens[current].type === TokenType.MULTIPLY ||
-                            tokens[current].type === TokenType.DIVIDE
-                        )) {
-                            // Arithmetic expression
-                            const operator = tokens[current].type;
-                            current++; // Skip operator
-                            const left = walk();
-                            const right = walk();
-                            
-                            const expressionTypes = {
-                                [TokenType.PLUS]: 'PlusExpression',
-                                [TokenType.MINUS]: 'MinusExpression',
-                                [TokenType.MULTIPLY]: 'MultiplyExpression',
-                                [TokenType.DIVIDE]: 'DivideExpression',
-                            };
-                            
-                            body = {
-                                type: expressionTypes[operator],
-                                left,
-                                right,
-                            };
-                                            } else {
-                        // Fallback to walk() for other cases
-                        debugLog('Using walk() fallback for function body');
-                        body = walk();
-                    }
-                        
-                        return {
-                            type: 'FunctionDeclaration',
-                            name,
-                            params,
-                            body,
-                        };
+                        // Function call from expression (e.g., parenthesized function, higher-order)
+                        localFunc = localEvalNodeWithScope(node.name, scope);
                     }
-                } else {
-                    // This is a variable assignment
-                    // Advance current to skip identifier and assignment token
-                    current += 2; // Skip identifier and assignment
                     
-                    debugLog('Assignment parsing', { 
-                        current: tokens[current], 
-                        next: tokens[current + 1],
-                        currentType: tokens[current]?.type,
-                        nextType: tokens[current + 1]?.type
-                    });
+                    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)];
                     
-                    // Parse the value directly without calling walk()
-                    let value;
+                    if (process.env.DEBUG) {
+                        console.log(`[DEBUG] localEvalNodeWithScope WhenExpression: whenValues =`, whenValues);
+                    }
                     
-                    // Check if the value is a function call
-                    if (tokens[current] && tokens[current].type === TokenType.IDENTIFIER &&
-                        tokens[current + 1] && tokens[current + 1].type !== TokenType.ASSIGNMENT &&
-                        tokens[current + 1].type !== TokenType.SEMICOLON &&
-                        tokens[current + 1].type !== TokenType.ARROW &&
-                        (tokens[current + 1].type === TokenType.NUMBER ||
-                         tokens[current + 1].type === TokenType.IDENTIFIER ||
-                         tokens[current + 1].type === TokenType.FUNCTION_REF ||
-                         tokens[current + 1].type === TokenType.LEFT_PAREN)) {
-                        // This is a function call as the value
-                        const funcName = tokens[current].value;
-                        current++; // Skip function name
-                        const args = [];
+                    for (const caseItem of node.cases) {
+                        // Handle both single patterns and arrays of patterns
+                        const patterns = caseItem.pattern.map(pat => localEvalNodeWithScope(pat, scope));
                         
-                        // Collect arguments until we hit a semicolon, closing parenthesis, or end
-                        while (current < tokens.length && 
-                               tokens[current].type !== TokenType.SEMICOLON &&
-                               tokens[current].type !== TokenType.RIGHT_PAREN) {
-                            // Handle function references (@functionName)
-                            if (tokens[current] && tokens[current].type === TokenType.FUNCTION_REF) {
-                                current++; // Skip @
-                                if (tokens[current] && tokens[current].type === TokenType.IDENTIFIER) {
-                                    args.push({
-                                        type: 'FunctionReference',
-                                        name: tokens[current].value,
-                                    });
-                                    current++;
-                                } else {
-                                    throw new Error('Expected function name after @');
+                        if (process.env.DEBUG) {
+                            console.log(`[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 < whenValues.length; i++) {
+                                const value = whenValues[i];
+                                const pattern = patterns[i];
+                                
+                                if (process.env.DEBUG) {
+                                    console.log(`[DEBUG] localEvalNodeWithScope WhenExpression: comparing value ${value} with pattern ${pattern}`);
                                 }
-                            } else {
-                                // Use walk() to parse complex arguments (including arithmetic expressions)
-                                const arg = walk();
-                                if (arg) {
-                                    args.push(arg);
+                                
+                                if (pattern === true) { // Wildcard pattern
+                                    // Wildcard always matches
+                                    if (process.env.DEBUG) {
+                                        console.log(`[DEBUG] localEvalNodeWithScope WhenExpression: wildcard matches`);
+                                    }
+                                    continue;
+                                } else if (value !== pattern) {
+                                    matches = false;
+                                    if (process.env.DEBUG) {
+                                        console.log(`[DEBUG] localEvalNodeWithScope WhenExpression: pattern does not match`);
+                                    }
+                                    break;
+                                } else {
+                                    if (process.env.DEBUG) {
+                                        console.log(`[DEBUG] localEvalNodeWithScope WhenExpression: pattern matches`);
+                                    }
                                 }
                             }
                         }
                         
-                        value = {
-                            type: 'FunctionCall',
-                            name: funcName,
-                            args,
-                        };
-                        
-                        debugLog('Function call parsed in assignment', { name: funcName, args });
-                    } else if (tokens[current] && tokens[current].type === TokenType.NUMBER) {
-                        // Check if this is the start of an arithmetic expression
-                        if (tokens[current + 1] && (
-                            tokens[current + 1].type === TokenType.PLUS ||
-                            tokens[current + 1].type === TokenType.MINUS ||
-                            tokens[current + 1].type === TokenType.MULTIPLY ||
-                            tokens[current + 1].type === TokenType.DIVIDE
-                        )) {
-                            // This is an arithmetic expression, use walk() to parse it
-                            debugLog('Parsing arithmetic expression in assignment');
-                            debugLog('Current token before walk()', tokens[current]);
-                            value = walk();
-                            debugLog('Arithmetic expression parsed', value);
-                        } else {
-                            // Simple number value
-                            value = {
-                                type: 'NumberLiteral',
-                                value: tokens[current].value,
-                            };
-                            current++;
-                        }
-                    } else if (tokens[current] && tokens[current].type === TokenType.STRING) {
-                        // Simple string value
-                        value = {
-                            type: 'StringLiteral',
-                            value: tokens[current].value,
-                        };
-                        current++;
-                    } else if (tokens[current] && tokens[current].type === TokenType.IDENTIFIER) {
-                        // Check if this is the start of an infix arithmetic expression
-                        if (tokens[current + 1] && (
-                            tokens[current + 1].type === TokenType.PLUS ||
-                            tokens[current + 1].type === TokenType.MINUS ||
-                            tokens[current + 1].type === TokenType.MULTIPLY ||
-                            tokens[current + 1].type === TokenType.DIVIDE
-                        )) {
-                            // This is an infix arithmetic expression
-                            const left = {
-                                type: 'Identifier',
-                                value: tokens[current].value,
-                            };
-                            current++; // Skip left operand
-                            const operator = tokens[current].type;
-                            current++; // Skip operator
-                            const right = walk();
-                            
-                            const expressionTypes = {
-                                [TokenType.PLUS]: 'PlusExpression',
-                                [TokenType.MINUS]: 'MinusExpression',
-                                [TokenType.MULTIPLY]: 'MultiplyExpression',
-                                [TokenType.DIVIDE]: 'DivideExpression',
-                            };
-                            
-                            value = {
-                                type: expressionTypes[operator],
-                                left,
-                                right,
-                            };
-                        } else {
-                            // Simple identifier value
-                            value = {
-                                type: 'Identifier',
-                                value: tokens[current].value,
-                            };
-                            current++;
+                        if (process.env.DEBUG) {
+                            console.log(`[DEBUG] localEvalNodeWithScope WhenExpression: case matches = ${matches}`);
                         }
-                    } else if (tokens[current] && (
-                        tokens[current].type === TokenType.PLUS ||
-                        tokens[current].type === TokenType.MINUS ||
-                        tokens[current].type === TokenType.MULTIPLY ||
-                        tokens[current].type === TokenType.DIVIDE
-                    )) {
-                        // Arithmetic expression value (prefix notation)
-                        const operator = tokens[current].type;
-                        current++; // Skip operator
-                        const left = walk();
-                        const right = walk();
-                        
-                        const expressionTypes = {
-                            [TokenType.PLUS]: 'PlusExpression',
-                            [TokenType.MINUS]: 'MinusExpression',
-                            [TokenType.MULTIPLY]: 'MultiplyExpression',
-                            [TokenType.DIVIDE]: 'DivideExpression',
-                        };
                         
-                        value = {
-                            type: expressionTypes[operator],
-                            left,
-                            right,
-                        };
-                    } else {
-                        // Fallback to walk() for other cases
-                        debugLog('Using walk() fallback for assignment value', { current: tokens[current] });
-                        value = walk();
+                        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 readline = require('readline');
+                    const rl = readline.createInterface({
+                        input: process.stdin,
+                        output: process.stdout
+                    });
                     
-                    return {
-                        type: 'AssignmentExpression',
-                        name,
-                        value,
-                    };
-                }
-            }
-            
-
-            
-            // Check if this is followed by an operator
-            if (tokens[current + 1] && (
-                tokens[current + 1].type === TokenType.PLUS ||
-                tokens[current + 1].type === TokenType.MINUS ||
-                tokens[current + 1].type === TokenType.MULTIPLY ||
-                tokens[current + 1].type === TokenType.DIVIDE
-            )) {
-                const left = {
-                    type: 'Identifier',
-                    value: token.value,
-                };
-                const operator = tokens[current + 1].type;
-                current += 2; // Skip identifier and operator
-                const right = walk();
-                
-                const expressionTypes = {
-                    [TokenType.PLUS]: 'PlusExpression',
-                    [TokenType.MINUS]: 'MinusExpression',
-                    [TokenType.MULTIPLY]: 'MultiplyExpression',
-                    [TokenType.DIVIDE]: 'DivideExpression',
-                };
-                
-                return {
-                    type: expressionTypes[operator],
-                    left,
-                    right,
-                };
-            }
-            
-            // Check if this is followed by an operator
-            if (tokens[current + 1] && (
-                tokens[current + 1].type === TokenType.PLUS ||
-                tokens[current + 1].type === TokenType.MINUS ||
-                tokens[current + 1].type === TokenType.MULTIPLY ||
-                tokens[current + 1].type === TokenType.DIVIDE
-            )) {
-                const left = {
-                    type: 'Identifier',
-                    value: token.value,
-                };
-                const operator = tokens[current + 1].type;
-                current += 2; // Skip identifier and operator
-                const right = walk();
-                
-                const expressionTypes = {
-                    [TokenType.PLUS]: 'PlusExpression',
-                    [TokenType.MINUS]: 'MinusExpression',
-                    [TokenType.MULTIPLY]: 'MultiplyExpression',
-                    [TokenType.DIVIDE]: 'DivideExpression',
-                };
-                
-                return {
-                    type: expressionTypes[operator],
-                    left,
-                    right,
-                };
-            }
-            
-            current++;
-            return {
-                type: 'Identifier',
-                value: token.value,
-            };
-        }
-
-
-
-        // Pattern matching: value : pattern1 : result1 pattern2 : result2 ...
-        if (token.type === TokenType.CASE) {
-            current++; // Skip 'case'
-            
-            // Parse the value being matched (wrap in array for interpreter compatibility)
-            const valueNode = walk();
-            const value = [valueNode];
-            
-            // Expect 'of'
-            if (tokens[current].type !== TokenType.OF) {
-                throw new Error('Expected "of" after "case"');
-            }
-            current++; // Skip 'of'
-            
-            const cases = [];
-            
-            // Parse cases until we hit a semicolon or end
-            while (current < tokens.length && tokens[current].type !== TokenType.SEMICOLON) {
-                const patternNode = walk();
-                const resultNode = walk();
-                cases.push({ 
-                    pattern: [patternNode], 
-                    result: [resultNode] 
-                });
-            }
-            
-            return {
-                type: 'CaseExpression',
-                value,
-                cases,
-            };
-        }
-
-        if (token.type === TokenType.IF) {
-            current++;
-            let node = {
-                type: 'IfExpression',
-                test: walk(),
-                consequent: walk(),
-                alternate: tokens[current].type === TokenType.ELSE ? (current++, walk()) : null,
-            };
-            return node;
-        }
-
-        if (token.type === TokenType.FUNCTION) {
-            current++;
-            let node = {
-                type: 'FunctionDeclaration',
-                name: tokens[current++].value,
-                params: [],
-                body: [],
-            };
-            while (tokens[current].type !== TokenType.RIGHT_PAREN) {
-                node.params.push(tokens[current++].value);
-            }
-            current++; // Skip right paren
-            while (tokens[current].type !== TokenType.RIGHT_BRACE) {
-                node.body.push(walk());
-            }
-            current++; // Skip right brace
-            return node;
-        }
-
-        if (token.type === TokenType.IDENTIFIER && tokens[current + 1].type === TokenType.LEFT_PAREN) {
-            current++;
-            let node = {
-                type: 'FunctionCall',
-                name: token.value,
-                args: [],
-            };
-            current++; // Skip left paren
-            while (tokens[current].type !== TokenType.RIGHT_PAREN) {
-                node.args.push(walk());
+                    return new Promise((resolve) => {
+                        rl.question('', (input) => {
+                            rl.close();
+                            const num = parseInt(input);
+                            resolve(isNaN(num) ? input : num);
+                        });
+                    });
+                case 'IOOutExpression':
+                    const localOutputValue = localEvalNodeWithScope(node.value, scope);
+                    console.log(localOutputValue);
+                    return localOutputValue;
+                case 'IOAssertExpression':
+                    const localAssertionValue = localEvalNodeWithScope(node.value, scope);
+                    if (!localAssertionValue) {
+                        throw new Error('Assertion failed');
+                    }
+                    return localAssertionValue;
+                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}`);
             }
-            current++; // Skip right paren
-            return node;
+        } finally {
+            callStackTracker.pop();
         }
-
-        if (token.type === TokenType.SEMICOLON) {
-            current++;
-            return null;
-        }
-
-        throw new TypeError(token.type);
-    }
-
-    let ast = {
-        type: 'Program',
-        body: [],
     };
 
-    while (current < tokens.length) {
-        const node = walk();
-        if (node !== null && node !== undefined) {
-            ast.body.push(node);
-        }
-    }
-
-    return ast;
-}
-
-// Interpreter
-function interpreter(ast) {
-    debugLog('Starting interpreter with AST', ast);
-    let globalScope = {};
-    initializeStandardLibrary(globalScope);
-
-    function evalNode(node) {
-        if (!node) {
-            return undefined;
-        }
-        switch (node.type) {
-            case 'NumberLiteral':
-                return parseInt(node.value);
-            case 'StringLiteral':
-                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) < evalNode(node.right);
-            case 'GreaterThanExpression':
-                return evalNode(node.left) > evalNode(node.right);
-            case 'LessEqualExpression':
-                return evalNode(node.left) <= 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) && 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 && !rightVal) || (!leftVal && rightVal);
-            case 'NotExpression':
-                return !evalNode(node.operand);
-
-            case 'AssignmentExpression':
-                if (globalScope.hasOwnProperty(node.name)) {
-                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                }
-                const value = evalNode(node.value);
-                globalScope[node.name] = value;
-                return;
-            case 'Identifier':
-                const identifierValue = globalScope[node.value];
-                if (identifierValue === undefined) {
-                    throw new Error(`Variable ${node.value} is not defined`);
-                }
-                return identifierValue;
-            case 'FunctionReference':
-                const functionValue = globalScope[node.name];
-                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 'IfExpression':
-                return evalNode(node.test) ? evalNode(node.consequent) : node.alternate ? evalNode(node.alternate) : undefined;
-            case 'FunctionDeclaration':
-                if (globalScope.hasOwnProperty(node.name)) {
-                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                }
-                globalScope[node.name] = function(...args) {
-                    let localScope = Object.create(globalScope);
-                    for (let i = 0; i < node.params.length; i++) {
-                        localScope[node.params[i]] = args[i];
+    /**
+     * 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.
+     */
+    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');
                     }
-                    // Create a new evalNode function that uses localScope
-                    const localEvalNode = (node) => {
-                        if (!node) {
-                            return undefined;
+                    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) < localEvalNode(node.right);
+                case 'GreaterThanExpression':
+                    return localEvalNode(node.left) > localEvalNode(node.right);
+                case 'LessEqualExpression':
+                    return localEvalNode(node.left) <= 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) && 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 && !rightVal) || (!leftVal && 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;
                         }
-                        switch (node.type) {
-                            case 'NumberLiteral':
-                                return parseInt(node.value);
-                            case 'StringLiteral':
-                                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) < localEvalNode(node.right);
-                            case 'GreaterThanExpression':
-                                return localEvalNode(node.left) > localEvalNode(node.right);
-                            case 'LessEqualExpression':
-                                return localEvalNode(node.left) <= 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) && 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 && !rightVal) || (!leftVal && rightVal);
-                            case 'NotExpression':
-                                return !localEvalNode(node.operand);
-
-                            case 'AssignmentExpression':
-                                if (localScope.hasOwnProperty(node.name)) {
-                                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                                }
-                                localScope[node.name] = localEvalNode(node.value);
-                                return;
-                            case 'Identifier':
-                                const identifierValue = localScope[node.value];
-                                if (identifierValue === undefined && node.value) {
-                                    return node.value; // Return as string literal if not found in scope
-                                }
-                                return identifierValue;
-                            case 'FunctionReference':
-                                const localFunctionValue = localScope[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 'IfExpression':
-                                return localEvalNode(node.test) ? localEvalNode(node.consequent) : node.alternate ? localEvalNode(node.alternate) : undefined;
-                            case 'FunctionDeclaration':
-                                if (localScope.hasOwnProperty(node.name)) {
-                                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                                }
-                                localScope[node.name] = function(...args) {
-                                    let nestedScope = Object.create(localScope);
-                                    for (let i = 0; i < node.params.length; i++) {
-                                        nestedScope[node.params[i]] = args[i];
-                                    }
-                                    return localEvalNode(node.body);
-                                };
-                                return;
-                                        case 'FunctionCall':
-                if (localScope[node.name] instanceof Function) {
-                    let args = node.args.map(localEvalNode);
-                    return localScope[node.name](...args);
-                }
-                throw new Error(`Function ${node.name} is not defined`);
-            case 'CaseExpression':
-                // Evaluate the values being matched (e.g., [x, y])
-                const values = node.value.map(localEvalNode);
-                
-                for (const caseItem of node.cases) {
-                    // Evaluate the pattern (e.g., [0, 0] or [0, _])
-                    const pattern = caseItem.pattern.map(localEvalNode);
+                    }
+                    
+                    return table;
+                case 'TableAccess':
+                    const tableValue = localEvalNode(node.table);
+                    let keyValue;
                     
-                    // Check if pattern matches values
-                    let matches = true;
-                    for (let i = 0; i < Math.max(values.length, pattern.length); i++) {
-                        const value = values[i];
-                        const patternValue = pattern[i];
+                    // 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`);
+                        };
                         
-                        // Wildcard always matches
-                        if (patternValue === true) continue;
+                        // Store the placeholder in global scope
+                        globalScope[node.name] = placeholder;
                         
-                        // Otherwise, values must be equal
-                        if (value !== patternValue) {
-                            matches = false;
-                            break;
-                        }
+                        // 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;
                     }
                     
-                    if (matches) {
-                        // Evaluate all results
-                        const results = caseItem.result.map(localEvalNode);
-                        // If there's only one result, return it directly (preserves number type)
-                        if (results.length === 1) {
-                            return results[0];
-                        }
-                        // Otherwise join multiple results as string
-                        return results.join(' ');
+                    globalScope[node.name] = localEvalNode(node.value);
+                    return;
+                case 'Identifier':
+                    const identifierValue = globalScope[node.value];
+                    if (identifierValue === undefined && node.value) {
+                        return node.value;
                     }
-                }
-                throw new Error('No matching pattern found');
-                        case 'WildcardPattern':
-                            return true; // Wildcard always matches
-                                    case 'IOInExpression':
-                // Read from stdin
-                const readline = require('readline');
-                const rl = readline.createInterface({
-                    input: process.stdin,
-                    output: process.stdout
-                });
-                
-                return new Promise((resolve) => {
-                    rl.question('', (input) => {
-                        rl.close();
-                        const num = parseInt(input);
-                        resolve(isNaN(num) ? input : num);
-                    });
-                });
-                        case 'IOOutExpression':
-                            // Output to stdout
-                            const localOutputValue = localEvalNode(node.value);
-                            console.log(localOutputValue);
-                            return localOutputValue;
-                        case 'IOAssertExpression':
-                            // Assert that left operator right is true
-                            const localLeftValue = localEvalNode(node.left);
-                            const localRightValue = localEvalNode(node.right);
-                            
-                            let localAssertionPassed = false;
-                            switch (node.operator) {
-                                case TokenType.EQUALS:
-                                    localAssertionPassed = localLeftValue === localRightValue;
-                                    break;
-                                case TokenType.LESS_THAN:
-                                    localAssertionPassed = localLeftValue < localRightValue;
-                                    break;
-                                case TokenType.GREATER_THAN:
-                                    localAssertionPassed = localLeftValue > localRightValue;
-                                    break;
-                                case TokenType.LESS_EQUAL:
-                                    localAssertionPassed = localLeftValue <= localRightValue;
-                                    break;
-                                case TokenType.GREATER_EQUAL:
-                                    localAssertionPassed = localLeftValue >= localRightValue;
-                                    break;
-                                case TokenType.NOT_EQUAL:
-                                    localAssertionPassed = localLeftValue !== localRightValue;
-                                    break;
-                                default:
-                                    throw new Error(`Unsupported comparison operator in assertion: ${node.operator}`);
+                    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 < node.params.length; i++) {
+                                nestedScope[node.params[i]] = args[i];
                             }
-                            
-                            if (!localAssertionPassed) {
-                                const operatorSymbol = {
-                                    [TokenType.EQUALS]: '=',
-                                    [TokenType.LESS_THAN]: '<',
-                                    [TokenType.GREATER_THAN]: '>',
-                                    [TokenType.LESS_EQUAL]: '<=',
-                                    [TokenType.GREATER_EQUAL]: '>=',
-                                    [TokenType.NOT_EQUAL]: '!='
-                                }[node.operator];
-                                throw new Error(`Assertion failed: ${JSON.stringify(localLeftValue)} ${operatorSymbol} ${JSON.stringify(localRightValue)}`);
+                            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 < node.parameters.length; i++) {
+                                nestedScope[node.parameters[i]] = args[i];
                             }
-                            return localLeftValue;
-                        default:
-                            throw new Error(`Unknown node type: ${node.type}`);
+                            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);
                     }
-                };
-                    return localEvalNode(node.body);
-                };
-                return;
-            case 'FunctionCall':
-                if (globalScope[node.name] instanceof Function) {
-                    let args = node.args.map(evalNode);
-                    return globalScope[node.name](...args);
-                }
-                throw new Error(`Function ${node.name} is not defined`);
-            case 'CaseExpression':
-                debugLog('CaseExpression evaluation', { value: node.value, cases: node.cases });
-                // Evaluate the values being matched (e.g., [x, y])
-                const values = node.value.map(evalNode);
-                
-                for (const caseItem of node.cases) {
-                    // Evaluate the pattern (e.g., [0, 0] or [0, _])
-                    const pattern = caseItem.pattern.map(evalNode);
                     
-                    // Check if pattern matches values
-                    let matches = true;
-                    for (let i = 0; i < Math.max(values.length, pattern.length); i++) {
-                        const value = values[i];
-                        const patternValue = pattern[i];
-                        
-                        // Wildcard always matches
-                        if (patternValue === true) continue;
+                    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);
                         
-                        // Otherwise, values must be equal
-                        if (value !== patternValue) {
+                        // Check if patterns match the values
+                        let matches = true;
+                        if (whenValues.length !== patterns.length) {
                             matches = false;
-                            break;
+                        } else {
+                            for (let i = 0; i < whenValues.length; i++) {
+                                const value = whenValues[i];
+                                const pattern = patterns[i];
+                                
+                                if (pattern === true) { // Wildcard pattern
+                                    // Wildcard always matches
+                                    continue;
+                                } 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 readline = require('readline');
+                    const rl = readline.createInterface({
+                        input: process.stdin,
+                        output: process.stdout
+                    });
                     
-                    if (matches) {
-                        // Evaluate all results
-                        const results = caseItem.result.map(evalNode);
-                        // If there's only one result, return it directly (preserves number type)
-                        if (results.length === 1) {
-                            return results[0];
-                        }
-                        // Otherwise join multiple results as string
-                        return results.join(' ');
+                    return new Promise((resolve) => {
+                        rl.question('', (input) => {
+                            rl.close();
+                            const num = parseInt(input);
+                            resolve(isNaN(num) ? input : num);
+                        });
+                    });
+                case 'IOOutExpression':
+                    const localOutputValue = localEvalNode(node.value);
+                    console.log(localOutputValue);
+                    return localOutputValue;
+                case 'IOAssertExpression':
+                    const localAssertionValue = localEvalNode(node.value);
+                    if (!localAssertionValue) {
+                        throw new Error('Assertion failed');
                     }
-                }
-                throw new Error('No matching pattern found');
-            case 'WildcardPattern':
-                return true; // Wildcard always matches
-                                        case 'IOInExpression':
-                                // Read from stdin
-                                const readline = require('readline');
-                                const rl = readline.createInterface({
-                                    input: process.stdin,
-                                    output: process.stdout
-                                });
-                                
-                                return new Promise((resolve) => {
-                                    rl.question('', (input) => {
-                                        rl.close();
-                                        const num = parseInt(input);
-                                        resolve(isNaN(num) ? input : num);
-                                    });
-                                });
-            case 'IOOutExpression':
-                // Output to stdout
-                const outputValue = evalNode(node.value);
-                console.log(outputValue);
-                return outputValue;
-            case 'IOAssertExpression':
-                // Assert that left operator right is true
-                const leftValue = evalNode(node.left);
-                const rightValue = evalNode(node.right);
-                
-                let assertionPassed = false;
-                switch (node.operator) {
-                    case TokenType.EQUALS:
-                        assertionPassed = leftValue === rightValue;
-                        break;
-                    case TokenType.LESS_THAN:
-                        assertionPassed = leftValue < rightValue;
-                        break;
-                    case TokenType.GREATER_THAN:
-                        assertionPassed = leftValue > rightValue;
-                        break;
-                    case TokenType.LESS_EQUAL:
-                        assertionPassed = leftValue <= rightValue;
-                        break;
-                    case TokenType.GREATER_EQUAL:
-                        assertionPassed = leftValue >= rightValue;
-                        break;
-                    case TokenType.NOT_EQUAL:
-                        assertionPassed = leftValue !== rightValue;
-                        break;
-                    default:
-                        throw new Error(`Unsupported comparison operator in assertion: ${node.operator}`);
-                }
-                
-                if (!assertionPassed) {
-                    const operatorSymbol = {
-                        [TokenType.EQUALS]: '=',
-                        [TokenType.LESS_THAN]: '<',
-                        [TokenType.GREATER_THAN]: '>',
-                        [TokenType.LESS_EQUAL]: '<=',
-                        [TokenType.GREATER_EQUAL]: '>=',
-                        [TokenType.NOT_EQUAL]: '!='
-                    }[node.operator];
-                    throw new Error(`Assertion failed: ${JSON.stringify(leftValue)} ${operatorSymbol} ${JSON.stringify(rightValue)}`);
-                }
-                return leftValue;
-            default:
-                throw new Error(`Unknown node type: ${node.type}`);
+                    return localAssertionValue;
+                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) { // Skip undefined nodes
+        if (node) {
             lastResult = evalNode(node);
         }
     }
     
-    // Handle async results (from interactive IO operations)
     if (lastResult instanceof Promise) {
         return lastResult.then(result => {
             return result;
@@ -2005,413 +1447,295 @@ function interpreter(ast) {
     return lastResult;
 }
 
-// Debug configuration
-const DEBUG = process.env.DEBUG === 'true' || process.argv.includes('--debug');
-
+/**
+ * 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.
+ */
 function debugLog(message, data = null) {
-    if (DEBUG) {
+    if (process.env.DEBUG) {
+        console.log(`[DEBUG] ${message}`);
         if (data) {
-            console.log(`[DEBUG] ${message}:`, JSON.stringify(data, null, 2));
-        } else {
-            console.log(`[DEBUG] ${message}`);
+            console.log(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.
+ */
 function debugError(message, error = null) {
-    if (DEBUG) {
+    if (process.env.DEBUG) {
         console.error(`[DEBUG ERROR] ${message}`);
         if (error) {
-            console.error(`[DEBUG ERROR] Details:`, error);
+            console.error(error);
         }
     }
 }
 
-// Main execution logic
-const fs = require('fs');
-const readline = require('readline');
-
 /**
- * Execute a script file
+ * 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 complex
+ * nested expressions can lead to deep call stacks.
  */
-function executeFile(filePath) {
-    try {
-        debugLog(`Executing file: ${filePath}`);
-        
-        const input = fs.readFileSync(filePath, 'utf-8');
-        debugLog('Input content', input);
+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);
         
-        const tokens = lexer(input);
-        debugLog('Tokens generated', tokens);
+        // Track maximum depth
+        if (this.stack.length > this.maxDepth) {
+            this.maxDepth = this.stack.length;
+        }
         
-        const ast = parser(tokens);
-        debugLog('AST generated', ast);
+        // Count function calls
+        const key = `${functionName}${context ? `:${context}` : ''}`;
+        this.callCounts.set(key, (this.callCounts.get(key) || 0) + 1);
         
-        const result = interpreter(ast);
-        debugLog('Interpreter result', result);
+        // 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}`);
+        }
         
-        // Only print result if it's not undefined
-        if (result !== undefined) {
-            console.log(result);
+        if (process.env.DEBUG && this.stack.length % 100 === 0) {
+            console.log(`[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<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.
+ */
+async function readFile(filePath) {
+    // Check if we're in a browser environment
+    if (typeof window !== 'undefined') {
+        // Browser environment - would need to implement file input or fetch
+        throw new Error('File I/O not supported in browser environment');
+    }
+    
+    // Node.js or Bun environment
+    try {
+        // Try dynamic import for ES modules compatibility
+        const fs = await import('fs');
+        return fs.readFileSync(filePath, 'utf8');
     } catch (error) {
-        debugError('Execution failed', error);
-        console.error(`Error executing file: ${error.message}`);
-        process.exit(1);
+        // Fallback to require for older Node.js versions
+        const fs = require('fs');
+        return fs.readFileSync(filePath, 'utf8');
     }
 }
 
 /**
- * Start REPL mode
+ * Reads a file, tokenizes, parses, and interprets it.
+ * 
+ * @param {string} filePath - Path to the file to execute
+ * @returns {Promise<*>} 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.
  */
-function startREPL() {
-    console.log('Scripting Language REPL');
-    console.log('Type expressions to evaluate. End with semicolon (;) to execute.');
-    console.log('Type "exit" to quit.');
-    console.log('');
-    
-    const rl = readline.createInterface({
-        input: process.stdin,
-        output: process.stdout,
-        prompt: '> '
-    });
-    
-    // Create a persistent global scope for the REPL
-    let globalScope = {};
-    initializeStandardLibrary(globalScope);
-    let currentInput = '';
-    
-    rl.prompt();
-    
-    rl.on('line', (line) => {
-        const trimmedLine = line.trim();
-        
-        if (trimmedLine === 'exit' || trimmedLine === 'quit') {
-            rl.close();
-            return;
+async function executeFile(filePath) {
+    try {
+        // Validate file extension
+        if (!filePath.endsWith('.txt')) {
+            throw new Error('Only .txt files are supported');
         }
         
-        // Add the line to current input
-        currentInput += line + '\n';
+        const input = await readFile(filePath);
+        
+        debugLog('Input:', input);
+        
+        const tokens = lexer(input);
+        debugLog('Tokens:', tokens);
         
-        // Check if the input ends with a semicolon
-        if (currentInput.trim().endsWith(';')) {
-            try {
-                const tokens = lexer(currentInput);
-                const ast = parser(tokens);
-                
-                // Create a custom interpreter that uses the persistent scope
-                const result = interpreterWithScope(ast, globalScope);
-                
-                // Only print result if it's not undefined
-                if (result !== undefined) {
-                    console.log(result);
+        const ast = parser(tokens);
+        debugLog('AST:', JSON.stringify(ast, null, 2));
+        
+        const result = interpreter(ast);
+        
+        if (result instanceof Promise) {
+            result.then(finalResult => {
+                if (finalResult !== undefined) {
+                    console.log(finalResult);
                 }
-            } catch (error) {
-                console.error(`Error: ${error.message}`);
+                // Print call stack statistics after execution
+                const stats = callStackTracker.getStats();
+                console.log('\n=== CALL STACK STATISTICS ===');
+                console.log('Maximum call stack depth:', stats.maxDepth);
+                console.log('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
+            }).catch(error => {
+                console.error(`Error executing file: ${error.message}`);
+                // Print call stack statistics on error
+                const stats = callStackTracker.getStats();
+                console.error('\n=== CALL STACK STATISTICS ON ERROR ===');
+                console.error('Maximum call stack depth:', stats.maxDepth);
+                console.error('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
+                process.exit(1);
+            });
+        } else {
+            if (result !== undefined) {
+                console.log(result);
             }
-            
-            // Reset current input for next expression
-            currentInput = '';
+            // Print call stack statistics after execution
+            const stats = callStackTracker.getStats();
+            console.log('\n=== CALL STACK STATISTICS ===');
+            console.log('Maximum call stack depth:', stats.maxDepth);
+            console.log('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
         }
-        
-        rl.prompt();
-    });
-    
-    rl.on('close', () => {
-        console.log('Goodbye!');
-        process.exit(0);
-    });
+    } catch (error) {
+        console.error(`Error executing file: ${error.message}`);
+        // Print call stack statistics on error
+        const stats = callStackTracker.getStats();
+        console.error('\n=== CALL STACK STATISTICS ON ERROR ===');
+        console.error('Maximum call stack depth:', stats.maxDepth);
+        console.error('Function call counts:', JSON.stringify(stats.callCounts, null, 2));
+        process.exit(1);
+    }
 }
 
 /**
- * Interpreter that uses a provided global scope
+ * 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.
  */
-function interpreterWithScope(ast, globalScope) {
-    function evalNode(node) {
-        if (!node) {
-            return undefined;
-        }
-        switch (node.type) {
-            case 'NumberLiteral':
-                return parseInt(node.value);
-            case 'StringLiteral':
-                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 'AssignmentExpression':
-                if (globalScope.hasOwnProperty(node.name)) {
-                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                }
-                const value = evalNode(node.value);
-                globalScope[node.name] = value;
-                return;
-            case 'Identifier':
-                const identifierValue = globalScope[node.value];
-                if (identifierValue === undefined) {
-                    throw new Error(`Variable ${node.value} is not defined`);
-                }
-                return identifierValue;
-            case 'FunctionDeclaration':
-                if (globalScope.hasOwnProperty(node.name)) {
-                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                }
-                globalScope[node.name] = function(...args) {
-                    let localScope = Object.create(globalScope);
-                    for (let i = 0; i < node.params.length; i++) {
-                        localScope[node.params[i]] = args[i];
-                    }
-                    return localEvalNode(node.body);
-                };
-                return;
-            case 'FunctionCall':
-                if (globalScope[node.name] instanceof Function) {
-                    let args = node.args.map(evalNode);
-                    return globalScope[node.name](...args);
-                }
-                throw new Error(`Function ${node.name} is not defined`);
-            case 'CaseExpression':
-                const values = node.value.map(evalNode);
-                
-                for (const caseItem of node.cases) {
-                    const pattern = caseItem.pattern.map(evalNode);
-                    
-                    let matches = true;
-                    for (let i = 0; i < Math.max(values.length, pattern.length); i++) {
-                        const value = values[i];
-                        const patternValue = pattern[i];
-                        
-                        if (patternValue === true) continue;
-                        
-                        if (value !== patternValue) {
-                            matches = false;
-                            break;
-                        }
-                    }
-                    
-                    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 readline = require('readline');
-                const rl = readline.createInterface({
-                    input: process.stdin,
-                    output: process.stdout
-                });
-                
-                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);
-                console.log(outputValue);
-                return outputValue;
-            case 'FunctionReference':
-                const functionValue = globalScope[node.name];
-                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;
-
-            default:
-                throw new Error(`Unknown node type: ${node.type}`);
-        }
-    }
+async function main() {
+    const args = process.argv.slice(2);
 
-    const localEvalNode = (node) => {
-        if (!node) {
-            return undefined;
-        }
-        switch (node.type) {
-            case 'NumberLiteral':
-                return parseInt(node.value);
-            case 'StringLiteral':
-                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) < localEvalNode(node.right);
-            case 'GreaterThanExpression':
-                return localEvalNode(node.left) > localEvalNode(node.right);
-            case 'LessEqualExpression':
-                return localEvalNode(node.left) <= 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) && 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 && !rightVal) || (!leftVal && rightVal);
-            case 'NotExpression':
-                return !localEvalNode(node.operand);
-            case 'AssignmentExpression':
-                if (globalScope.hasOwnProperty(node.name)) {
-                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                }
-                globalScope[node.name] = localEvalNode(node.value);
-                return;
-            case 'Identifier':
-                const identifierValue = globalScope[node.value];
-                if (identifierValue === undefined && node.value) {
-                    return node.value;
-                }
-                return identifierValue;
-            case 'FunctionDeclaration':
-                if (globalScope.hasOwnProperty(node.name)) {
-                    throw new Error(`Cannot reassign immutable variable: ${node.name}`);
-                }
-                globalScope[node.name] = function(...args) {
-                    let nestedScope = Object.create(globalScope);
-                    for (let i = 0; i < node.params.length; i++) {
-                        nestedScope[node.params[i]] = args[i];
-                    }
-                    return localEvalNode(node.body);
-                };
-                return;
-            case 'FunctionCall':
-                if (globalScope[node.name] instanceof Function) {
-                    let args = node.args.map(localEvalNode);
-                    return globalScope[node.name](...args);
-                }
-                throw new Error(`Function ${node.name} is not defined`);
-            case 'CaseExpression':
-                const values = node.value.map(localEvalNode);
-                
-                for (const caseItem of node.cases) {
-                    const pattern = caseItem.pattern.map(localEvalNode);
-                    
-                    let matches = true;
-                    for (let i = 0; i < Math.max(values.length, pattern.length); i++) {
-                        const value = values[i];
-                        const patternValue = pattern[i];
-                        
-                        if (patternValue === true) continue;
-                        
-                        if (value !== patternValue) {
-                            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 readline = require('readline');
-                const rl = readline.createInterface({
-                    input: process.stdin,
-                    output: process.stdout
-                });
-                
-                return new Promise((resolve) => {
-                    rl.question('', (input) => {
-                        rl.close();
-                        const num = parseInt(input);
-                        resolve(isNaN(num) ? input : num);
-                    });
-                });
-            case 'IOOutExpression':
-                const localOutputValue = localEvalNode(node.value);
-                console.log(localOutputValue);
-                return localOutputValue;
-            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;
-            default:
-                throw new Error(`Unknown node type: ${node.type}`);
-        }
-    };
-
-    let lastResult;
-    for (let node of ast.body) {
-        if (node) {
-            lastResult = evalNode(node);
-        }
-    }
-    
-    if (lastResult instanceof Promise) {
-        return lastResult.then(result => {
-            return result;
-        });
+    if (args.length === 0) {
+        console.error('Usage: node lang.js <file>');
+        console.error('  Provide a file path to execute');
+        process.exit(1);
+    } else if (args.length === 1) {
+        // Execute the file
+        const filePath = args[0];
+        await executeFile(filePath);
+    } else {
+        // Too many arguments
+        console.error('Usage: node lang.js <file>');
+        console.error('  Provide exactly one file path to execute');
+        process.exit(1);
     }
-    
-    return lastResult;
 }
 
-// Check command line arguments
-const args = process.argv.slice(2);
-
-if (args.length === 0) {
-    // No arguments - start REPL mode
-    startREPL();
-} else if (args.length === 1) {
-    // One argument - execute the file
-    const filePath = args[0];
-    executeFile(filePath);
-} else {
-    // Too many arguments
-    console.error('Usage: node lang.js [file]');
-    console.error('  If no file is provided, starts REPL mode');
-    console.error('  If file is provided, executes the file');
+// Start the program
+main().catch(error => {
+    console.error('Fatal error:', error.message);
     process.exit(1);
-}
\ No newline at end of file
+});
\ No newline at end of file