"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Parser = void 0;
const PRECEDENCES = {
    "=": 2 /* Precedence.Assign */,
    "|>": 3 /* Precedence.Pipe */,
    "||": 4 /* Precedence.Or */,
    "&&": 5 /* Precedence.And */,
    OR: 4 /* Precedence.Or */,
    AND: 5 /* Precedence.And */,
    "==": 6 /* Precedence.Equals */,
    "!=": 6 /* Precedence.Equals */,
    "<": 7 /* Precedence.LessGreater */,
    ">": 7 /* Precedence.LessGreater */,
    "<=": 7 /* Precedence.LessGreater */,
    ">=": 7 /* Precedence.LessGreater */,
    "+": 8 /* Precedence.Sum */,
    "-": 8 /* Precedence.Sum */,
    "/": 9 /* Precedence.Product */,
    "*": 9 /* Precedence.Product */,
    "%": 9 /* Precedence.Product */,
    "(": 11 /* Precedence.Call */,
    "!": 11 /* Precedence.Call */,
    "::": 11 /* Precedence.Call */,
    "[": 12 /* Precedence.Index */,
    ".": 12 /* Precedence.Index */,
};
/**
 * Recursive-descent / Pratt parser for Nexus source.
 */
class Parser {
    lexer;
    curToken;
    peekToken;
    errors = [];
    prefixParseFns = new Map();
    infixParseFns = new Map();
    constructor(lexer) {
        this.lexer = lexer;
        this.registerPrefix("IDENT", () => this.parseIdentifierOrConstructor());
        this.registerPrefix("TYPE", () => this.parseKeywordAsIdent("type"));
        this.registerPrefix("INT", () => this.parseIntegerLiteral());
        this.registerPrefix("STRING", () => this.parseStringLiteral());
        this.registerPrefix("!", () => this.parsePrefixExpression());
        this.registerPrefix("-", () => this.parsePrefixExpression());
        this.registerPrefix("&", () => this.parseRefExpression());
        this.registerPrefix("TRUE", () => this.parseBoolean());
        this.registerPrefix("FALSE", () => this.parseBoolean());
        this.registerPrefix("NULL", () => this.parseNullLiteral());
        this.registerPrefix("(", () => this.parseGroupedExpression());
        this.registerPrefix("IF", () => this.parseIfExpression());
        this.registerPrefix("FN", () => this.parseFunctionLiteral(false));
        this.registerPrefix("ASYNC", () => this.parseAsyncExpression());
        this.registerPrefix("AWAIT", () => this.parseAwaitExpression());
        this.registerPrefix("SPAWN", () => this.parseSpawnExpression());
        this.registerPrefix("CHAN", () => this.parseChanExpression());
        this.registerPrefix("MATCH", () => this.parseMatchExpression());
        this.registerPrefix("MOVE", () => this.parseMoveExpression());
        this.registerPrefix("PERFORM", () => this.parsePerformExpression());
        this.registerPrefix("HANDLE", () => this.parseHandleExpression());
        this.registerPrefix("REGION", () => this.parseRegionExpression());
        this.registerPrefix("REFLECT", () => this.parseReflectExpression());
        this.registerPrefix("[", () => this.parseArrayLiteral());
        this.registerPrefix("{", () => this.parseHashOrBlock());
        this.registerInfix("+", (left) => this.parseInfixExpression(left));
        this.registerInfix("-", (left) => this.parseInfixExpression(left));
        this.registerInfix("/", (left) => this.parseInfixExpression(left));
        this.registerInfix("*", (left) => this.parseInfixExpression(left));
        this.registerInfix("%", (left) => this.parseInfixExpression(left));
        this.registerInfix("==", (left) => this.parseInfixExpression(left));
        this.registerInfix("!=", (left) => this.parseInfixExpression(left));
        this.registerInfix("<", (left) => this.parseInfixExpression(left));
        this.registerInfix(">", (left) => this.parseInfixExpression(left));
        this.registerInfix("<=", (left) => this.parseInfixExpression(left));
        this.registerInfix(">=", (left) => this.parseInfixExpression(left));
        this.registerInfix("&&", (left) => this.parseInfixExpression(left));
        this.registerInfix("||", (left) => this.parseInfixExpression(left));
        this.registerInfix("AND", (left) => this.parseInfixExpression(left));
        this.registerInfix("OR", (left) => this.parseInfixExpression(left));
        this.registerInfix("(", (left) => this.parseCallExpression(left));
        this.registerInfix("!", (left) => this.parseMacroInvocation(left));
        this.registerInfix("::", (left) => this.parsePathConstructor(left));
        this.registerInfix("[", (left) => this.parseIndexExpression(left));
        this.registerInfix(".", (left) => this.parseMemberExpression(left));
        this.registerInfix("=", (left) => this.parseAssignExpression(left));
        this.registerInfix("|>", (left) => this.parsePipeExpression(left));
        this.nextToken();
        this.nextToken();
    }
    getErrors() {
        return this.errors;
    }
    parseProgram() {
        const program = {
            type: "Program",
            statements: [],
            tokenLiteral: () => program.statements.length > 0
                ? program.statements[0].tokenLiteral()
                : "",
        };
        while (this.curToken.type !== "EOF") {
            const stmt = this.parseStatement();
            if (stmt) {
                program.statements.push(stmt);
            }
            this.nextToken();
        }
        for (const err of this.lexer.getErrors()) {
            this.errors.push(err);
        }
        return program;
    }
    registerPrefix(type, fn) {
        this.prefixParseFns.set(type, fn);
    }
    registerInfix(type, fn) {
        this.infixParseFns.set(type, fn);
    }
    nextToken() {
        this.curToken = this.peekToken;
        this.peekToken = this.lexer.nextToken();
    }
    parseStatement() {
        switch (this.curToken.type) {
            case "LET":
                return this.parseLetStatement();
            case "RETURN":
                return this.parseReturnStatement();
            case "IMPORT":
                return this.parseImportStatement();
            case "WHILE":
                return this.parseWhileStatement();
            case "BREAK":
                return this.parseBreakStatement();
            case "CONTINUE":
                return this.parseContinueStatement();
            case "ENUM":
                return this.parseEnumDeclaration();
            case "MACRO":
                return this.parseMacroDefinition();
            case "EXTERN":
                return this.parseExternDeclaration();
            case "EFFECT":
                return this.parseEffectDeclaration();
            case "STRUCT":
                return this.parseStructDeclaration();
            case ";":
                return null;
            default:
                return this.parseExpressionStatement();
        }
    }
    parseLetStatement() {
        const token = this.curToken;
        let mutable = false;
        if (this.peekTokenIs("MUT")) {
            this.nextToken();
            mutable = true;
        }
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const name = {
            kind: "expression",
            type: "Identifier",
            token: this.curToken,
            value: this.curToken.literal,
            tokenLiteral: () => name.token.literal,
        };
        let typeAnnotation = null;
        if (this.peekTokenIs(":")) {
            this.nextToken();
            typeAnnotation = this.parseTypeAnnotation();
        }
        if (!this.expectPeek("=")) {
            return null;
        }
        this.nextToken();
        const value = this.parseExpression(1 /* Precedence.Lowest */);
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        const stmt = {
            kind: "statement",
            type: "LetStatement",
            token,
            name,
            mutable,
            typeAnnotation,
            value,
            tokenLiteral: () => token.literal,
        };
        return stmt;
    }
    parseTypeAnnotation() {
        let reference = false;
        let mutable = false;
        let token = this.peekToken;
        if (this.peekTokenIs("&")) {
            this.nextToken();
            reference = true;
            token = this.curToken;
            if (this.peekTokenIs("MUT")) {
                this.nextToken();
                mutable = true;
            }
        }
        if (!this.expectPeek("IDENT") && !this.expectPeekTypeKeyword()) {
            return null;
        }
        return {
            type: "TypeAnnotation",
            name: this.curToken.literal,
            mutable,
            reference,
            token,
        };
    }
    expectPeekTypeKeyword() {
        const typeKeywords = [
            "FN",
            "CHAN",
            "TRUE",
            "FALSE",
        ];
        // Allow common type names that are keywords? Prefer IDENT only.
        // Keep helper for future extension; currently unused path returns false.
        void typeKeywords;
        return false;
    }
    parseReturnStatement() {
        const token = this.curToken;
        this.nextToken();
        const returnValue = this.parseExpression(1 /* Precedence.Lowest */);
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "ReturnStatement",
            token,
            returnValue,
            tokenLiteral: () => token.literal,
        };
    }
    parseImportStatement() {
        const token = this.curToken;
        if (!this.expectPeek("STRING")) {
            return null;
        }
        const path = this.curToken.literal;
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "ImportStatement",
            token,
            path,
            tokenLiteral: () => token.literal,
        };
    }
    parseWhileStatement() {
        const token = this.curToken;
        if (!this.expectPeek("(")) {
            return null;
        }
        this.nextToken();
        const condition = this.parseExpression(1 /* Precedence.Lowest */);
        if (!this.expectPeek(")")) {
            return null;
        }
        if (!this.expectPeek("{")) {
            return null;
        }
        const body = this.parseBlockStatement();
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "WhileStatement",
            token,
            condition,
            body,
            tokenLiteral: () => token.literal,
        };
    }
    parseBreakStatement() {
        const token = this.curToken;
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "BreakStatement",
            token,
            tokenLiteral: () => token.literal,
        };
    }
    parseContinueStatement() {
        const token = this.curToken;
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "ContinueStatement",
            token,
            tokenLiteral: () => token.literal,
        };
    }
    parseExpressionStatement() {
        const token = this.curToken;
        const expression = this.parseExpression(1 /* Precedence.Lowest */);
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "ExpressionStatement",
            token,
            expression,
            tokenLiteral: () => token.literal,
        };
    }
    parseEnumDeclaration() {
        const token = this.curToken;
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const name = this.parseIdentifier();
        if (!this.expectPeek("{")) {
            return null;
        }
        const variants = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            if (!this.curTokenIs("IDENT")) {
                this.errors.push(`expected variant name, got ${this.curToken.type} at line ${this.curToken.line}, column ${this.curToken.column}`);
                break;
            }
            const variantName = this.parseIdentifier();
            const fields = [];
            if (this.peekTokenIs("(")) {
                this.nextToken();
                if (!this.peekTokenIs(")")) {
                    this.nextToken();
                    fields.push(this.parseIdentifier());
                    while (this.peekTokenIs(",")) {
                        this.nextToken();
                        this.nextToken();
                        fields.push(this.parseIdentifier());
                    }
                }
                if (!this.expectPeek(")")) {
                    return null;
                }
            }
            variants.push({ name: variantName, fields });
            if (this.peekTokenIs(",")) {
                this.nextToken();
                this.nextToken();
            }
            else {
                this.nextToken();
            }
        }
        if (!this.curTokenIs("}")) {
            this.errors.push(`expected '}' after enum variants at line ${this.curToken.line}, column ${this.curToken.column}`);
        }
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "EnumDeclaration",
            token,
            name,
            variants,
            tokenLiteral: () => token.literal,
        };
    }
    parseMacroDefinition() {
        const token = this.curToken;
        if (!this.expectPeek("RULES")) {
            return null;
        }
        if (!this.expectPeek("!")) {
            return null;
        }
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const name = this.parseIdentifier();
        if (!this.expectPeek("{")) {
            return null;
        }
        const rules = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            if (!this.curTokenIs("(")) {
                this.errors.push(`expected '(' to start macro pattern at line ${this.curToken.line}, column ${this.curToken.column}`);
                break;
            }
            const pattern = this.parseMacroPattern();
            if (!this.expectPeek("=>")) {
                return null;
            }
            this.nextToken();
            const body = this.parseExpression(1 /* Precedence.Lowest */);
            if (!body) {
                return null;
            }
            rules.push({ pattern, body });
            if (this.peekTokenIs(";")) {
                this.nextToken();
            }
            if (this.peekTokenIs(",")) {
                this.nextToken();
            }
            this.nextToken();
        }
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "MacroDefinition",
            token,
            name,
            rules,
            tokenLiteral: () => token.literal,
        };
    }
    parseMacroPattern() {
        const fragments = [];
        this.nextToken(); // skip '('
        while (!this.curTokenIs(")") && !this.curTokenIs("EOF")) {
            if (this.curTokenIs("IDENT") && this.curToken.literal.startsWith("$")) {
                const metaName = this.curToken.literal.slice(1);
                if (this.peekTokenIs(":")) {
                    this.nextToken();
                    this.nextToken();
                    const kindLiteral = this.curToken.literal;
                    const kind = kindLiteral === "expr" ||
                        kindLiteral === "ident" ||
                        kindLiteral === "tt"
                        ? kindLiteral
                        : "expr";
                    fragments.push({ kind, name: metaName });
                }
                else {
                    fragments.push({ kind: "expr", name: metaName });
                }
            }
            else if (this.curTokenIs("IDENT") ||
                this.curTokenIs("INT") ||
                this.curTokenIs("STRING") ||
                this.curToken.type === "," ||
                this.curToken.type === "+" ||
                this.curToken.type === "-" ||
                this.curToken.type === "*" ||
                this.curToken.type === "/" ||
                this.curToken.type === "!" ||
                this.curToken.type === "=" ||
                this.curToken.type === "(" ||
                this.curToken.type === ")" ||
                this.curToken.type === "{" ||
                this.curToken.type === "}") {
                fragments.push({
                    kind: "literal",
                    literal: this.curToken.literal,
                    token: this.curToken,
                });
            }
            else {
                fragments.push({
                    kind: "literal",
                    literal: this.curToken.literal,
                    token: this.curToken,
                });
            }
            this.nextToken();
        }
        return fragments;
    }
    parseExternDeclaration() {
        const token = this.curToken;
        // extern "C" from "lib.dll" { fn name(a, b) -> int; }
        if (!this.expectPeek("STRING")) {
            // allow skipping ABI string
            if (this.peekTokenIs("FROM")) {
                // ok
            }
            else if (this.curTokenIs("IDENT") || this.peekTokenIs("IDENT")) {
                // optional ABI identifier like C
                if (this.peekTokenIs("IDENT") || this.peekTokenIs("STRING")) {
                    this.nextToken();
                }
            }
        }
        let library = "";
        if (this.peekTokenIs("FROM")) {
            this.nextToken();
            if (!this.expectPeek("STRING")) {
                return null;
            }
            library = this.curToken.literal;
        }
        else if (this.curTokenIs("STRING")) {
            library = this.curToken.literal;
            if (this.peekTokenIs("FROM")) {
                this.nextToken();
                if (!this.expectPeek("STRING")) {
                    return null;
                }
                library = this.curToken.literal;
            }
        }
        else if (this.peekTokenIs("STRING")) {
            this.nextToken();
            library = this.curToken.literal;
        }
        if (!this.expectPeek("{")) {
            return null;
        }
        const functions = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            if (!this.curTokenIs("FN")) {
                this.errors.push(`expected fn in extern block at line ${this.curToken.line}, column ${this.curToken.column}`);
                break;
            }
            if (!this.expectPeek("IDENT")) {
                return null;
            }
            const fname = this.parseIdentifier();
            if (!this.expectPeek("(")) {
                return null;
            }
            const parameters = this.parseFunctionParameters();
            if (!parameters) {
                return null;
            }
            let returnType = "void";
            if (this.peekTokenIs("-") && this.peekTokenLiteralIsBeyond(">", 1)) {
                // handle -> 
                this.nextToken(); // -
                if (this.peekTokenIs(">") || this.curToken.literal === "-") {
                    // We used separate tokens; support IDENT after "->" via ">" then type
                }
            }
            // Parse "->" as "-" then we need ">" - but ">" is a token. Use "-" followed by checking...
            // Better: look for custom. We'll accept: ) IDENT for return type after optional :
            if (this.peekTokenIs(":")) {
                this.nextToken();
                this.nextToken();
                returnType = this.curToken.literal;
            }
            else if (this.peekTokenIs("IDENT")) {
                // allow `fn foo(x) int`
                this.nextToken();
                returnType = this.curToken.literal;
            }
            // Also support `-> type` where - and > are separate... 
            // After parameters we're at ')'. Check peek for '-'
            // Actually parseFunctionParameters already consumed ')'.
            // Re-check: if next is '-' then '>' then type
            if (this.peekTokenIs("-")) {
                this.nextToken();
                if (this.peekTokenIs(">") || this.curTokenIs("-")) {
                    if (this.peekTokenIs(">")) {
                        this.nextToken();
                    }
                    this.nextToken();
                    returnType = this.curToken.literal;
                }
            }
            functions.push({ name: fname, parameters, returnType });
            if (this.peekTokenIs(";")) {
                this.nextToken();
            }
            this.nextToken();
        }
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "ExternDeclaration",
            token,
            library,
            functions,
            tokenLiteral: () => token.literal,
        };
    }
    peekTokenLiteralIsBeyond(lit, _n) {
        const snap = this.lexer.checkpoint();
        const next = this.lexer.nextToken();
        this.lexer.restore(snap);
        return next.literal === lit || next.type === lit;
    }
    parseExpression(precedence) {
        const prefix = this.prefixParseFns.get(this.curToken.type);
        if (!prefix) {
            this.errors.push(`no prefix parse function for ${this.curToken.type} at line ${this.curToken.line}, column ${this.curToken.column}`);
            return null;
        }
        let left = prefix();
        if (!left) {
            return null;
        }
        while (!this.peekTokenIs(";") &&
            precedence < this.peekPrecedence()) {
            const infix = this.infixParseFns.get(this.peekToken.type);
            if (!infix) {
                return left;
            }
            this.nextToken();
            left = infix(left);
            if (!left) {
                return null;
            }
        }
        return left;
    }
    parseIdentifier() {
        const ident = {
            kind: "expression",
            type: "Identifier",
            token: this.curToken,
            value: this.curToken.literal,
            tokenLiteral: () => ident.token.literal,
        };
        return ident;
    }
    /** Allow reserved words like `type` to be used as callables/idents. */
    parseKeywordAsIdent(name) {
        const ident = {
            kind: "expression",
            type: "Identifier",
            token: this.curToken,
            value: name,
            tokenLiteral: () => name,
        };
        return ident;
    }
    parseIdentifierOrConstructor() {
        const ident = this.parseIdentifier();
        // Bare Variant(args) is handled via call expression on Identifier.
        // Enum::Variant handled via :: infix.
        return ident;
    }
    parseIntegerLiteral() {
        const token = this.curToken;
        const value = Number.parseInt(token.literal, 10);
        if (Number.isNaN(value)) {
            this.errors.push(`could not parse ${JSON.stringify(token.literal)} as integer at line ${token.line}, column ${token.column}`);
            return null;
        }
        return {
            kind: "expression",
            type: "IntegerLiteral",
            token,
            value,
            tokenLiteral: () => token.literal,
        };
    }
    parseStringLiteral() {
        const token = this.curToken;
        return {
            kind: "expression",
            type: "StringLiteral",
            token,
            value: token.literal,
            tokenLiteral: () => token.literal,
        };
    }
    parseBoolean() {
        const token = this.curToken;
        return {
            kind: "expression",
            type: "BooleanLiteral",
            token,
            value: this.curTokenIs("TRUE"),
            tokenLiteral: () => token.literal,
        };
    }
    parseNullLiteral() {
        const token = this.curToken;
        return {
            kind: "expression",
            type: "NullLiteral",
            token,
            tokenLiteral: () => token.literal,
        };
    }
    parseArrayLiteral() {
        const token = this.curToken;
        const elements = this.parseExpressionList("]");
        if (!elements) {
            return null;
        }
        return {
            kind: "expression",
            type: "ArrayLiteral",
            token,
            elements,
            tokenLiteral: () => token.literal,
        };
    }
    parseHashOrBlock() {
        // Distinguish `{ "k": v }` / `{ k: v }` hashes from block expressions.
        if (this.peekTokenIs("}")) {
            return this.parseHashLiteral();
        }
        if ((this.peekTokenIs("STRING") || this.peekTokenIs("IDENT")) &&
            this.peekTokenLiteralIsBeyond(":", 1)) {
            return this.parseHashLiteral();
        }
        return this.parseBlockExpression();
    }
    parseHashLiteral() {
        const token = this.curToken;
        const pairs = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            const key = this.parseExpression(1 /* Precedence.Lowest */);
            if (!key || !this.expectPeek(":")) {
                return null;
            }
            this.nextToken();
            const value = this.parseExpression(1 /* Precedence.Lowest */);
            if (!value) {
                return null;
            }
            pairs.push({ key, value });
            if (this.peekTokenIs(",")) {
                this.nextToken();
            }
            this.nextToken();
        }
        if (!this.curTokenIs("}")) {
            this.errors.push(`expected '}' after hash literal at line ${this.curToken.line}, column ${this.curToken.column}`);
            return null;
        }
        return {
            kind: "expression",
            type: "HashLiteral",
            token,
            pairs,
            tokenLiteral: () => token.literal,
        };
    }
    parseIndexExpression(left) {
        const token = this.curToken;
        this.nextToken();
        const index = this.parseExpression(1 /* Precedence.Lowest */);
        if (!this.expectPeek("]")) {
            return null;
        }
        return {
            kind: "expression",
            type: "IndexExpression",
            token,
            left,
            index,
            tokenLiteral: () => token.literal,
        };
    }
    parseMemberExpression(left) {
        const token = this.curToken;
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const property = this.parseIdentifier();
        return {
            kind: "expression",
            type: "MemberExpression",
            token,
            object: left,
            property,
            tokenLiteral: () => token.literal,
        };
    }
    parseAssignExpression(left) {
        const token = this.curToken;
        this.nextToken();
        const value = this.parseExpression(1 /* Precedence.Lowest */);
        return {
            kind: "expression",
            type: "AssignExpression",
            token,
            name: left,
            value,
            tokenLiteral: () => token.literal,
        };
    }
    parsePipeExpression(left) {
        const token = this.curToken;
        this.nextToken();
        const right = this.parseExpression(3 /* Precedence.Pipe */);
        return {
            kind: "expression",
            type: "PipeExpression",
            token,
            left,
            right,
            tokenLiteral: () => token.literal,
        };
    }
    parsePrefixExpression() {
        const token = this.curToken;
        const operator = token.literal;
        this.nextToken();
        const right = this.parseExpression(10 /* Precedence.Prefix */);
        return {
            kind: "expression",
            type: "PrefixExpression",
            token,
            operator,
            right,
            tokenLiteral: () => token.literal,
        };
    }
    parseRefExpression() {
        const token = this.curToken;
        let mutable = false;
        if (this.peekTokenIs("MUT")) {
            this.nextToken();
            mutable = true;
        }
        this.nextToken();
        const value = this.parseExpression(10 /* Precedence.Prefix */);
        return {
            kind: "expression",
            type: "RefExpression",
            token,
            mutable,
            value,
            tokenLiteral: () => token.literal,
        };
    }
    parseMoveExpression() {
        const token = this.curToken;
        this.nextToken();
        const value = this.parseExpression(10 /* Precedence.Prefix */);
        return {
            kind: "expression",
            type: "MoveExpression",
            token,
            value,
            tokenLiteral: () => token.literal,
        };
    }
    parseInfixExpression(left) {
        const token = this.curToken;
        const operator = token.literal;
        const precedence = this.curPrecedence();
        this.nextToken();
        const right = this.parseExpression(precedence);
        return {
            kind: "expression",
            type: "InfixExpression",
            token,
            left,
            operator,
            right,
            tokenLiteral: () => token.literal,
        };
    }
    parseGroupedExpression() {
        this.nextToken();
        const exp = this.parseExpression(1 /* Precedence.Lowest */);
        if (!this.expectPeek(")")) {
            return null;
        }
        return exp;
    }
    parseBlockExpression() {
        const block = this.parseBlockStatement();
        // Blocks are statements structurally but valid as expression bodies
        // (match arms, async bodies, etc.).
        const asExpr = block;
        asExpr.kind = "expression";
        return asExpr;
    }
    parseIfExpression() {
        const token = this.curToken;
        if (!this.expectPeek("(")) {
            return null;
        }
        this.nextToken();
        const condition = this.parseExpression(1 /* Precedence.Lowest */);
        if (!this.expectPeek(")")) {
            return null;
        }
        if (!this.expectPeek("{")) {
            return null;
        }
        const consequence = this.parseBlockStatement();
        let alternative = null;
        if (this.peekTokenIs("ELSE")) {
            this.nextToken();
            if (!this.expectPeek("{")) {
                return null;
            }
            alternative = this.parseBlockStatement();
        }
        return {
            kind: "expression",
            type: "IfExpression",
            token,
            condition,
            consequence,
            alternative,
            tokenLiteral: () => token.literal,
        };
    }
    parseBlockStatement() {
        const token = this.curToken;
        const statements = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            const stmt = this.parseStatement();
            if (stmt) {
                statements.push(stmt);
            }
            this.nextToken();
        }
        if (this.curTokenIs("EOF")) {
            this.errors.push("unexpected EOF while parsing block statement");
        }
        return {
            kind: "statement",
            type: "BlockStatement",
            token,
            statements,
            tokenLiteral: () => token.literal,
        };
    }
    parseFunctionLiteral(isAsync) {
        const token = this.curToken;
        if (!this.expectPeek("(")) {
            return null;
        }
        const parameters = this.parseFunctionParameters();
        if (!parameters) {
            return null;
        }
        if (!this.expectPeek("{")) {
            return null;
        }
        const body = this.parseBlockStatement();
        return {
            kind: "expression",
            type: "FunctionLiteral",
            token,
            parameters,
            body,
            isAsync,
            tokenLiteral: () => token.literal,
        };
    }
    parseAsyncExpression() {
        const token = this.curToken;
        if (this.peekTokenIs("FN")) {
            this.nextToken();
            return this.parseFunctionLiteral(true);
        }
        if (this.peekTokenIs("{")) {
            this.nextToken();
            const body = this.parseBlockStatement();
            const node = {
                kind: "expression",
                type: "AsyncExpression",
                token,
                body,
                tokenLiteral: () => token.literal,
            };
            return node;
        }
        this.nextToken();
        const body = this.parseExpression(10 /* Precedence.Prefix */);
        if (!body) {
            return null;
        }
        const node = {
            kind: "expression",
            type: "AsyncExpression",
            token,
            body,
            tokenLiteral: () => token.literal,
        };
        return node;
    }
    parseAwaitExpression() {
        const token = this.curToken;
        this.nextToken();
        const argument = this.parseExpression(10 /* Precedence.Prefix */);
        return {
            kind: "expression",
            type: "AwaitExpression",
            token,
            argument,
            tokenLiteral: () => token.literal,
        };
    }
    parseSpawnExpression() {
        const token = this.curToken;
        this.nextToken();
        const argument = this.parseExpression(10 /* Precedence.Prefix */);
        return {
            kind: "expression",
            type: "SpawnExpression",
            token,
            argument,
            tokenLiteral: () => token.literal,
        };
    }
    parseChanExpression() {
        const token = this.curToken;
        if (!this.expectPeek("(")) {
            return null;
        }
        let capacity = null;
        if (!this.peekTokenIs(")")) {
            this.nextToken();
            capacity = this.parseExpression(1 /* Precedence.Lowest */);
        }
        if (!this.expectPeek(")")) {
            return null;
        }
        return {
            kind: "expression",
            type: "ChanExpression",
            token,
            capacity,
            tokenLiteral: () => token.literal,
        };
    }
    parseMatchExpression() {
        const token = this.curToken;
        this.nextToken();
        const scrutinee = this.parseExpression(1 /* Precedence.Lowest */);
        if (!this.expectPeek("{")) {
            return null;
        }
        const arms = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            const armToken = this.curToken;
            const pattern = this.parsePattern();
            if (!this.peekTokenIs("=>") && !this.peekTokenIs("->")) {
                this.errors.push(`expected => or -> after match pattern at line ${this.peekToken.line}, column ${this.peekToken.column}`);
                return null;
            }
            this.nextToken();
            this.nextToken();
            const body = this.parseExpression(1 /* Precedence.Lowest */);
            if (!body) {
                return null;
            }
            arms.push({ pattern, body, token: armToken });
            if (this.peekTokenIs(",")) {
                this.nextToken();
            }
            if (this.peekTokenIs(";")) {
                this.nextToken();
            }
            this.nextToken();
        }
        return {
            kind: "expression",
            type: "MatchExpression",
            token,
            scrutinee,
            arms,
            tokenLiteral: () => token.literal,
        };
    }
    parsePattern() {
        if (this.curTokenIs("IDENT") && this.curToken.literal === "_") {
            return { kind: "wildcard", token: this.curToken };
        }
        if (this.curTokenIs("INT")) {
            const lit = this.parseIntegerLiteral();
            return { kind: "literal", value: lit };
        }
        if (this.curTokenIs("STRING")) {
            return { kind: "literal", value: this.parseStringLiteral() };
        }
        if (this.curTokenIs("TRUE") || this.curTokenIs("FALSE")) {
            return { kind: "literal", value: this.parseBoolean() };
        }
        if (this.curTokenIs("IDENT")) {
            const first = this.parseIdentifier();
            // Enum::Variant or Enum::Variant(...)
            if (this.peekTokenIs("::")) {
                this.nextToken();
                if (!this.expectPeek("IDENT")) {
                    return { kind: "ident", name: first };
                }
                const variant = this.parseIdentifier();
                const fields = [];
                if (this.peekTokenIs("(")) {
                    this.nextToken();
                    if (!this.peekTokenIs(")")) {
                        this.nextToken();
                        fields.push(this.parsePattern());
                        while (this.peekTokenIs(",")) {
                            this.nextToken();
                            this.nextToken();
                            fields.push(this.parsePattern());
                        }
                    }
                    if (!this.expectPeek(")")) {
                        // continue with what we have
                    }
                }
                return {
                    kind: "variant",
                    enumName: first.value,
                    variant,
                    fields,
                    token: first.token,
                };
            }
            // Variant(...)
            if (this.peekTokenIs("(")) {
                this.nextToken();
                const fields = [];
                if (!this.peekTokenIs(")")) {
                    this.nextToken();
                    fields.push(this.parsePattern());
                    while (this.peekTokenIs(",")) {
                        this.nextToken();
                        this.nextToken();
                        fields.push(this.parsePattern());
                    }
                }
                if (!this.expectPeek(")")) {
                    // keep going
                }
                return {
                    kind: "variant",
                    enumName: null,
                    variant: first,
                    fields,
                    token: first.token,
                };
            }
            return { kind: "ident", name: first };
        }
        this.errors.push(`invalid pattern at line ${this.curToken.line}, column ${this.curToken.column}`);
        return {
            kind: "wildcard",
            token: this.curToken,
        };
    }
    parseFunctionParameters() {
        const identifiers = [];
        if (this.peekTokenIs(")")) {
            this.nextToken();
            return identifiers;
        }
        this.nextToken();
        identifiers.push(this.parseIdentifier());
        while (this.peekTokenIs(",")) {
            this.nextToken();
            this.nextToken();
            identifiers.push(this.parseIdentifier());
        }
        if (!this.expectPeek(")")) {
            return null;
        }
        return identifiers;
    }
    parseCallExpression(fn) {
        const token = this.curToken;
        const args = this.parseExpressionList(")");
        if (!args) {
            return null;
        }
        // Promote Ident(args) that looks like a variant constructor when uppercase
        if (fn.type === "Identifier") {
            const name = fn.value;
            if (name.length > 0 && name[0] >= "A" && name[0] <= "Z") {
                const ctor = {
                    kind: "expression",
                    type: "ConstructorExpression",
                    token,
                    enumName: null,
                    variant: fn,
                    arguments: args,
                    tokenLiteral: () => token.literal,
                };
                return ctor;
            }
        }
        return {
            kind: "expression",
            type: "CallExpression",
            token,
            function: fn,
            arguments: args,
            tokenLiteral: () => token.literal,
        };
    }
    parsePathConstructor(left) {
        // left::Variant or left::Variant(args)
        if (left.type !== "Identifier") {
            this.errors.push(`expected identifier before :: at line ${this.curToken.line}, column ${this.curToken.column}`);
            return null;
        }
        const enumName = left.value;
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const variant = this.parseIdentifier();
        const token = variant.token;
        let args = [];
        if (this.peekTokenIs("(")) {
            this.nextToken();
            const list = this.parseExpressionList(")");
            if (!list) {
                return null;
            }
            args = list;
        }
        const ctor = {
            kind: "expression",
            type: "ConstructorExpression",
            token,
            enumName,
            variant,
            arguments: args,
            tokenLiteral: () => token.literal,
        };
        return ctor;
    }
    parseMacroInvocation(left) {
        // name!(args)
        if (left.type !== "Identifier") {
            this.errors.push(`macro invocation requires identifier at line ${this.curToken.line}, column ${this.curToken.column}`);
            return null;
        }
        const name = left;
        const token = this.curToken; // !
        if (!this.expectPeek("(")) {
            return null;
        }
        const args = this.parseExpressionList(")");
        if (!args) {
            return null;
        }
        const inv = {
            kind: "expression",
            type: "MacroInvocation",
            token,
            name,
            arguments: args,
            rawTokens: [],
            tokenLiteral: () => name.value + "!",
        };
        return inv;
    }
    parseEffectDeclaration() {
        const token = this.curToken;
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const name = this.parseIdentifier();
        if (!this.expectPeek("{")) {
            return null;
        }
        const operations = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            if (!this.curTokenIs("IDENT")) {
                this.errors.push(`expected effect operation name at line ${this.curToken.line}, column ${this.curToken.column}`);
                break;
            }
            const opName = this.parseIdentifier();
            let parameters = [];
            if (this.peekTokenIs("(")) {
                this.nextToken();
                const params = this.parseFunctionParameters();
                if (!params) {
                    return null;
                }
                parameters = params;
            }
            operations.push({ name: opName, parameters });
            if (this.peekTokenIs(",")) {
                this.nextToken();
            }
            if (this.peekTokenIs(";")) {
                this.nextToken();
            }
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "EffectDeclaration",
            token,
            name,
            operations,
            tokenLiteral: () => token.literal,
        };
    }
    parseStructDeclaration() {
        const token = this.curToken;
        const attributes = [];
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const name = this.parseIdentifier();
        if (!this.expectPeek("{")) {
            return null;
        }
        const fields = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            if (!this.curTokenIs("IDENT")) {
                break;
            }
            const fieldName = this.parseIdentifier();
            let typeName = "any";
            if (this.peekTokenIs(":")) {
                this.nextToken();
                this.nextToken();
                typeName = this.curToken.literal;
            }
            fields.push({ name: fieldName, typeName });
            if (this.peekTokenIs(",")) {
                this.nextToken();
            }
            if (this.peekTokenIs(";")) {
                this.nextToken();
            }
            this.nextToken();
        }
        if (this.peekTokenIs(";")) {
            this.nextToken();
        }
        return {
            kind: "statement",
            type: "StructDeclaration",
            token,
            name,
            fields,
            attributes,
            tokenLiteral: () => token.literal,
        };
    }
    parsePerformExpression() {
        const token = this.curToken;
        this.nextToken();
        if (!this.curTokenIs("IDENT")) {
            this.errors.push(`expected operation after perform at line ${this.curToken.line}, column ${this.curToken.column}`);
            return null;
        }
        let effectName = null;
        let operation = this.parseIdentifier();
        if (this.peekTokenIs("::")) {
            this.nextToken();
            effectName = operation.value;
            if (!this.expectPeek("IDENT")) {
                return null;
            }
            operation = this.parseIdentifier();
        }
        let args = [];
        if (this.peekTokenIs("(")) {
            this.nextToken();
            const list = this.parseExpressionList(")");
            if (!list) {
                return null;
            }
            args = list;
        }
        return {
            kind: "expression",
            type: "PerformExpression",
            token,
            effectName,
            operation,
            arguments: args,
            tokenLiteral: () => token.literal,
        };
    }
    parseHandleExpression() {
        const token = this.curToken;
        if (!this.expectPeek("{")) {
            return null;
        }
        const body = this.parseBlockStatement();
        if (!this.expectPeek("WITH")) {
            return null;
        }
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const effectName = this.parseIdentifier();
        if (!this.expectPeek("{")) {
            return null;
        }
        const handlers = [];
        this.nextToken();
        while (!this.curTokenIs("}") && !this.curTokenIs("EOF")) {
            if (!this.curTokenIs("IDENT")) {
                break;
            }
            const armToken = this.curToken;
            const op = this.parseIdentifier();
            let parameters = [];
            if (this.peekTokenIs("(")) {
                this.nextToken();
                const params = this.parseFunctionParameters();
                if (!params) {
                    return null;
                }
                parameters = params;
            }
            if (!this.expectPeek("=>")) {
                return null;
            }
            this.nextToken();
            const armBody = this.parseExpression(1 /* Precedence.Lowest */);
            if (!armBody) {
                return null;
            }
            handlers.push({
                operation: op,
                parameters,
                body: armBody,
                token: armToken,
            });
            if (this.peekTokenIs(",")) {
                this.nextToken();
            }
            if (this.peekTokenIs(";")) {
                this.nextToken();
            }
            this.nextToken();
        }
        return {
            kind: "expression",
            type: "HandleExpression",
            token,
            body,
            effectName,
            handlers,
            tokenLiteral: () => token.literal,
        };
    }
    parseRegionExpression() {
        const token = this.curToken;
        let name = null;
        if (this.peekTokenIs("IDENT")) {
            this.nextToken();
            name = this.parseIdentifier();
        }
        if (!this.expectPeek("{")) {
            return null;
        }
        const body = this.parseBlockStatement();
        return {
            kind: "expression",
            type: "RegionExpression",
            token,
            name,
            body,
            tokenLiteral: () => token.literal,
        };
    }
    parseReflectExpression() {
        const token = this.curToken;
        if (!this.expectPeek("(")) {
            return null;
        }
        if (!this.expectPeek("IDENT")) {
            return null;
        }
        const target = this.parseIdentifier();
        if (!this.expectPeek(")")) {
            return null;
        }
        return {
            kind: "expression",
            type: "ReflectExpression",
            token,
            target,
            tokenLiteral: () => token.literal,
        };
    }
    parseExpressionList(end) {
        const list = [];
        if (this.peekTokenIs(end)) {
            this.nextToken();
            return list;
        }
        this.nextToken();
        const first = this.parseExpression(1 /* Precedence.Lowest */);
        if (first) {
            list.push(first);
        }
        while (this.peekTokenIs(",")) {
            this.nextToken();
            this.nextToken();
            const exp = this.parseExpression(1 /* Precedence.Lowest */);
            if (exp) {
                list.push(exp);
            }
        }
        if (!this.expectPeek(end)) {
            return null;
        }
        return list;
    }
    curTokenIs(type) {
        return this.curToken.type === type;
    }
    peekTokenIs(type) {
        return this.peekToken.type === type;
    }
    expectPeek(type) {
        if (this.peekTokenIs(type)) {
            this.nextToken();
            return true;
        }
        this.errors.push(`expected next token to be ${type}, got ${this.peekToken.type} instead at line ${this.peekToken.line}, column ${this.peekToken.column}`);
        return false;
    }
    peekPrecedence() {
        return PRECEDENCES[this.peekToken.type] ?? 1 /* Precedence.Lowest */;
    }
    curPrecedence() {
        return PRECEDENCES[this.curToken.type] ?? 1 /* Precedence.Lowest */;
    }
}
exports.Parser = Parser;
//# sourceMappingURL=parser.js.map