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::Shape(fields) => {
20 let inner: Vec<String> = fields
21 .iter()
22 .map(|f| {
23 let opt = if f.optional { "?" } else { "" };
24 format!("{}{opt}: {}", f.name, format_type(&f.type_expr))
25 })
26 .collect();
27 format!("{{{}}}", inner.join(", "))
28 }
29 TypeExpr::List(inner) => format!("list<{}>", format_type(inner)),
30 TypeExpr::Iter(inner) => format!("iter<{}>", format_type(inner)),
31 TypeExpr::DictType(k, v) => format!("dict<{}, {}>", format_type(k), format_type(v)),
32 TypeExpr::Applied { name, args } => {
33 let args_str = args.iter().map(format_type).collect::<Vec<_>>().join(", ");
34 format!("{name}<{args_str}>")
35 }
36 TypeExpr::FnType {
37 params,
38 return_type,
39 } => {
40 let params_str = params
41 .iter()
42 .map(format_type)
43 .collect::<Vec<_>>()
44 .join(", ");
45 format!("fn({}) -> {}", params_str, format_type(return_type))
46 }
47 TypeExpr::Never => "never".to_string(),
48 TypeExpr::LitString(s) => format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")),
49 TypeExpr::LitInt(v) => v.to_string(),
50 }
51}
52
53pub fn shape_mismatch_detail(expected: &TypeExpr, actual: &TypeExpr) -> Option<String> {
57 if let (TypeExpr::Shape(ef), TypeExpr::Shape(af)) = (expected, actual) {
58 let mut details = Vec::new();
59 for field in ef {
60 if field.optional {
61 continue;
62 }
63 match af.iter().find(|f| f.name == field.name) {
64 None => details.push(format!(
65 "missing field '{}' ({})",
66 field.name,
67 format_type(&field.type_expr)
68 )),
69 Some(actual_field) => {
70 let e_str = format_type(&field.type_expr);
71 let a_str = format_type(&actual_field.type_expr);
72 if e_str != a_str {
73 details.push(format!(
74 "field '{}' has type {}, expected {}",
75 field.name, a_str, e_str
76 ));
77 }
78 }
79 }
80 }
81 if details.is_empty() {
82 None
83 } else {
84 Some(details.join("; "))
85 }
86 } else {
87 None
88 }
89}
90
91pub(super) fn is_obvious_type(value: &SNode, _ty: &TypeExpr) -> bool {
94 matches!(
95 &value.node,
96 Node::IntLiteral(_)
97 | Node::FloatLiteral(_)
98 | Node::StringLiteral(_)
99 | Node::BoolLiteral(_)
100 | Node::NilLiteral
101 | Node::ListLiteral(_)
102 | Node::DictLiteral(_)
103 | Node::InterpolatedString(_)
104 )
105}