add support for custom theming in mers errors

this also includes support for the NoTheme,
a theme which doesn't add any color to mers'
output.
If you compile mers with --no-default-features,
the `colored` dependency will disappear and
mers_lib will fall back to NoTheme.
This commit is contained in:
Mark 2024-06-26 01:02:19 +02:00
parent f055d2089f
commit a78367f27c
24 changed files with 517 additions and 336 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mers" name = "mers"
version = "0.8.13" version = "0.8.14"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "dynamically typed but type-checked programming language" 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 # 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] [dependencies]
mers_lib = "0.8.13" # mers_lib = "0.8.14"
# mers_lib = { path = "../mers_lib" } mers_lib = { path = "../mers_lib" }
clap = { version = "4.3.19", features = ["derive"] } clap = { version = "4.3.19", features = ["derive"] }
colored = "2.1.0" colored = { version = "2.1.0", optional = true }

View File

@ -3,6 +3,7 @@ use mers_lib::prelude_compile::*;
use std::{path::PathBuf, process::exit, sync::Arc}; use std::{path::PathBuf, process::exit, sync::Arc};
mod cfg_globals; mod cfg_globals;
#[cfg(feature = "colored-output")]
mod pretty_print; mod pretty_print;
#[derive(Parser)] #[derive(Parser)]
@ -35,7 +36,14 @@ enum Command {
#[command(subcommand)] #[command(subcommand)]
source: From, 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 /// Add syntax highlighting to the code
#[cfg(feature = "colored-output")]
PrettyPrint { PrettyPrint {
#[command(subcommand)] #[command(subcommand)]
source: From, source: From,
@ -82,19 +90,19 @@ fn main() {
let srca = Arc::new(src.clone()); let srca = Arc::new(src.clone());
match parse(&mut src, &srca) { match parse(&mut src, &srca) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(20); exit(20);
} }
Ok(parsed) => { Ok(parsed) => {
let (i1, _, i3) = config.infos(); let (i1, _, i3) = config.infos();
match compile(&*parsed, i1) { match compile(&*parsed, i1) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(24); exit(24);
} }
Ok(compiled) => match check(&*compiled, i3) { Ok(compiled) => match check(&*compiled, i3) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(28); exit(28);
} }
Ok(output_type) => eprintln!("{output_type}"), Ok(output_type) => eprintln!("{output_type}"),
@ -108,24 +116,24 @@ fn main() {
let srca = Arc::new(src.clone()); let srca = Arc::new(src.clone());
match parse(&mut src, &srca) { match parse(&mut src, &srca) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(255); exit(255);
} }
Ok(parsed) => { Ok(parsed) => {
let (i1, mut i2, i3) = config.infos(); let (i1, mut i2, i3) = config.infos();
match compile(&*parsed, i1) { match compile(&*parsed, i1) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(255); exit(255);
} }
Ok(compiled) => match check(&*compiled, i3) { Ok(compiled) => match check(&*compiled, i3) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(255); exit(255);
} }
Ok(_) => { Ok(_) => {
if let Err(e) = compiled.run(&mut i2) { if let Err(e) = compiled.run(&mut i2) {
eprintln!("Error while running:\n{e}"); eprintln!("Error while running:\n{e:?}");
std::process::exit(1); std::process::exit(1);
} }
} }
@ -139,19 +147,19 @@ fn main() {
let srca = Arc::new(src.clone()); let srca = Arc::new(src.clone());
match parse(&mut src, &srca) { match parse(&mut src, &srca) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(255); exit(255);
} }
Ok(parsed) => { Ok(parsed) => {
let (i1, mut i2, _) = config.infos(); let (i1, mut i2, _) = config.infos();
match compile(&*parsed, i1) { match compile(&*parsed, i1) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(255); exit(255);
} }
Ok(compiled) => { Ok(compiled) => {
if let Err(e) = compiled.run(&mut i2) { if let Err(e) = compiled.run(&mut i2) {
eprintln!("Error while running:\n{e}"); eprintln!("Error while running:\n{e:?}");
std::process::exit(1); std::process::exit(1);
} }
} }
@ -159,8 +167,14 @@ fn main() {
} }
} }
} }
#[cfg(feature = "colored-output")]
Command::PrettyPrint { source } => { Command::PrettyPrint { source } => {
pretty_print::pretty_print(get_source(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);
}
} }
} }

View File

@ -7,7 +7,7 @@ pub fn pretty_print(mut src: Source) {
let srca = Arc::new(src.clone()); let srca = Arc::new(src.clone());
match parse(&mut src, &srca) { match parse(&mut src, &srca) {
Err(e) => { Err(e) => {
eprintln!("{e}"); eprintln!("{e:?}");
exit(28); exit(28);
} }
Ok(parsed) => { Ok(parsed) => {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mers_lib" name = "mers_lib"
version = "0.8.13" version = "0.8.14"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "library to use the mers language in other projects" description = "library to use the mers language in other projects"
@ -10,9 +10,14 @@ repository = "https://github.com/Dummi26/mers"
[features] [features]
default = ["parse"] default = ["parse"]
# for parsing and running mers code (for most situations: just enable parse)
parse = ["run"] parse = ["run"]
run = [] run = []
# color features are used when formatting error messages.
ecolor-term = ["dep:colored"]
[dependencies] [dependencies]
colored = "2.0.4" colored = { version = "2.0.4", optional = true }
line-span = "0.1.5" line-span = "0.1.5"

View File

@ -24,7 +24,7 @@ fn show(src: String) {
); );
eprintln!("{src}"); eprintln!("{src}");
match parse_compile_check_run(src) { match parse_compile_check_run(src) {
Err(e) => eprintln!("{e}"), Err(e) => eprintln!("{e:?}"),
Ok((t, v)) => eprintln!("Returned `{}` :: `{t}`", v.get()), Ok((t, v)) => eprintln!("Returned `{}` :: `{t}`", v.get()),
} }
} }

View File

@ -14,7 +14,7 @@ fn main() {
eprintln!("This is not:"); eprintln!("This is not:");
let e = run("5.rust_func".to_owned()).err().unwrap(); let e = run("5.rust_func".to_owned()).err().unwrap();
eprintln!("{e}"); eprintln!("{e:?}");
} }
fn run(src: String) -> Result<(), CheckError> { fn run(src: String) -> Result<(), CheckError> {
let mut source = Source::new_from_string(src); let mut source = Source::new_from_string(src);

View File

@ -1,10 +1,9 @@
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
rc::Rc, rc::Rc,
sync::{atomic::AtomicUsize, Arc}, sync::{atomic::AtomicU32, Arc},
}; };
use colored::{ColoredString, Colorize};
use line_span::LineSpans; use line_span::LineSpans;
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
@ -44,65 +43,169 @@ impl SourceRange {
&self.in_file &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)] #[derive(Clone)]
pub struct CheckError(pub Vec<CheckErrorComponent>); pub struct CheckError(pub Vec<CheckErrorComponent>);
#[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; UnknownVariable,
pub const HashUnknown: Color = Color::Red; WhitespaceAfterHashtag,
pub const HashIncludeCantLoadFile: Color = Color::Red; HashUnknown,
pub const HashIncludeNotAString: Color = Color::Red; HashIncludeCantLoadFile,
pub const HashIncludeErrorInIncludedFile: Color = Color::Red; 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; StacktraceDescend,
pub const BackslashEscapeEOF: Color = Color::Red; StacktraceDescendHashInclude,
pub const StringEOF: Color = Color::Red;
pub const IfConditionNotBool: Color = Color::Red; MaximumRuntimeExceeded,
pub const ChainWithNonFunction: Color = Color::Yellow;
pub const Function: Color = Color::BrightMagenta; InCodePositionLine,
pub const FunctionArgument: Color = Color::BrightBlue; }
pub const InitFrom: Color = Color::BrightCyan; pub trait Theme<T> {
pub const InitTo: Color = Color::Green; fn color(&self, text: &str, color: EColor, t: &mut T);
pub const AssignFrom: Color = InitFrom; }
pub const AssignTo: Color = InitTo; pub trait ThemeTo<T>: Theme<T> {
pub const AssignTargetNonReference: Color = Color::BrightYellow; fn color_to(&self, text: &str, color: EColor) -> T;
}
impl<T: Theme<String> + ?Sized> ThemeTo<String> 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<EColor>)>,
theme: &(impl Theme<String> + ?Sized),
) -> String {
let mut t = String::new();
colorize_gen(message, &mut t, |t, a| a.push_str(t), theme);
t
}
pub fn colorize_gen<T>(
message: &Vec<(String, Option<EColor>)>,
t: &mut T,
direct: impl Fn(&str, &mut T),
theme: &(impl Theme<T> + ?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 struct NoTheme;
pub const AsTypeTypeAnnotation: Color = InitTo; impl Theme<String> for NoTheme {
fn color(&self, text: &str, _color: EColor, t: &mut String) {
t.push_str(text);
}
}
pub const BadCharInTupleType: Color = Color::Red; #[cfg(feature = "ecolor-term")]
pub const BadCharInFunctionType: Color = Color::Red; pub struct TermDefaultTheme;
pub const BadTypeFromParsed: Color = Color::Blue; #[cfg(feature = "ecolor-term")]
pub const TypeAnnotationNoClosingBracket: Color = Color::Blue; impl Theme<String> 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; EColor::UnknownVariable => 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;
pub const StacktraceDescend: Color = Color::Yellow; EColor::WhitespaceAfterHashtag => Color::Red,
pub const StacktraceDescendHashInclude: Color = 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)] #[derive(Clone)]
pub enum CheckErrorComponent { pub enum CheckErrorComponent {
Message(String), Message(Vec<(String, Option<EColor>)>),
Error(CheckError), Error(CheckError),
ErrorWithDifferentSource(CheckError), ErrorWithDifferentSource(CheckError),
Source(Vec<(SourceRange, Option<colored::Color>)>), Source(Vec<(SourceRange, Option<EColor>)>),
} }
pub struct CheckErrorHRConfig { pub struct CheckErrorHRConfig {
color_index_ptr: Rc<AtomicUsize>, color_index_ptr: Rc<AtomicU32>,
color_index: usize, color_index: u32,
theme: Rc<dyn Theme<String>>,
is_inner: bool, is_inner: bool,
style: u8, style: u8,
idt_start: String, idt_start: String,
@ -113,7 +216,7 @@ pub struct CheckErrorHRConfig {
show_comments: bool, show_comments: bool,
} }
type BorderCharsSet = [[&'static str; 4]; 3]; type BorderCharsSet = [[&'static str; 4]; 3];
pub struct IndentStr<'a>(&'a str, ColoredString); pub struct IndentStr<'a>(&'a str, String);
impl Display for IndentStr<'_> { impl Display for IndentStr<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.0, self.1) write!(f, "{}{}", self.0, self.1)
@ -143,17 +246,11 @@ impl CheckErrorHRConfig {
["", "", "", ""], ["", "", "", ""],
], ],
]; ];
fn color(&self, s: &str) -> ColoredString { fn color(&self, s: &str) -> String {
match self.color_index % 8 { let mut t = String::new();
0 => s.bright_white(), self.theme
1 => s.bright_green(), .color(s, EColor::Indent(self.color_index), &mut t);
2 => s.bright_purple(), return t;
3 => s.bright_cyan(),
4 => s.bright_red(),
5 => s.bright_yellow(),
6 => s.bright_magenta(),
_ => s.bright_blue(),
}
} }
pub fn indent_start(&self, right: bool) -> IndentStr { pub fn indent_start(&self, right: bool) -> IndentStr {
IndentStr( IndentStr(
@ -202,6 +299,7 @@ impl CheckErrorHRConfig {
Self { Self {
color_index_ptr: self.color_index_ptr.clone(), color_index_ptr: self.color_index_ptr.clone(),
color_index, color_index,
theme: Rc::clone(&self.theme),
is_inner: true, is_inner: true,
style, style,
idt_start: if is_first { idt_start: if is_first {
@ -231,6 +329,7 @@ impl CheckErrorHRConfig {
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub struct CheckErrorDisplay<'a> { pub struct CheckErrorDisplay<'a> {
e: &'a CheckError, e: &'a CheckError,
theme: Rc<dyn Theme<String>>,
pub show_comments: bool, pub show_comments: bool,
} }
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
@ -247,7 +346,8 @@ impl Display for CheckErrorDisplay<'_> {
f, f,
&CheckErrorHRConfig { &CheckErrorHRConfig {
color_index: 0, 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, is_inner: false,
style: CheckErrorHRConfig::STYLE_DEFAULT, style: CheckErrorHRConfig::STYLE_DEFAULT,
idt_start: String::new(), idt_start: String::new(),
@ -272,10 +372,16 @@ impl CheckError {
self.0.push(v); self.0.push(v);
self 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<EColor>)>) -> Self {
self.add(CheckErrorComponent::Message(s)) 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<EColor>)>) -> &mut Self {
self.add_mut(CheckErrorComponent::Message(s)) self.add_mut(CheckErrorComponent::Message(s))
} }
pub(crate) fn err(self, e: Self) -> Self { 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 { pub(crate) fn err_with_diff_src_mut(&mut self, e: CheckError) -> &mut Self {
self.add_mut(CheckErrorComponent::ErrorWithDifferentSource(e)) self.add_mut(CheckErrorComponent::ErrorWithDifferentSource(e))
} }
pub(crate) fn src(self, s: Vec<(SourceRange, Option<colored::Color>)>) -> Self { pub(crate) fn src(self, s: Vec<(SourceRange, Option<EColor>)>) -> Self {
self.add(CheckErrorComponent::Source(s)) self.add(CheckErrorComponent::Source(s))
} }
pub(crate) fn src_mut(&mut self, s: Vec<(SourceRange, Option<colored::Color>)>) -> &mut Self { pub(crate) fn src_mut(&mut self, s: Vec<(SourceRange, Option<EColor>)>) -> &mut Self {
self.add_mut(CheckErrorComponent::Source(s)) self.add_mut(CheckErrorComponent::Source(s))
} }
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub fn display<'a>(&'a self) -> CheckErrorDisplay<'a> { pub fn display<'a>(&'a self, theme: impl Theme<String> + 'static) -> CheckErrorDisplay<'a> {
CheckErrorDisplay { CheckErrorDisplay {
e: self, e: self,
theme: Rc::new(theme),
show_comments: true, 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 /// will, unless empty, end in a newline
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
fn human_readable( fn human_readable(
@ -330,6 +448,7 @@ impl CheckError {
} }
match component { match component {
CheckErrorComponent::Message(msg) => { CheckErrorComponent::Message(msg) => {
let msg = colorize_str(msg, cfg.theme.as_ref());
let lines = msg.lines().collect::<Vec<_>>(); let lines = msg.lines().collect::<Vec<_>>();
let lc = lines.len(); let lc = lines.len();
for (i, line) in lines.into_iter().enumerate() { for (i, line) in lines.into_iter().enumerate() {
@ -403,26 +522,30 @@ impl CheckError {
f, f,
"{}{}", "{}{}",
indent!(true, false, ADD_RIGHT_BITS), indent!(true, false, ADD_RIGHT_BITS),
format!( cfg.theme.color_to(
&format!(
"Line {first_line_nr} ({}..{}){}", "Line {first_line_nr} ({}..{}){}",
start_with_comments + 1 - first_line_start, start_with_comments + 1 - first_line_start,
end_with_comments - last_line_start, end_with_comments - last_line_start,
src_from, src_from,
),
EColor::InCodePositionLine
) )
.bright_black()
)?; )?;
} else { } else {
writeln!( writeln!(
f, f,
"{}{}", "{}{}",
indent!(true, false, ADD_RIGHT_BITS), indent!(true, false, ADD_RIGHT_BITS),
format!( cfg.theme.color_to(
&format!(
"Lines {first_line_nr}-{last_line_nr} ({}..{}){}", "Lines {first_line_nr}-{last_line_nr} ({}..{}){}",
start_with_comments + 1 - first_line_start, start_with_comments + 1 - first_line_start,
end_with_comments - last_line_start, end_with_comments - last_line_start,
src_from, src_from,
),
EColor::InCodePositionLine
) )
.bright_black()
)?; )?;
} }
let lines = if cfg.show_comments { let lines = if cfg.show_comments {
@ -441,7 +564,7 @@ impl CheckError {
for (highlight_index, (highlight_pos, color)) in for (highlight_index, (highlight_pos, color)) in
highlights.iter().enumerate() highlights.iter().enumerate()
{ {
if let Some(color) = color { if let Some(color) = *color {
let (highlight_start, highlight_end) = if cfg.show_comments let (highlight_start, highlight_end) = if cfg.show_comments
{ {
( (
@ -515,7 +638,7 @@ impl CheckError {
f, f,
"{}{}", "{}{}",
" ".repeat(hl_space), " ".repeat(hl_space),
"~".repeat(hl_len).color(*color) &cfg.theme.color_to(&"~".repeat(hl_len), color)
)?; )?;
} }
} }
@ -538,21 +661,20 @@ impl CheckError {
} }
impl From<String> for CheckError { impl From<String> for CheckError {
fn from(value: String) -> Self { fn from(value: String) -> Self {
Self::new().msg(value) Self::new().msg_str(value)
} }
} }
impl From<&str> for CheckError { impl From<&str> for CheckError {
fn from(value: &str) -> Self { fn from(value: &str) -> Self {
Self::new().msg(value.to_owned()) value.to_owned().into()
} }
} }
impl Debug for CheckError { impl Debug for CheckError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}") #[cfg(feature = "ecolor-term")]
} let e = self.display_term();
} #[cfg(not(feature = "ecolor-term"))]
impl Display for CheckError { let e = self.display_notheme();
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{e}")
write!(f, "{}", self.display())
} }
} }

View File

@ -4,7 +4,7 @@ use line_span::{LineSpan, LineSpanExt};
use crate::{ use crate::{
data::Type, data::Type,
errors::{error_colors, CheckError, SourcePos}, errors::{CheckError, EColor, SourcePos},
program::{ program::{
self, self,
parsed::{block::Block, CompInfo}, 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() { for (try_stmt, used) in info.global.unused_try_statements.lock().unwrap().iter() {
if used.iter().any(|v| v.is_some()) { if used.iter().any(|v| v.is_some()) {
let err = err.get_or_insert_with(|| { let err = err.get_or_insert_with(|| {
CheckError::new().msg(format!( CheckError::from(format!(
"There are `.try` statements with unused functions!" "There are `.try` statements with unused functions!"
)) ))
}); });
@ -62,7 +62,7 @@ pub fn check_mut(
.enumerate() .enumerate()
.filter_map(|v| Some((v.0, v.1.clone()?))) .filter_map(|v| Some((v.0, v.1.clone()?)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
err.msg_mut(format!( err.msg_mut_str(format!(
"Here, {}function{} {} {} unused:", "Here, {}function{} {} {} unused:",
if unused.len() == 1 { "the " } else { "" }, if unused.len() == 1 { "the " } else { "" },
if unused.len() == 1 { "" } else { "s" }, if unused.len() == 1 { "" } else { "s" },
@ -87,9 +87,9 @@ pub fn check_mut(
src.push(( src.push((
src_range, src_range,
Some(if i % 2 == 0 { Some(if i % 2 == 0 {
error_colors::TryUnusedFunction1 EColor::TryUnusedFunction1
} else { } else {
error_colors::TryUnusedFunction2 EColor::TryUnusedFunction2
}), }),
)); ));
} }

View File

@ -3,7 +3,7 @@ use std::{path::PathBuf, sync::Arc};
use super::{Source, SourceFrom, SourcePos}; use super::{Source, SourceFrom, SourcePos};
use crate::{ use crate::{
data::Data, data::Data,
errors::{error_colors, CheckError}, errors::{CheckError, EColor},
program::{ program::{
self, self,
parsed::{as_type::AsType, MersStatement}, parsed::{as_type::AsType, MersStatement},
@ -30,9 +30,9 @@ pub fn parse(
let name = name.trim().to_owned(); let name = name.trim().to_owned();
src.skip_whitespace(); src.skip_whitespace();
if !matches!(src.next_char(), Some(']')) { if !matches!(src.next_char(), Some(']')) {
return Err( return Err(CheckError::from(format!(
CheckError::new().msg(format!("Expected ']' after type name in [[type_name]]")) "Expected ']' after type name in [[type_name]]"
); )));
} }
src.skip_whitespace(); src.skip_whitespace();
if src.peek_word_allow_colon() == ":=" { if src.peek_word_allow_colon() == ":=" {
@ -43,12 +43,12 @@ pub fn parse(
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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), Err(e) => return Err(e),
}; };
if !matches!(src.next_char(), Some(']')) { 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]" "Expected ']' after statement in [[type_name] := statement]"
))); )));
} }
@ -63,7 +63,7 @@ pub fn parse(
let as_type = super::types::parse_type(src, srca)?; let as_type = super::types::parse_type(src, srca)?;
src.skip_whitespace(); src.skip_whitespace();
if !matches!(src.next_char(), Some(']')) { 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]" "Expected ']' after type definition in [[type_name] type_definition]"
))); )));
} }
@ -84,16 +84,16 @@ pub fn parse(
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(pos_in_src, src.get_pos(), srca).into(), (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) { let statement = match parse(src, srca) {
Ok(Some(v)) => v, Ok(Some(v)) => v,
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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), Err(e) => return Err(e),
}; };
@ -120,7 +120,7 @@ pub fn parse(
let source = parse(src, srca)?.ok_or_else(|| { let source = parse(src, srca)?.ok_or_else(|| {
CheckError::new() CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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 { first = Box::new(program::parsed::init_to::InitTo {
pos_in_src: (first.source_range().start(), src.get_pos(), srca).into(), 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(), (first.source_range().start(), src.get_pos(), srca).into(),
None, None,
)]) )])
.msg(format!("EOF after `=`")) .msg_str(format!("EOF after `=`"))
})?; })?;
first = Box::new(program::parsed::assign_to::AssignTo { first = Box::new(program::parsed::assign_to::AssignTo {
pos_in_src: (pos_in_src, src.get_pos(), srca).into(), pos_in_src: (pos_in_src, src.get_pos(), srca).into(),
@ -153,7 +153,7 @@ pub fn parse(
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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), Err(e) => return Err(e),
}; };
@ -182,10 +182,10 @@ pub fn parse(
pos_after_first = src.get_pos(); pos_after_first = src.get_pos();
} else { } else {
return Err(CheckError::new() return Err(CheckError::new()
.msg(format!("Expected `(` after `.try`")) .msg_str(format!("Expected `(` after `.try`"))
.src(vec![( .src(vec![(
(dot_in_src, src.get_pos(), srca).into(), (dot_in_src, src.get_pos(), srca).into(),
Some(error_colors::TryBadSyntax), Some(EColor::TryBadSyntax),
)])); )]));
} }
} else { } else {
@ -194,7 +194,7 @@ pub fn parse(
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((dot_in_src, src.get_pos(), srca).into(), None)]) .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), Err(e) => return Err(e),
}; };
@ -265,16 +265,16 @@ pub fn parse_no_chain(
if src.peek_char().is_none() { if src.peek_char().is_none() {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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()) { if src.peek_char().is_some_and(|ch| ch.is_whitespace()) {
src.skip_whitespace(); src.skip_whitespace();
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(pos_in_src, src.get_pos(), srca).into(), (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() { match src.next_word() {
"include" => { "include" => {
@ -313,19 +313,19 @@ pub fn parse_no_chain(
((pos_in_src, end_in_src, srca).into(), None), ((pos_in_src, end_in_src, srca).into(), None),
( (
(string_in_src, src.get_pos(), srca).into(), (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 { } else {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
((pos_in_src, end_in_src, srca).into(), None), ((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)." "#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() return Err(CheckError::new()
.src(vec![( .src(vec![(
(pos_in_src, src.get_pos(), srca).into(), (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(), (pos_in_src, src.get_pos(), srca).into(),
None, None,
)]) )])
.msg(format!("EOF after `:` in object"))) .msg_str(format!("EOF after `:` in object")))
} }
Err(e) => { Err(e) => {
return Err(CheckError::new() return Err(CheckError::new()
@ -379,7 +379,9 @@ pub fn parse_no_chain(
(pos_in_src, src.get_pos(), srca).into(), (pos_in_src, src.get_pos(), srca).into(),
None, None,
)]) )])
.msg(format!("Error in statement after `:` in object")) .msg_str(format!(
"Error in statement after `:` in object"
))
.err(e)) .err(e))
} }
}, },
@ -430,7 +432,7 @@ pub fn parse_no_chain(
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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), Err(e) => return Err(e),
}; };
@ -439,7 +441,7 @@ pub fn parse_no_chain(
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
.msg(format!("EOF after `if <condition>`"))) .msg_str(format!("EOF after `if <condition>`")))
} }
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
@ -453,7 +455,7 @@ pub fn parse_no_chain(
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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), Err(e) => return Err(e),
}) })
@ -476,7 +478,7 @@ pub fn parse_no_chain(
Ok(None) => { Ok(None) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .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), Err(e) => return Err(e),
}; };
@ -578,17 +580,17 @@ pub fn parse_string_custom_end(
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(backslash_in_src, src.get_pos(), srca).into(), (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 => { None => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(backslash_in_src, src.get_pos(), srca).into(), (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 { } else if ch == closing_char {
@ -600,9 +602,9 @@ pub fn parse_string_custom_end(
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(opening, src.get_pos(), srca).into(), (opening, src.get_pos(), srca).into(),
Some(error_colors::StringEOF), Some(EColor::StringEOF),
)]) )])
.msg(format!( .msg_str(format!(
"EOF in string literal{}", "EOF in string literal{}",
if closing_char != '"' { if closing_char != '"' {
format!( format!(

View File

@ -2,7 +2,7 @@ use std::sync::Arc;
use crate::{ use crate::{
data::{self, Type}, data::{self, Type},
errors::{error_colors, CheckError}, errors::{CheckError, EColor},
}; };
use super::Source; use super::Source;
@ -35,7 +35,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
} else { } else {
format!("EOF") format!("EOF")
}; };
return Err(CheckError::new().msg(format!( return Err(CheckError::new().msg_str(format!(
"No closing ] in reference type with opening [! Found {nc} instead" "No closing ] in reference type with opening [! Found {nc} instead"
))); )));
} }
@ -75,10 +75,10 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
((pos_in_src, src.get_pos(), srca).into(), None), ((pos_in_src, src.get_pos(), srca).into(), None),
( (
(pos1, src.get_pos(), srca).into(), (pos1, src.get_pos(), srca).into(),
Some(error_colors::BadCharInFunctionType), Some(EColor::BadCharInFunctionType),
), ),
]).msg(format!("Unexpected character in function type, expected arrow `->` but found `,`.")) ]).msg_str(format!("Unexpected character in function type, expected arrow `->` 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!("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() == "->" => { Some('-') if src.peek_word() == "->" => {
@ -92,8 +92,8 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
Some(')') => break, Some(')') => break,
_ => return Err(CheckError::new().src(vec![ _ => return Err(CheckError::new().src(vec![
((pos_in_src, src.get_pos(), srca).into(), None), ((pos_in_src, src.get_pos(), srca).into(), None),
((pos2, src.get_pos(), srca).into(), Some(error_colors::BadCharInFunctionType)), ((pos2, src.get_pos(), srca).into(), Some(EColor::BadCharInFunctionType)),
]).msg(format!("Expected comma `,` after `In -> Out` part of function type"))) ]).msg_str(format!("Expected comma `,` after `In -> Out` part of function type")))
} }
} else { } else {
let pos1 = src.get_pos(); let pos1 = src.get_pos();
@ -102,10 +102,10 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
((pos_in_src, src.get_pos(), srca).into(), None), ((pos_in_src, src.get_pos(), srca).into(), None),
( (
(pos1, src.get_pos(), srca).into(), (pos1, src.get_pos(), srca).into(),
Some(error_colors::BadCharInTupleType), Some(EColor::BadCharInTupleType),
), ),
]).msg(format!("Unexpected character in tuple type, expected comma `,` but found arrow `->`.")) ]).msg_str(format!("Unexpected character in tuple type, expected comma `,` but found arrow `->`."))
.msg(format!("If you wanted to write a function type, use `(Input -> Output)` instead of `Input -> Output`."))); .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<Source>) -> Result<ParsedT
((pos_in_src, src.get_pos(), srca).into(), None), ((pos_in_src, src.get_pos(), srca).into(), None),
( (
(ppos, src.get_pos(), srca).into(), (ppos, src.get_pos(), srca).into(),
Some(error_colors::BadCharInTupleType), Some(EColor::BadCharInTupleType),
), ),
]) ])
.msg(format!( .msg_str(format!(
"Unexpected character in tuple type, expected comma ',' or ')'." "Unexpected character in tuple type, expected comma ',' or ')'."
))); )));
} }
@ -150,7 +150,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
if src.next_char() != Some(':') { if src.next_char() != Some(':') {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos(), srca).into(), None)]) .src(vec![((pos_in_src, src.get_pos(), srca).into(), None)])
.msg(format!("Expected colon ':' in object type"))); .msg_str(format!("Expected colon ':' in object type")));
} }
src.skip_whitespace(); src.skip_whitespace();
inner.push((field, parse_type(src, srca)?)); inner.push((field, parse_type(src, srca)?));
@ -236,11 +236,11 @@ pub fn type_from_parsed(
{ {
Some(Ok(t)) => as_type.add_all(&*t), Some(Ok(t)) => as_type.add_all(&*t),
Some(Err(_)) => { 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" "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 ParsedType::TypeWithInfo(name, additional_info) => match info
.scopes .scopes
@ -248,12 +248,12 @@ pub fn type_from_parsed(
.find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v)) .find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v))
{ {
Some(Ok(t)) => { 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" "Type: specified type with info, but type {t} doesn't need it"
))) )))
} }
Some(Err(f)) => as_type.add_all(&*f(&additional_info, info)?), 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}'"))),
}, },
} }
} }

View File

@ -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()); 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 { } else {
return Err(format!("Arguments must be (reference, function)").into()); return Err(format!("Arguments must be (reference, function)").into());

View File

@ -38,7 +38,7 @@ impl Config {
if let Some(f) = t.as_any().downcast_ref::<data::function::FunctionT>() { if let Some(f) = t.as_any().downcast_ref::<data::function::FunctionT>() {
match f.o(&Type::empty_tuple()) { match f.o(&Type::empty_tuple()) {
Ok(t) => out.add_all(&t), 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 { } else {
return Err(format!("thread: argument wasn't a function").into()); return Err(format!("thread: argument wasn't a function").into());
@ -69,7 +69,7 @@ impl Config {
out: Arc::new(|a, _i| { out: Arc::new(|a, _i| {
for t in a.types.iter() { for t in a.types.iter() {
if !t.as_any().is::<ThreadT>() { if !t.as_any().is::<ThreadT>() {
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)) Ok(Type::new(data::bool::BoolT))
@ -93,7 +93,7 @@ impl Config {
if let Some(t) = t.as_any().downcast_ref::<ThreadT>() { if let Some(t) = t.as_any().downcast_ref::<ThreadT>() {
out.add_all(&t.0); out.add_all(&t.0);
} else { } 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) Ok(out)

View File

@ -3,11 +3,9 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use colored::Colorize;
use crate::{ use crate::{
data::{self, Data, Type}, data::{self, Data, Type},
errors::error_colors, errors::{CheckError, EColor},
program::{self, run::CheckInfo}, program::{self, run::CheckInfo},
}; };
@ -33,11 +31,10 @@ impl Config {
Arc::new(data::tuple::TupleT(vec![])), Arc::new(data::tuple::TupleT(vec![])),
])) ]))
} else { } else {
Err(format!( Err(CheckError::new().msg(vec![
"expected (), got {}", ("expected (), got ".to_owned(), None),
a.to_string().color(error_colors::FunctionArgument) (a.to_string(), Some(EColor::FunctionArgument)),
) ]))
.into())
} }
}), }),
run: Arc::new(|_a, _i| { run: Arc::new(|_a, _i| {

View File

@ -1,10 +1,8 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use colored::Colorize;
use crate::{ use crate::{
data::{self, Data}, data::{self, Data},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
info::{self, Local}, info::{self, Local},
parsing::Source, parsing::Source,
program::{self}, program::{self},
@ -36,13 +34,13 @@ impl MersStatement for IncludeMers {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
self.pos_in_src.clone(), 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)" "Error in #include! (note: inner errors may refer to a different file)"
.color(error_colors::HashIncludeErrorInIncludedFile) .to_owned(),
.to_string(), Some(EColor::HashIncludeErrorInIncludedFile),
) )])
.err_with_diff_src(e)) .err_with_diff_src(e))
} }
}; };

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
info::Local, info::Local,
program::{self}, program::{self},
}; };
@ -50,9 +50,9 @@ impl MersStatement for Variable {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
self.pos_in_src.clone(), 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)));
}, },
})) }))
} }

View File

@ -1,8 +1,6 @@
use colored::Colorize;
use crate::{ use crate::{
data::{Data, Type}, data::{Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
parsing::types::ParsedType, parsing::types::ParsedType,
}; };
@ -33,7 +31,7 @@ impl MersStatement for AsType {
CheckError::new() CheckError::new()
.src(vec![( .src(vec![(
self.type_pos_in_src.clone(), self.type_pos_in_src.clone(),
Some(error_colors::BadTypeFromParsed), Some(EColor::BadTypeFromParsed),
)]) )])
.err(e) .err(e)
})?; })?;
@ -43,22 +41,23 @@ impl MersStatement for AsType {
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
( (
self.type_pos_in_src.clone(), self.type_pos_in_src.clone(),
Some(error_colors::AsTypeTypeAnnotation), Some(EColor::AsTypeTypeAnnotation),
), ),
( (
self.statement.source_range(), self.statement.source_range(),
Some(error_colors::AsTypeStatementWithTooBroadType), Some(EColor::AsTypeStatementWithTooBroadType),
), ),
]) ])
.msg(format!( .msg(vec![
"Type must be included in {}, but the actual type {} isn't.", ("Type must be included in ".to_owned(), None),
as_type (as_type.to_string(), Some(EColor::AsTypeTypeAnnotation)),
.to_string() (", but the actual type ".to_owned(), None),
.color(error_colors::AsTypeTypeAnnotation), (
return_type return_type.to_string(),
.to_string() Some(EColor::AsTypeStatementWithTooBroadType),
.color(error_colors::AsTypeStatementWithTooBroadType) ),
))); (" isn't.".to_owned(), None),
]));
} }
Ok(if self.expand_type { Ok(if self.expand_type {
as_type.clone() as_type.clone()

View File

@ -1,8 +1,6 @@
use colored::Colorize;
use crate::{ use crate::{
data::{self, Data, Type}, data::{self, Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
}; };
use super::{CheckInfo, MersStatement}; use super::{CheckInfo, MersStatement};
@ -37,10 +35,10 @@ impl MersStatement for AssignTo {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
(self.target.source_range(), Some(error_colors::InitTo)), (self.target.source_range(), Some(EColor::InitTo)),
(self.source.source_range(), Some(error_colors::InitFrom)), (self.source.source_range(), Some(EColor::InitFrom)),
]) ])
.msg(format!("Cannot initialize:")) .msg_str(format!("Cannot initialize:"))
.err(e)); .err(e));
} }
}; };
@ -50,15 +48,17 @@ impl MersStatement for AssignTo {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
(self.target.source_range(), Some(error_colors::AssignTo)), (self.target.source_range(), Some(EColor::AssignTo)),
(self.source.source_range(), Some(error_colors::AssignFrom)), (self.source.source_range(), Some(EColor::AssignFrom)),
]) ])
.msg(format!( .msg(vec![
"can't assign {} to {} because it isn't included in {}", ("can't assign ".to_owned(), None),
source.to_string().color(error_colors::AssignFrom), (source.to_string(), Some(EColor::AssignFrom)),
target.to_string().color(error_colors::AssignTo), (" to ".to_owned(), None),
t (target.to_string(), Some(EColor::AssignTo)),
))); (" because it isn't included in ".to_owned(), None),
(t.to_string(), None),
]));
} }
} else { } else {
return Err(CheckError::new() return Err(CheckError::new()
@ -66,13 +66,16 @@ impl MersStatement for AssignTo {
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
( (
self.target.source_range(), self.target.source_range(),
Some(error_colors::AssignTargetNonReference), Some(EColor::AssignTargetNonReference),
), ),
]) ])
.msg(format!( .msg(vec![
"can't assign to {}", ("can't assign to ".to_owned(), None),
"non-reference!".color(error_colors::AssignTargetNonReference) (
))); "non-reference!".to_owned(),
Some(EColor::AssignTargetNonReference),
),
]));
} }
} }
Ok(Type::empty_tuple()) Ok(Type::empty_tuple())

View File

@ -1,8 +1,6 @@
use colored::Colorize;
use crate::{ use crate::{
data::{Data, Type}, data::{Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
parsing::Source, parsing::Source,
}; };
@ -44,29 +42,27 @@ impl MersStatement for Chain {
CheckError::new() CheckError::new()
.src(vec![( .src(vec![(
self.pos_in_src.clone(), 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) .err_with_diff_src(e)
} else { } else {
CheckError::new() CheckError::new()
.src(vec![ .src(vec![
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
( (self.first.source_range(), Some(EColor::FunctionArgument)),
self.first.source_range(), (self.chained.source_range(), Some(EColor::Function)),
Some(error_colors::FunctionArgument), ])
), .msg(vec![
(self.chained.source_range(), Some(error_colors::Function)), ("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) .err(e)
}) })
} }
@ -77,13 +73,14 @@ impl MersStatement for Chain {
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
( (
self.chained.source_range(), self.chained.source_range(),
Some(error_colors::ChainWithNonFunction), Some(EColor::ChainWithNonFunction),
), ),
]) ])
.msg(format!( .msg(vec![
"cannot chain with a non-function ({})", ("cannot chain with a non-function (".to_owned(), None),
func.to_string().color(error_colors::ChainWithNonFunction) (func.to_string(), Some(EColor::ChainWithNonFunction)),
))); (")".to_owned(), None),
]));
} }
} }
Ok(o) Ok(o)
@ -98,12 +95,12 @@ impl MersStatement for Chain {
Err(e) => Err(if let Some(_) = &self.as_part_of_include { Err(e) => Err(if let Some(_) = &self.as_part_of_include {
CheckError::new().err_with_diff_src(e).src(vec![( CheckError::new().err_with_diff_src(e).src(vec![(
self.pos_in_src.clone(), self.pos_in_src.clone(),
Some(error_colors::StacktraceDescendHashInclude), Some(EColor::StacktraceDescendHashInclude),
)]) )])
} else { } else {
CheckError::new().err(e).src(vec![ CheckError::new().err(e).src(vec![
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
(self.source_range(), Some(error_colors::StacktraceDescend)), (self.source_range(), Some(EColor::StacktraceDescend)),
]) ])
}), }),
} }

View File

@ -1,10 +1,8 @@
use std::{fmt::Debug, sync::Arc}; use std::{fmt::Debug, sync::Arc};
use colored::Colorize;
use crate::{ use crate::{
data::{Data, Type}, data::{Data, Type},
errors::{CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
}; };
use super::{CheckInfo, Info, MersStatement}; use super::{CheckInfo, Info, MersStatement};
@ -45,12 +43,17 @@ impl MersStatement for CustomType {
} else { } else {
if let Err(e) = t { if let Err(e) = t {
return Err(CheckError::new() return Err(CheckError::new()
.msg(format!( .msg(vec![
" {} {} {} (`[[_] := ...]` indicates that `...` must be type-correct)", (
"<<".bright_red(), " << Custom type-test failed! >>".to_owned(),
"Custom type-test failed!".bright_red(), Some(EColor::CustomTypeTestFailed),
">>".bright_red(), ),
)) (
" (`[[_] := ...]` indicates that `...` must be type-correct)"
.to_owned(),
None,
),
])
.err(e)); .err(e));
} }
} }

View File

@ -1,10 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use colored::Colorize;
use crate::{ use crate::{
data::{self, tuple::TupleT, Data, Type}, data::{self, tuple::TupleT, Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
}; };
use super::MersStatement; use super::MersStatement;
@ -33,16 +31,21 @@ impl MersStatement for If {
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
( (
self.condition.source_range(), self.condition.source_range(),
Some(error_colors::IfConditionNotBool), Some(EColor::IfConditionNotBool),
), ),
]) ])
.msg(format!( .msg(vec![
"The {} in an if-statement must return bool, not {}", ("The ".to_owned(), None),
"condition".color(error_colors::IfConditionNotBool), ("condition".to_owned(), Some(EColor::IfConditionNotBool)),
cond_return_type (
.to_string() " in an if-statement must return bool, not ".to_owned(),
.color(error_colors::IfConditionNotBool), None,
))); ),
(
cond_return_type.to_string(),
Some(EColor::IfConditionNotBool),
),
]));
} }
let mut t = self.on_true.check(info, None)?; let mut t = self.on_true.check(info, None)?;
if let Some(f) = &self.on_false { if let Some(f) = &self.on_false {

View File

@ -7,7 +7,7 @@ use std::{
use crate::{ use crate::{
data::{self, Data, Type}, data::{self, Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
info, info,
}; };
@ -98,10 +98,10 @@ pub trait MersStatement: Debug + Send + Sync {
if let Some(cutoff) = info.global.limit_runtime { if let Some(cutoff) = info.global.limit_runtime {
if Instant::now() >= cutoff { if Instant::now() >= cutoff {
return Err(CheckError::new() return Err(CheckError::new()
.msg("maximum runtime exceeded".to_owned()) .msg_str("maximum runtime exceeded".to_owned())
.src(vec![( .src(vec![(
self.source_range(), self.source_range(),
Some(error_colors::MaximumRuntimeExceeded), Some(EColor::MaximumRuntimeExceeded),
)])); )]));
} }
} }

View File

@ -1,10 +1,8 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use colored::Colorize;
use crate::{ use crate::{
data::{self, object::ObjectT, Data, Type}, data::{self, object::ObjectT, Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
}; };
use super::MersStatement; 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() for (i, ((sn, _), (tn, t))) in self.elems.iter().zip(t.0.iter()).enumerate()
{ {
if sn != tn { if sn != tn {
return Err(format!("can't init an {} with type {}{} - field mismatch: {sn} != {tn}", "object".color(error_colors::InitTo), return Err(CheckError::new().msg(vec![
t.to_string().color(error_colors::InitFrom), ("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 { if print_is_part_of {
format!( (", which is part of ".to_owned(), None)
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
} else { } else {
format!("") (String::new(), None)
} },
).into()); 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); acc[i].add_all(&t);
} }
} else { } else {
return Err(format!( return Err(CheckError::new().msg(vec![
"can't init an {} with type {}{} - source has {}", ("can't init an ".to_owned(), None),
"object".color(error_colors::InitTo), ("object".to_owned(), Some(EColor::InitTo)),
t.to_string().color(error_colors::InitFrom), (" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
if print_is_part_of { if print_is_part_of {
format!( (", which is part of ".to_owned(), None)
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
} else { } 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()) format!("less fields ({}, not {})", t.0.len(), self.elems.len())
} else { } else {
format!( format!(
@ -74,25 +85,30 @@ impl MersStatement for Object {
.collect::<String>(), .collect::<String>(),
data::object::ObjectT(t.0.iter().take(self.elems.len()).cloned().collect()) data::object::ObjectT(t.0.iter().take(self.elems.len()).cloned().collect())
) )
} }, None)
) ]));
.into());
} }
} else { } else {
return Err(format!( return Err(CheckError::new().msg(vec![
"can't init an {} with type {}{} - only objects can be assigned to objects", ("can't init an ".to_owned(), None),
"object".color(error_colors::InitTo), ("object".to_owned(), Some(EColor::InitTo)),
t.to_string().color(error_colors::InitFrom), (" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
if print_is_part_of { if print_is_part_of {
format!( (", which is part of ".to_owned(), None)
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
} else { } else {
format!("") (format!(""), None)
} },
) if print_is_part_of {
.into()); (init_to.to_string(), Some(EColor::InitFrom))
} else {
(format!(""), None)
},
(
" - only objects can be assigned to objects".to_owned(),
None,
),
]));
} }
} }
Some(acc) Some(acc)

View File

@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex};
use crate::{ use crate::{
data::{self, Data, Type}, data::{self, Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
}; };
use super::{Info, MersStatement}; use super::{Info, MersStatement};
@ -62,16 +62,13 @@ impl MersStatement for Try {
} }
} else { } else {
return Err(CheckError::new() return Err(CheckError::new()
.msg(format!( .msg_str(format!(
"try: #{} is not a function, type is {ft} within {func}.", "try: #{} is not a function, type is {ft} within {func}.",
i + 1 i + 1
)) ))
.src(vec![ .src(vec![
(self.source_range(), None), (self.source_range(), None),
( (self.funcs[i].source_range(), Some(EColor::TryNotAFunction)),
self.funcs[i].source_range(),
Some(error_colors::TryNotAFunction),
),
])); ]));
} }
} }
@ -88,15 +85,17 @@ impl MersStatement for Try {
} }
if !found { if !found {
let mut err = CheckError::new() let mut err = CheckError::new()
.msg(format!( .msg_str(format!(
"try: no function found for argument of type {arg}." "try: no function found for argument of type {arg}."
)) ))
.src(vec![( .src(vec![(
self.pos_in_src.clone(), self.pos_in_src.clone(),
Some(error_colors::TryNoFunctionFound), Some(EColor::TryNoFunctionFound),
)]); )]);
for (i, e) in errs.into_iter().enumerate() { 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); return Err(err);
} }

View File

@ -1,10 +1,8 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use colored::Colorize;
use crate::{ use crate::{
data::{self, tuple::TupleT, Data, Type}, data::{self, tuple::TupleT, Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{CheckError, EColor, SourceRange},
}; };
use super::MersStatement; use super::MersStatement;
@ -32,33 +30,54 @@ impl MersStatement for Tuple {
vec[i].add_all(&e); vec[i].add_all(&e);
} }
} else { } else {
return Err( return Err(CheckError::new().msg(vec![
format!("can't init a {} with type {}{} - only tuples with the same length ({}) can be assigned.", ("can't init a ".to_owned(), None),
"tuple".color(error_colors::InitTo), ("tuple".to_owned(), Some(EColor::InitTo)),
t.to_string().color(error_colors::InitFrom), (" with type ".to_owned(), None),
(t.to_string(), Some(EColor::InitFrom)),
(
if print_is_part_of { 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 { } else {
format!("") String::new()
}, },
self.elems.len() None,
).into()); ),
}
} 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),
if print_is_part_of { if print_is_part_of {
format!( (init_to.to_string(), Some(EColor::InitFrom))
", which is part of {}",
init_to.to_string().color(error_colors::InitFrom)
)
} else { } else {
format!("") (String::new(), None)
},
(
format!(
" - only tuples with the same length ({}) can be assigned",
self.elems.len()
),
None,
),
]));
} }
) } else {
.into()); 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 {
(init_to.to_string(), Some(EColor::InitFrom))
} else {
(String::new(), None)
},
(" - only tuples can be assigned to tuples".to_owned(), None),
]));
} }
} }
Some(vec) Some(vec)