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) => {
15 if let Some(inner) = optional_sugar_inner(types) {
16 return format!("{}?", format_type(inner));
17 }
18 types
19 .iter()
20 .map(format_type)
21 .collect::<Vec<_>>()
22 .join(" | ")
23 }
24 TypeExpr::Intersection(types) => types
25 .iter()
26 .map(|m| match m {
27 TypeExpr::Union(members) if optional_sugar_inner(members).is_some() => {
30 format_type(m)
31 }
32 TypeExpr::Union(_) => format!("({})", format_type(m)),
34 _ => format_type(m),
35 })
36 .collect::<Vec<_>>()
37 .join(" & "),
38 TypeExpr::Shape(fields) => {
39 let inner: Vec<String> = fields
40 .iter()
41 .map(|f| {
42 let opt = if f.optional { "?" } else { "" };
43 format!("{}{opt}: {}", f.name, format_type(&f.type_expr))
44 })
45 .collect();
46 format!("{{{}}}", inner.join(", "))
47 }
48 TypeExpr::OpenShape { fields, rests } => {
49 let mut parts: Vec<String> = fields
50 .iter()
51 .map(|f| {
52 let opt = if f.optional { "?" } else { "" };
53 format!("{}{opt}: {}", f.name, format_type(&f.type_expr))
54 })
55 .collect();
56 for rest in rests {
57 parts.push(format!("...{}", format_type(rest)));
58 }
59 format!("{{{}}}", parts.join(", "))
60 }
61 TypeExpr::List(inner) => format!("list<{}>", format_type(inner)),
62 TypeExpr::Tuple(elements) => {
63 let elements = elements
64 .iter()
65 .map(format_type)
66 .collect::<Vec<_>>()
67 .join(", ");
68 format!("tuple<{elements}>")
69 }
70 TypeExpr::Iter(inner) => format!("iter<{}>", format_type(inner)),
71 TypeExpr::Generator(inner) => format!("Generator<{}>", format_type(inner)),
72 TypeExpr::Stream(inner) => format!("Stream<{}>", format_type(inner)),
73 TypeExpr::DictType(k, v) => format!("dict<{}, {}>", format_type(k), format_type(v)),
74 TypeExpr::Applied { name, args } => {
75 let args_str = args.iter().map(format_type).collect::<Vec<_>>().join(", ");
76 format!("{name}<{args_str}>")
77 }
78 TypeExpr::FnType {
79 params,
80 return_type,
81 } => {
82 let params_str = params
83 .iter()
84 .map(format_type)
85 .collect::<Vec<_>>()
86 .join(", ");
87 format!("fn({}) -> {}", params_str, format_type(return_type))
88 }
89 TypeExpr::Never => "never".to_string(),
90 TypeExpr::LitString(s) => format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")),
91 TypeExpr::LitInt(v) => v.to_string(),
92 TypeExpr::Owned(inner) => format!("owned<{}>", format_type(inner)),
93 }
94}
95
96pub fn shape_mismatch_detail(expected: &TypeExpr, actual: &TypeExpr) -> Option<String> {
100 if let (TypeExpr::Shape(ef), TypeExpr::Shape(af)) = (expected, actual) {
101 let mut details = Vec::new();
102 for field in ef {
103 if field.optional {
104 continue;
105 }
106 match af.iter().find(|f| f.name == field.name) {
107 None => details.push(format!(
108 "missing field '{}' ({})",
109 field.name,
110 format_type(&field.type_expr)
111 )),
112 Some(actual_field) => {
113 let e_str = format_type(&field.type_expr);
114 let a_str = format_type(&actual_field.type_expr);
115 if e_str != a_str {
116 details.push(format!(
117 "field '{}' has type {}, expected {}",
118 field.name, a_str, e_str
119 ));
120 }
121 }
122 }
123 }
124 if details.is_empty() {
125 None
126 } else {
127 Some(details.join("; "))
128 }
129 } else {
130 None
131 }
132}
133
134fn optional_sugar_inner(types: &[TypeExpr]) -> Option<&TypeExpr> {
141 if types.len() != 2 {
142 return None;
143 }
144 let nil_idx = types
145 .iter()
146 .position(|t| matches!(t, TypeExpr::Named(n) if n == "nil"))?;
147 let inner = &types[1 - nil_idx];
148 if matches!(
149 inner,
150 TypeExpr::Union(_) | TypeExpr::Intersection(_) | TypeExpr::FnType { .. }
151 ) {
152 return None;
153 }
154 if matches!(inner, TypeExpr::Named(n) if n == "nil") {
155 return None;
156 }
157 Some(inner)
158}
159
160pub(super) fn is_obvious_type(value: &SNode, _ty: &TypeExpr) -> bool {
163 matches!(
164 &value.node,
165 Node::IntLiteral(_)
166 | Node::FloatLiteral(_)
167 | Node::StringLiteral(_)
168 | Node::BoolLiteral(_)
169 | Node::NilLiteral
170 | Node::ListLiteral(_)
171 | Node::DictLiteral(_)
172 | Node::InterpolatedString(_)
173 )
174}