1use crate::types::*;
4use indexmap::IndexMap;
5
6#[derive(Debug, Clone)]
7pub struct TypeDef {
8 pub params: Vec<String>,
9 pub kind: TypeDefKind,
10}
11
12#[derive(Debug, Clone)]
13pub enum TypeDefKind {
14 Union(IndexMap<String, Option<Ty>>),
16 Alias(Ty),
18 Opaque,
20}
21
22#[derive(Debug, Clone, Default)]
23pub struct TypeEnv {
24 pub types: IndexMap<String, TypeDef>,
26 pub ctor_to_type: IndexMap<String, String>,
28}
29
30impl TypeEnv {
31 pub fn new_with_builtins() -> Self {
32 let mut e = TypeEnv::default();
33 let mut r_variants = IndexMap::new();
35 r_variants.insert("Ok".into(), Some(Ty::Var(0))); r_variants.insert("Err".into(), Some(Ty::Var(1))); e.types.insert("Result".into(), TypeDef {
38 params: vec!["T".into(), "E".into()],
39 kind: TypeDefKind::Union(r_variants),
40 });
41 e.ctor_to_type.insert("Ok".into(), "Result".into());
42 e.ctor_to_type.insert("Err".into(), "Result".into());
43
44 let mut o_variants = IndexMap::new();
46 o_variants.insert("Some".into(), Some(Ty::Var(0))); o_variants.insert("None".into(), None);
48 e.types.insert("Option".into(), TypeDef {
49 params: vec!["T".into()],
50 kind: TypeDefKind::Union(o_variants),
51 });
52 e.ctor_to_type.insert("Some".into(), "Option".into());
53 e.ctor_to_type.insert("None".into(), "Option".into());
54
55 e.types.insert("Nil".into(), TypeDef {
57 params: vec![],
58 kind: TypeDefKind::Alias(Ty::Unit),
59 });
60
61 e.types.insert("Map".into(), TypeDef { params: vec!["K".into(), "V".into()], kind: TypeDefKind::Opaque });
63 e.types.insert("Set".into(), TypeDef { params: vec!["T".into()], kind: TypeDefKind::Opaque });
64
65 let mut sp_variants = IndexMap::new();
69 sp_variants.insert("PStr".into(), Some(Ty::str()));
70 sp_variants.insert("PInt".into(), Some(Ty::int()));
71 sp_variants.insert("PFloat".into(), Some(Ty::Con("Float".into(), vec![])));
72 sp_variants.insert("PBool".into(), Some(Ty::Con("Bool".into(), vec![])));
73 sp_variants.insert("PNull".into(), None);
74 e.types.insert("SqlParam".into(), TypeDef {
75 params: vec![],
76 kind: TypeDefKind::Union(sp_variants),
77 });
78 for ctor in &["PStr", "PInt", "PFloat", "PBool", "PNull"] {
79 e.ctor_to_type.insert((*ctor).into(), "SqlParam".into());
80 }
81
82 e.types.insert("SqlTx".into(), TypeDef { params: vec![], kind: TypeDefKind::Opaque });
87
88 e.types.insert("Iter".into(), TypeDef { params: vec!["T".into()], kind: TypeDefKind::Opaque });
92
93 e.types.insert("Stream".into(), TypeDef { params: vec!["T".into()], kind: TypeDefKind::Opaque });
100
101 let mut tz_variants = IndexMap::new();
107 tz_variants.insert("Utc".into(), None);
108 tz_variants.insert("Local".into(), None);
109 tz_variants.insert("Offset".into(), Some(Ty::int()));
110 tz_variants.insert("Iana".into(), Some(Ty::str()));
111 e.types.insert("Tz".into(), TypeDef {
112 params: vec![],
113 kind: TypeDefKind::Union(tz_variants),
114 });
115 for ctor in &["Utc", "Local", "Offset", "Iana"] {
116 e.ctor_to_type.insert((*ctor).into(), "Tz".into());
117 }
118
119 let mut http_err_variants = IndexMap::new();
125 http_err_variants.insert("NetworkError".into(), Some(Ty::str()));
126 http_err_variants.insert("TimeoutError".into(), None);
127 http_err_variants.insert("TlsError".into(), Some(Ty::str()));
128 http_err_variants.insert("DecodeError".into(), Some(Ty::str()));
129 e.types.insert("HttpError".into(), TypeDef {
130 params: vec![],
131 kind: TypeDefKind::Union(http_err_variants),
132 });
133 for ctor in &["NetworkError", "TimeoutError", "TlsError", "DecodeError"] {
134 e.ctor_to_type.insert((*ctor).into(), "HttpError".into());
135 }
136
137 let mut req_fields = IndexMap::new();
146 req_fields.insert("method".into(), Ty::str());
147 req_fields.insert("url".into(), Ty::str());
148 req_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
149 req_fields.insert("body".into(), Ty::Con("Option".into(), vec![Ty::bytes()]));
150 req_fields.insert("timeout_ms".into(), Ty::Con("Option".into(), vec![Ty::int()]));
151 e.types.insert("HttpRequest".into(), TypeDef {
152 params: vec![],
153 kind: TypeDefKind::Alias(Ty::Record(req_fields)),
154 });
155
156 let mut resp_fields = IndexMap::new();
160 resp_fields.insert("status".into(), Ty::int());
161 resp_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
162 resp_fields.insert("body".into(), Ty::bytes());
163 e.types.insert("HttpResponse".into(), TypeDef {
164 params: vec![],
165 kind: TypeDefKind::Alias(Ty::Record(resp_fields)),
166 });
167
168 let mut mat_fields = IndexMap::new();
175 mat_fields.insert("rows".into(), Ty::int());
176 mat_fields.insert("cols".into(), Ty::int());
177 mat_fields.insert("data".into(), Ty::List(Box::new(Ty::float())));
178 e.types.insert("Matrix".into(), TypeDef {
179 params: vec![],
180 kind: TypeDefKind::Alias(Ty::Record(mat_fields)),
181 });
182
183 let mut net_req_fields = IndexMap::new();
187 net_req_fields.insert("method".into(), Ty::str());
188 net_req_fields.insert("path".into(), Ty::str());
189 net_req_fields.insert("query".into(), Ty::str());
190 net_req_fields.insert("body".into(), Ty::str());
191 net_req_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
192 e.types.insert("Request".into(), TypeDef {
193 params: vec![],
194 kind: TypeDefKind::Alias(Ty::Record(net_req_fields)),
195 });
196
197 let mut net_resp_fields = IndexMap::new();
200 net_resp_fields.insert("status".into(), Ty::int());
201 net_resp_fields.insert("body".into(), Ty::str());
202 net_resp_fields.insert("headers".into(), Ty::Con("Map".into(), vec![Ty::str(), Ty::str()]));
203 e.types.insert("Response".into(), TypeDef {
204 params: vec![],
205 kind: TypeDefKind::Alias(Ty::Record(net_resp_fields)),
206 });
207
208 let mut ws_conn_fields = IndexMap::new();
211 ws_conn_fields.insert("id".into(), Ty::str());
212 ws_conn_fields.insert("path".into(), Ty::str());
213 ws_conn_fields.insert("subprotocol".into(), Ty::str());
214 e.types.insert("WsConn".into(), TypeDef {
215 params: vec![],
216 kind: TypeDefKind::Alias(Ty::Record(ws_conn_fields)),
217 });
218
219 let mut ws_msg_variants = IndexMap::new();
221 ws_msg_variants.insert("WsText".into(), Some(Ty::str()));
222 ws_msg_variants.insert("WsBinary".into(), Some(Ty::List(Box::new(Ty::int()))));
223 ws_msg_variants.insert("WsPing".into(), None);
224 ws_msg_variants.insert("WsClose".into(), None);
225 e.types.insert("WsMessage".into(), TypeDef {
226 params: vec![],
227 kind: TypeDefKind::Union(ws_msg_variants),
228 });
229 for ctor in &["WsText", "WsBinary", "WsPing", "WsClose"] {
230 e.ctor_to_type.insert((*ctor).into(), "WsMessage".into());
231 }
232
233 let mut ws_act_variants = IndexMap::new();
238 ws_act_variants.insert("WsSend".into(), Some(Ty::str()));
239 ws_act_variants.insert("WsSendBinary".into(), Some(Ty::List(Box::new(Ty::int()))));
240 ws_act_variants.insert("WsNoOp".into(), None);
241 e.types.insert("WsAction".into(), TypeDef {
242 params: vec![],
243 kind: TypeDefKind::Union(ws_act_variants),
244 });
245 for ctor in &["WsSend", "WsSendBinary", "WsNoOp"] {
246 e.ctor_to_type.insert((*ctor).into(), "WsAction".into());
247 }
248
249 e
250 }
251
252 pub fn add_user_type(&mut self, name: &str, decl: lex_ast::TypeDecl) -> Result<(), String> {
253 match &decl.definition {
254 lex_ast::TypeExpr::Union { variants } => {
255 let mut vmap = IndexMap::new();
256 for v in variants {
257 let payload = v.payload.as_ref().map(|p| ty_from_canon(p, &decl.params));
258 vmap.insert(v.name.clone(), payload);
259 self.ctor_to_type.insert(v.name.clone(), name.to_string());
260 }
261 self.types.insert(name.to_string(), TypeDef {
262 params: decl.params.clone(),
263 kind: TypeDefKind::Union(vmap),
264 });
265 }
266 other => {
267 let ty = ty_from_canon_env(other, &decl.params, self);
268 self.types.insert(name.to_string(), TypeDef {
269 params: decl.params.clone(),
270 kind: TypeDefKind::Alias(ty),
271 });
272 }
273 }
274 Ok(())
275 }
276}
277
278pub fn ty_from_canon(t: &lex_ast::TypeExpr, params: &[String]) -> Ty {
282 match t {
283 lex_ast::TypeExpr::Named { name, args } => {
284 if let Some(idx) = params.iter().position(|p| p == name) {
286 if !args.is_empty() {
287 return Ty::Con(name.clone(), args.iter().map(|a| ty_from_canon(a, params)).collect());
289 }
290 return Ty::Var(idx as u32);
291 }
292 match name.as_str() {
294 "Int" => return Ty::int(),
295 "Float" => return Ty::float(),
296 "Bool" => return Ty::bool(),
297 "Str" => return Ty::str(),
298 "Bytes" => return Ty::bytes(),
299 "Unit" | "Nil" => return Ty::Unit,
300 "Never" => return Ty::Never,
301 "List" if args.len() == 1 => return Ty::List(Box::new(ty_from_canon(&args[0], params))),
302 "Tuple" => return Ty::Tuple(args.iter().map(|a| ty_from_canon(a, params)).collect()),
307 _ => {}
308 }
309 Ty::Con(name.clone(), args.iter().map(|a| ty_from_canon(a, params)).collect())
310 }
311 lex_ast::TypeExpr::Record { fields } => {
312 let mut m = IndexMap::new();
313 for f in fields { m.insert(f.name.clone(), ty_from_canon(&f.ty, params)); }
314 Ty::Record(m)
315 }
316 lex_ast::TypeExpr::Tuple { items } => Ty::Tuple(items.iter().map(|t| ty_from_canon(t, params)).collect()),
317 lex_ast::TypeExpr::Function { params: ps, effects, ret } => {
318 let effs = EffectSet {
320 concrete: {
321 let mut s = std::collections::BTreeSet::new();
322 for e in effects {
323 let arg = e.arg.as_ref().map(|a| match a {
324 lex_ast::EffectArg::Str { value } => crate::types::EffectArg::Str(value.clone()),
325 lex_ast::EffectArg::Int { value } => crate::types::EffectArg::Int(*value),
326 lex_ast::EffectArg::Ident { value } => crate::types::EffectArg::Ident(value.clone()),
327 });
328 s.insert(crate::types::EffectKind { name: e.name.clone(), arg });
329 }
330 s
331 },
332 var: None,
333 };
334 Ty::Function {
335 params: ps.iter().map(|t| ty_from_canon(t, params)).collect(),
336 effects: effs,
337 ret: Box::new(ty_from_canon(ret, params)),
338 }
339 }
340 lex_ast::TypeExpr::Union { .. } => {
341 Ty::Unit
343 }
344 lex_ast::TypeExpr::Refined { base, .. } => {
345 ty_from_canon(base, params)
355 }
356 lex_ast::TypeExpr::RecordWithSpreads { .. } => {
357 Ty::Unit
359 }
360 }
361}
362
363pub fn ty_from_canon_env(t: &lex_ast::TypeExpr, params: &[String], env: &TypeEnv) -> Ty {
367 match t {
368 lex_ast::TypeExpr::RecordWithSpreads { spreads, fields } => {
369 let mut m = IndexMap::new();
370 for spread_name in spreads {
371 if let Some(td) = env.types.get(spread_name.as_str()) {
372 if let TypeDefKind::Alias(Ty::Record(spread_fields)) = &td.kind {
373 for (k, v) in spread_fields {
374 m.insert(k.clone(), v.clone());
375 }
376 }
377 }
378 }
379 for f in fields {
380 m.insert(f.name.clone(), ty_from_canon_env(&f.ty, params, env));
381 }
382 Ty::Record(m)
383 }
384 other => ty_from_canon(other, params),
385 }
386}