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::sequence::TokenSeparatedSequence;
17pub use crate::ast::sequence::TokenSeparatedSequenceExt;
18pub use crate::ast::trivia::Trivia;
19pub use crate::ast::trivia::TriviaKind;
20pub use crate::ast::trivia::TriviaSequenceExt;
21use crate::error::ParseError;
22
23pub mod ast;
24pub mod node;
25pub mod sequence;
26pub mod trivia;
27
28#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
29pub struct Program<'arena> {
30 pub file_id: FileId,
31 pub source_text: &'arena str,
32 pub trivia: Sequence<'arena, Trivia<'arena>>,
33 pub statements: Sequence<'arena, Statement<'arena>>,
34 pub errors: Vec<'arena, ParseError>,
35}
36
37impl Program<'_> {
38 #[inline]
40 #[must_use]
41 pub fn has_errors(&self) -> bool {
42 !self.errors.is_empty()
43 }
44
45 #[must_use]
47 pub fn has_script(&self) -> bool {
48 for statement in &self.statements {
49 if !matches!(statement, Statement::Inline(_)) {
50 return true;
51 }
52 }
53
54 false
55 }
56}
57
58impl HasSpan for Program<'_> {
59 fn span(&self) -> Span {
60 let start = self.statements.first().map_or_else(Position::zero, |stmt| stmt.span().start);
61 let end = self.statements.last().map_or_else(Position::zero, |stmt| stmt.span().end);
62
63 Span::new(self.file_id, start, end)
64 }
65}