1pub mod byte;
11pub mod char;
12pub mod checker;
13pub mod float;
14pub mod int;
15pub mod list;
16pub mod map;
17pub mod option;
18pub mod result;
19pub mod string;
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum Type {
23 Int,
24 Float,
25 Str,
26 Bool,
27 Unit,
28 Result(Box<Type>, Box<Type>),
29 Option(Box<Type>),
30 List(Box<Type>),
31 Tuple(Vec<Type>),
32 Map(Box<Type>, Box<Type>),
33 Fn(Vec<Type>, Box<Type>, Vec<String>),
34 Unknown, Named(String), }
37
38impl Type {
39 pub fn compatible(&self, other: &Type) -> bool {
43 if matches!(self, Type::Unknown) || matches!(other, Type::Unknown) {
44 return true;
45 }
46 match (self, other) {
47 (Type::Int, Type::Int) => true,
48 (Type::Float, Type::Float) => true,
49 (Type::Int, Type::Float) => true,
51 (Type::Str, Type::Str) => true,
52 (Type::Bool, Type::Bool) => true,
53 (Type::Unit, Type::Unit) => true,
54 (Type::Result(a1, b1), Type::Result(a2, b2)) => a1.compatible(a2) && b1.compatible(b2),
55 (Type::Option(a), Type::Option(b)) => a.compatible(b),
56 (Type::List(a), Type::List(b)) => a.compatible(b),
57 (Type::Tuple(a), Type::Tuple(b)) => {
58 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.compatible(y))
59 }
60 (Type::Map(k1, v1), Type::Map(k2, v2)) => k1.compatible(k2) && v1.compatible(v2),
61 (Type::Fn(p1, r1, e1), Type::Fn(p2, r2, e2)) => {
62 p1.len() == p2.len()
63 && p1.iter().zip(p2.iter()).all(|(a, b)| a.compatible(b))
64 && r1.compatible(r2)
65 && e1.iter().all(|eff| e2.contains(eff))
66 }
67 (Type::Named(a), Type::Named(b)) => a == b,
68 _ => false,
69 }
70 }
71
72 pub fn display(&self) -> String {
73 match self {
74 Type::Int => "Int".to_string(),
75 Type::Float => "Float".to_string(),
76 Type::Str => "String".to_string(),
77 Type::Bool => "Bool".to_string(),
78 Type::Unit => "Unit".to_string(),
79 Type::Result(ok, err) => format!("Result<{}, {}>", ok.display(), err.display()),
80 Type::Option(inner) => format!("Option<{}>", inner.display()),
81 Type::List(inner) => format!("List<{}>", inner.display()),
82 Type::Tuple(items) => format!(
83 "({})",
84 items
85 .iter()
86 .map(Type::display)
87 .collect::<Vec<_>>()
88 .join(", ")
89 ),
90 Type::Map(key, value) => format!("Map<{}, {}>", key.display(), value.display()),
91 Type::Fn(params, ret, effects) => {
92 let ps: Vec<String> = params.iter().map(|p| p.display()).collect();
93 if effects.is_empty() {
94 format!("Fn({}) -> {}", ps.join(", "), ret.display())
95 } else {
96 format!(
97 "Fn({}) -> {} ! [{}]",
98 ps.join(", "),
99 ret.display(),
100 effects.join(", ")
101 )
102 }
103 }
104 Type::Unknown => "Unknown".to_string(),
105 Type::Named(n) => n.clone(),
106 }
107 }
108}
109
110pub fn parse_type_str_strict(s: &str) -> Result<Type, String> {
115 let s = s.trim();
116 if s.is_empty() || s == "Any" {
117 return Err(s.to_string());
118 }
119 if let Some(fn_ty) = parse_fn_type_strict(s)? {
120 return Ok(fn_ty);
121 }
122
123 if s.starts_with('(') && s.ends_with(')') {
124 let inner = &s[1..s.len() - 1];
125 let parts = split_top_level(inner, ',')?;
126 if parts.len() < 2 {
127 return Err(s.to_string());
128 }
129 let elems = parts
130 .into_iter()
131 .map(parse_type_str_strict)
132 .collect::<Result<Vec<_>, _>>()?;
133 return Ok(Type::Tuple(elems));
134 }
135
136 match s {
137 "Int" => Ok(Type::Int),
138 "Float" => Ok(Type::Float),
139 "String" | "Str" => Ok(Type::Str),
140 "Bool" => Ok(Type::Bool),
141 "Unit" => Ok(Type::Unit),
142 _ => {
143 if let Some(inner) = strip_wrapper(s, "Result<", ">") {
144 if let Some((ok_s, err_s)) = split_top_level_comma(inner) {
145 let ok_ty = parse_type_str_strict(ok_s)?;
146 let err_ty = parse_type_str_strict(err_s)?;
147 return Ok(Type::Result(Box::new(ok_ty), Box::new(err_ty)));
148 }
149 return Err(s.to_string());
150 }
151 if let Some(inner) = strip_wrapper(s, "Option<", ">") {
152 let inner_ty = parse_type_str_strict(inner)?;
153 return Ok(Type::Option(Box::new(inner_ty)));
154 }
155 if let Some(inner) = strip_wrapper(s, "List<", ">") {
156 let inner_ty = parse_type_str_strict(inner)?;
157 return Ok(Type::List(Box::new(inner_ty)));
158 }
159 if let Some(inner) = strip_wrapper(s, "Map<", ">") {
160 if let Some((key_s, value_s)) = split_top_level_comma(inner) {
161 let key_ty = parse_type_str_strict(key_s)?;
162 if !matches!(key_ty, Type::Int | Type::Float | Type::Str | Type::Bool) {
163 return Err(s.to_string());
164 }
165 let value_ty = parse_type_str_strict(value_s)?;
166 return Ok(Type::Map(Box::new(key_ty), Box::new(value_ty)));
167 }
168 return Err(s.to_string());
169 }
170
171 if s.chars().next().map_or(false, |c| c.is_uppercase())
174 && s.chars()
175 .all(|c| c.is_alphanumeric() || c == '_' || c == '.')
176 {
177 return Ok(Type::Named(s.to_string()));
178 }
179
180 Err(s.to_string())
181 }
182 }
183}
184
185pub fn parse_type_str(s: &str) -> Type {
189 let s = s.trim();
190 if s.starts_with("Fn(") {
191 if let Ok(Some(fn_ty)) = parse_fn_type_strict(s) {
192 return fn_ty;
193 }
194 return Type::Unknown;
195 }
196 if s.starts_with('(') && s.ends_with(')') {
197 let inner = &s[1..s.len() - 1];
198 if let Ok(parts) = split_top_level(inner, ',') {
199 if parts.len() >= 2 {
200 return Type::Tuple(parts.into_iter().map(parse_type_str).collect());
201 }
202 }
203 return Type::Unknown;
204 }
205 match s {
206 "Int" => Type::Int,
207 "Float" => Type::Float,
208 "String" | "Str" => Type::Str,
209 "Bool" => Type::Bool,
210 "Unit" => Type::Unit,
211 "" => Type::Unknown,
212 _ => {
213 if let Some(inner) = strip_wrapper(s, "Result<", ">") {
215 if let Some((ok_str, err_str)) = split_top_level_comma(inner) {
217 return Type::Result(
218 Box::new(parse_type_str(ok_str)),
219 Box::new(parse_type_str(err_str)),
220 );
221 }
222 }
223 if let Some(inner) = strip_wrapper(s, "Option<", ">") {
224 return Type::Option(Box::new(parse_type_str(inner)));
225 }
226 if let Some(inner) = strip_wrapper(s, "List<", ">") {
227 return Type::List(Box::new(parse_type_str(inner)));
228 }
229 if let Some(inner) = strip_wrapper(s, "Map<", ">") {
230 if let Some((key_str, value_str)) = split_top_level_comma(inner) {
231 return Type::Map(
232 Box::new(parse_type_str(key_str)),
233 Box::new(parse_type_str(value_str)),
234 );
235 }
236 }
237 if s.chars().next().map_or(false, |c| c.is_uppercase())
240 && s.chars()
241 .all(|c| c.is_alphanumeric() || c == '_' || c == '.')
242 && s != "Any"
243 {
244 return Type::Named(s.to_string());
245 }
246 Type::Unknown
248 }
249 }
250}
251
252fn parse_fn_type_strict(s: &str) -> Result<Option<Type>, String> {
253 if !s.starts_with("Fn(") {
254 return Ok(None);
255 }
256
257 let close_idx = find_matching_paren(s, 2).ok_or_else(|| s.to_string())?;
258 let params_src = &s[3..close_idx];
259
260 let after_params = s[close_idx + 1..].trim_start();
261 if !after_params.starts_with("->") {
262 return Err(s.to_string());
263 }
264 let ret_and_effects = after_params[2..].trim();
265 if ret_and_effects.is_empty() {
266 return Err(s.to_string());
267 }
268
269 let (ret_src, effects) = split_fn_effects_suffix(ret_and_effects)?;
270 let ret_ty = parse_type_str_strict(ret_src)?;
271 let params = parse_type_list_strict(params_src)?;
272 Ok(Some(Type::Fn(params, Box::new(ret_ty), effects)))
273}
274
275fn parse_type_list_strict(src: &str) -> Result<Vec<Type>, String> {
276 if src.trim().is_empty() {
277 return Ok(vec![]);
278 }
279 split_top_level(src, ',')?
280 .into_iter()
281 .map(|part| {
282 let part = part.trim();
283 if part.is_empty() {
284 Err(src.to_string())
285 } else {
286 parse_type_str_strict(part)
287 }
288 })
289 .collect()
290}
291
292fn split_fn_effects_suffix(src: &str) -> Result<(&str, Vec<String>), String> {
293 if let Some(bang_idx) = find_top_level_bang(src) {
294 let ret_src = src[..bang_idx].trim();
295 if ret_src.is_empty() {
296 return Err(src.to_string());
297 }
298 let effects_src = src[bang_idx + 1..].trim();
299 if !(effects_src.starts_with('[') && effects_src.ends_with(']')) {
300 return Err(src.to_string());
301 }
302 let inner = &effects_src[1..effects_src.len() - 1];
303 let effects = if inner.trim().is_empty() {
304 vec![]
305 } else {
306 split_top_level(inner, ',')?
307 .into_iter()
308 .map(|part| {
309 let name = part.trim();
310 if name.is_empty() {
311 Err(src.to_string())
312 } else {
313 Ok(name.to_string())
314 }
315 })
316 .collect::<Result<Vec<_>, _>>()?
317 };
318 Ok((ret_src, effects))
319 } else {
320 Ok((src.trim(), vec![]))
321 }
322}
323
324fn find_matching_paren(s: &str, open_idx: usize) -> Option<usize> {
325 if s.as_bytes().get(open_idx).copied() != Some(b'(') {
326 return None;
327 }
328 let mut depth = 1usize;
329 for (i, ch) in s.char_indices().skip(open_idx + 1) {
330 match ch {
331 '(' => depth += 1,
332 ')' => {
333 depth -= 1;
334 if depth == 0 {
335 return Some(i);
336 }
337 }
338 _ => {}
339 }
340 }
341 None
342}
343
344fn find_top_level_bang(s: &str) -> Option<usize> {
345 let mut angle = 0usize;
346 let mut paren = 0usize;
347 let mut bracket = 0usize;
348
349 for (i, ch) in s.char_indices() {
350 match ch {
351 '<' => angle += 1,
352 '>' => angle = angle.saturating_sub(1),
353 '(' => paren += 1,
354 ')' => paren = paren.saturating_sub(1),
355 '[' => bracket += 1,
356 ']' => bracket = bracket.saturating_sub(1),
357 '!' if angle == 0 && paren == 0 && bracket == 0 => return Some(i),
358 _ => {}
359 }
360 }
361
362 None
363}
364
365fn split_top_level<'a>(s: &'a str, delimiter: char) -> Result<Vec<&'a str>, String> {
366 let mut out = Vec::new();
367 let mut start = 0usize;
368 let mut angle = 0usize;
369 let mut paren = 0usize;
370 let mut bracket = 0usize;
371
372 for (i, ch) in s.char_indices() {
373 match ch {
374 '<' => angle += 1,
375 '>' => {
376 if angle == 0 {
377 return Err(s.to_string());
378 }
379 angle -= 1;
380 }
381 '(' => paren += 1,
382 ')' => {
383 if paren == 0 {
384 return Err(s.to_string());
385 }
386 paren -= 1;
387 }
388 '[' => bracket += 1,
389 ']' => {
390 if bracket == 0 {
391 return Err(s.to_string());
392 }
393 bracket -= 1;
394 }
395 _ if ch == delimiter && angle == 0 && paren == 0 && bracket == 0 => {
396 out.push(&s[start..i]);
397 start = i + ch.len_utf8();
398 }
399 _ => {}
400 }
401 }
402
403 if angle != 0 || paren != 0 || bracket != 0 {
404 return Err(s.to_string());
405 }
406 out.push(&s[start..]);
407 Ok(out)
408}
409
410fn strip_wrapper<'a>(s: &'a str, prefix: &str, suffix: &str) -> Option<&'a str> {
412 if s.starts_with(prefix) && s.ends_with(suffix) {
413 let inner = &s[prefix.len()..s.len() - suffix.len()];
414 Some(inner)
415 } else {
416 None
417 }
418}
419
420fn split_top_level_comma(s: &str) -> Option<(&str, &str)> {
422 let mut angle = 0usize;
423 let mut paren = 0usize;
424 let mut bracket = 0usize;
425 for (i, ch) in s.char_indices() {
426 match ch {
427 '<' => angle += 1,
428 '>' => angle = angle.saturating_sub(1),
429 '(' => paren += 1,
430 ')' => paren = paren.saturating_sub(1),
431 '[' => bracket += 1,
432 ']' => bracket = bracket.saturating_sub(1),
433 ',' if angle == 0 && paren == 0 && bracket == 0 => {
434 return Some((&s[..i], &s[i + 1..]));
435 }
436 _ => {}
437 }
438 }
439 None
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[test]
447 fn test_primitives() {
448 assert_eq!(parse_type_str("Int"), Type::Int);
449 assert_eq!(parse_type_str("Float"), Type::Float);
450 assert_eq!(parse_type_str("String"), Type::Str);
451 assert_eq!(parse_type_str("Bool"), Type::Bool);
452 assert_eq!(parse_type_str("Unit"), Type::Unit);
453 }
454
455 #[test]
456 fn test_generics() {
457 assert_eq!(
458 parse_type_str("Result<Int, String>"),
459 Type::Result(Box::new(Type::Int), Box::new(Type::Str))
460 );
461 assert_eq!(
462 parse_type_str("Option<Bool>"),
463 Type::Option(Box::new(Type::Bool))
464 );
465 assert_eq!(parse_type_str("List<Int>"), Type::List(Box::new(Type::Int)));
466 assert_eq!(
467 parse_type_str("Map<String, Int>"),
468 Type::Map(Box::new(Type::Str), Box::new(Type::Int))
469 );
470 assert_eq!(
471 parse_type_str("(Int, String)"),
472 Type::Tuple(vec![Type::Int, Type::Str])
473 );
474 }
475
476 #[test]
477 fn test_nested() {
478 assert_eq!(
479 parse_type_str("Result<Float, String>"),
480 Type::Result(Box::new(Type::Float), Box::new(Type::Str))
481 );
482 }
483
484 #[test]
485 fn test_unknown() {
486 assert_eq!(
488 parse_type_str("SomeUnknownType"),
489 Type::Named("SomeUnknownType".to_string())
490 );
491 assert_eq!(parse_type_str(""), Type::Unknown);
493 }
494
495 #[test]
496 fn test_compatible() {
497 assert!(Type::Int.compatible(&Type::Int));
498 assert!(!Type::Int.compatible(&Type::Str));
499 assert!(Type::Unknown.compatible(&Type::Int));
500 assert!(Type::Int.compatible(&Type::Unknown));
501 assert!(Type::Int.compatible(&Type::Float)); assert!(Type::Result(Box::new(Type::Int), Box::new(Type::Str))
503 .compatible(&Type::Result(Box::new(Type::Int), Box::new(Type::Str))));
504 assert!(Type::Map(Box::new(Type::Str), Box::new(Type::Int))
505 .compatible(&Type::Map(Box::new(Type::Str), Box::new(Type::Int))));
506 assert!(!Type::Map(Box::new(Type::Str), Box::new(Type::Int))
507 .compatible(&Type::Map(Box::new(Type::Int), Box::new(Type::Int))));
508 }
509
510 #[test]
511 fn test_function_type_parsing() {
512 assert_eq!(
513 parse_type_str_strict("Fn(Int, String) -> Bool").unwrap(),
514 Type::Fn(vec![Type::Int, Type::Str], Box::new(Type::Bool), vec![])
515 );
516 assert_eq!(
517 parse_type_str_strict("Fn(Int) -> Int ! [Console]").unwrap(),
518 Type::Fn(
519 vec![Type::Int],
520 Box::new(Type::Int),
521 vec!["Console".to_string()]
522 )
523 );
524 }
525
526 #[test]
527 fn test_function_effect_compatibility_subset() {
528 let pure = Type::Fn(vec![Type::Int], Box::new(Type::Int), vec![]);
529 let console = Type::Fn(
530 vec![Type::Int],
531 Box::new(Type::Int),
532 vec!["Console".to_string()],
533 );
534
535 assert!(pure.compatible(&console));
536 assert!(!console.compatible(&pure));
537 }
538
539 #[test]
540 fn test_strict_parser_accepts_valid_generics() {
541 assert_eq!(
542 parse_type_str_strict("Result<Int, String>").unwrap(),
543 Type::Result(Box::new(Type::Int), Box::new(Type::Str))
544 );
545 assert_eq!(
546 parse_type_str_strict("List<Option<Float>>").unwrap(),
547 Type::List(Box::new(Type::Option(Box::new(Type::Float))))
548 );
549 assert_eq!(
550 parse_type_str_strict("Map<String, Int>").unwrap(),
551 Type::Map(Box::new(Type::Str), Box::new(Type::Int))
552 );
553 assert_eq!(
554 parse_type_str_strict("(Int, String)").unwrap(),
555 Type::Tuple(vec![Type::Int, Type::Str])
556 );
557 }
558
559 #[test]
560 fn test_strict_parser_accepts_user_defined_types() {
561 assert_eq!(
563 parse_type_str_strict("Result<MyError, String>").unwrap(),
564 Type::Result(
565 Box::new(Type::Named("MyError".to_string())),
566 Box::new(Type::Str)
567 )
568 );
569 assert_eq!(
570 parse_type_str_strict("Option<Shape>").unwrap(),
571 Type::Option(Box::new(Type::Named("Shape".to_string())))
572 );
573 assert_eq!(
574 parse_type_str_strict("List<User>").unwrap(),
575 Type::List(Box::new(Type::Named("User".to_string())))
576 );
577 assert!(parse_type_str_strict("integ").is_err());
579 }
580
581 #[test]
582 fn test_dotted_named_type() {
583 assert_eq!(
584 parse_type_str("Tcp.Connection"),
585 Type::Named("Tcp.Connection".to_string())
586 );
587 assert_eq!(
588 parse_type_str_strict("Tcp.Connection").unwrap(),
589 Type::Named("Tcp.Connection".to_string())
590 );
591 assert_eq!(
592 parse_type_str_strict("Result<Tcp.Connection, String>").unwrap(),
593 Type::Result(
594 Box::new(Type::Named("Tcp.Connection".to_string())),
595 Box::new(Type::Str)
596 )
597 );
598 }
599
600 #[test]
601 fn test_strict_parser_rejects_malformed_generics() {
602 assert!(parse_type_str_strict("Result<Int>").is_err());
603 assert!(parse_type_str_strict("Option<Int, String>").is_err());
604 assert!(parse_type_str_strict("Map<Int>").is_err());
605 assert!(parse_type_str_strict("Map<List<Int>, String>").is_err());
606 assert!(parse_type_str_strict("(Int)").is_err());
607 assert!(parse_type_str_strict("Fn(Int) Int").is_err());
608 assert!(parse_type_str_strict("Fn(Int) -> ! [Console]").is_err());
609 }
610}