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]
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 }

View File

@ -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);
}
}
}

View File

@ -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) => {

View File

@ -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"

View File

@ -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()),
}
}

View File

@ -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);

View File

@ -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<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;
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<T> {
fn color(&self, text: &str, color: EColor, t: &mut T);
}
pub trait ThemeTo<T>: Theme<T> {
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 const AsTypeTypeAnnotation: Color = InitTo;
pub struct NoTheme;
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;
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<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;
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<EColor>)>),
Error(CheckError),
ErrorWithDifferentSource(CheckError),
Source(Vec<(SourceRange, Option<colored::Color>)>),
Source(Vec<(SourceRange, Option<EColor>)>),
}
pub struct CheckErrorHRConfig {
color_index_ptr: Rc<AtomicUsize>,
color_index: usize,
color_index_ptr: Rc<AtomicU32>,
color_index: u32,
theme: Rc<dyn Theme<String>>,
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<dyn Theme<String>>,
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<EColor>)>) -> 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<EColor>)>) -> &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<colored::Color>)>) -> Self {
pub(crate) fn src(self, s: Vec<(SourceRange, Option<EColor>)>) -> Self {
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))
}
#[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 {
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::<Vec<_>>();
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<String> 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}")
}
}

View File

@ -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::<Vec<_>>();
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
}),
));
}

View File

@ -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 <condition>`")))
.msg_str(format!("EOF after `if <condition>`")))
}
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!(

View File

@ -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<Source>) -> Result<ParsedT
} else {
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"
)));
}
@ -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),
(
(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(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<Source>) -> Result<ParsedT
Some(')') => 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<Source>) -> Result<ParsedT
((pos_in_src, src.get_pos(), srca).into(), None),
(
(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(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<Source>) -> Result<ParsedT
((pos_in_src, src.get_pos(), srca).into(), None),
(
(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 ')'."
)));
}
@ -150,7 +150,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc<Source>) -> Result<ParsedT
if src.next_char() != Some(':') {
return Err(CheckError::new()
.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();
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(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}'"))),
},
}
}

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());
}
},
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());

View File

@ -38,7 +38,7 @@ impl Config {
if let Some(f) = t.as_any().downcast_ref::<data::function::FunctionT>() {
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::<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))
@ -93,7 +93,7 @@ impl Config {
if let Some(t) = t.as_any().downcast_ref::<ThreadT>() {
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)

View File

@ -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| {

View File

@ -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))
}
};

View File

@ -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)));
},
}))
}

View File

@ -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()

View File

@ -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())

View File

@ -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)),
])
}),
}

View File

@ -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));
}
}

View File

@ -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 {

View File

@ -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),
)]));
}
}

View File

@ -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::<String>(),
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)

View File

@ -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);
}

View File

@ -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)