about summary refs log tree commit diff stats
path: root/js/baba-yaga/src/core/interpreter.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/baba-yaga/src/core/interpreter.js')
-rw-r--r--js/baba-yaga/src/core/interpreter.js2812
1 files changed, 2812 insertions, 0 deletions
diff --git a/js/baba-yaga/src/core/interpreter.js b/js/baba-yaga/src/core/interpreter.js
new file mode 100644
index 0000000..5a2de80
--- /dev/null
+++ b/js/baba-yaga/src/core/interpreter.js
@@ -0,0 +1,2812 @@
+
+// interpreter.js
+
+import { tokenTypes } from './lexer.js';
+import { RuntimeError, TypeError, ErrorHelpers } from './error.js';
+import { createDefaultJSBridge } from './js-bridge.js';
+
+function createInterpreter(ast, host = {}) {
+  const scope = host.scope || new Map();
+  const types = new Map();
+
+  // Initialize global scope with io object
+  const hostIo = (host && host.io) ? host.io : {};
+  const hostOut = typeof hostIo.out === 'function' ? hostIo.out : (...xs) => console.log(...xs);
+  const hostIn = typeof hostIo.in === 'function' ? hostIo.in : () => '';
+  const hostDebug = typeof hostIo.debug === 'function' ? hostIo.debug : (...xs) => console.log('[DEBUG]', ...xs);
+  const hostAddListener = typeof hostIo.addListener === 'function' ? hostIo.addListener : () => () => {};
+  const hostDeliver = typeof hostIo.deliver === 'function' ? hostIo.deliver : () => {};
+  
+  // Initialize JavaScript bridge for interop
+  const jsBridge = createDefaultJSBridge(host.jsBridgeConfig || {});
+
+  // Helper functions for Result type creation
+  function createOkResult(value) {
+    return {
+      type: 'Result',
+      variant: 'Ok',
+      value: value
+    };
+  }
+
+  function createErrResult(message) {
+    return {
+      type: 'Result',
+      variant: 'Err',
+      value: String(message)
+    };
+  }
+
+  // Converters for IO boundaries
+  function toPlain(value) {
+    if (value && typeof value.value === 'number') return value.value;
+    if (Array.isArray(value)) return value.map(toPlain);
+    if (value && value.type === 'Object' && value.properties instanceof Map) {
+      const obj = {};
+      for (const [k, v] of value.properties.entries()) obj[k] = toPlain(v);
+      return obj;
+    }
+    return value;
+  }
+
+  function fromPlain(value) {
+    if (Array.isArray(value)) return value.map(fromPlain);
+    if (value && typeof value === 'object' && !(value.type === 'Object' && value.properties instanceof Map)) {
+      const mapped = {};
+      for (const [k, v] of Object.entries(value)) mapped[k] = fromPlain(v);
+      return createObjectFromPlain(mapped);
+    }
+    if (typeof value === 'number') return { value, isFloat: !Number.isInteger(value) };
+    return value;
+  }
+
+  scope.set('io', {
+    type: 'Object',
+    properties: new Map([
+      ['out', {
+        type: 'NativeFunction',
+        call: (args) => {
+          // Convert our custom number format to regular numbers for display
+          const displayArgs = args.map(arg => {
+            if (arg && typeof arg.value === 'number') {
+              return arg.value;
+            }
+            if (Array.isArray(arg)) {
+              return arg.map(item => {
+                if (item && typeof item.value === 'number') {
+                  return item.value;
+                }
+                return item;
+              });
+            }
+            return arg;
+          });
+          hostOut(...displayArgs);
+        },
+      }],
+      ['print', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length === 0) {
+            hostOut('');
+            return;
+          }
+          
+          // Enhanced formatting for different data types
+          const formatValue = (arg, options = {}) => {
+            const { gridMode = false, cellAlive = '█', cellDead = '·' } = options;
+            
+            // Handle numbers
+            if (arg && typeof arg.value === 'number') {
+              return arg.value;
+            }
+            
+            // Handle arrays (potential grids)
+            if (Array.isArray(arg)) {
+              // Check if this looks like a 2D grid (array of arrays with numbers)
+              const isGrid = arg.length > 0 && 
+                            Array.isArray(arg[0]) && 
+                            arg.every(row => Array.isArray(row) && 
+                              row.every(cell => typeof cell === 'number' || (cell && typeof cell.value === 'number')));
+              
+              if (isGrid && gridMode) {
+                // Format as a visual grid
+                return arg.map(row => 
+                  row.map(cell => {
+                    const value = (cell && typeof cell.value === 'number') ? cell.value : cell;
+                    return value === 1 ? cellAlive : cellDead;
+                  }).join('')
+                ).join('\n');
+              } else {
+                // Regular array formatting
+                return arg.map(item => {
+                  if (item && typeof item.value === 'number') {
+                    return item.value;
+                  }
+                  return item;
+                });
+              }
+            }
+            
+            // Handle functions
+            if (arg && (arg.type === 'NativeFunction' || arg.type === 'Function')) {
+              return '<function>';
+            }
+            
+            // Handle Result types
+            if (arg && arg.type === 'Result') {
+              return `${arg.variant}(${formatValue(arg.value, options)})`;
+            }
+            
+            // Handle Objects
+            if (arg && arg.type === 'Object' && arg.properties instanceof Map) {
+              const obj = Object.fromEntries(
+                Array.from(arg.properties.entries()).map(([k, v]) => [k, formatValue(v, options)])
+              );
+              return JSON.stringify(obj, null, 2);
+            }
+            
+            return String(arg);
+          };
+          
+          // Process arguments
+          if (args.length === 1) {
+            // Single argument - try to detect if it's a grid
+            const formatted = formatValue(args[0], { gridMode: true });
+            hostOut(formatted);
+          } else if (args.length === 2 && typeof args[0] === 'string') {
+            // Two arguments: format string and data
+            const format = args[0];
+            const data = args[1];
+            
+            if (format === 'grid') {
+              const formatted = formatValue(data, { gridMode: true });
+              hostOut(formatted);
+            } else if (format === 'grid-custom') {
+              // Expected: io.print "grid-custom" { data: grid, alive: "X", dead: " " }
+              if (data && data.type === 'Object' && data.properties instanceof Map) {
+                const gridData = data.properties.get('data');
+                const alive = data.properties.get('alive') || '█';
+                const dead = data.properties.get('dead') || '·';
+                const formatted = formatValue(gridData, { 
+                  gridMode: true, 
+                  cellAlive: alive, 
+                  cellDead: dead 
+                });
+                hostOut(formatted);
+              } else {
+                hostOut('Error: grid-custom format requires { data, alive, dead } object');
+              }
+            } else {
+              // Regular formatting with format string
+              const formatted = formatValue(data);
+              hostOut(`${format}: ${formatted}`);
+            }
+          } else {
+            // Multiple arguments - format each normally
+            const formatted = args.map(arg => formatValue(arg));
+            hostOut(...formatted);
+          }
+        },
+      }],
+      ['in', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 0) {
+            throw new Error('io.in expects no arguments');
+          }
+          const data = hostIn();
+          return typeof data === 'string' ? data : String(data ?? '');
+        },
+      }],
+      ['emit', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length < 1 || args.length > 2) {
+            throw new Error('io.emit expects 1 or 2 arguments: topic, [data]');
+          }
+          const topic = String(args[0]);
+          const data = args.length === 2 ? toPlain(args[1]) : undefined;
+          hostDeliver({ topic, data });
+        },
+      }],
+      ['listen', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 2) {
+            throw new Error('io.listen expects exactly 2 arguments: topic, handler');
+          }
+          const topic = String(args[0]);
+          const handler = args[1];
+          if (!handler || handler.type !== 'Function') {
+            throw new Error('io.listen handler must be a function');
+          }
+          // Wrap the language-level handler in a host-callable function
+          const unsubscribe = hostAddListener(topic, (event) => {
+            const ev = createObjectFromPlain({ topic: String(event && event.topic || topic), data: fromPlain(event && event.data) });
+            const callScope = new Map(handler.closure);
+            const paramName = typeof handler.params[0] === 'string' ? handler.params[0] : (handler.params[0] && handler.params[0].name);
+            if (paramName) callScope.set(paramName, ev);
+            const originalScope = new Map(scope);
+            scope.clear();
+            for (const [key, value] of callScope.entries()) {
+              scope.set(key, value);
+            }
+            try {
+              visit(handler.body);
+            } catch (e) {
+              // Surface handler errors via host deliver if available
+              try { hostDeliver({ topic: 'cmd.render', data: { error: e && e.message ? e.message : String(e) } }); } catch {}
+              throw e;
+            } finally {
+              scope.clear();
+              for (const [key, value] of originalScope.entries()) {
+                scope.set(key, value);
+              }
+            }
+          });
+          // Return nothing (Unit)
+          return undefined;
+        },
+      }],
+      
+      // JavaScript Interop Functions
+      ['callJS', {
+        type: 'NativeFunction',
+        signature: '(functionName: String, args: [Any]) -> Result',
+        call: (args) => {
+          if (args.length < 1 || args.length > 2) {
+            throw new Error('io.callJS expects 1 or 2 arguments: functionName, [args]');
+          }
+          
+          const functionName = String(args[0]);
+          let callArgs = [];
+          
+          if (args.length === 2) {
+            if (Array.isArray(args[1])) {
+              callArgs = args[1];
+            } else {
+              callArgs = [args[1]];
+            }
+          }
+          
+          // Convert Baba Yaga args to JS args
+          const jsArgs = callArgs.map(arg => jsBridge.convertBabaValueToJS(arg));
+          
+          const result = jsBridge.callFunction(functionName, jsArgs);
+          
+          if (result.type === 'success') {
+            // Store the raw JavaScript value with a special marker
+            const jsValue = {
+              type: 'JSValue',
+              value: result.value,
+              // Also include a converted version for display
+              converted: jsBridge.convertJSValueToBaba(result.value)
+            };
+            return createOkResult(jsValue);
+          } else {
+            return createErrResult(result.error);
+          }
+        },
+      }],
+      
+      ['callJSAsync', {
+        type: 'NativeFunction',
+        signature: '(functionName: String, args: [Any]) -> Result',
+        call: async (args) => {
+          if (args.length < 1 || args.length > 2) {
+            throw new Error('io.callJSAsync expects 1 or 2 arguments: functionName, [args]');
+          }
+          
+          const functionName = String(args[0]);
+          const callArgs = args.length === 2 ? (Array.isArray(args[1]) ? args[1] : [args[1]]) : [];
+          
+          // Convert Baba Yaga args to JS args
+          const jsArgs = callArgs.map(arg => jsBridge.convertBabaValueToJS(arg));
+          
+          const result = await jsBridge.callFunctionAsync(functionName, jsArgs);
+          
+          if (result.type === 'success') {
+            const babaValue = jsBridge.convertJSValueToBaba(result.value);
+            return createOkResult(babaValue);
+          } else {
+            return createErrResult(result.error);
+          }
+        },
+      }],
+      
+      ['getProperty', {
+        type: 'NativeFunction',
+        signature: '(obj: Any, propName: String) -> Result',
+        call: (args) => {
+          if (args.length !== 2) {
+            throw new Error('io.getProperty expects exactly 2 arguments: obj, propName');
+          }
+          
+          let obj;
+          // Check if this is a JSValue from io.callJS
+          if (args[0] && args[0].type === 'JSValue') {
+            obj = args[0].value; // Use the raw JavaScript value
+          } else {
+            obj = jsBridge.convertBabaValueToJS(args[0]);
+          }
+          
+          const propName = String(args[1]);
+          
+          const result = jsBridge.getProperty(obj, propName);
+          
+          if (result.type === 'success') {
+            const babaValue = jsBridge.convertJSValueToBaba(result.value);
+            return createOkResult(babaValue);
+          } else {
+            return createErrResult(result.error);
+          }
+        },
+      }],
+      
+      ['setProperty', {
+        type: 'NativeFunction',
+        signature: '(obj: Any, propName: String, value: Any) -> Result',
+        call: (args) => {
+          if (args.length !== 3) {
+            throw new Error('io.setProperty expects exactly 3 arguments: obj, propName, value');
+          }
+          
+          let obj;
+          // Check if this is a JSValue from io.callJS
+          if (args[0] && args[0].type === 'JSValue') {
+            obj = args[0].value; // Use the raw JavaScript value
+          } else {
+            obj = jsBridge.convertBabaValueToJS(args[0]);
+          }
+          
+          const propName = String(args[1]);
+          const value = jsBridge.convertBabaValueToJS(args[2]);
+          
+          const result = jsBridge.setProperty(obj, propName, value);
+          
+          if (result.type === 'success') {
+            const babaValue = jsBridge.convertJSValueToBaba(result.value);
+            return createOkResult(babaValue);
+          } else {
+            return createErrResult(result.error);
+          }
+        },
+      }],
+      
+      ['hasProperty', {
+        type: 'NativeFunction',
+        signature: '(obj: Any, propName: String) -> Bool',
+        call: (args) => {
+          if (args.length !== 2) {
+            throw new Error('io.hasProperty expects exactly 2 arguments: obj, propName');
+          }
+          
+          let obj;
+          // Check if this is a JSValue from io.callJS
+          if (args[0] && args[0].type === 'JSValue') {
+            obj = args[0].value; // Use the raw JavaScript value
+          } else {
+            obj = jsBridge.convertBabaValueToJS(args[0]);
+          }
+          
+          const propName = String(args[1]);
+          
+          return jsBridge.hasProperty(obj, propName);
+        },
+      }],
+      
+      ['jsArrayToList', {
+        type: 'NativeFunction',
+        signature: '(jsArray: Any) -> Result',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('io.jsArrayToList expects exactly 1 argument: jsArray');
+          }
+          
+          let jsArray;
+          // Check if this is a JSValue from io.callJS
+          if (args[0] && args[0].type === 'JSValue') {
+            jsArray = args[0].value; // Use the raw JavaScript value
+          } else {
+            jsArray = jsBridge.convertBabaValueToJS(args[0]);
+          }
+          
+          const result = jsBridge.jsArrayToList(jsArray);
+          
+          if (result.type === 'success') {
+            const babaList = result.value.map(item => jsBridge.convertJSValueToBaba(item));
+            return createOkResult(babaList);
+          } else {
+            return createErrResult(result.error);
+          }
+        },
+      }],
+      
+      ['listToJSArray', {
+        type: 'NativeFunction',
+        signature: '(list: [Any]) -> Any',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('io.listToJSArray expects exactly 1 argument: list');
+          }
+          
+          const babaList = args[0];
+          const result = jsBridge.listToJSArray(babaList);
+          
+          if (result.type === 'success') {
+            return result.value;
+          } else {
+            throw new Error(result.error);
+          }
+        },
+      }],
+      
+      ['objectToTable', {
+        type: 'NativeFunction',
+        signature: '(obj: Any) -> Result',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('io.objectToTable expects exactly 1 argument: obj');
+          }
+          
+          let jsObj;
+          // Check if this is a JSValue from io.callJS
+          if (args[0] && args[0].type === 'JSValue') {
+            jsObj = args[0].value; // Use the raw JavaScript value
+          } else {
+            jsObj = jsBridge.convertBabaValueToJS(args[0]);
+          }
+          
+          const result = jsBridge.objectToTable(jsObj);
+          
+          if (result.type === 'success') {
+            return createOkResult(result.value);
+          } else {
+            return createErrResult(result.error);
+          }
+        },
+      }],
+      
+      ['tableToObject', {
+        type: 'NativeFunction',
+        signature: '(table: Table) -> Any',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('io.tableToObject expects exactly 1 argument: table');
+          }
+          
+          const babaTable = args[0];
+          const result = jsBridge.tableToObject(babaTable);
+          
+          if (result.type === 'success') {
+            return result.value;
+          } else {
+            throw new Error(result.error);
+          }
+        },
+      }],
+      
+      ['getLastJSError', {
+        type: 'NativeFunction',
+        signature: '() -> Result',
+        call: (args) => {
+          if (args.length !== 0) {
+            throw new Error('io.getLastJSError expects no arguments');
+          }
+          
+          const error = jsBridge.getLastError();
+          if (error) {
+            return createOkResult(jsBridge.convertJSValueToBaba(error));
+          } else {
+            return createErrResult('No JavaScript error');
+          }
+        },
+      }],
+      
+      ['clearJSError', {
+        type: 'NativeFunction',
+        signature: '() -> Unit',
+        call: (args) => {
+          if (args.length !== 0) {
+            throw new Error('io.clearJSError expects no arguments');
+          }
+          
+          jsBridge.clearLastError();
+          return undefined;
+        },
+      }],
+    ]),
+  });
+
+  // Expose host-provided data binding (if present) as a global `data`
+  // Temporarily disabled to debug member access issue
+  // if (Object.prototype.hasOwnProperty.call(hostIo, 'data')) {
+  //   scope.set('data', fromPlain(hostIo.data));
+  // }
+
+  // Helper to create language number literal from JS number
+  function num(n) {
+    return { value: n, isFloat: false };
+  }
+
+  // Helper to create our object/table value with proxy access
+  function createObjectFromPlain(plain) {
+    const properties = new Map();
+    for (const [k, v] of Object.entries(plain)) {
+      properties.set(k, v);
+    }
+    const base = { type: 'Object', properties };
+    return new Proxy(base, {
+      get(target, prop, receiver) {
+        if (prop in target) return Reflect.get(target, prop, receiver);
+        if (typeof prop === 'string' && target.properties && target.properties.has(prop)) {
+          return target.properties.get(prop);
+        }
+        return undefined;
+      },
+    });
+  }
+
+  // shape: returns a metadata table describing the argument
+  scope.set('shape', {
+    type: 'NativeFunction',
+    call: (args) => {
+      if (args.length !== 1) {
+        throw new Error('shape expects exactly 1 argument');
+      }
+      const x = args[0];
+
+      // Numbers in this language may be wrapped {value,isFloat}
+      const isNumberWrapped = x && typeof x === 'object' && typeof x.value === 'number';
+      const isList = Array.isArray(x);
+      const isString = typeof x === 'string';
+      const isTable = x && typeof x === 'object' && x.type === 'Object' && x.properties instanceof Map;
+
+      if (isList) {
+        const n = x.length;
+        return createObjectFromPlain({
+          kind: 'List',
+          rank: num(1),
+          shape: [num(n)],
+          size: num(n),
+          isEmpty: n === 0,
+        });
+      }
+      if (isString) {
+        const n = x.length;
+        return createObjectFromPlain({
+          kind: 'String',
+          rank: num(1),
+          shape: [num(n)],
+          size: num(n),
+          isEmpty: n === 0,
+        });
+      }
+      if (isTable) {
+        const keys = Array.from(x.properties.keys());
+        const k = keys.length;
+        return createObjectFromPlain({
+          kind: 'Table',
+          rank: num(1),
+          shape: [num(k)],
+          size: num(k),
+          keys,
+          isEmpty: k === 0,
+        });
+      }
+      if (isNumberWrapped || typeof x === 'number' || typeof x === 'boolean') {
+        return createObjectFromPlain({
+          kind: 'Scalar',
+          rank: num(0),
+          shape: [],
+          size: num(1),
+          isEmpty: false,
+        });
+      }
+      // Fallback descriptor
+      return createObjectFromPlain({
+        kind: 'Unknown',
+        rank: num(0),
+        shape: [],
+        size: num(1),
+        isEmpty: false,
+      });
+    },
+  });
+
+  scope.set('map', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const func = args[0];
+      const list = args[1];
+      if (func.type !== 'Function') {
+        throw new Error('Map expects a function as the first argument.');
+      }
+      if (!Array.isArray(list)) {
+        throw new Error('Map expects a list as the second argument.');
+      }
+      return list.map(item => {
+        const callScope = new Map(func.closure);
+        callScope.set(func.params[0].name, item);
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        const result = visit(func.body);
+        scope.clear();
+        for (const [key, value] of originalScope.entries()) {
+          scope.set(key, value);
+        }
+        return result;
+      });
+    },
+  });
+
+  scope.set('filter', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const func = args[0];
+      const list = args[1];
+      if (func.type !== 'Function') {
+        throw new Error('Filter expects a function as the first argument.');
+      }
+      if (!Array.isArray(list)) {
+        throw new Error('Filter expects a list as the second argument.');
+      }
+      return list.filter(item => {
+        const callScope = new Map(func.closure);
+        callScope.set(func.params[0].name, item);
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        const result = visit(func.body);
+        scope.clear();
+        for (const [key, value] of originalScope.entries()) {
+          scope.set(key, value);
+        }
+        return result;
+      });
+    },
+  });
+
+  scope.set('length', {
+    type: 'NativeFunction',
+    call: (args) => {
+      if (args.length !== 1) {
+        throw new Error('length expects exactly 1 argument');
+      }
+      const arg = args[0];
+      if (Array.isArray(arg)) {
+        return { value: arg.length, isFloat: false };
+      } else if (typeof arg === 'string') {
+        return { value: arg.length, isFloat: false };
+      } else {
+        throw new Error('length expects a list or string as argument');
+      }
+    },
+  });
+
+  scope.set('reduce', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const func = args[0];
+      let accumulator = args[1];
+      const list = args[2];
+      if (func.type !== 'Function') {
+        throw new Error('Reduce expects a function as the first argument.');
+      }
+      if (!Array.isArray(list)) {
+        throw new Error('Reduce expects a list as the third argument.');
+      }
+      list.forEach(item => {
+        const callScope = new Map(func.closure);
+        callScope.set(func.params[0].name, accumulator);
+        callScope.set(func.params[1].name, item);
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        accumulator = visit(func.body);
+        scope.clear();
+        for (const [key, value] of originalScope.entries()) {
+          scope.set(key, value);
+        }
+      });
+      return accumulator;
+    },
+  });
+
+  // Scan operations (cumulative operations)
+  scope.set('scan', {
+    type: 'NativeFunction',
+    signature: '(func: (T, T) -> T, init: T, list: [T]) -> [T]',
+    call: (args) => {
+      const func = args[0];
+      let accumulator = args[1];
+      const list = args[2];
+      if (func.type !== 'Function') {
+        throw new Error('Scan expects a function as the first argument.');
+      }
+      if (!Array.isArray(list)) {
+        throw new Error('Scan expects a list as the third argument.');
+      }
+      
+      const result = [accumulator]; // Start with initial value
+      
+      list.forEach(item => {
+        const paramName1 = typeof func.params[0] === 'string' ? func.params[0] : func.params[0].name;
+        const paramName2 = typeof func.params[1] === 'string' ? func.params[1] : func.params[1].name;
+        
+        const callScope = new Map(func.closure);
+        callScope.set(paramName1, accumulator);
+        callScope.set(paramName2, item);
+        
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        
+        accumulator = visit(func.body);
+        result.push(accumulator);
+        
+        scope.clear();
+        for (const [key, value] of originalScope.entries()) {
+          scope.set(key, value);
+        }
+      });
+      
+      return result;
+    },
+  });
+
+  // Cumulative sum utility
+  scope.set('cumsum', {
+    type: 'NativeFunction',
+    signature: '(list: [Number]) -> [Number]',
+    call: (args) => {
+      const list = args[0];
+      if (!Array.isArray(list)) {
+        throw new Error('cumsum expects a list as the first argument.');
+      }
+      
+      // Create an add function
+      const addFunc = {
+        type: 'Function',
+        params: ['acc', 'x'],
+        body: { 
+          type: 'BinaryExpression', 
+          operator: '+', 
+          left: { type: 'Identifier', name: 'acc' },
+          right: { type: 'Identifier', name: 'x' }
+        },
+        closure: new Map()
+      };
+      
+      const scanFunc = scope.get('scan');
+      return scanFunc.call([addFunc, { value: 0, isFloat: false }, list]);
+    },
+  });
+
+  // Cumulative product utility
+  scope.set('cumprod', {
+    type: 'NativeFunction',
+    signature: '(list: [Number]) -> [Number]',
+    call: (args) => {
+      const list = args[0];
+      if (!Array.isArray(list)) {
+        throw new Error('cumprod expects a list as the first argument.');
+      }
+      
+      // Create a multiply function
+      const mulFunc = {
+        type: 'Function',
+        params: ['acc', 'x'],
+        body: { 
+          type: 'BinaryExpression', 
+          operator: '*', 
+          left: { type: 'Identifier', name: 'acc' },
+          right: { type: 'Identifier', name: 'x' }
+        },
+        closure: new Map()
+      };
+      
+      const scanFunc = scope.get('scan');
+      return scanFunc.call([mulFunc, { value: 1, isFloat: false }, list]);
+    },
+  });
+
+  // List operations - all immutable
+  scope.set('append', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const list = args[0];
+      const element = args[1];
+      if (!Array.isArray(list)) {
+        throw new Error('Append expects a list as the first argument.');
+      }
+      return [...list, element];
+    },
+  });
+
+  scope.set('prepend', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const element = args[0];
+      const list = args[1];
+      if (!Array.isArray(list)) {
+        throw new Error('Prepend expects a list as the second argument.');
+      }
+      return [element, ...list];
+    },
+  });
+
+  scope.set('concat', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const list1 = args[0];
+      const list2 = args[1];
+      if (!Array.isArray(list1)) {
+        throw new Error('Concat expects a list as the first argument.');
+      }
+      if (!Array.isArray(list2)) {
+        throw new Error('Concat expects a list as the second argument.');
+      }
+      return [...list1, ...list2];
+    },
+  });
+
+  scope.set('update', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const list = args[0];
+      const index = args[1];
+      const value = args[2];
+      if (!Array.isArray(list)) {
+        throw new Error('Update expects a list as the first argument.');
+      }
+      // Handle our custom number format for index
+      const indexValue = index && typeof index.value === 'number' ? index.value : index;
+      if (typeof indexValue !== 'number' || indexValue < 0 || indexValue >= list.length) {
+        throw new RuntimeError(
+          `Index out of bounds: ${indexValue}`,
+          null,
+          host.source || '',
+          [`Valid indices are 0 to ${list.length - 1}`, 'Check list length before accessing elements']
+        );
+      }
+      const newList = [...list];
+      newList[indexValue] = value;
+      return newList;
+    },
+  });
+
+  scope.set('removeAt', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const list = args[0];
+      const index = args[1];
+      if (!Array.isArray(list)) {
+        throw new Error('RemoveAt expects a list as the first argument.');
+      }
+      // Handle our custom number format for index
+      const indexValue = index && typeof index.value === 'number' ? index.value : index;
+      if (typeof indexValue !== 'number' || indexValue < 0 || indexValue >= list.length) {
+        throw new RuntimeError(
+          `Index out of bounds: ${indexValue}`,
+          null,
+          host.source || '',
+          [`Valid indices are 0 to ${list.length - 1}`, 'Check list length before accessing elements']
+        );
+      }
+      return list.filter((_, i) => i !== indexValue);
+    },
+  });
+
+  scope.set('slice', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const list = args[0];
+      const start = args[1];
+      const end = args.length === 3 ? args[2] : list.length;
+      if (!Array.isArray(list)) {
+        throw new Error('Slice expects a list as the first argument.');
+      }
+      // Handle our custom number format for indices
+      const startValue = start && typeof start.value === 'number' ? start.value : start;
+      const endValue = end && typeof end.value === 'number' ? end.value : end;
+      if (typeof startValue !== 'number' || startValue < 0) {
+        throw new Error(`Invalid start index: ${startValue}`);
+      }
+      if (typeof endValue !== 'number' || endValue < startValue || endValue > list.length) {
+        throw new Error(`Invalid end index: ${endValue}`);
+      }
+      return list.slice(startValue, endValue);
+    },
+  });
+
+  // Monadic operations
+  scope.set('flatMap', {
+    type: 'NativeFunction',
+    signature: '(func: (T) -> [U], list: [T]) -> [U]',
+    call: (args) => {
+      const func = args[0];
+      const list = args[1];
+      if (func.type !== 'Function') {
+        throw new Error('flatMap expects a function as the first argument.');
+      }
+      if (!Array.isArray(list)) {
+        throw new Error('flatMap expects a list as the second argument.');
+      }
+      
+      const result = [];
+      list.forEach(item => {
+        const paramName = typeof func.params[0] === 'string' ? func.params[0] : func.params[0].name;
+        const callScope = new Map(func.closure);
+        callScope.set(paramName, item);
+        
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        
+        const mapped = visit(func.body);
+        
+        scope.clear();
+        for (const [key, value] of originalScope.entries()) {
+          scope.set(key, value);
+        }
+        
+        if (Array.isArray(mapped)) {
+          result.push(...mapped);
+        } else {
+          result.push(mapped);
+        }
+      });
+      
+      return result;
+    },
+  });
+
+  // Array broadcasting operations (APL/K inspired)
+  scope.set('broadcast', {
+    type: 'NativeFunction',
+    signature: '(op: (T, U) -> V, scalar: T, array: [U]) -> [V]',
+    call: (args) => {
+      const op = args[0];
+      const scalar = args[1];
+      const array = args[2];
+      
+      if (op.type !== 'Function') {
+        throw new Error('broadcast expects a function as the first argument.');
+      }
+      if (!Array.isArray(array)) {
+        throw new Error('broadcast expects an array as the third argument.');
+      }
+      
+      return array.map(item => {
+        const param1Name = typeof op.params[0] === 'string' ? op.params[0] : op.params[0].name;
+        const param2Name = typeof op.params[1] === 'string' ? op.params[1] : op.params[1].name;
+        
+        const callScope = new Map(op.closure);
+        callScope.set(param1Name, scalar);
+        callScope.set(param2Name, item);
+        
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        
+        try {
+          return visit(op.body);
+        } finally {
+          scope.clear();
+          for (const [key, value] of originalScope.entries()) {
+            scope.set(key, value);
+          }
+        }
+      });
+    },
+  });
+
+  scope.set('zipWith', {
+    type: 'NativeFunction',
+    signature: '(op: (T, U) -> V, array1: [T], array2: [U]) -> [V]',
+    call: (args) => {
+      const op = args[0];
+      const array1 = args[1];
+      const array2 = args[2];
+      
+      if (op.type !== 'Function') {
+        throw new Error('zipWith expects a function as the first argument.');
+      }
+      if (!Array.isArray(array1)) {
+        throw new Error('zipWith expects an array as the second argument.');
+      }
+      if (!Array.isArray(array2)) {
+        throw new Error('zipWith expects an array as the third argument.');
+      }
+      
+      const minLength = Math.min(array1.length, array2.length);
+      const result = [];
+      
+      for (let i = 0; i < minLength; i++) {
+        const param1Name = typeof op.params[0] === 'string' ? op.params[0] : op.params[0].name;
+        const param2Name = typeof op.params[1] === 'string' ? op.params[1] : op.params[1].name;
+        
+        const callScope = new Map(op.closure);
+        callScope.set(param1Name, array1[i]);
+        callScope.set(param2Name, array2[i]);
+        
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        
+        try {
+          result.push(visit(op.body));
+        } finally {
+          scope.clear();
+          for (const [key, value] of originalScope.entries()) {
+            scope.set(key, value);
+          }
+        }
+      }
+      
+      return result;
+    },
+  });
+
+  scope.set('reshape', {
+    type: 'NativeFunction',
+    signature: '(shape: [Int], array: [T]) -> [[T]]',
+    call: (args) => {
+      const shape = args[0];
+      const array = args[1];
+      
+      if (!Array.isArray(shape)) {
+        throw new Error('reshape expects an array of dimensions as the first argument.');
+      }
+      if (!Array.isArray(array)) {
+        throw new Error('reshape expects an array as the second argument.');
+      }
+      
+      // For now, support only 2D reshape (matrix)
+      if (shape.length !== 2) {
+        throw new Error('reshape currently supports only 2D reshaping.');
+      }
+      
+      const rows = shape[0] && typeof shape[0].value === 'number' ? shape[0].value : shape[0];
+      const cols = shape[1] && typeof shape[1].value === 'number' ? shape[1].value : shape[1];
+      
+      if (rows * cols !== array.length) {
+        throw new Error(`Cannot reshape array of length ${array.length} into ${rows}x${cols} matrix.`);
+      }
+      
+      const result = [];
+      for (let i = 0; i < rows; i++) {
+        const row = [];
+        for (let j = 0; j < cols; j++) {
+          row.push(array[i * cols + j]);
+        }
+        result.push(row);
+      }
+      
+      return result;
+    },
+  });
+
+  // Advanced array indexing operations
+  scope.set('at', {
+    type: 'NativeFunction',
+    signature: '(indices: [Int], array: [T]) -> [T]',
+    call: (args) => {
+      const indices = args[0];
+      const array = args[1];
+      if (!Array.isArray(indices)) {
+        throw new Error('at expects an array of indices as the first argument.');
+      }
+      if (!Array.isArray(array)) {
+        throw new Error('at expects an array as the second argument.');
+      }
+      
+      return indices.map(index => {
+        const indexValue = index && typeof index.value === 'number' ? index.value : index;
+        if (typeof indexValue !== 'number' || indexValue < 0 || indexValue >= array.length) {
+          throw new RuntimeError(
+          `Index out of bounds: ${indexValue}`,
+          null,
+          host.source || '',
+          [`Valid indices are 0 to ${list.length - 1}`, 'Check list length before accessing elements']
+        );
+        }
+        return array[indexValue];
+      });
+    },
+  });
+
+  scope.set('where', {
+    type: 'NativeFunction',
+    signature: '(predicate: (T) -> Bool, array: [T]) -> [Int]',
+    call: (args) => {
+      const predicate = args[0];
+      const array = args[1];
+      if (predicate.type !== 'Function') {
+        throw new Error('where expects a function as the first argument.');
+      }
+      if (!Array.isArray(array)) {
+        throw new Error('where expects an array as the second argument.');
+      }
+      
+      const result = [];
+      array.forEach((item, index) => {
+        const paramName = typeof predicate.params[0] === 'string' ? predicate.params[0] : predicate.params[0].name;
+        const callScope = new Map(predicate.closure);
+        callScope.set(paramName, item);
+        
+        const originalScope = new Map(scope);
+        scope.clear();
+        for (const [key, value] of callScope.entries()) {
+          scope.set(key, value);
+        }
+        
+        const matches = visit(predicate.body);
+        
+        scope.clear();
+        for (const [key, value] of originalScope.entries()) {
+          scope.set(key, value);
+        }
+        
+        if (matches) {
+          result.push({ value: index, isFloat: false });
+        }
+      });
+      
+      return result;
+    },
+  });
+
+  scope.set('take', {
+    type: 'NativeFunction',
+    signature: '(n: Int, array: [T]) -> [T]',
+    call: (args) => {
+      const n = args[0];
+      const array = args[1];
+      if (!Array.isArray(array)) {
+        throw new Error('take expects an array as the second argument.');
+      }
+      
+      const nValue = n && typeof n.value === 'number' ? n.value : n;
+      if (typeof nValue !== 'number' || nValue < 0) {
+        throw new Error(`take expects a non-negative number, got: ${nValue}`);
+      }
+      
+      return array.slice(0, nValue);
+    },
+  });
+
+  scope.set('drop', {
+    type: 'NativeFunction',
+    signature: '(n: Int, array: [T]) -> [T]',
+    call: (args) => {
+      const n = args[0];
+      const array = args[1];
+      if (!Array.isArray(array)) {
+        throw new Error('drop expects an array as the second argument.');
+      }
+      
+      const nValue = n && typeof n.value === 'number' ? n.value : n;
+      if (typeof nValue !== 'number' || nValue < 0) {
+        throw new Error(`drop expects a non-negative number, got: ${nValue}`);
+      }
+      
+      return array.slice(nValue);
+    },
+  });
+
+  // Table operations - all immutable
+  scope.set('set', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const table = args[0];
+      const key = args[1];
+      const value = args[2];
+      if (table.type !== 'Object' || !table.properties) {
+        throw new Error('Set expects a table as the first argument.');
+      }
+      const newProperties = new Map(table.properties);
+      newProperties.set(key, value);
+      return { type: 'Object', properties: newProperties };
+    },
+  });
+
+  scope.set('remove', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const table = args[0];
+      const key = args[1];
+      if (table.type !== 'Object' || !table.properties) {
+        throw new Error('Remove expects a table as the first argument.');
+      }
+      const newProperties = new Map(table.properties);
+      newProperties.delete(key);
+      return { type: 'Object', properties: newProperties };
+    },
+  });
+
+  scope.set('merge', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const table1 = args[0];
+      const table2 = args[1];
+      if (table1.type !== 'Object' || !table1.properties) {
+        throw new Error('Merge expects a table as the first argument.');
+      }
+      if (table2.type !== 'Object' || !table2.properties) {
+        throw new Error('Merge expects a table as the second argument.');
+      }
+      const newProperties = new Map(table1.properties);
+      for (const [key, value] of table2.properties.entries()) {
+        newProperties.set(key, value);
+      }
+      return { type: 'Object', properties: newProperties };
+    },
+  });
+
+  scope.set('keys', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const table = args[0];
+      if (table.type !== 'Object' || !table.properties) {
+        throw new Error('Keys expects a table as the first argument.');
+      }
+      return Array.from(table.properties.keys());
+    },
+  });
+
+  scope.set('values', {
+    type: 'NativeFunction',
+    call: (args) => {
+      const table = args[0];
+      if (table.type !== 'Object' || !table.properties) {
+        throw new Error('Values expects a table as the first argument.');
+      }
+      return Array.from(table.properties.values());
+    },
+  });
+
+  // String functions
+  scope.set('str', {
+    type: 'Object',
+    properties: new Map([
+      ['concat', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length < 2) {
+            throw new Error('str.concat expects at least 2 arguments');
+          }
+          return args.map(arg => String(arg)).join('');
+        },
+      }],
+      ['split', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 2) {
+            throw new Error('str.split expects exactly 2 arguments: string and delimiter');
+          }
+          const str = String(args[0]);
+          const delimiter = String(args[1]);
+          return str.split(delimiter);
+        },
+      }],
+      ['join', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 2) {
+            throw new Error('str.join expects exactly 2 arguments: array and delimiter');
+          }
+          if (!Array.isArray(args[0])) {
+            throw new Error('str.join expects an array as the first argument');
+          }
+          const array = args[0];
+          const delimiter = String(args[1]);
+          return array.map(item => String(item)).join(delimiter);
+        },
+      }],
+      ['length', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('str.length expects exactly 1 argument');
+          }
+          return { value: String(args[0]).length, isFloat: false };
+        },
+      }],
+      ['substring', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length < 2 || args.length > 3) {
+            throw new Error('str.substring expects 2 or 3 arguments: string, start, [end]');
+          }
+          const str = String(args[0]);
+          // Handle our custom number format for start and end
+          const start = args[1] && typeof args[1].value === 'number' ? args[1].value : Number(args[1]);
+          const end = args.length === 3 ? (args[2] && typeof args[2].value === 'number' ? args[2].value : Number(args[2])) : undefined;
+          return str.substring(start, end);
+        },
+      }],
+      ['replace', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 3) {
+            throw new Error('str.replace expects exactly 3 arguments: string, search, replace');
+          }
+          const str = String(args[0]);
+          const search = String(args[1]);
+          const replace = String(args[2]);
+          return str.replace(new RegExp(search, 'g'), replace);
+        },
+      }],
+      ['trim', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('str.trim expects exactly 1 argument');
+          }
+          return String(args[0]).trim();
+        },
+      }],
+      ['upper', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('str.upper expects exactly 1 argument');
+          }
+          return String(args[0]).toUpperCase();
+        },
+      }],
+      ['lower', {
+        type: 'NativeFunction',
+        call: (args) => {
+          if (args.length !== 1) {
+            throw new Error('str.lower expects exactly 1 argument');
+          }
+          return String(args[0]).toLowerCase();
+        },
+      }],
+    ]),
+  });
+
+  // math namespace
+  scope.set('math', {
+    type: 'Object',
+    properties: new Map([
+      ['abs', { type: 'NativeFunction', call: ([x]) => ({ value: Math.abs((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['sign', { type: 'NativeFunction', call: ([x]) => ({ value: Math.sign((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['floor', { type: 'NativeFunction', call: ([x]) => ({ value: Math.floor((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['ceil', { type: 'NativeFunction', call: ([x]) => ({ value: Math.ceil((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['round', { type: 'NativeFunction', call: ([x]) => ({ value: Math.round((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['trunc', { type: 'NativeFunction', call: ([x]) => ({ value: Math.trunc((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['min', { type: 'NativeFunction', call: ([a, b]) => ({ value: Math.min((a && typeof a.value === 'number') ? a.value : Number(a), (b && typeof b.value === 'number') ? b.value : Number(b)), isFloat: true }) }],
+      ['max', { type: 'NativeFunction', call: ([a, b]) => ({ value: Math.max((a && typeof a.value === 'number') ? a.value : Number(a), (b && typeof b.value === 'number') ? b.value : Number(b)), isFloat: true }) }],
+      ['clamp', { type: 'NativeFunction', call: ([x, lo, hi]) => {
+        const xv = (x && typeof x.value === 'number') ? x.value : Number(x);
+        const lov = (lo && typeof lo.value === 'number') ? lo.value : Number(lo);
+        const hiv = (hi && typeof hi.value === 'number') ? hi.value : Number(hi);
+        return { value: Math.min(Math.max(xv, lov), hiv), isFloat: true };
+      }}],
+      ['pow', { type: 'NativeFunction', call: ([x, y]) => ({ value: Math.pow((x && typeof x.value === 'number') ? x.value : Number(x), (y && typeof y.value === 'number') ? y.value : Number(y)), isFloat: true }) }],
+      ['sqrt', { type: 'NativeFunction', call: ([x]) => { const v = (x && typeof x.value === 'number') ? x.value : Number(x); if (v < 0) throw new Error('Domain error: sqrt expects x >= 0'); return { value: Math.sqrt(v), isFloat: true }; } }],
+      ['exp', { type: 'NativeFunction', call: ([x]) => ({ value: Math.exp((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['log', { type: 'NativeFunction', call: ([x]) => { const v = (x && typeof x.value === 'number') ? x.value : Number(x); if (v <= 0) throw new Error('Domain error: log expects x > 0'); return { value: Math.log(v), isFloat: true }; } }],
+      ['sin', { type: 'NativeFunction', call: ([x]) => ({ value: Math.sin((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['cos', { type: 'NativeFunction', call: ([x]) => ({ value: Math.cos((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['tan', { type: 'NativeFunction', call: ([x]) => ({ value: Math.tan((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['asin', { type: 'NativeFunction', call: ([x]) => ({ value: Math.asin((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['acos', { type: 'NativeFunction', call: ([x]) => ({ value: Math.acos((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['atan', { type: 'NativeFunction', call: ([x]) => ({ value: Math.atan((x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['atan2', { type: 'NativeFunction', call: ([y, x]) => ({ value: Math.atan2((y && typeof y.value === 'number') ? y.value : Number(y), (x && typeof x.value === 'number') ? x.value : Number(x)), isFloat: true }) }],
+      ['deg', { type: 'NativeFunction', call: ([r]) => ({ value: ((r && typeof r.value === 'number') ? r.value : Number(r)) * (180 / Math.PI), isFloat: true }) }],
+      ['rad', { type: 'NativeFunction', call: ([d]) => ({ value: ((d && typeof d.value === 'number') ? d.value : Number(d)) * (Math.PI / 180), isFloat: true }) }],
+      ['random', { type: 'NativeFunction', call: () => ({ value: Math.random(), isFloat: true }) }],
+      ['randomInt', { type: 'NativeFunction', call: ([lo, hi]) => { const a = ~~((lo && typeof lo.value === 'number') ? lo.value : Number(lo)); const b = ~~((hi && typeof hi.value === 'number') ? hi.value : Number(hi)); if (a > b) throw new Error('Invalid range: lo > hi'); const n = a + Math.floor(Math.random() * (b - a + 1)); return { value: n, isFloat: false }; } }],
+    ])
+  });
+
+  // validate namespace - (value: any) -> Bool
+  scope.set('validate', {
+    type: 'Object',
+    properties: new Map([
+      ['notEmpty', { 
+        type: 'NativeFunction', 
+        signature: '(value: any) -> Bool',
+        call: ([value]) => {
+          if (value === null || value === undefined) return false;
+          if (typeof value === 'string') return value.length > 0;
+          if (Array.isArray(value)) return value.length > 0;
+          if (value && typeof value === 'object' && value.properties instanceof Map) return value.properties.size > 0;
+          return true;
+        }
+      }],
+      ['range', { 
+        type: 'NativeFunction', 
+        signature: '(min: Number, max: Number, value: Number) -> Bool',
+        call: ([min, max, value]) => {
+          const minVal = (min && typeof min.value === 'number') ? min.value : Number(min);
+          const maxVal = (max && typeof max.value === 'number') ? max.value : Number(max);
+          const val = (value && typeof value.value === 'number') ? value.value : Number(value);
+          return val >= minVal && val <= maxVal;
+        }
+      }],
+      ['email', { 
+        type: 'NativeFunction', 
+        signature: '(email: String) -> Bool',
+        call: ([email]) => {
+          const emailStr = String(email);
+          const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+          return emailRegex.test(emailStr);
+        }
+      }],
+      ['type', { 
+        type: 'NativeFunction', 
+        signature: '(expectedType: String, value: any) -> Bool',
+        call: ([expectedType, value]) => {
+          const expected = String(expectedType);
+          const actual = getRuntimeType(value);
+          return isTypeAssignable(actual, expected);
+        }
+      }],
+    ])
+  });
+
+  // sort namespace
+  scope.set('sort', {
+    type: 'Object',
+    properties: new Map([
+      ['by', { 
+        type: 'NativeFunction', 
+        signature: '(list: [T], keyFunc: (T) -> U) -> [T]',
+        call: ([list, keyFunc]) => {
+          if (!Array.isArray(list)) {
+            throw new Error('sort.by expects an array as the first argument');
+          }
+          if (!keyFunc || keyFunc.type !== 'Function') {
+            throw new Error('sort.by expects a function as the second argument');
+          }
+          
+          return [...list].sort((a, b) => {
+            // Helper to call a function with one argument
+            const callFunction = (func, arg) => {
+              const paramName = typeof func.params[0] === 'string' ? func.params[0] : func.params[0].name;
+              const callScope = new Map(func.closure);
+              callScope.set(paramName, arg);
+              
+              // Save current scope
+              const originalScope = new Map(scope);
+              scope.clear();
+              for (const [k, v] of callScope.entries()) scope.set(k, v);
+              
+              try {
+                return visit(func.body);
+              } finally {
+                // Restore original scope
+                scope.clear();
+                for (const [k, v] of originalScope.entries()) scope.set(k, v);
+              }
+            };
+            
+            const keyA = callFunction(keyFunc, a);
+            const keyB = callFunction(keyFunc, b);
+            
+            // Handle numeric comparison
+            const numA = (keyA && typeof keyA.value === 'number') ? keyA.value : Number(keyA);
+            const numB = (keyB && typeof keyB.value === 'number') ? keyB.value : Number(keyB);
+            if (!isNaN(numA) && !isNaN(numB)) {
+              return numA - numB;
+            }
+            
+            // Handle string comparison
+            const strA = String(keyA);
+            const strB = String(keyB);
+            return strA.localeCompare(strB);
+          });
+        }
+      }],
+    ])
+  });
+
+  // group namespace
+  scope.set('group', {
+    type: 'Object',
+    properties: new Map([
+      ['by', { 
+        type: 'NativeFunction', 
+        signature: '(list: [T], keyFunc: (T) -> U) -> Table',
+        call: ([list, keyFunc]) => {
+          if (!Array.isArray(list)) {
+            throw new Error('group.by expects an array as the first argument');
+          }
+          if (!keyFunc || keyFunc.type !== 'Function') {
+            throw new Error('group.by expects a function as the second argument');
+          }
+          
+          const groups = new Map();
+          
+          // Helper to call a function with one argument
+          const callFunction = (func, arg) => {
+            const paramName = typeof func.params[0] === 'string' ? func.params[0] : func.params[0].name;
+            const callScope = new Map(func.closure);
+            callScope.set(paramName, arg);
+            
+            // Save current scope
+            const originalScope = new Map(scope);
+            scope.clear();
+            for (const [k, v] of callScope.entries()) scope.set(k, v);
+            
+            try {
+              return visit(func.body);
+            } finally {
+              // Restore original scope
+              scope.clear();
+              for (const [k, v] of originalScope.entries()) scope.set(k, v);
+            }
+          };
+          
+          for (const item of list) {
+            const key = callFunction(keyFunc, item);
+            const keyStr = String(key);
+            
+            if (!groups.has(keyStr)) {
+              groups.set(keyStr, []);
+            }
+            groups.get(keyStr).push(item);
+          }
+          
+          return {
+            type: 'Object',
+            properties: groups
+          };
+        }
+      }],
+    ])
+  });
+
+  // text namespace - enhanced string processing
+  scope.set('text', {
+    type: 'Object',
+    properties: new Map([
+      ['lines', { 
+        type: 'NativeFunction', 
+        signature: '(text: String) -> [String]',
+        call: ([text]) => {
+          const str = String(text);
+          return str.split(/\r?\n/);
+        }
+      }],
+      ['words', { 
+        type: 'NativeFunction', 
+        signature: '(text: String) -> [String]',
+        call: ([text]) => {
+          const str = String(text);
+          return str.trim().split(/\s+/).filter(word => word.length > 0);
+        }
+      }],
+      ['padLeft', { 
+        type: 'NativeFunction', 
+        signature: '(width: Int, text: String) -> String',
+        call: ([width, text]) => {
+          const w = (width && typeof width.value === 'number') ? width.value : Number(width);
+          const str = String(text);
+          return str.padStart(w, ' ');
+        }
+      }],
+      ['padRight', { 
+        type: 'NativeFunction', 
+        signature: '(width: Int, text: String) -> String',
+        call: ([width, text]) => {
+          const w = (width && typeof width.value === 'number') ? width.value : Number(width);
+          const str = String(text);
+          return str.padEnd(w, ' ');
+        }
+      }],
+    ])
+  });
+
+  // debug namespace - enhanced debugging tools
+  scope.set('debug', {
+    type: 'Object',
+    properties: new Map([
+      ['print', { 
+        type: 'NativeFunction', 
+        signature: '(value: any) -> Unit',
+        call: (args) => {
+          if (args.length === 0) {
+            hostDebug('');
+            return;
+          }
+          
+          const formatDebugValue = (value, name = null) => {
+            const prefix = name ? `${name}: ` : '';
+            const type = getRuntimeType(value);
+            
+            if (value && value.type === 'Function') {
+              const params = value.params ? value.params.map(p => typeof p === 'string' ? p : p.name).join(', ') : '';
+              return `${prefix}<function: (${params}) -> ...> (${type})`;
+            }
+            
+            if (Array.isArray(value)) {
+              return `${prefix}[${value.map(v => String(v)).join(', ')}] (${type}, length: ${value.length})`;
+            }
+            
+            if (value && typeof value === 'object' && value.properties instanceof Map) {
+              const props = Array.from(value.properties.entries()).map(([k, v]) => `${k}: ${String(v)}`).join(', ');
+              return `${prefix}{${props}} (${type}, size: ${value.properties.size})`;
+            }
+            
+            const displayValue = (value && typeof value.value === 'number') ? value.value : value;
+            return `${prefix}${String(displayValue)} (${type})`;
+          };
+          
+          for (let i = 0; i < args.length; i++) {
+            const formatted = formatDebugValue(args[i]);
+            hostDebug(formatted);
+          }
+        }
+      }],
+      ['inspect', { 
+        type: 'NativeFunction', 
+        signature: '(value: any) -> String',
+        call: ([value]) => {
+          const type = getRuntimeType(value);
+          let details = `Type: ${type}\n`;
+          
+          // Try to get shape information, but handle errors gracefully
+          try {
+            const shapeFunc = scope.get('shape');
+            if (shapeFunc && shapeFunc.type === 'NativeFunction') {
+              const shape = shapeFunc.call([value]);
+              if (shape && shape.properties) {
+                for (const [key, val] of shape.properties.entries()) {
+                  details += `${key}: ${String(val)}\n`;
+                }
+              }
+            }
+          } catch (e) {
+            // If shape fails, just continue without shape info
+            details += `Shape: Unable to determine (${e.message})\n`;
+          }
+          
+          if (value && value.type === 'Function') {
+            const params = value.params ? value.params.map(p => typeof p === 'string' ? p : p.name).join(', ') : '';
+            details += `Parameters: (${params})\n`;
+            if (value.signature) {
+              details += `Signature: ${value.signature}\n`;
+            }
+          }
+          
+          if (value && value.type === 'NativeFunction') {
+            details += `Native Function: Built-in system function\n`;
+            if (value.signature) {
+              details += `Signature: ${value.signature}\n`;
+            }
+          }
+          
+          return details.trim();
+        }
+      }],
+    ])
+  });
+
+  // random namespace - enhanced random utilities
+  scope.set('random', {
+    type: 'Object',
+    properties: new Map([
+      ['choice', { 
+        type: 'NativeFunction', 
+        signature: '(list: [T]) -> T',
+        call: ([list]) => {
+          if (!Array.isArray(list) || list.length === 0) {
+            throw new Error('random.choice expects a non-empty array');
+          }
+          const index = Math.floor(Math.random() * list.length);
+          return list[index];
+        }
+      }],
+      ['shuffle', { 
+        type: 'NativeFunction', 
+        signature: '(list: [T]) -> [T]',
+        call: ([list]) => {
+          if (!Array.isArray(list)) {
+            throw new Error('random.shuffle expects an array');
+          }
+          const result = [...list];
+          for (let i = result.length - 1; i > 0; i--) {
+            const j = Math.floor(Math.random() * (i + 1));
+            [result[i], result[j]] = [result[j], result[i]];
+          }
+          return result;
+        }
+      }],
+      ['range', { 
+        type: 'NativeFunction', 
+        signature: '(min: Int, max: Int) -> Int',
+        call: ([min, max]) => {
+          const minVal = (min && typeof min.value === 'number') ? min.value : Number(min);
+          const maxVal = (max && typeof max.value === 'number') ? max.value : Number(max);
+          if (minVal > maxVal) throw new Error('Invalid range: min > max');
+          const result = minVal + Math.floor(Math.random() * (maxVal - minVal + 1));
+          return { value: result, isFloat: false };
+        }
+      }],
+      ['seed', { 
+        type: 'NativeFunction', 
+        signature: '(seed: Int) -> Unit',
+        call: ([seed]) => {
+          // Note: JavaScript doesn't have a built-in seeded random, so this is a placeholder
+          // In a real implementation, you'd use a seeded PRNG library
+          const seedVal = (seed && typeof seed.value === 'number') ? seed.value : Number(seed);
+          hostDebug(`Random seed set to ${seedVal} (Note: JavaScript Math.random is not seeded)`);
+          return undefined;
+        }
+      }],
+    ])
+  });
+
+  // Function combinators
+  scope.set('flip', {
+    type: 'NativeFunction',
+    signature: '(func: (A, B) -> C) -> (B, A) -> C',
+    call: (args) => {
+      const func = args[0];
+      if (func.type !== 'Function') {
+        throw new Error('flip expects a function as the first argument.');
+      }
+      
+      return {
+        type: 'Function',
+        params: func.params.length >= 2 ? [func.params[1], func.params[0], ...func.params.slice(2)] : func.params,
+        body: func.body,
+        closure: new Map(func.closure),
+        signature: func.signature,
+      };
+    },
+  });
+
+  scope.set('apply', {
+    type: 'NativeFunction',
+    signature: '(func: (T) -> U, value: T) -> U',
+    call: (args) => {
+      const func = args[0];
+      const value = args[1];
+      if (func.type !== 'Function') {
+        throw new Error('apply expects a function as the first argument.');
+      }
+      
+      // Call the function with the value
+      const paramName = typeof func.params[0] === 'string' ? func.params[0] : func.params[0].name;
+      const callScope = new Map(func.closure);
+      callScope.set(paramName, value);
+      
+      const originalScope = new Map(scope);
+      scope.clear();
+      for (const [key, val] of callScope.entries()) {
+        scope.set(key, val);
+      }
+      
+      try {
+        return visit(func.body);
+      } finally {
+        scope.clear();
+        for (const [key, val] of originalScope.entries()) {
+          scope.set(key, val);
+        }
+      }
+    },
+  });
+
+  scope.set('pipe', {
+    type: 'NativeFunction',
+    signature: '(value: T, func: (T) -> U) -> U',
+    call: (args) => {
+      const value = args[0];
+      const func = args[1];
+      if (func.type !== 'Function') {
+        throw new Error('pipe expects a function as the second argument.');
+      }
+      
+      // Apply the function to the value (reverse of apply)
+      const applyFunc = scope.get('apply');
+      return applyFunc.call([func, value]);
+    },
+  });
+
+  scope.set('compose', {
+    type: 'NativeFunction',
+    signature: '(f: (B) -> C, g: (A) -> B) -> (A) -> C',
+    call: (args) => {
+      const f = args[0];
+      const g = args[1];
+      if (f.type !== 'Function' || g.type !== 'Function') {
+        throw new Error('compose expects two functions as arguments.');
+      }
+      
+      return {
+        type: 'Function',
+        params: g.params,
+        body: {
+          type: 'FunctionCall',
+          callee: { type: 'Identifier', name: 'f' },
+          arguments: [{
+            type: 'FunctionCall',
+            callee: { type: 'Identifier', name: 'g' },
+            arguments: g.params.map(p => ({ 
+              type: 'Identifier', 
+              name: typeof p === 'string' ? p : p.name 
+            }))
+          }]
+        },
+        closure: new Map([...g.closure, ['f', f], ['g', g]]),
+        signature: f.signature,
+      };
+    },
+  });
+
+  // Utility functions - top-level functions
+  scope.set('chunk', {
+    type: 'NativeFunction',
+    signature: '(list: [T], size: Int) -> [[T]]',
+    call: ([list, size]) => {
+      if (!Array.isArray(list)) {
+        throw new Error('chunk expects an array as the first argument');
+      }
+      const chunkSize = (size && typeof size.value === 'number') ? size.value : Number(size);
+      if (chunkSize <= 0) {
+        throw new Error('chunk size must be positive');
+      }
+      
+      const result = [];
+      for (let i = 0; i < list.length; i += chunkSize) {
+        result.push(list.slice(i, i + chunkSize));
+      }
+      return result;
+    }
+  });
+
+  scope.set('range', {
+    type: 'NativeFunction',
+    signature: '(start: Int, end: Int) -> [Int]',
+    call: ([start, end]) => {
+      const startVal = (start && typeof start.value === 'number') ? start.value : Number(start);
+      const endVal = (end && typeof end.value === 'number') ? end.value : Number(end);
+      
+      const result = [];
+      if (startVal <= endVal) {
+        for (let i = startVal; i <= endVal; i++) {
+          result.push({ value: i, isFloat: false });
+        }
+      } else {
+        for (let i = startVal; i >= endVal; i--) {
+          result.push({ value: i, isFloat: false });
+        }
+      }
+      return result;
+    }
+  });
+
+  scope.set('repeat', {
+    type: 'NativeFunction',
+    signature: '(count: Int, value: T) -> [T]',
+    call: ([count, value]) => {
+      const n = (count && typeof count.value === 'number') ? count.value : Number(count);
+      if (n < 0) {
+        throw new Error('repeat count must be non-negative');
+      }
+      
+      const result = [];
+      for (let i = 0; i < n; i++) {
+        result.push(value);
+      }
+      return result;
+    }
+  });
+
+  scope.set('assert', {
+    type: 'NativeFunction',
+    signature: '(condition: Bool, message: String) -> Unit',
+    call: ([condition, message]) => {
+      const isTrue = Boolean(condition);
+      if (!isTrue) {
+        const msg = message ? String(message) : 'Assertion failed';
+        throw new Error(`Assertion failed: ${msg}`);
+      }
+      return undefined;
+    }
+  });
+
+  function visit(node) {
+    switch (node.type) {
+      case 'Program':
+        return visitProgram(node);
+      case 'TypeDeclaration':
+        return visitTypeDeclaration(node);
+      case 'VariableDeclaration':
+        return visitVariableDeclaration(node);
+      case 'FunctionDeclaration':
+        return visitFunctionDeclaration(node);
+      case 'CurriedFunctionDeclaration':
+        return visitCurriedFunctionDeclaration(node);
+      case 'FunctionDeclarationBody':
+        return visitFunctionDeclarationBody(node);
+      case 'WithHeader':
+        return visitWithHeader(node);
+      case 'CurriedFunctionBody':
+        return visitCurriedFunctionBody(node);
+      case 'FunctionCall':
+        return visitFunctionCall(node);
+      case 'WhenExpression':
+        return visitWhenExpression(node);
+      case 'BinaryExpression':
+        return visitBinaryExpression(node);
+      case 'UnaryExpression':
+        return visitUnaryExpression(node);
+      case 'NumberLiteral':
+        return { value: node.value, isFloat: node.isFloat };
+      case 'StringLiteral':
+        return node.value;
+      case 'BooleanLiteral':
+        return node.value;
+      case 'Identifier':
+        return visitIdentifier(node);
+      case 'ListLiteral':
+        return visitListLiteral(node);
+      case 'TableLiteral':
+        return visitTableLiteral(node);
+      case 'AnonymousFunction':
+        return visitAnonymousFunction(node);
+      case 'MemberExpression':
+        return visitMemberExpression(node);
+      case 'TypePattern':
+        return node.name;
+      case 'WildcardPattern':
+        return '_'; // Wildcard pattern always matches
+      case 'ResultExpression':
+        return visitResultExpression(node);
+      default:
+        throw new Error(`Unknown node type: ${node.type}`);
+    }
+  }
+
+  function visitProgram(node) {
+    // First pass: add all function declarations to scope for mutual recursion support
+    for (const statement of node.body) {
+      if (statement.type === 'FunctionDeclaration') {
+        const func = {
+          type: 'Function',
+          params: statement.params,
+          body: statement.body,
+          closure: new Map(scope),
+          returnType: statement.returnType,
+        };
+        scope.set(statement.name, func);
+        
+        // Add the function to its own closure for recursion support
+        func.closure.set(statement.name, func);
+      }
+    }
+    
+    // Second pass: execute all statements in order
+    let result;
+    for (const statement of node.body) {
+      if (statement.type === 'FunctionDeclaration') {
+        // Function declarations are already handled in the first pass
+        continue;
+      }
+      result = visit(statement);
+    }
+    return result;
+  }
+
+  function visitTypeDeclaration(node) {
+    types.set(node.name, node.typeAnnotation);
+  }
+
+  function visitVariableDeclaration(node) {
+    const value = visit(node.value);
+    if (types.has(node.name)) {
+      const expectedType = types.get(node.name);
+      const actualType = getRuntimeType(value);
+      if (!isTypeAssignable(actualType, expectedType)) {
+        const displayValue = value && typeof value.value === 'number' ? value.value : value;
+        throw new Error(`Type mismatch for ${node.name}: expected ${expectedType} but got ${actualType} (value: ${JSON.stringify(displayValue)})`);
+      }
+    }
+    scope.set(node.name, value);
+    return value; // Return the assigned value
+  }
+
+  function visitFunctionDeclaration(node) {
+    // Function declarations are now handled in visitProgram for mutual recursion support
+    // This function is kept for backward compatibility but should not be called directly
+    const func = {
+      type: 'Function',
+      params: node.params,
+      body: node.body,
+      closure: new Map(scope),
+      returnType: node.returnType,
+    };
+    scope.set(node.name, func);
+    
+    // Add the function to its own closure for recursion support
+    func.closure.set(node.name, func);
+  }
+
+  function visitFunctionDeclarationBody(node) {
+    const func = {
+      type: 'Function',
+      params: node.params,
+      body: node.body,
+      closure: new Map(scope),
+      returnType: node.returnType,
+    };
+    return func;
+  }
+
+  function visitCurriedFunctionDeclaration(node) {
+    // Create a curried function with type information
+    const func = {
+      type: 'Function',
+      params: [node.param], // First typed parameter
+      body: node.body, // CurriedFunctionBody containing remaining params
+      closure: new Map(scope),
+      returnType: node.returnType, // Function type like (Float -> Float)
+      isCurried: true,
+    };
+    scope.set(node.name, func);
+    
+    // Add the function to its own closure for recursion support
+    func.closure.set(node.name, func);
+  }
+
+  function visitCurriedFunctionBody(node) {
+    // Handle the remaining parameters and body of a curried function
+    const func = {
+      type: 'Function',
+      params: node.params, // Remaining untyped parameters
+      body: node.body, // Final expression
+      closure: new Map(scope),
+      returnType: node.returnType, // Pass along the final return type
+      isCurriedBody: true,
+    };
+    return func;
+  }
+
+  // Helper function to get the runtime type of a value
+  function getRuntimeType(value) {
+    // Handle our custom number format with isFloat property
+    if (value && typeof value.value === 'number') {
+      return value.isFloat ? 'Float' : 'Int';
+    }
+    
+    if (typeof value === 'number') {
+      // Fallback for regular numbers
+      return Number.isInteger(value) ? 'Int' : 'Float';
+    }
+    if (typeof value === 'string') {
+      return 'String';
+    }
+    if (typeof value === 'boolean') {
+      return 'Bool';
+    }
+    if (Array.isArray(value)) {
+      return 'List';
+    }
+    if (value && value.type === 'Object' && value.properties) {
+      return 'Table';
+    }
+    if (value && value.type === 'Result') {
+      return 'Result';
+    }
+    return 'Unknown';
+  }
+
+  // Type assignability with numeric lattice: Int ⊂ Float ⊂ Number
+  function isTypeAssignable(actualType, expectedType) {
+    if (!expectedType) return true;
+    
+    // Handle complex type objects
+    if (typeof expectedType === 'object') {
+      if (expectedType.type === 'PrimitiveType') {
+        return isTypeAssignable(actualType, expectedType.name);
+      }
+      if (expectedType.type === 'FunctionType') {
+        return actualType === 'Function'; // TODO: Could add deeper function signature validation
+      }
+    }
+    
+    // Handle string types
+    if (expectedType === actualType) return true;
+    if (expectedType === 'Number') {
+      return actualType === 'Int' || actualType === 'Float';
+    }
+    if (expectedType === 'Float') {
+      return actualType === 'Float' || actualType === 'Int';
+    }
+    return false;
+  }
+
+  // Helper function to validate argument types
+  function validateArgumentType(argValue, expectedType, paramName, functionName) {
+    if (!expectedType) {
+      return; // No type annotation, skip validation
+    }
+
+    const actualType = getRuntimeType(argValue);
+    
+    if (!isTypeAssignable(actualType, expectedType)) {
+      // Extract the actual value for display
+      const displayValue = argValue && typeof argValue.value === 'number' ? argValue.value : argValue;
+      const expectedTypeStr = typeof expectedType === 'object' ? 
+        (expectedType.type === 'PrimitiveType' ? expectedType.name : 
+         expectedType.type === 'FunctionType' ? 'Function' : JSON.stringify(expectedType)) :
+        expectedType;
+      throw new Error(
+        `Type mismatch in function '${functionName}': ` +
+        `Expected ${expectedTypeStr} for parameter '${paramName}', ` +
+        `but got ${actualType} (value: ${JSON.stringify(displayValue)})`
+      );
+    }
+  }
+
+  // Helper function to validate return type
+  function validateReturnType(returnValue, expectedType, functionName) {
+    if (!expectedType) {
+      return; // No return type annotation, skip validation
+    }
+
+    // Handle function type validation
+    if (expectedType && typeof expectedType === 'object' && expectedType.type === 'FunctionType') {
+      if (returnValue.type !== 'Function') {
+        throw new Error(
+          `Return type mismatch in function '${functionName}': ` +
+          `Expected function, but got ${getRuntimeType(returnValue)}`
+        );
+      }
+      // TODO: Could add more detailed function signature validation here
+      return;
+    }
+
+    const actualType = getRuntimeType(returnValue);
+    
+    // Allow Int where Float is expected (numeric widening)
+    if (!isTypeAssignable(actualType, expectedType)) {
+      // Extract the actual value for display
+      const displayValue = returnValue && typeof returnValue.value === 'number' ? returnValue.value : returnValue;
+      const expectedTypeStr = typeof expectedType === 'object' ? 
+        (expectedType.type === 'PrimitiveType' ? expectedType.name : 
+         expectedType.type === 'FunctionType' ? 'Function' : JSON.stringify(expectedType)) :
+        expectedType;
+      throw new Error(
+        `Return type mismatch in function '${functionName}': ` +
+        `Expected ${expectedTypeStr}, but got ${actualType} (value: ${JSON.stringify(displayValue)})`
+      );
+    }
+  }
+
+  function visitFunctionCall(node) {
+    const callee = visit(node.callee);
+    
+    // Handle native functions (like io.out)
+    if (callee.type === 'NativeFunction') {
+      const args = node.arguments.map(arg => visit(arg));
+      return callee.call(args);
+    }
+
+    if (callee.type !== 'Function') {
+      throw new Error(`${node.callee.name} is not a function`);
+    }
+
+    const args = node.arguments.map(arg => visit(arg));
+
+    // Validate argument types (only for typed functions)
+    const functionName = node.callee.name || 'anonymous';
+    for (let i = 0; i < Math.min(args.length, callee.params.length); i++) {
+      const param = callee.params[i];
+      // Handle both string parameters (anonymous functions) and object parameters (typed functions)
+      const paramName = typeof param === 'string' ? param : (param.name || param.value);
+      // Check if this is a typed parameter (has a type annotation)
+      // Typed parameters have { name: string, type: string }
+      // Untyped parameters have { type: 'Identifier', name: string }
+      const expectedType = typeof param === 'string' ? null : (param.type && param.type !== 'Identifier' ? param.type : null);
+      // Only validate if the parameter has a type annotation
+      if (expectedType) {
+        validateArgumentType(args[i], expectedType, paramName, functionName);
+      }
+    }
+
+    if (args.length < callee.params.length) {
+      // Partial application
+      const newParams = callee.params.slice(args.length);
+      const newBody = callee.body;
+      const newClosure = new Map(callee.closure);
+      for (let i = 0; i < args.length; i++) {
+        // Handle both string parameters (anonymous functions) and object parameters (typed functions)
+        const paramName = typeof callee.params[i] === 'string' ? callee.params[i] : callee.params[i].name;
+        newClosure.set(paramName, args[i]);
+      }
+      return {
+        type: 'Function',
+        params: newParams,
+        body: newBody,
+        closure: newClosure,
+        returnType: callee.returnType,
+      };
+    } else if (args.length > callee.params.length) {
+      throw new Error(`Too many arguments for function ${functionName}. Expected ${callee.params.length} but got ${args.length}`);
+    }
+
+    // Create the call scope by combining the global scope with the function's closure and arguments
+    const callScope = new Map(scope); // Start with global scope for mutual recursion
+    // Add function's closure variables
+    for (const [key, value] of callee.closure.entries()) {
+      callScope.set(key, value);
+    }
+    // Add function parameters
+    for (let i = 0; i < callee.params.length; i++) {
+      // Handle both string parameters (anonymous functions) and object parameters (typed functions)
+      const paramName = typeof callee.params[i] === 'string' ? callee.params[i] : callee.params[i].name;
+      callScope.set(paramName, args[i]);
+    }
+
+    // Save the current scope and set up the function's scope
+    const originalScope = new Map(scope);
+    scope.clear();
+    // Set up the function's scope with global scope, closure variables, and parameters
+    for (const [key, value] of callScope.entries()) {
+      scope.set(key, value);
+    }
+
+    let result;
+    if (callee.body.type === 'FunctionDeclarationBody') {
+        // This is a curried function, return the next nested function
+        result = visit(callee.body);
+    } else if (callee.body.type === 'CurriedFunctionBody') {
+        // This is a new-style typed curried function body
+        result = visit(callee.body);
+    } else if (callee.body && callee.body.type === 'WithHeader') {
+        // Execute with-header before evaluating body
+        result = visitWithHeader(callee.body);
+    } else {
+        // This is the final function body, execute it
+        result = visit(callee.body);
+    }
+
+    // Validate return type
+    validateReturnType(result, callee.returnType, functionName);
+
+    scope.clear();
+    for (const [key, value] of originalScope.entries()) {
+      scope.set(key, value);
+    }
+
+    return result;
+  }
+
+  // Execute a with-header node in current scope context
+  function visitWithHeader(node) {
+    const withTypes = new Map();
+    const withScope = new Map(scope);
+    const originalScope = new Map(scope);
+
+    // Helper to set scope from a map
+    function setScopeFrom(map) {
+      scope.clear();
+      for (const [k, v] of map.entries()) scope.set(k, v);
+    }
+
+    if (node.recursive) {
+      // Pre-bind placeholders
+      for (const entry of node.entries) {
+        if (entry.type === 'WithTypeDecl') {
+          withTypes.set(entry.name, entry.typeAnnotation);
+        } else if (entry.type === 'WithAssign') {
+          withScope.set(entry.name, undefined);
+        }
+      }
+      setScopeFrom(withScope);
+      for (const entry of node.entries) {
+        if (entry.type !== 'WithAssign') continue;
+        let boundValue;
+        // If the value is an anonymous function AST, build a Function that
+        // closes over the withScope (by reference) to enable mutual recursion
+        if (entry.value && entry.value.type === 'AnonymousFunction') {
+          boundValue = {
+            type: 'Function',
+            params: entry.value.params,
+            body: entry.value.body,
+            closure: withScope,
+          };
+        } else {
+          boundValue = visit(entry.value);
+        }
+        if (!boundValue || boundValue.type !== 'Function') {
+          throw new Error(`with rec expects function-valued bindings. '${entry.name}' is not a function`);
+        }
+        withScope.set(entry.name, boundValue);
+      }
+      // Validate typed locals if any
+      for (const [name, expectedType] of withTypes.entries()) {
+        if (!withScope.has(name)) continue;
+        const actual = withScope.get(name);
+        if (!isTypeAssignable(getRuntimeType(actual), expectedType)) {
+          const displayValue = actual && typeof actual.value === 'number' ? actual.value : actual;
+          const expectedTypeStr = typeof expectedType === 'object' ? (expectedType.type === 'PrimitiveType' ? expectedType.name : 'Function') : expectedType;
+          throw new Error(`Type mismatch for ${name}: expected ${expectedTypeStr} but got ${getRuntimeType(actual)} (value: ${JSON.stringify(displayValue)})`);
+        }
+      }
+      // Evaluate body in with-scope
+      setScopeFrom(withScope);
+      try {
+        return visit(node.body);
+      } finally {
+        setScopeFrom(originalScope);
+      }
+    } else {
+      // Non-rec: process in order
+      for (const entry of node.entries) {
+        if (entry.type === 'WithTypeDecl') {
+          withTypes.set(entry.name, entry.typeAnnotation);
+          continue;
+        }
+        // Assign
+        setScopeFrom(withScope);
+        const value = visit(entry.value);
+        // Type-check if declared
+        if (withTypes.has(entry.name)) {
+          const expectedType = withTypes.get(entry.name);
+          if (!isTypeAssignable(getRuntimeType(value), expectedType)) {
+            const displayValue = value && typeof value.value === 'number' ? value.value : value;
+            const expectedTypeStr = typeof expectedType === 'object' ? (expectedType.type === 'PrimitiveType' ? expectedType.name : 'Function') : expectedType;
+            throw new Error(`Type mismatch for ${entry.name}: expected ${expectedTypeStr} but got ${getRuntimeType(value)} (value: ${JSON.stringify(displayValue)})`);
+          }
+        }
+        withScope.set(entry.name, value);
+      }
+      // Evaluate body
+      setScopeFrom(withScope);
+      try {
+        return visit(node.body);
+      } finally {
+        setScopeFrom(originalScope);
+      }
+    }
+  }
+
+  function visitWhenExpression(node) {
+    const discriminantValues = node.discriminants.map(d => visit(d));
+
+    for (const whenCase of node.cases) {
+      const patterns = whenCase.patterns;
+      if (patterns.length !== discriminantValues.length) {
+        continue;
+      }
+
+      let match = true;
+      const caseScope = new Map();
+
+      for (let i = 0; i < patterns.length; i++) {
+        const pattern = patterns[i];
+        const discriminantValue = discriminantValues[i];
+        const discriminantName = node.discriminants[i].type === 'Identifier' ? node.discriminants[i].name : null;
+
+        if (pattern.type === 'WildcardPattern') {
+          continue;
+        }
+
+        if (pattern.type === 'GuardPattern') {
+          // For guard patterns, the underlying pattern is treated as a binding pattern
+          const underlyingPattern = pattern.pattern;
+          let underlyingMatch = true;
+          
+          if (underlyingPattern.type === 'WildcardPattern') {
+            // Wildcard always matches
+          } else if (underlyingPattern.type === 'Identifier') {
+            // Identifier patterns always match (they bind the value)
+            underlyingMatch = true;
+          } else if (underlyingPattern.type === 'TypePattern') {
+            const expectedType = underlyingPattern.name;
+            let actualType;
+            if (types.has(discriminantName)) {
+              actualType = types.get(discriminantName);
+            } else {
+              actualType = getRuntimeType(discriminantValue);
+            }
+            underlyingMatch = isTypeAssignable(actualType, expectedType);
+          } else {
+            // For literal patterns, check direct equality
+            const patternValue = visit(underlyingPattern);
+            const discriminantValueForComparison = discriminantValue;
+            
+            if (patternValue && typeof patternValue.value === 'number' && 
+                discriminantValueForComparison && typeof discriminantValueForComparison.value === 'number') {
+              underlyingMatch = (patternValue.value === discriminantValueForComparison.value);
+            } else {
+              underlyingMatch = (patternValue === discriminantValueForComparison);
+            }
+          }
+          
+          if (!underlyingMatch) {
+            match = false;
+            break;
+          }
+          
+          // Now evaluate the guard with the discriminant value in scope
+          const originalScope = new Map(scope);
+          // Add discriminant value to scope for guard evaluation
+          // If the underlying pattern is an identifier, use that name
+          if (underlyingPattern.type === 'Identifier') {
+            scope.set(underlyingPattern.name, discriminantValue);
+          } else if (discriminantName) {
+            scope.set(discriminantName, discriminantValue);
+          }
+          
+          try {
+            const guardResult = visit(pattern.guard);
+            if (!guardResult) {
+              match = false;
+              break;
+            }
+          } finally {
+            // Restore original scope
+            scope.clear();
+            for (const [key, value] of originalScope.entries()) {
+              scope.set(key, value);
+            }
+          }
+        } else if (pattern.type === 'TypePattern') {
+          const expectedType = pattern.name;
+          let actualType;
+
+          if (types.has(discriminantName)) {
+            actualType = types.get(discriminantName);
+          } else {
+            actualType = getRuntimeType(discriminantValue);
+          }
+
+          if (!isTypeAssignable(actualType, expectedType)) {
+            match = false;
+            break;
+          }
+        } else if (pattern.type === 'ResultPattern') {
+          if (discriminantValue.variant !== pattern.variant) {
+            match = false;
+            break;
+          }
+          caseScope.set(pattern.identifier.name, discriminantValue.value);
+        } else if (pattern.type === 'ListPattern') {
+          if (!Array.isArray(discriminantValue) || pattern.elements.length !== discriminantValue.length) {
+            match = false;
+            break;
+          }
+          for (let j = 0; j < pattern.elements.length; j++) {
+            const subPattern = pattern.elements[j];
+            const subDiscriminantValue = discriminantValue[j];
+            if (subPattern.type === 'WildcardPattern') {
+              continue;
+            }
+            const patternValue = visit(subPattern);
+            // Handle our custom number format for comparison
+            if (patternValue && typeof patternValue.value === 'number' && 
+                subDiscriminantValue && typeof subDiscriminantValue.value === 'number') {
+              if (patternValue.value !== subDiscriminantValue.value) {
+                match = false;
+                break;
+              }
+            } else if (patternValue !== subDiscriminantValue) {
+              match = false;
+              break;
+            }
+          }
+          if (!match) break;
+        } else if (pattern.type === 'TablePattern') {
+          if (discriminantValue.type !== 'Object') {
+            match = false;
+            break;
+          }
+          for (const prop of pattern.properties) {
+            if (!discriminantValue.properties.has(prop.key)) {
+              match = false;
+              break;
+            }
+            if (prop.key === '_') {
+              let foundMatchForWildcard = false;
+              for (const [discKey, discValue] of discriminantValue.properties.entries()) {
+                if (prop.value.type === 'WildcardPattern') {
+                  foundMatchForWildcard = true;
+                  break;
+                }
+                if (visit(prop.value) === discValue) {
+                  foundMatchForWildcard = true;
+                  break;
+                }
+              }
+              if (!foundMatchForWildcard) {
+                match = false;
+                break;
+              }
+            } else {
+              if (!discriminantValue.properties.has(prop.key)) {
+                match = false;
+                break;
+              }
+              const subDiscriminantValue = discriminantValue.properties.get(prop.key);
+              if (prop.value.type === 'WildcardPattern') {
+                continue;
+              }
+              const propValue = visit(prop.value);
+              // Handle our custom number format for comparison
+              if (propValue && typeof propValue.value === 'number' && 
+                  subDiscriminantValue && typeof subDiscriminantValue.value === 'number') {
+                if (propValue.value !== subDiscriminantValue.value) {
+                  match = false;
+                  break;
+                }
+              } else if (propValue !== subDiscriminantValue) {
+                match = false;
+                break;
+              }
+            }
+          }
+          if (!match) break;
+        } else {
+          // Handle literal value comparisons
+          const patternValue = visit(pattern);
+          const discriminantValueForComparison = discriminantValue;
+          
+          // For numeric values, compare the actual values
+          if (patternValue && typeof patternValue.value === 'number' && 
+              discriminantValueForComparison && typeof discriminantValueForComparison.value === 'number') {
+            if (patternValue.value !== discriminantValueForComparison.value) {
+              match = false;
+              break;
+            }
+          } else if (patternValue !== discriminantValueForComparison) {
+            match = false;
+            break;
+          }
+        }
+      }
+
+      if (match) {
+        const originalScope = new Map(scope);
+        for (const [key, value] of caseScope.entries()) {
+          scope.set(key, value);
+        }
+
+        const result = visit(whenCase.consequent);
+
+        for (const [key, value] of caseScope.entries()) {
+          scope.delete(key);
+        }
+        return result;
+      }
+    }
+    return undefined;
+  }
+
+  function visitUnaryExpression(node) {
+    const operand = visit(node.operand);
+    
+    if (node.operator === '-') {
+      const operandValue = operand && typeof operand.value === 'number' ? operand.value : operand;
+      const result = -operandValue;
+      // Preserve the float/int type
+      if (typeof result === 'number') {
+        const operandIsFloat = operand && typeof operand.value === 'number' && operand.isFloat;
+        return { value: result, isFloat: operandIsFloat };
+      }
+      return result;
+    }
+    
+    throw new Error(`Unknown unary operator: ${node.operator}`);
+  }
+
+  function visitBinaryExpression(node) {
+    const left = visit(node.left);
+    const right = visit(node.right);
+    
+    // Extract numeric values for arithmetic operations
+    const leftValue = left && typeof left.value === 'number' ? left.value : left;
+    const rightValue = right && typeof right.value === 'number' ? right.value : right;
+    
+    switch (node.operator) {
+      case '+':
+        const result = leftValue + rightValue;
+        // For string concatenation, preserve the string type
+        if (typeof leftValue === 'string' || typeof rightValue === 'string') {
+          return result;
+        }
+        // For numeric addition, preserve the float/int type
+        if (typeof result === 'number') {
+          const leftIsFloat = left && typeof left.value === 'number' && left.isFloat;
+          const rightIsFloat = right && typeof right.value === 'number' && right.isFloat;
+          return { value: result, isFloat: leftIsFloat || rightIsFloat };
+        }
+        return result;
+      case '-':
+        const subResult = leftValue - rightValue;
+        if (typeof subResult === 'number') {
+          const leftIsFloat = left && typeof left.value === 'number' && left.isFloat;
+          const rightIsFloat = right && typeof right.value === 'number' && right.isFloat;
+          return { value: subResult, isFloat: leftIsFloat || rightIsFloat };
+        }
+        return subResult;
+      case '*':
+        const mulResult = leftValue * rightValue;
+        if (typeof mulResult === 'number') {
+          const leftIsFloat = left && typeof left.value === 'number' && left.isFloat;
+          const rightIsFloat = right && typeof right.value === 'number' && right.isFloat;
+          return { value: mulResult, isFloat: leftIsFloat || rightIsFloat };
+        }
+        return mulResult;
+      case '/':
+        if (rightValue === 0) {
+          throw new RuntimeError(
+            'Division by zero',
+            node.location,
+            host.source || '',
+            ['Check if the divisor is zero before dividing', 'Use conditional logic to handle zero divisors']
+          );
+        }
+        const divResult = leftValue / rightValue;
+        if (typeof divResult === 'number') {
+          const leftIsFloat = left && typeof left.value === 'number' && left.isFloat;
+          const rightIsFloat = right && typeof right.value === 'number' && right.isFloat;
+          return { value: divResult, isFloat: leftIsFloat || rightIsFloat };
+        }
+        return divResult;
+      case '%':
+        const modResult = leftValue % rightValue;
+        if (typeof modResult === 'number') {
+          const leftIsFloat = left && typeof left.value === 'number' && left.isFloat;
+          const rightIsFloat = right && typeof right.value === 'number' && right.isFloat;
+          return { value: modResult, isFloat: leftIsFloat || rightIsFloat };
+        }
+        return modResult;
+      case '..':
+        // String concatenation using .. operator
+        return String(leftValue) + String(rightValue);
+      case '=':
+        return leftValue === rightValue;
+      case '>':
+        return leftValue > rightValue;
+      case '<':
+        return leftValue < rightValue;
+      case '>=':
+        return leftValue >= rightValue;
+      case '<=':
+        return leftValue <= rightValue;
+      case '!=':
+        return leftValue !== rightValue;
+      case 'and':
+        return Boolean(leftValue) && Boolean(rightValue);
+      case 'or':
+        return Boolean(leftValue) || Boolean(rightValue);
+      case 'xor':
+        return Boolean(leftValue) !== Boolean(rightValue); // XOR for booleans
+      default:
+        throw new Error(`Unknown operator: ${node.operator}`);
+    }
+  }
+
+  function visitIdentifier(node) {
+    if (scope.has(node.name)) {
+      return scope.get(node.name);
+    }
+    throw ErrorHelpers.undefinedVariable(node.name, host.source || '', node.location);
+  }
+
+  function visitMemberExpression(node) {
+    const object = visit(node.object);
+    let propertyKey;
+
+    if (node.property.type === 'Identifier') {
+      propertyKey = node.property.name;
+    } else if (node.property.type === 'NumberLiteral' || node.property.type === 'StringLiteral') {
+      propertyKey = node.property.value;
+    } else if (node.property.type === 'MemberExpression') {
+      // For nested member access like e.data.input, we need to recursively visit
+      // the property to get the intermediate object, then access the final property
+      const intermediateObject = visit(node.property);
+      // The intermediate object is the result of the nested access
+      return intermediateObject;
+    } else {
+      throw new Error(`Unsupported property type for member access: ${node.property.type}`);
+    }
+
+    // Handle null/undefined objects
+    if (object == null) {
+      return null;
+    }
+
+    // Handle list element access
+    if (Array.isArray(object) && typeof propertyKey === 'number') {
+      if (propertyKey < 0 || propertyKey >= object.length) {
+        throw new RuntimeError(
+          `Index out of bounds: ${propertyKey}`,
+          node.location,
+          host.source || '',
+          [`Valid indices are 0 to ${object.length - 1}`, 'Check list length before accessing elements']
+        );
+      }
+      return object[propertyKey];
+    }
+
+    // Handle table property access
+    if (object.type === 'Object' && object.properties.has(propertyKey)) {
+      return object.properties.get(propertyKey);
+    }
+
+    // Throw error for undefined properties
+    throw ErrorHelpers.undefinedProperty(propertyKey, object, host.source || '', node.location);
+  }
+
+  function visitResultExpression(node) {
+    return {
+      type: 'Result',
+      variant: node.variant,
+      value: visit(node.value),
+    };
+  }
+
+  function visitListLiteral(node) {
+    return node.elements.map(element => visit(element));
+  }
+
+  function visitTableLiteral(node) {
+    const properties = new Map();
+    for (const prop of node.properties) {
+      properties.set(prop.key, visit(prop.value));
+    }
+    const base = { type: 'Object', properties };
+    // Expose direct property access for convenience (e.g., result.state)
+    return new Proxy(base, {
+      get(target, prop, receiver) {
+        if (prop in target) {
+          return Reflect.get(target, prop, receiver);
+        }
+        if (typeof prop === 'string' && target.properties && target.properties.has(prop)) {
+          return target.properties.get(prop);
+        }
+        return undefined;
+      },
+    });
+  }
+
+  function visitAnonymousFunction(node) {
+    return {
+      type: 'Function',
+      params: node.params,
+      body: node.body,
+      closure: new Map(scope),
+    };
+  }
+
+  function interpret() {
+    return visit(ast);
+  }
+
+  return {
+    interpret,
+    scope, // Expose scope for testing
+  };
+}
+
+export { createInterpreter };