Skip to main content

ecma_syntax_cat/
statement.rs

1//! ECMAScript statements.
2
3use crate::class::Class;
4use crate::declaration::VariableDeclaration;
5use crate::expression::Expression;
6use crate::function::Function;
7use crate::identifier::Identifier;
8use crate::pattern::Pattern;
9use crate::span::Spanned;
10
11/// A statement paired with its source span.
12pub type Statement = Spanned<StatementKind>;
13
14/// The shape of an ECMAScript statement.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum StatementKind {
17    /// A block: `{ ... }`.
18    Block {
19        /// Statements inside the block.
20        body: Vec<Statement>,
21    },
22    /// An empty statement (`;`).
23    Empty,
24    /// `debugger;`
25    Debugger,
26    /// An expression evaluated as a statement.
27    Expression {
28        /// The expression.
29        expression: Expression,
30    },
31    /// `if (test) consequent else? alternate`.
32    If {
33        /// The test expression.
34        test: Expression,
35        /// The body run when `test` is truthy.
36        consequent: Box<Statement>,
37        /// The optional `else` branch.
38        alternate: Option<Box<Statement>>,
39    },
40    /// `switch (discriminant) { cases }`.
41    Switch {
42        /// The discriminant expression.
43        discriminant: Expression,
44        /// The cases.
45        cases: Vec<SwitchCase>,
46    },
47    /// `for (init; test; update) body`.
48    For {
49        /// Optional initialiser (declaration or expression).
50        init: Option<ForInit>,
51        /// Optional loop-continuation test.
52        test: Option<Expression>,
53        /// Optional update expression evaluated after each iteration.
54        update: Option<Expression>,
55        /// The loop body.
56        body: Box<Statement>,
57    },
58    /// `for (left in right) body`.
59    ForIn {
60        /// The iteration target (declaration or assignment-target pattern).
61        left: ForLeft,
62        /// The object whose enumerable properties are iterated.
63        right: Expression,
64        /// The loop body.
65        body: Box<Statement>,
66    },
67    /// `for (left of right) body`, possibly `for await`.
68    ForOf {
69        /// The iteration target.
70        left: ForLeft,
71        /// The iterable expression.
72        right: Expression,
73        /// The loop body.
74        body: Box<Statement>,
75        /// True for `for await (...) of`.
76        is_await: bool,
77    },
78    /// `while (test) body`.
79    While {
80        /// The test expression.
81        test: Expression,
82        /// The loop body.
83        body: Box<Statement>,
84    },
85    /// `do body while (test);`.
86    DoWhile {
87        /// The loop body.
88        body: Box<Statement>,
89        /// The test expression checked after each iteration.
90        test: Expression,
91    },
92    /// `return argument?;`
93    Return {
94        /// Optional return value.
95        argument: Option<Expression>,
96    },
97    /// `throw argument;`
98    Throw {
99        /// The value to throw.
100        argument: Expression,
101    },
102    /// `try { ... } catch (e) { ... } finally { ... }`.
103    Try {
104        /// The protected block.
105        block: Vec<Statement>,
106        /// Optional catch clause.
107        handler: Option<CatchClause>,
108        /// Optional finally block.
109        finalizer: Option<Vec<Statement>>,
110    },
111    /// `break label?;`
112    Break {
113        /// Optional label to break out of.
114        label: Option<Identifier>,
115    },
116    /// `continue label?;`
117    Continue {
118        /// Optional label to continue.
119        label: Option<Identifier>,
120    },
121    /// `label: body` -- attach a label to a loop or block.
122    Labeled {
123        /// The label name.
124        label: Identifier,
125        /// The labelled statement.
126        body: Box<Statement>,
127    },
128    /// A variable declaration treated as a statement.
129    VariableDeclaration(VariableDeclaration),
130    /// A function declaration.
131    FunctionDeclaration(Function),
132    /// A class declaration.
133    ClassDeclaration(Class),
134}
135
136/// One arm of a `switch` statement.
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct SwitchCase {
139    test: Option<Expression>,
140    consequent: Vec<Statement>,
141}
142
143impl SwitchCase {
144    /// Build a case.  `test = None` is the `default:` arm.
145    #[must_use]
146    pub fn new(test: Option<Expression>, consequent: Vec<Statement>) -> Self {
147        Self { test, consequent }
148    }
149
150    /// The case label expression, or `None` for `default:`.
151    #[must_use]
152    pub fn test(&self) -> Option<&Expression> {
153        self.test.as_ref()
154    }
155
156    /// The statements that run when this case is selected.
157    #[must_use]
158    pub fn consequent(&self) -> &[Statement] {
159        &self.consequent
160    }
161}
162
163/// The catch clause of a `try` statement.
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct CatchClause {
166    param: Option<Pattern>,
167    body: Vec<Statement>,
168}
169
170impl CatchClause {
171    /// Build a catch clause.  `param = None` represents bare `catch { ... }`
172    /// without a binding.
173    #[must_use]
174    pub fn new(param: Option<Pattern>, body: Vec<Statement>) -> Self {
175        Self { param, body }
176    }
177
178    /// The binding receiving the thrown value, if any.
179    #[must_use]
180    pub fn param(&self) -> Option<&Pattern> {
181        self.param.as_ref()
182    }
183
184    /// The statements in the catch body.
185    #[must_use]
186    pub fn body(&self) -> &[Statement] {
187        &self.body
188    }
189}
190
191/// The initialiser slot of a `for(;;)` loop.
192#[derive(Debug, Clone, PartialEq, Eq)]
193pub enum ForInit {
194    /// `for (let i = 0; ...)`
195    Declaration(VariableDeclaration),
196    /// `for (i = 0; ...)`
197    Expression(Expression),
198}
199
200/// The left-hand side of `for-in` / `for-of`.
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub enum ForLeft {
203    /// `for (let x in obj)`
204    Declaration(VariableDeclaration),
205    /// `for (x in obj)`
206    Pattern(Pattern),
207}
208
209impl std::fmt::Display for StatementKind {
210    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211        match self {
212            Self::Block { body } => write_block(f, body),
213            Self::Empty => f.write_str(";"),
214            Self::Debugger => f.write_str("debugger;"),
215            Self::Expression { expression } => write!(f, "{expression};"),
216            Self::If {
217                test,
218                consequent,
219                alternate,
220            } => write_if(f, test, consequent, alternate.as_deref()),
221            Self::Switch {
222                discriminant,
223                cases,
224            } => write_switch(f, discriminant, cases),
225            Self::For {
226                init,
227                test,
228                update,
229                body,
230            } => write_for(f, init.as_ref(), test.as_ref(), update.as_ref(), body),
231            Self::ForIn { left, right, body } => write!(f, "for ({left} in {right}) {body}"),
232            Self::ForOf {
233                left,
234                right,
235                body,
236                is_await,
237            } => write_for_of(f, left, right, body, *is_await),
238            Self::While { test, body } => write!(f, "while ({test}) {body}"),
239            Self::DoWhile { body, test } => write!(f, "do {body} while ({test});"),
240            Self::Return { argument } => write_return(f, argument.as_ref()),
241            Self::Throw { argument } => write!(f, "throw {argument};"),
242            Self::Try {
243                block,
244                handler,
245                finalizer,
246            } => write_try(f, block, handler.as_ref(), finalizer.as_ref()),
247            Self::Break { label } => write_break_continue(f, "break", label.as_ref()),
248            Self::Continue { label } => write_break_continue(f, "continue", label.as_ref()),
249            Self::Labeled { label, body } => write!(f, "{label}: {body}"),
250            Self::VariableDeclaration(decl) => write!(f, "{decl};"),
251            Self::FunctionDeclaration(func) => write!(f, "{func}"),
252            Self::ClassDeclaration(class) => write!(f, "{class}"),
253        }
254    }
255}
256
257fn write_block(f: &mut std::fmt::Formatter<'_>, body: &[Statement]) -> std::fmt::Result {
258    let lines = body
259        .iter()
260        .map(|s| format!("  {s}"))
261        .collect::<Vec<_>>()
262        .join("\n");
263    if body.is_empty() {
264        f.write_str("{}")
265    } else {
266        write!(f, "{{\n{lines}\n}}")
267    }
268}
269
270fn write_if(
271    f: &mut std::fmt::Formatter<'_>,
272    test: &Expression,
273    consequent: &Statement,
274    alternate: Option<&Statement>,
275) -> std::fmt::Result {
276    write!(f, "if ({test}) {consequent}")?;
277    match alternate {
278        Some(alt) => write!(f, " else {alt}"),
279        None => Ok(()),
280    }
281}
282
283fn write_switch(
284    f: &mut std::fmt::Formatter<'_>,
285    discriminant: &Expression,
286    cases: &[SwitchCase],
287) -> std::fmt::Result {
288    write!(f, "switch ({discriminant}) {{ ")?;
289    let body = cases
290        .iter()
291        .map(|c| match c.test() {
292            Some(t) => format!("case {t}: ..."),
293            None => "default: ...".to_owned(),
294        })
295        .collect::<Vec<_>>()
296        .join(" ");
297    write!(f, "{body} }}")
298}
299
300fn write_for(
301    f: &mut std::fmt::Formatter<'_>,
302    init: Option<&ForInit>,
303    test: Option<&Expression>,
304    update: Option<&Expression>,
305    body: &Statement,
306) -> std::fmt::Result {
307    let init_str = init.map_or(String::new(), |i| format!("{i}"));
308    let test_str = test.map_or(String::new(), |t| format!("{t}"));
309    let update_str = update.map_or(String::new(), |u| format!("{u}"));
310    write!(f, "for ({init_str}; {test_str}; {update_str}) {body}")
311}
312
313fn write_for_of(
314    f: &mut std::fmt::Formatter<'_>,
315    left: &ForLeft,
316    right: &Expression,
317    body: &Statement,
318    is_await: bool,
319) -> std::fmt::Result {
320    let keyword = if is_await { "for await" } else { "for" };
321    write!(f, "{keyword} ({left} of {right}) {body}")
322}
323
324fn write_return(
325    f: &mut std::fmt::Formatter<'_>,
326    argument: Option<&Expression>,
327) -> std::fmt::Result {
328    match argument {
329        Some(arg) => write!(f, "return {arg};"),
330        None => f.write_str("return;"),
331    }
332}
333
334fn write_try(
335    f: &mut std::fmt::Formatter<'_>,
336    block: &[Statement],
337    handler: Option<&CatchClause>,
338    finalizer: Option<&Vec<Statement>>,
339) -> std::fmt::Result {
340    write!(f, "try {{ {} stmts }}", block.len())?;
341    if let Some(h) = handler {
342        match h.param() {
343            Some(p) => write!(f, " catch ({p}) {{ ... }}")?,
344            None => write!(f, " catch {{ ... }}")?,
345        }
346    }
347    if let Some(fin) = finalizer {
348        write!(f, " finally {{ {} stmts }}", fin.len())?;
349    }
350    Ok(())
351}
352
353fn write_break_continue(
354    f: &mut std::fmt::Formatter<'_>,
355    keyword: &str,
356    label: Option<&Identifier>,
357) -> std::fmt::Result {
358    match label {
359        Some(l) => write!(f, "{keyword} {l};"),
360        None => write!(f, "{keyword};"),
361    }
362}
363
364impl std::fmt::Display for ForInit {
365    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366        match self {
367            Self::Declaration(decl) => write!(f, "{decl}"),
368            Self::Expression(expr) => write!(f, "{expr}"),
369        }
370    }
371}
372
373impl std::fmt::Display for ForLeft {
374    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375        match self {
376            Self::Declaration(decl) => write!(f, "{decl}"),
377            Self::Pattern(pat) => write!(f, "{pat}"),
378        }
379    }
380}