{"version":3,"file":"parser.cjs","sources":["../../../src/fql/parser.ts"],"sourcesContent":["/**\n * FQL Parser\n * Converts tokens into an Abstract Syntax Tree (AST)\n */\n\nimport type { Token } from \"./lexer.js\";\nimport { TokenType } from \"./lexer.js\";\nimport type { FQLNode, TermNode, PhraseNode, AndNode, OrNode, NotNode, FilterNode, FieldNode, ScoreNode, LangNode } from \"./ast.js\";\n\nexport class FQLSyntaxError extends Error {\n  public position: number;\n  \n  constructor(message: string, position: number) {\n    super(message);\n    this.name = \"FQLSyntaxError\";\n    this.position = position;\n  }\n}\n\nexport class FQLParser {\n  private tokens: Token[] = [];\n  private current: number = 0;\n\n  /**\n   * Parse tokens into an AST\n   */\n  parse(tokens: Token[]): FQLNode {\n    this.tokens = tokens;\n    this.current = 0;\n\n    if (this.tokens.length === 0 || this.tokens[0].type === TokenType.EOF) {\n      throw new FQLSyntaxError(\"Empty query\", 0);\n    }\n\n    const ast = this.parseExpression();\n\n    // Ensure we consumed all tokens\n    if (!this.isAtEnd()) {\n      throw new FQLSyntaxError(`Unexpected token '${this.peek().value}' at position ${this.peek().position}`, this.peek().position);\n    }\n\n    return ast;\n  }\n\n  /**\n   * expression → or_expr\n   */\n  private parseExpression(): FQLNode {\n    return this.parseOrExpression();\n  }\n\n  /**\n   * or_expr → and_expr ( OR and_expr )*\n   */\n  private parseOrExpression(): FQLNode {\n    let left = this.parseAndExpression();\n\n    while (this.match(TokenType.OR)) {\n      const right = this.parseAndExpression();\n      left = {\n        type: \"or\",\n        left,\n        right,\n      } as OrNode;\n    }\n\n    return left;\n  }\n\n  /**\n   * and_expr → not_expr ( AND not_expr )*\n   */\n  private parseAndExpression(): FQLNode {\n    let left = this.parseNotExpression();\n\n    while (this.match(TokenType.AND)) {\n      const right = this.parseNotExpression();\n      left = {\n        type: \"and\",\n        left,\n        right,\n      } as AndNode;\n    }\n\n    return left;\n  }\n\n  /**\n   * not_expr → NOT? primary\n   */\n  private parseNotExpression(): FQLNode {\n    if (this.match(TokenType.NOT)) {\n      const child = this.parsePrimary();\n      return {\n        type: \"not\",\n        child,\n      } as NotNode;\n    }\n\n    return this.parsePrimary();\n  }\n\n  /**\n   * primary → filter | field | lang | score | term | phrase | grouped\n   */\n  private parsePrimary(): FQLNode {\n    // Grouped expression: ( expression )\n    if (this.match(TokenType.LPAREN)) {\n      const expr = this.parseExpression();\n      if (!this.match(TokenType.RPAREN)) {\n        throw new FQLSyntaxError(`Expected ')' at position ${this.peek().position}`, this.peek().position);\n      }\n      return expr;\n    }\n\n    // Filter: EXACT:value, FUZZY:value, etc.\n    if (this.check(TokenType.EXACT) || this.check(TokenType.FUZZY) || this.check(TokenType.PHONETIC) || this.check(TokenType.PREFIX) || this.check(TokenType.REGEX) || this.check(TokenType.COMPOUND)) {\n      return this.parseFilter();\n    }\n\n    // Language: LANG:german term\n    if (this.check(TokenType.LANG)) {\n      return this.parseLang();\n    }\n\n    // Quoted phrase\n    if (this.check(TokenType.QUOTED)) {\n      const token = this.advance();\n      const phrase: PhraseNode = {\n        type: \"phrase\",\n        value: token.value,\n      };\n\n      // Check for SCORE filter after phrase\n      if (this.check(TokenType.SCORE)) {\n        return this.parseScore(phrase);\n      }\n\n      return phrase;\n    }\n\n    // Term (could be field:value or just term)\n    if (this.check(TokenType.TERM)) {\n      const token = this.advance();\n\n      // Check if it's a field selector: term:expression\n      if (this.match(TokenType.COLON)) {\n        const child = this.parsePrimary();\n        const field: FieldNode = {\n          type: \"field\",\n          field: token.value,\n          child,\n        };\n        return field;\n      }\n\n      // Just a regular term\n      const term: TermNode = {\n        type: \"term\",\n        value: token.value,\n      };\n\n      // Check for SCORE filter after term\n      if (this.check(TokenType.SCORE)) {\n        return this.parseScore(term);\n      }\n\n      return term;\n    }\n\n    throw new FQLSyntaxError(`Unexpected token '${this.peek().value}' at position ${this.peek().position}`, this.peek().position);\n  }\n\n  /**\n   * filter → (EXACT|FUZZY|PHONETIC|PREFIX|REGEX|COMPOUND) COLON value\n   */\n  private parseFilter(): FQLNode {\n    const filterToken = this.advance();\n    const filterType = filterToken.value.toLowerCase() as \"exact\" | \"fuzzy\" | \"phonetic\" | \"prefix\" | \"regex\" | \"compound\";\n\n    if (!this.match(TokenType.COLON)) {\n      throw new FQLSyntaxError(`Expected ':' after ${filterToken.value} at position ${this.peek().position}`, this.peek().position);\n    }\n\n    let value: string;\n\n    if (this.check(TokenType.QUOTED)) {\n      value = this.advance().value;\n    } else if (this.check(TokenType.TERM)) {\n      value = this.advance().value;\n    } else {\n      throw new FQLSyntaxError(`Expected value after ${filterToken.value}: at position ${this.peek().position}`, this.peek().position);\n    }\n\n    const filter: FilterNode = {\n      type: \"filter\",\n      filterType,\n      value,\n    };\n\n    // Check for SCORE filter after filter\n    if (this.check(TokenType.SCORE)) {\n      return this.parseScore(filter);\n    }\n\n    return filter;\n  }\n\n  /**\n   * lang → LANG COLON TERM expression\n   */\n  private parseLang(): LangNode {\n    this.advance(); // Consume LANG\n\n    if (!this.match(TokenType.COLON)) {\n      throw new FQLSyntaxError(`Expected ':' after LANG at position ${this.peek().position}`, this.peek().position);\n    }\n\n    if (!this.check(TokenType.TERM)) {\n      throw new FQLSyntaxError(`Expected language name after LANG: at position ${this.peek().position}`, this.peek().position);\n    }\n\n    const language = this.advance().value;\n    const child = this.parsePrimary();\n\n    return {\n      type: \"lang\",\n      language,\n      child,\n    };\n  }\n\n  /**\n   * score → expression SCORE (>|<|>=|<=) NUMBER\n   */\n  private parseScore(child: FQLNode): ScoreNode {\n    this.advance(); // Consume SCORE\n\n    if (!this.check(TokenType.SCORE_OP)) {\n      throw new FQLSyntaxError(`Expected score operator (>, <, >=, <=) at position ${this.peek().position}`, this.peek().position);\n    }\n\n    const operator = this.advance().value as \">\" | \"<\" | \">=\" | \"<=\";\n\n    if (!this.check(TokenType.NUMBER)) {\n      throw new FQLSyntaxError(`Expected number after ${operator} at position ${this.peek().position}`, this.peek().position);\n    }\n\n    const threshold = parseFloat(this.advance().value);\n\n    if (isNaN(threshold) || threshold < 0 || threshold > 1) {\n      throw new FQLSyntaxError(`Score threshold must be between 0 and 1`, this.previous().position);\n    }\n\n    return {\n      type: \"score\",\n      operator,\n      threshold,\n      child,\n    };\n  }\n\n  // Helper methods\n\n  private match(...types: TokenType[]): boolean {\n    for (const type of types) {\n      if (this.check(type)) {\n        this.advance();\n        return true;\n      }\n    }\n    return false;\n  }\n\n  private check(type: TokenType): boolean {\n    if (this.isAtEnd()) return false;\n    return this.peek().type === type;\n  }\n\n  private advance(): Token {\n    if (!this.isAtEnd()) this.current++;\n    return this.previous();\n  }\n\n  private isAtEnd(): boolean {\n    return this.peek().type === TokenType.EOF;\n  }\n\n  private peek(): Token {\n    return this.tokens[this.current];\n  }\n\n  private previous(): Token {\n    return this.tokens[this.current - 1];\n  }\n}\n"],"names":["TokenType"],"mappings":"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"}