#!/usr/bin/env python3
"""
JavaScript Code Generation Backend

Generates JavaScript code from BASIC programs that runs in:
- Browser (via <script> tag or standalone HTML)
- Node.js (command-line execution)

Generated code uses a switch-based control flow system for GOTO/GOSUB.
"""

from typing import List, Dict, Set, Optional, Any
from src.ast_nodes import *
from src.semantic_analyzer import SymbolTable, VarType
from src.codegen_backend import CodeGenBackend
from src.tokens import TokenType


class JavaScriptBackend(CodeGenBackend):
    """
    JavaScript code generator for browser and Node.js execution.

    Features:
    - Switch-based control flow (PC variable)
    - Variable-indexed FOR loops
    - Built-in BASIC functions
    - Environment detection (browser vs Node.js)
    - Self-contained runtime
    """

    def __init__(self, symbols: SymbolTable, config: Optional[Dict[str, Any]] = None):
        super().__init__(symbols)
        self.indent_level = 0
        self.line_labels: Set[int] = set()  # Line numbers that need case statements
        self.next_line_map: Dict[int, Optional[int]] = {}  # Map line -> next line
        self.config = config or {}

        # Track what features are used
        self.uses_input = False
        self.uses_line_input = False
        self.uses_write = False
        self.uses_lprint = False
        self.uses_gosub = False
        self.uses_data = False
        self.uses_random = False
        self.uses_error_handling = False
        self.uses_file_io = False

        # DATA/READ/RESTORE support
        self.data_values: List[Any] = []
        self.data_types: List[str] = []  # 'number' or 'string'

        # DEF FN functions
        self.def_fn_functions: List[DefFnStatementNode] = []

        # FOR/NEXT matching - map FOR line to line after NEXT
        self.for_to_after_next: Dict[int, Optional[int]] = {}

        # Track which variable a NEXT statement should use (for NEXT without variable)
        # Maps (line_number, stmt_index) -> var_name
        self.next_var_map: Dict[tuple, str] = {}

    def get_file_extension(self) -> str:
        """JavaScript files use .js extension"""
        return '.js'

    def get_compiler_command(self, source_file: str, output_file: str) -> List[str]:
        """
        No compilation needed for JavaScript - it's interpreted.
        For Node.js, can make executable with shebang and chmod.
        """
        return []  # No compilation step

    def indent(self) -> str:
        """Return current indentation"""
        return '  ' * self.indent_level

    def _token_to_operator(self, token: TokenType) -> str:
        """Convert TokenType to operator string"""
        operator_map = {
            TokenType.PLUS: '+',
            TokenType.MINUS: '-',
            TokenType.MULTIPLY: '*',
            TokenType.DIVIDE: '/',
            TokenType.POWER: '^',
            TokenType.BACKSLASH: '//',  # Integer division (JavaScript doesn't have this, use Math.floor)
            TokenType.MOD: '%',
            TokenType.EQUAL: '=',
            TokenType.LESS_THAN: '<',
            TokenType.GREATER_THAN: '>',
            TokenType.LESS_EQUAL: '<=',
            TokenType.GREATER_EQUAL: '>=',
            TokenType.NOT_EQUAL: '<>',
            TokenType.AND: 'AND',
            TokenType.OR: 'OR',
            TokenType.NOT: 'NOT',
            TokenType.XOR: 'XOR',
            TokenType.EQV: 'EQV',
            TokenType.IMP: 'IMP',
        }
        return operator_map.get(token, str(token))

    def generate(self, program: ProgramNode) -> str:
        """Generate JavaScript code for the program"""
        # Analysis passes
        self._collect_line_labels(program)
        self._collect_data_values(program)
        self._collect_def_fn(program)
        self._analyze_features(program)
        self._match_for_next(program)

        code = []

        # Header
        code.append('#!/usr/bin/env node')
        code.append('// Generated by MBASIC-2025 JavaScript Backend')
        code.append(f'// Source: {self.config.get("source_file", "program.bas")}')
        code.append('')
        code.append('(function() {')
        code.append("  'use strict';")
        code.append('')

        self.indent_level = 1

        # Runtime library
        code.append(self.indent() + '// ===== Runtime Library =====')
        code.extend(self._generate_runtime())
        code.append('')

        # Variables
        code.append(self.indent() + '// ===== Variables =====')
        code.extend(self._generate_variables())
        code.append('')

        # DEF FN functions
        if self.def_fn_functions:
            code.append(self.indent() + '// ===== DEF FN Functions =====')
            code.extend(self._generate_def_fn_functions())
            code.append('')

        # Main program
        code.append(self.indent() + '// ===== Program =====')
        code.append(self.indent() + 'function main() {')
        self.indent_level += 1

        # Initialize PC to first line
        first_line = min(line.line_number for line in program.lines) if program.lines else 10
        code.append(self.indent() + f'let _pc = {first_line};')
        code.append('')

        # Main execution loop with switch statement
        code.append(self.indent() + 'while (_pc !== null) {')
        self.indent_level += 1
        code.append(self.indent() + 'switch (_pc) {')
        self.indent_level += 1

        # Generate code for each line
        for line in program.lines:
            code.extend(self._generate_line(line))

        # Default case - unknown line
        code.append(self.indent() + 'default:')
        self.indent_level += 1
        code.append(self.indent() + '_error(`Undefined line number ${_pc}`);')
        code.append(self.indent() + 'return;')
        self.indent_level -= 1

        self.indent_level -= 1
        code.append(self.indent() + '}')  # End switch
        self.indent_level -= 1
        code.append(self.indent() + '}')  # End while

        self.indent_level -= 1
        code.append(self.indent() + '}')  # End main()
        code.append('')

        # Run the program
        code.append(self.indent() + '// ===== Execute =====')
        code.append(self.indent() + 'try {')
        self.indent_level += 1
        code.append(self.indent() + 'main();')
        self.indent_level -= 1
        code.append(self.indent() + '} catch (e) {')
        self.indent_level += 1
        code.append(self.indent() + '_print(e.message);')
        code.append(self.indent() + 'if (typeof process !== "undefined") process.exit(1);')
        self.indent_level -= 1
        code.append(self.indent() + '}')

        self.indent_level = 0
        code.append('})();')
        code.append('')

        return '\n'.join(code)

    def _collect_line_labels(self, program: ProgramNode):
        """Collect all line numbers that are referenced"""
        # All lines need labels in switch statement
        for i, line in enumerate(program.lines):
            self.line_labels.add(line.line_number)

            # Build next line map
            if i + 1 < len(program.lines):
                self.next_line_map[line.line_number] = program.lines[i + 1].line_number
            else:
                self.next_line_map[line.line_number] = None  # Last line

    def _collect_data_values(self, program: ProgramNode):
        """Collect all DATA values"""
        for line in program.lines:
            for stmt in line.statements:
                if isinstance(stmt, DataStatementNode):
                    for value in stmt.values:
                        # Extract actual values from AST nodes
                        if isinstance(value, StringNode):
                            self.data_values.append(value.value)
                            self.data_types.append('string')
                        elif isinstance(value, NumberNode):
                            self.data_values.append(value.value)
                            self.data_types.append('number')
                        elif isinstance(value, str):
                            self.data_values.append(value)
                            self.data_types.append('string')
                        else:
                            # For other types, try to get the value
                            self.data_values.append(value)
                            self.data_types.append('number')

    def _collect_def_fn(self, program: ProgramNode):
        """Collect all DEF FN statements"""
        for line in program.lines:
            for stmt in line.statements:
                if isinstance(stmt, DefFnStatementNode):
                    self.def_fn_functions.append(stmt)

    def _match_for_next(self, program: ProgramNode):
        """Match FOR statements with their corresponding NEXT statements"""
        for_stack = []  # Stack of (line_num, var_name) for nested FOR loops

        for i, line in enumerate(program.lines):
            for stmt_idx, stmt in enumerate(line.statements):
                if isinstance(stmt, ForStatementNode):
                    # Push FOR onto stack
                    var_name = stmt.variable.name.lower()
                    for_stack.append((line.line_number, var_name))
                elif isinstance(stmt, NextStatementNode):
                    # Match NEXT with FOR
                    if stmt.variables:
                        # NEXT with variable(s) - match specific FOR(s)
                        for var in reversed(stmt.variables):
                            var_name = var.name.lower()
                            # Find matching FOR
                            for j in range(len(for_stack) - 1, -1, -1):
                                if for_stack[j][1] == var_name:
                                    for_line = for_stack.pop(j)[0]
                                    # Map FOR line to line after NEXT
                                    next_line = self.next_line_map.get(line.line_number)
                                    self.for_to_after_next[for_line] = next_line
                                    break
                    else:
                        # NEXT without variable - match most recent FOR
                        if for_stack:
                            for_line, var_name = for_stack.pop()
                            next_line = self.next_line_map.get(line.line_number)
                            self.for_to_after_next[for_line] = next_line
                            # Record which variable this NEXT should use
                            self.next_var_map[(line.line_number, stmt_idx)] = var_name

    def _analyze_features(self, program: ProgramNode):
        """Analyze which features the program uses"""
        for line in program.lines:
            for stmt in line.statements:
                if isinstance(stmt, InputStatementNode):
                    if stmt.file_number:
                        self.uses_file_io = True
                    else:
                        self.uses_input = True
                elif isinstance(stmt, LineInputStatementNode):
                    if stmt.file_number:
                        self.uses_file_io = True
                    else:
                        self.uses_line_input = True
                elif isinstance(stmt, (PrintStatementNode, PrintUsingStatementNode)):
                    if stmt.file_number:
                        self.uses_file_io = True
                elif isinstance(stmt, WriteStatementNode):
                    if stmt.file_number:
                        self.uses_file_io = True
                    else:
                        self.uses_write = True
                elif isinstance(stmt, LprintStatementNode):
                    if stmt.file_number:
                        self.uses_file_io = True
                    else:
                        self.uses_lprint = True
                elif isinstance(stmt, (OpenStatementNode, CloseStatementNode, ResetStatementNode)):
                    self.uses_file_io = True
                elif isinstance(stmt, GosubStatementNode):
                    self.uses_gosub = True
                elif isinstance(stmt, ReadStatementNode):
                    self.uses_data = True
                elif isinstance(stmt, RestoreStatementNode):
                    self.uses_data = True
                elif isinstance(stmt, (OnErrorStatementNode, ErrorStatementNode, ResumeStatementNode)):
                    self.uses_error_handling = True
                # Check for RND function in expressions
                self._check_for_rnd(stmt, set())

    def _check_for_rnd(self, node, visited):
        """Recursively check for RND function usage (with cycle detection)"""
        # Avoid infinite recursion on circular references
        node_id = id(node)
        if node_id in visited:
            return
        visited.add(node_id)

        if isinstance(node, FunctionCallNode) and node.name.lower() == 'rnd':
            self.uses_random = True
            return

        # Recursively check children
        if hasattr(node, '__dict__'):
            for attr_name, attr_value in node.__dict__.items():
                # Skip certain attributes that create cycles (like parent pointers, line_num, column, etc.)
                if attr_name in ('line_num', 'column', 'original_case', 'explicit_type_suffix'):
                    continue

                if isinstance(attr_value, list):
                    for item in attr_value:
                        if hasattr(item, '__dict__'):
                            self._check_for_rnd(item, visited)
                elif hasattr(attr_value, '__dict__') and not isinstance(attr_value, (str, int, float, bool)):
                    self._check_for_rnd(attr_value, visited)

    def _generate_runtime(self) -> List[str]:
        """Generate runtime library functions"""
        code = []

        # Error handling
        code.append(self.indent() + 'function _error(msg) {')
        self.indent_level += 1
        code.append(self.indent() + 'throw new Error("?" + msg);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        # Print function
        code.append(self.indent() + 'function _print(str, newline = true) {')
        self.indent_level += 1
        code.append(self.indent() + 'const s = String(str);')
        code.append(self.indent() + 'if (typeof window !== "undefined") {')
        self.indent_level += 1
        code.append(self.indent() + '// Browser')
        code.append(self.indent() + 'const output = document.getElementById("output");')
        code.append(self.indent() + 'if (output) {')
        self.indent_level += 1
        code.append(self.indent() + 'output.textContent += s;')
        code.append(self.indent() + 'if (newline) output.textContent += "\\n";')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        self.indent_level -= 1
        code.append(self.indent() + '} else if (typeof process !== "undefined") {')
        self.indent_level += 1
        code.append(self.indent() + '// Node.js')
        code.append(self.indent() + 'if (newline) {')
        self.indent_level += 1
        code.append(self.indent() + 'console.log(s);')
        self.indent_level -= 1
        code.append(self.indent() + '} else {')
        self.indent_level += 1
        code.append(self.indent() + 'process.stdout.write(s);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        # PRINT USING function (simplified)
        code.append(self.indent() + 'function _print_using(format, values) {')
        self.indent_level += 1
        code.append(self.indent() + '// Simplified PRINT USING - handles basic numeric and string formatting')
        code.append(self.indent() + 'let result = "";')
        code.append(self.indent() + 'let valueIdx = 0;')
        code.append(self.indent() + 'let i = 0;')
        code.append(self.indent() + 'while (i < format.length) {')
        self.indent_level += 1
        code.append(self.indent() + 'const ch = format[i];')
        code.append(self.indent() + 'if (ch === "#") {')
        self.indent_level += 1
        code.append(self.indent() + '// Numeric field - count # symbols')
        code.append(self.indent() + 'let fieldWidth = 0;')
        code.append(self.indent() + 'let decimals = 0;')
        code.append(self.indent() + 'let sawDecimal = false;')
        code.append(self.indent() + 'while (i < format.length && (format[i] === "#" || format[i] === ".")) {')
        self.indent_level += 1
        code.append(self.indent() + 'if (format[i] === ".") sawDecimal = true;')
        code.append(self.indent() + 'else if (sawDecimal) decimals++;')
        code.append(self.indent() + 'else fieldWidth++;')
        code.append(self.indent() + 'i++;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'if (valueIdx < values.length) {')
        self.indent_level += 1
        code.append(self.indent() + 'const val = Number(values[valueIdx++]);')
        code.append(self.indent() + 'result += val.toFixed(decimals).padStart(fieldWidth + decimals + (decimals > 0 ? 1 : 0));')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'continue;')
        self.indent_level -= 1
        code.append(self.indent() + '} else if (ch === "!" && valueIdx < values.length) {')
        self.indent_level += 1
        code.append(self.indent() + '// First character of string')
        code.append(self.indent() + 'result += String(values[valueIdx++]).charAt(0);')
        code.append(self.indent() + 'i++;')
        code.append(self.indent() + 'continue;')
        self.indent_level -= 1
        code.append(self.indent() + '} else if (ch === "&" && valueIdx < values.length) {')
        self.indent_level += 1
        code.append(self.indent() + '// Entire string')
        code.append(self.indent() + 'result += String(values[valueIdx++]);')
        code.append(self.indent() + 'i++;')
        code.append(self.indent() + 'continue;')
        self.indent_level -= 1
        code.append(self.indent() + '} else if (ch === "\\\\" && i + 1 < format.length && format[i + 1] === "\\\\") {')
        self.indent_level += 1
        code.append(self.indent() + '// String field \\\\...\\\\')
        code.append(self.indent() + 'let width = 2;')
        code.append(self.indent() + 'i += 2;')
        code.append(self.indent() + 'while (i < format.length && format[i] !== "\\\\") { width++; i++; }')
        code.append(self.indent() + 'if (i < format.length) i++;')
        code.append(self.indent() + 'if (valueIdx < values.length) {')
        self.indent_level += 1
        code.append(self.indent() + 'result += String(values[valueIdx++]).substring(0, width);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'continue;')
        self.indent_level -= 1
        code.append(self.indent() + '} else {')
        self.indent_level += 1
        code.append(self.indent() + '// Literal character')
        code.append(self.indent() + 'result += ch;')
        code.append(self.indent() + 'i++;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'return result;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        # Math functions
        code.append(self.indent() + '// Math functions')
        code.append(self.indent() + 'const _abs = Math.abs;')
        code.append(self.indent() + 'const _int = Math.floor;')
        code.append(self.indent() + 'const _sqr = Math.sqrt;')
        code.append(self.indent() + 'const _sin = Math.sin;')
        code.append(self.indent() + 'const _cos = Math.cos;')
        code.append(self.indent() + 'const _tan = Math.tan;')
        code.append(self.indent() + 'const _atn = Math.atan;')
        code.append(self.indent() + 'const _log = Math.log;')
        code.append(self.indent() + 'const _exp = Math.exp;')
        code.append(self.indent() + 'function _sgn(n) { return n > 0 ? 1 : n < 0 ? -1 : 0; }')
        code.append(self.indent() + 'function _fix(n) { return n >= 0 ? Math.floor(n) : Math.ceil(n); }')
        code.append(self.indent() + 'function _cint(n) { return Math.round(n); }')
        code.append(self.indent() + 'function _csng(n) { return Math.fround(n); }  // Single precision via ES6')
        code.append(self.indent() + 'function _cdbl(n) { return n; }  // Double precision (already JS number)')
        code.append('')
        code.append(self.indent() + '// Type coercion for DEFINT/DEFSNG/DEFDBL variable assignment')
        code.append(self.indent() + 'function _toInt(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// BASIC INTEGER: truncate toward zero, clamp to -32768..32767')
        code.append(self.indent() + 'n = n >= 0 ? Math.floor(n) : Math.ceil(n);')
        code.append(self.indent() + 'if (n > 32767) return 32767;')
        code.append(self.indent() + 'if (n < -32768) return -32768;')
        code.append(self.indent() + 'return n;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _toSingle(n) { return Math.fround(n); }  // ES6 single-precision')
        code.append(self.indent() + 'function _toDouble(n) { return n; }  // JS Number is already 64-bit double')
        code.append('')

        # RND function
        if self.uses_random:
            code.append(self.indent() + 'let _rnd_last = 0.5;')
            code.append(self.indent() + 'function _rnd(x) {')
            self.indent_level += 1
            code.append(self.indent() + 'if (x === undefined || x > 0) {')
            self.indent_level += 1
            code.append(self.indent() + '_rnd_last = Math.random();')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (x < 0) {')
            self.indent_level += 1
            code.append(self.indent() + '// Seed (can\'t truly seed Math.random)')
            code.append(self.indent() + '_rnd_last = Math.abs(Math.sin(x) * 43758.5453);')
            code.append(self.indent() + '_rnd_last = _rnd_last - Math.floor(_rnd_last);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'return _rnd_last;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # String functions
        code.append(self.indent() + '// String functions')
        code.append(self.indent() + 'function _left(str, n) { return String(str).substring(0, n); }')
        code.append(self.indent() + 'function _right(str, n) { const s = String(str); return s.substring(s.length - n); }')
        code.append(self.indent() + 'function _mid(str, start, len) {')
        self.indent_level += 1
        code.append(self.indent() + 'const s = String(str);')
        code.append(self.indent() + 'if (len === undefined) return s.substring(start - 1);')
        code.append(self.indent() + 'return s.substring(start - 1, start - 1 + len);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _len(str) { return String(str).length; }')
        code.append(self.indent() + 'function _chr(n) { return String.fromCharCode(n); }')
        code.append(self.indent() + 'function _asc(str) { return String(str).charCodeAt(0) || 0; }')
        code.append(self.indent() + 'function _str(n) { return " " + n; }')
        code.append(self.indent() + 'function _val(str) { return parseFloat(String(str).trim()) || 0; }')
        code.append(self.indent() + 'function _instr(start, str1, str2) {')
        self.indent_level += 1
        code.append(self.indent() + '// INSTR with optional start position')
        code.append(self.indent() + 'if (str2 === undefined) {')
        self.indent_level += 1
        code.append(self.indent() + '// Two argument form: INSTR(str1, str2)')
        code.append(self.indent() + 'str2 = str1;')
        code.append(self.indent() + 'str1 = start;')
        code.append(self.indent() + 'start = 1;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'const pos = String(str1).indexOf(String(str2), start - 1);')
        code.append(self.indent() + 'return pos === -1 ? 0 : pos + 1;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _space(n) { return " ".repeat(n); }')
        code.append(self.indent() + 'function _string(n, ch) {')
        self.indent_level += 1
        code.append(self.indent() + '// STRING$(n, code) or STRING$(n, string)')
        code.append(self.indent() + 'const char = typeof ch === "number" ? String.fromCharCode(ch) : String(ch).charAt(0);')
        code.append(self.indent() + 'return char.repeat(n);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _hex(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// HEX$(n) - convert to hexadecimal string')
        code.append(self.indent() + 'return Math.floor(n).toString(16).toUpperCase();')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _oct(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// OCT$(n) - convert to octal string')
        code.append(self.indent() + 'return Math.floor(n).toString(8);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'let _print_pos = 0;  // Track print position for POS()')
        code.append(self.indent() + 'function _pos() {')
        self.indent_level += 1
        code.append(self.indent() + '// POS(0) - current print position (1-based)')
        code.append(self.indent() + '// Note: Simplified - would need to track actual cursor position')
        code.append(self.indent() + 'return _print_pos + 1;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        # Print formatting functions
        code.append(self.indent() + '// Print formatting')
        code.append(self.indent() + 'function _tab(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// TAB(n) - move to column n (1-based)')
        code.append(self.indent() + '// Note: Simplified - just returns spaces')
        code.append(self.indent() + 'return " ".repeat(Math.max(0, n - 1));')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _spc(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// SPC(n) - print n spaces')
        code.append(self.indent() + 'return " ".repeat(Math.max(0, n));')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        # INPUT functions
        if self.uses_input:
            code.append(self.indent() + '// INPUT functions')
            code.append(self.indent() + 'function _input_str(prompt) {')
            self.indent_level += 1
            code.append(self.indent() + 'if (typeof window !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + '// Browser')
            code.append(self.indent() + 'const result = window.prompt(prompt);')
            code.append(self.indent() + 'return result === null ? "" : result;')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (typeof process !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + '// Node.js - synchronous input')
            code.append(self.indent() + 'process.stdout.write(prompt);')
            code.append(self.indent() + 'const readline = require("readline");')
            code.append(self.indent() + 'const rl = readline.createInterface({')
            self.indent_level += 1
            code.append(self.indent() + 'input: process.stdin,')
            code.append(self.indent() + 'output: process.stdout')
            self.indent_level -= 1
            code.append(self.indent() + '});')
            code.append(self.indent() + '// Note: This is async - for true sync input, use readline-sync package')
            code.append(self.indent() + 'return new Promise((resolve) => {')
            self.indent_level += 1
            code.append(self.indent() + 'rl.question("", (answer) => {')
            self.indent_level += 1
            code.append(self.indent() + 'rl.close();')
            code.append(self.indent() + 'resolve(answer);')
            self.indent_level -= 1
            code.append(self.indent() + '});')
            self.indent_level -= 1
            code.append(self.indent() + '});')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'return "";')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')
            code.append(self.indent() + 'function _input_num(prompt) {')
            self.indent_level += 1
            code.append(self.indent() + 'const str = _input_str(prompt);')
            code.append(self.indent() + 'return parseFloat(str) || 0;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')
            code.append(self.indent() + 'function _input_int(prompt) {')
            self.indent_level += 1
            code.append(self.indent() + 'const str = _input_str(prompt);')
            code.append(self.indent() + 'return _toInt(parseFloat(str) || 0);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # LINE INPUT function
        if self.uses_line_input:
            code.append(self.indent() + '// LINE INPUT function - reads entire line without parsing')
            code.append(self.indent() + 'function _line_input(prompt) {')
            self.indent_level += 1
            code.append(self.indent() + 'if (typeof window !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + '// Browser')
            code.append(self.indent() + 'const result = window.prompt(prompt);')
            code.append(self.indent() + 'return result === null ? "" : result;')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (typeof process !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + '// Node.js - synchronous input (same as _input_str)')
            code.append(self.indent() + 'process.stdout.write(prompt);')
            code.append(self.indent() + 'const readline = require("readline");')
            code.append(self.indent() + 'const rl = readline.createInterface({')
            self.indent_level += 1
            code.append(self.indent() + 'input: process.stdin,')
            code.append(self.indent() + 'output: process.stdout')
            self.indent_level -= 1
            code.append(self.indent() + '});')
            code.append(self.indent() + '// Note: This is async - for true sync input, use readline-sync package')
            code.append(self.indent() + 'return new Promise((resolve) => {')
            self.indent_level += 1
            code.append(self.indent() + 'rl.question("", (answer) => {')
            self.indent_level += 1
            code.append(self.indent() + 'rl.close();')
            code.append(self.indent() + 'resolve(answer);')
            self.indent_level -= 1
            code.append(self.indent() + '});')
            self.indent_level -= 1
            code.append(self.indent() + '});')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'return "";')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # WRITE function
        if self.uses_write:
            code.append(self.indent() + '// WRITE function - format values for CSV output')
            code.append(self.indent() + 'function _write_format(value) {')
            self.indent_level += 1
            code.append(self.indent() + '// Strings are quoted, numbers are unquoted')
            code.append(self.indent() + 'if (typeof value === "string") {')
            self.indent_level += 1
            code.append(self.indent() + '// Quote strings and escape internal quotes')
            code.append(self.indent() + 'return \'"\' + String(value).replace(/"/g, \'\\\\"\') + \'"\';')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '// Numbers are unquoted')
            code.append(self.indent() + 'return String(value);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # LPRINT function
        if self.uses_lprint:
            code.append(self.indent() + '// LPRINT function - print to line printer (console.log)')
            code.append(self.indent() + 'function _lprint(str, newline = true) {')
            self.indent_level += 1
            code.append(self.indent() + 'const s = String(str);')
            code.append(self.indent() + 'if (newline) {')
            self.indent_level += 1
            code.append(self.indent() + 'console.log(s);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '// Node.js and modern browsers support this')
            code.append(self.indent() + 'if (typeof process !== "undefined" && process.stdout) {')
            self.indent_level += 1
            code.append(self.indent() + 'process.stdout.write(s);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '// Browser fallback - no good way to avoid newline')
            code.append(self.indent() + 'console.log(s);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # File I/O support
        if self.uses_file_io:
            code.append(self.indent() + '// File I/O support')
            code.append(self.indent() + 'const _files = {};  // Map file number -> file object')
            code.append('')

            # File operations wrapper
            code.append(self.indent() + '// File operations')
            code.append(self.indent() + 'const _fileOps = {')
            self.indent_level += 1

            # Node.js file operations
            code.append(self.indent() + 'nodejs: {')
            self.indent_level += 1
            code.append(self.indent() + 'open: (filenum, filename, mode, reclen) => {')
            self.indent_level += 1
            code.append(self.indent() + 'const fs = require("fs");')
            code.append(self.indent() + 'if (mode === "I") {')
            self.indent_level += 1
            code.append(self.indent() + '// Input mode - read entire file')
            code.append(self.indent() + 'const content = fs.readFileSync(filename, "utf8");')
            code.append(self.indent() + '_files[filenum] = { mode: "I", content, pos: 0, filename };')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (mode === "O") {')
            self.indent_level += 1
            code.append(self.indent() + '// Output mode - write to file')
            code.append(self.indent() + '_files[filenum] = { mode: "O", content: "", filename };')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (mode === "A") {')
            self.indent_level += 1
            code.append(self.indent() + '// Append mode')
            code.append(self.indent() + 'let content = "";')
            code.append(self.indent() + 'try { content = fs.readFileSync(filename, "utf8"); } catch(e) {}')
            code.append(self.indent() + '_files[filenum] = { mode: "A", content, filename };')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (mode === "R") {')
            self.indent_level += 1
            code.append(self.indent() + '// Random access mode')
            code.append(self.indent() + 'const recordLength = reclen || 128;')
            code.append(self.indent() + 'let content = "";')
            code.append(self.indent() + 'try { content = fs.readFileSync(filename, "binary"); } catch(e) {}')
            code.append(self.indent() + '_files[filenum] = {')
            self.indent_level += 1
            code.append(self.indent() + 'mode: "R",')
            code.append(self.indent() + 'content,')
            code.append(self.indent() + 'filename,')
            code.append(self.indent() + 'recordLength,')
            code.append(self.indent() + 'currentRecord: 1,')
            code.append(self.indent() + 'buffer: " ".repeat(recordLength),')
            code.append(self.indent() + 'fields: {}  // Map field variable name -> {start, width}')
            self.indent_level -= 1
            code.append(self.indent() + '};')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_error("Unsupported file mode: " + mode);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '},')

            code.append(self.indent() + 'close: (filenum) => {')
            self.indent_level += 1
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (file && (file.mode === "O" || file.mode === "A")) {')
            self.indent_level += 1
            code.append(self.indent() + 'const fs = require("fs");')
            code.append(self.indent() + 'fs.writeFileSync(file.filename, file.content, "utf8");')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (file && file.mode === "R") {')
            self.indent_level += 1
            code.append(self.indent() + '// Save random file')
            code.append(self.indent() + 'const fs = require("fs");')
            code.append(self.indent() + 'fs.writeFileSync(file.filename, file.content, "binary");')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'delete _files[filenum];')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '},')

            # Browser file operations (using localStorage)
            code.append(self.indent() + 'browser: {')
            self.indent_level += 1
            code.append(self.indent() + 'open: (filenum, filename, mode, reclen) => {')
            self.indent_level += 1
            code.append(self.indent() + 'const key = "mbasic_file_" + filename;')
            code.append(self.indent() + 'if (mode === "I") {')
            self.indent_level += 1
            code.append(self.indent() + 'const content = localStorage.getItem(key) || "";')
            code.append(self.indent() + '_files[filenum] = { mode: "I", content, pos: 0, filename };')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (mode === "O") {')
            self.indent_level += 1
            code.append(self.indent() + '_files[filenum] = { mode: "O", content: "", filename };')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (mode === "A") {')
            self.indent_level += 1
            code.append(self.indent() + 'const content = localStorage.getItem(key) || "";')
            code.append(self.indent() + '_files[filenum] = { mode: "A", content, filename };')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (mode === "R") {')
            self.indent_level += 1
            code.append(self.indent() + '// Random access mode')
            code.append(self.indent() + 'const recordLength = reclen || 128;')
            code.append(self.indent() + 'const content = localStorage.getItem(key) || "";')
            code.append(self.indent() + '_files[filenum] = {')
            self.indent_level += 1
            code.append(self.indent() + 'mode: "R",')
            code.append(self.indent() + 'content,')
            code.append(self.indent() + 'filename,')
            code.append(self.indent() + 'recordLength,')
            code.append(self.indent() + 'currentRecord: 1,')
            code.append(self.indent() + 'buffer: " ".repeat(recordLength),')
            code.append(self.indent() + 'fields: {}  // Map field variable name -> {start, width}')
            self.indent_level -= 1
            code.append(self.indent() + '};')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_error("Unsupported file mode: " + mode);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '},')

            code.append(self.indent() + 'close: (filenum) => {')
            self.indent_level += 1
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (file && (file.mode === "O" || file.mode === "A" || file.mode === "R")) {')
            self.indent_level += 1
            code.append(self.indent() + 'const key = "mbasic_file_" + file.filename;')
            code.append(self.indent() + 'localStorage.setItem(key, file.content);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'delete _files[filenum];')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '};')
            code.append('')

            # Helper functions
            code.append(self.indent() + 'function _fopen(filenum, filename, mode, reclen) {')
            self.indent_level += 1
            code.append(self.indent() + 'if (typeof process !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + '_fileOps.nodejs.open(filenum, filename, mode, reclen);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_fileOps.browser.open(filenum, filename, mode, reclen);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _fclose(filenum) {')
            self.indent_level += 1
            code.append(self.indent() + 'if (filenum === undefined) {')
            self.indent_level += 1
            code.append(self.indent() + '// CLOSE with no arguments - close all files')
            code.append(self.indent() + 'for (const fn in _files) {')
            self.indent_level += 1
            code.append(self.indent() + '_fclose(parseInt(fn));')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + 'if (typeof process !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + '_fileOps.nodejs.close(filenum);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_fileOps.browser.close(filenum);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _fprint(filenum, str, newline = true) {')
            self.indent_level += 1
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'if (file.mode !== "O" && file.mode !== "A") _error("File #" + filenum + " not open for output");')
            code.append(self.indent() + 'file.content += String(str);')
            code.append(self.indent() + 'if (newline) file.content += "\\n";')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _finput(filenum) {')
            self.indent_level += 1
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'if (file.mode !== "I") _error("File #" + filenum + " not open for input");')
            code.append(self.indent() + '// Read one value (comma or newline delimited)')
            code.append(self.indent() + 'let value = "";')
            code.append(self.indent() + 'while (file.pos < file.content.length) {')
            self.indent_level += 1
            code.append(self.indent() + 'const ch = file.content[file.pos++];')
            code.append(self.indent() + 'if (ch === "," || ch === "\\n") break;')
            code.append(self.indent() + 'value += ch;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'return value.trim();')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _fline_input(filenum) {')
            self.indent_level += 1
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'if (file.mode !== "I") _error("File #" + filenum + " not open for input");')
            code.append(self.indent() + '// Read one line')
            code.append(self.indent() + 'let line = "";')
            code.append(self.indent() + 'while (file.pos < file.content.length) {')
            self.indent_level += 1
            code.append(self.indent() + 'const ch = file.content[file.pos++];')
            code.append(self.indent() + 'if (ch === "\\n") break;')
            code.append(self.indent() + 'line += ch;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'return line;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            # File position functions
            code.append(self.indent() + 'function _eof(filenum) {')
            self.indent_level += 1
            code.append(self.indent() + '// EOF() - Test for end of file')
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'if (file.mode !== "I") _error("File #" + filenum + " not open for input");')
            code.append(self.indent() + 'return file.pos >= file.content.length ? -1 : 0;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _lof(filenum) {')
            self.indent_level += 1
            code.append(self.indent() + '// LOF() - Length of file in bytes')
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'return file.content.length;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _loc(filenum) {')
            self.indent_level += 1
            code.append(self.indent() + '// LOC() - Current position in file')
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'return file.pos || 0;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            # File management functions
            code.append(self.indent() + 'function _fkill(filename) {')
            self.indent_level += 1
            code.append(self.indent() + '// KILL - Delete file')
            code.append(self.indent() + 'if (typeof process !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + 'const fs = require("fs");')
            code.append(self.indent() + 'try {')
            self.indent_level += 1
            code.append(self.indent() + 'fs.unlinkSync(filename);')
            self.indent_level -= 1
            code.append(self.indent() + '} catch (e) {')
            self.indent_level += 1
            code.append(self.indent() + '_error("Cannot delete file: " + filename);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '// Browser - remove from localStorage')
            code.append(self.indent() + 'const key = "mbasic_file_" + filename;')
            code.append(self.indent() + 'if (localStorage.getItem(key) !== null) {')
            self.indent_level += 1
            code.append(self.indent() + 'localStorage.removeItem(key);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_error("File not found: " + filename);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _fname(oldname, newname) {')
            self.indent_level += 1
            code.append(self.indent() + '// NAME - Rename file')
            code.append(self.indent() + 'if (typeof process !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + 'const fs = require("fs");')
            code.append(self.indent() + 'try {')
            self.indent_level += 1
            code.append(self.indent() + 'fs.renameSync(oldname, newname);')
            self.indent_level -= 1
            code.append(self.indent() + '} catch (e) {')
            self.indent_level += 1
            code.append(self.indent() + '_error("Cannot rename file: " + oldname);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '// Browser - rename in localStorage')
            code.append(self.indent() + 'const oldkey = "mbasic_file_" + oldname;')
            code.append(self.indent() + 'const newkey = "mbasic_file_" + newname;')
            code.append(self.indent() + 'const content = localStorage.getItem(oldkey);')
            code.append(self.indent() + 'if (content !== null) {')
            self.indent_level += 1
            code.append(self.indent() + 'localStorage.setItem(newkey, content);')
            code.append(self.indent() + 'localStorage.removeItem(oldkey);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_error("File not found: " + oldname);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _ffiles(filespec) {')
            self.indent_level += 1
            code.append(self.indent() + '// FILES - List directory')
            code.append(self.indent() + 'if (typeof process !== "undefined") {')
            self.indent_level += 1
            code.append(self.indent() + 'const fs = require("fs");')
            code.append(self.indent() + 'try {')
            self.indent_level += 1
            code.append(self.indent() + 'const files = fs.readdirSync(".");')
            code.append(self.indent() + '// Simple pattern matching (* and ?)')
            code.append(self.indent() + 'const pattern = filespec.replace(/\\*/g, ".*").replace(/\\?/g, ".");')
            code.append(self.indent() + 'const regex = new RegExp("^" + pattern + "$", "i");')
            code.append(self.indent() + 'const matches = files.filter(f => regex.test(f));')
            code.append(self.indent() + 'if (matches.length > 0) {')
            self.indent_level += 1
            code.append(self.indent() + 'console.log(matches.join("  "));')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + 'console.log("File not found");')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '} catch (e) {')
            self.indent_level += 1
            code.append(self.indent() + '_error("Cannot list directory");')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '// Browser - list files in localStorage')
            code.append(self.indent() + 'const prefix = "mbasic_file_";')
            code.append(self.indent() + 'const files = [];')
            code.append(self.indent() + 'for (let i = 0; i < localStorage.length; i++) {')
            self.indent_level += 1
            code.append(self.indent() + 'const key = localStorage.key(i);')
            code.append(self.indent() + 'if (key.startsWith(prefix)) {')
            self.indent_level += 1
            code.append(self.indent() + 'files.push(key.substring(prefix.length));')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'if (files.length > 0) {')
            self.indent_level += 1
            code.append(self.indent() + 'console.log(files.join("  "));')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + 'console.log("File not found");')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # Random file access functions
        if self.uses_file_io:
            code.append(self.indent() + '// Random file access functions')
            code.append(self.indent() + 'function _ffield(filenum, fields) {')
            self.indent_level += 1
            code.append(self.indent() + '// FIELD - Define buffer layout for random file')
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'if (file.mode !== "R") _error("File #" + filenum + " not open for random access");')
            code.append(self.indent() + 'let pos = 0;')
            code.append(self.indent() + 'for (const [width, varname] of fields) {')
            self.indent_level += 1
            code.append(self.indent() + 'file.fields[varname] = { start: pos, width: width };')
            code.append(self.indent() + 'pos += width;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _flset(varname, value) {')
            self.indent_level += 1
            code.append(self.indent() + '// LSET - Left-justify string in field variable')
            code.append(self.indent() + '// Find which file has this field variable')
            code.append(self.indent() + 'for (const filenum in _files) {')
            self.indent_level += 1
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (file.mode === "R" && file.fields[varname]) {')
            self.indent_level += 1
            code.append(self.indent() + 'const field = file.fields[varname];')
            code.append(self.indent() + 'const str = String(value).substring(0, field.width).padEnd(field.width, " ");')
            code.append(self.indent() + 'file.buffer = file.buffer.substring(0, field.start) + str + file.buffer.substring(field.start + field.width);')
            code.append(self.indent() + 'return;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + '_error("Field variable not found: " + varname);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _frset(varname, value) {')
            self.indent_level += 1
            code.append(self.indent() + '// RSET - Right-justify string in field variable')
            code.append(self.indent() + 'for (const filenum in _files) {')
            self.indent_level += 1
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (file.mode === "R" && file.fields[varname]) {')
            self.indent_level += 1
            code.append(self.indent() + 'const field = file.fields[varname];')
            code.append(self.indent() + 'const str = String(value).substring(0, field.width).padStart(field.width, " ");')
            code.append(self.indent() + 'file.buffer = file.buffer.substring(0, field.start) + str + file.buffer.substring(field.start + field.width);')
            code.append(self.indent() + 'return;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + '_error("Field variable not found: " + varname);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _fget(filenum, recnum) {')
            self.indent_level += 1
            code.append(self.indent() + '// GET - Read record from random file into buffer')
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'if (file.mode !== "R") _error("File #" + filenum + " not open for random access");')
            code.append(self.indent() + 'const recordNum = recnum !== undefined ? recnum : file.currentRecord;')
            code.append(self.indent() + 'const pos = (recordNum - 1) * file.recordLength;')
            code.append(self.indent() + 'if (pos < 0) _error("Invalid record number");')
            code.append(self.indent() + '// Read record from file content')
            code.append(self.indent() + 'if (pos >= file.content.length) {')
            self.indent_level += 1
            code.append(self.indent() + '// Beyond end of file - fill buffer with spaces')
            code.append(self.indent() + 'file.buffer = " ".repeat(file.recordLength);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + 'file.buffer = file.content.substring(pos, pos + file.recordLength).padEnd(file.recordLength, " ");')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + '// Update field variables from buffer')
            code.append(self.indent() + 'for (const varname in file.fields) {')
            self.indent_level += 1
            code.append(self.indent() + 'const field = file.fields[varname];')
            code.append(self.indent() + 'const value = file.buffer.substring(field.start, field.start + field.width);')
            code.append(self.indent() + 'if (typeof window !== "undefined") window[varname] = value;')
            code.append(self.indent() + 'else global[varname] = value;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'file.currentRecord = recordNum + 1;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

            code.append(self.indent() + 'function _fput(filenum, recnum) {')
            self.indent_level += 1
            code.append(self.indent() + '// PUT - Write buffer to record in random file')
            code.append(self.indent() + 'const file = _files[filenum];')
            code.append(self.indent() + 'if (!file) _error("File #" + filenum + " not open");')
            code.append(self.indent() + 'if (file.mode !== "R") _error("File #" + filenum + " not open for random access");')
            code.append(self.indent() + 'const recordNum = recnum !== undefined ? recnum : file.currentRecord;')
            code.append(self.indent() + 'const pos = (recordNum - 1) * file.recordLength;')
            code.append(self.indent() + 'if (pos < 0) _error("Invalid record number");')
            code.append(self.indent() + '// Extend file content if necessary')
            code.append(self.indent() + 'if (pos + file.recordLength > file.content.length) {')
            self.indent_level += 1
            code.append(self.indent() + 'file.content = file.content.padEnd(pos + file.recordLength, " ");')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + '// Write buffer to file content')
            code.append(self.indent() + 'file.content = file.content.substring(0, pos) + file.buffer + file.content.substring(pos + file.recordLength);')
            code.append(self.indent() + 'file.currentRecord = recordNum + 1;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # GOSUB/RETURN
        if self.uses_gosub:
            code.append(self.indent() + '// GOSUB/RETURN stack')
            code.append(self.indent() + 'const _gosub_stack = [];')
            code.append(self.indent() + 'function _gosub(return_line) {')
            self.indent_level += 1
            code.append(self.indent() + '_gosub_stack.push(return_line);')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'function _return() {')
            self.indent_level += 1
            code.append(self.indent() + 'if (_gosub_stack.length === 0) _error("RETURN without GOSUB");')
            code.append(self.indent() + 'return _gosub_stack.pop();')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # FOR/NEXT loop state (variable-indexed approach)
        code.append(self.indent() + '// FOR/NEXT loop state')
        code.append(self.indent() + 'const _for_loops = {};  // Map var name -> {end, step, line}')
        code.append('')

        # Error handling
        if self.uses_error_handling:
            code.append(self.indent() + '// Error handling')
            code.append(self.indent() + 'let _error_handler = null;  // Line number of error handler (0 = disabled)')
            code.append(self.indent() + 'let _error_handler_is_gosub = false;  // True if ON ERROR GOSUB')
            code.append(self.indent() + 'let _error_line = 0;  // Line where error occurred (ERL)')
            code.append(self.indent() + 'let _error_code = 0;  // Error code (ERR)')
            code.append(self.indent() + 'let _in_error_handler = false;  // Prevent recursive errors')
            code.append(self.indent() + 'function _trigger_error(code, line) {')
            self.indent_level += 1
            code.append(self.indent() + 'if (_in_error_handler) {')
            self.indent_level += 1
            code.append(self.indent() + '_error("Error in error handler");')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + '_error_code = code;')
            code.append(self.indent() + '_error_line = line;')
            code.append(self.indent() + 'if (_error_handler === null) {')
            self.indent_level += 1
            code.append(self.indent() + '_error(`Error ${code} at line ${line}`);')
            self.indent_level -= 1
            code.append(self.indent() + '} else if (_error_handler === 0) {')
            self.indent_level += 1
            code.append(self.indent() + '_error(`Error ${code} at line ${line}`);')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_in_error_handler = true;')
            code.append(self.indent() + 'if (_error_handler_is_gosub) {')
            self.indent_level += 1
            code.append(self.indent() + '_gosub(line);  // Return to line that caused error')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'return _error_handler;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'function _erl() { return _error_line; }')
            code.append(self.indent() + 'function _err() { return _error_code; }')
            code.append('')

        # DATA/READ/RESTORE
        if self.uses_data or self.data_values:
            code.append(self.indent() + '// DATA/READ/RESTORE')
            code.append(self.indent() + f'const _data = {self._format_data_array()};')
            code.append(self.indent() + 'let _data_ptr = 0;')
            code.append(self.indent() + 'function _read() {')
            self.indent_level += 1
            code.append(self.indent() + 'if (_data_ptr >= _data.length) _error("Out of DATA");')
            code.append(self.indent() + 'return _data[_data_ptr++];')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'function _restore() {')
            self.indent_level += 1
            code.append(self.indent() + '_data_ptr = 0;')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append('')

        # Binary data conversion functions (MKI$/MKS$/MKD$, CVI/CVS/CVD)
        code.append(self.indent() + '// Binary data conversion functions')
        code.append(self.indent() + 'function _mki(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// MKI$ - Convert integer to 2-byte string')
        code.append(self.indent() + 'n = _toInt(n);')
        code.append(self.indent() + 'if (n < 0) n = 65536 + n;  // Two\'s complement')
        code.append(self.indent() + 'return String.fromCharCode(n & 0xFF, (n >> 8) & 0xFF);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _mks(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// MKS$ - Convert single to 4-byte string')
        code.append(self.indent() + 'const buf = new ArrayBuffer(4);')
        code.append(self.indent() + 'new DataView(buf).setFloat32(0, n, true);  // little-endian')
        code.append(self.indent() + 'const arr = new Uint8Array(buf);')
        code.append(self.indent() + 'return String.fromCharCode(...arr);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _mkd(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// MKD$ - Convert double to 8-byte string')
        code.append(self.indent() + 'const buf = new ArrayBuffer(8);')
        code.append(self.indent() + 'new DataView(buf).setFloat64(0, n, true);  // little-endian')
        code.append(self.indent() + 'const arr = new Uint8Array(buf);')
        code.append(self.indent() + 'return String.fromCharCode(...arr);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _cvi(s) {')
        self.indent_level += 1
        code.append(self.indent() + '// CVI - Convert 2-byte string to integer')
        code.append(self.indent() + 'let n = s.charCodeAt(0) | (s.charCodeAt(1) << 8);')
        code.append(self.indent() + 'if (n >= 32768) n = n - 65536;  // Two\'s complement')
        code.append(self.indent() + 'return n;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _cvs(s) {')
        self.indent_level += 1
        code.append(self.indent() + '// CVS - Convert 4-byte string to single')
        code.append(self.indent() + 'const buf = new ArrayBuffer(4);')
        code.append(self.indent() + 'const arr = new Uint8Array(buf);')
        code.append(self.indent() + 'for (let i = 0; i < 4; i++) arr[i] = s.charCodeAt(i) || 0;')
        code.append(self.indent() + 'return new DataView(buf).getFloat32(0, true);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _cvd(s) {')
        self.indent_level += 1
        code.append(self.indent() + '// CVD - Convert 8-byte string to double')
        code.append(self.indent() + 'const buf = new ArrayBuffer(8);')
        code.append(self.indent() + 'const arr = new Uint8Array(buf);')
        code.append(self.indent() + 'for (let i = 0; i < 8; i++) arr[i] = s.charCodeAt(i) || 0;')
        code.append(self.indent() + 'return new DataView(buf).getFloat64(0, true);')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        # Memory and system functions (stubs - no direct hardware in JS)
        code.append(self.indent() + '// Memory/system functions (stubs - no hardware access in JavaScript)')
        code.append(self.indent() + 'function _fre(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// FRE - Returns free memory (stub: always returns 32767)')
        code.append(self.indent() + 'return 32767;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _peek(addr) {')
        self.indent_level += 1
        code.append(self.indent() + '// PEEK - Read memory location (returns random byte like interpreter)')
        code.append(self.indent() + 'return (Math.random() * 256) | 0;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _inp(port) {')
        self.indent_level += 1
        code.append(self.indent() + '// INP - Read I/O port (stub: returns 0)')
        code.append(self.indent() + 'console.warn("INP not supported in JavaScript - returning 0");')
        code.append(self.indent() + 'return 0;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _varptr(v) {')
        self.indent_level += 1
        code.append(self.indent() + '// VARPTR - Get variable address (stub: returns 0)')
        code.append(self.indent() + 'console.warn("VARPTR not supported in JavaScript - returning 0");')
        code.append(self.indent() + 'return 0;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'function _usr(n) {')
        self.indent_level += 1
        code.append(self.indent() + '// USR - Call machine code routine (stub: returns argument)')
        code.append(self.indent() + 'console.warn("USR not supported in JavaScript - returning argument");')
        code.append(self.indent() + 'return n;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        # INKEY$ function
        code.append(self.indent() + '// INKEY$ - Get key without waiting')
        code.append(self.indent() + 'let _inkey_buffer = "";')
        code.append(self.indent() + 'function _inkey() {')
        self.indent_level += 1
        code.append(self.indent() + '// Returns empty string if no key pressed, otherwise the key')
        code.append(self.indent() + 'if (_inkey_buffer.length > 0) {')
        self.indent_level += 1
        code.append(self.indent() + 'const key = _inkey_buffer[0];')
        code.append(self.indent() + '_inkey_buffer = _inkey_buffer.slice(1);')
        code.append(self.indent() + 'return key;')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + 'return "";')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append(self.indent() + '// Setup keyboard handler for browser')
        code.append(self.indent() + 'if (typeof window !== "undefined") {')
        self.indent_level += 1
        code.append(self.indent() + 'document.addEventListener("keypress", (e) => {')
        self.indent_level += 1
        code.append(self.indent() + '_inkey_buffer += e.key;')
        self.indent_level -= 1
        code.append(self.indent() + '});')
        self.indent_level -= 1
        code.append(self.indent() + '}')
        code.append('')

        return code

    def _format_data_array(self) -> str:
        """Format DATA values as JavaScript array literal"""
        if not self.data_values:
            return '[]'

        items = []
        for value, vtype in zip(self.data_values, self.data_types):
            if vtype == 'string':
                items.append(f'"{self._escape_string(value)}"')
            else:
                items.append(str(value))

        return '[' + ', '.join(items) + ']'

    def _escape_string(self, s: str) -> str:
        """Escape string for JavaScript"""
        # Handle StringNode if passed by mistake
        if isinstance(s, StringNode):
            s = s.value
        return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r').replace('\t', '\\t')

    def _generate_variables(self) -> List[str]:
        """Generate variable declarations"""
        code = []

        for var_name, var_info in sorted(self.symbols.variables.items()):
            # Use simple lowercase name - type coercion happens at assignment time
            js_name = self._mangle_var_name(var_name)

            # Skip arrays - they're handled separately
            if var_info.is_array:
                # Generate array initialization
                dimensions = var_info.dimensions or [10]  # Default size
                if var_info.var_type == VarType.STRING:
                    init_value = '""'
                else:
                    init_value = '0'
                code.append(self.indent() + f'let {js_name} = {self._create_array_init(dimensions, init_value)};')
            else:
                # Regular variable
                if var_info.var_type == VarType.STRING:
                    code.append(self.indent() + f'let {js_name} = "";')
                elif var_info.var_type == VarType.INTEGER:
                    code.append(self.indent() + f'let {js_name} = 0;')
                else:  # SINGLE or DOUBLE
                    code.append(self.indent() + f'let {js_name} = 0;')

        return code

    def _create_array_init(self, dimensions: List[int], init_value: str) -> str:
        """Create nested array initialization"""
        if len(dimensions) == 1:
            size = dimensions[0] + 1  # BASIC arrays are 0-based with OPTION BASE
            return f'new Array({size}).fill({init_value})'
        else:
            size = dimensions[0] + 1
            inner = self._create_array_init(dimensions[1:], init_value)
            return f'Array.from({{length: {size}}}, () => {inner})'

    def _mangle_var_name(self, name: str) -> str:
        """Convert BASIC variable name to JavaScript identifier.

        In BASIC, FOO%, FOO!, FOO#, FOO$ are all DIFFERENT variables.
        We add type suffixes to distinguish them:
        - FOO (SINGLE/default) -> foo
        - FOO% (INTEGER) -> foo_int
        - FOO# (DOUBLE) -> foo_dbl
        - FOO$ (STRING) -> foo_str
        """
        # Remove type suffix to get base name
        base_name = name.rstrip('$%!#')

        # Make lowercase for consistency
        js_name = base_name.lower()

        # Add type suffix to distinguish different typed variables
        if name.endswith('$'):
            js_name = js_name + '_str'
        elif name.endswith('%'):
            js_name = js_name + '_int'
        elif name.endswith('#'):
            js_name = js_name + '_dbl'
        # SINGLE (! or no suffix) gets no suffix - it's the default

        # Prefix to avoid JS keyword conflicts
        if js_name in ('if', 'for', 'while', 'return', 'function', 'var', 'let', 'const',
                       'class', 'new', 'delete', 'typeof', 'void', 'this', 'with',
                       'switch', 'case', 'default', 'break', 'continue', 'throw', 'try',
                       'catch', 'finally', 'do', 'in', 'of', 'import', 'export', 'null'):
            js_name = 'v_' + js_name

        return js_name

    def _generate_def_fn_functions(self) -> List[str]:
        """Generate DEF FN functions"""
        code = []

        for def_fn in self.def_fn_functions:
            # Function name - DEF FNA becomes _fn_a
            func_name = f'_fn_{def_fn.name.lower()}'

            # Parameters
            params = [self._mangle_var_name(p.name + (p.type_suffix or '')) for p in def_fn.parameters]
            param_str = ', '.join(params)

            # Function body
            expr_code = self._generate_expression(def_fn.expression)

            code.append(self.indent() + f'function {func_name}({param_str}) {{')
            self.indent_level += 1
            code.append(self.indent() + f'return {expr_code};')
            self.indent_level -= 1
            code.append(self.indent() + '}')

        return code

    def _generate_line(self, line: LineNode) -> List[str]:
        """Generate code for one line"""
        code = []

        # Case label for this line
        code.append(self.indent() + f'case {line.line_number}:')
        self.indent_level += 1

        # Generate statements
        for i, stmt in enumerate(line.statements):
            stmt_code = self._generate_statement(stmt, line.line_number, i, len(line.statements))
            code.extend(stmt_code)

            # Check if statement changed control flow (GOTO, RETURN, END, etc.)
            if self._is_control_flow_statement(stmt):
                # Control flow statement handles PC itself
                self.indent_level -= 1
                return code

        # Default: continue to next line
        next_line = self.next_line_map.get(line.line_number)
        if next_line is not None:
            code.append(self.indent() + f'_pc = {next_line};')
        else:
            code.append(self.indent() + '_pc = null;')
        code.append(self.indent() + 'break;')

        self.indent_level -= 1

        return code

    def _is_control_flow_statement(self, stmt) -> bool:
        """Check if statement changes control flow"""
        return isinstance(stmt, (
            GotoStatementNode,
            OnGotoStatementNode,
            OnGosubStatementNode,
            GosubStatementNode,
            ReturnStatementNode,
            EndStatementNode,
            ResumeStatementNode,
        ))

    def _generate_statement(self, stmt, line_number: int, stmt_index: int, total_stmts: int) -> List[str]:
        """Generate code for a statement"""
        code = []

        if isinstance(stmt, PrintStatementNode):
            code.extend(self._generate_print(stmt))
        elif isinstance(stmt, PrintUsingStatementNode):
            code.extend(self._generate_print_using(stmt))
        elif isinstance(stmt, LetStatementNode):
            code.extend(self._generate_let(stmt))
        elif isinstance(stmt, EndStatementNode):
            code.append(self.indent() + '_pc = null;')
            code.append(self.indent() + 'return;')
        elif isinstance(stmt, GotoStatementNode):
            code.extend(self._generate_goto(stmt))
        elif isinstance(stmt, OnGotoStatementNode):
            code.extend(self._generate_on_goto(stmt))
        elif isinstance(stmt, OnGosubStatementNode):
            # Calculate return address for ON GOSUB
            is_last_stmt = (stmt_index == total_stmts - 1)
            if is_last_stmt:
                return_address = self.next_line_map.get(line_number, None)
            else:
                return_address = line_number  # Next statement on same line
            code.extend(self._generate_on_gosub(stmt, return_address))
        elif isinstance(stmt, GosubStatementNode):
            # Calculate return address - same logic as FOR loop
            is_last_stmt = (stmt_index == total_stmts - 1)
            if is_last_stmt:
                return_address = self.next_line_map.get(line_number, None)
            else:
                return_address = line_number  # Next statement on same line
            code.extend(self._generate_gosub(stmt, return_address))
        elif isinstance(stmt, ReturnStatementNode):
            code.extend(self._generate_return(stmt))
        elif isinstance(stmt, ForStatementNode):
            # Calculate return line for FOR loop
            is_last_stmt = (stmt_index == total_stmts - 1)
            if is_last_stmt:
                return_line = self.next_line_map.get(line_number, None)
            else:
                return_line = line_number  # Next statement on same line
            code.extend(self._generate_for(stmt, line_number, return_line))
        elif isinstance(stmt, NextStatementNode):
            code.extend(self._generate_next(stmt, line_number, stmt_index))
        elif isinstance(stmt, WhileStatementNode):
            code.extend(self._generate_while(stmt))
        elif isinstance(stmt, WendStatementNode):
            code.extend(self._generate_wend(stmt))
        elif isinstance(stmt, IfStatementNode):
            code.extend(self._generate_if(stmt, line_number))
        elif isinstance(stmt, InputStatementNode):
            code.extend(self._generate_input(stmt))
        elif isinstance(stmt, LineInputStatementNode):
            code.extend(self._generate_line_input(stmt))
        elif isinstance(stmt, WriteStatementNode):
            code.extend(self._generate_write(stmt))
        elif isinstance(stmt, LprintStatementNode):
            code.extend(self._generate_lprint(stmt))
        elif isinstance(stmt, ReadStatementNode):
            code.extend(self._generate_read(stmt))
        elif isinstance(stmt, RestoreStatementNode):
            code.extend(self._generate_restore(stmt))
        elif isinstance(stmt, RandomizeStatementNode):
            code.extend(self._generate_randomize(stmt))
        elif isinstance(stmt, StopStatementNode):
            code.extend(self._generate_stop(stmt))
        elif isinstance(stmt, SwapStatementNode):
            code.extend(self._generate_swap(stmt))
        elif isinstance(stmt, MidAssignmentStatementNode):
            code.extend(self._generate_mid_assignment(stmt))
        elif isinstance(stmt, EraseStatementNode):
            code.extend(self._generate_erase(stmt))
        elif isinstance(stmt, OpenStatementNode):
            code.extend(self._generate_open(stmt))
        elif isinstance(stmt, CloseStatementNode):
            code.extend(self._generate_close(stmt))
        elif isinstance(stmt, ResetStatementNode):
            code.extend(self._generate_reset(stmt))
        elif isinstance(stmt, KillStatementNode):
            code.extend(self._generate_kill(stmt))
        elif isinstance(stmt, NameStatementNode):
            code.extend(self._generate_name(stmt))
        elif isinstance(stmt, FilesStatementNode):
            code.extend(self._generate_files(stmt))
        elif isinstance(stmt, FieldStatementNode):
            code.extend(self._generate_field(stmt))
        elif isinstance(stmt, LsetStatementNode):
            code.extend(self._generate_lset(stmt))
        elif isinstance(stmt, RsetStatementNode):
            code.extend(self._generate_rset(stmt))
        elif isinstance(stmt, GetStatementNode):
            code.extend(self._generate_get(stmt))
        elif isinstance(stmt, PutStatementNode):
            code.extend(self._generate_put(stmt))
        elif isinstance(stmt, ChainStatementNode):
            code.extend(self._generate_chain(stmt))
        elif isinstance(stmt, OnErrorStatementNode):
            code.extend(self._generate_on_error(stmt))
        elif isinstance(stmt, ErrorStatementNode):
            code.extend(self._generate_error(stmt, line_number))
        elif isinstance(stmt, ResumeStatementNode):
            code.extend(self._generate_resume(stmt, line_number))
        elif isinstance(stmt, DataStatementNode):
            # DATA statements are processed during initialization
            pass
        elif isinstance(stmt, DimStatementNode):
            # DIM statements are processed during variable generation
            pass
        elif isinstance(stmt, RemarkStatementNode):
            # Comments are skipped
            pass
        elif isinstance(stmt, PokeStatementNode):
            code.extend(self._generate_poke(stmt))
        elif isinstance(stmt, OutStatementNode):
            code.extend(self._generate_out(stmt))
        elif isinstance(stmt, WaitStatementNode):
            code.extend(self._generate_wait(stmt))
        elif isinstance(stmt, CallStatementNode):
            code.extend(self._generate_call(stmt))
        elif isinstance(stmt, WidthStatementNode):
            code.extend(self._generate_width(stmt))
        elif isinstance(stmt, ClearStatementNode):
            code.extend(self._generate_clear(stmt))
        elif isinstance(stmt, CommonStatementNode):
            code.extend(self._generate_common(stmt))
        elif isinstance(stmt, DefTypeStatementNode):
            # DEF type statements are processed during variable analysis
            pass
        else:
            code.append(self.indent() + f'// Unsupported: {type(stmt).__name__}')

        return code

    def _generate_print(self, stmt: PrintStatementNode) -> List[str]:
        """Generate PRINT statement"""
        code = []

        # Choose print function based on file number
        if stmt.file_number:
            filenum_expr = self._generate_expression(stmt.file_number)
            print_func = f'_fprint({filenum_expr}, '
        else:
            print_func = '_print('

        if not stmt.expressions:
            # Empty PRINT - just newline
            code.append(self.indent() + f'{print_func}"");')
        else:
            for i, expr in enumerate(stmt.expressions):
                is_last = (i == len(stmt.expressions) - 1)
                separator = stmt.separators[i] if i < len(stmt.separators) else None

                expr_code = self._generate_expression(expr)

                # Determine if we should add a newline
                # Newline if last item and no separator (None or '\n' means end of statement)
                newline = 'true' if (is_last and separator in (None, '\n')) else 'false'

                code.append(self.indent() + f'{print_func}{expr_code}, {newline});')

                # Handle separator (comma for tab, semicolon for no space)
                if separator == ',':
                    code.append(self.indent() + f'{print_func}"\\t", false);')
                elif separator == ';':
                    # Semicolon means no space - already handled
                    pass

        return code

    def _generate_print_using(self, stmt: PrintUsingStatementNode) -> List[str]:
        """Generate PRINT USING statement"""
        code = []

        # Skip file I/O for now
        if stmt.file_number:
            code.append(self.indent() + '// PRINT #file, USING not supported')
            return code

        format_expr = self._generate_expression(stmt.format_string)

        # Generate array of expressions to format
        expr_codes = [self._generate_expression(expr) for expr in stmt.expressions]
        expr_array = '[' + ', '.join(expr_codes) + ']'

        # Call _print_using runtime function
        code.append(self.indent() + f'_print(_print_using({format_expr}, {expr_array}));')

        return code

    def _generate_let(self, stmt: LetStatementNode) -> List[str]:
        """Generate LET statement (assignment)"""
        code = []

        # Get variable type for coercion
        var_key = stmt.variable.name + (stmt.variable.type_suffix or '')
        var_type = self._get_var_type(var_key)

        # Check if assigning to array element
        if stmt.variable.subscripts:
            var_name = self._mangle_var_name(var_key)
            access = var_name
            for idx_expr in stmt.variable.subscripts:
                idx = self._generate_expression(idx_expr)
                access = f'{access}[{idx}]'
            expr_code = self._generate_expression(stmt.expression)
            expr_code = self._apply_type_coercion(expr_code, var_type)
            code.append(self.indent() + f'{access} = {expr_code};')
        else:
            var_name = self._mangle_var_name(var_key)
            expr_code = self._generate_expression(stmt.expression)
            expr_code = self._apply_type_coercion(expr_code, var_type)
            code.append(self.indent() + f'{var_name} = {expr_code};')

        return code

    def _get_var_type(self, var_key: str) -> Optional[VarType]:
        """Look up variable type from symbol table.

        Symbol table uses uppercase names, so we uppercase the lookup key.
        """
        # Symbol table uses uppercase
        upper_key = var_key.upper()
        var_info = self.symbols.variables.get(upper_key)
        if var_info:
            return var_info.var_type
        # Check without suffix (might be stored differently)
        base_name = upper_key.rstrip('$%!#')
        var_info = self.symbols.variables.get(base_name)
        if var_info:
            return var_info.var_type
        return None

    def _apply_type_coercion(self, expr_code: str, var_type) -> str:
        """Wrap expression with type coercion function based on target variable type.

        Note: We compare by .name attribute rather than identity to handle the case
        where VarType is imported from different module paths (e.g., 'semantic_analyzer'
        vs 'src.semantic_analyzer'). This can happen due to Python's module caching
        when the same file is imported via different paths.
        """
        if var_type is None:
            return expr_code
        # Compare by name to handle cross-module enum comparison
        type_name = var_type.name if hasattr(var_type, 'name') else str(var_type)
        if type_name == 'INTEGER':
            return f'_toInt({expr_code})'
        elif type_name == 'SINGLE':
            return f'_toSingle({expr_code})'
        elif type_name == 'DOUBLE':
            # No coercion needed - JS Number is already 64-bit double
            return expr_code
        elif type_name == 'STRING':
            # String assignments handled separately
            return expr_code
        return expr_code

    def _generate_expression(self, expr) -> str:
        """Generate JavaScript expression"""
        if isinstance(expr, NumberNode):
            return str(expr.value)
        elif isinstance(expr, StringNode):
            return f'"{self._escape_string(expr.value)}"'
        elif isinstance(expr, VariableNode):
            # Check if it's an array access
            if expr.subscripts:
                var_name = self._mangle_var_name(expr.name + (expr.type_suffix or ''))
                access = var_name
                for idx_expr in expr.subscripts:
                    idx = self._generate_expression(idx_expr)
                    access = f'{access}[{idx}]'
                return access
            else:
                return self._mangle_var_name(expr.name + (expr.type_suffix or ''))
        elif isinstance(expr, BinaryOpNode):
            left = self._generate_expression(expr.left)
            right = self._generate_expression(expr.right)
            op = self._token_to_operator(expr.operator)

            # Map BASIC operators to JavaScript
            if op == '^':
                return f'Math.pow({left}, {right})'
            elif op == '//':
                # Integer division
                return f'Math.floor({left} / {right})'
            elif op in ['+', '-', '*', '/', '<', '>', '<=', '>=', '=', '<>']:
                if op == '=':
                    js_op = '==='
                elif op == '<>':
                    js_op = '!=='
                else:
                    js_op = op
                return f'({left} {js_op} {right})'
            elif op == 'AND':
                return f'({left} && {right})'
            elif op == 'OR':
                return f'({left} || {right})'
            elif op == 'NOT':
                return f'(!{right})'
            elif op == '%':  # MOD
                return f'({left} % {right})'
            else:
                return f'({left} {op} {right})'
        elif isinstance(expr, UnaryOpNode):
            operand = self._generate_expression(expr.operand)
            op = self._token_to_operator(expr.operator)
            return f'({op}{operand})'
        elif isinstance(expr, FunctionCallNode):
            return self._generate_function_call(expr)
        else:
            return f'/* Unknown expr: {type(expr).__name__} */'

    def _generate_function_call(self, func: FunctionCallNode) -> str:
        """Generate function call"""
        func_name = func.name.lower()
        args = [self._generate_expression(arg) for arg in func.arguments]

        # Check if it's a DEF FN function (starts with 'fn')
        # User-defined functions from DEF FN
        if func_name.startswith('fn'):
            # FNA -> _fn_a, FNFOO -> _fn_foo
            return f'_fn_{func_name[2:]}({", ".join(args)})'

        # Map BASIC function names to JavaScript
        js_func_map = {
            # Math functions
            'abs': '_abs',
            'int': '_int',
            'sqr': '_sqr',
            'sin': '_sin',
            'cos': '_cos',
            'tan': '_tan',
            'atn': '_atn',
            'log': '_log',
            'exp': '_exp',
            'rnd': '_rnd',
            'sgn': '_sgn',
            'fix': '_fix',
            'cint': '_cint',
            'csng': '_csng',
            'cdbl': '_cdbl',
            # String functions
            'left$': '_left',
            'right$': '_right',
            'mid$': '_mid',
            'len': '_len',
            'chr$': '_chr',
            'asc': '_asc',
            'str$': '_str',
            'val': '_val',
            'instr': '_instr',
            'space$': '_space',
            'string$': '_string',
            'hex$': '_hex',
            'oct$': '_oct',
            'pos': '_pos',
            # Print formatting
            'tab': '_tab',
            'spc': '_spc',
            # Error handling
            'erl': '_erl',
            'err': '_err',
            # File I/O functions
            'eof': '_eof',
            'lof': '_lof',
            'loc': '_loc',
            # Binary data conversion
            'mki$': '_mki',
            'mks$': '_mks',
            'mkd$': '_mkd',
            'cvi': '_cvi',
            'cvs': '_cvs',
            'cvd': '_cvd',
            # Memory/system functions
            'fre': '_fre',
            'peek': '_peek',
            'inp': '_inp',
            'varptr': '_varptr',
            'usr': '_usr',
            # Input
            'inkey$': '_inkey',
        }

        js_func = js_func_map.get(func_name, func_name)
        return f'{js_func}({", ".join(args)})'

    def _generate_goto(self, stmt: GotoStatementNode) -> List[str]:
        """Generate GOTO statement"""
        code = []
        code.append(self.indent() + f'_pc = {stmt.line_number};')
        code.append(self.indent() + 'break;')
        return code

    def _generate_on_goto(self, stmt: OnGotoStatementNode) -> List[str]:
        """Generate ON...GOTO statement"""
        code = []
        expr = self._generate_expression(stmt.expression)
        code.append(self.indent() + f'{{')
        self.indent_level += 1
        code.append(self.indent() + f'const _idx = Math.floor({expr});')
        code.append(self.indent() + f'const _targets = [{", ".join(str(t) for t in stmt.line_numbers)}];')
        code.append(self.indent() + f'if (_idx >= 1 && _idx <= _targets.length) {{')
        self.indent_level += 1
        code.append(self.indent() + f'_pc = _targets[_idx - 1];')
        code.append(self.indent() + f'break;')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')
        code.append(self.indent() + f'// Fall through if index out of range')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')
        return code

    def _generate_on_gosub(self, stmt: OnGosubStatementNode, return_address: Optional[int]) -> List[str]:
        """Generate ON...GOSUB statement"""
        code = []
        expr = self._generate_expression(stmt.expression)
        code.append(self.indent() + f'{{')
        self.indent_level += 1
        code.append(self.indent() + f'const _idx = Math.floor({expr});')
        code.append(self.indent() + f'const _targets = [{", ".join(str(t) for t in stmt.line_numbers)}];')
        code.append(self.indent() + f'if (_idx >= 1 && _idx <= _targets.length) {{')
        self.indent_level += 1
        if return_address is not None:
            code.append(self.indent() + f'_gosub({return_address});')
        else:
            code.append(self.indent() + f'_gosub(null);  // End of program')
        code.append(self.indent() + f'_pc = _targets[_idx - 1];')
        code.append(self.indent() + f'break;')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')
        code.append(self.indent() + f'// Fall through if index out of range')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')
        return code

    def _generate_gosub(self, stmt: GosubStatementNode, return_address: Optional[int]) -> List[str]:
        """Generate GOSUB statement"""
        code = []
        if return_address is not None:
            code.append(self.indent() + f'_gosub({return_address});')
        else:
            code.append(self.indent() + f'_gosub(null);  // End of program')
        code.append(self.indent() + f'_pc = {stmt.line_number};')
        code.append(self.indent() + 'break;')
        return code

    def _generate_return(self, stmt: ReturnStatementNode) -> List[str]:
        """Generate RETURN statement"""
        code = []
        code.append(self.indent() + '_pc = _return();')
        code.append(self.indent() + 'break;')
        return code

    def _generate_for(self, stmt: ForStatementNode, for_line: int, return_line: Optional[int]) -> List[str]:
        """Generate FOR statement - variable-indexed approach"""
        code = []
        var_key = stmt.variable.name + (stmt.variable.type_suffix or '')
        var_name = self._mangle_var_name(var_key)
        var_type = self._get_var_type(var_key)
        start_expr = self._generate_expression(stmt.start_expr)
        end_expr = self._generate_expression(stmt.end_expr)

        if stmt.step_expr:
            step_expr = self._generate_expression(stmt.step_expr)
        else:
            step_expr = '1'

        # Apply type coercion to initial value
        start_expr = self._apply_type_coercion(start_expr, var_type)

        # Initialize loop variable and store state
        code.append(self.indent() + f'{{')
        self.indent_level += 1
        code.append(self.indent() + f'const _end = {end_expr};')
        code.append(self.indent() + f'const _step = {step_expr};')
        code.append(self.indent() + f'{var_name} = {start_expr};')

        # Check initial condition before entering loop
        code.append(self.indent() + f'const _start_ok = (_step > 0) ? ({var_name} <= _end) : ({var_name} >= _end);')
        code.append(self.indent() + f'if (_start_ok) {{')
        self.indent_level += 1

        # Determine type coercion function name for NEXT increment
        type_coerce_fn = 'null'
        if var_type == VarType.INTEGER:
            type_coerce_fn = '_toInt'
        elif var_type == VarType.SINGLE:
            type_coerce_fn = '_toSingle'

        code.append(self.indent() + f'_for_loops["{var_name}"] = {{')
        self.indent_level += 1
        code.append(self.indent() + f'end: _end,')
        code.append(self.indent() + f'step: _step,')
        code.append(self.indent() + f'coerce: {type_coerce_fn},')
        if return_line is not None:
            code.append(self.indent() + f'line: {return_line}')
        else:
            code.append(self.indent() + f'line: null  // End of program')
        self.indent_level -= 1
        code.append(self.indent() + f'}};')

        self.indent_level -= 1
        code.append(self.indent() + f'}} else {{')
        self.indent_level += 1
        code.append(self.indent() + f'// Skip loop - condition not met')
        # Jump to line after NEXT if we know where it is
        skip_to_line = self.for_to_after_next.get(for_line)
        if skip_to_line is not None:
            code.append(self.indent() + f'_pc = {skip_to_line};')
            code.append(self.indent() + f'break;')
        else:
            code.append(self.indent() + f'// Continue to next statement (NEXT not found)')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')

        return code

    def _generate_next(self, stmt: NextStatementNode, line_number: int, stmt_index: int) -> List[str]:
        """Generate NEXT statement - check condition and loop back"""
        code = []

        # Get variable names
        variables = stmt.variables
        if not variables:
            # NEXT without variable - look up which variable to use
            var_name_base = self.next_var_map.get((line_number, stmt_index))
            if var_name_base:
                # Create a temporary VariableNode for processing
                class TempVar:
                    def __init__(self, name):
                        self.name = name
                        self.type_suffix = ''
                variables = [TempVar(var_name_base)]
            else:
                # No matching FOR found
                code.append(self.indent() + '_error("NEXT without FOR");')
                return code

        # Handle each variable (NEXT can have multiple variables: NEXT I, J, K)
        for var in variables:
            var_name = self._mangle_var_name(var.name + (getattr(var, 'type_suffix', '') or ''))

            # Increment variable and check condition
            code.append(self.indent() + f'if (_for_loops["{var_name}"]) {{')
            self.indent_level += 1
            code.append(self.indent() + f'const _loop = _for_loops["{var_name}"];')
            code.append(self.indent() + f'{var_name} = _loop.coerce ? _loop.coerce({var_name} + _loop.step) : ({var_name} + _loop.step);')
            code.append(self.indent() + f'const _continue = (_loop.step > 0) ? ({var_name} <= _loop.end) : ({var_name} >= _loop.end);')
            code.append(self.indent() + f'if (_continue) {{')
            self.indent_level += 1
            code.append(self.indent() + f'_pc = _loop.line;')
            code.append(self.indent() + f'break;')
            self.indent_level -= 1
            code.append(self.indent() + f'}} else {{')
            self.indent_level += 1
            code.append(self.indent() + f'delete _for_loops["{var_name}"];')
            self.indent_level -= 1
            code.append(self.indent() + f'}}')
            self.indent_level -= 1
            code.append(self.indent() + f'}}')

        return code

    def _generate_while(self, stmt: WhileStatementNode) -> List[str]:
        """Generate WHILE statement"""
        code = []
        condition = self._generate_expression(stmt.condition)
        code.append(self.indent() + f'while ({condition}) {{')
        self.indent_level += 1
        return code

    def _generate_wend(self, stmt: WendStatementNode) -> List[str]:
        """Generate WEND statement"""
        code = []
        self.indent_level -= 1
        code.append(self.indent() + '}')
        return code

    def _generate_if(self, stmt: IfStatementNode, current_line: int) -> List[str]:
        """Generate IF statement"""
        code = []
        condition = self._generate_expression(stmt.condition)

        if stmt.then_statements:
            # Multi-line IF...THEN...END IF
            code.append(self.indent() + f'if ({condition}) {{')
            self.indent_level += 1
            for s in stmt.then_statements:
                code.extend(self._generate_statement(s, current_line, 0, 1))
            self.indent_level -= 1

            if stmt.else_statements:
                code.append(self.indent() + '} else {')
                self.indent_level += 1
                for s in stmt.else_statements:
                    code.extend(self._generate_statement(s, current_line, 0, 1))
                self.indent_level -= 1

            code.append(self.indent() + '}')
        elif stmt.then_line_number:
            # IF...THEN line_number (GOTO)
            code.append(self.indent() + f'if ({condition}) {{')
            self.indent_level += 1
            code.append(self.indent() + f'_pc = {stmt.then_line_number};')
            code.append(self.indent() + 'break;')
            self.indent_level -= 1
            code.append(self.indent() + '}')

        return code

    def _generate_input(self, stmt: InputStatementNode) -> List[str]:
        """Generate INPUT statement"""
        code = []

        # Check for file I/O
        if stmt.file_number:
            filenum_expr = self._generate_expression(stmt.file_number)
            # Generate input for each variable from file
            for var in stmt.variables:
                var_key = var.name + (var.type_suffix or '')
                var_name = self._mangle_var_name(var_key)
                var_type = self._get_var_type(var_key)

                if var_type == VarType.STRING:
                    # String input from file
                    code.append(self.indent() + f'{var_name} = _finput({filenum_expr});')
                elif var_type == VarType.INTEGER:
                    # Integer input from file - use _toInt for proper truncation
                    code.append(self.indent() + f'{var_name} = _toInt(parseFloat(_finput({filenum_expr})) || 0);')
                elif var_type == VarType.SINGLE:
                    # Single precision input from file
                    code.append(self.indent() + f'{var_name} = _toSingle(parseFloat(_finput({filenum_expr})) || 0);')
                else:
                    # Double precision or unknown - no coercion needed
                    code.append(self.indent() + f'{var_name} = parseFloat(_finput({filenum_expr})) || 0;')
        else:
            # Interactive input
            # Build prompt string
            if stmt.prompt:
                prompt_str = self._escape_string(stmt.prompt)
            else:
                prompt_str = "? "

            # Generate input for each variable
            for var in stmt.variables:
                var_key = var.name + (var.type_suffix or '')
                var_name = self._mangle_var_name(var_key)
                var_type = self._get_var_type(var_key)

                if var_type == VarType.STRING:
                    # String input
                    code.append(self.indent() + f'{var_name} = _input_str("{prompt_str}");')
                elif var_type == VarType.INTEGER:
                    # Integer input - _input_int already uses _toInt
                    code.append(self.indent() + f'{var_name} = _input_int("{prompt_str}");')
                elif var_type == VarType.SINGLE:
                    # Single precision input
                    code.append(self.indent() + f'{var_name} = _toSingle(_input_num("{prompt_str}"));')
                else:
                    # Double precision or unknown - no coercion needed
                    code.append(self.indent() + f'{var_name} = _input_num("{prompt_str}");')

        return code

    def _generate_line_input(self, stmt: LineInputStatementNode) -> List[str]:
        """Generate LINE INPUT statement - reads entire line without parsing"""
        code = []

        # LINE INPUT always reads into a string variable
        var_name = self._mangle_var_name(stmt.variable.name + (stmt.variable.type_suffix or ''))

        # Check for file I/O
        if stmt.file_number:
            filenum_expr = self._generate_expression(stmt.file_number)
            code.append(self.indent() + f'{var_name} = _fline_input({filenum_expr});')
        else:
            # Interactive input
            # Build prompt string
            if stmt.prompt:
                prompt_expr = self._generate_expression(stmt.prompt)
            else:
                prompt_expr = '"? "'

            code.append(self.indent() + f'{var_name} = _line_input({prompt_expr});')

        return code

    def _generate_write(self, stmt: WriteStatementNode) -> List[str]:
        """Generate WRITE statement - CSV formatted output"""
        code = []

        # Determine output function
        if stmt.file_number:
            filenum_expr = self._generate_expression(stmt.file_number)
            print_func = f'_fprint({filenum_expr}, '
        else:
            print_func = '_print('

        # Build CSV output from expressions
        # WRITE outputs: strings quoted, numbers unquoted, comma separated
        if not stmt.expressions:
            # Empty WRITE - just newline
            code.append(self.indent() + f'{print_func}"");')
        else:
            parts = []
            for expr in stmt.expressions:
                expr_code = self._generate_expression(expr)
                # Wrap expression in _write_format() to handle quoting
                parts.append(f'_write_format({expr_code})')

            # Join with commas and print
            output = ' + "," + '.join(parts)
            code.append(self.indent() + f'{print_func}{output});')

        return code

    def _generate_lprint(self, stmt: LprintStatementNode) -> List[str]:
        """Generate LPRINT statement - print to line printer (console.log)"""
        code = []

        # Skip file I/O for now
        if stmt.file_number:
            code.append(self.indent() + '// LPRINT #file not supported')
            return code

        # LPRINT is like PRINT but outputs to console.log instead of output div
        if not stmt.expressions:
            # Empty LPRINT - just newline
            code.append(self.indent() + 'console.log("");')
        else:
            for i, expr in enumerate(stmt.expressions):
                is_last = (i == len(stmt.expressions) - 1)
                separator = stmt.separators[i] if i < len(stmt.separators) else None

                expr_code = self._generate_expression(expr)

                # Determine if we should add a newline
                # Newline if last item and no separator (None or '\n' means end of statement)
                newline = 'true' if (is_last and separator in (None, '\n')) else 'false'

                code.append(self.indent() + f'_lprint({expr_code}, {newline});')

                # Handle separator (comma for tab, semicolon for no space)
                if separator == ',':
                    code.append(self.indent() + '_lprint("\\t", false);')
                elif separator == ';':
                    # Semicolon means no space - already handled
                    pass

        return code

    def _generate_read(self, stmt: ReadStatementNode) -> List[str]:
        """Generate READ statement"""
        code = []
        for var in stmt.variables:
            var_name = self._mangle_var_name(var.name + (var.type_suffix or ''))
            code.append(self.indent() + f'{var_name} = _read();')
        return code

    def _generate_restore(self, stmt: RestoreStatementNode) -> List[str]:
        """Generate RESTORE statement"""
        code = []
        code.append(self.indent() + '_restore();')
        return code

    def _generate_randomize(self, stmt: RandomizeStatementNode) -> List[str]:
        """Generate RANDOMIZE statement"""
        code = []
        if stmt.seed:
            # RANDOMIZE with seed
            seed_expr = self._generate_expression(stmt.seed)
            code.append(self.indent() + f'_rnd(-{seed_expr});  // Seed random generator')
        else:
            # RANDOMIZE without seed - use current time
            code.append(self.indent() + '_rnd(-(new Date().getTime()));  // Seed with current time')
        return code

    def _generate_stop(self, stmt: StopStatementNode) -> List[str]:
        """Generate STOP statement - halts execution"""
        code = []
        code.append(self.indent() + '_pc = null;')
        code.append(self.indent() + 'return;')
        return code

    def _generate_swap(self, stmt: SwapStatementNode) -> List[str]:
        """Generate SWAP statement - swaps two variables"""
        code = []
        var1_name = self._mangle_var_name(stmt.var1.name + (stmt.var1.type_suffix or ''))
        var2_name = self._mangle_var_name(stmt.var2.name + (stmt.var2.type_suffix or ''))

        # Use destructuring assignment for clean swap
        code.append(self.indent() + f'[{var1_name}, {var2_name}] = [{var2_name}, {var1_name}];')
        return code

    def _generate_mid_assignment(self, stmt: MidAssignmentStatementNode) -> List[str]:
        """Generate MID$ assignment - modifies substring in place

        Syntax: MID$(string_var, start, length) = value
        Replaces 'length' characters starting at 'start' (1-based) with 'value'
        """
        code = []

        # Generate the variable reference (can be simple variable or array element)
        var_expr = self._generate_expression(stmt.string_var)
        start_expr = self._generate_expression(stmt.start)
        length_expr = self._generate_expression(stmt.length)
        value_expr = self._generate_expression(stmt.value)

        # MID$ assignment in JavaScript:
        # var = var.substring(0, start-1) + value.substring(0, length) + var.substring(start-1+length)
        # Need to handle the case where value is shorter/longer than length

        code.append(self.indent() + '{')
        self.indent_level += 1

        # Save the expressions to temporary variables to avoid re-evaluation
        code.append(self.indent() + f'const _mid_start = {start_expr};')
        code.append(self.indent() + f'const _mid_len = {length_expr};')
        code.append(self.indent() + f'const _mid_val = String({value_expr}).substring(0, _mid_len);')
        code.append(self.indent() + f'const _mid_str = String({var_expr});')

        # Build the new string
        code.append(self.indent() +
                   f'{var_expr} = _mid_str.substring(0, _mid_start - 1) + ' +
                   f'_mid_val + _mid_str.substring(_mid_start - 1 + _mid_len);')

        self.indent_level -= 1
        code.append(self.indent() + '}')

        return code

    def _generate_erase(self, stmt: EraseStatementNode) -> List[str]:
        """Generate ERASE statement - resets arrays to default values"""
        code = []
        for array_name in stmt.array_names:
            # Look up the array in the symbol table to get its type and dimensions
            var_info = self.symbols.variables.get(array_name)
            if var_info and var_info.is_array:
                js_name = self._mangle_var_name(array_name)
                dimensions = var_info.dimensions or [10]

                # Determine default value based on type
                if var_info.var_type == VarType.STRING:
                    init_value = '""'
                else:
                    init_value = '0'

                # Recreate the array with default values
                code.append(self.indent() + f'{js_name} = {self._create_array_init(dimensions, init_value)};')
        return code

    def _generate_open(self, stmt: OpenStatementNode) -> List[str]:
        """Generate OPEN statement - open file for I/O"""
        code = []
        filenum_expr = self._generate_expression(stmt.file_number)
        filename_expr = self._generate_expression(stmt.filename)
        mode = stmt.mode  # "I", "O", "A", or "R"

        # For random files, include record length (default 128 if not specified)
        if stmt.record_length:
            reclen_expr = self._generate_expression(stmt.record_length)
            code.append(self.indent() + f'_fopen({filenum_expr}, {filename_expr}, "{mode}", {reclen_expr});')
        else:
            code.append(self.indent() + f'_fopen({filenum_expr}, {filename_expr}, "{mode}");')
        return code

    def _generate_close(self, stmt: CloseStatementNode) -> List[str]:
        """Generate CLOSE statement - close file(s)"""
        code = []
        if not stmt.file_numbers:
            # CLOSE with no arguments - close all files
            code.append(self.indent() + '_fclose();')
        else:
            # CLOSE #1, #2, ...
            for file_number in stmt.file_numbers:
                filenum_expr = self._generate_expression(file_number)
                code.append(self.indent() + f'_fclose({filenum_expr});')
        return code

    def _generate_reset(self, stmt: ResetStatementNode) -> List[str]:
        """Generate RESET statement - close all open files"""
        code = []
        code.append(self.indent() + '_fclose();  // RESET - close all files')
        return code

    def _generate_kill(self, stmt: KillStatementNode) -> List[str]:
        """Generate KILL statement - delete file"""
        code = []
        filename_expr = self._generate_expression(stmt.filename)
        code.append(self.indent() + f'_fkill({filename_expr});')
        return code

    def _generate_name(self, stmt: NameStatementNode) -> List[str]:
        """Generate NAME statement - rename file"""
        code = []
        old_filename_expr = self._generate_expression(stmt.old_filename)
        new_filename_expr = self._generate_expression(stmt.new_filename)
        code.append(self.indent() + f'_fname({old_filename_expr}, {new_filename_expr});')
        return code

    def _generate_files(self, stmt: FilesStatementNode) -> List[str]:
        """Generate FILES statement - list directory"""
        code = []
        if stmt.filespec:
            filespec_expr = self._generate_expression(stmt.filespec)
            code.append(self.indent() + f'_ffiles({filespec_expr});')
        else:
            code.append(self.indent() + '_ffiles("*");')
        return code

    def _generate_field(self, stmt: FieldStatementNode) -> List[str]:
        """Generate FIELD statement - define random-access file buffer layout"""
        code = []
        filenum_expr = self._generate_expression(stmt.file_number)

        # Build array of field definitions: [[width1, var1], [width2, var2], ...]
        fields_array = []
        for width, var in stmt.fields:
            width_expr = self._generate_expression(width)
            var_name = var.name  # Variable name (string variable)
            fields_array.append(f'[{width_expr}, "{var_name}"]')

        fields_str = '[' + ', '.join(fields_array) + ']'
        code.append(self.indent() + f'_ffield({filenum_expr}, {fields_str});')
        return code

    def _generate_lset(self, stmt: LsetStatementNode) -> List[str]:
        """Generate LSET statement - left-justify string in field variable"""
        code = []
        var_name = stmt.variable.name
        value_expr = self._generate_expression(stmt.expression)
        code.append(self.indent() + f'_flset("{var_name}", {value_expr});')
        return code

    def _generate_rset(self, stmt: RsetStatementNode) -> List[str]:
        """Generate RSET statement - right-justify string in field variable"""
        code = []
        var_name = stmt.variable.name
        value_expr = self._generate_expression(stmt.expression)
        code.append(self.indent() + f'_frset("{var_name}", {value_expr});')
        return code

    def _generate_get(self, stmt: GetStatementNode) -> List[str]:
        """Generate GET statement - read record from random-access file"""
        code = []
        filenum_expr = self._generate_expression(stmt.file_number)
        if stmt.record_number:
            rec_expr = self._generate_expression(stmt.record_number)
            code.append(self.indent() + f'_fget({filenum_expr}, {rec_expr});')
        else:
            code.append(self.indent() + f'_fget({filenum_expr});')
        return code

    def _generate_put(self, stmt: PutStatementNode) -> List[str]:
        """Generate PUT statement - write record to random-access file"""
        code = []
        filenum_expr = self._generate_expression(stmt.file_number)
        if stmt.record_number:
            rec_expr = self._generate_expression(stmt.record_number)
            code.append(self.indent() + f'_fput({filenum_expr}, {rec_expr});')
        else:
            code.append(self.indent() + f'_fput({filenum_expr});')
        return code

    def _generate_chain(self, stmt: ChainStatementNode) -> List[str]:
        """Generate CHAIN statement - chain to another program

        Browser: Navigate to new HTML page (window.location.href)
        Node.js: Spawn new process and exit (child_process.spawn)

        Note: Like the Z80 backend, we only support basic CHAIN "filename"
        MERGE, line number, ALL, and DELETE options are not supported.
        """
        code = []

        # Warn about unsupported options
        if stmt.merge:
            code.append(self.indent() + '// MERGE option not supported in compiled code')
        if stmt.start_line:
            code.append(self.indent() + '// Start line not supported in compiled code')
        if stmt.all_flag:
            code.append(self.indent() + '// ALL option not supported - no COMMON in MBASIC 5.21')
        if stmt.delete_range:
            code.append(self.indent() + '// DELETE option not supported in compiled code')

        filename_expr = self._generate_expression(stmt.filename)

        code.append(self.indent() + '// CHAIN to another program')
        code.append(self.indent() + 'if (typeof window !== "undefined") {')
        self.indent_level += 1
        code.append(self.indent() + '// Browser: Navigate to new HTML page')
        code.append(self.indent() + f'window.location.href = {filename_expr} + ".html";')
        self.indent_level -= 1
        code.append(self.indent() + '} else if (typeof process !== "undefined") {')
        self.indent_level += 1
        code.append(self.indent() + '// Node.js: Spawn new program and exit')
        code.append(self.indent() + 'const { spawn } = require("child_process");')
        code.append(self.indent() + f'const newProgram = {filename_expr} + ".js";')
        code.append(self.indent() + 'spawn("node", [newProgram], {')
        self.indent_level += 1
        code.append(self.indent() + 'stdio: "inherit",  // Pass through stdin/stdout/stderr')
        code.append(self.indent() + 'detached: false')
        self.indent_level -= 1
        code.append(self.indent() + '});')
        code.append(self.indent() + 'process.exit(0);  // Exit current program')
        self.indent_level -= 1
        code.append(self.indent() + '}')

        return code

    def _generate_on_error(self, stmt: OnErrorStatementNode) -> List[str]:
        """Generate ON ERROR GOTO/GOSUB statement"""
        code = []
        if stmt.line_number == 0:
            # ON ERROR GOTO 0 - disable error handling
            code.append(self.indent() + '_error_handler = 0;')
        else:
            # ON ERROR GOTO line or ON ERROR GOSUB line
            code.append(self.indent() + f'_error_handler = {stmt.line_number};')
            code.append(self.indent() + f'_error_handler_is_gosub = {str(stmt.is_gosub).lower()};')
        return code

    def _generate_error(self, stmt: ErrorStatementNode, current_line: int) -> List[str]:
        """Generate ERROR statement - simulate an error"""
        code = []
        error_code = self._generate_expression(stmt.error_code)
        code.append(self.indent() + f'{{')
        self.indent_level += 1
        code.append(self.indent() + f'const _next = _trigger_error({error_code}, {current_line});')
        code.append(self.indent() + f'if (_next !== undefined) {{')
        self.indent_level += 1
        code.append(self.indent() + f'_pc = _next;')
        code.append(self.indent() + f'break;')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')
        self.indent_level -= 1
        code.append(self.indent() + f'}}')
        return code

    def _generate_resume(self, stmt: ResumeStatementNode, current_line: int) -> List[str]:
        """Generate RESUME statement"""
        code = []
        code.append(self.indent() + '_in_error_handler = false;')

        if stmt.line_number is None:
            # RESUME - return to line that caused error
            code.append(self.indent() + '_pc = _error_line;')
            code.append(self.indent() + 'break;')
        elif stmt.line_number == 0:
            # RESUME NEXT - continue at next line after error
            code.append(self.indent() + f'const _next_line = {self.next_line_map.get(current_line, "null")};')
            code.append(self.indent() + 'if (_next_line !== null) {')
            self.indent_level += 1
            code.append(self.indent() + '_pc = _next_line;')
            self.indent_level -= 1
            code.append(self.indent() + '} else {')
            self.indent_level += 1
            code.append(self.indent() + '_pc = null;  // End of program')
            self.indent_level -= 1
            code.append(self.indent() + '}')
            code.append(self.indent() + 'break;')
        else:
            # RESUME line_number - continue at specific line
            code.append(self.indent() + f'_pc = {stmt.line_number};')
            code.append(self.indent() + 'break;')

        return code

    def _generate_poke(self, stmt: PokeStatementNode) -> List[str]:
        """Generate POKE statement - write to memory address (stub: warning only)"""
        code = []
        addr = self._generate_expression(stmt.address)
        val = self._generate_expression(stmt.value)
        code.append(self.indent() + f'console.warn("POKE " + ({addr}) + "," + ({val}) + " - not supported in JavaScript");')
        return code

    def _generate_out(self, stmt: OutStatementNode) -> List[str]:
        """Generate OUT statement - write to I/O port (stub: warning only)"""
        code = []
        port = self._generate_expression(stmt.port)
        val = self._generate_expression(stmt.value)
        code.append(self.indent() + f'console.warn("OUT " + ({port}) + "," + ({val}) + " - not supported in JavaScript");')
        return code

    def _generate_wait(self, stmt: WaitStatementNode) -> List[str]:
        """Generate WAIT statement - wait for I/O port (stub: returns immediately)"""
        code = []
        port = self._generate_expression(stmt.port)
        and_mask = self._generate_expression(stmt.and_mask)
        code.append(self.indent() + f'console.warn("WAIT " + ({port}) + "," + ({and_mask}) + " - not supported in JavaScript, returning immediately");')
        return code

    def _generate_call(self, stmt: CallStatementNode) -> List[str]:
        """Generate CALL statement - call machine code (stub: warning only)"""
        code = []
        addr = self._generate_expression(stmt.address)
        code.append(self.indent() + f'console.warn("CALL " + ({addr}) + " - not supported in JavaScript");')
        return code

    def _generate_width(self, stmt: WidthStatementNode) -> List[str]:
        """Generate WIDTH statement - set line width (stub: warning only)

        In MBASIC, WIDTH sets the console/printer line width.
        In JavaScript, console/browser output doesn't have fixed width.
        """
        code = []
        if stmt.file_number:
            filenum = self._generate_expression(stmt.file_number)
            width = self._generate_expression(stmt.width)
            code.append(self.indent() + f'// WIDTH #{filenum}, {width} - ignored in JavaScript')
        else:
            width = self._generate_expression(stmt.width)
            code.append(self.indent() + f'// WIDTH {width} - ignored in JavaScript')
        return code

    def _generate_clear(self, stmt: ClearStatementNode) -> List[str]:
        """Generate CLEAR statement - clear variables and set memory limits (stub)

        In MBASIC, CLEAR resets all variables and optionally sets string/stack space.
        In JavaScript, we just reinitialize variables (without actually doing anything
        since JS handles memory automatically).
        """
        code = []
        code.append(self.indent() + '// CLEAR - JavaScript manages memory automatically')
        return code

    def _generate_common(self, stmt: CommonStatementNode) -> List[str]:
        """Generate COMMON statement - share variables between CHAINed programs (stub)

        COMMON declares variables to be shared between CHAINed programs.
        In JavaScript browser, we could use sessionStorage/localStorage.
        For now, just warn that it's not fully supported.
        """
        code = []
        var_names = ', '.join(v.name for v in stmt.variables)
        code.append(self.indent() + f'// COMMON {var_names} - variable sharing not supported in JavaScript')
        return code
