"use strict";
/**
 * Modular Lexer Implementation - Compatible Version
 *
 * This implementation works with the existing Token interface and provides
 * a clean, modular approach to lexical analysis using the Strategy Pattern.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.demonstrateModularLexer = exports.ModularLexer = exports.OperatorRecognizer = exports.StringRecognizer = exports.NumberRecognizer = exports.IdentifierRecognizer = exports.KeywordRecognizer = exports.BaseTokenRecognizer = exports.LexicalError = void 0;
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-unused-vars */
const types_1 = require("../types");
const keyword_map_1 = require("../keyword-map");
class LexicalError extends Error {
    constructor(message, line, column, char) {
        super(message);
        this.line = line;
        this.column = column;
        this.char = char;
        this.name = 'LexicalError';
    }
}
exports.LexicalError = LexicalError;
// ===== BASE RECOGNIZER =====
class BaseTokenRecognizer {
    constructor(priority) {
        this.priority = priority;
    }
    createToken(type, value, line, column) {
        return { type, value, line, column };
    }
    peek(input, position, offset = 0) {
        const pos = position + offset;
        return pos >= input.length ? '\0' : input[pos];
    }
    isAtEnd(input, position) {
        return position >= input.length;
    }
}
exports.BaseTokenRecognizer = BaseTokenRecognizer;
// ===== RECOGNIZERS =====
class KeywordRecognizer extends BaseTokenRecognizer {
    constructor() {
        super(10); // High priority
        this.keywords = keyword_map_1.KEYWORDS;
    }
    canRecognize(input, position) {
        const char = this.peek(input, position);
        return /[\u0400-\u04FF]/.test(char);
    }
    recognize(input, position, line, column) {
        let current = position;
        let value = '';
        // Read Cyrillic identifier
        while (!this.isAtEnd(input, current) &&
            this.isCyrillicIdentifierChar(this.peek(input, current))) {
            value += input[current];
            current++;
        }
        if (value.length === 0) {
            return null;
        }
        const tokenType = this.keywords.get(value) || types_1.TokenType.IDENTIFIER;
        return {
            token: this.createToken(tokenType, value, line, column),
            consumed: current - position,
        };
    }
    isCyrillicIdentifierChar(char) {
        return /[\u0400-\u04FF]/.test(char) || /[0-9_]/.test(char);
    }
}
exports.KeywordRecognizer = KeywordRecognizer;
class IdentifierRecognizer extends BaseTokenRecognizer {
    constructor() {
        super(5); // Medium priority
    }
    canRecognize(input, position) {
        const char = this.peek(input, position);
        return /[a-zA-Z_]/.test(char);
    }
    recognize(input, position, line, column) {
        let current = position;
        let value = '';
        // Read Latin identifier
        while (!this.isAtEnd(input, current) && this.isIdentifierChar(this.peek(input, current))) {
            value += input[current];
            current++;
        }
        if (value.length === 0) {
            return null;
        }
        return {
            token: this.createToken(types_1.TokenType.IDENTIFIER, value, line, column),
            consumed: current - position,
        };
    }
    isIdentifierChar(char) {
        return /[a-zA-Z0-9_]/.test(char);
    }
}
exports.IdentifierRecognizer = IdentifierRecognizer;
class NumberRecognizer extends BaseTokenRecognizer {
    constructor() {
        super(8); // High priority
    }
    canRecognize(input, position) {
        const char = this.peek(input, position);
        return /[0-9]/.test(char);
    }
    recognize(input, position, line, column) {
        let current = position;
        let value = '';
        // Read integer part
        while (!this.isAtEnd(input, current) && /[0-9]/.test(this.peek(input, current))) {
            value += input[current];
            current++;
        }
        // Check for decimal point
        if (this.peek(input, current) === '.' && /[0-9]/.test(this.peek(input, current, 1))) {
            value += input[current]; // add '.'
            current++;
            // Read decimal part
            while (!this.isAtEnd(input, current) && /[0-9]/.test(this.peek(input, current))) {
                value += input[current];
                current++;
            }
        }
        if (value.length === 0) {
            return null;
        }
        return {
            token: this.createToken(types_1.TokenType.NUMBER, value, line, column),
            consumed: current - position,
        };
    }
}
exports.NumberRecognizer = NumberRecognizer;
class StringRecognizer extends BaseTokenRecognizer {
    constructor() {
        super(9); // High priority
    }
    canRecognize(input, position) {
        const char = this.peek(input, position);
        return char === '"' || char === "'";
    }
    recognize(input, position, line, column) {
        let current = position;
        const quote = input[current];
        current++; // consume opening quote
        let value = '';
        while (!this.isAtEnd(input, current) && this.peek(input, current) !== quote) {
            if (this.peek(input, current) === '\\') {
                current++; // consume backslash
                const escaped = this.handleEscapeSequence(input, current);
                value += escaped.char;
                current += escaped.consumed;
            }
            else {
                value += input[current];
                current++;
            }
        }
        if (this.isAtEnd(input, current)) {
            return null; // Unterminated string
        }
        current++; // consume closing quote
        return {
            token: this.createToken(types_1.TokenType.STRING, value, line, column),
            consumed: current - position,
        };
    }
    handleEscapeSequence(input, position) {
        if (this.isAtEnd(input, position)) {
            return { char: '\\', consumed: 0 };
        }
        const char = input[position];
        switch (char) {
            case 'n':
                return { char: '\n', consumed: 1 };
            case 't':
                return { char: '\t', consumed: 1 };
            case 'r':
                return { char: '\r', consumed: 1 };
            case '\\':
                return { char: '\\', consumed: 1 };
            case '"':
                return { char: '"', consumed: 1 };
            case "'":
                return { char: "'", consumed: 1 };
            case 'u': {
                // Unicode escape sequence \uXXXX
                let unicode = '';
                let consumed = 1; // for 'u'
                for (let i = 0; i < 4; i++) {
                    if (!this.isAtEnd(input, position + consumed) &&
                        /[0-9a-fA-F]/.test(input[position + consumed])) {
                        unicode += input[position + consumed];
                        consumed++;
                    }
                    else {
                        break;
                    }
                }
                if (unicode.length === 4) {
                    return { char: String.fromCodePoint(Number.parseInt(unicode, 16)), consumed };
                }
                else {
                    return { char: char, consumed: 1 };
                }
            }
            default:
                return { char: char, consumed: 1 };
        }
    }
}
exports.StringRecognizer = StringRecognizer;
class OperatorRecognizer extends BaseTokenRecognizer {
    constructor() {
        super(6); // Medium priority
        this.operators = new Map([
            // Compound assignment operators (check longest first)
            ['**=', types_1.TokenType.EXPONENT_ASSIGN],
            ['+=', types_1.TokenType.PLUS_ASSIGN],
            ['-=', types_1.TokenType.MINUS_ASSIGN],
            ['*=', types_1.TokenType.MULTIPLY_ASSIGN],
            ['/=', types_1.TokenType.DIVIDE_ASSIGN],
            ['%=', types_1.TokenType.MODULO_ASSIGN],
            // Comparison operators
            ['===', types_1.TokenType.STRICT_EQUAL],
            ['!==', types_1.TokenType.STRICT_NOT_EQUAL],
            ['==', types_1.TokenType.EQUAL],
            ['!=', types_1.TokenType.NOT_EQUAL],
            ['<=', types_1.TokenType.LESS_EQUAL],
            ['>=', types_1.TokenType.GREATER_EQUAL],
            // Logical operators
            ['&&', types_1.TokenType.AND],
            ['||', types_1.TokenType.OR],
            // Increment/Decrement
            ['++', types_1.TokenType.INCREMENT],
            ['--', types_1.TokenType.DECREMENT],
            // Power operator
            ['**', types_1.TokenType.EXPONENT],
            // Arrow function
            ['=>', types_1.TokenType.ARROW],
            // Spread operator
            ['...', types_1.TokenType.SPREAD],
            // Single character operators
            ['+', types_1.TokenType.PLUS],
            ['-', types_1.TokenType.MINUS],
            ['*', types_1.TokenType.MULTIPLY],
            ['/', types_1.TokenType.DIVIDE],
            ['%', types_1.TokenType.MODULO],
            ['=', types_1.TokenType.ASSIGN],
            ['<', types_1.TokenType.LESS_THAN],
            ['>', types_1.TokenType.GREATER_THAN],
            ['!', types_1.TokenType.NOT],
            ['&', types_1.TokenType.BITWISE_AND],
            ['|', types_1.TokenType.BITWISE_OR],
            ['^', types_1.TokenType.BITWISE_XOR],
            ['~', types_1.TokenType.BITWISE_NOT],
            ['?', types_1.TokenType.QUESTION],
            [':', types_1.TokenType.COLON],
            [';', types_1.TokenType.SEMICOLON],
            [',', types_1.TokenType.COMMA],
            ['.', types_1.TokenType.DOT],
            ['(', types_1.TokenType.LEFT_PAREN],
            [')', types_1.TokenType.RIGHT_PAREN],
            ['{', types_1.TokenType.LEFT_BRACE],
            ['}', types_1.TokenType.RIGHT_BRACE],
            ['[', types_1.TokenType.LEFT_BRACKET],
            [']', types_1.TokenType.RIGHT_BRACKET],
        ]);
        // Sort operators by length (longest first) for greedy matching
        this.sortedOperators = Array.from(this.operators.keys()).sort((a, b) => b.length - a.length);
    }
    canRecognize(input, position) {
        const char = this.peek(input, position);
        // Check if any operator starts with this character
        return this.sortedOperators.some(op => op[0] === char);
    }
    recognize(input, position, line, column) {
        for (const operator of this.sortedOperators) {
            if (this.matchesAt(input, position, operator)) {
                return {
                    token: this.createToken(this.operators.get(operator), operator, line, column),
                    consumed: operator.length,
                };
            }
        }
        return null;
    }
    matchesAt(input, position, operator) {
        for (let i = 0; i < operator.length; i++) {
            if (this.isAtEnd(input, position + i) || input[position + i] !== operator[i]) {
                return false;
            }
        }
        return true;
    }
}
exports.OperatorRecognizer = OperatorRecognizer;
// ===== MODULAR LEXER =====
class ModularLexer {
    constructor(recognizers) {
        this.recognizers = recognizers || [
            new KeywordRecognizer(),
            new IdentifierRecognizer(),
            new NumberRecognizer(),
            new StringRecognizer(),
            new OperatorRecognizer(),
        ];
        // Sort by priority (highest first)
        this.recognizers.sort((a, b) => b.priority - a.priority);
    }
    tokenize(source) {
        const tokens = [];
        const errors = [];
        let position = 0;
        let line = 1;
        let column = 1;
        while (position < source.length) {
            // Skip whitespace
            if (this.isWhitespace(source[position])) {
                if (source[position] === '\n') {
                    line++;
                    column = 1;
                }
                else {
                    column++;
                }
                position++;
                continue;
            }
            // Try to recognize a token
            const result = this.nextToken(source, position, line, column);
            if (result) {
                tokens.push(result.token);
                position += result.consumed;
                column += result.consumed;
            }
            else {
                // Unrecognized character
                const char = source[position];
                errors.push(new LexicalError(`Unexpected character: '${char}'`, line, column, char));
                position++;
                column++;
            }
        }
        // Add EOF token
        tokens.push({
            type: types_1.TokenType.EOF,
            value: '',
            line,
            column,
        });
        return { tokens, errors };
    }
    nextToken(source, position, line, column) {
        for (const recognizer of this.recognizers) {
            if (recognizer.canRecognize(source, position)) {
                const result = recognizer.recognize(source, position, line, column);
                if (result) {
                    return result;
                }
            }
        }
        return null;
    }
    isWhitespace(char) {
        return /\s/.test(char);
    }
    // Factory methods
    static withRecognizers(recognizers) {
        return new ModularLexer(recognizers);
    }
    static minimal() {
        return new ModularLexer([
            new KeywordRecognizer(),
            new IdentifierRecognizer(),
            new NumberRecognizer(),
            new StringRecognizer(),
            new OperatorRecognizer(),
        ]);
    }
}
exports.ModularLexer = ModularLexer;
// ===== DEMO FUNCTION =====
function demonstrateModularLexer() {
    console.log('🔤 Modular Lexer Architecture Demo');
    console.log('=====================================\n');
    const code = `
тағйирёбанда исм = "Ҷаҳон";
функсия салом(номи: сатр): сатр {
    бозгашт "Салом, " + номи + "!";
}

рақам натиҷа = 42;
мантиқӣ дуруст_аст = дуруст;
`;
    const lexer = new ModularLexer();
    const result = lexer.tokenize(code);
    console.log('📝 Source Code:');
    console.log(code);
    console.log('\n🎯 Tokens:');
    result.tokens.forEach((token, index) => {
        if (token.type !== types_1.TokenType.EOF) {
            console.log(`${index + 1}. ${token.type}: "${token.value}" at line ${token.line}, column ${token.column}`);
        }
    });
    if (result.errors.length > 0) {
        console.log('\n❌ Errors:');
        result.errors.forEach(error => {
            console.log(`- ${error.message} at line ${error.line}, column ${error.column}`);
        });
    }
    console.log('\n✅ Lexical analysis completed successfully!');
    console.log(`📊 Generated ${result.tokens.length} tokens with ${result.errors.length} errors.`);
}
exports.demonstrateModularLexer = demonstrateModularLexer;
// Run the demo if this file is executed directly
if (require.main === module) {
    demonstrateModularLexer();
}
//# sourceMappingURL=modular-lexer-compatible.js.map