1use crate::{Lex, Parse, ParseStream, Span, Spanned, Token, TokenParseError, TokenStream};
4
5pub struct Terminated<S> {
7 pub statement: S,
9 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 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#[derive(Spanned)]
48pub struct Body<S> {
49 pub statements: Vec<Terminated<S>>,
51 pub tail: Option<S>,
53}
54
55impl<S> Body<S> {
56 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}