1use cove_diag::{Span, Spanned};
10
11pub type Ident = Spanned<String>;
12
13#[derive(Clone, Debug)]
16pub struct SourceUnit {
17 pub uses: Vec<Use>,
18 pub items: Vec<Item>,
19 pub span: Span,
20}
21
22#[derive(Clone, Debug)]
24pub struct Use {
25 pub path: Vec<Ident>,
26 pub span: Span,
27}
28
29#[derive(Clone, Debug)]
31pub struct Item {
32 pub doc: Option<String>,
33 pub exported: bool,
34 pub is_test: bool,
41 pub is_opaque: bool,
49 pub kind: ItemKind,
50 pub span: Span,
51}
52
53#[derive(Clone, Debug)]
54pub enum ItemKind {
55 Fn(FnDecl),
56 Struct(StructDecl),
57 Enum(EnumDecl),
58 Trait(TraitDecl),
60 Impl(ImplBlock),
61 TypeAlias(TypeAlias),
63}
64
65#[derive(Clone, Debug)]
70pub struct GenericParam {
71 pub name: Ident,
72 pub bounds: Vec<Ident>,
74 pub span: Span,
75}
76
77impl GenericParam {
78 pub fn unbounded(name: Ident) -> GenericParam {
80 let span = name.span;
81 GenericParam {
82 name,
83 bounds: Vec::new(),
84 span,
85 }
86 }
87}
88
89impl std::fmt::Display for GenericParam {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 f.write_str(&self.name.node)?;
93 for (i, bound) in self.bounds.iter().enumerate() {
94 f.write_str(if i == 0 { ": " } else { " + " })?;
95 f.write_str(&bound.node)?;
96 }
97 Ok(())
98 }
99}
100
101#[derive(Clone, Debug)]
102pub struct FnDecl {
103 pub name: Ident,
104 pub is_async: bool,
105 pub generics: Vec<GenericParam>,
106 pub receiver: Option<Receiver>,
108 pub params: Vec<Param>,
109 pub return_type: Option<Type>,
110 pub body: Block,
111 pub span: Span,
112}
113
114#[derive(Clone, Copy, Debug)]
116pub struct Receiver {
117 pub is_var: bool,
119 pub span: Span,
120}
121
122#[derive(Clone, Debug)]
123pub struct Param {
124 pub is_var: bool,
127 pub name: Ident,
128 pub ty: Option<Type>,
129 pub variadic: bool,
131 pub default: Option<Expr>,
132 pub span: Span,
133}
134
135#[derive(Clone, Debug)]
136pub struct StructDecl {
137 pub name: Ident,
138 pub generics: Vec<GenericParam>,
139 pub fields: Vec<Field>,
140 pub span: Span,
141}
142
143#[derive(Clone, Debug)]
144pub struct Field {
145 pub doc: Option<String>,
146 pub name: Ident,
147 pub ty: Type,
148 pub span: Span,
149}
150
151#[derive(Clone, Debug)]
152pub struct EnumDecl {
153 pub name: Ident,
154 pub generics: Vec<GenericParam>,
155 pub cases: Vec<EnumCase>,
156 pub span: Span,
157}
158
159#[derive(Clone, Debug)]
160pub struct EnumCase {
161 pub doc: Option<String>,
162 pub name: Ident,
163 pub payload: Vec<Type>,
165 pub span: Span,
166}
167
168#[derive(Clone, Debug)]
173pub struct TraitDecl {
174 pub name: Ident,
175 pub methods: Vec<TraitMethod>,
176 pub span: Span,
177}
178
179#[derive(Clone, Debug)]
184pub struct TraitMethod {
185 pub doc: Option<String>,
186 pub name: Ident,
187 pub is_async: bool,
188 pub receiver: Option<Receiver>,
191 pub params: Vec<Param>,
192 pub return_type: Option<Type>,
193 pub default: Option<Block>,
194 pub span: Span,
195}
196
197#[derive(Clone, Debug)]
200pub struct ImplBlock {
201 pub trait_name: Option<Ident>,
204 pub type_name: Ident,
205 pub generics: Vec<GenericParam>,
206 pub items: Vec<Item>,
207 pub span: Span,
208}
209
210#[derive(Clone, Debug)]
211pub struct TypeAlias {
212 pub name: Ident,
213 pub generics: Vec<GenericParam>,
214 pub ty: Type,
215 pub span: Span,
216}
217
218#[derive(Clone, Debug)]
220pub struct Type {
221 pub kind: TypeKind,
222 pub span: Span,
223}
224
225#[derive(Clone, Debug)]
226pub enum TypeKind {
227 Named { path: Vec<Ident>, args: Vec<Type> },
229 Fn {
231 is_async: bool,
232 params: Vec<Param>,
233 return_type: Option<Box<Type>>,
234 },
235 Dyn(Ident),
238 Unit,
240}
241
242impl std::fmt::Display for Type {
246 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247 match &self.kind {
248 TypeKind::Unit => write!(f, "()"),
249 TypeKind::Dyn(name) => write!(f, "dyn {}", name.node),
250 TypeKind::Named { path, args } => {
251 let path = path
252 .iter()
253 .map(|segment| segment.node.as_str())
254 .collect::<Vec<_>>()
255 .join(".");
256 write!(f, "{path}")?;
257 if !args.is_empty() {
258 let args = args
259 .iter()
260 .map(|arg| arg.to_string())
261 .collect::<Vec<_>>()
262 .join(", ");
263 write!(f, "<{args}>")?;
264 }
265 Ok(())
266 }
267 TypeKind::Fn {
268 is_async,
269 params,
270 return_type,
271 } => {
272 if *is_async {
273 write!(f, "async ")?;
274 }
275 let params = params
276 .iter()
277 .map(|param| param.to_string())
278 .collect::<Vec<_>>()
279 .join(", ");
280 write!(f, "fn({params})")?;
281 if let Some(return_type) = return_type {
282 write!(f, " -> {return_type}")?;
283 }
284 Ok(())
285 }
286 }
287 }
288}
289
290impl std::fmt::Display for Param {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 let Some(ty) = &self.ty else {
297 return write!(f, "{}", self.name.node);
298 };
299 if self.is_var {
300 write!(f, "var ")?;
301 }
302 if !self.name.node.is_empty() {
303 write!(f, "{}: ", self.name.node)?;
304 }
305 write!(f, "{ty}")?;
306 if self.variadic {
307 write!(f, "...")?;
308 }
309 if let Some(default) = &self.default {
313 write!(f, " = {}", crate::format::format_expr(default))?;
314 }
315 Ok(())
316 }
317}
318
319#[derive(Clone, Debug)]
320pub struct Block {
321 pub statements: Vec<Stmt>,
322 pub tail: Option<Box<Expr>>,
324 pub span: Span,
325}
326
327#[derive(Clone, Debug)]
328pub struct Stmt {
329 pub kind: StmtKind,
330 pub span: Span,
331}
332
333#[derive(Clone, Debug)]
334pub enum StmtKind {
335 Let {
337 is_var: bool,
338 name: Ident,
339 ty: Option<Type>,
340 value: Expr,
341 },
342 Expr(Expr),
343 Item(Box<Item>),
345}
346
347#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
366pub struct ExprId(pub u32);
367
368impl ExprId {
369 pub const UNSET: ExprId = ExprId(u32::MAX);
375}
376
377#[derive(Clone, Debug)]
378pub struct Expr {
379 pub id: ExprId,
382 pub kind: ExprKind,
383 pub span: Span,
384}
385
386#[derive(Clone, Debug)]
388pub struct Arg {
389 pub label: Option<Ident>,
391 pub is_var: bool,
393 pub spread: bool,
395 pub value: Expr,
396 pub span: Span,
397}
398
399#[derive(Clone, Debug)]
400pub enum ExprKind {
401 Int(i64),
402 Float(f64),
403 Bool(bool),
404 Duration(i64),
405 Str(Vec<StrPart>),
407 Unit,
409 Ident(String),
411 ArrayLit(Vec<Expr>),
413 Field {
415 base: Box<Expr>,
416 name: Ident,
417 },
418 Call {
420 callee: Box<Expr>,
421 generics: Vec<Type>,
422 args: Vec<Arg>,
423 trailing: Option<Box<Expr>>,
425 },
426 Unary {
427 op: UnaryOp,
428 operand: Box<Expr>,
429 },
430 Binary {
431 op: BinaryOp,
432 lhs: Box<Expr>,
433 rhs: Box<Expr>,
434 },
435 Assign {
437 op: Option<BinaryOp>,
438 target: Box<Expr>,
439 value: Box<Expr>,
440 },
441 Try(Box<Expr>),
443 Await(Box<Expr>),
444 Block(Block),
445 If {
446 condition: Box<Expr>,
447 then_branch: Block,
448 else_branch: Option<Box<Expr>>,
449 },
450 Match {
452 scrutinee: Box<Expr>,
453 arms: Vec<MatchArm>,
454 },
455 For {
459 binding: Ident,
460 iterable: Box<Expr>,
461 body: Block,
462 },
463 While {
464 condition: Box<Expr>,
465 body: Block,
466 },
467 Return(Option<Box<Expr>>),
468 Break(Option<Box<Expr>>),
472 Continue,
475 Lambda {
477 is_async: bool,
478 params: Vec<Param>,
479 body: Block,
480 },
481 Scope {
483 name: Ident,
484 body: Block,
485 },
486 Range {
488 start: Box<Expr>,
489 end: Box<Expr>,
490 inclusive_end: bool,
491 },
492}
493
494#[derive(Clone, Debug)]
496pub enum StrPart {
497 Text(String),
498 Interpolation(Expr),
499}
500
501#[derive(Clone, Copy, Debug, PartialEq, Eq)]
502pub enum UnaryOp {
503 Not,
504 Neg,
505}
506
507#[derive(Clone, Copy, Debug, PartialEq, Eq)]
508pub enum BinaryOp {
509 Add,
510 Sub,
511 Mul,
512 Div,
513 Rem,
514 Eq,
515 Ne,
516 Lt,
517 Le,
518 Gt,
519 Ge,
520 Is,
523 And,
524 Or,
525}
526
527#[derive(Clone, Debug)]
528pub struct MatchArm {
529 pub pattern: Pattern,
530 pub body: Expr,
531 pub span: Span,
532}
533
534#[derive(Clone, Debug)]
535pub struct Pattern {
536 pub kind: PatternKind,
537 pub span: Span,
538}
539
540#[derive(Clone, Debug)]
541pub enum PatternKind {
542 Wildcard,
544 Binding(String),
546 Literal(Expr),
548 Variant {
550 path: Vec<Ident>,
551 payload: Vec<Pattern>,
552 },
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558
559 fn span() -> Span {
560 Span::new(cove_diag::FileId(0), 0, 0)
561 }
562
563 fn ident(name: &str) -> Ident {
564 Spanned::new(name.to_string(), span())
565 }
566
567 fn named(path: &[&str], args: Vec<Type>) -> Type {
568 Type {
569 kind: TypeKind::Named {
570 path: path.iter().map(|s| ident(s)).collect(),
571 args,
572 },
573 span: span(),
574 }
575 }
576
577 fn unit() -> Type {
578 Type {
579 kind: TypeKind::Unit,
580 span: span(),
581 }
582 }
583
584 fn param(name: &str, ty: Option<Type>) -> Param {
585 Param {
586 is_var: false,
587 name: ident(name),
588 ty,
589 variadic: false,
590 default: None,
591 span: span(),
592 }
593 }
594
595 #[test]
596 fn displays_a_plain_named_type() {
597 assert_eq!(named(&["Int"], vec![]).to_string(), "Int");
598 }
599
600 #[test]
601 fn displays_a_named_type_with_one_argument() {
602 assert_eq!(
603 named(&["Array"], vec![named(&["String"], vec![])]).to_string(),
604 "Array<String>"
605 );
606 }
607
608 #[test]
609 fn displays_a_dotted_path() {
610 assert_eq!(
611 named(&["http", "Request"], vec![]).to_string(),
612 "http.Request"
613 );
614 }
615
616 #[test]
617 fn displays_a_named_type_with_two_arguments() {
618 assert_eq!(
619 named(
620 &["Result"],
621 vec![named(&["Unit"], vec![]), named(&["Error"], vec![])]
622 )
623 .to_string(),
624 "Result<Unit, Error>"
625 );
626 }
627
628 #[test]
629 fn displays_nested_generics() {
630 let ty = named(
631 &["Map"],
632 vec![
633 named(&["String"], vec![]),
634 named(&["Array"], vec![named(&["EventHandler"], vec![])]),
635 ],
636 );
637 assert_eq!(ty.to_string(), "Map<String, Array<EventHandler>>");
638 }
639
640 #[test]
641 fn displays_unit() {
642 assert_eq!(unit().to_string(), "()");
643 }
644
645 #[test]
646 fn displays_a_fn_type_with_named_params_and_return_type() {
647 let ty = Type {
648 kind: TypeKind::Fn {
649 is_async: false,
650 params: vec![
651 param("name", Some(named(&["Type"], vec![]))),
652 param("other", Some(named(&["Type"], vec![]))),
653 ],
654 return_type: Some(Box::new(named(&["Ret"], vec![]))),
655 },
656 span: span(),
657 };
658 assert_eq!(ty.to_string(), "fn(name: Type, other: Type) -> Ret");
659 }
660
661 #[test]
662 fn displays_an_async_fn_type() {
663 let ty = Type {
664 kind: TypeKind::Fn {
665 is_async: true,
666 params: vec![],
667 return_type: None,
668 },
669 span: span(),
670 };
671 assert_eq!(ty.to_string(), "async fn()");
672 }
673
674 #[test]
675 fn displays_a_fn_type_with_no_return_type() {
676 let ty = Type {
677 kind: TypeKind::Fn {
678 is_async: false,
679 params: vec![param("x", Some(named(&["Int"], vec![])))],
680 return_type: None,
681 },
682 span: span(),
683 };
684 assert_eq!(ty.to_string(), "fn(x: Int)");
685 }
686
687 #[test]
688 fn displays_an_unnamed_fn_type_param() {
689 let ty = Type {
690 kind: TypeKind::Fn {
691 is_async: false,
692 params: vec![param("", Some(named(&["String"], vec![])))],
693 return_type: None,
694 },
695 span: span(),
696 };
697 assert_eq!(ty.to_string(), "fn(String)");
698 }
699
700 #[test]
701 fn displays_a_var_and_variadic_fn_type_param() {
702 let mut p = param("items", Some(named(&["Int"], vec![])));
703 p.is_var = true;
704 p.variadic = true;
705 let ty = Type {
706 kind: TypeKind::Fn {
707 is_async: false,
708 params: vec![p],
709 return_type: None,
710 },
711 span: span(),
712 };
713 assert_eq!(ty.to_string(), "fn(var items: Int...)");
714 }
715
716 #[test]
717 fn displays_a_lambda_param_with_no_type() {
718 assert_eq!(param("x", None).to_string(), "x");
719 }
720}
721
722#[cfg(test)]
723mod param_tests {
724 use super::*;
725 use cove_diag::SourceMap;
726
727 fn parse_one(source: &str) -> SourceUnit {
728 let mut sources = SourceMap::new();
729 let file = sources.add("test.cove", source.to_string());
730 crate::parse_file(&sources, file).expect("source parses")
731 }
732
733 fn signature(source: &str) -> String {
734 let unit = parse_one(source);
735 let ItemKind::Fn(decl) = &unit.items[0].kind else {
736 panic!("expected a function");
737 };
738 decl.params
739 .iter()
740 .map(ToString::to_string)
741 .collect::<Vec<_>>()
742 .join(", ")
743 }
744
745 #[test]
746 fn a_parameter_renders_its_default() {
747 assert_eq!(
748 signature("export fn f(name: String = \"world\") {\n}\n"),
749 "name: String = \"world\""
750 );
751 assert_eq!(
752 signature("export fn f(count: Int = 1) {\n}\n"),
753 "count: Int = 1"
754 );
755 }
756
757 #[test]
758 fn a_parameter_without_a_default_renders_without_one() {
759 assert_eq!(
760 signature("export fn f(name: String) {\n}\n"),
761 "name: String"
762 );
763 }
764
765 #[test]
766 fn var_and_variadic_survive_alongside_a_default() {
767 assert_eq!(
768 signature("export fn f(var out: Int) {\n}\n"),
769 "var out: Int"
770 );
771 assert_eq!(
772 signature("export fn f(items: Int...) {\n}\n"),
773 "items: Int..."
774 );
775 }
776}