spade_parser/
statements.rs

1use spade_ast::{AttributeList, Binding, Expression, Register, Statement};
2use spade_common::{
3    location_info::{lspan, AsLabel, Loc, WithLocation},
4    name::Visibility,
5};
6use spade_diagnostics::{diag_bail, Diagnostic};
7use spade_macros::trace_parser;
8
9use crate::{
10    error::Result, item_type::UnitKindLocal, lexer::TokenKind, peek_for, KeywordPeekingParser,
11    ParseStackEntry, Parser, Token,
12};
13
14pub(crate) struct BindingParser {}
15
16impl KeywordPeekingParser<Loc<Statement>> for BindingParser {
17    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
18        |kind| kind == &TokenKind::Let
19    }
20
21    fn parse(
22        &self,
23        parser: &mut Parser,
24        attributes: &AttributeList,
25        visibility: &Loc<Visibility>,
26    ) -> Result<Loc<Statement>> {
27        let start_token = parser.eat_unconditional()?;
28        parser.disallow_visibility(visibility, &start_token)?;
29
30        let (pattern, start_span) = parser.pattern()?.separate();
31
32        let ty = if parser.peek_and_eat(&TokenKind::Colon)?.is_some() {
33            Some(parser.type_spec()?)
34        } else {
35            None
36        };
37
38        parser.eat(&TokenKind::Assignment)?;
39        let (value, end_span) = parser.expression()?.separate();
40
41        Ok(Statement::Binding(Binding {
42            pattern,
43            ty,
44            value,
45            attrs: attributes.clone(),
46        })
47        .between(parser.file_id, &start_span, &end_span))
48    }
49}
50
51pub(crate) struct RegisterParser {}
52
53impl KeywordPeekingParser<Loc<Statement>> for RegisterParser {
54    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
55        |kind| kind == &TokenKind::Reg
56    }
57
58    fn parse(
59        &self,
60        parser: &mut Parser,
61        attributes: &AttributeList,
62        visibility: &Loc<Visibility>,
63    ) -> Result<Loc<Statement>> {
64        let start_token = parser.eat_unconditional()?;
65        parser.disallow_visibility(visibility, &start_token)?;
66
67        // NOTE: It might be nicer to use () but that complicates the compiler slightly more
68        // annoying to write, so I'll use [] initially as a proof of concept
69        let cond = if parser.peek_kind(&TokenKind::OpenBracket)? {
70            Some(
71                parser
72                    .surrounded(
73                        &TokenKind::OpenBracket,
74                        Parser::expression,
75                        &TokenKind::CloseBracket,
76                    )?
77                    .0,
78            )
79        } else {
80            None
81        };
82
83        // If this is a reg marker for a pipeline
84        if parser.peek_kind(&TokenKind::Semi)? || parser.peek_kind(&TokenKind::Asterisk)? {
85            let count = if let Some(ast) = parser.peek_and_eat(&TokenKind::Asterisk)? {
86                match parser.type_expression() {
87                    Ok(t) => Some(t),
88                    Err(diag) => {
89                        return Err(
90                            diag.secondary_label(ast, "* is used to specify a register count")
91                        )
92                    }
93                }
94            } else {
95                None
96            };
97
98            let full_loc = if let Some(c) = &count {
99                ().between(parser.file_id, &start_token, &c.loc())
100            } else {
101                ().at(parser.file_id, &start_token)
102            };
103
104            return Ok(Statement::PipelineRegMarker(count, cond).at_loc(&full_loc));
105        }
106
107        parser
108            .unit_context
109            .allows_reg(().at(parser.file_id, &start_token.span()))?;
110
111        // Clock selection
112        let (clock, _clock_paren_span) = parser.surrounded(
113            &TokenKind::OpenParen,
114            |s| s.expression().map(Some),
115            &TokenKind::CloseParen,
116        )?;
117
118        // Identifier parsing cannot fail since we map it into a Some. Therefore,
119        // unwrap is safe
120        let clock = clock.unwrap();
121
122        // Name
123        let pattern = parser.pattern()?;
124
125        // Optional type
126        let value_type = if parser.peek_and_eat(&TokenKind::Colon)?.is_some() {
127            Some(parser.type_spec()?)
128        } else {
129            None
130        };
131
132        // Optional reset
133        let reset = parser.register_reset()?;
134        let initial = parser.register_initial()?;
135        // Try parsing reset again, if we find two resets, error out
136        let reset = match (reset, parser.register_reset()?) {
137            (Some(first), None) => Some(first),
138            (None, Some(second)) => Some(second),
139            (Some(first), Some(second)) => {
140                return Err(Diagnostic::error(
141                    ().between_locs(&second.0, &second.1),
142                    "Multiple resets specified",
143                )
144                .primary_label("Second reset")
145                .secondary_label(().between_locs(&first.0, &first.1), "First reset"))
146            }
147            (None, None) => None,
148        };
149
150        // Value
151        parser.eat(&TokenKind::Assignment)?;
152        let (value, end_span) = parser.expression()?.separate();
153
154        let span = lspan(start_token.span).merge(end_span);
155        let result = Statement::Register(
156            Register {
157                pattern,
158                clock,
159                reset,
160                initial,
161                value,
162                value_type,
163                attributes: attributes.clone(),
164            }
165            .at(parser.file_id, &span),
166        )
167        .at(parser.file_id, &span);
168        Ok(result)
169    }
170}
171
172impl<'a> Parser<'a> {
173    #[trace_parser]
174    pub fn register_reset_definition(&mut self) -> Result<(Loc<Expression>, Loc<Expression>)> {
175        let condition = self.expression()?;
176        self.eat(&TokenKind::Colon)?;
177        let value = self.expression()?;
178
179        Ok((condition, value))
180    }
181
182    #[trace_parser]
183    pub fn register_reset(&mut self) -> Result<Option<(Loc<Expression>, Loc<Expression>)>> {
184        peek_for!(self, &TokenKind::Reset);
185        let (reset, _) = self.surrounded(
186            &TokenKind::OpenParen,
187            |s| s.register_reset_definition().map(Some),
188            &TokenKind::CloseParen,
189        )?;
190        // NOTE: Safe unwrap, register_reset_definition can not fail
191        Ok(Some(reset.unwrap()))
192    }
193
194    #[trace_parser]
195    pub fn register_initial(&mut self) -> Result<Option<Loc<Expression>>> {
196        peek_for!(self, &TokenKind::Initial);
197        let (reset, _) = self.surrounded(
198            &TokenKind::OpenParen,
199            Self::expression,
200            &TokenKind::CloseParen,
201        )?;
202        Ok(Some(reset))
203    }
204}
205
206pub(crate) struct DeclParser {}
207
208impl KeywordPeekingParser<Loc<Statement>> for DeclParser {
209    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
210        |kind| kind == &TokenKind::Decl
211    }
212
213    fn parse(
214        &self,
215        parser: &mut Parser,
216        attributes: &AttributeList,
217        visibility: &Loc<Visibility>,
218    ) -> Result<Loc<Statement>> {
219        let start_token = parser.eat_unconditional()?;
220        parser.disallow_attributes(attributes, &start_token)?;
221        parser.disallow_visibility(visibility, &start_token)?;
222
223        let mut identifiers = vec![];
224        while parser.peek_cond(|t| t.is_identifier(), "expected identifier")? {
225            identifiers.push(parser.identifier()?);
226
227            if parser.peek_and_eat(&TokenKind::Comma)?.is_none() {
228                break;
229            }
230        }
231
232        if identifiers.is_empty() {
233            return Err(Diagnostic::error(start_token.loc(), "empty decl statement")
234                .primary_label("this decl does not declare anything"));
235        }
236
237        let last_ident = identifiers.last().unwrap().clone();
238
239        Ok(Statement::Declaration(identifiers).between(
240            parser.file_id,
241            &start_token.span,
242            &last_ident,
243        ))
244    }
245}
246
247pub(crate) struct LabelParser {}
248
249impl KeywordPeekingParser<Loc<Statement>> for LabelParser {
250    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
251        |kind| matches!(kind, TokenKind::Label(_))
252    }
253
254    fn parse(
255        &self,
256        parser: &mut Parser,
257        attributes: &AttributeList,
258        visibility: &Loc<Visibility>,
259    ) -> Result<Loc<Statement>> {
260        let tok @ Token {
261            kind: TokenKind::Label(l),
262            ..
263        } = &parser.eat_unconditional()?
264        else {
265            diag_bail!(
266                parser.peek()?,
267                "Label parser was called but it did not get a label"
268            )
269        };
270        parser.disallow_attributes(attributes, &tok)?;
271        parser.disallow_visibility(visibility, &tok)?;
272
273        Ok(Statement::Label(l.clone().at(parser.file_id, &tok.span)).at(parser.file_id, &tok.span))
274    }
275}
276
277pub(crate) struct AssertParser {}
278
279impl KeywordPeekingParser<Loc<Statement>> for AssertParser {
280    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
281        |kind| kind == &TokenKind::Assert
282    }
283
284    fn parse(
285        &self,
286        parser: &mut Parser,
287        attributes: &AttributeList,
288        visibility: &Loc<Visibility>,
289    ) -> Result<Loc<Statement>> {
290        let tok = parser.eat_unconditional()?;
291        parser.disallow_attributes(attributes, &tok)?;
292        parser.disallow_visibility(visibility, &tok)?;
293
294        let expr = parser.expression()?;
295
296        Ok(Statement::Assert(expr.clone()).between(parser.file_id, &tok.span, &expr))
297    }
298}
299
300pub(crate) struct SetParser {}
301
302impl KeywordPeekingParser<Loc<Statement>> for SetParser {
303    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
304        |kind| kind == &TokenKind::Set
305    }
306
307    fn parse(
308        &self,
309        parser: &mut Parser,
310        attributes: &AttributeList,
311        visibility: &Loc<Visibility>,
312    ) -> Result<Loc<Statement>> {
313        let tok = parser.eat_unconditional()?;
314        parser.disallow_attributes(attributes, &tok)?;
315        parser.disallow_visibility(visibility, &tok)?;
316
317        let target = parser.expression()?;
318
319        parser.eat(&TokenKind::Assignment)?;
320
321        let value = parser.expression()?;
322
323        Ok(Statement::Set {
324            target,
325            value: value.clone(),
326        }
327        .between(parser.file_id, &tok.span, &value))
328    }
329}