diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index d524ec5..c360319 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -1,9 +1,10 @@ use std::{ fmt::{Debug, Display}, - sync::Arc, + rc::Rc, + sync::{atomic::AtomicUsize, Arc}, }; -use colored::Colorize; +use colored::{ColoredString, Colorize}; use line_span::LineSpans; #[cfg(feature = "parse")] @@ -89,6 +90,7 @@ pub enum CheckErrorComponent { } #[derive(Clone)] pub struct CheckErrorHRConfig { + color_index: Rc, indent_start: String, indent_default: String, indent_end: String, @@ -113,6 +115,7 @@ impl Display for CheckErrorDisplay<'_> { self.e.human_readable( f, &CheckErrorHRConfig { + color_index: Rc::new(AtomicUsize::new(0)), indent_start: String::new(), indent_default: String::new(), indent_end: String::new(), @@ -160,10 +163,10 @@ impl CheckError { let len = self.0.len(); for (i, component) in self.0.iter().enumerate() { macro_rules! indent { - () => { - if i + 1 == len { + ($s:expr, $e:expr) => { + if $e && i + 1 == len { &cfg.indent_end - } else if i == 0 { + } else if $s && i == 0 { &cfg.indent_start } else { &cfg.indent_default @@ -171,20 +174,27 @@ impl CheckError { }; } match component { - CheckErrorComponent::Message(msg) => writeln!(f, "{}{msg}", indent!())?, + CheckErrorComponent::Message(msg) => { + let lines = msg.lines().collect::>(); + let lc = lines.len(); + for (i, line) in lines.into_iter().enumerate() { + writeln!(f, "{}{line}", indent!(i == 0, i + 1 == lc))? + } + } CheckErrorComponent::Error(err) => { + let clr = Self::get_color(&cfg.color_index); let mut cfg = cfg.clone(); - cfg.indent_start.push_str("│"); - cfg.indent_default.push_str("│"); - cfg.indent_end.push_str("└"); + cfg.indent_start.push_str(&clr("│").to_string()); + cfg.indent_default.push_str(&clr("│").to_string()); + cfg.indent_end.push_str(&clr("└").to_string()); err.human_readable(f, &cfg)?; } CheckErrorComponent::ErrorWithDifferentSource(err) => { + let clr = Self::get_color(&cfg.color_index); let mut cfg = cfg.clone(); - cfg.indent_start.push_str(&"│".bright_yellow().to_string()); - cfg.indent_default - .push_str(&"│".bright_yellow().to_string()); - cfg.indent_end.push_str(&"└".bright_yellow().to_string()); + cfg.indent_start.push_str(&clr("┃").bold().to_string()); + cfg.indent_default.push_str(&clr("┃").bold().to_string()); + cfg.indent_end.push_str(&clr("┗").bold().to_string()); err.human_readable(f, &cfg)?; } CheckErrorComponent::Source(highlights) => { @@ -243,7 +253,7 @@ impl CheckError { writeln!( f, "{}Line {first_line_nr} ({}..{}){}", - indent!(), + indent!(true, false), start_with_comments + 1 - first_line_start, end_with_comments - last_line_start, src_from, @@ -252,7 +262,7 @@ impl CheckError { writeln!( f, "{}Lines {first_line_nr}-{last_line_nr} ({}..{}){}", - indent!(), + indent!(true, false), start_with_comments + 1 - first_line_start, end_with_comments - last_line_start, src_from, @@ -263,11 +273,13 @@ impl CheckError { } else { src.src()[start..end].line_spans().collect::>() }; - for line in lines { + let lines_count = lines.len(); + for (line_nr_rel, line) in lines.into_iter().enumerate() { + let last_line = line_nr_rel + 1 == lines_count; let line_start = line.start(); let line_end = line.end(); let line = line.as_str(); - writeln!(f, "{} {line}", indent!())?; + let mut line_printed = false; let mut right = 0; for (pos, color) in highlights { if let Some(color) = color { @@ -284,6 +296,11 @@ impl CheckError { let highlight_end = highlight_end - start; if highlight_start < line_end && highlight_end > line_start { + if !line_printed { + // this isn't the last line (important for indent) + writeln!(f, "{} {line}", indent!(false, false))?; + line_printed = true; + } // where the highlight starts in this line let hl_start = highlight_start.saturating_sub(line_start); @@ -301,7 +318,7 @@ impl CheckError { let hl_len = hl_len.min(line.len() - right); right += hl_space + hl_len; if print_indent && right != 0 { - write!(f, "{} ", indent!())?; + write!(f, "{} ", indent!(false, false))?; } write!( f, @@ -312,6 +329,10 @@ impl CheckError { } } } + if !line_printed { + // may be last line (important for indent) + writeln!(f, "{} {line}", indent!(false, last_line))?; + } if right != 0 { writeln!(f)?; } @@ -323,6 +344,19 @@ impl CheckError { } Ok(()) } + fn get_color(i: &AtomicUsize) -> impl Fn(&str) -> ColoredString { + let i = i.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + move |s| match i % 8 { + 0 => s.bright_white(), + 1 => s.bright_green(), + 2 => s.bright_purple(), + 3 => s.bright_cyan(), + 4 => s.bright_red(), + 5 => s.bright_yellow(), + 6 => s.bright_magenta(), + _ => s.bright_blue(), + } + } } impl From for CheckError { fn from(value: String) -> Self { diff --git a/mers_lib/src/program/configs/mod.rs b/mers_lib/src/program/configs/mod.rs index 58468c4..a7390f5 100755 --- a/mers_lib/src/program/configs/mod.rs +++ b/mers_lib/src/program/configs/mod.rs @@ -84,11 +84,19 @@ impl Config { } } - pub fn add_var(mut self, name: String, val: Data) -> Self { + pub fn add_var(self, name: String, val: Data) -> Self { let t = val.get().as_type(); + self.add_var_arc(name, Arc::new(RwLock::new(val)), t) + } + pub fn add_var_arc( + mut self, + name: String, + val: Arc>, + val_type: crate::data::Type, + ) -> Self { self.info_parsed.scopes[0].init_var(name, (0, self.globals)); - self.info_run.scopes[0].init_var(self.globals, Arc::new(RwLock::new(val))); - self.info_check.scopes[0].init_var(self.globals, t); + self.info_run.scopes[0].init_var(self.globals, val); + self.info_check.scopes[0].init_var(self.globals, val_type); self.globals += 1; self } diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index e2e82c9..e975ca8 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -28,39 +28,56 @@ impl Config { out: Arc::new(|a, _i| { let mut out = Type::empty(); for t in a.types.iter() { - if let Some(t) = t.as_any().downcast_ref::() { - if t.0.len() != 2 { - return Err(format!("cannot use try with tuple argument where len != 2 (got len {})", t.0.len()).into()); + if let Some(outer_tuple) = t.as_any().downcast_ref::() { + if outer_tuple.0.len() != 2 { + return Err(format!("cannot use try with tuple argument where len != 2 (got len {})", outer_tuple.0.len()).into()); } - let arg_type = &t.0[0]; - let functions = &t.0[1]; + let arg_type = &outer_tuple.0[0]; + let functions = &outer_tuple.0[1]; + let mut used_functions_and_errors = vec![]; for arg_type in arg_type.subtypes_type().types.iter() { let arg_type = Type::newm(vec![arg_type.clone()]); // possibilities for the tuple (f1, f2, f3, ..., fn) - for ft in functions.types.iter() { + for (fti, ft) in functions.types.iter().enumerate() { + if used_functions_and_errors.len() <= fti { + used_functions_and_errors.push(vec![]); + } let mut tuple_fallible = true; let mut tuple_possible = false; if let Some(ft) = ft.as_any().downcast_ref::() { // f1, f2, f3, ..., fn let mut func_errors = vec![]; - for ft in ft.0.iter() { + let mut skip_checks = false; + for (fi, ft) in ft.0.iter().enumerate() { + if used_functions_and_errors[fti].len() <= fi { + used_functions_and_errors[fti].push(vec![]); + } let mut func_fallible = false; // possibilities for f_ - for ft in ft.types.iter() { + for (fvi, ft) in ft.types.iter().enumerate() { if let Some(ft) = ft.as_any().downcast_ref::() { - func_errors.push(match ft.0(&arg_type) { - Err(e) => { - func_fallible = true; - Some(e) - } - Ok(o) => { - tuple_possible = true; - for t in o.types { - out.add(t); + if used_functions_and_errors[fti][fi].len() <= fvi { + used_functions_and_errors[fti][fi].push(Some(vec![])); + } + if !skip_checks { + func_errors.push((fvi, match ft.0(&arg_type) { + Err(e) => { + func_fallible = true; + if let Some(errs) = &mut used_functions_and_errors[fti][fi][fvi] { + errs.push(e.clone()); + } + Some(e) } - None - }, - }); + Ok(o) => { + used_functions_and_errors[fti][fi][fvi] = None; + tuple_possible = true; + for t in o.types { + out.add(t); + } + None + }, + })); + } } else { return Err(format!("try: arguments f1-fn must be functions").into()); } @@ -69,7 +86,7 @@ impl Config { if !func_fallible { tuple_fallible = false; if tuple_possible { - break; + skip_checks = true; } } } @@ -77,8 +94,8 @@ impl Config { // if the argument is {arg_type}, there is no infallible function. add a fallback function to handle this case! let mut e = CheckError::new() .msg(format!("if the argument is {arg_type}, there is no infallible function.")) - .msg(format!("Add a fallback function to handle this case!")); - for (i, err) in func_errors.into_iter().enumerate() { + .msg(format!("Add a function to handle this case!")); + for (i, err) in func_errors.into_iter() { if let Some(err) = err { e = e .msg(format!("Error for function #{}:", i + 1)) @@ -92,6 +109,26 @@ impl Config { } } } + for (functions_posibility_index, functions_possibility) in used_functions_and_errors.into_iter().enumerate() { + for (func_index, func_possibilities) in functions_possibility.into_iter().enumerate() { + for (func_possibility_index, errors_if_unused) in func_possibilities.into_iter().enumerate() { + if let Some(errs) = errors_if_unused { + let mut e = CheckError::new().msg(format!("try: For the argument {t}:\nFunction #{}{} is never used.{}", + func_index + 1, + if functions_posibility_index != 0 || func_possibility_index != 0 { + format!(" (func-tuple possibility {}, function possibility {})", functions_posibility_index + 1, func_possibility_index + 1) + } else { + format!("") + }, + if errs.is_empty() { "" } else { " Errors:" })); + for err in errs { + e = e.err(err); + } + return Err(e); + } + } + } + } } else { return Err(format!("cannot use try with non-tuple argument").into()); } diff --git a/mers_lib/src/program/configs/with_multithreading.rs b/mers_lib/src/program/configs/with_multithreading.rs index 0087f57..381388e 100755 --- a/mers_lib/src/program/configs/with_multithreading.rs +++ b/mers_lib/src/program/configs/with_multithreading.rs @@ -40,6 +40,8 @@ impl Config { Ok(t) => out.add(Arc::new(t)), Err(e) => return Err(CheckError::new().msg(format!("Can't call thread on a function which can't be called on an empty tuple: ")).err(e)) } + } else { + return Err(format!("thread: argument wasn't a function").into()); } } Ok(Type::new(ThreadT(out)))