@@ -281,62 +281,62 @@ impl Default for AnalysisOptions {
281281 "NOW" . to_owned ( ) ,
282282 Type :: App {
283283 args : vec ! [ ] . into ( ) ,
284- result : Box :: new ( Type :: String ) ,
284+ result : Box :: new ( Type :: DateTime ) ,
285285 aggregate : false ,
286286 } ,
287287 ) ,
288288 (
289289 "YEAR" . to_owned ( ) ,
290290 Type :: App {
291- args : vec ! [ Type :: String ] . into ( ) ,
291+ args : vec ! [ Type :: Date ] . into ( ) ,
292292 result : Box :: new ( Type :: Number ) ,
293293 aggregate : false ,
294294 } ,
295295 ) ,
296296 (
297297 "MONTH" . to_owned ( ) ,
298298 Type :: App {
299- args : vec ! [ Type :: String ] . into ( ) ,
299+ args : vec ! [ Type :: Date ] . into ( ) ,
300300 result : Box :: new ( Type :: Number ) ,
301301 aggregate : false ,
302302 } ,
303303 ) ,
304304 (
305305 "DAY" . to_owned ( ) ,
306306 Type :: App {
307- args : vec ! [ Type :: String ] . into ( ) ,
307+ args : vec ! [ Type :: Date ] . into ( ) ,
308308 result : Box :: new ( Type :: Number ) ,
309309 aggregate : false ,
310310 } ,
311311 ) ,
312312 (
313313 "HOUR" . to_owned ( ) ,
314314 Type :: App {
315- args : vec ! [ Type :: String ] . into ( ) ,
315+ args : vec ! [ Type :: Time ] . into ( ) ,
316316 result : Box :: new ( Type :: Number ) ,
317317 aggregate : false ,
318318 } ,
319319 ) ,
320320 (
321321 "MINUTE" . to_owned ( ) ,
322322 Type :: App {
323- args : vec ! [ Type :: String ] . into ( ) ,
323+ args : vec ! [ Type :: Time ] . into ( ) ,
324324 result : Box :: new ( Type :: Number ) ,
325325 aggregate : false ,
326326 } ,
327327 ) ,
328328 (
329329 "SECOND" . to_owned ( ) ,
330330 Type :: App {
331- args : vec ! [ Type :: String ] . into ( ) ,
331+ args : vec ! [ Type :: Time ] . into ( ) ,
332332 result : Box :: new ( Type :: Number ) ,
333333 aggregate : false ,
334334 } ,
335335 ) ,
336336 (
337337 "WEEKDAY" . to_owned ( ) ,
338338 Type :: App {
339- args : vec ! [ Type :: String ] . into ( ) ,
339+ args : vec ! [ Type :: Date ] . into ( ) ,
340340 result : Box :: new ( Type :: Number ) ,
341341 aggregate : false ,
342342 } ,
@@ -429,7 +429,7 @@ impl Default for AnalysisOptions {
429429 event_type_info : Type :: Record ( BTreeMap :: from ( [
430430 ( "specversion" . to_owned ( ) , Type :: String ) ,
431431 ( "id" . to_owned ( ) , Type :: String ) ,
432- ( "time" . to_owned ( ) , Type :: String ) ,
432+ ( "time" . to_owned ( ) , Type :: DateTime ) ,
433433 ( "source" . to_owned ( ) , Type :: String ) ,
434434 ( "subject" . to_owned ( ) , Type :: Subject ) ,
435435 ( "type" . to_owned ( ) , Type :: String ) ,
@@ -500,26 +500,65 @@ struct CheckContext {
500500 use_source_based : bool ,
501501}
502502
503+ /// Context for controlling analysis behavior.
504+ ///
505+ /// This struct allows you to configure how expressions are analyzed,
506+ /// such as whether aggregate functions are allowed in the current context.
503507#[ derive( Default ) ]
504- struct AnalysisContext {
505- allow_agg_func : bool ,
508+ pub struct AnalysisContext {
509+ /// Controls whether aggregate functions (like COUNT, SUM, AVG) are allowed
510+ /// in the current analysis context.
511+ ///
512+ /// Set to `true` to allow aggregate functions, `false` to reject them.
513+ /// Defaults to `false`.
514+ pub allow_agg_func : bool ,
506515}
507516
508- struct Analysis < ' a > {
517+ /// A type checker and static analyzer for EventQL expressions.
518+ ///
519+ /// This struct maintains the analysis state including scopes and type information.
520+ /// It can be used to perform type checking on individual expressions or entire queries.
521+ pub struct Analysis < ' a > {
522+ /// The analysis options containing type information for functions and event types.
509523 options : & ' a AnalysisOptions ,
524+ /// Stack of previous scopes for nested scope handling.
510525 prev_scopes : Vec < Scope > ,
526+ /// The current scope containing variable bindings and their types.
511527 scope : Scope ,
512528}
513529
514530impl < ' a > Analysis < ' a > {
515- fn new ( options : & ' a AnalysisOptions ) -> Self {
531+ /// Creates a new analysis instance with the given options.
532+ pub fn new ( options : & ' a AnalysisOptions ) -> Self {
516533 Self {
517534 options,
518535 prev_scopes : Default :: default ( ) ,
519536 scope : Scope :: default ( ) ,
520537 }
521538 }
522539
540+ /// Returns a reference to the current scope.
541+ ///
542+ /// The scope contains variable bindings and their types for the current
543+ /// analysis context. Note that this only includes local variable bindings
544+ /// and does not include global definitions such as built-in functions
545+ /// (e.g., `COUNT`, `NOW`) or event type information, which are stored
546+ /// in the `AnalysisOptions`.
547+ pub fn scope ( & self ) -> & Scope {
548+ & self . scope
549+ }
550+
551+ /// Returns a mutable reference to the current scope.
552+ ///
553+ /// This allows you to modify the scope by adding or removing variable bindings.
554+ /// This is useful when you need to set up custom type environments before
555+ /// analyzing expressions. Note that this only provides access to local variable
556+ /// bindings; global definitions like built-in functions are managed through
557+ /// `AnalysisOptions` and cannot be modified via the scope.
558+ pub fn scope_mut ( & mut self ) -> & mut Scope {
559+ & mut self . scope
560+ }
561+
523562 fn enter_scope ( & mut self ) {
524563 if self . scope . is_empty ( ) {
525564 return ;
@@ -537,7 +576,35 @@ impl<'a> Analysis<'a> {
537576 }
538577 }
539578
540- fn analyze_query ( & mut self , query : Query < Raw > ) -> AnalysisResult < Query < Typed > > {
579+ /// Performs static analysis on a parsed query.
580+ ///
581+ /// This method analyzes an entire EventQL query, performing type checking on all
582+ /// clauses including sources, predicates, group by, order by, and projections.
583+ /// It returns a typed version of the query with type information attached.
584+ ///
585+ /// # Arguments
586+ ///
587+ /// * `query` - A parsed query in its raw (untyped) form
588+ ///
589+ /// # Returns
590+ ///
591+ /// Returns a typed query with all type information resolved, or an error if
592+ /// type checking fails for any part of the query.
593+ ///
594+ /// # Example
595+ ///
596+ /// ```rust
597+ /// use eventql_parser::{parse_query, prelude::{Analysis, AnalysisOptions}};
598+ ///
599+ /// let query = parse_query("FROM e IN events WHERE [1,2,3] CONTAINS e.data.price PROJECT INTO e").unwrap();
600+ ///
601+ /// let options = AnalysisOptions::default();
602+ /// let mut analysis = Analysis::new(&options);
603+ ///
604+ /// let typed_query = analysis.analyze_query(query);
605+ /// assert!(typed_query.is_ok());
606+ /// ```
607+ pub fn analyze_query ( & mut self , query : Query < Raw > ) -> AnalysisResult < Query < Typed > > {
541608 self . enter_scope ( ) ;
542609
543610 let mut sources = Vec :: with_capacity ( query. sources . len ( ) ) ;
@@ -892,7 +959,36 @@ impl<'a> Analysis<'a> {
892959 }
893960 }
894961
895- fn analyze_expr (
962+ /// Analyzes an expression and checks it against an expected type.
963+ ///
964+ /// This method performs type checking on an expression, verifying that all operations
965+ /// are type-safe and that the expression's type is compatible with the expected type.
966+ ///
967+ /// # Arguments
968+ ///
969+ /// * `ctx` - The analysis context controlling analysis behavior
970+ /// * `expr` - The expression to analyze
971+ /// * `expect` - The expected type of the expression
972+ ///
973+ /// # Returns
974+ ///
975+ /// Returns the actual type of the expression after checking compatibility with the expected type,
976+ /// or an error if type checking fails.
977+ ///
978+ /// # Example
979+ ///
980+ /// ```rust
981+ /// use eventql_parser::prelude::{tokenize, Parser, Analysis, AnalysisContext, AnalysisOptions, Type};
982+ ///
983+ /// let tokens = tokenize("1 + 2").unwrap();
984+ /// let expr = Parser::new(tokens.as_slice()).parse_expr().unwrap();
985+ /// let options = AnalysisOptions::default();
986+ /// let mut analysis = Analysis::new(&options);
987+ ///
988+ /// let result = analysis.analyze_expr(&AnalysisContext::default(), &expr, Type::Number);
989+ /// assert!(result.is_ok());
990+ /// ```
991+ pub fn analyze_expr (
896992 & mut self ,
897993 ctx : & AnalysisContext ,
898994 expr : & Expr ,
0 commit comments