Skip to main content

spade_parser/
statements.rs

1use spade_ast::{AttributeList, Binding, Expression, Register, Statement, token::TokenKind};
2use spade_common::{
3    location_info::{AsLabel, Loc, WithLocation, lspan},
4    name::Visibility,
5};
6use spade_diagnostics::{Diagnostic, diag_bail};
7use spade_macros::trace_parser;
8
9use crate::{
10    KeywordPeekingParser, ParseStackEntry, Parser, Token, error::Result, item_type::UnitKindLocal,
11    peek_for,
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(false)?)
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(false)?)
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.clone()).merge(end_span);
155        let result = Statement::Register(
156            Register {
157                keyword: ().at(parser.file_id(), &start_token),
158                pattern,
159                clock,
160                reset,
161                initial,
162                value,
163                value_type,
164                attributes: attributes.clone(),
165            }
166            .at(parser.file_id(), &span),
167        )
168        .at(parser.file_id(), &span);
169        Ok(result)
170    }
171}
172
173impl<'a> Parser<'a> {
174    #[trace_parser]
175    pub fn register_reset_definition(&mut self) -> Result<(Loc<Expression>, Loc<Expression>)> {
176        let condition = self.expression()?;
177        self.eat(&TokenKind::Colon)?;
178        let value = self.expression()?;
179
180        Ok((condition, value))
181    }
182
183    #[trace_parser]
184    pub fn register_reset(&mut self) -> Result<Option<(Loc<Expression>, Loc<Expression>)>> {
185        peek_for!(self, &TokenKind::Reset);
186        let (reset, _) = self.surrounded(
187            &TokenKind::OpenParen,
188            |s| s.register_reset_definition().map(Some),
189            &TokenKind::CloseParen,
190        )?;
191        // NOTE: Safe unwrap, register_reset_definition can not fail
192        Ok(Some(reset.unwrap()))
193    }
194
195    #[trace_parser]
196    pub fn register_initial(&mut self) -> Result<Option<Loc<Expression>>> {
197        peek_for!(self, &TokenKind::Initial);
198        let (reset, _) = self.surrounded(
199            &TokenKind::OpenParen,
200            Self::expression,
201            &TokenKind::CloseParen,
202        )?;
203        Ok(Some(reset))
204    }
205}
206
207pub(crate) struct DeclParser {}
208
209impl KeywordPeekingParser<Loc<Statement>> for DeclParser {
210    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
211        |kind| kind == &TokenKind::Decl
212    }
213
214    fn parse(
215        &self,
216        parser: &mut Parser,
217        attributes: &AttributeList,
218        visibility: &Loc<Visibility>,
219    ) -> Result<Loc<Statement>> {
220        let start_token = parser.eat_unconditional()?;
221        parser.disallow_attributes(attributes, &start_token)?;
222        parser.disallow_visibility(visibility, &start_token)?;
223
224        let mut identifiers = vec![];
225        while parser.peek_cond(|t| t.is_identifier(), "expected identifier")? {
226            identifiers.push(parser.identifier()?);
227
228            if parser.peek_and_eat(&TokenKind::Comma)?.is_none() {
229                break;
230            }
231        }
232
233        if identifiers.is_empty() {
234            return Err(Diagnostic::error(start_token.loc(), "empty decl statement")
235                .primary_label("this decl does not declare anything"));
236        }
237
238        let last_ident = identifiers.last().unwrap().clone();
239
240        Ok(Statement::Declaration(identifiers).between(
241            parser.file_id(),
242            &start_token.span,
243            &last_ident,
244        ))
245    }
246}
247
248pub(crate) struct LabelParser {}
249
250impl KeywordPeekingParser<Loc<Statement>> for LabelParser {
251    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
252        |kind| matches!(kind, TokenKind::Label(_))
253    }
254
255    fn parse(
256        &self,
257        parser: &mut Parser,
258        attributes: &AttributeList,
259        visibility: &Loc<Visibility>,
260    ) -> Result<Loc<Statement>> {
261        let tok @ Token {
262            kind: TokenKind::Label(l),
263            ..
264        } = &parser.eat_unconditional()?
265        else {
266            diag_bail!(
267                parser.peek()?,
268                "Label parser was called but it did not get a label"
269            )
270        };
271        parser.disallow_attributes(attributes, &tok)?;
272        parser.disallow_visibility(visibility, &tok)?;
273
274        Ok(Statement::Label(l.clone().at(parser.file_id(), &tok.span))
275            .at(parser.file_id(), &tok.span))
276    }
277}
278
279pub(crate) struct AssertParser {}
280
281impl KeywordPeekingParser<Loc<Statement>> for AssertParser {
282    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
283        |kind| kind == &TokenKind::Assert
284    }
285
286    fn parse(
287        &self,
288        parser: &mut Parser,
289        attributes: &AttributeList,
290        visibility: &Loc<Visibility>,
291    ) -> Result<Loc<Statement>> {
292        let tok = parser.eat_unconditional()?;
293        parser.disallow_attributes(attributes, &tok)?;
294        parser.disallow_visibility(visibility, &tok)?;
295
296        let expr = parser.expression()?;
297
298        Ok(Statement::Assert(expr.clone()).between(parser.file_id(), &tok.span, &expr))
299    }
300}
301
302pub(crate) struct SetParser {}
303
304impl KeywordPeekingParser<Loc<Statement>> for SetParser {
305    fn is_leading_token(&self) -> fn(&TokenKind) -> bool {
306        |kind| kind == &TokenKind::Set
307    }
308
309    fn parse(
310        &self,
311        parser: &mut Parser,
312        attributes: &AttributeList,
313        visibility: &Loc<Visibility>,
314    ) -> Result<Loc<Statement>> {
315        let tok = parser.eat_unconditional()?;
316        parser.disallow_attributes(attributes, &tok)?;
317        parser.disallow_visibility(visibility, &tok)?;
318
319        let target = parser.expression()?;
320
321        parser.eat(&TokenKind::Assignment)?;
322
323        let value = parser.expression()?;
324
325        Ok(Statement::Set {
326            target,
327            value: value.clone(),
328        }
329        .between(parser.file_id(), &tok.span, &value))
330    }
331}