Skip to content

Commit e208c9e

Browse files
authored
feat: introduce session type (#23)
1 parent 2ac4f7f commit e208c9e

10 files changed

Lines changed: 645 additions & 399 deletions

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ A complete lexer and parser for EventQL (EQL), a query language designed for eve
2424
## Quick Start
2525

2626
```rust
27-
use eventql_parser::parse_query;
27+
use eventql_parser::Session;
2828

2929
fn main() {
30-
let query = parse_query(
30+
let mut session = Session::builder().build();
31+
let query = session.parse(
3132
"FROM e IN events WHERE e.id == 1 PROJECT INTO e"
3233
).unwrap();
3334

src/analysis.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ impl AnalysisOptions {
104104

105105
self
106106
}
107+
108+
pub fn empty() -> Self {
109+
Self {
110+
default_scope: Scope::default(),
111+
event_type_info: Type::default(),
112+
custom_types: HashSet::default(),
113+
}
114+
}
107115
}
108116

109117
impl Default for AnalysisOptions {
@@ -469,7 +477,7 @@ impl Default for AnalysisOptions {
469477
/// # Returns
470478
///
471479
/// Returns a typed query on success, or an `AnalysisError` if type checking fails.
472-
pub fn static_analysis(
480+
pub(crate) fn static_analysis(
473481
arena: &ExprArena,
474482
options: &AnalysisOptions,
475483
query: Query<Raw>,
@@ -617,16 +625,12 @@ impl<'a> Analysis<'a> {
617625
/// # Example
618626
///
619627
/// ```rust
620-
/// use eventql_parser::{parse_query, prelude::{Analysis, AnalysisOptions}};
621-
/// use eventql_parser::arena::ExprArena;
622-
///
623-
/// let mut arena = ExprArena::default();
624-
/// let query = parse_query(&mut arena, "FROM e IN events WHERE [1,2,3] CONTAINS e.data.price PROJECT INTO e").unwrap();
628+
/// use eventql_parser::Session;
625629
///
626-
/// let options = AnalysisOptions::default();
627-
/// let mut analysis = Analysis::new(&arena, &options);
630+
/// let mut session = Session::builder().build();
631+
/// let query = session.parse("FROM e IN events WHERE [1,2,3] CONTAINS e.data.price PROJECT INTO e").unwrap();
628632
///
629-
/// let typed_query = analysis.analyze_query(query);
633+
/// let typed_query = session.run_static_analysis(query);
630634
/// assert!(typed_query.is_ok());
631635
/// ```
632636
pub fn analyze_query(&mut self, query: Query<Raw>) -> AnalysisResult<Query<Typed>> {
@@ -1224,16 +1228,12 @@ impl<'a> Analysis<'a> {
12241228
/// # Example
12251229
///
12261230
/// ```rust
1227-
/// use eventql_parser::prelude::{tokenize, Parser, Analysis, AnalysisContext, AnalysisOptions, Type};
1228-
/// use eventql_parser::arena::ExprArena;
1231+
/// use eventql_parser::Session;
12291232
///
1230-
/// let mut arena = ExprArena::default();
1231-
/// let tokens = tokenize("1 + 2").unwrap();
1232-
/// let expr = Parser::new(&mut arena, tokens.as_slice()).parse_expr().unwrap();
1233-
/// let options = AnalysisOptions::default();
1234-
/// let mut analysis = Analysis::new(&arena, &options);
1233+
/// let mut session = Session::builder().build();
1234+
/// let query = session.parse("FROM e IN events PROJECT INTO { price: 1 + 2 }").unwrap();
12351235
///
1236-
/// let result = analysis.analyze_expr(&mut AnalysisContext::default(), expr, Type::Number);
1236+
/// let result = session.run_static_analysis(query);
12371237
/// assert!(result.is_ok());
12381238
/// ```
12391239
pub fn analyze_expr(

src/ast.rs

Lines changed: 10 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@
1111
//! - [`Value`] - The various kinds of expression values (literals, operators, etc.)
1212
//! - [`Source`] - Data sources in FROM clauses
1313
//!
14-
use crate::arena::ExprArena;
1514
use crate::{
16-
analysis::{AnalysisOptions, Typed, static_analysis},
17-
error::{AnalysisError, Error},
15+
error::AnalysisError,
1816
token::{Operator, Token},
1917
};
2018
use ordered_float::OrderedFloat;
@@ -234,13 +232,13 @@ pub enum Type {
234232
/// # Examples
235233
///
236234
/// ```
237-
/// use eventql_parser::{parse_query, prelude::AnalysisOptions};
238-
/// use eventql_parser::arena::ExprArena;
235+
/// use eventql_parser::Session;
239236
///
240-
/// let mut arena = ExprArena::default();
241-
/// let query = parse_query(&mut arena, "FROM e IN events PROJECT INTO { ts: e.data.timestamp as CustomTimestamp }").unwrap();
242-
/// let options = AnalysisOptions::default().add_custom_type("CustomTimestamp");
243-
/// let typed_query = query.run_static_analysis(&arena, &options).unwrap();
237+
/// let mut session = Session::builder()
238+
/// .declare_custom_type("CustomTimestamp")
239+
/// .build();
240+
/// let query = session.parse("FROM e IN events PROJECT INTO { ts: e.data.timestamp as CustomTimestamp }").unwrap();
241+
/// let typed_query = session.run_static_analysis(query).unwrap();
244242
/// ```
245243
Custom(String),
246244
}
@@ -778,12 +776,10 @@ pub struct Raw;
778776
/// # Examples
779777
///
780778
/// ```
781-
/// use eventql_parser::parse_query;
782-
/// use eventql_parser::arena::ExprArena;
779+
/// use eventql_parser::Session;
783780
///
784-
/// let mut arena = ExprArena::default();
785-
/// let query = parse_query(
786-
/// &mut arena,
781+
/// let mut session = Session::builder().use_stdlib().build();
782+
/// let query = session.parse(
787783
/// "FROM e IN events \
788784
/// WHERE e.price > 100 \
789785
/// ORDER BY e.timestamp DESC \
@@ -825,36 +821,3 @@ pub struct Query<A> {
825821
/// This provides compile-time guarantees about the query's type safety.
826822
pub meta: A,
827823
}
828-
829-
impl Query<Raw> {
830-
/// Performs static analysis on this raw query.
831-
///
832-
/// This is a convenience method that runs type checking and variable scoping
833-
/// analysis on the query, converting it from a raw (untyped) query to a
834-
/// typed query.
835-
///
836-
/// The analysis validates:
837-
/// - Variable declarations and scoping
838-
/// - Type compatibility in expressions and operations
839-
/// - Valid field accesses on record types
840-
/// - Correct function argument types and counts
841-
/// - Aggregate function usage restrictions (only in PROJECT INTO)
842-
/// - No mixing of aggregate functions with source-bound fields
843-
/// - Aggregate function arguments are source-bound fields
844-
/// - Non-empty record literals in projections
845-
///
846-
/// # Arguments
847-
///
848-
/// * `options` - Configuration containing type information and default scope
849-
///
850-
/// # Returns
851-
///
852-
/// Returns a typed query on success, or an error if type checking fails.
853-
pub fn run_static_analysis(
854-
self,
855-
arena: &ExprArena,
856-
options: &AnalysisOptions,
857-
) -> crate::Result<Query<Typed>> {
858-
static_analysis(arena, options, self).map_err(Error::Analysis)
859-
}
860-
}

src/lexer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use nom::{IResult, Parser};
3333
/// - **Strings**: Double-quoted string literals (e.g., `"hello"`)
3434
/// - **Operators**: Arithmetic (`+`, `-`, `*`, `/`), comparison (`==`, `!=`, `<`, `<=`, `>`, `>=`), logical (`AND`, `OR`, `XOR`, `NOT`)
3535
/// - **Symbols**: Structural characters (`(`, `)`, `[`, `]`, `{`, `}`, `.`, `,`, `:`)
36-
pub fn tokenize(input: &str) -> Result<Vec<Token<'_>>, LexerError> {
36+
pub(crate) fn tokenize(input: &str) -> Result<Vec<Token<'_>>, LexerError> {
3737
let mut input = Text::new(input);
3838
let mut tokens = Vec::new();
3939

0 commit comments

Comments
 (0)