diff --git a/README.md b/README.md index 03e55f0..0479a8f 100644 --- a/README.md +++ b/README.md @@ -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(); diff --git a/src/analysis.rs b/src/analysis.rs index bcfa53f..abde3c9 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -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 { @@ -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, @@ -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) -> AnalysisResult> { @@ -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( diff --git a/src/ast.rs b/src/ast.rs index 7748e90..7b30c72 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -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; @@ -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), } @@ -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 \ @@ -825,36 +821,3 @@ pub struct Query { /// This provides compile-time guarantees about the query's type safety. pub meta: A, } - -impl Query { - /// 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> { - static_analysis(arena, options, self).map_err(Error::Analysis) - } -} diff --git a/src/lexer.rs b/src/lexer.rs index 0ea44a1..51ed282 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -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>, LexerError> { +pub(crate) fn tokenize(input: &str) -> Result>, LexerError> { let mut input = Text::new(input); let mut tokens = Vec::new(); diff --git a/src/lib.rs b/src/lib.rs index 0df9709..458fcb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,12 @@ mod tests; mod token; use crate::arena::ExprArena; -use crate::prelude::{parse, tokenize}; +use crate::lexer::tokenize; +use crate::prelude::{AnalysisOptions, Typed, parse, static_analysis}; +use crate::token::Token; pub use ast::*; +use rustc_hash::FxHashMap; +use unicase::Ascii; /// Convenience module that re-exports all public types and functions. /// @@ -25,48 +29,365 @@ pub mod prelude { pub use super::analysis::*; pub use super::ast::*; pub use super::error::*; - pub use super::lexer::*; pub use super::parser::*; pub use super::token::*; } pub type Result = std::result::Result; -/// Parse an EventQL query string into an abstract syntax tree. +/// `SessionBuilder` is a builder for `Session` objects. /// -/// This is the main entry point for parsing EventQL queries. It performs both -/// lexical analysis (tokenization) and syntactic analysis (parsing) in a single call. -/// # Examples -/// -/// ``` -/// use eventql_parser::parse_query; -/// use eventql_parser::arena::ExprArena; -/// -/// let mut arena = ExprArena::default(); -/// -/// // Parse a simple query -/// let query = parse_query(&mut arena, "FROM e IN events WHERE e.id == 1 PROJECT INTO e").unwrap(); -/// assert!(query.predicate.is_some()); -/// -/// // Parse with multiple clauses -/// let complex = parse_query( -/// &mut arena, -/// "FROM e IN events \ -/// WHERE e.price > 100 \ -/// ORDER BY e.timestamp DESC \ -/// TOP 10 \ -/// PROJECT INTO {id: e.id, price: e.price}" -/// ).unwrap(); -/// assert!(complex.order_by.is_some()); -/// assert!(complex.limit.is_some()); +/// It allows for the configuration of analysis options, such as declaring +/// functions (both regular and aggregate), event types, and custom types, +/// before building an `EventQL` parsing session. +pub struct SessionBuilder { + options: AnalysisOptions, +} + +impl SessionBuilder { + /// Declares a new function with the given name, arguments, and return type. + /// + /// This function adds a new entry to the session's default scope, allowing + /// the parser to recognize and type-check calls to this function. + /// + /// # Arguments + /// + /// * `name` - The name of the function. + /// * `args` - The arguments the function accepts, which can be converted into `FunArgs`. + /// * `result` - The return type of the function. + pub fn declare_func(self, name: &str, args: impl Into, result: Type) -> Self { + self.declare_func_when(true, name, args, result) + } + + /// Conditionally declares a new function with the given name, arguments, and return type. + /// + /// This function behaves like `declare_func` but only declares the function + /// if the `test` argument is `true`. This is useful for conditionally + /// including functions based on configuration or features. + /// + /// # Arguments + /// + /// * `test` - A boolean indicating whether to declare the function. + /// * `name` - The name of the function. + /// * `args` - The arguments the function accepts, which can be converted into `FunArgs`. + /// * `result` - The return type of the function. + pub fn declare_func_when( + mut self, + test: bool, + name: &str, + args: impl Into, + result: Type, + ) -> Self { + if test { + self.options.default_scope.entries.insert( + name, + Type::App { + args: args.into(), + result: Box::new(result), + aggregate: false, + }, + ); + } + + self + } + + /// Declares a new aggregate function with the given name, arguments, and return type. + /// + /// Similar to `declare_func`, but marks the function as an aggregate function. + /// Aggregate functions have specific rules for where they can be used in an EQL query. + /// + /// # Arguments + /// + /// * `name` - The name of the aggregate function. + /// * `args` - The arguments the aggregate function accepts. + /// * `result` - The return type of the aggregate function. + pub fn declare_agg_func(self, name: &str, args: impl Into, result: Type) -> Self { + self.declare_agg_func_when(true, name, args, result) + } + + /// Conditionally declares a new aggregate function. + /// + /// Behaves like `declare_agg_func` but only declares the function + /// if the `test` argument is `true`. + /// + /// # Arguments + /// + /// * `test` - A boolean indicating whether to declare the aggregate function. + /// * `name` - The name of the aggregate function. + /// * `args` - The arguments the aggregate function accepts. + /// * `result` - The return type of the aggregate function. + pub fn declare_agg_func_when( + mut self, + test: bool, + name: &str, + args: impl Into, + result: Type, + ) -> Self { + if test { + self.options.default_scope.entries.insert( + name, + Type::App { + args: args.into(), + result: Box::new(result), + aggregate: true, + }, + ); + } + + self + } + + /// Conditionally declares the expected type of event records. + /// + /// This type information is crucial for type-checking event properties + /// accessed in EQL queries (e.g., `e.id`, `e.data.value`). + /// The declaration only happens if `test` is `true`. + /// + /// # Arguments + /// + /// * `test` - A boolean indicating whether to declare the event type. + /// * `tpe` - The `Type` representing the structure of event records. + pub fn declare_event_type_when(mut self, test: bool, tpe: Type) -> Self { + if test { + self.options.event_type_info = tpe; + } + + self + } + + /// Declares the expected type of event records. + /// + /// This type information is crucial for type-checking event properties + /// accessed in EQL queries (e.g., `e.id`, `e.data.value`). + /// + /// # Arguments + /// + /// * `tpe` - The `Type` representing the structure of event records. + pub fn declare_event_type(mut self, tpe: Type) -> Self { + self.options.event_type_info = tpe; + self + } + + /// Conditionally declares a custom type that can be used in EQL queries. + /// + /// This allows the type-checker to recognize and validate custom types + /// that might be used in type conversions or record definitions. + /// The declaration only happens if `test` is `true`. + /// + /// # Arguments + /// + /// * `test` - A boolean indicating whether to declare the custom type. + /// * `name` - The name of the custom type. + pub fn declare_custom_type_when(mut self, test: bool, name: &str) -> Self { + if test { + self.options + .custom_types + .insert(Ascii::new(name.to_owned())); + } + + self + } + + /// Declares a custom type that can be used in EQL queries. + /// + /// This allows the type-checker to recognize and validate custom types + /// that might be used in type conversions or record definitions. + /// + /// # Arguments + /// + /// * `name` - The name of the custom type. + pub fn declare_custom_type(mut self, name: &str) -> Self { + self.options + .custom_types + .insert(Ascii::new(name.to_owned())); + self + } + + /// Includes the standard library of functions and event types in the session. + /// + /// This method pre-configures the `SessionBuilder` with a set of commonly + /// used functions (e.g., mathematical, string, date/time) and a default + /// event type definition. Calling this method is equivalent to calling + /// `declare_func` and `declare_agg_func` for all standard library functions, + /// and `declare_event_type` for the default event structure. + pub fn use_stdlib(self) -> Self { + self.declare_func("ABS", vec![Type::Number], Type::Number) + .declare_func("CEIL", vec![Type::Number], Type::Number) + .declare_func("FLOOR", vec![Type::Number], Type::Number) + .declare_func("ROUND", vec![Type::Number], Type::Number) + .declare_func("COS", vec![Type::Number], Type::Number) + .declare_func("EXP", vec![Type::Number], Type::Number) + .declare_func("POW", vec![Type::Number, Type::Number], Type::Number) + .declare_func("SQRT", vec![Type::Number], Type::Number) + .declare_func("RAND", vec![], Type::Number) + .declare_func("PI", vec![Type::Number], Type::Number) + .declare_func("LOWER", vec![Type::String], Type::String) + .declare_func("UPPER", vec![Type::String], Type::String) + .declare_func("TRIM", vec![Type::String], Type::String) + .declare_func("LTRIM", vec![Type::String], Type::String) + .declare_func("RTRIM", vec![Type::String], Type::String) + .declare_func("LEN", vec![Type::String], Type::Number) + .declare_func("INSTR", vec![Type::String], Type::Number) + .declare_func( + "SUBSTRING", + vec![Type::String, Type::Number, Type::Number], + Type::String, + ) + .declare_func( + "REPLACE", + vec![Type::String, Type::String, Type::String], + Type::String, + ) + .declare_func("STARTSWITH", vec![Type::String, Type::String], Type::Bool) + .declare_func("ENDSWITH", vec![Type::String, Type::String], Type::Bool) + .declare_func("NOW", vec![], Type::DateTime) + .declare_func("YEAR", vec![Type::Date], Type::Number) + .declare_func("MONTH", vec![Type::Date], Type::Number) + .declare_func("DAY", vec![Type::Date], Type::Number) + .declare_func("HOUR", vec![Type::Time], Type::Number) + .declare_func("MINUTE", vec![Type::Time], Type::Number) + .declare_func("SECOND", vec![Type::Time], Type::Number) + .declare_func("WEEKDAY", vec![Type::Date], Type::Number) + .declare_func( + "IF", + vec![Type::Bool, Type::Unspecified, Type::Unspecified], + Type::Unspecified, + ) + .declare_agg_func( + "COUNT", + FunArgs { + values: vec![Type::Bool], + needed: 0, + }, + Type::Number, + ) + .declare_agg_func("SUM", vec![Type::Number], Type::Number) + .declare_agg_func("AVG", vec![Type::Number], Type::Number) + .declare_agg_func("MIN", vec![Type::Number], Type::Number) + .declare_agg_func("MAX", vec![Type::Number], Type::Number) + .declare_agg_func("MEDIAN", vec![Type::Number], Type::Number) + .declare_agg_func("STDDEV", vec![Type::Number], Type::Number) + .declare_agg_func("VARIANCE", vec![Type::Number], Type::Number) + .declare_agg_func("UNIQUE", vec![Type::Unspecified], Type::Unspecified) + .declare_event_type(Type::Record(FxHashMap::from_iter([ + ("specversion".to_owned(), Type::String), + ("id".to_owned(), Type::String), + ("time".to_owned(), Type::DateTime), + ("source".to_owned(), Type::String), + ("subject".to_owned(), Type::Subject), + ("type".to_owned(), Type::String), + ("datacontenttype".to_owned(), Type::String), + ("data".to_owned(), Type::Unspecified), + ("predecessorhash".to_owned(), Type::String), + ("hash".to_owned(), Type::String), + ("traceparent".to_owned(), Type::String), + ("tracestate".to_owned(), Type::String), + ("signature".to_owned(), Type::String), + ]))) + } + + /// Builds the `Session` object with the configured analysis options. + /// + /// This consumes the `SessionBuilder` and returns a `Session` instance + /// ready for tokenizing, parsing, and analyzing EventQL queries. + pub fn build(self) -> Session { + Session { + arena: ExprArena::default(), + options: self.options, + } + } +} + +impl Default for SessionBuilder { + fn default() -> Self { + Self { + options: AnalysisOptions::empty(), + } + } +} + +/// `Session` is the main entry point for parsing and analyzing EventQL queries. /// -/// // Handle errors -/// match parse_query(&mut arena, "FROM e IN events WHERE") { -/// Ok(_) => println!("Parsed successfully"), -/// Err(e) => println!("Parse error: {}", e), -/// } -/// ``` -pub fn parse_query(arena: &mut ExprArena, input: &str) -> Result> { - let tokens = tokenize(input)?; - Ok(parse(arena, tokens.as_slice())?) +/// It holds the necessary context, such as the expression arena and analysis options, +/// to perform lexical analysis, parsing, and static analysis of EQL query strings. +pub struct Session { + arena: ExprArena, + options: AnalysisOptions, +} + +impl Session { + /// Creates a new `SessionBuilder` for configuring and building a `Session`. + /// + /// This is the recommended way to create a `Session` instance, allowing + /// for customization of functions, event types, and custom types. + /// + /// # Returns + /// + /// A new `SessionBuilder` instance. + pub fn builder() -> SessionBuilder { + SessionBuilder::default() + } + + /// Tokenize an EventQL query string. + /// + /// This function performs lexical analysis on the input string, converting it + /// into a sequence of tokens. Each token includes position information (line + /// and column numbers) for error reporting. + /// # Recognized Tokens + /// + /// - **Identifiers**: Alphanumeric names starting with a letter (e.g., `events`, `e`) + /// - **Keywords**: Case-insensitive SQL-like keywords detected by the parser + /// - **Numbers**: Floating-point literals (e.g., `42`, `3.14`) + /// - **Strings**: Double-quoted string literals (e.g., `"hello"`) + /// - **Operators**: Arithmetic (`+`, `-`, `*`, `/`), comparison (`==`, `!=`, `<`, `<=`, `>`, `>=`), logical (`AND`, `OR`, `XOR`, `NOT`) + /// - **Symbols**: Structural characters (`(`, `)`, `[`, `]`, `{`, `}`, `.`, `,`, `:`) + pub fn tokenize<'a>(&self, input: &'a str) -> Result>> { + let tokens = tokenize(input)?; + Ok(tokens) + } + + /// Parse an EventQL query string into an abstract syntax tree. + /// + /// This is the main entry point for parsing EventQL queries. It performs both + /// lexical analysis (tokenization) and syntactic analysis (parsing) in a single call. + /// # Examples + /// + /// ``` + /// use eventql_parser::Session; + /// + /// // Parse a simple query + /// let mut session = Session::builder().use_stdlib().build(); + /// let query = session.parse("FROM e IN events WHERE e.id == \"1\" PROJECT INTO e").unwrap(); + /// assert!(query.predicate.is_some()); + /// ``` + pub fn parse(&mut self, input: &str) -> Result> { + let tokens = self.tokenize(input)?; + Ok(parse(&mut self.arena, tokens.as_slice())?) + } + + /// Performs static analysis on an EventQL query. + /// + /// This function takes a raw (untyped) query and performs type checking and + /// variable scoping analysis. It validates that: + /// - All variables are properly declared + /// - Types match expected types in expressions and operations + /// - Field accesses are valid for their record types + /// - Function calls have the correct argument types + /// - Aggregate functions are only used in PROJECT INTO clauses + /// - Aggregate functions are not mixed with source-bound fields in projections + /// - Aggregate function arguments are source-bound fields (not constants or function results) + /// - Record literals are non-empty in projection contexts + /// + /// # Arguments + /// + /// * `options` - Configuration containing type information and default scope + /// * `query` - The raw query to analyze + /// + /// # Returns + /// + /// Returns a typed query on success, or an `AnalysisError` if type checking fails. + pub fn run_static_analysis(&self, query: Query) -> Result> { + Ok(static_analysis(&self.arena, &self.options, query)?) + } } diff --git a/src/parser.rs b/src/parser.rs index 2b31781..7d181a4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -38,12 +38,10 @@ impl<'a> Parser<'a> { /// # Example /// /// ```rust - /// use eventql_parser::prelude::{tokenize, Parser}; - /// use eventql_parser::arena::ExprArena; + /// use eventql_parser::Session; /// - /// let mut arena = ExprArena::default(); - /// let tokens = tokenize("1 + 2").unwrap(); - /// let parser = Parser::new(&mut arena, tokens.as_slice()); + /// let session = Session::builder().build(); + /// let tokens = session.tokenize("1 + 2").unwrap(); /// ``` pub fn new(arena: &'a mut ExprArena, input: &'a [Token<'a>]) -> Self { Self { @@ -214,12 +212,10 @@ impl<'a> Parser<'a> { /// # Example /// /// ```rust - /// use eventql_parser::prelude::{tokenize, Parser}; - /// use eventql_parser::arena::ExprArena; + /// use eventql_parser::Session; /// - /// let mut arena = ExprArena::default(); - /// let tokens = tokenize("NOW()").unwrap(); - /// let expr = Parser::new(&mut arena, tokens.as_slice()).parse_expr().unwrap(); + /// let session = Session::builder().build(); + /// let tokens = session.tokenize("NOW()").unwrap(); /// ``` pub fn parse_expr(&mut self) -> ParseResult { let token = self.peek(); @@ -567,7 +563,10 @@ fn binding_pow(op: Operator) -> (u64, u64) { /// 3. Additive (`+`, `-`) /// 4. Comparison (`<`, `<=`, `>`, `>=`, `==`, `!=`) /// 5. Logical (`AND`, `OR`, `XOR`) -pub fn parse<'a>(arena: &'a mut ExprArena, input: &'a [Token<'a>]) -> ParseResult> { +pub(crate) fn parse<'a>( + arena: &'a mut ExprArena, + input: &'a [Token<'a>], +) -> ParseResult> { let mut parser = Parser::new(arena, input); parser.parse_query() diff --git a/src/tests/analysis.rs b/src/tests/analysis.rs index 9ea7ad4..9539ebd 100644 --- a/src/tests/analysis.rs +++ b/src/tests/analysis.rs @@ -1,238 +1,218 @@ use crate::{ - Type, - arena::ExprArena, - lexer::tokenize, - parse_query, + Session, Type, parser::Parser, - prelude::{Analysis, AnalysisContext, AnalysisOptions}, + prelude::{Analysis, AnalysisContext}, }; #[test] fn test_infer_wrong_where_clause_1() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/infer_wrong_where_clause_1.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/infer_wrong_where_clause_1.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_infer_wrong_where_clause_2() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/infer_wrong_where_clause_2.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/infer_wrong_where_clause_2.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_rename_duplicate_variable_names() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/rename_duplicate_variable_names.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/rename_duplicate_variable_names.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_rename_non_existing_variable() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/rename_non_existing_variable.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/rename_non_existing_variable.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_rename_subquery() { - let mut arena = ExprArena::default(); - let query = parse_query(&mut arena, include_str!("./resources/rename_subquery.eql")); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/rename_subquery.eql")); insta::with_settings!({sort_maps => true}, { insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })) }); } #[test] fn test_analyze_valid_contains() { - let mut arena = ExprArena::default(); - let query = parse_query(&mut arena, include_str!("./resources/valid_contains.eql")); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/valid_contains.eql")); insta::with_settings!({sort_maps => true}, { insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); }) } #[test] fn test_analyze_invalid_type_contains() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/invalid_type_contains.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/invalid_type_contains.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_valid_type_conversion() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/valid_type_conversion.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/valid_type_conversion.eql")); insta::assert_yaml_snapshot!(query.map(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_invalid_type_conversion_custom_type() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/type_conversion_custom_type.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/type_conversion_custom_type.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_valid_type_conversion_custom_type() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/type_conversion_custom_type.eql"), - ); + let mut session = Session::builder() + .use_stdlib() + .declare_custom_type("Foobar") + .build(); + let query = session.parse(include_str!("./resources/type_conversion_custom_type.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis( - &arena, - &AnalysisOptions::default().add_custom_type("Foobar"), - ) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_valid_type_conversion_weird_case() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/valid_type_conversion-weird-case.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/valid_type_conversion-weird-case.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_prevent_using_aggregate_with_source_based_props() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/aggregate_with_sourced_bases_props.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/aggregate_with_sourced_bases_props.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_valid_agg_usage() { - let mut arena = ExprArena::default(); - let query = parse_query(&mut arena, include_str!("./resources/valid_agg_usage.eql")); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/valid_agg_usage.eql")); insta::with_settings!({sort_maps => true}, { insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })) }); } #[test] fn test_analyze_reject_agg_in_predicate() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/reject_agg_in_predicate.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/reject_agg_in_predicate.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_agg_must_use_source_bound() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/agg_must_use_source_bound.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/agg_must_use_source_bound.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_optional_param_func() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/optional_param_func.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/optional_param_func.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_typecheck_datetime_contravariance_1() { - let mut arena = ExprArena::default(); - let tokens = tokenize("e.time").unwrap(); - let expr = Parser::new(&mut arena, tokens.as_slice()) + let mut session = Session::builder().use_stdlib().build(); + let tokens = session.tokenize("e.time").unwrap(); + let expr = Parser::new(&mut session.arena, tokens.as_slice()) .parse_expr() .unwrap(); - let options = &AnalysisOptions::default(); - let mut analysis = Analysis::new(&arena, &options); + + let mut analysis = Analysis::new(&session.arena, &session.options); analysis .scope_mut() .entries - .insert("e".to_string(), options.event_type_info.clone()); + .insert("e".to_string(), session.options.event_type_info.clone()); // `e.time` is a `Type::DateTime` but it will typecheck if a `Type::Date` is expected insta::assert_yaml_snapshot!(analysis.analyze_expr( @@ -244,13 +224,13 @@ fn test_typecheck_datetime_contravariance_1() { #[test] fn test_typecheck_datetime_contravariance_2() { - let mut arena = ExprArena::default(); - let tokens = tokenize("NOW()").unwrap(); - let expr = Parser::new(&mut arena, tokens.as_slice()) + let mut session = Session::builder().use_stdlib().build(); + let tokens = session.tokenize("NOW()").unwrap(); + let expr = Parser::new(&mut session.arena, tokens.as_slice()) .parse_expr() .unwrap(); - let options = &AnalysisOptions::default(); - let mut analysis = Analysis::new(&arena, &options); + + let mut analysis = Analysis::new(&session.arena, &session.options); // `NOW()` is a `Type::DateTime` but it will typecheck if a `Type::Time` is expected insta::assert_yaml_snapshot!(analysis.analyze_expr( @@ -262,13 +242,13 @@ fn test_typecheck_datetime_contravariance_2() { #[test] fn test_typecheck_datetime_contravariance_3() { - let mut arena = ExprArena::default(); - let tokens = tokenize("YEAR(NOW())").unwrap(); - let expr = Parser::new(&mut arena, tokens.as_slice()) + let mut session = Session::builder().use_stdlib().build(); + let tokens = session.tokenize("YEAR(NOW())").unwrap(); + let expr = Parser::new(&mut session.arena, tokens.as_slice()) .parse_expr() .unwrap(); - let options = &AnalysisOptions::default(); - let mut analysis = Analysis::new(&arena, &options); + + let mut analysis = Analysis::new(&session.arena, &session.options); insta::assert_yaml_snapshot!(analysis.analyze_expr( &mut AnalysisContext::default(), @@ -279,13 +259,13 @@ fn test_typecheck_datetime_contravariance_3() { #[test] fn test_typecheck_datetime_contravariance_4() { - let mut arena = ExprArena::default(); - let tokens = tokenize("HOUR(NOW())").unwrap(); - let expr = Parser::new(&mut arena, tokens.as_slice()) + let mut session = Session::builder().use_stdlib().build(); + let tokens = session.tokenize("HOUR(NOW())").unwrap(); + let expr = Parser::new(&mut session.arena, tokens.as_slice()) .parse_expr() .unwrap(); - let options = &AnalysisOptions::default(); - let mut analysis = Analysis::new(&arena, &options); + + let mut analysis = Analysis::new(&session.arena, &session.options); insta::assert_yaml_snapshot!(analysis.analyze_expr( &mut AnalysisContext::default(), @@ -296,178 +276,162 @@ fn test_typecheck_datetime_contravariance_4() { #[test] fn test_analyze_allow_regular_property_project_into() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/allow_regular_property_project_into.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/allow_regular_property_project_into.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_undeclared_variable_in_project_into_clause() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/undeclared_variable_in_project_into_clause.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/undeclared_variable_in_project_into_clause.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_lowercase_function() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/lowercase_function.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/lowercase_function.eql")); insta::with_settings!({sort_maps => true}, { insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })) }); } #[test] fn test_analyze_project_agg_value() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/project_agg_value.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/project_agg_value.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_reject_constant_expr_in_project_into_clause() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/reject_constant_expr.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/reject_constant_expr.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_allow_constant_agg_func() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/allow_constant_agg_func.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/allow_constant_agg_func.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_reject_group_by_with_order_by_no_agg() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/reject_group_by_with_order_by_no_agg.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/reject_group_by_with_order_by_no_agg.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_accept_group_by_with_order_by_with_agg() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/accept_group_by_with_order_by_with_agg.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/accept_group_by_with_order_by_with_agg.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_reject_group_by_no_agg() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/reject_group_by_no_agg.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/reject_group_by_no_agg.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_reject_group_by_no_agg_in_rec() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/reject_group_by_no_agg_in_rec.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!( + "./resources/reject_group_by_no_agg_in_rec.eql" + )); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_analyze_accept_group_by_with_agg_rec() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/accept_group_by_with_agg_rec.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/accept_group_by_with_agg_rec.eql")); insta::with_settings!({sort_maps => true}, { insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })) }); } #[test] fn test_reject_invalid_having_clause() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/reject_invalid_having_clause.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/reject_invalid_having_clause.eql")); insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })); } #[test] fn test_accept_valid_having_clause() { - let mut arena = ExprArena::default(); - let query = parse_query( - &mut arena, - include_str!("./resources/valid_having_clause.eql"), - ); + let mut session = Session::builder().use_stdlib().build(); + let query = session.parse(include_str!("./resources/valid_having_clause.eql")); insta::with_settings!({sort_maps => true}, { insta::assert_yaml_snapshot!(query.and_then(|q| { - q.run_static_analysis(&arena, &Default::default()) - .map(|q| q.view(&arena)) + session + .run_static_analysis(q) + .map(|q| q.view(&session.arena)) })) }); } diff --git a/src/tests/lexer.rs b/src/tests/lexer.rs index 016e39a..2a6ca58 100644 --- a/src/tests/lexer.rs +++ b/src/tests/lexer.rs @@ -1,16 +1,19 @@ -use crate::lexer::tokenize; +use crate::Session; #[test] fn test_lexer_all_kind() { - insta::assert_yaml_snapshot!(tokenize("foo != 123(]{.:")); + let session = Session::builder().build(); + insta::assert_yaml_snapshot!(session.tokenize("foo != 123(]{.:")); } #[test] fn test_lexer_negative_number() { - insta::assert_yaml_snapshot!(tokenize("-123.456")); + let session = Session::builder().build(); + insta::assert_yaml_snapshot!(session.tokenize("-123.456")); } #[test] fn test_lexer_comment() { - insta::assert_yaml_snapshot!(tokenize("// useless comment\n ")); + let session = Session::builder().build(); + insta::assert_yaml_snapshot!(session.tokenize("// useless comment\n ")); } diff --git a/src/tests/parser.rs b/src/tests/parser.rs index b516d4b..c2831ab 100644 --- a/src/tests/parser.rs +++ b/src/tests/parser.rs @@ -1,113 +1,107 @@ -use crate::arena::ExprArena; -use crate::lexer::tokenize; -use crate::parser::parse; +use crate::Session; #[test] fn test_parse_from_events_nested_data() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/from_events_nested_data.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/from_events_nested_data.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parse_from_events_using_subquery() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/from_events_using_subquery.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/from_events_using_subquery.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parse_from_events_where_subject_project_record_with_count() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!( + let mut session = Session::builder().build(); + let query = session.parse(include_str!( "./resources/from_events_where_subject_project_record_with_count.eql" - )) - .unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + )); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parse_from_events_with_top_identity_projection() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!( + let mut session = Session::builder().build(); + let query = session.parse(include_str!( "./resources/from_events_with_top_identity_projection.eql" - )) - .unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + )); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parse_from_events_with_type_to_project_record() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!( + let mut session = Session::builder().build(); + let query = session.parse(include_str!( "./resources/from_events_with_type_to_project_record.eql" - )) - .unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + )); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parse_binary_op() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/parser_binary_op.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/parser_binary_op.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_unhinged_unary_op() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/parser_unhinged_unary_op.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/parser_unhinged_unary_op.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_from_events_with_group_by_and_having() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!( + let mut session = Session::builder().build(); + let query = session.parse(include_str!( "./resources/from_events_with_group_by_and_having.eql" - )) - .unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + )); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_from_events_with_distinct() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/from_events_with_distinct.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/from_events_with_distinct.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_valid_contains() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/valid_contains.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/valid_contains.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_valid_type_conversion() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/valid_type_conversion.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/valid_type_conversion.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_invalid_type_conversion_expr() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/invalid_type_conversion_expr.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/invalid_type_conversion_expr.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_with_comment() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/with_comment.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/with_comment.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } #[test] fn test_parser_order_by_no_ordering() { - let mut arena = ExprArena::default(); - let tokens = tokenize(include_str!("./resources/query_order_by_no_ordering.eql")).unwrap(); - insta::assert_yaml_snapshot!(parse(&mut arena, tokens.as_slice()).map(|q| q.view(&arena))); + let mut session = Session::builder().build(); + let query = session.parse(include_str!("./resources/query_order_by_no_ordering.eql")); + insta::assert_yaml_snapshot!(query.map(|q| q.view(&session.arena))); } diff --git a/src/tests/snapshots/eventql_parser__tests__parser__parser_invalid_type_conversion_expr.snap b/src/tests/snapshots/eventql_parser__tests__parser__parser_invalid_type_conversion_expr.snap index 112ebf3..662e144 100644 --- a/src/tests/snapshots/eventql_parser__tests__parser__parser_invalid_type_conversion_expr.snap +++ b/src/tests/snapshots/eventql_parser__tests__parser__parser_invalid_type_conversion_expr.snap @@ -1,8 +1,9 @@ --- source: src/tests/parser.rs -expression: parse(tokens.as_slice()) +expression: query.map(|q| q.view(&session.arena)) --- Err: - ExpectedType: - - 3 - - 37 + Parser: + ExpectedType: + - 3 + - 37