Skip to main content

ecma_syntax_cat/
program.rs

1//! The top-level [`Program`] node.
2
3use crate::module::ModuleItem;
4use crate::span::Spanned;
5use crate::statement::Statement;
6
7/// A complete parsed ECMAScript source paired with its source span.
8pub type Program = Spanned<ProgramKind>;
9
10/// Whether the program is a Script or a Module.  ECMA-262 treats the two
11/// goals as distinct, with differences such as top-level `await` and the
12/// availability of `import`/`export`.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ProgramKind {
15    /// A Script: top-level body is a sequence of statements.
16    Script {
17        /// Top-level statements.
18        body: Vec<Statement>,
19    },
20    /// A Module: top-level body may include import/export declarations.
21    Module {
22        /// Top-level module items.
23        body: Vec<ModuleItem>,
24    },
25}
26
27impl ProgramKind {
28    /// Build a Script program.
29    #[must_use]
30    pub fn script(body: Vec<Statement>) -> Self {
31        Self::Script { body }
32    }
33
34    /// Build a Module program.
35    #[must_use]
36    pub fn module(body: Vec<ModuleItem>) -> Self {
37        Self::Module { body }
38    }
39}
40
41impl std::fmt::Display for ProgramKind {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        match self {
44            Self::Script { body } => write_items(f, "Script", body.iter().map(|s| format!("{s}"))),
45            Self::Module { body } => write_items(f, "Module", body.iter().map(|m| format!("{m}"))),
46        }
47    }
48}
49
50fn write_items<I: Iterator<Item = String>>(
51    f: &mut std::fmt::Formatter<'_>,
52    kind: &str,
53    items: I,
54) -> std::fmt::Result {
55    write!(f, "{kind} {{ ")?;
56    let body = items.collect::<Vec<_>>().join("; ");
57    write!(f, "{body} }}")
58}