/**
 * Unified Script Parser
 *
 * Based on FCS parser (superset) with Buddy Script additions:
 * - AwaitExpression in unary parsing
 * - ForCStyle statement (C-style for loops)
 * - Export keyword
 * - Question token for ternary (instead of checking Colon value)
 * - Arrow functions / lambdas
 *
 * Extensions: .bs (primary), .fcs (backward-compatible alias)
 */
import { TokenType, } from './types.js';
import { tokenize as lexerTokenize } from './lexer.js';
import { createLoopGuard, LoopTimeoutError } from '../utils/errors.js';
/** Maximum recursion depth for recursive descent parsing */
const MAX_RECURSION_DEPTH = 500;
export class FCSParser {
    tokens;
    current = 0;
    recursionDepth = 0;
    constructor(tokens) {
        // Filter out comments
        this.tokens = tokens.filter(t => t.type !== TokenType.Comment);
    }
    /**
     * Track recursion depth to prevent stack overflow on deeply nested input
     */
    enterRecursion(context) {
        this.recursionDepth++;
        if (this.recursionDepth > MAX_RECURSION_DEPTH) {
            throw new LoopTimeoutError(`Maximum recursion depth exceeded in ${context}. Input may be too deeply nested or malformed.`, MAX_RECURSION_DEPTH, context);
        }
    }
    exitRecursion() {
        this.recursionDepth--;
    }
    parse() {
        const statements = [];
        const guard = createLoopGuard({
            maxIterations: 100000,
            context: 'program parsing',
        });
        while (!this.isAtEnd()) {
            guard();
            this.skipNewlines();
            if (this.isAtEnd())
                break;
            const stmt = this.parseDeclaration();
            if (stmt) {
                statements.push(stmt);
            }
            this.skipNewlines();
        }
        return { type: 'Program', statements };
    }
    // ============================================
    // Statement Parsing
    // ============================================
    parseDeclaration() {
        this.enterRecursion('parseDeclaration');
        try {
            // Check for decorators
            const decoratorGuard = createLoopGuard({
                maxIterations: 1000,
                context: 'decorator parsing',
            });
            while (this.match(TokenType.Decorator)) {
                decoratorGuard();
                // Decorators stored but not used yet
                void this.previous().value;
            }
            if (this.matchKeyword('func', 'function') || this.matchKeyword('async')) {
                return this.parseFunctionDeclaration();
            }
            if (this.matchKeyword('class')) {
                return this.parseClassDeclaration();
            }
            if (this.matchKeyword('let', 'const', 'var')) {
                return this.parseVarDeclaration();
            }
            if (this.matchKeyword('import')) {
                return this.parseImportStatement();
            }
            if (this.matchKeyword('export')) {
                return this.parseExportStatement();
            }
            if (this.matchKeyword('test')) {
                return this.parseTestDeclaration();
            }
            return this.parseStatement();
        }
        catch (error) {
            // Re-throw recursion/loop errors without synchronizing
            if (error instanceof LoopTimeoutError) {
                throw error;
            }
            this.synchronize();
            return null;
        }
        finally {
            this.exitRecursion();
        }
    }
    parseStatement() {
        if (this.matchKeyword('if'))
            return this.parseIfStatement();
        if (this.matchKeyword('while'))
            return this.parseWhileStatement();
        if (this.matchKeyword('for'))
            return this.parseForStatement();
        if (this.matchKeyword('return'))
            return this.parseReturnStatement();
        if (this.matchKeyword('break'))
            return this.parseBreakStatement();
        if (this.matchKeyword('continue'))
            return this.parseContinueStatement();
        if (this.matchKeyword('try'))
            return this.parseTryStatement();
        if (this.matchKeyword('throw'))
            return this.parseThrowStatement();
        if (this.matchKeyword('assert'))
            return this.parseAssertStatement();
        if (this.match(TokenType.LeftBrace))
            return this.parseBlockStatement();
        return this.parseExpressionStatement();
    }
    parseFunctionDeclaration() {
        const isAsync = this.previous().value === 'async';
        if (isAsync) {
            this.consumeKeyword('func', 'function');
        }
        const name = this.consume(TokenType.Identifier, "Expected function name").value;
        this.consume(TokenType.LeftParen, "Expected '(' after function name");
        const parameters = this.parseParameters();
        this.consume(TokenType.RightParen, "Expected ')' after parameters");
        let returnType;
        if (this.match(TokenType.Colon)) {
            returnType = this.consume(TokenType.Identifier, "Expected return type").value;
        }
        this.consume(TokenType.LeftBrace, "Expected '{' before function body");
        const body = this.parseBlockStatement();
        return {
            type: 'FunctionDeclaration',
            name,
            parameters,
            body,
            isAsync,
            returnType,
        };
    }
    parseParameters() {
        const parameters = [];
        if (!this.check(TokenType.RightParen)) {
            do {
                const name = this.consume(TokenType.Identifier, "Expected parameter name").value;
                let type;
                let defaultValue;
                if (this.match(TokenType.Colon)) {
                    type = this.consume(TokenType.Identifier, "Expected parameter type").value;
                }
                if (this.match(TokenType.Assign)) {
                    defaultValue = this.parseExpression();
                }
                parameters.push({ name, type, defaultValue });
            } while (this.match(TokenType.Comma));
        }
        return parameters;
    }
    parseVarDeclaration() {
        const keyword = this.previous().value;
        const isConst = keyword === 'const';
        const name = this.consume(TokenType.Identifier, "Expected variable name").value;
        let varType;
        if (this.match(TokenType.Colon)) {
            varType = this.consume(TokenType.Identifier, "Expected type").value;
        }
        let initializer = null;
        if (this.match(TokenType.Assign)) {
            initializer = this.parseExpression();
        }
        else if (isConst) {
            throw new Error("Const variable must be initialized");
        }
        this.consumeStatementEnd();
        return {
            type: 'VarDeclaration',
            name,
            initializer,
            isConst,
            varType,
        };
    }
    parseClassDeclaration() {
        const name = this.consume(TokenType.Identifier, "Expected class name").value;
        let baseClass;
        if (this.match(TokenType.Colon)) {
            baseClass = this.consume(TokenType.Identifier, "Expected base class name").value;
        }
        this.consume(TokenType.LeftBrace, "Expected '{' before class body");
        const members = [];
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'class body parsing',
        });
        while (!this.check(TokenType.RightBrace) && !this.isAtEnd()) {
            guard();
            this.skipNewlines();
            if (this.check(TokenType.RightBrace))
                break;
            if (this.matchKeyword('func', 'function', 'async')) {
                members.push(this.parseFunctionDeclaration());
            }
            else if (this.matchKeyword('let', 'const', 'var')) {
                members.push(this.parseVarDeclaration());
            }
            else {
                // Skip unexpected tokens in class body to prevent infinite loop
                this.advance();
            }
            this.skipNewlines();
        }
        this.consume(TokenType.RightBrace, "Expected '}' after class body");
        return {
            type: 'ClassDeclaration',
            name,
            baseClass,
            members,
        };
    }
    parseIfStatement() {
        // Optional parentheses
        const hasParen = this.match(TokenType.LeftParen);
        const condition = this.parseExpression();
        if (hasParen) {
            this.consume(TokenType.RightParen, "Expected ')' after condition");
        }
        const thenBranch = this.match(TokenType.LeftBrace)
            ? this.parseBlockStatement()
            : this.parseStatement();
        let elseBranch = null;
        if (this.matchKeyword('else')) {
            if (this.matchKeyword('if')) {
                elseBranch = this.parseIfStatement();
            }
            else {
                elseBranch = this.match(TokenType.LeftBrace)
                    ? this.parseBlockStatement()
                    : this.parseStatement();
            }
        }
        return {
            type: 'If',
            condition,
            thenBranch,
            elseBranch,
        };
    }
    parseWhileStatement() {
        const hasParen = this.match(TokenType.LeftParen);
        const condition = this.parseExpression();
        if (hasParen) {
            this.consume(TokenType.RightParen, "Expected ')' after condition");
        }
        const body = this.match(TokenType.LeftBrace)
            ? this.parseBlockStatement()
            : this.parseStatement();
        return {
            type: 'While',
            condition,
            body,
        };
    }
    parseForStatement() {
        // Check for C-style for loop: for (init; test; update) { }
        if (this.match(TokenType.LeftParen)) {
            return this.parseForCStyleStatement();
        }
        // For-in loop: for x in iterable { }
        const variable = this.consume(TokenType.Identifier, "Expected variable name").value;
        this.consumeKeyword('in');
        const iterable = this.parseExpression();
        const body = this.match(TokenType.LeftBrace)
            ? this.parseBlockStatement()
            : this.parseStatement();
        return {
            type: 'For',
            variable,
            iterable,
            body,
        };
    }
    parseForCStyleStatement() {
        // Parse init
        let init = null;
        if (!this.check(TokenType.Semicolon)) {
            if (this.matchKeyword('let', 'const', 'var')) {
                init = this.parseVarDeclaration();
            }
            else {
                init = this.parseExpression();
                this.consume(TokenType.Semicolon, "Expected ';' after for init");
            }
        }
        else {
            this.advance(); // skip ;
        }
        // If parseVarDeclaration already consumed the semicolon, skip
        // Parse test
        let test = null;
        if (!this.check(TokenType.Semicolon)) {
            test = this.parseExpression();
        }
        this.consume(TokenType.Semicolon, "Expected ';' after for condition");
        // Parse update
        let update = null;
        if (!this.check(TokenType.RightParen)) {
            update = this.parseExpression();
        }
        this.consume(TokenType.RightParen, "Expected ')' after for clauses");
        // Parse body
        const body = this.match(TokenType.LeftBrace)
            ? this.parseBlockStatement()
            : this.parseStatement();
        return {
            type: 'ForCStyle',
            init,
            test,
            update,
            body,
        };
    }
    parseBlockStatement() {
        const statements = [];
        const guard = createLoopGuard({
            maxIterations: 100000,
            context: 'block statement parsing',
        });
        while (!this.check(TokenType.RightBrace) && !this.isAtEnd()) {
            guard();
            this.skipNewlines();
            if (this.check(TokenType.RightBrace))
                break;
            const stmt = this.parseDeclaration();
            if (stmt) {
                statements.push(stmt);
            }
            this.skipNewlines();
        }
        this.consume(TokenType.RightBrace, "Expected '}' after block");
        return { type: 'Block', statements };
    }
    parseExpressionStatement() {
        const expression = this.parseExpression();
        this.consumeStatementEnd();
        return { type: 'ExpressionStmt', expression };
    }
    parseReturnStatement() {
        let value = null;
        if (!this.check(TokenType.Newline) && !this.check(TokenType.Semicolon) && !this.check(TokenType.RightBrace)) {
            value = this.parseExpression();
        }
        this.consumeStatementEnd();
        return { type: 'Return', value };
    }
    parseBreakStatement() {
        this.consumeStatementEnd();
        return { type: 'Break' };
    }
    parseContinueStatement() {
        this.consumeStatementEnd();
        return { type: 'Continue' };
    }
    parseTryStatement() {
        this.consume(TokenType.LeftBrace, "Expected '{' after 'try'");
        const tryBlock = this.parseBlockStatement();
        const catchClauses = [];
        const catchGuard = createLoopGuard({
            maxIterations: 1000,
            context: 'catch clause parsing',
        });
        while (this.matchKeyword('catch')) {
            catchGuard();
            let variable;
            let type;
            if (this.match(TokenType.LeftParen)) {
                if (this.match(TokenType.Identifier)) {
                    variable = this.previous().value;
                    if (this.match(TokenType.Colon)) {
                        type = this.consume(TokenType.Identifier, "Expected exception type").value;
                    }
                }
                this.consume(TokenType.RightParen, "Expected ')' after catch parameters");
            }
            this.consume(TokenType.LeftBrace, "Expected '{' after catch");
            const body = this.parseBlockStatement();
            catchClauses.push({ variable, type, body });
        }
        let finallyBlock = null;
        if (this.matchKeyword('finally')) {
            this.consume(TokenType.LeftBrace, "Expected '{' after 'finally'");
            finallyBlock = this.parseBlockStatement();
        }
        return {
            type: 'Try',
            tryBlock,
            catchClauses,
            finallyBlock,
        };
    }
    parseThrowStatement() {
        const expression = this.parseExpression();
        this.consumeStatementEnd();
        return { type: 'Throw', expression };
    }
    parseImportStatement() {
        const names = [];
        let module = '';
        let alias;
        if (this.match(TokenType.LeftBrace)) {
            // import { name1, name2 } from "module"
            do {
                names.push(this.consume(TokenType.Identifier, "Expected import name").value);
            } while (this.match(TokenType.Comma));
            this.consume(TokenType.RightBrace, "Expected '}' after import names");
            this.consumeKeyword('from');
            module = this.consume(TokenType.String, "Expected module name").value;
        }
        else if (this.match(TokenType.Multiply)) {
            // import * as alias from "module"
            this.consumeKeyword('as');
            alias = this.consume(TokenType.Identifier, "Expected alias name").value;
            this.consumeKeyword('from');
            module = this.consume(TokenType.String, "Expected module name").value;
        }
        else if (this.check(TokenType.String)) {
            // import "module"
            module = this.consume(TokenType.String, "Expected module name").value;
        }
        else if (this.check(TokenType.Identifier)) {
            // import name OR import name from "module"
            const firstIdent = this.consume(TokenType.Identifier, "Expected import name").value;
            if (this.checkKeyword('from')) {
                names.push(firstIdent);
                this.consumeKeyword('from');
                module = this.consume(TokenType.String, "Expected module name").value;
            }
            else {
                // Simple import like: import fc
                module = firstIdent;
            }
        }
        this.consumeStatementEnd();
        return {
            type: 'Import',
            module,
            names,
            alias,
        };
    }
    parseExportStatement() {
        // export let/const/function/class
        const declaration = this.parseDeclaration();
        if (!declaration) {
            throw new Error("Expected declaration after 'export'");
        }
        return {
            type: 'Export',
            declaration,
        };
    }
    parseTestDeclaration() {
        const name = this.consume(TokenType.String, "Expected test name as string").value;
        const tags = [];
        if (this.match(TokenType.LeftBracket)) {
            do {
                if (this.match(TokenType.String)) {
                    tags.push(this.previous().value);
                }
            } while (this.match(TokenType.Comma));
            this.consume(TokenType.RightBracket, "Expected ']' after tags");
        }
        this.consume(TokenType.LeftBrace, "Expected '{' before test body");
        const body = this.parseBlockStatement();
        return {
            type: 'TestDeclaration',
            name,
            body,
            tags,
        };
    }
    parseAssertStatement() {
        const condition = this.parseExpression();
        let message;
        if (this.match(TokenType.Comma)) {
            if (this.match(TokenType.String)) {
                message = this.previous().value;
            }
        }
        this.consumeStatementEnd();
        return {
            type: 'Assert',
            condition,
            message,
        };
    }
    // ============================================
    // Expression Parsing
    // ============================================
    parseExpression() {
        return this.parseAssignment();
    }
    parseAssignment() {
        const expr = this.parseTernary();
        if (this.match(TokenType.Assign, TokenType.PlusAssign, TokenType.MinusAssign, TokenType.MultiplyAssign, TokenType.DivideAssign)) {
            const operator = this.previous().type;
            const value = this.parseAssignment();
            return {
                type: 'Assignment',
                target: expr,
                operator,
                value,
            };
        }
        return expr;
    }
    parseTernary() {
        let expr = this.parsePipeline();
        // Use dedicated Question token
        if (this.match(TokenType.Question)) {
            const consequent = this.parseExpression();
            this.consume(TokenType.Colon, "Expected ':' in ternary expression");
            const alternate = this.parseTernary();
            return {
                type: 'Ternary',
                condition: expr,
                consequent,
                alternate,
            };
        }
        return expr;
    }
    parsePipeline() {
        let expr = this.parseLogicalOr();
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'pipeline expression parsing',
        });
        while (this.match(TokenType.Pipeline)) {
            guard();
            const right = this.parseLogicalOr();
            // Transform pipeline: expr |> func => func(expr)
            if (right.type === 'Call') {
                const call = right;
                call.arguments.unshift(expr);
                expr = call;
            }
            else if (right.type === 'Identifier') {
                expr = {
                    type: 'Call',
                    callee: right,
                    arguments: [expr],
                };
            }
            else {
                throw new Error("Invalid pipeline target");
            }
        }
        return expr;
    }
    parseLogicalOr() {
        let expr = this.parseLogicalAnd();
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'logical OR expression parsing',
        });
        while (this.match(TokenType.Or)) {
            guard();
            const operator = this.previous().type;
            const right = this.parseLogicalAnd();
            expr = { type: 'Binary', left: expr, operator, right };
        }
        return expr;
    }
    parseLogicalAnd() {
        let expr = this.parseEquality();
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'logical AND expression parsing',
        });
        while (this.match(TokenType.And)) {
            guard();
            const operator = this.previous().type;
            const right = this.parseEquality();
            expr = { type: 'Binary', left: expr, operator, right };
        }
        return expr;
    }
    parseEquality() {
        let expr = this.parseComparison();
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'equality expression parsing',
        });
        while (this.match(TokenType.Equal, TokenType.NotEqual)) {
            guard();
            const operator = this.previous().type;
            const right = this.parseComparison();
            expr = { type: 'Binary', left: expr, operator, right };
        }
        return expr;
    }
    parseComparison() {
        let expr = this.parseAddition();
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'comparison expression parsing',
        });
        while (this.match(TokenType.Less, TokenType.Greater, TokenType.LessEqual, TokenType.GreaterEqual)) {
            guard();
            const operator = this.previous().type;
            const right = this.parseAddition();
            expr = { type: 'Binary', left: expr, operator, right };
        }
        return expr;
    }
    parseAddition() {
        let expr = this.parseMultiplication();
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'addition expression parsing',
        });
        while (this.match(TokenType.Plus, TokenType.Minus)) {
            guard();
            const operator = this.previous().type;
            const right = this.parseMultiplication();
            expr = { type: 'Binary', left: expr, operator, right };
        }
        return expr;
    }
    parseMultiplication() {
        let expr = this.parsePower();
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'multiplication expression parsing',
        });
        while (this.match(TokenType.Multiply, TokenType.Divide, TokenType.Modulo)) {
            guard();
            const operator = this.previous().type;
            const right = this.parsePower();
            expr = { type: 'Binary', left: expr, operator, right };
        }
        return expr;
    }
    parsePower() {
        let expr = this.parseUnary();
        if (this.match(TokenType.Power)) {
            const right = this.parsePower(); // Right associative
            expr = { type: 'Binary', left: expr, operator: TokenType.Power, right };
        }
        return expr;
    }
    parseUnary() {
        if (this.match(TokenType.Not, TokenType.Minus)) {
            const operator = this.previous().type;
            const operand = this.parseUnary();
            return { type: 'Unary', operator, operand };
        }
        // Await expression (from Buddy Script)
        if (this.matchKeyword('await')) {
            const argument = this.parseUnary();
            return { type: 'Await', argument };
        }
        return this.parsePostfix();
    }
    parsePostfix() {
        let expr = this.parsePrimary();
        // Guard against infinite loops in postfix parsing
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'postfix expression parsing',
        });
        while (true) {
            guard();
            if (this.match(TokenType.LeftParen)) {
                expr = this.parseCall(expr);
            }
            else if (this.match(TokenType.LeftBracket)) {
                const index = this.parseExpression();
                this.consume(TokenType.RightBracket, "Expected ']' after index");
                expr = { type: 'Index', object: expr, index };
            }
            else if (this.match(TokenType.Dot)) {
                const member = this.consume(TokenType.Identifier, "Expected member name").value;
                expr = { type: 'Member', object: expr, member, computed: false };
            }
            else {
                break;
            }
        }
        return expr;
    }
    parseCall(callee) {
        const args = [];
        const namedArgs = {};
        if (!this.check(TokenType.RightParen)) {
            do {
                // Check for named argument: name: value
                if (this.check(TokenType.Identifier) && this.checkNext(TokenType.Colon)) {
                    const name = this.advance().value;
                    this.advance(); // :
                    namedArgs[name] = this.parseExpression();
                }
                else {
                    args.push(this.parseExpression());
                }
            } while (this.match(TokenType.Comma));
        }
        this.consume(TokenType.RightParen, "Expected ')' after arguments");
        const result = {
            type: 'Call',
            callee,
            arguments: args,
        };
        if (Object.keys(namedArgs).length > 0) {
            result.namedArgs = namedArgs;
        }
        return result;
    }
    parsePrimary() {
        if (this.match(TokenType.Boolean)) {
            return {
                type: 'Literal',
                value: this.previous().value === 'true',
                tokenType: TokenType.Boolean,
            };
        }
        if (this.match(TokenType.Null)) {
            return { type: 'Literal', value: null, tokenType: TokenType.Null };
        }
        if (this.match(TokenType.Number)) {
            const value = this.previous().value;
            const num = value.includes('.') ? parseFloat(value) : parseInt(value, 10);
            return { type: 'Literal', value: num, tokenType: TokenType.Number };
        }
        if (this.match(TokenType.String)) {
            const value = this.previous().value;
            // Check for interpolation
            if (value.includes('${')) {
                return this.parseStringInterpolation(value);
            }
            return { type: 'Literal', value, tokenType: TokenType.String };
        }
        if (this.match(TokenType.Identifier)) {
            return { type: 'Identifier', name: this.previous().value };
        }
        if (this.match(TokenType.LeftParen)) {
            // Check for lambda / arrow function
            if (this.check(TokenType.Identifier) || this.check(TokenType.RightParen)) {
                const checkpoint = this.current;
                const parameters = [];
                if (!this.check(TokenType.RightParen)) {
                    do {
                        if (this.match(TokenType.Identifier)) {
                            parameters.push(this.previous().value);
                        }
                        else {
                            this.current = checkpoint;
                            break;
                        }
                    } while (this.match(TokenType.Comma));
                }
                if (this.current !== checkpoint && this.match(TokenType.RightParen) && this.match(TokenType.Arrow)) {
                    const body = this.match(TokenType.LeftBrace)
                        ? this.parseBlockStatement()
                        : this.parseExpression();
                    return { type: 'Lambda', parameters, body };
                }
                this.current = checkpoint;
            }
            const expr = this.parseExpression();
            this.consume(TokenType.RightParen, "Expected ')' after expression");
            return expr;
        }
        if (this.match(TokenType.LeftBracket)) {
            return this.parseArrayLiteral();
        }
        if (this.match(TokenType.LeftBrace)) {
            return this.parseDictLiteral();
        }
        throw new Error(`Unexpected token: ${this.peek().type} (${this.peek().value}) at line ${this.peek().line}`);
    }
    parseStringInterpolation(value) {
        const parts = [];
        let current = 0;
        const guard = createLoopGuard({
            maxIterations: 10000,
            context: 'string interpolation parsing',
        });
        while (current < value.length) {
            guard();
            const start = value.indexOf('${', current);
            if (start === -1) {
                if (current < value.length) {
                    parts.push({
                        type: 'Literal',
                        value: value.substring(current),
                        tokenType: TokenType.String,
                    });
                }
                break;
            }
            if (start > current) {
                parts.push({
                    type: 'Literal',
                    value: value.substring(current, start),
                    tokenType: TokenType.String,
                });
            }
            const end = value.indexOf('}', start + 2);
            if (end === -1) {
                throw new Error("Unterminated string interpolation");
            }
            const exprCode = value.substring(start + 2, end);
            const tokens = lexerTokenize(exprCode);
            const parser = new FCSParser(tokens);
            parts.push(parser.parseExpression());
            current = end + 1;
        }
        return { type: 'Interpolation', parts };
    }
    parseArrayLiteral() {
        const elements = [];
        if (!this.check(TokenType.RightBracket)) {
            do {
                elements.push(this.parseExpression());
            } while (this.match(TokenType.Comma));
        }
        this.consume(TokenType.RightBracket, "Expected ']' after array elements");
        return { type: 'Array', elements };
    }
    parseDictLiteral() {
        const elements = new Map();
        if (!this.check(TokenType.RightBrace)) {
            do {
                let key;
                if (this.match(TokenType.String)) {
                    key = this.previous().value;
                }
                else if (this.match(TokenType.Identifier)) {
                    key = this.previous().value;
                }
                else {
                    throw new Error("Expected string or identifier for dict key");
                }
                this.consume(TokenType.Colon, "Expected ':' after dict key");
                const value = this.parseExpression();
                elements.set(key, value);
            } while (this.match(TokenType.Comma));
        }
        this.consume(TokenType.RightBrace, "Expected '}' after dict elements");
        return { type: 'Dict', elements };
    }
    // ============================================
    // Helper Methods
    // ============================================
    isAtEnd() {
        return this.peek().type === TokenType.EOF;
    }
    peek() {
        return this.tokens[Math.min(this.current, this.tokens.length - 1)];
    }
    previous() {
        return this.tokens[this.current - 1];
    }
    advance() {
        if (!this.isAtEnd())
            this.current++;
        return this.previous();
    }
    check(type) {
        return this.peek().type === type;
    }
    checkNext(type) {
        if (this.current + 1 >= this.tokens.length)
            return false;
        return this.tokens[this.current + 1].type === type;
    }
    checkKeyword(...keywords) {
        if (this.peek().type !== TokenType.Keyword)
            return false;
        return keywords.includes(this.peek().value);
    }
    match(...types) {
        for (const type of types) {
            if (this.check(type)) {
                this.advance();
                return true;
            }
        }
        return false;
    }
    matchKeyword(...keywords) {
        if (this.checkKeyword(...keywords)) {
            this.advance();
            return true;
        }
        return false;
    }
    consume(type, message) {
        if (this.check(type))
            return this.advance();
        throw new Error(`${message} at line ${this.peek().line}, got ${this.peek().type}`);
    }
    consumeKeyword(...keywords) {
        if (this.checkKeyword(...keywords)) {
            this.advance();
        }
        else {
            throw new Error(`Expected one of [${keywords.join(', ')}] at line ${this.peek().line}`);
        }
    }
    consumeStatementEnd() {
        if (this.match(TokenType.Semicolon) || this.match(TokenType.Newline)) {
            return;
        }
        if (this.isAtEnd())
            return;
        // Allow implicit semicolon before certain tokens
        if (this.check(TokenType.RightBrace) || this.checkKeyword('else')) {
            return;
        }
    }
    skipNewlines() {
        const guard = createLoopGuard({
            maxIterations: 100000,
            context: 'newline skipping',
        });
        while (this.match(TokenType.Newline)) {
            guard();
            // Skip
        }
    }
    synchronize() {
        this.advance();
        const guard = createLoopGuard({
            maxIterations: 100000,
            context: 'error synchronization',
        });
        while (!this.isAtEnd()) {
            guard();
            if (this.previous().type === TokenType.Semicolon)
                return;
            if (this.previous().type === TokenType.Newline)
                return;
            if (this.checkKeyword('if', 'for', 'while', 'let', 'const', 'func', 'function', 'class', 'return')) {
                return;
            }
            this.advance();
        }
    }
}
/** @deprecated Use FCSParser instead */
export const Parser = FCSParser;
export function parse(tokens) {
    return new FCSParser(tokens).parse();
}
//# sourceMappingURL=parser.js.map