roan_ast/ast/
statements.rs

1use crate::{ast::expr::Expr, GetSpan, Token};
2use roan_error::TextSpan;
3use std::fmt::{Debug, Formatter};
4
5/// Represents a statement in the AST.
6///
7/// A statement can be an expression, a declaration, a control flow construct, etc.
8#[derive(Clone, Debug, PartialEq)]
9pub enum Stmt {
10    /// An expression statement.
11    Expr(Box<Expr>),
12    /// A `use` statement for importing modules or items.
13    Use(Use),
14    /// A block of statements enclosed in braces.
15    Block(Block),
16    /// An `if` statement with optional `else if` and `else` blocks.
17    If(If),
18    /// A `return` statement to exit a function.
19    Return(Return),
20    /// A function declaration.
21    Fn(Fn),
22    /// A variable declaration.
23    Let(Let),
24    /// A `throw` statement for exception handling.
25    Throw(Throw),
26    /// A `try` statement for handling errors.
27    Try(Try),
28    /// A `break` statement to exit a loop.
29    Break(Token),
30    /// A `continue` statement to skip the current iteration of a loop.
31    Continue(Token),
32    /// A `loop` statement to create an infinite loop.
33    Loop(Loop),
34    /// A `while` statement to create a loop with a condition.
35    While(While),
36    /// A struct definition.
37    Struct(Struct),
38    /// A trait definition.
39    TraitDef(TraitDef),
40    /// A struct implementation.
41    StructImpl(StructImpl),
42    /// A trait implementation.
43    TraitImpl(TraitImpl),
44    /// A const statement
45    Const(Const),
46}
47
48#[derive(Clone, Debug, PartialEq)]
49pub struct Const {
50    pub expr: Box<Expr>,
51    pub ident: Token,
52    pub public: bool,
53}
54
55#[derive(Clone, Debug, PartialEq)]
56pub struct Struct {
57    pub struct_token: Token,
58    pub name: Token,
59    pub fields: Vec<StructField>,
60    pub public: bool,
61    pub impls: Vec<StructImpl>,
62    pub trait_impls: Vec<TraitImpl>,
63}
64
65#[derive(Clone, Debug, PartialEq)]
66pub struct StructField {
67    pub ident: Token,
68    pub type_annotation: TypeAnnotation,
69}
70
71#[derive(Clone, Debug, PartialEq)]
72pub struct TraitDef {
73    pub trait_token: Token,
74    pub name: Token,
75    pub methods: Vec<Fn>,
76    pub public: bool,
77}
78
79#[derive(Clone, Debug, PartialEq)]
80pub struct StructImpl {
81    pub impl_token: Token,
82    pub struct_name: Token,
83    pub methods: Vec<Fn>,
84}
85
86#[derive(Clone, Debug, PartialEq)]
87pub struct TraitImpl {
88    pub impl_token: Token,
89    pub trait_name: Token,
90    pub for_token: Token,
91    pub struct_name: Token,
92    pub methods: Vec<Fn>,
93}
94
95#[derive(Clone, Debug, PartialEq)]
96pub struct Loop {
97    pub loop_token: Token,
98    pub block: Block,
99}
100
101#[derive(Clone, Debug, PartialEq)]
102pub struct While {
103    pub while_token: Token,
104    pub condition: Box<Expr>,
105    pub block: Block,
106}
107
108/// Represents a `throw` statement in the AST.
109///
110/// The `throw` statement is used to raise an exception with a specified value.
111#[derive(Clone, Debug, PartialEq)]
112pub struct Throw {
113    /// The expression representing the value to be thrown.
114    pub value: Box<Expr>,
115    /// The token corresponding to the `throw` keyword in the source code.
116    pub token: Token,
117}
118
119/// Represents a `try` statement in the AST.
120///
121/// The `try` statement is used for error handling, allowing execution of a block of code
122/// and catching any errors that occur.
123#[derive(Clone, Debug, PartialEq)]
124pub struct Try {
125    /// The token corresponding to the `try` keyword in the source code.
126    pub try_token: Token,
127    /// The block of code to execute within the `try` statement.
128    pub try_block: Block,
129    /// The identifier token for the caught error.
130    pub error_ident: Token,
131    /// The block of code to execute if an error is caught.
132    pub catch_block: Block,
133}
134
135/// Represents a variable declaration (`let` statement) in the AST.
136///
137/// A `let` statement declares a new variable with an optional type annotation and initializer.
138#[derive(Clone, Debug, PartialEq)]
139pub struct Let {
140    /// The token representing the identifier (variable name).
141    pub ident: Token,
142    /// The expression used to initialize the variable.
143    pub initializer: Box<Expr>,
144    /// An optional type annotation specifying the type of the variable.
145    pub type_annotation: Option<TypeAnnotation>,
146}
147
148impl From<Expr> for Stmt {
149    /// Converts an `Expr` into a `Stmt::Expr`.
150    ///
151    /// # Arguments
152    ///
153    /// * `expr` - The expression to convert into a statement.
154    ///
155    /// # Returns
156    ///
157    /// A `Stmt::Expr` variant containing the provided expression.
158    fn from(expr: Expr) -> Self {
159        Stmt::Expr(Box::new(expr))
160    }
161}
162
163impl Stmt {
164    pub fn into_function(self) -> Fn {
165        match self {
166            Stmt::Fn(f) => f,
167            _ => panic!("Expected function"),
168        }
169    }
170
171    /// Creates a new `Loop` statement.
172    ///
173    /// # Arguments
174    /// * `loop_token` - The token representing the `loop` keyword.
175    /// * `block` - The block of code to execute within the loop.
176    ///
177    /// # Returns
178    /// A `Stmt::Loop` variant containing the provided components.
179    pub fn new_loop(loop_token: Token, block: Block) -> Self {
180        Stmt::Loop(Loop { loop_token, block })
181    }
182
183    /// Creates a new `While` statement.
184    ///
185    /// # Arguments
186    /// * `while_token` - The token representing the `while` keyword.
187    /// * `condition` - The expression to evaluate as the loop condition.
188    /// * `block` - The block of code to execute within the loop.
189    ///
190    /// # Returns
191    /// A `Stmt::While` variant containing the provided components.
192    pub fn new_while(while_token: Token, condition: Expr, block: Block) -> Self {
193        Stmt::While(While {
194            while_token,
195            condition: Box::new(condition),
196            block,
197        })
198    }
199
200    /// Creates a new `Break` statement.
201    ///
202    /// # Arguments
203    /// * `break_token` - The token representing the `break` keyword.
204    ///
205    /// # Returns
206    /// A `Stmt::Break` variant containing the provided token.
207    pub fn new_break(break_token: Token) -> Self {
208        Stmt::Break(break_token)
209    }
210
211    /// Creates a new `Continue` statement.
212    ///
213    /// # Arguments
214    /// * `continue_token` - The token representing the `continue` keyword.
215    ///
216    /// # Returns
217    /// A `Stmt::Continue` variant containing the provided token.
218    pub fn new_continue(continue_token: Token) -> Self {
219        Stmt::Continue(continue_token)
220    }
221
222    /// Creates a new `Try` statement.
223    ///
224    /// # Arguments
225    ///
226    /// * `try_token` - The token for the `try` keyword.
227    /// * `try_block` - The block of code to execute within the `try`.
228    /// * `error_ident` - The identifier token for the caught error.
229    /// * `catch_block` - The block of code to execute if an error is caught.
230    ///
231    /// # Returns
232    ///
233    /// A `Stmt::Try` variant containing the provided components.
234    pub fn new_try(
235        try_token: Token,
236        try_block: Block,
237        error_ident: Token,
238        catch_block: Block,
239    ) -> Self {
240        Stmt::Try(Try {
241            try_token,
242            try_block,
243            error_ident,
244            catch_block,
245        })
246    }
247
248    /// Creates a new `Throw` statement.
249    ///
250    /// # Arguments
251    ///
252    /// * `token` - The token representing the `throw` keyword.
253    /// * `value` - The expression to be thrown.
254    ///
255    /// # Returns
256    ///
257    /// A `Stmt::Throw` variant containing the provided value and token.
258    pub fn new_throw(token: Token, value: Expr) -> Self {
259        Stmt::Throw(Throw {
260            value: Box::new(value),
261            token,
262        })
263    }
264
265    /// Creates a new function (`Fn`) statement.
266    ///
267    /// # Arguments
268    ///
269    /// * `fn_token` - The token representing the `fn` keyword.
270    /// * `name` - The name of the function.
271    /// * `params` - A vector of function parameters.
272    /// * `body` - The block of code representing the function body.
273    /// * `public` - A boolean indicating if the function is public.
274    /// * `return_type` - An optional return type annotation.
275    /// * `is_static` - A boolean indicating if the function is static.
276    ///
277    /// # Returns
278    ///
279    /// A `Stmt::Fn` variant containing the provided function details.
280    pub fn new_fn(
281        fn_token: Token,
282        name: String,
283        params: Vec<FnParam>,
284        body: Block,
285        public: bool,
286        return_type: Option<FunctionType>,
287        is_static: bool,
288    ) -> Self {
289        Stmt::Fn(Fn {
290            fn_token,
291            name,
292            params,
293            body,
294            public,
295            return_type,
296            is_static,
297        })
298    }
299
300    /// Creates a new `Use` statement.
301    ///
302    /// # Arguments
303    ///
304    /// * `use_token` - The token representing the `use` keyword.
305    /// * `from` - The token representing the module or path to import from.
306    /// * `items` - A vector of tokens representing the items to import.
307    ///
308    /// # Returns
309    ///
310    /// A `Stmt::Use` variant containing the provided import details.
311    pub fn new_use(use_token: Token, from: Token, items: Vec<Token>) -> Self {
312        Stmt::Use(Use {
313            use_token,
314            from,
315            items,
316        })
317    }
318
319    /// Creates a new `If` statement.
320    ///
321    /// # Arguments
322    ///
323    /// * `if_token` - The token representing the `if` keyword.
324    /// * `condition` - The expression to evaluate as the condition.
325    /// * `then_block` - The block of code to execute if the condition is true.
326    /// * `else_ifs` - A vector of `ElseBlock` representing `else if` clauses.
327    /// * `else_block` - An optional `ElseBlock` representing the `else` clause.
328    ///
329    /// # Returns
330    ///
331    /// A `Stmt::If` variant containing the provided condition and blocks.
332    pub fn new_if(
333        if_token: Token,
334        condition: Box<Expr>,
335        then_block: Block,
336        else_ifs: Vec<ElseBlock>,
337        else_block: Option<ElseBlock>,
338    ) -> Self {
339        Stmt::If(If {
340            if_token,
341            condition,
342            then_block,
343            else_ifs,
344            else_block,
345        })
346    }
347
348    /// Creates a new `Let` statement.
349    ///
350    /// # Arguments
351    ///
352    /// * `ident` - The token representing the variable identifier.
353    /// * `initializer` - The expression used to initialize the variable.
354    /// * `type_annotation` - An optional type annotation for the variable.
355    ///
356    /// # Returns
357    ///
358    /// A `Stmt::Let` variant containing the provided variable details.
359    pub fn new_let(
360        ident: Token,
361        initializer: Box<Expr>,
362        type_annotation: Option<TypeAnnotation>,
363    ) -> Self {
364        Stmt::Let(Let {
365            ident,
366            initializer,
367            type_annotation,
368        })
369    }
370
371    /// Creates a new `Return` statement.
372    ///
373    /// # Arguments
374    ///
375    /// * `return_token` - The token representing the `return` keyword.
376    /// * `expr` - An optional expression to return.
377    ///
378    /// # Returns
379    ///
380    /// A `Stmt::Return` variant containing the provided return value.
381    pub fn new_return(return_token: Token, expr: Option<Box<Expr>>) -> Self {
382        Stmt::Return(Return { return_token, expr })
383    }
384
385    /// Creates a new `Struct` statement.
386    ///
387    /// # Arguments
388    /// * `struct_token` - The token representing the `struct` keyword.
389    /// * `name` - The name of the struct.
390    ///
391    /// # Returns
392    /// A `Stmt::Struct` variant containing the provided struct details.
393    pub fn new_struct(
394        struct_token: Token,
395        name: Token,
396        fields: Vec<StructField>,
397        public: bool,
398    ) -> Self {
399        Stmt::Struct(Struct {
400            struct_token,
401            name,
402            fields,
403            public,
404            impls: vec![],
405            trait_impls: vec![],
406        })
407    }
408
409    /// Creates a new `Const` statement.
410    ///
411    /// # Arguments
412    /// * `expr` - The expression to assign to the constant.
413    /// * `ident` - The identifier token for the constant.
414    /// * `public` - A boolean indicating if the constant is public.
415    ///
416    /// # Returns
417    /// A `Stmt::Const` variant containing the provided constant details.
418    pub fn new_const(expr: Box<Expr>, ident: Token, public: bool) -> Self {
419        Stmt::Const(Const {
420            expr,
421            ident,
422            public,
423        })
424    }
425
426    /// Creates a new `TraitDef` statement.
427    ///
428    /// # Arguments
429    /// * `trait_token` - The token representing the `trait` keyword.
430    /// * `name` - The name of the trait.
431    /// * `methods` - A vector of function declarations representing the trait methods.
432    pub fn new_trait_def(trait_token: Token, name: Token, methods: Vec<Fn>, public: bool) -> Self {
433        Stmt::TraitDef(TraitDef {
434            trait_token,
435            name,
436            methods,
437            public,
438        })
439    }
440
441    /// Creates a new `StructImpl` statement.
442    ///
443    /// # Arguments
444    /// * `impl_token` - The token representing the `impl` keyword.
445    /// * `struct_name` - The name of the struct being implemented.
446    /// * `methods` - A vector of function declarations representing the struct methods.
447    ///
448    /// # Returns
449    /// A `Stmt::StructImpl` variant containing the provided struct implementation details.
450    pub fn new_struct_impl(impl_token: Token, struct_name: Token, methods: Vec<Fn>) -> Self {
451        Stmt::StructImpl(StructImpl {
452            impl_token,
453            struct_name,
454            methods,
455        })
456    }
457
458    /// Creates a new `TraitImpl` statement.
459    ///
460    /// # Arguments
461    /// * `impl_token` - The token representing the `impl` keyword.
462    /// * `trait_name` - The name of the trait being implemented.
463    /// * `for_token` - The token representing the `for` keyword.
464    /// * `struct_name` - The name of the struct implementing the trait.
465    /// * `methods` - A vector of function declarations representing the trait methods.
466    ///
467    /// # Returns
468    /// A `Stmt::TraitImpl` variant containing the provided trait implementation details.
469    pub fn new_trait_impl(
470        impl_token: Token,
471        trait_name: Token,
472        for_token: Token,
473        struct_name: Token,
474        methods: Vec<Fn>,
475    ) -> Self {
476        Stmt::TraitImpl(TraitImpl {
477            impl_token,
478            trait_name,
479            for_token,
480            struct_name,
481            methods,
482        })
483    }
484}
485
486impl Stmt {
487    /// Retrieves a reference to the function (`Fn`) contained within the statement.
488    ///
489    /// # Panics
490    ///
491    /// Panics if the statement is not a `Fn` variant.
492    ///
493    /// # Returns
494    ///
495    /// A reference to the contained `Fn` struct.
496    pub fn as_function(&self) -> &Fn {
497        match self {
498            Stmt::Fn(f) => f,
499            _ => panic!("Expected function"),
500        }
501    }
502}
503
504/// Represents a function parameter in the AST.
505///
506/// Each parameter has an identifier, an optional type annotation, and a flag indicating
507/// whether it is a rest parameter (e.g., `...args`).
508#[derive(Clone, Debug, PartialEq)]
509pub struct FnParam {
510    /// The token representing the parameter identifier.
511    pub ident: Token,
512    /// The type annotation of the parameter.
513    pub type_annotation: Option<TypeAnnotation>,
514    /// Indicates whether the parameter is a rest parameter.
515    pub is_rest: bool,
516}
517
518impl GetSpan for FnParam {
519    fn span(&self) -> TextSpan {
520        let mut span = vec![self.ident.span.clone()];
521
522        if let Some(type_annotation) = &self.type_annotation {
523            span.push(type_annotation.span());
524        }
525
526        TextSpan::combine(span).unwrap()
527    }
528}
529
530impl FnParam {
531    /// Creates a new function parameter.
532    ///
533    /// # Arguments
534    ///
535    /// * `ident` - The token representing the parameter identifier.
536    /// * `type_annotation` - The type annotation of the parameter.
537    /// * `is_rest` - A boolean indicating if the parameter is a rest parameter.
538    ///
539    /// # Returns
540    ///
541    /// A new `FnParam` instance.
542    pub fn new(ident: Token, type_annotation: Option<TypeAnnotation>, is_rest: bool) -> Self {
543        Self {
544            ident,
545            type_annotation,
546            is_rest,
547        }
548    }
549}
550
551/// Represents a type annotation in the AST.
552///
553/// A type annotation consists of a colon and the type name.
554#[derive(Debug, Clone, PartialEq)]
555pub struct TypeAnnotation {
556    /// The token representing the colon (`:`) in the type annotation.
557    pub colon: Token,
558    /// The token representing the type name.
559    pub type_name: Token,
560    /// Is this type an array?
561    pub is_array: bool,
562    /// Is nullable?
563    pub is_nullable: bool,
564}
565
566impl TypeAnnotation {
567    pub fn is_any(&self) -> bool {
568        self.type_name.literal() == "anytype"
569    }
570}
571
572impl GetSpan for TypeAnnotation {
573    fn span(&self) -> TextSpan {
574        TextSpan::combine(vec![self.colon.span.clone(), self.type_name.span.clone()]).unwrap()
575    }
576}
577
578/// Represents a function type annotation in the AST.
579///
580/// A function type includes an arrow (`->`) and the return type.
581#[derive(Debug, Clone, PartialEq)]
582pub struct FunctionType {
583    /// The token representing the arrow (`->`) in the function type.
584    pub arrow: Token,
585    /// The token representing the return type.
586    pub type_name: Token,
587    /// Is this type an array?
588    pub is_array: bool,
589    /// Is nullable?
590    pub is_nullable: bool,
591}
592
593impl FunctionType {
594    /// Creates a new function type annotation.
595    ///
596    /// # Arguments
597    ///
598    /// * `arrow` - The token representing the arrow (`->`).
599    /// * `type_name` - The token representing the return type.
600    ///
601    /// # Returns
602    ///
603    /// A new `FunctionType` instance.
604    pub fn new(arrow: Token, type_name: Token, is_array: bool, is_nullable: bool) -> Self {
605        Self {
606            arrow,
607            type_name,
608            is_array,
609            is_nullable,
610        }
611    }
612}
613
614/// Represents a function declaration in the AST.
615///
616/// A function includes its name, parameters, body, export status, and an optional return type.
617#[derive(Clone, PartialEq, Debug)]
618pub struct Fn {
619    /// The token representing the `fn` keyword.
620    pub fn_token: Token,
621    /// The name of the function.
622    pub name: String,
623    /// The list of parameters for the function.
624    pub params: Vec<FnParam>,
625    /// The body of the function as a block of statements.
626    pub body: Block,
627    /// Indicates whether the function is public.
628    pub public: bool,
629    /// An optional return type annotation.
630    pub return_type: Option<FunctionType>,
631    /// Indicates whether the function is static.
632    pub is_static: bool,
633}
634
635/// Represents an `if` statement in the AST.
636///
637/// An `if` statement includes a condition, a `then` block, and optional `else if` and `else` blocks.
638#[derive(Clone, Debug, PartialEq)]
639pub struct If {
640    /// The token representing the `if` keyword.
641    pub if_token: Token,
642    /// The condition expression to evaluate.
643    pub condition: Box<Expr>,
644    /// The block of code to execute if the condition is true.
645    pub then_block: Block,
646    /// A list of `else if` blocks.
647    pub else_ifs: Vec<ElseBlock>,
648    /// An optional `else` block.
649    pub else_block: Option<ElseBlock>,
650}
651
652/// Represents an `else` or `else if` block in the AST.
653///
654/// An `ElseBlock` can optionally include a condition (for `else if`) and contains a block of statements.
655#[derive(Clone, Debug, PartialEq)]
656pub struct ElseBlock {
657    /// The condition expression for an `else if` block. `None` for a plain `else` block.
658    pub condition: Box<Expr>,
659    /// The block of code to execute for this `else if` or `else` block.
660    pub block: Block,
661    /// Indicates whether this block is an `else if` (`true`) or a plain `else` (`false`).
662    pub else_if: bool,
663}
664
665/// Represents a `use` statement for importing modules or items in the AST.
666///
667/// A `use` statement specifies the source module and the items to import.
668#[derive(Clone, Debug, PartialEq)]
669pub struct Use {
670    /// The token representing the `use` keyword.
671    pub use_token: Token,
672    /// The token representing the module or path to import from.
673    pub from: Token,
674    /// A list of tokens representing the items to import.
675    pub items: Vec<Token>,
676}
677
678/// Represents a block of statements enclosed in braces in the AST.
679///
680/// A `Block` contains a sequence of statements that are executed together.
681#[derive(Clone, PartialEq)]
682pub struct Block {
683    /// The list of statements contained within the block.
684    pub stmts: Vec<Stmt>,
685}
686
687impl Debug for Block {
688    /// Custom implementation of the `Debug` trait for the `Block` struct.
689    ///
690    /// This provides a formatted debug representation, displaying the number of statements.
691    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
692        f.debug_struct("Block")
693            .field("stmts", &self.stmts.len())
694            .finish()
695    }
696}
697
698/// Represents a `return` statement in the AST.
699///
700/// A `return` statement exits a function, optionally returning an expression.
701#[derive(Clone, PartialEq, Debug)]
702pub struct Return {
703    /// The token representing the `return` keyword.
704    pub return_token: Token,
705    /// An optional expression to return from the function.
706    pub expr: Option<Box<Expr>>,
707}