// NanoGraph Query Grammar (.gq files) WHITESPACE = _{ " " | "\t" | "\r" | "\n" } COMMENT = _{ LINE_COMMENT | BLOCK_COMMENT } LINE_COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } BLOCK_COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" } query_file = { SOI ~ query_decl* ~ EOI } query_decl = { "query" ~ ident ~ "(" ~ param_list? ~ ")" ~ query_annotation* ~ "{" ~ query_body ~ "}" } query_annotation = { description_annotation | instruction_annotation } description_annotation = { "@description" ~ "(" ~ string_lit ~ ")" } instruction_annotation = { "@instruction" ~ "(" ~ string_lit ~ ")" } query_body = { read_query_body | mutation_body } mutation_body = { mutation_stmt+ } read_query_body = { match_clause ~ return_clause ~ order_clause? ~ limit_clause? } mutation_stmt = { insert_stmt | update_stmt | delete_stmt } insert_stmt = { "insert" ~ type_name ~ "{" ~ mutation_assignment+ ~ "}" } update_stmt = { "update" ~ type_name ~ "set" ~ "{" ~ mutation_assignment+ ~ "}" ~ "where" ~ mutation_predicate } delete_stmt = { "delete" ~ type_name ~ "where" ~ mutation_predicate } mutation_assignment = { ident ~ ":" ~ match_value ~ ","? } mutation_predicate = { ident ~ comp_op ~ match_value } param_list = { param ~ ("," ~ param)* } param = { variable ~ ":" ~ type_ref } type_ref = { (list_type | base_type | vector_type) ~ "?"? } list_type = { "[" ~ base_type ~ "]" } vector_type = { "Vector" ~ "(" ~ integer ~ ")" } base_type = { "String" | "Blob" | "Bool" | "I32" | "I64" | "U32" | "U64" | "F32" | "F64" | "DateTime" | "Date" } match_clause = { "match" ~ "{" ~ clause+ ~ "}" } clause = { negation | binding | traversal | filter | text_search_clause } text_search_clause = { search_call | fuzzy_call | match_text_call } // Binding: $p: Person { name: "Alice" } binding = { variable ~ ":" ~ type_name ~ ("{" ~ prop_match_list ~ "}")? } prop_match_list = { prop_match ~ ("," ~ prop_match)* ~ ","? } prop_match = { ident ~ ":" ~ match_value } match_value = { literal | variable | now_call } // Traversal: $p knows $f traversal = { variable ~ edge_ident ~ traversal_bounds? ~ variable } traversal_bounds = { "{" ~ integer ~ "," ~ integer? ~ "}" } // Filter: $f.age > 25 filter = { expr ~ filter_op ~ expr } // Negation: not { ... } negation = { "not" ~ "{" ~ clause+ ~ "}" } // Return clause — projections separated by commas or newlines return_clause = { "return" ~ "{" ~ projection+ ~ "}" } projection = { expr ~ ("as" ~ ident)? ~ ","? } // Order clause order_clause = { "order" ~ "{" ~ ordering ~ ("," ~ ordering)* ~ "}" } ordering = { nearest_ordering | (expr ~ order_dir?) } nearest_ordering = { "nearest" ~ "(" ~ prop_access ~ "," ~ expr ~ ")" } order_dir = { "asc" | "desc" } // Limit clause limit_clause = { "limit" ~ integer } // Expressions expr = { now_call | nearest_ordering | search_call | fuzzy_call | match_text_call | bm25_call | rrf_call | agg_call | prop_access | variable | literal | ident } now_call = { "now" ~ "(" ~ ")" } search_call = { "search" ~ "(" ~ expr ~ "," ~ expr ~ ")" } fuzzy_call = { "fuzzy" ~ "(" ~ expr ~ "," ~ expr ~ ("," ~ expr)? ~ ")" } match_text_call = { "match_text" ~ "(" ~ expr ~ "," ~ expr ~ ")" } bm25_call = { "bm25" ~ "(" ~ expr ~ "," ~ expr ~ ")" } rank_expr = { nearest_ordering | bm25_call } rrf_call = { "rrf" ~ "(" ~ rank_expr ~ "," ~ rank_expr ~ ("," ~ expr)? ~ ")" } prop_access = { variable ~ "." ~ ident } agg_call = { agg_func ~ "(" ~ expr ~ ")" } agg_func = { "count" | "sum" | "avg" | "min" | "max" } comp_op = { ">=" | "<=" | "!=" | ">" | "<" | "=" } filter_op = { "contains" | comp_op } // Terminals variable = @{ "$" ~ (ident_chars | "_") } ident_chars = @{ (ASCII_ALPHA_LOWER | "_") ~ (ASCII_ALPHANUMERIC | "_")* } // Edge identifier — lowercase start, same as ident but used in traversal context // Must not match keywords edge_ident = @{ !("not" ~ !ASCII_ALPHANUMERIC) ~ (ASCII_ALPHA_LOWER | "_") ~ (ASCII_ALPHANUMERIC | "_")* } type_name = @{ ASCII_ALPHA_UPPER ~ (ASCII_ALPHANUMERIC | "_")* } ident = @{ (ASCII_ALPHA_LOWER | "_") ~ (ASCII_ALPHANUMERIC | "_")* } literal = { list_lit | datetime_lit | date_lit | string_lit | float_lit | integer | bool_lit } date_lit = { "date" ~ "(" ~ string_lit ~ ")" } datetime_lit = { "datetime" ~ "(" ~ string_lit ~ ")" } list_lit = { "[" ~ (literal ~ ("," ~ literal)*)? ~ "]" } string_lit = @{ "\"" ~ string_char* ~ "\"" } string_char = @{ !("\"" | "\\") ~ ANY | "\\" ~ ANY } float_lit = @{ ASCII_DIGIT+ ~ "." ~ ASCII_DIGIT+ } integer = @{ ASCII_DIGIT+ } bool_lit = { "true" | "false" }