{"version":3,"file":"lexer.cjs","sources":["../../../src/fql/lexer.ts"],"sourcesContent":["/**\n * FQL Lexer (Tokenizer)\n * Converts FQL query strings into tokens for parsing\n */\n\nexport const TokenType = {\n  TERM: \"TERM\",\n  QUOTED: \"QUOTED\",\n  AND: \"AND\",\n  OR: \"OR\",\n  NOT: \"NOT\",\n  LPAREN: \"LPAREN\",\n  RPAREN: \"RPAREN\",\n  COLON: \"COLON\",\n  EXACT: \"EXACT\",\n  FUZZY: \"FUZZY\",\n  PHONETIC: \"PHONETIC\",\n  PREFIX: \"PREFIX\",\n  REGEX: \"REGEX\",\n  COMPOUND: \"COMPOUND\",\n  LANG: \"LANG\",\n  SCORE: \"SCORE\",\n  SCORE_OP: \"SCORE_OP\",\n  NUMBER: \"NUMBER\",\n  EOF: \"EOF\",\n} as const;\n\nexport type TokenType = (typeof TokenType)[keyof typeof TokenType];\n\nexport interface Token {\n  type: TokenType;\n  value: string;\n  position: number;\n}\n\nexport class FQLLexer {\n  private input: string = \"\";\n  private position: number = 0;\n  private tokens: Token[] = [];\n\n  /**\n   * Tokenize an FQL query string\n   */\n  tokenize(input: string): Token[] {\n    this.input = input.trim();\n    this.position = 0;\n    this.tokens = [];\n\n    while (this.position < this.input.length) {\n      this.skipWhitespace();\n\n      if (this.position >= this.input.length) break;\n\n      const char = this.input[this.position];\n\n      // Parentheses\n      if (char === \"(\") {\n        this.tokens.push({ type: TokenType.LPAREN, value: \"(\", position: this.position });\n        this.position++;\n        continue;\n      }\n\n      if (char === \")\") {\n        this.tokens.push({ type: TokenType.RPAREN, value: \")\", position: this.position });\n        this.position++;\n        continue;\n      }\n\n      // Colon\n      if (char === \":\") {\n        this.tokens.push({ type: TokenType.COLON, value: \":\", position: this.position });\n        this.position++;\n        continue;\n      }\n\n      // Quoted strings\n      if (char === '\"' || char === \"'\") {\n        this.tokenizeQuotedString(char);\n        continue;\n      }\n\n      // Numbers (for score thresholds)\n      if (this.isDigit(char)) {\n        this.tokenizeNumber();\n        continue;\n      }\n\n      // Keywords and terms\n      if (this.isAlpha(char)) {\n        this.tokenizeKeywordOrTerm();\n        continue;\n      }\n\n      // Score operators (>, <, >=, <=)\n      if (char === \">\" || char === \"<\") {\n        this.tokenizeScoreOperator();\n        continue;\n      }\n\n      // Unknown character - skip it\n      this.position++;\n    }\n\n    // Add EOF token\n    this.tokens.push({ type: TokenType.EOF, value: \"\", position: this.position });\n\n    return this.tokens;\n  }\n\n  private skipWhitespace(): void {\n    while (this.position < this.input.length && /\\s/.test(this.input[this.position])) {\n      this.position++;\n    }\n  }\n\n  private isAlpha(char: string): boolean {\n    return /[a-zA-ZäöüßÄÖÜàâäæçéèêëïîôùûüÿœÀÂÄÆÇÉÈÊËÏÎÔÙÛÜŸŒáéíóúñüÁÉÍÓÚÑÜ_]/.test(char);\n  }\n\n  private isDigit(char: string): boolean {\n    return /[0-9.]/.test(char);\n  }\n\n  private isAlphaNumeric(char: string): boolean {\n    return this.isAlpha(char) || this.isDigit(char);\n  }\n\n  private tokenizeQuotedString(quote: string): void {\n    const start = this.position;\n    this.position++; // Skip opening quote\n\n    let value = \"\";\n    while (this.position < this.input.length && this.input[this.position] !== quote) {\n      value += this.input[this.position];\n      this.position++;\n    }\n\n    if (this.position >= this.input.length) {\n      throw new Error(`Unclosed quote at position ${start}`);\n    }\n\n    this.position++; // Skip closing quote\n\n    this.tokens.push({ type: TokenType.QUOTED, value, position: start });\n  }\n\n  private tokenizeNumber(): void {\n    const start = this.position;\n    let value = \"\";\n\n    while (this.position < this.input.length && this.isDigit(this.input[this.position])) {\n      value += this.input[this.position];\n      this.position++;\n    }\n\n    this.tokens.push({ type: TokenType.NUMBER, value, position: start });\n  }\n\n  private tokenizeKeywordOrTerm(): void {\n    const start = this.position;\n    let value = \"\";\n\n    while (this.position < this.input.length && this.isAlphaNumeric(this.input[this.position])) {\n      value += this.input[this.position];\n      this.position++;\n    }\n\n    const upperValue = value.toUpperCase();\n\n    // Check for keywords\n    switch (upperValue) {\n      case \"AND\":\n        this.tokens.push({ type: TokenType.AND, value: upperValue, position: start });\n        break;\n      case \"OR\":\n        this.tokens.push({ type: TokenType.OR, value: upperValue, position: start });\n        break;\n      case \"NOT\":\n        this.tokens.push({ type: TokenType.NOT, value: upperValue, position: start });\n        break;\n      case \"EXACT\":\n        this.tokens.push({ type: TokenType.EXACT, value: upperValue, position: start });\n        break;\n      case \"FUZZY\":\n        this.tokens.push({ type: TokenType.FUZZY, value: upperValue, position: start });\n        break;\n      case \"PHONETIC\":\n        this.tokens.push({ type: TokenType.PHONETIC, value: upperValue, position: start });\n        break;\n      case \"PREFIX\":\n        this.tokens.push({ type: TokenType.PREFIX, value: upperValue, position: start });\n        break;\n      case \"REGEX\":\n        this.tokens.push({ type: TokenType.REGEX, value: upperValue, position: start });\n        break;\n      case \"COMPOUND\":\n        this.tokens.push({ type: TokenType.COMPOUND, value: upperValue, position: start });\n        break;\n      case \"LANG\":\n        this.tokens.push({ type: TokenType.LANG, value: upperValue, position: start });\n        break;\n      case \"SCORE\":\n        this.tokens.push({ type: TokenType.SCORE, value: upperValue, position: start });\n        break;\n      default:\n        // Regular term (preserve original case)\n        this.tokens.push({ type: TokenType.TERM, value, position: start });\n        break;\n    }\n  }\n\n  private tokenizeScoreOperator(): void {\n    const start = this.position;\n    let value = this.input[this.position];\n    this.position++;\n\n    // Check for >= or <=\n    if (this.position < this.input.length && this.input[this.position] === \"=\") {\n      value += \"=\";\n      this.position++;\n    }\n\n    this.tokens.push({ type: TokenType.SCORE_OP, value, position: start });\n  }\n}\n"],"names":[],"mappings":";;AAKO,MAAM,YAAY;AAAA,EACvB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,UAAU;AAAA,EACV,MAAM;AAAA,EACN,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,KAAK;AACP;AAUO,MAAM,SAAS;AAAA,EACZ,QAAgB;AAAA,EAChB,WAAmB;AAAA,EACnB,SAAkB,CAAA;AAAA;AAAA;AAAA;AAAA,EAK1B,SAAS,OAAwB;AAC/B,SAAK,QAAQ,MAAM,KAAA;AACnB,SAAK,WAAW;AAChB,SAAK,SAAS,CAAA;AAEd,WAAO,KAAK,WAAW,KAAK,MAAM,QAAQ;AACxC,WAAK,eAAA;AAEL,UAAI,KAAK,YAAY,KAAK,MAAM,OAAQ;AAExC,YAAM,OAAO,KAAK,MAAM,KAAK,QAAQ;AAGrC,UAAI,SAAS,KAAK;AAChB,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,QAAQ,OAAO,KAAK,UAAU,KAAK,SAAA,CAAU;AAChF,aAAK;AACL;AAAA,MACF;AAEA,UAAI,SAAS,KAAK;AAChB,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,QAAQ,OAAO,KAAK,UAAU,KAAK,SAAA,CAAU;AAChF,aAAK;AACL;AAAA,MACF;AAGA,UAAI,SAAS,KAAK;AAChB,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,OAAO,OAAO,KAAK,UAAU,KAAK,SAAA,CAAU;AAC/E,aAAK;AACL;AAAA,MACF;AAGA,UAAI,SAAS,OAAO,SAAS,KAAK;AAChC,aAAK,qBAAqB,IAAI;AAC9B;AAAA,MACF;AAGA,UAAI,KAAK,QAAQ,IAAI,GAAG;AACtB,aAAK,eAAA;AACL;AAAA,MACF;AAGA,UAAI,KAAK,QAAQ,IAAI,GAAG;AACtB,aAAK,sBAAA;AACL;AAAA,MACF;AAGA,UAAI,SAAS,OAAO,SAAS,KAAK;AAChC,aAAK,sBAAA;AACL;AAAA,MACF;AAGA,WAAK;AAAA,IACP;AAGA,SAAK,OAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,SAAA,CAAU;AAE5E,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,iBAAuB;AAC7B,WAAO,KAAK,WAAW,KAAK,MAAM,UAAU,KAAK,KAAK,KAAK,MAAM,KAAK,QAAQ,CAAC,GAAG;AAChF,WAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,QAAQ,MAAuB;AACrC,WAAO,mEAAmE,KAAK,IAAI;AAAA,EACrF;AAAA,EAEQ,QAAQ,MAAuB;AACrC,WAAO,SAAS,KAAK,IAAI;AAAA,EAC3B;AAAA,EAEQ,eAAe,MAAuB;AAC5C,WAAO,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,IAAI;AAAA,EAChD;AAAA,EAEQ,qBAAqB,OAAqB;AAChD,UAAM,QAAQ,KAAK;AACnB,SAAK;AAEL,QAAI,QAAQ;AACZ,WAAO,KAAK,WAAW,KAAK,MAAM,UAAU,KAAK,MAAM,KAAK,QAAQ,MAAM,OAAO;AAC/E,eAAS,KAAK,MAAM,KAAK,QAAQ;AACjC,WAAK;AAAA,IACP;AAEA,QAAI,KAAK,YAAY,KAAK,MAAM,QAAQ;AACtC,YAAM,IAAI,MAAM,8BAA8B,KAAK,EAAE;AAAA,IACvD;AAEA,SAAK;AAEL,SAAK,OAAO,KAAK,EAAE,MAAM,UAAU,QAAQ,OAAO,UAAU,OAAO;AAAA,EACrE;AAAA,EAEQ,iBAAuB;AAC7B,UAAM,QAAQ,KAAK;AACnB,QAAI,QAAQ;AAEZ,WAAO,KAAK,WAAW,KAAK,MAAM,UAAU,KAAK,QAAQ,KAAK,MAAM,KAAK,QAAQ,CAAC,GAAG;AACnF,eAAS,KAAK,MAAM,KAAK,QAAQ;AACjC,WAAK;AAAA,IACP;AAEA,SAAK,OAAO,KAAK,EAAE,MAAM,UAAU,QAAQ,OAAO,UAAU,OAAO;AAAA,EACrE;AAAA,EAEQ,wBAA8B;AACpC,UAAM,QAAQ,KAAK;AACnB,QAAI,QAAQ;AAEZ,WAAO,KAAK,WAAW,KAAK,MAAM,UAAU,KAAK,eAAe,KAAK,MAAM,KAAK,QAAQ,CAAC,GAAG;AAC1F,eAAS,KAAK,MAAM,KAAK,QAAQ;AACjC,WAAK;AAAA,IACP;AAEA,UAAM,aAAa,MAAM,YAAA;AAGzB,YAAQ,YAAA;AAAA,MACN,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,YAAY,UAAU,MAAA,CAAO;AAC5E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,IAAI,OAAO,YAAY,UAAU,MAAA,CAAO;AAC3E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,YAAY,UAAU,MAAA,CAAO;AAC5E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,OAAO,OAAO,YAAY,UAAU,MAAA,CAAO;AAC9E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,OAAO,OAAO,YAAY,UAAU,MAAA,CAAO;AAC9E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,UAAU,OAAO,YAAY,UAAU,MAAA,CAAO;AACjF;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,QAAQ,OAAO,YAAY,UAAU,MAAA,CAAO;AAC/E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,OAAO,OAAO,YAAY,UAAU,MAAA,CAAO;AAC9E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,UAAU,OAAO,YAAY,UAAU,MAAA,CAAO;AACjF;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,MAAM,OAAO,YAAY,UAAU,MAAA,CAAO;AAC7E;AAAA,MACF,KAAK;AACH,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,OAAO,OAAO,YAAY,UAAU,MAAA,CAAO;AAC9E;AAAA,MACF;AAEE,aAAK,OAAO,KAAK,EAAE,MAAM,UAAU,MAAM,OAAO,UAAU,OAAO;AACjE;AAAA,IAAA;AAAA,EAEN;AAAA,EAEQ,wBAA8B;AACpC,UAAM,QAAQ,KAAK;AACnB,QAAI,QAAQ,KAAK,MAAM,KAAK,QAAQ;AACpC,SAAK;AAGL,QAAI,KAAK,WAAW,KAAK,MAAM,UAAU,KAAK,MAAM,KAAK,QAAQ,MAAM,KAAK;AAC1E,eAAS;AACT,WAAK;AAAA,IACP;AAEA,SAAK,OAAO,KAAK,EAAE,MAAM,UAAU,UAAU,OAAO,UAAU,OAAO;AAAA,EACvE;AACF;;;"}