Skip to main content

mago_syntax/ast/
mod.rs

1#![expect(clippy::module_inception)]
2
3use std::fmt::Debug;
4
5use bumpalo::collections::Vec;
6use serde::Serialize;
7
8use mago_database::file::FileId;
9use mago_span::HasSpan;
10use mago_span::Position;
11use mago_span::Span;
12
13pub use crate::ast::ast::*;
14pub use crate::ast::node::*;
15pub use crate::ast::sequence::Sequence;
16pub use crate::ast::trivia::Trivia;
17pub use crate::ast::trivia::TriviaKind;
18use crate::error::ParseError;
19
20pub mod ast;
21pub mod node;
22pub mod sequence;
23pub mod trivia;
24
25#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
26pub struct Program<'arena> {
27    pub file_id: FileId,
28    pub source_text: &'arena str,
29    pub trivia: Sequence<'arena, Trivia<'arena>>,
30    pub statements: Sequence<'arena, Statement<'arena>>,
31    pub errors: Vec<'arena, ParseError>,
32}
33
34impl Program<'_> {
35    /// Returns `true` if the program contains any parsing errors.
36    #[inline]
37    pub fn has_errors(&self) -> bool {
38        !self.errors.is_empty()
39    }
40
41    /// Returns `true` if the program contains any non-inline script statements.
42    #[must_use]
43    pub fn has_script(&self) -> bool {
44        for statement in &self.statements {
45            if !matches!(statement, Statement::Inline(_)) {
46                return true;
47            }
48        }
49
50        false
51    }
52}
53
54impl HasSpan for Program<'_> {
55    fn span(&self) -> Span {
56        let start = self.statements.first().map_or_else(Position::zero, |stmt| stmt.span().start);
57        let end = self.statements.last().map_or_else(Position::zero, |stmt| stmt.span().end);
58
59        Span::new(self.file_id, start, end)
60    }
61}