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}