Skip to main content

m2_syn/parser/
body.rs

1//! Value-returning bodies with semicolon-terminated discarded statements.
2
3use crate::{Lex, Parse, ParseStream, Span, Spanned, Token, TokenParseError, TokenStream};
4
5/// A statement whose value is discarded by a trailing semicolon.
6pub struct Terminated<S> {
7    /// The statement whose result is discarded.
8    pub statement: S,
9    /// The terminating semicolon.
10    pub semicolon: Token![;],
11}
12
13impl<S: Spanned> Spanned for Terminated<S> {
14    fn try_span(&self) -> Option<Span> {
15        self.statement.try_span()?.join(self.semicolon.span())
16    }
17}
18
19impl<S> Terminated<S> {
20    /// Creates a value-discarding terminated statement.
21    pub fn new(statement: S, semicolon: Token![;]) -> Self {
22        Self {
23            statement,
24            semicolon,
25        }
26    }
27}
28
29impl<S: Parse> Parse for Terminated<S> {
30    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
31        reject_empty_statement(input)?;
32        Ok(Self::new(S::parse(input)?, Parse::parse(input)?))
33    }
34}
35
36impl<S: Lex> Lex for Terminated<S> {
37    fn to_tokens(&self, output: &mut TokenStream) {
38        self.statement.to_tokens(output);
39        self.semicolon.to_tokens(output);
40    }
41}
42
43/// Statements discarded by semicolons followed by an optional returned tail.
44///
45/// This is analogous to a Rust block: every terminated statement is evaluated
46/// for effects, while the unterminated tail supplies the body's value.
47#[derive(Spanned)]
48pub struct Body<S> {
49    /// Statements terminated by semicolons.
50    pub statements: Vec<Terminated<S>>,
51    /// The optional unterminated value returned by the body.
52    pub tail: Option<S>,
53}
54
55impl<S> Body<S> {
56    /// Creates a body from discarded statements and its returned tail.
57    pub fn new(statements: Vec<Terminated<S>>, tail: Option<S>) -> Self {
58        Self { statements, tail }
59    }
60}
61
62impl<S: Parse> Parse for Body<S> {
63    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
64        let mut statements = Vec::new();
65        while !input.is_eof() {
66            reject_empty_statement(input)?;
67            let statement = S::parse(input)?;
68            if input
69                .peek()
70                .is_some_and(<Token![;] as Token>::matches_token_tree)
71            {
72                statements.push(Terminated::new(statement, Parse::parse(input)?));
73            } else {
74                return Ok(Self::new(statements, Some(statement)));
75            }
76        }
77        Ok(Self::new(statements, None))
78    }
79}
80
81fn reject_empty_statement(input: &ParseStream) -> Result<(), TokenParseError> {
82    let Some(token) = input
83        .peek()
84        .filter(|token| <Token![;] as Token>::matches_token_tree(token))
85    else {
86        return Ok(());
87    };
88    Err(TokenParseError::unexpected(
89        "a statement before `;`",
90        ";",
91        token.expect_span(),
92    ))
93}
94
95impl<S: Lex> Lex for Body<S> {
96    fn to_tokens(&self, output: &mut TokenStream) {
97        self.statements.to_tokens(output);
98        self.tail.to_tokens(output);
99    }
100}