spade_parser/
items.rs

1use spade_ast::{
2    AttributeList, Enum, Expression, ImplBlock, Struct, TraitDef, TraitSpec, TypeDeclKind,
3    TypeDeclaration, TypeSpec, Unit, UseStatement,
4};
5use spade_ast::{Item, Module};
6use spade_common::location_info::{AsLabel, Loc, WithLocation};
7use spade_diagnostics::Diagnostic;
8
9use crate::error::UnexpectedToken;
10use crate::{
11    error::CSErrorTransformations, lexer::TokenKind, KeywordPeekingParser, Parser, Result,
12};
13
14pub(crate) struct UnitParser {}
15
16impl KeywordPeekingParser<Loc<Unit>> for UnitParser {
17    fn leading_tokens(&self) -> Vec<TokenKind> {
18        vec![
19            TokenKind::Function,
20            TokenKind::Entity,
21            TokenKind::Pipeline,
22            TokenKind::Extern,
23        ]
24    }
25
26    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<Loc<Unit>> {
27        let head = if let Some(head) = parser.unit_head(attributes)? {
28            head
29        } else {
30            panic!("Matched unit head but matches! returned true")
31        };
32
33        parser.set_item_context(head.unit_kind.clone())?;
34
35        let allow_stages = head.unit_kind.is_pipeline();
36        let after_head_token = parser.peek()?; // will be ; if it's extern
37        let block_result = (|| {
38            if let Some(block) = parser.block(allow_stages)? {
39                let (block, block_span) = block.separate();
40                Ok((Some(block), block_span))
41            } else if parser.peek_kind(&TokenKind::Semi)? {
42                let tok = parser.eat_unconditional()?;
43
44                Ok((None, ().at(parser.file_id, &tok.span).span))
45            } else {
46                let next = parser.peek()?;
47                Err(Diagnostic::error(
48                    next.clone(),
49                    format!("Unexpected `{}`, expected body or `;`", next.kind.as_str(),),
50                )
51                .primary_label(format!("Unexpected {}", &next.kind.as_str()))
52                .secondary_label(&head, format!("Expected body for this {}", head.unit_kind)))
53            }
54        })();
55
56        let (block, block_span) = match block_result {
57            Ok(inner) => inner,
58            Err(e) => {
59                parser.clear_item_context();
60                return Err(e);
61            }
62        };
63
64        if head.extern_token.is_some() && block.is_some() {
65            return Err(Diagnostic::error(
66                head.extern_token.unwrap(),
67                "`extern` units cannot have a body",
68            )
69            .span_suggest_remove("Remove this body", block.as_ref().unwrap()));
70        } else if head.extern_token.is_none() & block.is_none() {
71            return Err(Diagnostic::error(
72                &head,
73                format!("This {} is missing a body", head.unit_kind),
74            )
75            .span_suggest_replace(
76                format!("Did you forget to add the {}'s body?", head.unit_kind),
77                after_head_token,
78                " {}",
79            )
80            .span_suggest_insert_before(
81                format!("Or did you mean to declare an `extern` {}?", head.unit_kind),
82                &head.unit_kind,
83                "extern ",
84            ));
85        }
86
87        parser.clear_item_context();
88
89        Ok(Unit {
90            head: head.inner.clone(),
91            body: block.map(|inner| inner.map(|inner| Expression::Block(Box::new(inner)))),
92        }
93        .between(parser.file_id, &head, &block_span))
94    }
95}
96
97pub(crate) struct TraitDefParser {}
98
99impl KeywordPeekingParser<Loc<TraitDef>> for TraitDefParser {
100    fn leading_tokens(&self) -> Vec<TokenKind> {
101        vec![TokenKind::Trait]
102    }
103
104    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<Loc<TraitDef>> {
105        let start_token = parser.eat_unconditional()?;
106        parser.disallow_attributes(attributes, &start_token)?;
107
108        let name = parser.identifier()?;
109
110        let type_params = parser.generics_list()?;
111
112        let where_clauses = parser.where_clauses()?;
113
114        let mut result = TraitDef {
115            name,
116            type_params,
117            where_clauses,
118            methods: vec![],
119        };
120
121        parser.eat(&TokenKind::OpenBrace)?;
122
123        while let Some(decl) = parser.unit_head(&AttributeList::empty())? {
124            result.methods.push(decl);
125            parser.eat(&TokenKind::Semi)?;
126        }
127        let end_token = parser.eat(&TokenKind::CloseBrace)?;
128
129        Ok(result.between(parser.file_id, &start_token.span, &end_token.span))
130    }
131}
132
133pub(crate) struct ImplBlockParser {}
134
135impl KeywordPeekingParser<Loc<ImplBlock>> for ImplBlockParser {
136    fn leading_tokens(&self) -> Vec<TokenKind> {
137        vec![TokenKind::Impl]
138    }
139
140    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<Loc<ImplBlock>> {
141        let start_token = parser.eat_unconditional()?;
142        parser.disallow_attributes(attributes, &start_token)?;
143
144        let type_params = parser.generics_list()?;
145
146        let trait_or_target_path = parser.type_spec()?;
147
148        let (r#trait, target) = if parser.peek_and_eat(&TokenKind::For)?.is_some() {
149            let (trait_path, params) = match trait_or_target_path.inner.clone() {
150                TypeSpec::Named(p, params) => (p, params),
151                other => {
152                    return Err(Diagnostic::error(
153                        trait_or_target_path,
154                        format!("{other} is not a trait"),
155                    ))
156                }
157            };
158            let r#trait = TraitSpec {
159                path: trait_path.clone(),
160                type_params: params,
161            }
162            .at_loc(&trait_or_target_path);
163
164            let target = parser.type_spec()?;
165
166            (Some(r#trait), target)
167        } else {
168            let target = trait_or_target_path;
169            (None, target)
170        };
171
172        let where_clauses = parser.where_clauses()?;
173
174        let (body, body_span) = parser.surrounded(
175            &TokenKind::OpenBrace,
176            Parser::impl_body,
177            &TokenKind::CloseBrace,
178        )?;
179
180        Ok(ImplBlock {
181            r#trait,
182            type_params,
183            target,
184            where_clauses,
185            units: body,
186        }
187        .between(parser.file_id, &start_token.span, &body_span.span))
188    }
189}
190
191pub(crate) struct StructParser {}
192
193impl KeywordPeekingParser<Loc<TypeDeclaration>> for StructParser {
194    fn leading_tokens(&self) -> Vec<TokenKind> {
195        vec![TokenKind::Struct]
196    }
197
198    fn parse(
199        &self,
200        parser: &mut Parser,
201        attributes: &AttributeList,
202    ) -> Result<Loc<TypeDeclaration>> {
203        let start_token = parser.eat_unconditional()?;
204
205        let port_keyword = parser
206            .peek_and_eat(&TokenKind::Port)?
207            .map(|tok| ().at(parser.file_id, &tok.span()));
208
209        let name = parser.identifier()?;
210
211        let type_params = parser.generics_list()?;
212
213        let (members, members_loc) = parser.surrounded(
214            &TokenKind::OpenBrace,
215            Parser::type_parameter_list,
216            &TokenKind::CloseBrace,
217        )?;
218        let members = members.at_loc(&members_loc);
219
220        let result = TypeDeclaration {
221            name: name.clone(),
222            kind: TypeDeclKind::Struct(
223                Struct {
224                    name,
225                    members,
226                    port_keyword,
227                    attributes: attributes.clone(),
228                }
229                .between(parser.file_id, &start_token.span, &members_loc),
230            ),
231            generic_args: type_params,
232        }
233        .between(parser.file_id, &start_token.span, &members_loc);
234
235        Ok(result)
236    }
237}
238
239pub(crate) struct EnumParser {}
240
241impl KeywordPeekingParser<Loc<TypeDeclaration>> for EnumParser {
242    fn leading_tokens(&self) -> Vec<TokenKind> {
243        vec![TokenKind::Enum]
244    }
245
246    fn parse(
247        &self,
248        parser: &mut Parser,
249        attributes: &AttributeList,
250    ) -> Result<Loc<TypeDeclaration>> {
251        let start_token = parser.eat_unconditional()?;
252
253        let name = parser.identifier()?;
254
255        let type_params = parser.generics_list()?;
256
257        let (options, options_loc) = parser.surrounded(
258            &TokenKind::OpenBrace,
259            |s: &mut Parser| {
260                s.comma_separated(Parser::enum_variant, &TokenKind::CloseBrace)
261                    .no_context()
262            },
263            &TokenKind::CloseBrace,
264        )?;
265
266        let result = TypeDeclaration {
267            name: name.clone(),
268            kind: TypeDeclKind::Enum(
269                Enum {
270                    name,
271                    variants: options,
272                    attributes: attributes.clone(),
273                }
274                .between(parser.file_id, &start_token.span, &options_loc),
275            ),
276            generic_args: type_params,
277        }
278        .between(parser.file_id, &start_token.span, &options_loc);
279
280        Ok(result)
281    }
282}
283
284pub(crate) struct ModuleParser {}
285
286impl KeywordPeekingParser<Item> for ModuleParser {
287    fn leading_tokens(&self) -> Vec<TokenKind> {
288        vec![TokenKind::Mod]
289    }
290
291    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<Item> {
292        let start = parser.eat_unconditional()?;
293        parser.disallow_attributes(attributes, &start)?;
294
295        let name = parser.identifier()?;
296
297        if parser.peek_kind(&TokenKind::OpenBrace)? {
298            let open_brace = parser.peek()?;
299            let (body, end) = parser.surrounded(
300                &TokenKind::OpenBrace,
301                Parser::module_body,
302                &TokenKind::CloseBrace,
303            )?;
304            Ok(Item::Module(
305                Module {
306                    name,
307                    body: body.between(parser.file_id, &open_brace.span, &end.span),
308                }
309                .between(parser.file_id, &start, &end),
310            ))
311        } else if parser.peek_and_eat(&TokenKind::Semi)?.is_some() {
312            Ok(Item::ExternalMod(name))
313        } else {
314            Err(UnexpectedToken {
315                got: parser.peek()?,
316                expected: vec![";", "{"],
317            }
318            .into())
319        }
320    }
321}
322
323pub(crate) struct UseParser {}
324
325impl KeywordPeekingParser<Loc<UseStatement>> for UseParser {
326    fn leading_tokens(&self) -> Vec<TokenKind> {
327        vec![TokenKind::Use]
328    }
329
330    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<Loc<UseStatement>> {
331        let start = parser.eat_unconditional()?;
332        parser.disallow_attributes(attributes, &start)?;
333
334        let path = parser.path()?;
335
336        let alias = if (parser.peek_and_eat(&TokenKind::As)?).is_some() {
337            Some(parser.identifier()?)
338        } else {
339            None
340        };
341
342        let end = parser.eat(&TokenKind::Semi)?;
343
344        Ok(UseStatement { path, alias }.between(parser.file_id, &start.span(), &end.span()))
345    }
346}