cas_parser/parser/ast/
stmt.rs

1use cas_error::Error;
2use crate::parser::{
3    ast::expr::Expr,
4    fmt::Latex,
5    token::Semicolon,
6    Parse,
7    Parser,
8};
9use std::{fmt, ops::Range};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14/// Represents a statement in CalcScript.
15///
16/// A statement is the building block of all CalcScript programs. A complete program is a sequence
17/// of one or more statements that are executed in order, and returns the value of the last
18/// statement. A statement's return value can be discarded by adding a semicolon at the end of the
19/// statement.
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub struct Stmt {
23    /// The expression of a statement.
24    pub expr: Expr,
25
26    /// The span of the semicolon that terminates the statement, if any.
27    ///
28    /// When this is [`None`], the statement is an expression, and will return the value of the
29    /// expression. Otherwise, the expression is evaluated for side effects, and the statement
30    /// returns the unit type `()`.
31    pub semicolon: Option<Range<usize>>,
32
33    /// The region of the source code that this statement was parsed from.
34    pub span: Range<usize>,
35}
36
37impl Stmt {
38    /// Returns the span of the statement.
39    pub fn span(&self) -> Range<usize> {
40        self.span.clone()
41    }
42}
43
44impl<'source> Parse<'source> for Stmt {
45    fn std_parse(
46        input: &mut Parser<'source>,
47        recoverable_errors: &mut Vec<Error>
48    ) -> Result<Self, Vec<Error>> {
49        let expr = input.try_parse::<Expr>().forward_errors(recoverable_errors)?;
50        let semicolon = if let Ok(semi) = input.try_parse::<Semicolon>().forward_errors(recoverable_errors) {
51            Some(semi.span.clone())
52        } else {
53            None
54        };
55        let stmt_span = if let Some(semicolon) = &semicolon {
56            expr.span().start..semicolon.end
57        } else {
58            expr.span()
59        };
60
61        Ok(Stmt {
62            expr,
63            semicolon,
64            span: stmt_span,
65        })
66    }
67}
68
69impl std::fmt::Display for Stmt {
70    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
71        write!(f, "{}", self.expr)?;
72        if self.semicolon.is_some() {
73            write!(f, ";")?;
74        }
75        Ok(())
76    }
77}
78
79impl Latex for Stmt {
80    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
81        self.expr.fmt_latex(f)?;
82        if self.semicolon.is_some() {
83            write!(f, ";")?;
84        }
85        Ok(())
86    }
87}