mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-30 02:49:39 +02:00
Initial public Omnigraph repository
This commit is contained in:
commit
338289656a
110 changed files with 60747 additions and 0 deletions
221
crates/omnigraph-compiler/src/query/ast.rs
Normal file
221
crates/omnigraph-compiler/src/query/ast.rs
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
pub const NOW_PARAM_NAME: &str = "__nanograph_now";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QueryFile {
|
||||
pub queries: Vec<QueryDecl>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QueryDecl {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub instruction: Option<String>,
|
||||
pub params: Vec<Param>,
|
||||
pub match_clause: Vec<Clause>,
|
||||
pub return_clause: Vec<Projection>,
|
||||
pub order_clause: Vec<Ordering>,
|
||||
pub limit: Option<u64>,
|
||||
pub mutation: Option<Mutation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Param {
|
||||
pub name: String,
|
||||
pub type_name: String,
|
||||
pub nullable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Clause {
|
||||
Binding(Binding),
|
||||
Traversal(Traversal),
|
||||
Filter(Filter),
|
||||
Negation(Vec<Clause>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Binding {
|
||||
pub variable: String,
|
||||
pub type_name: String,
|
||||
pub prop_matches: Vec<PropMatch>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PropMatch {
|
||||
pub prop_name: String,
|
||||
pub value: MatchValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MatchValue {
|
||||
Literal(Literal),
|
||||
Variable(String),
|
||||
Now,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Traversal {
|
||||
pub src: String,
|
||||
pub edge_name: String,
|
||||
pub dst: String,
|
||||
pub min_hops: u32,
|
||||
pub max_hops: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Filter {
|
||||
pub left: Expr,
|
||||
pub op: CompOp,
|
||||
pub right: Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CompOp {
|
||||
Eq,
|
||||
Ne,
|
||||
Gt,
|
||||
Lt,
|
||||
Ge,
|
||||
Le,
|
||||
Contains,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CompOp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Eq => write!(f, "="),
|
||||
Self::Ne => write!(f, "!="),
|
||||
Self::Gt => write!(f, ">"),
|
||||
Self::Lt => write!(f, "<"),
|
||||
Self::Ge => write!(f, ">="),
|
||||
Self::Le => write!(f, "<="),
|
||||
Self::Contains => write!(f, "contains"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expr {
|
||||
Now,
|
||||
PropAccess {
|
||||
variable: String,
|
||||
property: String,
|
||||
},
|
||||
Nearest {
|
||||
variable: String,
|
||||
property: String,
|
||||
query: Box<Expr>,
|
||||
},
|
||||
Search {
|
||||
field: Box<Expr>,
|
||||
query: Box<Expr>,
|
||||
},
|
||||
Fuzzy {
|
||||
field: Box<Expr>,
|
||||
query: Box<Expr>,
|
||||
max_edits: Option<Box<Expr>>,
|
||||
},
|
||||
MatchText {
|
||||
field: Box<Expr>,
|
||||
query: Box<Expr>,
|
||||
},
|
||||
Bm25 {
|
||||
field: Box<Expr>,
|
||||
query: Box<Expr>,
|
||||
},
|
||||
Rrf {
|
||||
primary: Box<Expr>,
|
||||
secondary: Box<Expr>,
|
||||
k: Option<Box<Expr>>,
|
||||
},
|
||||
Variable(String),
|
||||
Literal(Literal),
|
||||
Aggregate {
|
||||
func: AggFunc,
|
||||
arg: Box<Expr>,
|
||||
},
|
||||
AliasRef(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AggFunc {
|
||||
Count,
|
||||
Sum,
|
||||
Avg,
|
||||
Min,
|
||||
Max,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AggFunc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Count => write!(f, "count"),
|
||||
Self::Sum => write!(f, "sum"),
|
||||
Self::Avg => write!(f, "avg"),
|
||||
Self::Min => write!(f, "min"),
|
||||
Self::Max => write!(f, "max"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Literal {
|
||||
String(String),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
Date(String),
|
||||
DateTime(String),
|
||||
List(Vec<Literal>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Projection {
|
||||
pub expr: Expr,
|
||||
pub alias: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Ordering {
|
||||
pub expr: Expr,
|
||||
pub descending: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Mutation {
|
||||
Insert(InsertMutation),
|
||||
Update(UpdateMutation),
|
||||
Delete(DeleteMutation),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InsertMutation {
|
||||
pub type_name: String,
|
||||
pub assignments: Vec<MutationAssignment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UpdateMutation {
|
||||
pub type_name: String,
|
||||
pub assignments: Vec<MutationAssignment>,
|
||||
pub predicate: MutationPredicate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeleteMutation {
|
||||
pub type_name: String,
|
||||
pub predicate: MutationPredicate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MutationAssignment {
|
||||
pub property: String,
|
||||
pub value: MatchValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MutationPredicate {
|
||||
pub property: String,
|
||||
pub op: CompOp,
|
||||
pub value: MatchValue,
|
||||
}
|
||||
3
crates/omnigraph-compiler/src/query/mod.rs
Normal file
3
crates/omnigraph-compiler/src/query/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod ast;
|
||||
pub mod parser;
|
||||
pub mod typecheck;
|
||||
1689
crates/omnigraph-compiler/src/query/parser.rs
Normal file
1689
crates/omnigraph-compiler/src/query/parser.rs
Normal file
File diff suppressed because it is too large
Load diff
114
crates/omnigraph-compiler/src/query/query.pest
Normal file
114
crates/omnigraph-compiler/src/query/query.pest
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// 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_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" }
|
||||
2776
crates/omnigraph-compiler/src/query/typecheck.rs
Normal file
2776
crates/omnigraph-compiler/src/query/typecheck.rs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue