diff --git a/mers/Cargo.toml b/mers/Cargo.toml index 77283a1..18f25c9 100644 --- a/mers/Cargo.toml +++ b/mers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers" -version = "0.8.13" +version = "0.8.14" edition = "2021" license = "MIT OR Apache-2.0" description = "dynamically typed but type-checked programming language" @@ -10,8 +10,12 @@ repository = "https://github.com/Dummi26/mers" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["colored-output"] +colored-output = ["mers_lib/ecolor-term", "dep:colored"] + [dependencies] -mers_lib = "0.8.13" -# mers_lib = { path = "../mers_lib" } +# mers_lib = "0.8.14" +mers_lib = { path = "../mers_lib" } clap = { version = "4.3.19", features = ["derive"] } -colored = "2.1.0" +colored = { version = "2.1.0", optional = true } diff --git a/mers/src/main.rs b/mers/src/main.rs index 82f8706..00a428a 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -3,6 +3,7 @@ use mers_lib::prelude_compile::*; use std::{path::PathBuf, process::exit, sync::Arc}; mod cfg_globals; +#[cfg(feature = "colored-output")] mod pretty_print; #[derive(Parser)] @@ -35,7 +36,14 @@ enum Command { #[command(subcommand)] source: From, }, + /// Not available, because the colored-output default feature was disabled when building mers! + #[cfg(not(feature = "colored-output"))] + PrettyPrint { + #[command(subcommand)] + source: From, + }, /// Add syntax highlighting to the code + #[cfg(feature = "colored-output")] PrettyPrint { #[command(subcommand)] source: From, @@ -82,19 +90,19 @@ fn main() { let srca = Arc::new(src.clone()); match parse(&mut src, &srca) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(20); } Ok(parsed) => { let (i1, _, i3) = config.infos(); match compile(&*parsed, i1) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(24); } Ok(compiled) => match check(&*compiled, i3) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(28); } Ok(output_type) => eprintln!("{output_type}"), @@ -108,24 +116,24 @@ fn main() { let srca = Arc::new(src.clone()); match parse(&mut src, &srca) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(255); } Ok(parsed) => { let (i1, mut i2, i3) = config.infos(); match compile(&*parsed, i1) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(255); } Ok(compiled) => match check(&*compiled, i3) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(255); } Ok(_) => { if let Err(e) = compiled.run(&mut i2) { - eprintln!("Error while running:\n{e}"); + eprintln!("Error while running:\n{e:?}"); std::process::exit(1); } } @@ -139,19 +147,19 @@ fn main() { let srca = Arc::new(src.clone()); match parse(&mut src, &srca) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(255); } Ok(parsed) => { let (i1, mut i2, _) = config.infos(); match compile(&*parsed, i1) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(255); } Ok(compiled) => { if let Err(e) = compiled.run(&mut i2) { - eprintln!("Error while running:\n{e}"); + eprintln!("Error while running:\n{e:?}"); std::process::exit(1); } } @@ -159,8 +167,14 @@ fn main() { } } } + #[cfg(feature = "colored-output")] Command::PrettyPrint { source } => { pretty_print::pretty_print(get_source(source)); } + #[cfg(not(feature = "colored-output"))] + Command::PrettyPrint { source: _ } => { + eprintln!("feature colored-output must be enabled when compiling mers if you want to use pretty-print!"); + std::process::exit(180); + } } } diff --git a/mers/src/pretty_print.rs b/mers/src/pretty_print.rs index afa94ec..d918e0b 100644 --- a/mers/src/pretty_print.rs +++ b/mers/src/pretty_print.rs @@ -7,7 +7,7 @@ pub fn pretty_print(mut src: Source) { let srca = Arc::new(src.clone()); match parse(&mut src, &srca) { Err(e) => { - eprintln!("{e}"); + eprintln!("{e:?}"); exit(28); } Ok(parsed) => { diff --git a/mers_lib/Cargo.toml b/mers_lib/Cargo.toml index 6f00ddb..eec507e 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers_lib" -version = "0.8.13" +version = "0.8.14" edition = "2021" license = "MIT OR Apache-2.0" description = "library to use the mers language in other projects" @@ -10,9 +10,14 @@ repository = "https://github.com/Dummi26/mers" [features] default = ["parse"] + +# for parsing and running mers code (for most situations: just enable parse) parse = ["run"] run = [] +# color features are used when formatting error messages. +ecolor-term = ["dep:colored"] + [dependencies] -colored = "2.0.4" +colored = { version = "2.0.4", optional = true } line-span = "0.1.5" diff --git a/mers_lib/examples/00_parse_compile_check_run.rs b/mers_lib/examples/00_parse_compile_check_run.rs index 8f779a7..8d14052 100644 --- a/mers_lib/examples/00_parse_compile_check_run.rs +++ b/mers_lib/examples/00_parse_compile_check_run.rs @@ -24,7 +24,7 @@ fn show(src: String) { ); eprintln!("{src}"); match parse_compile_check_run(src) { - Err(e) => eprintln!("{e}"), + Err(e) => eprintln!("{e:?}"), Ok((t, v)) => eprintln!("Returned `{}` :: `{t}`", v.get()), } } diff --git a/mers_lib/examples/02_own_variable.rs b/mers_lib/examples/02_own_variable.rs index b5edf89..9dafb0c 100644 --- a/mers_lib/examples/02_own_variable.rs +++ b/mers_lib/examples/02_own_variable.rs @@ -14,7 +14,7 @@ fn main() { eprintln!("This is not:"); let e = run("5.rust_func".to_owned()).err().unwrap(); - eprintln!("{e}"); + eprintln!("{e:?}"); } fn run(src: String) -> Result<(), CheckError> { let mut source = Source::new_from_string(src); diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index 8407552..39e30f4 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -1,10 +1,9 @@ use std::{ fmt::{Debug, Display}, rc::Rc, - sync::{atomic::AtomicUsize, Arc}, + sync::{atomic::AtomicU32, Arc}, }; -use colored::{ColoredString, Colorize}; use line_span::LineSpans; #[cfg(feature = "parse")] @@ -44,65 +43,169 @@ impl SourceRange { &self.in_file } } +/// To `Display` this, use one of the `display` methods to get a struct with some configuration options. +/// The `Debug` impl of this is the same as `Display`ing `this.display_term()` or `this.display_notheme()`, depending on if the `ecolor-term` feature is enabled or not. +/// Since this may use ansi color codes, it should only be used when printing to a terminal, which is why `CheckError` itself has no `Display` implementation, only this one for `Debug`. #[derive(Clone)] pub struct CheckError(pub Vec); -#[allow(non_upper_case_globals)] -pub mod error_colors { - use colored::Color; - pub const UnknownVariable: Color = Color::Red; +#[derive(Clone, Copy)] +pub enum EColor { + Indent(u32), - pub const WhitespaceAfterHashtag: Color = Color::Red; - pub const HashUnknown: Color = Color::Red; - pub const HashIncludeCantLoadFile: Color = Color::Red; - pub const HashIncludeNotAString: Color = Color::Red; - pub const HashIncludeErrorInIncludedFile: Color = Color::Red; + UnknownVariable, + WhitespaceAfterHashtag, + HashUnknown, + HashIncludeCantLoadFile, + HashIncludeNotAString, + HashIncludeErrorInIncludedFile, + BackslashEscapeUnknown, + BackslashEscapeEOF, + StringEOF, + IfConditionNotBool, + ChainWithNonFunction, + Function, + FunctionArgument, + InitFrom, + InitTo, + AssignFrom, + AssignTo, + AssignTargetNonReference, + AsTypeStatementWithTooBroadType, + AsTypeTypeAnnotation, + BadCharInTupleType, + BadCharInFunctionType, + BadTypeFromParsed, + TypeAnnotationNoClosingBracket, + TryBadSyntax, + TryNoFunctionFound, + TryNotAFunction, + TryUnusedFunction1, + TryUnusedFunction2, + CustomTypeTestFailed, - pub const BackslashEscapeUnknown: Color = Color::Red; - pub const BackslashEscapeEOF: Color = Color::Red; - pub const StringEOF: Color = Color::Red; + StacktraceDescend, + StacktraceDescendHashInclude, - pub const IfConditionNotBool: Color = Color::Red; - pub const ChainWithNonFunction: Color = Color::Yellow; + MaximumRuntimeExceeded, - pub const Function: Color = Color::BrightMagenta; - pub const FunctionArgument: Color = Color::BrightBlue; + InCodePositionLine, +} - pub const InitFrom: Color = Color::BrightCyan; - pub const InitTo: Color = Color::Green; - pub const AssignFrom: Color = InitFrom; - pub const AssignTo: Color = InitTo; - pub const AssignTargetNonReference: Color = Color::BrightYellow; +pub trait Theme { + fn color(&self, text: &str, color: EColor, t: &mut T); +} +pub trait ThemeTo: Theme { + fn color_to(&self, text: &str, color: EColor) -> T; +} +impl + ?Sized> ThemeTo for T { + fn color_to(&self, text: &str, color: EColor) -> String { + let mut t = String::new(); + self.color(text, color, &mut t); + t + } +} +pub fn colorize_str( + message: &Vec<(String, Option)>, + theme: &(impl Theme + ?Sized), +) -> String { + let mut t = String::new(); + colorize_gen(message, &mut t, |t, a| a.push_str(t), theme); + t +} +pub fn colorize_gen( + message: &Vec<(String, Option)>, + t: &mut T, + direct: impl Fn(&str, &mut T), + theme: &(impl Theme + ?Sized), +) { + for (text, color) in message { + if let Some(color) = *color { + theme.color(text, color, t) + } else { + direct(text, t) + } + } +} - pub const AsTypeStatementWithTooBroadType: Color = InitFrom; - pub const AsTypeTypeAnnotation: Color = InitTo; +pub struct NoTheme; +impl Theme for NoTheme { + fn color(&self, text: &str, _color: EColor, t: &mut String) { + t.push_str(text); + } +} - pub const BadCharInTupleType: Color = Color::Red; - pub const BadCharInFunctionType: Color = Color::Red; - pub const BadTypeFromParsed: Color = Color::Blue; - pub const TypeAnnotationNoClosingBracket: Color = Color::Blue; +#[cfg(feature = "ecolor-term")] +pub struct TermDefaultTheme; +#[cfg(feature = "ecolor-term")] +impl Theme for TermDefaultTheme { + fn color(&self, text: &str, color: EColor, t: &mut String) { + use colored::{Color, Colorize}; + t.push_str(&text.color(match color { + EColor::Indent(n) => match n % 6 { + 0 => Color::Red, + 1 => Color::Green, + 2 => Color::Yellow, + 3 => Color::Blue, + 4 => Color::Magenta, + _ => Color::Cyan, + }, - pub const TryBadSyntax: Color = Color::Red; - pub const TryNoFunctionFound: Color = Color::Red; - pub const TryNotAFunction: Color = Color::Red; - pub const TryUnusedFunction1: Color = Color::Red; - pub const TryUnusedFunction2: Color = Color::BrightRed; + EColor::UnknownVariable => Color::Red, - pub const StacktraceDescend: Color = Color::Yellow; - pub const StacktraceDescendHashInclude: Color = Color::Red; + EColor::WhitespaceAfterHashtag => Color::Red, + EColor::HashUnknown => Color::Red, + EColor::HashIncludeCantLoadFile => Color::Red, + EColor::HashIncludeNotAString => Color::Red, + EColor::HashIncludeErrorInIncludedFile => Color::Red, - pub const MaximumRuntimeExceeded: Color = Color::BrightYellow; + EColor::BackslashEscapeUnknown => Color::Red, + EColor::BackslashEscapeEOF => Color::Red, + EColor::StringEOF => Color::Red, + + EColor::IfConditionNotBool => Color::Red, + EColor::ChainWithNonFunction => Color::Yellow, + + EColor::Function => Color::BrightMagenta, + EColor::FunctionArgument => Color::BrightBlue, + + EColor::InitFrom | EColor::AssignFrom | EColor::AsTypeStatementWithTooBroadType => { + Color::BrightCyan + } + EColor::InitTo | EColor::AssignTo | EColor::AsTypeTypeAnnotation => Color::Green, + EColor::AssignTargetNonReference => Color::BrightYellow, + + EColor::BadCharInTupleType => Color::Red, + EColor::BadCharInFunctionType => Color::Red, + EColor::BadTypeFromParsed => Color::Blue, + EColor::TypeAnnotationNoClosingBracket => Color::Blue, + + EColor::TryBadSyntax => Color::Red, + EColor::TryNoFunctionFound => Color::Red, + EColor::TryNotAFunction => Color::Red, + EColor::TryUnusedFunction1 => Color::Red, + EColor::TryUnusedFunction2 => Color::BrightRed, + EColor::CustomTypeTestFailed => Color::BrightRed, + + EColor::StacktraceDescend => Color::Yellow, + EColor::StacktraceDescendHashInclude => Color::Red, + EColor::MaximumRuntimeExceeded => Color::BrightYellow, + + EColor::InCodePositionLine => Color::BrightBlack, + })); + } } #[derive(Clone)] pub enum CheckErrorComponent { - Message(String), + Message(Vec<(String, Option)>), Error(CheckError), ErrorWithDifferentSource(CheckError), - Source(Vec<(SourceRange, Option)>), + Source(Vec<(SourceRange, Option)>), } pub struct CheckErrorHRConfig { - color_index_ptr: Rc, - color_index: usize, + color_index_ptr: Rc, + color_index: u32, + theme: Rc>, is_inner: bool, style: u8, idt_start: String, @@ -113,7 +216,7 @@ pub struct CheckErrorHRConfig { show_comments: bool, } type BorderCharsSet = [[&'static str; 4]; 3]; -pub struct IndentStr<'a>(&'a str, ColoredString); +pub struct IndentStr<'a>(&'a str, String); impl Display for IndentStr<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}{}", self.0, self.1) @@ -143,17 +246,11 @@ impl CheckErrorHRConfig { ["╩", "╙", "╜", "╨"], ], ]; - fn color(&self, s: &str) -> ColoredString { - match self.color_index % 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(), - } + fn color(&self, s: &str) -> String { + let mut t = String::new(); + self.theme + .color(s, EColor::Indent(self.color_index), &mut t); + return t; } pub fn indent_start(&self, right: bool) -> IndentStr { IndentStr( @@ -202,6 +299,7 @@ impl CheckErrorHRConfig { Self { color_index_ptr: self.color_index_ptr.clone(), color_index, + theme: Rc::clone(&self.theme), is_inner: true, style, idt_start: if is_first { @@ -231,6 +329,7 @@ impl CheckErrorHRConfig { #[cfg(feature = "parse")] pub struct CheckErrorDisplay<'a> { e: &'a CheckError, + theme: Rc>, pub show_comments: bool, } #[cfg(feature = "parse")] @@ -247,7 +346,8 @@ impl Display for CheckErrorDisplay<'_> { f, &CheckErrorHRConfig { color_index: 0, - color_index_ptr: Rc::new(AtomicUsize::new(1)), + color_index_ptr: Rc::new(AtomicU32::new(1)), + theme: Rc::clone(&self.theme), is_inner: false, style: CheckErrorHRConfig::STYLE_DEFAULT, idt_start: String::new(), @@ -272,10 +372,16 @@ impl CheckError { self.0.push(v); self } - pub(crate) fn msg(self, s: String) -> Self { + pub(crate) fn msg_str(self, s: String) -> Self { + self.add(CheckErrorComponent::Message(vec![(s, None)])) + } + pub(crate) fn msg_mut_str(&mut self, s: String) -> &mut Self { + self.add_mut(CheckErrorComponent::Message(vec![(s, None)])) + } + pub(crate) fn msg(self, s: Vec<(String, Option)>) -> Self { self.add(CheckErrorComponent::Message(s)) } - pub(crate) fn msg_mut(&mut self, s: String) -> &mut Self { + pub(crate) fn msg_mut(&mut self, s: Vec<(String, Option)>) -> &mut Self { self.add_mut(CheckErrorComponent::Message(s)) } pub(crate) fn err(self, e: Self) -> Self { @@ -290,19 +396,31 @@ impl CheckError { pub(crate) fn err_with_diff_src_mut(&mut self, e: CheckError) -> &mut Self { self.add_mut(CheckErrorComponent::ErrorWithDifferentSource(e)) } - pub(crate) fn src(self, s: Vec<(SourceRange, Option)>) -> Self { + pub(crate) fn src(self, s: Vec<(SourceRange, Option)>) -> Self { self.add(CheckErrorComponent::Source(s)) } - pub(crate) fn src_mut(&mut self, s: Vec<(SourceRange, Option)>) -> &mut Self { + pub(crate) fn src_mut(&mut self, s: Vec<(SourceRange, Option)>) -> &mut Self { self.add_mut(CheckErrorComponent::Source(s)) } #[cfg(feature = "parse")] - pub fn display<'a>(&'a self) -> CheckErrorDisplay<'a> { + pub fn display<'a>(&'a self, theme: impl Theme + 'static) -> CheckErrorDisplay<'a> { CheckErrorDisplay { e: self, + theme: Rc::new(theme), show_comments: true, } } + /// Like `display`, but doesn't use any theme (doesn't colorize its output) + #[cfg(feature = "parse")] + pub fn display_notheme<'a>(&'a self) -> CheckErrorDisplay<'a> { + self.display(NoTheme) + } + /// Like `display`, but uses the default terminal theme + #[cfg(feature = "parse")] + #[cfg(feature = "ecolor-term")] + pub fn display_term<'a>(&'a self) -> CheckErrorDisplay<'a> { + self.display(TermDefaultTheme) + } /// will, unless empty, end in a newline #[cfg(feature = "parse")] fn human_readable( @@ -330,6 +448,7 @@ impl CheckError { } match component { CheckErrorComponent::Message(msg) => { + let msg = colorize_str(msg, cfg.theme.as_ref()); let lines = msg.lines().collect::>(); let lc = lines.len(); for (i, line) in lines.into_iter().enumerate() { @@ -403,26 +522,30 @@ impl CheckError { f, "{}{}", indent!(true, false, ADD_RIGHT_BITS), - format!( - "Line {first_line_nr} ({}..{}){}", - start_with_comments + 1 - first_line_start, - end_with_comments - last_line_start, - src_from, + cfg.theme.color_to( + &format!( + "Line {first_line_nr} ({}..{}){}", + start_with_comments + 1 - first_line_start, + end_with_comments - last_line_start, + src_from, + ), + EColor::InCodePositionLine ) - .bright_black() )?; } else { writeln!( f, "{}{}", indent!(true, false, ADD_RIGHT_BITS), - format!( - "Lines {first_line_nr}-{last_line_nr} ({}..{}){}", - start_with_comments + 1 - first_line_start, - end_with_comments - last_line_start, - src_from, + cfg.theme.color_to( + &format!( + "Lines {first_line_nr}-{last_line_nr} ({}..{}){}", + start_with_comments + 1 - first_line_start, + end_with_comments - last_line_start, + src_from, + ), + EColor::InCodePositionLine ) - .bright_black() )?; } let lines = if cfg.show_comments { @@ -441,7 +564,7 @@ impl CheckError { for (highlight_index, (highlight_pos, color)) in highlights.iter().enumerate() { - if let Some(color) = color { + if let Some(color) = *color { let (highlight_start, highlight_end) = if cfg.show_comments { ( @@ -515,7 +638,7 @@ impl CheckError { f, "{}{}", " ".repeat(hl_space), - "~".repeat(hl_len).color(*color) + &cfg.theme.color_to(&"~".repeat(hl_len), color) )?; } } @@ -538,21 +661,20 @@ impl CheckError { } impl From for CheckError { fn from(value: String) -> Self { - Self::new().msg(value) + Self::new().msg_str(value) } } impl From<&str> for CheckError { fn from(value: &str) -> Self { - Self::new().msg(value.to_owned()) + value.to_owned().into() } } impl Debug for CheckError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self}") - } -} -impl Display for CheckError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.display()) + #[cfg(feature = "ecolor-term")] + let e = self.display_term(); + #[cfg(not(feature = "ecolor-term"))] + let e = self.display_notheme(); + write!(f, "{e}") } } diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index 710d108..15d4b38 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -4,7 +4,7 @@ use line_span::{LineSpan, LineSpanExt}; use crate::{ data::Type, - errors::{error_colors, CheckError, SourcePos}, + errors::{CheckError, EColor, SourcePos}, program::{ self, parsed::{block::Block, CompInfo}, @@ -53,7 +53,7 @@ pub fn check_mut( for (try_stmt, used) in info.global.unused_try_statements.lock().unwrap().iter() { if used.iter().any(|v| v.is_some()) { let err = err.get_or_insert_with(|| { - CheckError::new().msg(format!( + CheckError::from(format!( "There are `.try` statements with unused functions!" )) }); @@ -62,7 +62,7 @@ pub fn check_mut( .enumerate() .filter_map(|v| Some((v.0, v.1.clone()?))) .collect::>(); - err.msg_mut(format!( + err.msg_mut_str(format!( "Here, {}function{} {} {} unused:", if unused.len() == 1 { "the " } else { "" }, if unused.len() == 1 { "" } else { "s" }, @@ -87,9 +87,9 @@ pub fn check_mut( src.push(( src_range, Some(if i % 2 == 0 { - error_colors::TryUnusedFunction1 + EColor::TryUnusedFunction1 } else { - error_colors::TryUnusedFunction2 + EColor::TryUnusedFunction2 }), )); } diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 3dc9be9..03a7d0a 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -3,7 +3,7 @@ use std::{path::PathBuf, sync::Arc}; use super::{Source, SourceFrom, SourcePos}; use crate::{ data::Data, - errors::{error_colors, CheckError}, + errors::{CheckError, EColor}, program::{ self, parsed::{as_type::AsType, MersStatement}, @@ -30,9 +30,9 @@ pub fn parse( let name = name.trim().to_owned(); src.skip_whitespace(); if !matches!(src.next_char(), Some(']')) { - return Err( - CheckError::new().msg(format!("Expected ']' after type name in [[type_name]]")) - ); + return Err(CheckError::from(format!( + "Expected ']' after type name in [[type_name]]" + ))); } src.skip_whitespace(); if src.peek_word_allow_colon() == ":=" { @@ -43,12 +43,12 @@ pub fn parse( Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `[[...] := ...]` type definition"))) + .msg_str(format!("EOF after `[[...] := ...]` type definition"))) } Err(e) => return Err(e), }; if !matches!(src.next_char(), Some(']')) { - return Err(CheckError::new().msg(format!( + return Err(CheckError::new().msg_str(format!( "Expected ']' after statement in [[type_name] := statement]" ))); } @@ -63,7 +63,7 @@ pub fn parse( let as_type = super::types::parse_type(src, srca)?; src.skip_whitespace(); if !matches!(src.next_char(), Some(']')) { - return Err(CheckError::new().msg(format!( + return Err(CheckError::new().msg_str(format!( "Expected ']' after type definition in [[type_name] type_definition]" ))); } @@ -84,16 +84,16 @@ pub fn parse( return Err(CheckError::new() .src(vec![( (pos_in_src, src.get_pos(), srca).into(), - Some(error_colors::TypeAnnotationNoClosingBracket), + Some(EColor::TypeAnnotationNoClosingBracket), )]) - .msg(format!("Missing closing bracket ']' after type annotation"))); + .msg_str(format!("Missing closing bracket ']' after type annotation"))); } let statement = match parse(src, srca) { Ok(Some(v)) => v, Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `[...]` type annotation"))) + .msg_str(format!("EOF after `[...]` type annotation"))) } Err(e) => return Err(e), }; @@ -120,7 +120,7 @@ pub fn parse( let source = parse(src, srca)?.ok_or_else(|| { CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `:=`")) + .msg_str(format!("EOF after `:=`")) })?; first = Box::new(program::parsed::init_to::InitTo { pos_in_src: (first.source_range().start(), src.get_pos(), srca).into(), @@ -137,7 +137,7 @@ pub fn parse( (first.source_range().start(), src.get_pos(), srca).into(), None, )]) - .msg(format!("EOF after `=`")) + .msg_str(format!("EOF after `=`")) })?; first = Box::new(program::parsed::assign_to::AssignTo { pos_in_src: (pos_in_src, src.get_pos(), srca).into(), @@ -153,7 +153,7 @@ pub fn parse( Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `->`"))) + .msg_str(format!("EOF after `->`"))) } Err(e) => return Err(e), }; @@ -182,10 +182,10 @@ pub fn parse( pos_after_first = src.get_pos(); } else { return Err(CheckError::new() - .msg(format!("Expected `(` after `.try`")) + .msg_str(format!("Expected `(` after `.try`")) .src(vec![( (dot_in_src, src.get_pos(), srca).into(), - Some(error_colors::TryBadSyntax), + Some(EColor::TryBadSyntax), )])); } } else { @@ -194,7 +194,7 @@ pub fn parse( Ok(None) => { return Err(CheckError::new() .src(vec![((dot_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `.`"))) + .msg_str(format!("EOF after `.`"))) } Err(e) => return Err(e), }; @@ -265,16 +265,16 @@ pub fn parse_no_chain( if src.peek_char().is_none() { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after #"))); + .msg_str(format!("EOF after #"))); } if src.peek_char().is_some_and(|ch| ch.is_whitespace()) { src.skip_whitespace(); return Err(CheckError::new() .src(vec![( (pos_in_src, src.get_pos(), srca).into(), - Some(error_colors::WhitespaceAfterHashtag), + Some(EColor::WhitespaceAfterHashtag), )]) - .msg(format!("Whitespace after #"))); + .msg_str(format!("Whitespace after #"))); } match src.next_word() { "include" => { @@ -313,19 +313,19 @@ pub fn parse_no_chain( ((pos_in_src, end_in_src, srca).into(), None), ( (string_in_src, src.get_pos(), srca).into(), - Some(error_colors::HashIncludeCantLoadFile), + Some(EColor::HashIncludeCantLoadFile), ), ]) - .msg(format!("Can't load file '{file_path_str}': {e}"))); + .msg_str(format!("Can't load file '{file_path_str}': {e}"))); } } } else { return Err(CheckError::new() .src(vec![ ((pos_in_src, end_in_src, srca).into(), None), - ((string_in_src, src.get_pos(), srca).into(), Some(error_colors::HashIncludeNotAString)), + ((string_in_src, src.get_pos(), srca).into(), Some(EColor::HashIncludeNotAString)), ]) - .msg(format!( + .msg_str(format!( "#include must be followed by a string literal like \"file.mers\" (\" expected)." ))); } @@ -335,9 +335,9 @@ pub fn parse_no_chain( return Err(CheckError::new() .src(vec![( (pos_in_src, src.get_pos(), srca).into(), - Some(error_colors::HashUnknown), + Some(EColor::HashUnknown), )]) - .msg(msg)); + .msg_str(msg)); } } } @@ -371,7 +371,7 @@ pub fn parse_no_chain( (pos_in_src, src.get_pos(), srca).into(), None, )]) - .msg(format!("EOF after `:` in object"))) + .msg_str(format!("EOF after `:` in object"))) } Err(e) => { return Err(CheckError::new() @@ -379,7 +379,9 @@ pub fn parse_no_chain( (pos_in_src, src.get_pos(), srca).into(), None, )]) - .msg(format!("Error in statement after `:` in object")) + .msg_str(format!( + "Error in statement after `:` in object" + )) .err(e)) } }, @@ -430,7 +432,7 @@ pub fn parse_no_chain( Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF in `if`"))) + .msg_str(format!("EOF in `if`"))) } Err(e) => return Err(e), }; @@ -439,7 +441,7 @@ pub fn parse_no_chain( Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `if `"))) + .msg_str(format!("EOF after `if `"))) } Err(e) => return Err(e), }; @@ -453,7 +455,7 @@ pub fn parse_no_chain( Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `else`"))) + .msg_str(format!("EOF after `else`"))) } Err(e) => return Err(e), }) @@ -476,7 +478,7 @@ pub fn parse_no_chain( Ok(None) => { return Err(CheckError::new() .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) - .msg(format!("EOF after `loop`"))) + .msg_str(format!("EOF after `loop`"))) } Err(e) => return Err(e), }; @@ -578,17 +580,17 @@ pub fn parse_string_custom_end( return Err(CheckError::new() .src(vec![( (backslash_in_src, src.get_pos(), srca).into(), - Some(error_colors::BackslashEscapeUnknown), + Some(EColor::BackslashEscapeUnknown), )]) - .msg(format!("unknown backslash escape '\\{o}'"))); + .msg_str(format!("unknown backslash escape '\\{o}'"))); } None => { return Err(CheckError::new() .src(vec![( (backslash_in_src, src.get_pos(), srca).into(), - Some(error_colors::BackslashEscapeEOF), + Some(EColor::BackslashEscapeEOF), )]) - .msg(format!("EOF in backslash escape"))); + .msg_str(format!("EOF in backslash escape"))); } }); } else if ch == closing_char { @@ -600,9 +602,9 @@ pub fn parse_string_custom_end( return Err(CheckError::new() .src(vec![( (opening, src.get_pos(), srca).into(), - Some(error_colors::StringEOF), + Some(EColor::StringEOF), )]) - .msg(format!( + .msg_str(format!( "EOF in string literal{}", if closing_char != '"' { format!( diff --git a/mers_lib/src/parsing/types.rs b/mers_lib/src/parsing/types.rs index 5e92b5a..9767e45 100755 --- a/mers_lib/src/parsing/types.rs +++ b/mers_lib/src/parsing/types.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::{ data::{self, Type}, - errors::{error_colors, CheckError}, + errors::{CheckError, EColor}, }; use super::Source; @@ -35,7 +35,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result) -> Result` but found `,`.")) - .msg(format!("If you wanted this to be a tuple type instead, you may have used `Input -> Output` instead of `(Input -> Output)` for a function type somewhere."))); + ]).msg_str(format!("Unexpected character in function type, expected arrow `->` but found `,`.")) + .msg_str(format!("If you wanted this to be a tuple type instead, you may have used `Input -> Output` instead of `(Input -> Output)` for a function type somewhere."))); } } Some('-') if src.peek_word() == "->" => { @@ -92,8 +92,8 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result break, _ => return Err(CheckError::new().src(vec![ ((pos_in_src, src.get_pos(), srca).into(), None), - ((pos2, src.get_pos(), srca).into(), Some(error_colors::BadCharInFunctionType)), - ]).msg(format!("Expected comma `,` after `In -> Out` part of function type"))) + ((pos2, src.get_pos(), srca).into(), Some(EColor::BadCharInFunctionType)), + ]).msg_str(format!("Expected comma `,` after `In -> Out` part of function type"))) } } else { let pos1 = src.get_pos(); @@ -102,10 +102,10 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result`.")) - .msg(format!("If you wanted to write a function type, use `(Input -> Output)` instead of `Input -> Output`."))); + ]).msg_str(format!("Unexpected character in tuple type, expected comma `,` but found arrow `->`.")) + .msg_str(format!("If you wanted to write a function type, use `(Input -> Output)` instead of `Input -> Output`."))); } } _ => { @@ -116,10 +116,10 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result) -> Result as_type.add_all(&*t), Some(Err(_)) => { - return Err(CheckError::new().msg(format!( + return Err(CheckError::new().msg_str(format!( "Type: specified type without info, but type needs additional info" ))) } - None => return Err(CheckError::new().msg(format!("Unknown type '{name}'"))), + None => return Err(CheckError::new().msg_str(format!("Unknown type '{name}'"))), }, ParsedType::TypeWithInfo(name, additional_info) => match info .scopes @@ -248,12 +248,12 @@ pub fn type_from_parsed( .find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v)) { Some(Ok(t)) => { - return Err(CheckError::new().msg(format!( + return Err(CheckError::new().msg_str(format!( "Type: specified type with info, but type {t} doesn't need it" ))) } Some(Err(f)) => as_type.add_all(&*f(&additional_info, info)?), - None => return Err(CheckError::new().msg(format!("Unknown type '{name}'"))), + None => return Err(CheckError::new().msg_str(format!("Unknown type '{name}'"))), }, } } diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index 233860d..1136814 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -44,7 +44,7 @@ impl Config { return Err(format!("Function returns a value of type {out}, which isn't included in the type of the reference, {arg}.").into()); } }, - Err(e) => return Err(CheckError::new().msg(format!("Invalid argument type {arg} for function")).err(e)), + Err(e) => return Err(CheckError::new().msg_str(format!("Invalid argument type {arg} for function")).err(e)), } } else { return Err(format!("Arguments must be (reference, function)").into()); diff --git a/mers_lib/src/program/configs/with_multithreading.rs b/mers_lib/src/program/configs/with_multithreading.rs index 7f318e2..3c878ff 100755 --- a/mers_lib/src/program/configs/with_multithreading.rs +++ b/mers_lib/src/program/configs/with_multithreading.rs @@ -38,7 +38,7 @@ impl Config { if let Some(f) = t.as_any().downcast_ref::() { match f.o(&Type::empty_tuple()) { Ok(t) => out.add_all(&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)) + Err(e) => return Err(CheckError::new().msg_str(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()); @@ -69,7 +69,7 @@ impl Config { out: Arc::new(|a, _i| { for t in a.types.iter() { if !t.as_any().is::() { - return Err(CheckError::new().msg(format!("Cannot call thread_finished on a value of type {t}, which isn't a thread but part of the argument {a}."))); + return Err(CheckError::new().msg_str(format!("Cannot call thread_finished on a value of type {t}, which isn't a thread but part of the argument {a}."))); } } Ok(Type::new(data::bool::BoolT)) @@ -93,7 +93,7 @@ impl Config { if let Some(t) = t.as_any().downcast_ref::() { out.add_all(&t.0); } else { - return Err(CheckError::new().msg(format!("Cannot call thread_await on a value of type {t}, which isn't a thread but part of the argument {a}."))); + return Err(CheckError::new().msg_str(format!("Cannot call thread_await on a value of type {t}, which isn't a thread but part of the argument {a}."))); } } Ok(out) diff --git a/mers_lib/src/program/configs/with_stdio.rs b/mers_lib/src/program/configs/with_stdio.rs index 22ffbf5..cd75cd8 100755 --- a/mers_lib/src/program/configs/with_stdio.rs +++ b/mers_lib/src/program/configs/with_stdio.rs @@ -3,11 +3,9 @@ use std::{ sync::{Arc, Mutex}, }; -use colored::Colorize; - use crate::{ data::{self, Data, Type}, - errors::error_colors, + errors::{CheckError, EColor}, program::{self, run::CheckInfo}, }; @@ -33,11 +31,10 @@ impl Config { Arc::new(data::tuple::TupleT(vec![])), ])) } else { - Err(format!( - "expected (), got {}", - a.to_string().color(error_colors::FunctionArgument) - ) - .into()) + Err(CheckError::new().msg(vec![ + ("expected (), got ".to_owned(), None), + (a.to_string(), Some(EColor::FunctionArgument)), + ])) } }), run: Arc::new(|_a, _i| { diff --git a/mers_lib/src/program/parsed/include_mers.rs b/mers_lib/src/program/parsed/include_mers.rs index 6940fea..11471f2 100644 --- a/mers_lib/src/program/parsed/include_mers.rs +++ b/mers_lib/src/program/parsed/include_mers.rs @@ -1,10 +1,8 @@ use std::sync::{Arc, Mutex}; -use colored::Colorize; - use crate::{ data::{self, Data}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, info::{self, Local}, parsing::Source, program::{self}, @@ -36,13 +34,13 @@ impl MersStatement for IncludeMers { return Err(CheckError::new() .src(vec![( self.pos_in_src.clone(), - Some(error_colors::HashIncludeErrorInIncludedFile), + Some(EColor::HashIncludeErrorInIncludedFile), )]) - .msg( + .msg(vec![( "Error in #include! (note: inner errors may refer to a different file)" - .color(error_colors::HashIncludeErrorInIncludedFile) - .to_string(), - ) + .to_owned(), + Some(EColor::HashIncludeErrorInIncludedFile), + )]) .err_with_diff_src(e)) } }; diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index f4d388f..ae9e735 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -1,5 +1,5 @@ use crate::{ - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, info::Local, program::{self}, }; @@ -50,9 +50,9 @@ impl MersStatement for Variable { return Err(CheckError::new() .src(vec![( self.pos_in_src.clone(), - Some(error_colors::UnknownVariable), + Some(EColor::UnknownVariable), )]) - .msg(format!("No variable named '{}' found!", self.var))); + .msg_str(format!("No variable named '{}' found!", self.var))); }, })) } diff --git a/mers_lib/src/program/run/as_type.rs b/mers_lib/src/program/run/as_type.rs index c1d79dc..7d63219 100644 --- a/mers_lib/src/program/run/as_type.rs +++ b/mers_lib/src/program/run/as_type.rs @@ -1,8 +1,6 @@ -use colored::Colorize; - use crate::{ data::{Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, parsing::types::ParsedType, }; @@ -33,7 +31,7 @@ impl MersStatement for AsType { CheckError::new() .src(vec![( self.type_pos_in_src.clone(), - Some(error_colors::BadTypeFromParsed), + Some(EColor::BadTypeFromParsed), )]) .err(e) })?; @@ -43,22 +41,23 @@ impl MersStatement for AsType { (self.pos_in_src.clone(), None), ( self.type_pos_in_src.clone(), - Some(error_colors::AsTypeTypeAnnotation), + Some(EColor::AsTypeTypeAnnotation), ), ( self.statement.source_range(), - Some(error_colors::AsTypeStatementWithTooBroadType), + Some(EColor::AsTypeStatementWithTooBroadType), ), ]) - .msg(format!( - "Type must be included in {}, but the actual type {} isn't.", - as_type - .to_string() - .color(error_colors::AsTypeTypeAnnotation), - return_type - .to_string() - .color(error_colors::AsTypeStatementWithTooBroadType) - ))); + .msg(vec![ + ("Type must be included in ".to_owned(), None), + (as_type.to_string(), Some(EColor::AsTypeTypeAnnotation)), + (", but the actual type ".to_owned(), None), + ( + return_type.to_string(), + Some(EColor::AsTypeStatementWithTooBroadType), + ), + (" isn't.".to_owned(), None), + ])); } Ok(if self.expand_type { as_type.clone() diff --git a/mers_lib/src/program/run/assign_to.rs b/mers_lib/src/program/run/assign_to.rs index 6f4cb3f..0295925 100755 --- a/mers_lib/src/program/run/assign_to.rs +++ b/mers_lib/src/program/run/assign_to.rs @@ -1,8 +1,6 @@ -use colored::Colorize; - use crate::{ data::{self, Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, }; use super::{CheckInfo, MersStatement}; @@ -37,10 +35,10 @@ impl MersStatement for AssignTo { return Err(CheckError::new() .src(vec![ (self.pos_in_src.clone(), None), - (self.target.source_range(), Some(error_colors::InitTo)), - (self.source.source_range(), Some(error_colors::InitFrom)), + (self.target.source_range(), Some(EColor::InitTo)), + (self.source.source_range(), Some(EColor::InitFrom)), ]) - .msg(format!("Cannot initialize:")) + .msg_str(format!("Cannot initialize:")) .err(e)); } }; @@ -50,15 +48,17 @@ impl MersStatement for AssignTo { return Err(CheckError::new() .src(vec![ (self.pos_in_src.clone(), None), - (self.target.source_range(), Some(error_colors::AssignTo)), - (self.source.source_range(), Some(error_colors::AssignFrom)), + (self.target.source_range(), Some(EColor::AssignTo)), + (self.source.source_range(), Some(EColor::AssignFrom)), ]) - .msg(format!( - "can't assign {} to {} because it isn't included in {}", - source.to_string().color(error_colors::AssignFrom), - target.to_string().color(error_colors::AssignTo), - t - ))); + .msg(vec![ + ("can't assign ".to_owned(), None), + (source.to_string(), Some(EColor::AssignFrom)), + (" to ".to_owned(), None), + (target.to_string(), Some(EColor::AssignTo)), + (" because it isn't included in ".to_owned(), None), + (t.to_string(), None), + ])); } } else { return Err(CheckError::new() @@ -66,13 +66,16 @@ impl MersStatement for AssignTo { (self.pos_in_src.clone(), None), ( self.target.source_range(), - Some(error_colors::AssignTargetNonReference), + Some(EColor::AssignTargetNonReference), ), ]) - .msg(format!( - "can't assign to {}", - "non-reference!".color(error_colors::AssignTargetNonReference) - ))); + .msg(vec![ + ("can't assign to ".to_owned(), None), + ( + "non-reference!".to_owned(), + Some(EColor::AssignTargetNonReference), + ), + ])); } } Ok(Type::empty_tuple()) diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index 1fad44f..2edfe25 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -1,8 +1,6 @@ -use colored::Colorize; - use crate::{ data::{Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, parsing::Source, }; @@ -44,29 +42,27 @@ impl MersStatement for Chain { CheckError::new() .src(vec![( self.pos_in_src.clone(), - Some(error_colors::HashIncludeErrorInIncludedFile), + Some(EColor::HashIncludeErrorInIncludedFile), + )]) + .msg(vec![( + "Error in #include:".to_owned(), + Some(EColor::HashIncludeErrorInIncludedFile), )]) - .msg( - "Error in #include:" - .color(error_colors::HashIncludeErrorInIncludedFile) - .to_string(), - ) .err_with_diff_src(e) } else { CheckError::new() .src(vec![ (self.pos_in_src.clone(), None), - ( - self.first.source_range(), - Some(error_colors::FunctionArgument), - ), - (self.chained.source_range(), Some(error_colors::Function)), + (self.first.source_range(), Some(EColor::FunctionArgument)), + (self.chained.source_range(), Some(EColor::Function)), + ]) + .msg(vec![ + ("Can't call ".to_owned(), None), + ("this function".to_owned(), Some(EColor::Function)), + (" with an argument of type ".to_owned(), None), + (arg.to_string(), Some(EColor::FunctionArgument)), + (":".to_owned(), None), ]) - .msg(format!( - "Can't call {} with an argument of type {}:", - "this function".color(error_colors::Function), - arg.to_string().color(error_colors::FunctionArgument) - )) .err(e) }) } @@ -77,13 +73,14 @@ impl MersStatement for Chain { (self.pos_in_src.clone(), None), ( self.chained.source_range(), - Some(error_colors::ChainWithNonFunction), + Some(EColor::ChainWithNonFunction), ), ]) - .msg(format!( - "cannot chain with a non-function ({})", - func.to_string().color(error_colors::ChainWithNonFunction) - ))); + .msg(vec![ + ("cannot chain with a non-function (".to_owned(), None), + (func.to_string(), Some(EColor::ChainWithNonFunction)), + (")".to_owned(), None), + ])); } } Ok(o) @@ -98,12 +95,12 @@ impl MersStatement for Chain { Err(e) => Err(if let Some(_) = &self.as_part_of_include { CheckError::new().err_with_diff_src(e).src(vec![( self.pos_in_src.clone(), - Some(error_colors::StacktraceDescendHashInclude), + Some(EColor::StacktraceDescendHashInclude), )]) } else { CheckError::new().err(e).src(vec![ (self.pos_in_src.clone(), None), - (self.source_range(), Some(error_colors::StacktraceDescend)), + (self.source_range(), Some(EColor::StacktraceDescend)), ]) }), } diff --git a/mers_lib/src/program/run/custom_type.rs b/mers_lib/src/program/run/custom_type.rs index 910f8d4..d3b9bbf 100644 --- a/mers_lib/src/program/run/custom_type.rs +++ b/mers_lib/src/program/run/custom_type.rs @@ -1,10 +1,8 @@ use std::{fmt::Debug, sync::Arc}; -use colored::Colorize; - use crate::{ data::{Data, Type}, - errors::{CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, }; use super::{CheckInfo, Info, MersStatement}; @@ -45,12 +43,17 @@ impl MersStatement for CustomType { } else { if let Err(e) = t { return Err(CheckError::new() - .msg(format!( - " {} {} {} (`[[_] := ...]` indicates that `...` must be type-correct)", - "<<".bright_red(), - "Custom type-test failed!".bright_red(), - ">>".bright_red(), - )) + .msg(vec![ + ( + " << Custom type-test failed! >>".to_owned(), + Some(EColor::CustomTypeTestFailed), + ), + ( + " (`[[_] := ...]` indicates that `...` must be type-correct)" + .to_owned(), + None, + ), + ]) .err(e)); } } diff --git a/mers_lib/src/program/run/if.rs b/mers_lib/src/program/run/if.rs index 019f919..7846e27 100755 --- a/mers_lib/src/program/run/if.rs +++ b/mers_lib/src/program/run/if.rs @@ -1,10 +1,8 @@ use std::sync::Arc; -use colored::Colorize; - use crate::{ data::{self, tuple::TupleT, Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, }; use super::MersStatement; @@ -33,16 +31,21 @@ impl MersStatement for If { (self.pos_in_src.clone(), None), ( self.condition.source_range(), - Some(error_colors::IfConditionNotBool), + Some(EColor::IfConditionNotBool), ), ]) - .msg(format!( - "The {} in an if-statement must return bool, not {}", - "condition".color(error_colors::IfConditionNotBool), - cond_return_type - .to_string() - .color(error_colors::IfConditionNotBool), - ))); + .msg(vec![ + ("The ".to_owned(), None), + ("condition".to_owned(), Some(EColor::IfConditionNotBool)), + ( + " in an if-statement must return bool, not ".to_owned(), + None, + ), + ( + cond_return_type.to_string(), + Some(EColor::IfConditionNotBool), + ), + ])); } let mut t = self.on_true.check(info, None)?; if let Some(f) = &self.on_false { diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 72c0439..3718091 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -7,7 +7,7 @@ use std::{ use crate::{ data::{self, Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, info, }; @@ -98,10 +98,10 @@ pub trait MersStatement: Debug + Send + Sync { if let Some(cutoff) = info.global.limit_runtime { if Instant::now() >= cutoff { return Err(CheckError::new() - .msg("maximum runtime exceeded".to_owned()) + .msg_str("maximum runtime exceeded".to_owned()) .src(vec![( self.source_range(), - Some(error_colors::MaximumRuntimeExceeded), + Some(EColor::MaximumRuntimeExceeded), )])); } } diff --git a/mers_lib/src/program/run/object.rs b/mers_lib/src/program/run/object.rs index 6671944..b916335 100644 --- a/mers_lib/src/program/run/object.rs +++ b/mers_lib/src/program/run/object.rs @@ -1,10 +1,8 @@ use std::collections::VecDeque; -use colored::Colorize; - use crate::{ data::{self, object::ObjectT, Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, }; use super::MersStatement; @@ -31,34 +29,47 @@ impl MersStatement for Object { for (i, ((sn, _), (tn, t))) in self.elems.iter().zip(t.0.iter()).enumerate() { if sn != tn { - return Err(format!("can't init an {} with type {}{} - field mismatch: {sn} != {tn}", "object".color(error_colors::InitTo), - t.to_string().color(error_colors::InitFrom), - if print_is_part_of { - format!( - ", which is part of {}", - init_to.to_string().color(error_colors::InitFrom) - ) - } else { - format!("") - } - ).into()); + return Err(CheckError::new().msg(vec![ + ("can't init an ".to_owned(), None), + ("object".to_owned(), Some(EColor::InitTo)), + (" with type ".to_owned(), None), + (t.to_string(), Some(EColor::InitFrom)), + if print_is_part_of { + (", which is part of ".to_owned(), None) + } else { + (String::new(), None) + }, + if print_is_part_of { + (init_to.to_string(), Some(EColor::InitFrom)) + } else { + (String::new(), None) + }, + (" - field mismatch: ".to_owned(), None), + (sn.to_owned(), None), + (" != ".to_owned(), None), + (tn.to_owned(), None), + ])); } acc[i].add_all(&t); } } else { - return Err(format!( - "can't init an {} with type {}{} - source has {}", - "object".color(error_colors::InitTo), - t.to_string().color(error_colors::InitFrom), + return Err(CheckError::new().msg(vec![ + ("can't init an ".to_owned(), None), + ("object".to_owned(), Some(EColor::InitTo)), + (" with type ".to_owned(), None), + (t.to_string(), Some(EColor::InitFrom)), if print_is_part_of { - format!( - ", which is part of {}", - init_to.to_string().color(error_colors::InitFrom) - ) + (", which is part of ".to_owned(), None) } else { - format!("") + (format!(""), None) }, - if self.elems.len() > t.0.len() { + if print_is_part_of { + (init_to.to_string(), Some(EColor::InitFrom)) + } else { + (format!(""), None) + }, + (" - source has ".to_owned(), None), + (if self.elems.len() > t.0.len() { format!("less fields ({}, not {})", t.0.len(), self.elems.len()) } else { format!( @@ -74,25 +85,30 @@ impl MersStatement for Object { .collect::(), data::object::ObjectT(t.0.iter().take(self.elems.len()).cloned().collect()) ) - } - ) - .into()); + }, None) + ])); } } else { - return Err(format!( - "can't init an {} with type {}{} - only objects can be assigned to objects", - "object".color(error_colors::InitTo), - t.to_string().color(error_colors::InitFrom), + return Err(CheckError::new().msg(vec![ + ("can't init an ".to_owned(), None), + ("object".to_owned(), Some(EColor::InitTo)), + (" with type ".to_owned(), None), + (t.to_string(), Some(EColor::InitFrom)), if print_is_part_of { - format!( - ", which is part of {}", - init_to.to_string().color(error_colors::InitFrom) - ) + (", which is part of ".to_owned(), None) } else { - format!("") - } - ) - .into()); + (format!(""), None) + }, + if print_is_part_of { + (init_to.to_string(), Some(EColor::InitFrom)) + } else { + (format!(""), None) + }, + ( + " - only objects can be assigned to objects".to_owned(), + None, + ), + ])); } } Some(acc) diff --git a/mers_lib/src/program/run/try.rs b/mers_lib/src/program/run/try.rs index 0df64e8..9f5173d 100644 --- a/mers_lib/src/program/run/try.rs +++ b/mers_lib/src/program/run/try.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex}; use crate::{ data::{self, Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, }; use super::{Info, MersStatement}; @@ -62,16 +62,13 @@ impl MersStatement for Try { } } else { return Err(CheckError::new() - .msg(format!( + .msg_str(format!( "try: #{} is not a function, type is {ft} within {func}.", i + 1 )) .src(vec![ (self.source_range(), None), - ( - self.funcs[i].source_range(), - Some(error_colors::TryNotAFunction), - ), + (self.funcs[i].source_range(), Some(EColor::TryNotAFunction)), ])); } } @@ -88,15 +85,17 @@ impl MersStatement for Try { } if !found { let mut err = CheckError::new() - .msg(format!( + .msg_str(format!( "try: no function found for argument of type {arg}." )) .src(vec![( self.pos_in_src.clone(), - Some(error_colors::TryNoFunctionFound), + Some(EColor::TryNoFunctionFound), )]); for (i, e) in errs.into_iter().enumerate() { - err = err.msg(format!("Error for function #{}:", i + 1)).err(e); + err = err + .msg_str(format!("Error for function #{}:", i + 1)) + .err(e); } return Err(err); } diff --git a/mers_lib/src/program/run/tuple.rs b/mers_lib/src/program/run/tuple.rs index 9120fdd..8a1a7d9 100755 --- a/mers_lib/src/program/run/tuple.rs +++ b/mers_lib/src/program/run/tuple.rs @@ -1,10 +1,8 @@ use std::collections::VecDeque; -use colored::Colorize; - use crate::{ data::{self, tuple::TupleT, Data, Type}, - errors::{error_colors, CheckError, SourceRange}, + errors::{CheckError, EColor, SourceRange}, }; use super::MersStatement; @@ -32,33 +30,54 @@ impl MersStatement for Tuple { vec[i].add_all(&e); } } else { - return Err( - format!("can't init a {} with type {}{} - only tuples with the same length ({}) can be assigned.", - "tuple".color(error_colors::InitTo), - t.to_string().color(error_colors::InitFrom), + return Err(CheckError::new().msg(vec![ + ("can't init a ".to_owned(), None), + ("tuple".to_owned(), Some(EColor::InitTo)), + (" with type ".to_owned(), None), + (t.to_string(), Some(EColor::InitFrom)), + ( if print_is_part_of { - format!(", which is part of {}", init_to.to_string().color(error_colors::InitFrom)) + ", which is part of ".to_owned() } else { - format!("") + String::new() }, - self.elems.len() - ).into()); + None, + ), + if print_is_part_of { + (init_to.to_string(), Some(EColor::InitFrom)) + } else { + (String::new(), None) + }, + ( + format!( + " - only tuples with the same length ({}) can be assigned", + self.elems.len() + ), + None, + ), + ])); } } else { - return Err(format!( - "can't init a {} with type {}{} - only tuples can be assigned to tuples", - "tuple".color(error_colors::InitTo), - t.to_string().color(error_colors::InitFrom), + return Err(CheckError::new().msg(vec![ + ("can't init a ".to_owned(), None), + ("tuple".to_owned(), Some(EColor::InitTo)), + (" with type ".to_owned(), None), + (t.to_string(), Some(EColor::InitFrom)), + ( + if print_is_part_of { + ", which is part of ".to_owned() + } else { + String::new() + }, + None, + ), if print_is_part_of { - format!( - ", which is part of {}", - init_to.to_string().color(error_colors::InitFrom) - ) + (init_to.to_string(), Some(EColor::InitFrom)) } else { - format!("") - } - ) - .into()); + (String::new(), None) + }, + (" - only tuples can be assigned to tuples".to_owned(), None), + ])); } } Some(vec)