/**
 * Token Definitions for the Bash Lexer
 *
 * Contains all type definitions, enums, constants, and utility functions
 * used by the lexer's tokenization logic.
 */
// Default max heredoc size to prevent memory exhaustion (10MB)
export const DEFAULT_MAX_HEREDOC_SIZE = 10_485_760;
export var TokenType;
(function (TokenType) {
    // End of input
    TokenType["EOF"] = "EOF";
    // Newlines and separators
    TokenType["NEWLINE"] = "NEWLINE";
    TokenType["SEMICOLON"] = "SEMICOLON";
    TokenType["AMP"] = "AMP";
    // Operators
    TokenType["PIPE"] = "PIPE";
    TokenType["PIPE_AMP"] = "PIPE_AMP";
    TokenType["AND_AND"] = "AND_AND";
    TokenType["OR_OR"] = "OR_OR";
    TokenType["BANG"] = "BANG";
    // Redirections
    TokenType["LESS"] = "LESS";
    TokenType["GREAT"] = "GREAT";
    TokenType["DLESS"] = "DLESS";
    TokenType["DGREAT"] = "DGREAT";
    TokenType["LESSAND"] = "LESSAND";
    TokenType["GREATAND"] = "GREATAND";
    TokenType["LESSGREAT"] = "LESSGREAT";
    TokenType["DLESSDASH"] = "DLESSDASH";
    TokenType["CLOBBER"] = "CLOBBER";
    TokenType["TLESS"] = "TLESS";
    TokenType["AND_GREAT"] = "AND_GREAT";
    TokenType["AND_DGREAT"] = "AND_DGREAT";
    // Grouping
    TokenType["LPAREN"] = "LPAREN";
    TokenType["RPAREN"] = "RPAREN";
    TokenType["LBRACE"] = "LBRACE";
    TokenType["RBRACE"] = "RBRACE";
    // Special
    TokenType["DSEMI"] = "DSEMI";
    TokenType["SEMI_AND"] = "SEMI_AND";
    TokenType["SEMI_SEMI_AND"] = "SEMI_SEMI_AND";
    // Compound commands
    TokenType["DBRACK_START"] = "DBRACK_START";
    TokenType["DBRACK_END"] = "DBRACK_END";
    TokenType["DPAREN_START"] = "DPAREN_START";
    TokenType["DPAREN_END"] = "DPAREN_END";
    // Reserved words
    TokenType["IF"] = "IF";
    TokenType["THEN"] = "THEN";
    TokenType["ELSE"] = "ELSE";
    TokenType["ELIF"] = "ELIF";
    TokenType["FI"] = "FI";
    TokenType["FOR"] = "FOR";
    TokenType["WHILE"] = "WHILE";
    TokenType["UNTIL"] = "UNTIL";
    TokenType["DO"] = "DO";
    TokenType["DONE"] = "DONE";
    TokenType["CASE"] = "CASE";
    TokenType["ESAC"] = "ESAC";
    TokenType["IN"] = "IN";
    TokenType["FUNCTION"] = "FUNCTION";
    TokenType["SELECT"] = "SELECT";
    TokenType["TIME"] = "TIME";
    TokenType["COPROC"] = "COPROC";
    // Words and identifiers
    TokenType["WORD"] = "WORD";
    TokenType["NAME"] = "NAME";
    TokenType["NUMBER"] = "NUMBER";
    TokenType["ASSIGNMENT_WORD"] = "ASSIGNMENT_WORD";
    TokenType["FD_VARIABLE"] = "FD_VARIABLE";
    // Comments
    TokenType["COMMENT"] = "COMMENT";
    // Here-document content
    TokenType["HEREDOC_CONTENT"] = "HEREDOC_CONTENT";
})(TokenType || (TokenType = {}));
/**
 * Error thrown when the lexer encounters invalid input
 */
export class LexerError extends Error {
    line;
    column;
    constructor(message, line, column) {
        super(`line ${line}: ${message}`);
        this.line = line;
        this.column = column;
        this.name = "LexerError";
    }
}
/**
 * Reserved words in bash
 * Using Map to prevent prototype pollution (e.g., "constructor", "__proto__")
 */
