harn_parser/typechecker/
format.rs1use crate::ast::*;
9
10pub fn format_type(ty: &TypeExpr) -> String {
12 match ty {
13 TypeExpr::Named(n) => n.clone(),
14 TypeExpr::Union(types) => types
15 .iter()
16 .map(format_type)
17 .collect::<Vec<_>>()
18 .join(" | "),
19 TypeExpr::Intersection(types) => types
20 .iter()
21 .map(|m| match m {
22 TypeExpr::Union(_) => format!("({})", format_type(m)),
24 _ => format_type(m),
25 })
26 .collect::<Vec<_>>()
27 .join(" & "),
28 TypeExpr::Shape(fields) => {
29 let inner: Vec<String> = fields
30 .iter()
31 .map(|f| {
32 let opt = if f.optional { "?" } else { "" };
33 format!("{}{opt}: {}", f.name, format_type(&f.type_expr))
34 })
35 .collect();
36 format!("{{{}}}", inner.join(", "))
37 }
38 TypeExpr::List(inner) => format!("list<{}>", format_type(inner)),
39 TypeExpr::Iter(inner) => format!("iter<{}>", format_type(inner)),
40 TypeExpr::Generator(inner) => format!("Generator<{}>", format_type(inner)),
41 TypeExpr::Stream(inner) => format!("Stream<{}>", format_type(inner)),
42 TypeExpr::DictType(k, v) => format!("dict<{}, {}>", format_type(k), format_type(v)),
43 TypeExpr::Applied { name, args } => {
44 let args_str = args.iter().map(format_type).collect::<Vec<_>>().join(", ");
45 format!("{name}<{args_str}>")
46 }
47 TypeExpr::FnType {
48 params,
49 return_type,
50 } => {
51 let params_str = params
52 .iter()
53 .map(format_type)
54 .collect::<Vec<_>>()
55 .join(", ");
56 format!("fn({}) -> {}", params_str, format_type(return_type))
57 }
58 TypeExpr::Never => "never".to_string(),
59 TypeExpr::LitString(s) => format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")),
60 TypeExpr::LitInt(v) => v.to_string(),
61 }
62}
63
64pub fn shape_mismatch_detail(expected: &TypeExpr, actual: &TypeExpr) -> Option<String> {
68 if let (TypeExpr::Shape(ef), TypeExpr::Shape(af)) = (expected, actual) {
69 let mut details = Vec::new();
70 for field in ef {
71 if field.optional {
72 continue;
73 }
74 match af.iter().find(|f| f.name == field.name) {
75 None => details.push(format!(
76 "missing field '{}' ({})",
77 field.name,
78 format_type(&field.type_expr)
79 )),
80 Some(actual_field) => {
81 let e_str = format_type(&field.type_expr);
82 let a_str = format_type(&actual_field.type_expr);
83 if e_str != a_str {
84 details.push(format!(
85 "field '{}' has type {}, expected {}",
86 field.name, a_str, e_str
87 ));
88 }
89 }
90 }
91 }
92 if details.is_empty() {
93 None
94 } else {
95 Some(details.join("; "))
96 }
97 } else {
98 None
99 }
100}
101
102pub(super) fn is_obvious_type(value: &SNode, _ty: &TypeExpr) -> bool {
105 matches!(
106 &value.node,
107 Node::IntLiteral(_)
108 | Node::FloatLiteral(_)
109 | Node::StringLiteral(_)
110 | Node::BoolLiteral(_)
111 | Node::NilLiteral
112 | Node::ListLiteral(_)
113 | Node::DictLiteral(_)
114 | Node::InterpolatedString(_)
115 )
116}