// Hermes Query Language Grammar

WHITESPACE = _{ " " | "\t" | "\r" | "\n" }

// Basic tokens
term = @{ (ASCII_ALPHANUMERIC | "_" | "-")+ }
quoted_string = @{ "\"" ~ inner_string ~ "\"" }
inner_string = @{ (!("\"" | "\\") ~ ANY | "\\" ~ ANY)* }
number = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }

// Field specification
field_name = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
field_spec = { field_name ~ ":" }

// Query types
term_query = { field_spec? ~ term }
phrase_query = { field_spec? ~ quoted_string }

// Prefix query: field:value* or value*
// Value accepts any non-whitespace except quotes, parens, and * (for URLs, paths, etc.)
prefix_value = @{ (!(WHITESPACE | "\"" | "(" | ")" | "*") ~ ANY)+ }
prefix_query = { field_spec? ~ prefix_value ~ "*" }

// Vector query: field:ann([1.0, 2.0, 3.0], k=10, nprobe=32)
// or field:ann([1.0, 2.0, 3.0], 10)
vector_array = { "[" ~ number ~ ("," ~ number)* ~ "]" }
ann_param = { ("k" | "nprobe" | "rerank") ~ "=" ~ number }
ann_params = { number | ann_param ~ ("," ~ ann_param)* }
ann_query = { field_spec ~ "ann" ~ "(" ~ vector_array ~ ("," ~ ann_params)? ~ ")" }

// Sparse vector query: field:sparse({1: 0.5, 5: 0.3}, k=10)
sparse_entry = { number ~ ":" ~ number }
sparse_map = { "{" ~ sparse_entry ~ ("," ~ sparse_entry)* ~ "}" }
sparse_query = { field_spec ~ "sparse" ~ "(" ~ sparse_map ~ ("," ~ ann_params)? ~ ")" }

// Boolean operators
and_op = { "AND" | "&&" }
or_op = { "OR" | "||" }
not_op = { "NOT" | "-" | "!" }

// Grouping
group = { "(" ~ or_expr ~ ")" }

// Primary expression (vector queries added)
primary = { not_op? ~ (group | ann_query | sparse_query | phrase_query | prefix_query | term_query) }

// Boolean expressions
and_expr = { primary ~ (and_op ~ primary)* }
// Implicit OR: space-separated and_exprs without an explicit OR operator
// are treated as OR (matching parse_plain_text semantics).
or_expr = { and_expr ~ (or_op ~ and_expr | and_expr)* }

// Top-level query
query = { SOI ~ or_expr ~ EOI }
