#!/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 n; }  // Single precision (already JS number)')
        code.append(self.indent() + 'function _cdbl(n) { return n; }  // Double precision (already JS number)')
        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 parseInt(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('')

        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()):
            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"""
        # Remove type suffix and convert
        base_name = name.rstrip('$%!#')

        # Add suffix for type
        if name.endswith('$'):
            return base_name.lower() + '_str'
        elif name.endswith('%'):
            return base_name.lower() + '_int'
        elif name.endswith('#'):
            return base_name.lower() + '_dbl'
        else:
            return base_name.lower()

    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
        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, or separator is None
                newline = 'true' if (is_last and separator is None) 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 = []

        # Check if assigning to array element
        if stmt.variable.subscripts:
            var_name = self._mangle_var_name(stmt.variable.name + (stmt.variable.type_suffix or ''))
            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)
            code.append(self.indent() + f'{access} = {expr_code};')
        else:
            var_name = self._mangle_var_name(stmt.variable.name + (stmt.variable.type_suffix or ''))
            expr_code = self._generate_expression(stmt.expression)
            code.append(self.indent() + f'{var_name} = {expr_code};')

        return 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',
        }

        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_name = self._mangle_var_name(stmt.variable.name + (stmt.variable.type_suffix or ''))
        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'

        # 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

        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,')
        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.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_name = self._mangle_var_name(var.name + (var.type_suffix or ''))
                var_type = var.type_suffix or ''

                if var_type == '$':
                    # String input from file
                    code.append(self.indent() + f'{var_name} = _finput({filenum_expr});')
                elif var_type == '%':
                    # Integer input from file
                    code.append(self.indent() + f'{var_name} = parseInt(_finput({filenum_expr})) || 0;')
                else:
                    # Numeric input from file
                    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_name = self._mangle_var_name(var.name + (var.type_suffix or ''))
                var_type = var.type_suffix or ''

                if var_type == '$':
                    # String input
                    code.append(self.indent() + f'{var_name} = _input_str("{prompt_str}");')
                elif var_type == '%':
                    # Integer input
                    code.append(self.indent() + f'{var_name} = _input_int("{prompt_str}");')
                else:
                    # Numeric input (single/double precision)
                    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, or separator is None
                newline = 'true' if (is_last and separator is None) 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
