Skip to main content

mago_syntax/cst/
mod.rs

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