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