eventql_parser/ast.rs
1//! Abstract syntax tree (AST) types for EventQL.
2//!
3//! This module defines the structure of parsed EventQL queries as an abstract
4//! syntax tree. The AST represents the semantic structure of a query, making it
5//! easy to analyze, transform, or execute queries.
6//!
7//! # Core Types
8//!
9//! - [`Query`] - The root of the AST, representing a complete query
10//! - [`Expr`] - Expressions with position and type information
11//! - [`Value`] - The various kinds of expression values (literals, operators, etc.)
12//! - [`Source`] - Data sources in FROM clauses
13//!
14use std::{collections::BTreeMap, mem};
15
16use crate::{
17 analysis::{AnalysisOptions, Typed, static_analysis},
18 error::{AnalysisError, Error},
19 token::{Operator, Token},
20};
21use serde::Serialize;
22
23/// Position information for source code locations.
24///
25/// This struct tracks the line and column number of tokens and AST nodes,
26/// which is useful for error reporting and debugging.
27///
28/// # Examples
29///
30/// ```
31/// use eventql_parser::Pos;
32///
33/// let pos = Pos { line: 1, col: 10 };
34/// assert_eq!(pos.line, 1);
35/// assert_eq!(pos.col, 10);
36/// ```
37#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
38pub struct Pos {
39 /// Line number (1-indexed)
40 pub line: u32,
41 /// Column number (1-indexed)
42 pub col: u32,
43}
44
45impl From<Token<'_>> for Pos {
46 fn from(value: Token<'_>) -> Self {
47 Self {
48 line: value.line,
49 col: value.col,
50 }
51 }
52}
53
54/// Type information for expressions.
55///
56/// This enum represents the type of an expression in the E
57#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize)]
58pub enum Type {
59 /// Type has not been determined yet
60 #[default]
61 Unspecified,
62 /// Numeric type (f64)
63 Number,
64 /// String type
65 String,
66 /// Boolean type
67 Bool,
68 /// Array type
69 Array(Box<Type>),
70 /// Record (object) type
71 Record(BTreeMap<String, Type>),
72 /// Subject pattern type
73 Subject,
74 /// Function type
75 App { args: Vec<Type>, result: Box<Type> },
76 /// Date type (e.g., `2026-01-03`)
77 ///
78 /// Used when a field is explicitly converted to a date using the `AS DATE` syntax.
79 Date,
80 /// Time type (e.g., `13:45:39`)
81 ///
82 /// Used when a field is explicitly converted to a time using the `AS TIME` syntax.
83 Time,
84 /// DateTime type (e.g., `2026-01-01T13:45:39Z`)
85 ///
86 /// Used when a field is explicitly converted to a datetime using the `AS DATETIME` syntax.
87 DateTime,
88 /// Custom type not defined in the EventQL reference
89 ///
90 /// Used when a field is converted to a custom type registered in [`AnalysisOptions::custom_types`].
91 /// The string contains the custom type name as it appears in the query.
92 ///
93 /// # Examples
94 ///
95 /// ```
96 /// use eventql_parser::prelude::{parse_query, AnalysisOptions};
97 ///
98 /// let query = parse_query("FROM e IN events PROJECT INTO { ts: e.data.timestamp as CustomTimestamp }").unwrap();
99 /// let options = AnalysisOptions::default().add_custom_type("CustomTimestamp");
100 /// let typed_query = query.run_static_analysis(&options).unwrap();
101 /// ```
102 Custom(String),
103}
104
105impl Type {
106 pub fn as_record_or_panic_mut(&mut self) -> &mut BTreeMap<String, Type> {
107 if let Self::Record(r) = self {
108 return r;
109 }
110
111 panic!("expected record type, got {:?}", self);
112 }
113
114 /// Checks if two types are the same.
115 ///
116 /// * If `self` is `Type::Unspecified` then `self` is updated to the more specific `Type`.
117 /// * If `self` is `Type::Subject` and is checked against a `Type::String` then `self` is updated to `Type::String`
118 pub fn check(self, attrs: &Attrs, other: Type) -> Result<Type, AnalysisError> {
119 match (self, other) {
120 (Self::Unspecified, other) => Ok(other),
121 (this, Self::Unspecified) => Ok(this),
122 (Self::Subject, Self::Subject) => Ok(Self::Subject),
123
124 // Subjects are strings so there is no reason to reject a type
125 // when compared to a string. However, when it happens, we demote
126 // a subject to a string.
127 (Self::Subject, Self::String) => Ok(Self::String),
128 (Self::String, Self::Subject) => Ok(Self::String),
129
130 (Self::Number, Self::Number) => Ok(Self::Number),
131 (Self::String, Self::String) => Ok(Self::String),
132 (Self::Bool, Self::Bool) => Ok(Self::Bool),
133 (Self::Date, Self::Date) => Ok(Self::Date),
134 (Self::Time, Self::Time) => Ok(Self::Time),
135 (Self::DateTime, Self::DateTime) => Ok(Self::DateTime),
136 (Self::Custom(a), Self::Custom(b)) if a.eq_ignore_ascii_case(b.as_str()) => {
137 Ok(Self::Custom(a))
138 }
139 (Self::Array(mut a), Self::Array(b)) => {
140 *a = a.as_ref().clone().check(attrs, *b)?;
141 Ok(Self::Array(a))
142 }
143
144 (Self::Record(mut a), Self::Record(b)) if a.len() == b.len() => {
145 if a.is_empty() {
146 return Ok(Self::Record(a));
147 }
148
149 for (ak, bk) in a.keys().zip(b.keys()) {
150 if ak != bk {
151 return Err(AnalysisError::TypeMismatch(
152 attrs.pos.line,
153 attrs.pos.col,
154 Self::Record(a),
155 Self::Record(b),
156 ));
157 }
158 }
159
160 for (av, bv) in a.values_mut().zip(b.into_values()) {
161 let a = mem::take(av);
162 *av = a.check(attrs, bv)?;
163 }
164
165 Ok(Self::Record(a))
166 }
167
168 (
169 Self::App {
170 args: mut a_args,
171 result: mut a_res,
172 },
173 Self::App {
174 args: b_args,
175 result: b_res,
176 },
177 ) if a_args.len() == b_args.len() => {
178 if a_args.is_empty() {
179 let tmp = mem::take(a_res.as_mut());
180 *a_res = tmp.check(attrs, *b_res)?;
181 return Ok(Self::App {
182 args: a_args,
183 result: a_res,
184 });
185 }
186
187 for (a, b) in a_args.iter_mut().zip(b_args.into_iter()) {
188 let tmp = mem::take(a);
189 *a = tmp.check(attrs, b)?;
190 }
191
192 let tmp = mem::take(a_res.as_mut());
193 *a_res = tmp.check(attrs, *b_res)?;
194
195 Ok(Self::App {
196 args: a_args,
197 result: a_res,
198 })
199 }
200
201 (this, other) => Err(AnalysisError::TypeMismatch(
202 attrs.pos.line,
203 attrs.pos.col,
204 this,
205 other,
206 )),
207 }
208 }
209}
210
211/// Attributes attached to each expression node.
212///
213/// These attributes provide metadata about an expression, including its
214/// position in the source code, scope information, and type information.
215#[derive(Debug, Clone, Copy, Serialize)]
216pub struct Attrs {
217 /// Source position of this expression
218 pub pos: Pos,
219}
220
221impl Attrs {
222 /// Create new attributes with unspecified type.
223 pub fn new(pos: Pos) -> Self {
224 Self { pos }
225 }
226}
227
228/// An expression with metadata.
229///
230/// This is the fundamental building block of the AST. Every expression
231/// carries attributes (position, scope, type) and a value that determines
232/// what kind of expression it is.
233#[derive(Debug, Clone, Serialize)]
234pub struct Expr {
235 /// Metadata about this expression
236 pub attrs: Attrs,
237 /// The value/kind of this expression
238 pub value: Value,
239}
240
241/// Field access expression (e.g., `e.data.price`).
242///
243/// Represents accessing a field of a record or object using dot notation.
244/// Can be chained for nested field access.
245///
246/// # Examples
247///
248/// In the query `WHERE e.data.user.id == 1`, the expression `e.data.user.id`
249/// is parsed as nested `Access` nodes.
250#[derive(Debug, Clone, Serialize)]
251pub struct Access {
252 /// The target expression being accessed
253 pub target: Box<Expr>,
254 /// The name of the field being accessed
255 pub field: String,
256}
257
258/// Function application (e.g., `sum(e.price)`, `count()`).
259///
260/// Represents a function call with zero or more arguments.
261///
262/// # Examples
263///
264/// In the query `WHERE count(e.items) > 5`, the `count(e.items)` is an `App` node.
265#[derive(Debug, Clone, Serialize)]
266pub struct App {
267 /// Name of the function being called
268 pub func: String,
269 /// Arguments passed to the function
270 pub args: Vec<Expr>,
271}
272
273/// A field in a record literal (e.g., `{name: "Alice", age: 30}`).
274///
275/// Represents a key-value pair in a record construction.
276#[derive(Debug, Clone, Serialize)]
277pub struct Field {
278 /// Field name
279 pub name: String,
280 /// Field value expression
281 pub value: Expr,
282}
283
284/// Binary operation (e.g., `a + b`, `x == y`, `p AND q`).
285///
286/// Represents operations that take two operands, including arithmetic,
287/// comparison, and logical operators.
288///
289/// # Examples
290///
291/// In `WHERE e.price > 100 AND e.active == true`, there are multiple
292/// binary operations: `>`, `==`, and `AND`.
293#[derive(Debug, Clone, Serialize)]
294pub struct Binary {
295 /// Left-hand side operand
296 pub lhs: Box<Expr>,
297 /// The operator
298 pub operator: Operator,
299 /// Right-hand side operand
300 pub rhs: Box<Expr>,
301}
302
303/// Unary operation (e.g., `-x`, `NOT active`).
304///
305/// Represents operations that take a single operand.
306///
307/// # Examples
308///
309/// In `WHERE NOT e.deleted`, the `NOT e.deleted` is a unary operation.
310#[derive(Debug, Clone, Serialize)]
311pub struct Unary {
312 /// The operator (Add for +, Sub for -, Not for NOT)
313 pub operator: Operator,
314 /// The operand expression
315 pub expr: Box<Expr>,
316}
317
318/// The kind of value an expression represents.
319///
320/// This enum contains all the different types of expressions that can appear
321/// in an EventQL query, from simple literals to complex operations.
322#[derive(Debug, Clone, Serialize)]
323pub enum Value {
324 /// Numeric literal (e.g., `42`, `3.14`)
325 Number(f64),
326 /// String literal (e.g., `"hello"`)
327 String(String),
328 /// Boolean literal (`true` or `false`)
329 Bool(bool),
330 /// Identifier (e.g., variable name `e`, `x`)
331 Id(String),
332 /// Array literal (e.g., `[1, 2, 3]`)
333 Array(Vec<Expr>),
334 /// Record literal (e.g., `{name: "Alice", age: 30}`)
335 Record(Vec<Field>),
336 /// Field access (e.g., `e.data.price`)
337 Access(Access),
338 /// Function application (e.g., `sum(e.price)`)
339 App(App),
340 /// Binary operation (e.g., `a + b`, `x == y`)
341 Binary(Binary),
342 /// Unary operation (e.g., `-x`, `NOT active`)
343 Unary(Unary),
344 /// Grouped/parenthesized expression (e.g., `(a + b)`)
345 Group(Box<Expr>),
346}
347
348/// A source binding. A name attached to a source of events.
349///
350/// # Examples
351/// in `FROM e IN events`, `e` is the binding.
352#[derive(Debug, Clone, Serialize)]
353pub struct Binding {
354 /// Name attached to a source of events
355 pub name: String,
356 /// Position in the source code where that binding was introduced
357 pub pos: Pos,
358}
359
360/// A data source in a FROM clause.
361///
362/// Sources specify where data comes from in a query. Each source has a binding
363/// (the variable name) and a kind (what it binds to).
364///
365/// # Examples
366///
367/// In `FROM e IN events`, the source has:
368/// - `binding`: `"e"`
369/// - `kind`: `SourceKind::Name("events")`
370#[derive(Debug, Clone, Serialize)]
371pub struct Source<A> {
372 /// Variable name bound to this source
373 pub binding: Binding,
374 /// What this source represents
375 pub kind: SourceKind<A>,
376}
377
378/// The kind of data source.
379///
380/// EventQL supports three types of sources:
381/// - Named sources (e.g., `FROM e IN events`)
382/// - Subject patterns (e.g., `FROM e IN "users/john"`)
383/// - Subqueries (e.g., `FROM e IN (SELECT ...)`)
384#[derive(Debug, Clone, Serialize)]
385pub enum SourceKind<A> {
386 /// Named source (identifier)
387 Name(String),
388 /// Subject pattern (string literal used as event subject pattern)
389 Subject(String),
390 /// Nested subquery
391 Subquery(Box<Query<A>>),
392}
393
394/// ORDER BY clause specification.
395///
396/// Defines how query results should be sorted.
397///
398/// # Examples
399///
400/// In `ORDER BY e.timestamp DESC`, this would be represented as:
401/// - `expr`: expression for `e.timestamp`
402/// - `order`: `Order::Desc`
403#[derive(Debug, Clone, Serialize)]
404pub struct OrderBy {
405 /// Expression to sort by
406 pub expr: Expr,
407 /// Sort direction (ascending or descending)
408 pub order: Order,
409}
410
411/// Sort order direction.
412///
413/// Specifies whether sorting is ascending or descending.
414#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
415pub enum Order {
416 /// Ascending order (smallest to largest)
417 Asc,
418 /// Descending order (largest to smallest)
419 Desc,
420}
421
422/// GROUP BY clause specification
423///
424/// Defines how query results should be order by.
425/// # Examples
426///
427/// In `GROUP BY e.age HAVING age > 123`, this would be represented as:
428/// - `expr`: expression for `e.age`
429/// - `predicate`: `age > 123`
430#[derive(Debug, Clone, Serialize)]
431pub struct GroupBy {
432 /// Expression to group by
433 pub expr: Expr,
434
435 /// Predicate to filter groups after aggregation
436 pub predicate: Option<Expr>,
437}
438
439/// Result set limit specification.
440///
441/// EventQL supports two types of limits:
442/// - `TOP n` - Take the first n results
443/// - `SKIP n` - Skip the first n results
444///
445/// # Examples
446///
447/// - `TOP 10` limits to first 10 results
448/// - `SKIP 20` skips first 20 results
449#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
450pub enum Limit {
451 /// Skip the first n results
452 Skip(u64),
453 /// Take only the first n results
454 Top(u64),
455}
456
457/// Represents the state of a query that only has a valid syntax. There are no guarantee that all
458/// the variables exists or that the query is sound. For example, if the user is asking for an event
459/// that has field that should be a string or a number at the same time.
460#[derive(Debug, Clone, Copy, Serialize)]
461pub struct Raw;
462
463/// A complete EventQL query.
464///
465/// This is the root node of the AST, representing a full query with all its clauses.
466/// A query must have at least one source and a projection; other clauses are optional.
467///
468/// # Structure
469///
470/// ```text
471/// FROM <alias> <source>
472/// [FROM <alias> <source>] ...
473/// [WHERE <condition>]
474/// [GROUP BY <field> [HAVING <condition>]]
475/// [ORDER BY <field> ASC|DESC]
476/// [TOP|SKIP <n>]
477/// PROJECT INTO [DISTINCT] <projection>
478/// ```
479///
480/// # Examples
481///
482/// ```
483/// use eventql_parser::parse_query;
484///
485/// let query = parse_query(
486/// "FROM e IN events \
487/// WHERE e.price > 100 \
488/// ORDER BY e.timestamp DESC \
489/// TOP 10 \
490/// PROJECT INTO {id: e.id, price: e.price}"
491/// ).unwrap();
492///
493/// assert_eq!(query.sources.len(), 1);
494/// assert!(query.predicate.is_some());
495/// assert!(query.order_by.is_some());
496/// assert!(query.limit.is_some());
497/// ```
498#[derive(Debug, Clone, Serialize)]
499pub struct Query<A> {
500 /// Metadata about this query
501 pub attrs: Attrs,
502 /// FROM clause sources (must have at least one)
503 pub sources: Vec<Source<A>>,
504 /// Optional WHERE clause filter predicate
505 pub predicate: Option<Expr>,
506 /// Optional GROUP BY clause expression
507 pub group_by: Option<GroupBy>,
508 /// Optional ORDER BY clause
509 pub order_by: Option<OrderBy>,
510 /// Optional LIMIT clause (TOP or SKIP)
511 pub limit: Option<Limit>,
512 /// PROJECT INTO clause expression (required)
513 pub projection: Expr,
514 /// Remove duplicate rows from the query's results
515 pub distinct: bool,
516 /// Type-level metadata about the query's analysis state.
517 ///
518 /// This field uses a generic type parameter to track whether the query
519 /// is in a raw (unparsed/untyped) state or has been statically analyzed:
520 /// - `Query<Raw>`: Query parsed but not yet type-checked
521 /// - `Query<Typed>`: Query that has passed static analysis with validated
522 /// types and variable scopes
523 ///
524 /// This provides compile-time guarantees about the query's type safety.
525 pub meta: A,
526}
527
528impl Query<Raw> {
529 /// Performs static analysis on this raw query.
530 ///
531 /// This is a convenience method that runs type checking and variable scoping
532 /// analysis on the query, converting it from a raw (untyped) query to a
533 /// typed query.
534 ///
535 /// # Arguments
536 ///
537 /// * `options` - Configuration containing type information and default scope
538 ///
539 /// # Returns
540 ///
541 /// Returns a typed query on success, or an error if type checking fails.
542 pub fn run_static_analysis(self, options: &AnalysisOptions) -> crate::Result<Query<Typed>> {
543 static_analysis(options, self).map_err(Error::Analysis)
544 }
545}