export const RESERVED_WORDS = new Map([
    ["if", TokenType.IF],
    ["then", TokenType.THEN],
    ["else", TokenType.ELSE],
    ["elif", TokenType.ELIF],
    ["fi", TokenType.FI],
    ["for", TokenType.FOR],
    ["while", TokenType.WHILE],
    ["until", TokenType.UNTIL],
    ["do", TokenType.DO],
    ["done", TokenType.DONE],
    ["case", TokenType.CASE],
    ["esac", TokenType.ESAC],
    ["in", TokenType.IN],
    ["function", TokenType.FUNCTION],
    ["select", TokenType.SELECT],
    ["time", TokenType.TIME],
    ["coproc", TokenType.COPROC],
]);
/**
 * Check if a string is a valid assignment LHS with optional nested array subscript
 * Handles: VAR, a[0], a[x], a[a[0]], a[x+1], etc.
 */
export function isValidAssignmentLHS(str) {
    // Must start with valid variable name
    const match = str.match(/^[a-zA-Z_][a-zA-Z0-9_]*/);
    if (!match)
        return false;
    const afterName = str.slice(match[0].length);
    // If nothing after name, it's valid (simple variable)
    if (afterName === "" || afterName === "+")
        return true;
    // If it's an array subscript, need to check for balanced brackets
    if (afterName[0] === "[") {
        // Find matching close bracket (handling nested brackets)
        let depth = 0;
        let i = 0;
        for (; i < afterName.length; i++) {
            if (afterName[i] === "[")
                depth++;
            else if (afterName[i] === "]") {
                depth--;
                if (depth === 0)
                    break;
            }
        }
        // Must have found closing bracket
        if (depth !== 0 || i >= afterName.length)
            return false;
        // After closing bracket, only + is allowed (for +=)
        const afterBracket = afterName.slice(i + 1);
        return afterBracket === "" || afterBracket === "+";
    }
    return false;
}
/**
 * Find the index of assignment '=' or '+=' outside of brackets.
 * Returns the index of '=' (or '=' after '+') or -1 if not found.
 * For 'a[x=1]=value', returns the index of the second '='.
 */
export function findAssignmentEq(str) {
    let depth = 0;
    for (let i = 0; i < str.length; i++) {
        const c = str[i];
        if (c === "[") {
            depth++;
        }
        else if (c === "]") {
            depth--;
        }
        else if (depth === 0 && c === "=") {
            return i;
        }
        else if (depth === 0 && c === "+" && str[i + 1] === "=") {
            return i + 1; // Return position of '=' in '+='
        }
    }
    return -1;
}
/**
 * Three-character operators (simple ones without special handling)
 */
export const THREE_CHAR_OPS = [
    [";", ";", "&", TokenType.SEMI_SEMI_AND],
    ["<", "<", "<", TokenType.TLESS],
    ["&", ">", ">", TokenType.AND_DGREAT],
    // Note: <<- has special handling for heredoc, not included here
];
/**
 * Two-character operators (simple ones without special handling)
 * Note: << has special handling for heredoc, not included here
 */
export const TWO_CHAR_OPS = [
    ["[", "[", TokenType.DBRACK_START],
    ["]", "]", TokenType.DBRACK_END],
    ["(", "(", TokenType.DPAREN_START],
    [")", ")", TokenType.DPAREN_END],
    ["&", "&", TokenType.AND_AND],
    ["|", "|", TokenType.OR_OR],
    [";", ";", TokenType.DSEMI],
    [";", "&", TokenType.SEMI_AND],
    ["|", "&", TokenType.PIPE_AMP],
    [">", ">", TokenType.DGREAT],
    ["<", "&", TokenType.LESSAND],
    [">", "&", TokenType.GREATAND],
    ["<", ">", TokenType.LESSGREAT],
    [">", "|", TokenType.CLOBBER],
    ["&", ">", TokenType.AND_GREAT],
];
/**
 * Single-character operators (simple ones without special handling)
 * Note: {, }, ! have special handling, not included here
 */
export const SINGLE_CHAR_OPS = new Map([
    ["|", TokenType.PIPE],
    ["&", TokenType.AMP],
    [";", TokenType.SEMICOLON],
    ["(", TokenType.LPAREN],
    [")", TokenType.RPAREN],
    ["<", TokenType.LESS],
    [">", TokenType.GREAT],
]);
/**
 * Check if a string is a valid variable name
 */
export function isValidName(s) {
    return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s);
}
/**
 * Check if a character is a word boundary (ends a word token)
 */
export function isWordBoundary(char) {
    return (char === " " ||
        char === "\t" ||
        char === "\n" ||
        char === ";" ||
        char === "&" ||
        char === "|" ||
        char === "(" ||
        char === ")" ||
        char === "<" ||
        char === ">");
}
