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()?; 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}