Skip to main content

eventql_parser/
lib.rs

1//! EventQL parser library for parsing event sourcing query language.
2//!
3//! This library provides a complete lexer and parser for EventQL (EQL), a query language
4//! designed for event sourcing systems. It allows you to parse EQL query strings into
5//! an abstract syntax tree (AST) that can be analyzed or executed.
6pub mod arena;
7mod ast;
8mod error;
9mod lexer;
10mod parser;
11#[cfg(test)]
12mod tests;
13mod token;
14mod typing;
15
16use crate::arena::Arena;
17use crate::lexer::tokenize;
18use crate::prelude::{
19    Analysis, AnalysisOptions, FunArgs, Scope, Typed, display_type, parse, resolve_type_from_str,
20};
21use crate::token::Token;
22use crate::typing::TypeRef;
23pub use ast::*;
24use rustc_hash::FxHashMap;
25pub use typing::Type;
26
27/// Convenience module that re-exports all public types and functions.
28///
29/// This module provides a single import point for all the library's public API,
30/// including AST types, error types, lexer, parser, and token types.
31pub mod prelude {
32    pub use super::arena::*;
33    pub use super::ast::*;
34    pub use super::error::*;
35    pub use super::parser::*;
36    pub use super::token::*;
37    pub use super::typing::analysis::*;
38    pub use super::typing::*;
39}
40
41/// Builder for function argument specifications.
42///
43/// Allows defining function signatures with both required and optional parameters.
44/// When `required` equals the length of `args`, all parameters are required.
45pub struct FunArgsBuilder<'a> {
46    args: &'a [Type],
47    required: usize,
48}
49
50impl<'a> FunArgsBuilder<'a> {
51    /// Creates a new `FunArgsBuilder` with the given argument types and required count.
52    pub fn new(args: &'a [Type], required: usize) -> Self {
53        Self { args, required }
54    }
55}
56
57impl<'a> From<&'a [Type]> for FunArgsBuilder<'a> {
58    fn from(args: &'a [Type]) -> Self {
59        Self {
60            args,
61            required: args.len(),
62        }
63    }
64}
65
66impl<'a, const N: usize> From<&'a [Type; N]> for FunArgsBuilder<'a> {
67    fn from(value: &'a [Type; N]) -> Self {
68        Self {
69            args: value.as_slice(),
70            required: value.len(),
71        }
72    }
73}
74
75/// Builder for configuring type information on a [`SessionBuilder`].
76///
77/// Obtained by calling [`SessionBuilder::declare_type`]. Use [`define_record`](EventTypeBuilder::define_record)
78/// to define a record-shaped type. Call [`done`](EventTypeBuilder::done) to return to the [`SessionBuilder`].
79pub struct EventTypeBuilder<'a> {
80    parent: &'a mut SessionBuilder,
81}
82
83impl<'a> EventTypeBuilder<'a> {
84    /// Starts building a record-shaped event type with named fields.
85    pub fn define_record(self) -> EventTypeRecordBuilder<'a> {
86        EventTypeRecordBuilder {
87            inner: self,
88            props: Default::default(),
89        }
90    }
91
92    /// Registers a type for a specific named data source.
93    ///
94    /// Queries targeting `data_source` will use `tpe` for type checking instead of the default event type.
95    /// Data source names are case-insensitive.
96    pub fn data_source(self, data_source: &str, tpe: Type) -> Self {
97        let data_source = self.parent.arena.strings.alloc_no_case(data_source);
98
99        self.parent.options.data_sources.insert(data_source, tpe);
100
101        self
102    }
103
104    /// Finalizes type configuration and returns the [`SessionBuilder`].
105    pub fn done(self) {}
106}
107
108/// Builder for defining the fields of a record-shaped event type.
109///
110/// Obtained by calling [`EventTypeBuilder::define_record`]. Add fields with [`prop`](EventTypeRecordBuilder::prop)
111/// and finalize with [`as_default_event_type`](EventTypeRecordBuilder::as_default_event_type) or
112/// [`for_data_source`](EventTypeRecordBuilder::for_data_source) to return to the [`EventTypeBuilder`].
113pub struct EventTypeRecordBuilder<'a> {
114    inner: EventTypeBuilder<'a>,
115    props: FxHashMap<StrRef, Type>,
116}
117
118impl<'a> EventTypeRecordBuilder<'a> {
119    /// Conditionally adds a field to the event record type.
120    pub fn prop_when(mut self, test: bool, name: &str, tpe: Type) -> Self {
121        if test {
122            self.props
123                .insert(self.inner.parent.arena.strings.alloc(name), tpe);
124        }
125
126        self
127    }
128
129    /// Adds a field with the given name and type to the event record type.
130    pub fn prop(mut self, name: &str, tpe: Type) -> Self {
131        self.props
132            .insert(self.inner.parent.arena.strings.alloc(name), tpe);
133        self
134    }
135
136    /// Finalizes the event record type and returns the [`SessionBuilder`].
137    pub fn as_default_event_type(self) -> EventTypeBuilder<'a> {
138        let ptr = self.inner.parent.arena.types.alloc_record(self.props);
139        self.inner.parent.set_default_event_type(Type::Record(ptr));
140        self.inner
141    }
142
143    /// Finalizes the record type and registers it for a specific named data source.
144    ///
145    /// Queries targeting `data_source` will use this record type for type checking.
146    /// Data source names are case-insensitive. Returns the [`EventTypeBuilder`] to allow
147    /// chaining further type declarations.
148    pub fn for_data_source(self, data_source: &str) -> EventTypeBuilder<'a> {
149        let data_source = self.inner.parent.arena.strings.alloc_no_case(data_source);
150        let ptr = self.inner.parent.arena.types.alloc_record(self.props);
151
152        self.inner
153            .parent
154            .options
155            .data_sources
156            .insert(data_source, Type::Record(ptr));
157
158        self.inner
159    }
160
161    /// Creates a record type and returns it with its registered type reference.
162    ///
163    /// Use the returned [`Type`] where an API expects the record type directly. Use the
164    /// returned [`TypeRef`] when building another type that needs to point at this record,
165    /// such as [`Type::Array`].
166    ///
167    /// The [`TypeRef`] belongs to the current [`SessionBuilder`]'s arena and should only
168    /// be used with types configured through the same builder.
169    pub fn build(self) -> (Type, TypeRef) {
170        let ptr = self.inner.parent.arena.types.alloc_record(self.props);
171        let tpe = Type::Record(ptr);
172
173        let type_ref = self.inner.parent.arena.types.register_type(tpe);
174
175        (tpe, type_ref)
176    }
177}
178
179/// A specialized `Result` type for EventQL parser operations.
180pub type Result<A> = std::result::Result<A, error::Error>;
181
182/// `SessionBuilder` is a builder for `Session` objects.
183///
184/// It allows for the configuration of analysis options, such as declaring
185/// functions (both regular and aggregate), and event types before building an `EventQL` parsing session.
186#[derive(Default)]
187pub struct SessionBuilder {
188    arena: Arena,
189    options: AnalysisOptions,
190}
191
192impl SessionBuilder {
193    /// Declares a new function with the given name, arguments, and return type.
194    ///
195    /// This function adds a new entry to the session's default scope, allowing
196    /// the parser to recognize and type-check calls to this function.
197    ///
198    /// # Arguments
199    ///
200    /// * `name` - The name of the function.
201    /// * `args` - The arguments the function accepts, which can be converted into `FunArgs`.
202    /// * `result` - The return type of the function.
203    pub fn declare_func<'a>(
204        &mut self,
205        name: &'a str,
206        args: impl Into<FunArgsBuilder<'a>>,
207        result: Type,
208    ) {
209        let builder = args.into();
210        let name = self.arena.strings.alloc_no_case(name);
211        let args = self.arena.types.alloc_args(builder.args);
212
213        self.options.default_scope.declare(
214            name,
215            Type::App {
216                args: FunArgs {
217                    values: args,
218                    needed: builder.required,
219                },
220                result: self.arena.types.register_type(result),
221                aggregate: false,
222            },
223        );
224    }
225
226    /// Declares a new aggregate function with the given name, arguments, and return type.
227    ///
228    /// Similar to `declare_func`, but marks the function as an aggregate function.
229    /// Aggregate functions have specific rules for where they can be used in an EQL query.
230    ///
231    /// # Arguments
232    ///
233    /// * `name` - The name of the aggregate function.
234    /// * `args` - The arguments the aggregate function accepts.
235    /// * `result` - The return type of the aggregate function.
236    pub fn declare_agg_func<'a>(
237        &mut self,
238        name: &'a str,
239        args: impl Into<FunArgsBuilder<'a>>,
240        result: Type,
241    ) {
242        let builder = args.into();
243        let name = self.arena.strings.alloc_no_case(name);
244        let args = self.arena.types.alloc_args(builder.args);
245
246        self.options.default_scope.declare(
247            name,
248            Type::App {
249                args: FunArgs {
250                    values: args,
251                    needed: builder.required,
252                },
253                result: self.arena.types.register_type(result),
254                aggregate: true,
255            },
256        );
257    }
258
259    /// Conditionally declares the expected type of event records.
260    ///
261    /// This type information is crucial for type-checking event properties
262    /// accessed in EQL queries (e.g., `e.id`, `e.data.value`).
263    /// The declaration only happens if `test` is `true`.
264    ///
265    /// # Arguments
266    ///
267    /// * `test` - A boolean indicating whether to declare the event type.
268    /// * `tpe` - The `Type` representing the structure of event records.
269    pub fn set_default_event_type(&mut self, tpe: Type) {
270        self.options.default_event_type = tpe;
271    }
272
273    /// Declares the expected type of event records.
274    ///
275    /// This type information is crucial for type-checking event properties
276    /// accessed in EQL queries (e.g., `e.id`, `e.data.value`).
277    ///
278    /// # Arguments
279    ///
280    /// * `tpe` - The `Type` representing the structure of event records.
281    pub fn declare_type(&mut self) -> EventTypeBuilder<'_> {
282        EventTypeBuilder { parent: self }
283    }
284
285    /// Includes the standard library of functions and event types in the session.
286    ///
287    /// This method pre-configures the `SessionBuilder` with a set of commonly
288    /// used functions (e.g., mathematical, string, date/time) and a default
289    /// event type definition. Calling this method is equivalent to calling
290    /// `declare_func` and `declare_agg_func` for all standard library functions,
291    /// and `declare_event_type` for the default event structure.
292    pub fn use_stdlib(mut self) -> Self {
293        self.declare_func("abs", &[Type::Number], Type::Number);
294        self.declare_func("ceil", &[Type::Number], Type::Number);
295        self.declare_func("floor", &[Type::Number], Type::Number);
296        self.declare_func("round", &[Type::Number], Type::Number);
297        self.declare_func("cos", &[Type::Number], Type::Number);
298        self.declare_func("exp", &[Type::Number], Type::Number);
299        self.declare_func("pow", &[Type::Number, Type::Number], Type::Number);
300        self.declare_func("sqrt", &[Type::Number], Type::Number);
301        self.declare_func("rand", &[], Type::Number);
302        self.declare_func("pi", &[Type::Number], Type::Number);
303        self.declare_func("lower", &[Type::String], Type::String);
304        self.declare_func("upper", &[Type::String], Type::String);
305        self.declare_func("trim", &[Type::String], Type::String);
306        self.declare_func("ltrim", &[Type::String], Type::String);
307        self.declare_func("rtrim", &[Type::String], Type::String);
308        self.declare_func("len", &[Type::String], Type::Number);
309        self.declare_func("instr", &[Type::String], Type::Number);
310        self.declare_func(
311            "substring",
312            &[Type::String, Type::Number, Type::Number],
313            Type::String,
314        );
315        self.declare_func(
316            "replace",
317            &[Type::String, Type::String, Type::String],
318            Type::String,
319        );
320        self.declare_func("startswith", &[Type::String, Type::String], Type::Bool);
321        self.declare_func("endswith", &[Type::String, Type::String], Type::Bool);
322        self.declare_func("now", &[], Type::DateTime);
323        self.declare_func("year", &[Type::Date], Type::Number);
324        self.declare_func("month", &[Type::Date], Type::Number);
325        self.declare_func("day", &[Type::Date], Type::Number);
326        self.declare_func("hour", &[Type::Time], Type::Number);
327        self.declare_func("minute", &[Type::Time], Type::Number);
328        self.declare_func("second", &[Type::Time], Type::Number);
329        self.declare_func("weekday", &[Type::Date], Type::Number);
330        self.declare_func(
331            "IF",
332            &[Type::Bool, Type::Unspecified, Type::Unspecified],
333            Type::Unspecified,
334        );
335        self.declare_agg_func(
336            "count",
337            FunArgsBuilder {
338                args: &[Type::Bool],
339                required: 0,
340            },
341            Type::Number,
342        );
343        self.declare_agg_func("sum", &[Type::Number], Type::Number);
344        self.declare_agg_func("avg", &[Type::Number], Type::Number);
345        self.declare_agg_func("min", &[Type::Number], Type::Number);
346        self.declare_agg_func("max", &[Type::Number], Type::Number);
347        self.declare_agg_func("median", &[Type::Number], Type::Number);
348        self.declare_agg_func("stddev", &[Type::Number], Type::Number);
349        self.declare_agg_func("variance", &[Type::Number], Type::Number);
350        self.declare_agg_func("unique", &[Type::Unspecified], Type::Unspecified);
351        self.declare_type()
352            .data_source("eventtypes", Type::String)
353            .data_source("subjects", Type::String)
354            .define_record()
355            .prop("specversion", Type::String)
356            .prop("id", Type::String)
357            .prop("time", Type::DateTime)
358            .prop("source", Type::String)
359            .prop("subject", Type::Subject)
360            .prop("type", Type::String)
361            .prop("datacontenttype", Type::String)
362            .prop("data", Type::Unspecified)
363            .prop("predecessorhash", Type::String)
364            .prop("hash", Type::String)
365            .prop("traceparent", Type::String)
366            .prop("tracestate", Type::String)
367            .prop("signature", Type::String)
368            .as_default_event_type();
369
370        self
371    }
372
373    /// Builds the `Session` object with the configured analysis options.
374    ///
375    /// This consumes the `SessionBuilder` and returns a `Session` instance
376    /// ready for tokenizing, parsing, and analyzing EventQL queries.
377    pub fn build(mut self) -> Session {
378        self.arena.types.freeze();
379
380        Session {
381            arena: self.arena,
382            options: self.options,
383        }
384    }
385}
386
387/// `Session` is the main entry point for parsing and analyzing EventQL queries.
388///
389/// It holds the necessary context, such as the expression arena and analysis options,
390/// to perform lexical analysis, parsing, and static analysis of EQL query strings.
391pub struct Session {
392    arena: Arena,
393    options: AnalysisOptions,
394}
395
396impl Session {
397    /// Creates a new `SessionBuilder` for configuring and building a `Session`.
398    ///
399    /// This is the recommended way to create a `Session` instance, allowing
400    /// for customization of functions, and event types.
401    ///
402    /// # Returns
403    ///
404    /// A new `SessionBuilder` instance.
405    pub fn builder() -> SessionBuilder {
406        SessionBuilder::default()
407    }
408
409    /// Tokenize an EventQL query string.
410    ///
411    /// This function performs lexical analysis on the input string, converting it
412    /// into a sequence of tokens. Each token includes position information (line
413    /// and column numbers) for error reporting.
414    /// # Recognized Tokens
415    ///
416    /// - **Identifiers**: Alphanumeric names starting with a letter (e.g., `events`, `e`)
417    /// - **Keywords**: Case-insensitive SQL-like keywords detected by the parser
418    /// - **Numbers**: Floating-point literals (e.g., `42`, `3.14`)
419    /// - **Strings**: Double-quoted string literals (e.g., `"hello"`)
420    /// - **Operators**: Arithmetic (`+`, `-`, `*`, `/`), comparison (`==`, `!=`, `<`, `<=`, `>`, `>=`), logical (`AND`, `OR`, `XOR`, `NOT`)
421    /// - **Symbols**: Structural characters (`(`, `)`, `[`, `]`, `{`, `}`, `.`, `,`, `:`)
422    pub fn tokenize<'a>(&self, input: &'a str) -> Result<Vec<Token<'a>>> {
423        let tokens = tokenize(input)?;
424        Ok(tokens)
425    }
426
427    /// Parse an EventQL query string into an abstract syntax tree.
428    ///
429    /// This is the main entry point for parsing EventQL queries. It performs both
430    /// lexical analysis (tokenization) and syntactic analysis (parsing) in a single call.
431    /// # Examples
432    ///
433    /// ```
434    /// use eventql_parser::Session;
435    ///
436    /// // Parse a simple query
437    /// let mut session = Session::builder().use_stdlib().build();
438    /// let query = session.parse("FROM e IN events WHERE e.id == \"1\" PROJECT INTO e").unwrap();
439    /// assert!(query.predicate.is_some());
440    /// ```
441    pub fn parse(&mut self, input: &str) -> Result<Query<Raw>> {
442        let tokens = self.tokenize(input)?;
443        Ok(parse(&mut self.arena, tokens.as_slice())?)
444    }
445
446    /// Performs static analysis on an EventQL query.
447    ///
448    /// This function takes a raw (untyped) query and performs type checking and
449    /// variable scoping analysis. It validates that:
450    /// - All variables are properly declared
451    /// - Types match expected types in expressions and operations
452    /// - Field accesses are valid for their record types
453    /// - Function calls have the correct argument types
454    /// - Aggregate functions are only used in PROJECT INTO clauses
455    /// - Aggregate functions are not mixed with source-bound fields in projections
456    /// - Aggregate function arguments are source-bound fields (not constants or function results)
457    /// - Record literals are non-empty in projection contexts
458    ///
459    /// # Arguments
460    ///
461    /// * `options` - Configuration containing type information and default scope
462    /// * `query` - The raw query to analyze
463    ///
464    /// # Returns
465    ///
466    /// Returns a typed query on success, or an `AnalysisError` if type checking fails.
467    pub fn run_static_analysis(&mut self, query: Query<Raw>) -> Result<Query<Typed>> {
468        let mut analysis = self.analysis();
469        Ok(analysis.analyze_query(query)?)
470    }
471
472    /// Converts a type name string to its corresponding [`Type`] variant.
473    ///
474    /// This function performs case-insensitive matching for built-in type names defined
475    /// in the analysis options.
476    ///
477    /// # Returns
478    ///
479    /// * `Some(Type)` - If the name matches a built-in type
480    /// * `None` - If the name doesn't match any known type
481    ///
482    /// # Built-in Type Mappings
483    ///
484    /// The following type names are recognized (case-insensitive):
485    /// - `"string"` → [`Type::String`]
486    /// - `"int"` or `"float64"` → [`Type::Number`]
487    /// - `"boolean"` → [`Type::Bool`]
488    /// - `"date"` → [`Type::Date`]
489    /// - `"time"` → [`Type::Time`]
490    /// - `"datetime"` → [`Type::DateTime`]
491    pub fn resolve_type(&self, name: &str) -> Option<Type> {
492        resolve_type_from_str(name)
493    }
494
495    /// Provides human-readable string formatting for types.
496    ///
497    /// Function types display optional parameters with a `?` suffix. For example,
498    /// a function with signature `(boolean, number?) -> string` accepts 1 or 2 arguments.
499    /// Aggregate functions use `=>` instead of `->` in their signature.
500    pub fn display_type(&self, tpe: Type) -> String {
501        display_type(&self.arena, tpe)
502    }
503
504    /// Creates an [`Analysis`] instance for fine-grained control over static analysis.
505    ///
506    /// Use this when you need to analyze individual expressions or manage scopes manually,
507    /// rather than using [`run_static_analysis`](Session::run_static_analysis) for whole queries.
508    pub fn analysis(&mut self) -> Analysis<'_> {
509        Analysis::new(&mut self.arena, &self.options)
510    }
511
512    /// Returns a reference to the underlying [`Arena`].
513    pub fn arena(&self) -> &Arena {
514        &self.arena
515    }
516
517    /// Returns a mutable reference to the underlying [`Arena`].
518    pub fn arena_mut(&mut self) -> &mut Arena {
519        &mut self.arena
520    }
521
522    /// Returns the global [`Scope`]
523    pub fn global_scope(&self) -> &Scope {
524        &self.options.default_scope
525    }
526}