"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Lexer = void 0;
const types_1 = require("./types");
const keyword_map_1 = require("./keyword-map");
class Lexer {
    constructor(input) {
        this.position = 0;
        this.line = 1;
        this.column = 1;
        this.keywords = keyword_map_1.KEYWORDS;
        // Remove BOM if present
        this.input = input.codePointAt(0) === 0xfeff ? input.slice(1) : input;
    }
    tokenize() {
        const tokens = [];
        while (!this.isAtEnd()) {
            const token = this.nextToken();
            if (token.type !== types_1.TokenType.WHITESPACE) {
                tokens.push(token);
            }
        }
        tokens.push(this.createToken(types_1.TokenType.EOF, ''));
        return tokens;
    }
    handlePlusOperator() {
        if (this.peek() === '+') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.INCREMENT, '++', this.line, this.column - 2);
        }
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.PLUS_ASSIGN, '+=', this.line, this.column - 2);
        }
        return this.singleCharToken(types_1.TokenType.PLUS);
    }
    handleMinusOperator() {
        if (this.peek() === '-') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.DECREMENT, '--', this.line, this.column - 2);
        }
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.MINUS_ASSIGN, '-=', this.line, this.column - 2);
        }
        return this.singleCharToken(types_1.TokenType.MINUS);
    }
    handleMultiplyOperator() {
        if (this.peek() === '*') {
            this.advance();
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.EXPONENT_ASSIGN, '**=', this.line, this.column - 3);
            }
            this.advance();
            return this.createToken(types_1.TokenType.EXPONENT, '**', this.line, this.column - 2);
        }
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.MULTIPLY_ASSIGN, '*=', this.line, this.column - 2);
        }
        return this.singleCharToken(types_1.TokenType.MULTIPLY);
    }
    handleDivideOperator() {
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.DIVIDE_ASSIGN, '/=', this.line, this.column - 2);
        }
        return this.singleCharToken(types_1.TokenType.DIVIDE);
    }
    handleModuloOperator() {
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.MODULO_ASSIGN, '%=', this.line, this.column - 2);
        }
        return this.singleCharToken(types_1.TokenType.MODULO);
    }
    handleDotOperator() {
        if (this.peekNext() === '.' && this.peekNext(1) === '.') {
            this.advance();
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.SPREAD, '...');
        }
        return this.singleCharToken(types_1.TokenType.DOT);
    }
    handleEqualOperator(startLine, startColumn) {
        if (this.peek() === '=') {
            this.advance();
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.STRICT_EQUAL, '===', startLine, startColumn);
            }
            this.advance();
            return this.createToken(types_1.TokenType.EQUAL, '==', startLine, startColumn);
        }
        if (this.peek() === '>') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.ARROW, '=>', startLine, startColumn);
        }
        return this.singleCharToken(types_1.TokenType.ASSIGN);
    }
    handleNotOperator(startLine, startColumn) {
        if (this.peek() === '=') {
            this.advance();
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.STRICT_NOT_EQUAL, '!==', startLine, startColumn);
            }
            this.advance();
            return this.createToken(types_1.TokenType.NOT_EQUAL, '!=', startLine, startColumn);
        }
        return this.singleCharToken(types_1.TokenType.NOT);
    }
    handleLessThanOperator(startLine, startColumn) {
        if (this.peek() === '<') {
            this.advance();
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.LEFT_SHIFT_ASSIGN, '<<=', startLine, startColumn);
            }
            this.advance();
            return this.createToken(types_1.TokenType.LEFT_SHIFT, '<<', startLine, startColumn);
        }
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.LESS_EQUAL, '<=', startLine, startColumn);
        }
        return this.singleCharToken(types_1.TokenType.LESS_THAN);
    }
    handleGreaterThanOperator(startLine, startColumn) {
        if (this.peek() === '>') {
            this.advance();
            if (this.peek() === '>') {
                this.advance();
                if (this.peek() === '=') {
                    this.advance();
                    this.advance();
                    return this.createToken(types_1.TokenType.UNSIGNED_RIGHT_SHIFT_ASSIGN, '>>>=', startLine, startColumn);
                }
                this.advance();
                return this.createToken(types_1.TokenType.UNSIGNED_RIGHT_SHIFT, '>>>', startLine, startColumn);
            }
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.RIGHT_SHIFT_ASSIGN, '>>=', startLine, startColumn);
            }
            this.advance();
            return this.createToken(types_1.TokenType.RIGHT_SHIFT, '>>', startLine, startColumn);
        }
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.GREATER_EQUAL, '>=', startLine, startColumn);
        }
        return this.singleCharToken(types_1.TokenType.GREATER_THAN);
    }
    handleAmpersandOperator(startLine, startColumn) {
        if (this.peek() === '&') {
            this.advance();
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.AND_ASSIGN, '&&=', startLine, startColumn);
            }
            this.advance();
            return this.createToken(types_1.TokenType.AND, '&&', startLine, startColumn);
        }
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.BITWISE_AND_ASSIGN, '&=', startLine, startColumn);
        }
        this.advance();
        return this.createToken(types_1.TokenType.BITWISE_AND, '&', startLine, startColumn);
    }
    handlePipeOperator(startLine, startColumn) {
        if (this.peek() === '|') {
            this.advance();
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.OR_ASSIGN, '||=', startLine, startColumn);
            }
            this.advance();
            return this.createToken(types_1.TokenType.OR, '||', startLine, startColumn);
        }
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.BITWISE_OR_ASSIGN, '|=', startLine, startColumn);
        }
        this.advance();
        return this.createToken(types_1.TokenType.BITWISE_OR, '|', startLine, startColumn);
    }
    handleCaretOperator(startLine, startColumn) {
        if (this.peek() === '=') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.BITWISE_XOR_ASSIGN, '^=', startLine, startColumn);
        }
        this.advance();
        return this.createToken(types_1.TokenType.BITWISE_XOR, '^', startLine, startColumn);
    }
    handleQuestionOperator(startLine, startColumn) {
        if (this.peek() === '?') {
            this.advance();
            if (this.peek() === '=') {
                this.advance();
                this.advance();
                return this.createToken(types_1.TokenType.NULLISH_ASSIGN, '??=', startLine, startColumn);
            }
            this.advance();
            return this.createToken(types_1.TokenType.NULLISH_COALESCING, '??', startLine, startColumn);
        }
        if (this.peek() === '.') {
            this.advance();
            this.advance();
            return this.createToken(types_1.TokenType.OPTIONAL_CHAINING, '?.', startLine, startColumn);
        }
        this.advance();
        return this.createToken(types_1.TokenType.QUESTION, '?', startLine, startColumn);
    }
    nextToken() {
        this.skipWhitespace();
        if (this.isAtEnd()) {
            return this.createToken(types_1.TokenType.EOF, '');
        }
        const char = this.currentChar();
        const startLine = this.line;
        const startColumn = this.column;
        if (char === '/' && this.peek() === '/') {
            this.skipLineComment();
            return this.nextToken();
        }
        switch (char) {
            case '+':
                return this.handlePlusOperator();
            case '-':
                return this.handleMinusOperator();
            case '*':
                return this.handleMultiplyOperator();
            case '/':
                return this.handleDivideOperator();
            case '%':
                return this.handleModuloOperator();
            case '(':
                return this.singleCharToken(types_1.TokenType.LEFT_PAREN);
            case ')':
                return this.singleCharToken(types_1.TokenType.RIGHT_PAREN);
            case '{':
                return this.singleCharToken(types_1.TokenType.LEFT_BRACE);
            case '}':
                return this.singleCharToken(types_1.TokenType.RIGHT_BRACE);
            case '[':
                return this.singleCharToken(types_1.TokenType.LEFT_BRACKET);
            case ']':
                return this.singleCharToken(types_1.TokenType.RIGHT_BRACKET);
            case ';':
                return this.singleCharToken(types_1.TokenType.SEMICOLON);
            case ',':
                return this.singleCharToken(types_1.TokenType.COMMA);
            case '.':
                return this.handleDotOperator();
            case ':':
                return this.singleCharToken(types_1.TokenType.COLON);
            case '\n':
                this.advance();
                this.line++;
                this.column = 1;
                return this.createToken(types_1.TokenType.NEWLINE, '\n', startLine, startColumn);
        }
        if (char === '=')
            return this.handleEqualOperator(startLine, startColumn);
        if (char === '!')
            return this.handleNotOperator(startLine, startColumn);
        if (char === '<')
            return this.handleLessThanOperator(startLine, startColumn);
        if (char === '>')
            return this.handleGreaterThanOperator(startLine, startColumn);
        if (char === '&')
            return this.handleAmpersandOperator(startLine, startColumn);
        if (char === '|')
            return this.handlePipeOperator(startLine, startColumn);
        if (char === '^')
            return this.handleCaretOperator(startLine, startColumn);
        if (char === '?')
            return this.handleQuestionOperator(startLine, startColumn);
        if (char === '~') {
            this.advance();
            return this.createToken(types_1.TokenType.BITWISE_NOT, '~', startLine, startColumn);
        }
        if (char === '"' || char === "'") {
            return this.readString(char, startLine, startColumn);
        }
        if (char === '`') {
            return this.readTemplateLiteral(startLine, startColumn);
        }
        if (this.isDigit(char)) {
            return this.readNumber(startLine, startColumn);
        }
        if (this.isAlpha(char) || this.isCyrillic(char)) {
            return this.readIdentifier(startLine, startColumn);
        }
        throw new Error(`Unexpected character '${char}' at line ${this.line}, column ${this.column}`);
    }
    singleCharToken(type) {
        const char = this.currentChar();
        const line = this.line;
        const column = this.column;
        this.advance();
        return this.createToken(type, char, line, column);
    }
    processStringEscapeSequence() {
        this.advance(); // Skip backslash
        if (this.isAtEnd()) {
            return '';
        }
        const escaped = this.currentChar();
        this.advance();
        switch (escaped) {
            case 'n':
                return '\n';
            case 't':
                return '\t';
            case 'r':
                return '\r';
            case '\\':
                return '\\';
            case '"':
                return '"';
            case "'":
                return "'";
            default:
                return escaped;
        }
    }
    processStringCharacter() {
        const char = this.currentChar();
        this.advance();
        if (char === '\n') {
            this.line++;
            this.column = 1;
        }
        return char;
    }
    readString(quote, startLine, startColumn) {
        let value = '';
        this.advance(); // Skip opening quote
        while (!this.isAtEnd() && this.currentChar() !== quote) {
            if (this.currentChar() === '\\') {
                value += this.processStringEscapeSequence();
            }
            else {
                value += this.processStringCharacter();
            }
        }
        if (this.isAtEnd()) {
            throw new Error(`Unterminated string at line ${startLine}, column ${startColumn}`);
        }
        this.advance(); // Skip closing quote
        return this.createToken(types_1.TokenType.STRING, value, startLine, startColumn);
    }
    readTemplateLiteral(startLine, startColumn) {
        let value = '';
        this.advance(); // Skip opening backtick
        while (!this.isAtEnd() && this.currentChar() !== '`') {
            if (this.currentChar() === '\\') {
                value += this.handleEscapeSequence();
            }
            else if (this.currentChar() === '$' && this.peek() === '{') {
                value += this.handleInterpolation();
            }
            else {
                value += this.handleRegularCharacter();
            }
        }
        if (this.isAtEnd()) {
            throw new Error(`Unterminated template literal at line ${startLine}, column ${startColumn}`);
        }
        this.advance(); // Skip closing backtick
        return this.createToken(types_1.TokenType.TEMPLATE_LITERAL, value, startLine, startColumn);
    }
    handleEscapeSequence() {
        this.advance(); // Skip backslash
        if (this.isAtEnd())
            return '\\';
        const escaped = this.currentChar();
        this.advance();
        switch (escaped) {
            case 'n':
                return '\n';
            case 't':
                return '\t';
            case 'r':
                return '\r';
            case '\\':
                return '\\';
            case '`':
                return '`';
            case '$':
                return '$';
            default:
                return escaped;
        }
    }
    handleInterpolation() {
        let result = '${';
        this.advance(); // Skip $
        this.advance(); // Skip {
        let braceCount = 1;
        while (!this.isAtEnd() && braceCount > 0) {
            if (this.currentChar() === '{') {
                braceCount++;
            }
            else if (this.currentChar() === '}') {
                braceCount--;
            }
            result += this.currentChar();
            this.advance();
        }
        return result;
    }
    handleRegularCharacter() {
        return this.processStringCharacter();
    }
    readNumber(startLine, startColumn) {
        let value = '';
        let hasDecimal = false;
        while (!this.isAtEnd() && this.isDigit(this.currentChar())) {
            value += this.currentChar();
            this.advance();
        }
        // Handle decimal numbers
        if (!this.isAtEnd() && this.currentChar() === '.' && this.isDigit(this.peek())) {
            hasDecimal = true;
            value += this.currentChar();
            this.advance();
            while (!this.isAtEnd() && this.isDigit(this.currentChar())) {
                value += this.currentChar();
                this.advance();
            }
        }
        // Check for invalid number patterns (multiple decimal points)
        if (!this.isAtEnd() && this.currentChar() === '.' && hasDecimal) {
            throw new Error(`Invalid number format at line ${startLine}, column ${startColumn}: multiple decimal points`);
        }
        return this.createToken(types_1.TokenType.NUMBER, value, startLine, startColumn);
    }
    readIdentifier(startLine, startColumn) {
        let value = '';
        while (!this.isAtEnd() &&
            (this.isAlphaNumeric(this.currentChar()) || this.isCyrillic(this.currentChar()))) {
            value += this.currentChar();
            this.advance();
        }
        const tokenType = this.keywords.get(value.toLowerCase()) || types_1.TokenType.IDENTIFIER;
        return this.createToken(tokenType, value, startLine, startColumn);
    }
    skipWhitespace() {
        while (!this.isAtEnd() &&
            this.isWhitespace(this.currentChar()) &&
            this.currentChar() !== '\n') {
            this.advance();
        }
    }
    skipLineComment() {
        // Skip the '//'
        this.advance();
        this.advance();
        // Skip until end of line
        while (!this.isAtEnd() && this.currentChar() !== '\n') {
            this.advance();
        }
    }
    currentChar() {
        return this.input[this.position];
    }
    peek() {
        if (this.position + 1 >= this.input.length)
            return '\0';
        return this.input[this.position + 1];
    }
    advance() {
        if (!this.isAtEnd()) {
            this.position++;
            this.column++;
        }
    }
    isAtEnd() {
        return this.position >= this.input.length;
    }
    isDigit(char) {
        return char >= '0' && char <= '9';
    }
    isAlpha(char) {
        return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char === '_';
    }
    isAlphaNumeric(char) {
        return this.isAlpha(char) || this.isDigit(char);
    }
    isCyrillic(char) {
        const code = char.codePointAt(0);
        if (code === undefined)
            return false;
        return ((code >= 0x0400 && code <= 0x04ff) || // Cyrillic
            (code >= 0x0500 && code <= 0x052f)); // Cyrillic Supplement
    }
    isWhitespace(char) {
        return char === ' ' || char === '\t' || char === '\r';
    }
    peekNext(offset = 0) {
        const pos = this.position + 1 + offset;
        if (pos >= this.input.length)
            return '\0';
        return this.input[pos];
    }
    createToken(type, value, line, column) {
        return {
            type,
            value,
            line: line || this.line,
            column: column || this.column,
        };
    }
}
exports.Lexer = Lexer;
//# sourceMappingURL=lexer.js.map