Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ A complete lexer and parser for EventQL (EQL), a query language designed for eve
## Quick Start

```rust
use eventql_parser::parse_query;
use eventql_parser::Session;

fn main() {
let query = parse_query(
let mut session = Session::builder().build();
let query = session.parse(
"FROM e IN events WHERE e.id == 1 PROJECT INTO e"
).unwrap();

Expand Down
34 changes: 17 additions & 17 deletions src/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ impl AnalysisOptions {

self
}

pub fn empty() -> Self {
Self {
default_scope: Scope::default(),
event_type_info: Type::default(),
custom_types: HashSet::default(),
}
}
}

impl Default for AnalysisOptions {
Expand Down Expand Up @@ -469,7 +477,7 @@ impl Default for AnalysisOptions {
/// # Returns
///
/// Returns a typed query on success, or an `AnalysisError` if type checking fails.
pub fn static_analysis(
pub(crate) fn static_analysis(
arena: &ExprArena,
options: &AnalysisOptions,
query: Query<Raw>,
Expand Down Expand Up @@ -617,16 +625,12 @@ impl<'a> Analysis<'a> {
/// # Example
///
/// ```rust
/// use eventql_parser::{parse_query, prelude::{Analysis, AnalysisOptions}};
/// use eventql_parser::arena::ExprArena;
///
/// let mut arena = ExprArena::default();
/// let query = parse_query(&mut arena, "FROM e IN events WHERE [1,2,3] CONTAINS e.data.price PROJECT INTO e").unwrap();
/// use eventql_parser::Session;
///
/// let options = AnalysisOptions::default();
/// let mut analysis = Analysis::new(&arena, &options);
/// let mut session = Session::builder().build();
/// let query = session.parse("FROM e IN events WHERE [1,2,3] CONTAINS e.data.price PROJECT INTO e").unwrap();
///
/// let typed_query = analysis.analyze_query(query);
/// let typed_query = session.run_static_analysis(query);
/// assert!(typed_query.is_ok());
/// ```
pub fn analyze_query(&mut self, query: Query<Raw>) -> AnalysisResult<Query<Typed>> {
Expand Down Expand Up @@ -1224,16 +1228,12 @@ impl<'a> Analysis<'a> {
/// # Example
///
/// ```rust
/// use eventql_parser::prelude::{tokenize, Parser, Analysis, AnalysisContext, AnalysisOptions, Type};
/// use eventql_parser::arena::ExprArena;
/// use eventql_parser::Session;
///
/// let mut arena = ExprArena::default();
/// let tokens = tokenize("1 + 2").unwrap();
/// let expr = Parser::new(&mut arena, tokens.as_slice()).parse_expr().unwrap();
/// let options = AnalysisOptions::default();
/// let mut analysis = Analysis::new(&arena, &options);
/// let mut session = Session::builder().build();
/// let query = session.parse("FROM e IN events PROJECT INTO { price: 1 + 2 }").unwrap();
///
/// let result = analysis.analyze_expr(&mut AnalysisContext::default(), expr, Type::Number);
/// let result = session.run_static_analysis(query);
/// assert!(result.is_ok());
/// ```
pub fn analyze_expr(
Expand Down
57 changes: 10 additions & 47 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
//! - [`Value`] - The various kinds of expression values (literals, operators, etc.)
//! - [`Source`] - Data sources in FROM clauses
//!
use crate::arena::ExprArena;
use crate::{
analysis::{AnalysisOptions, Typed, static_analysis},
error::{AnalysisError, Error},
error::AnalysisError,
token::{Operator, Token},
};
use ordered_float::OrderedFloat;
Expand Down Expand Up @@ -234,13 +232,13 @@ pub enum Type {
/// # Examples
///
/// ```
/// use eventql_parser::{parse_query, prelude::AnalysisOptions};
/// use eventql_parser::arena::ExprArena;
/// use eventql_parser::Session;
///
/// let mut arena = ExprArena::default();
/// let query = parse_query(&mut arena, "FROM e IN events PROJECT INTO { ts: e.data.timestamp as CustomTimestamp }").unwrap();
/// let options = AnalysisOptions::default().add_custom_type("CustomTimestamp");
/// let typed_query = query.run_static_analysis(&arena, &options).unwrap();
/// let mut session = Session::builder()
/// .declare_custom_type("CustomTimestamp")
/// .build();
/// let query = session.parse("FROM e IN events PROJECT INTO { ts: e.data.timestamp as CustomTimestamp }").unwrap();
/// let typed_query = session.run_static_analysis(query).unwrap();
/// ```
Custom(String),
}
Expand Down Expand Up @@ -778,12 +776,10 @@ pub struct Raw;
/// # Examples
///
/// ```
/// use eventql_parser::parse_query;
/// use eventql_parser::arena::ExprArena;
/// use eventql_parser::Session;
///
/// let mut arena = ExprArena::default();
/// let query = parse_query(
/// &mut arena,
/// let mut session = Session::builder().use_stdlib().build();
/// let query = session.parse(
/// "FROM e IN events \
/// WHERE e.price > 100 \
/// ORDER BY e.timestamp DESC \
Expand Down Expand Up @@ -825,36 +821,3 @@ pub struct Query<A> {
/// This provides compile-time guarantees about the query's type safety.
pub meta: A,
}

impl Query<Raw> {
/// Performs static analysis on this raw query.
///
/// This is a convenience method that runs type checking and variable scoping
/// analysis on the query, converting it from a raw (untyped) query to a
/// typed query.
///
/// The analysis validates:
/// - Variable declarations and scoping
/// - Type compatibility in expressions and operations
/// - Valid field accesses on record types
/// - Correct function argument types and counts
/// - Aggregate function usage restrictions (only in PROJECT INTO)
/// - No mixing of aggregate functions with source-bound fields
/// - Aggregate function arguments are source-bound fields
/// - Non-empty record literals in projections
///
/// # Arguments
///
/// * `options` - Configuration containing type information and default scope
///
/// # Returns
///
/// Returns a typed query on success, or an error if type checking fails.
pub fn run_static_analysis(
self,
arena: &ExprArena,
options: &AnalysisOptions,
) -> crate::Result<Query<Typed>> {
static_analysis(arena, options, self).map_err(Error::Analysis)
}
}
2 changes: 1 addition & 1 deletion src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use nom::{IResult, Parser};
/// - **Strings**: Double-quoted string literals (e.g., `"hello"`)
/// - **Operators**: Arithmetic (`+`, `-`, `*`, `/`), comparison (`==`, `!=`, `<`, `<=`, `>`, `>=`), logical (`AND`, `OR`, `XOR`, `NOT`)
/// - **Symbols**: Structural characters (`(`, `)`, `[`, `]`, `{`, `}`, `.`, `,`, `:`)
pub fn tokenize(input: &str) -> Result<Vec<Token<'_>>, LexerError> {
pub(crate) fn tokenize(input: &str) -> Result<Vec<Token<'_>>, LexerError> {
let mut input = Text::new(input);
let mut tokens = Vec::new();

Expand Down
Loading