From 12925fed67a1a564ba668db69a53c266b2e9517d Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 16 Nov 2023 14:50:09 +0100 Subject: [PATCH] improved error messages - some small bugs are now fixed - include comments in error messages (if this causes issues, use --hide-comments) - colors should make more sense now - error-related things moved to mers_lib/src/errors/ --- mers/src/main.rs | 9 +- mers_lib/src/data/function.rs | 5 +- mers_lib/src/errors/mod.rs | 308 ++++++++++++++++++ mers_lib/src/lib.rs | 2 + mers_lib/src/parsing/errors.rs | 1 - mers_lib/src/parsing/mod.rs | 37 ++- mers_lib/src/parsing/statements.rs | 16 +- mers_lib/src/program/configs/with_base.rs | 3 +- mers_lib/src/program/configs/with_iters.rs | 13 +- mers_lib/src/program/configs/with_list.rs | 2 +- mers_lib/src/program/configs/with_math.rs | 6 +- .../program/configs/with_multithreading.rs | 6 +- mers_lib/src/program/configs/with_stdio.rs | 29 +- mers_lib/src/program/parsed/assign_to.rs | 6 +- mers_lib/src/program/parsed/block.rs | 6 +- mers_lib/src/program/parsed/chain.rs | 6 +- mers_lib/src/program/parsed/function.rs | 6 +- mers_lib/src/program/parsed/if.rs | 6 +- mers_lib/src/program/parsed/include_mers.rs | 8 +- mers_lib/src/program/parsed/init_to.rs | 6 +- mers_lib/src/program/parsed/mod.rs | 7 +- mers_lib/src/program/parsed/tuple.rs | 6 +- mers_lib/src/program/parsed/value.rs | 7 +- mers_lib/src/program/parsed/variable.rs | 6 +- mers_lib/src/program/run/assign_to.rs | 7 +- mers_lib/src/program/run/block.rs | 11 +- mers_lib/src/program/run/chain.rs | 7 +- mers_lib/src/program/run/function.rs | 9 +- mers_lib/src/program/run/if.rs | 9 +- mers_lib/src/program/run/mod.rs | 209 +----------- mers_lib/src/program/run/tuple.rs | 7 +- mers_lib/src/program/run/value.rs | 9 +- mers_lib/src/program/run/variable.rs | 7 +- 33 files changed, 462 insertions(+), 320 deletions(-) create mode 100644 mers_lib/src/errors/mod.rs delete mode 100755 mers_lib/src/parsing/errors.rs diff --git a/mers/src/main.rs b/mers/src/main.rs index 2c9e39f..f22e4c0 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -14,6 +14,9 @@ struct Args { /// perform checks to avoid runtime crashes #[arg(long, default_value_t = Check::Yes)] check: Check, + /// in error messages, hide comments and only show actual code + #[arg(long)] + hide_comments: bool, } #[derive(Subcommand)] enum Command { @@ -81,7 +84,7 @@ fn main() { let parsed = match parse(&mut source) { Ok(v) => v, Err(e) => { - eprintln!("{}", e.display(&source)); + eprintln!("{}", e.display(&source).show_comments(!args.hide_comments)); exit(20); } }; @@ -90,7 +93,7 @@ fn main() { let run = match parsed.compile(&mut info_parsed, Default::default()) { Ok(v) => v, Err(e) => { - eprintln!("{}", e.display(&source)); + eprintln!("{}", e.display(&source).show_comments(!args.hide_comments)); exit(24); } }; @@ -104,7 +107,7 @@ fn main() { let return_type = match run.check(&mut info_check, None) { Ok(v) => v, Err(e) => { - eprint!("{}", e.display(&source)); + eprint!("{}", e.display(&source).show_comments(!args.hide_comments)); exit(28); } }; diff --git a/mers_lib/src/data/function.rs b/mers_lib/src/data/function.rs index bd270dc..c793c2e 100755 --- a/mers_lib/src/data/function.rs +++ b/mers_lib/src/data/function.rs @@ -4,7 +4,10 @@ use std::{ sync::{Arc, Mutex}, }; -use crate::program::run::{CheckError, CheckInfo, Info}; +use crate::{ + errors::CheckError, + program::run::{CheckInfo, Info}, +}; use super::{Data, MersData, MersType, Type}; diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs new file mode 100644 index 0000000..85f8e33 --- /dev/null +++ b/mers_lib/src/errors/mod.rs @@ -0,0 +1,308 @@ +use std::fmt::Display; + +use colored::Colorize; +use line_span::LineSpans; + +#[cfg(feature = "parse")] +use crate::parsing::Source; + +#[derive(Clone, Copy, Debug)] +pub struct SourcePos(pub(crate) usize); +impl SourcePos { + pub fn pos(&self) -> usize { + self.0 + } +} + +#[derive(Clone, Copy, Debug)] +pub struct SourceRange { + start: SourcePos, + end: SourcePos, +} +impl From<(SourcePos, SourcePos)> for SourceRange { + fn from(value: (SourcePos, SourcePos)) -> Self { + SourceRange { + start: value.0, + end: value.1, + } + } +} +impl SourceRange { + pub fn start(&self) -> SourcePos { + self.start + } + pub fn end(&self) -> SourcePos { + self.end + } +} +#[derive(Clone, Debug)] +pub struct CheckError(Vec); +#[allow(non_upper_case_globals)] +pub mod error_colors { + use colored::Color; + + pub const UnknownVariable: Color = Color::Red; + + 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; + + pub const BackslashEscapeUnknown: Color = Color::Red; + pub const BackslashEscapeEOF: Color = Color::Red; + pub const StringEOF: Color = Color::Red; + + pub const IfConditionNotBool: Color = Color::Red; + pub const ChainWithNonFunction: Color = Color::Yellow; + + pub const Function: Color = Color::BrightMagenta; + pub const FunctionArgument: Color = Color::BrightBlue; + + 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; +} +#[derive(Clone, Debug)] +enum CheckErrorComponent { + Message(String), + Error(CheckError), + Source(Vec<(SourceRange, Option)>), +} +#[derive(Clone)] +pub struct CheckErrorHRConfig { + indent_start: String, + indent_default: String, + indent_end: String, + /// if true, shows "original" source code, if false, shows source with comments removed (this is what the parser uses internally) + show_comments: bool, +} +#[cfg(feature = "parse")] +pub struct CheckErrorDisplay<'a> { + e: &'a CheckError, + src: Option<&'a Source>, + pub show_comments: bool, +} +#[cfg(feature = "parse")] +impl<'a> CheckErrorDisplay<'a> { + pub fn show_comments(mut self, show_comments: bool) -> Self { + self.show_comments = show_comments; + self + } +} +#[cfg(feature = "parse")] +impl Display for CheckErrorDisplay<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.e.human_readable( + f, + self.src, + &CheckErrorHRConfig { + indent_start: String::new(), + indent_default: String::new(), + indent_end: String::new(), + show_comments: self.show_comments, + }, + ) + } +} +impl CheckError { + pub fn new() -> Self { + CheckError(vec![]) + } + fn add(mut self, v: CheckErrorComponent) -> Self { + self.0.push(v); + self + } + pub(crate) fn msg(self, s: String) -> Self { + self.add(CheckErrorComponent::Message(s)) + } + pub(crate) fn err(self, e: Self) -> Self { + self.add(CheckErrorComponent::Error(e)) + } + pub(crate) fn src(self, s: Vec<(SourceRange, Option)>) -> Self { + self.add(CheckErrorComponent::Source(s)) + } + #[cfg(feature = "parse")] + pub fn display<'a>(&'a self, src: &'a Source) -> CheckErrorDisplay<'a> { + CheckErrorDisplay { + e: self, + src: Some(src), + show_comments: true, + } + } + #[cfg(feature = "parse")] + pub fn display_no_src<'a>(&'a self) -> CheckErrorDisplay<'a> { + CheckErrorDisplay { + e: self, + src: None, + show_comments: true, + } + } + /// will, unless empty, end in a newline + #[cfg(feature = "parse")] + fn human_readable( + &self, + f: &mut std::fmt::Formatter<'_>, + src: Option<&Source>, + cfg: &CheckErrorHRConfig, + ) -> std::fmt::Result { + let len = self.0.len(); + for (i, component) in self.0.iter().enumerate() { + macro_rules! indent { + () => { + if i + 1 == len { + &cfg.indent_end + } else if i == 0 { + &cfg.indent_start + } else { + &cfg.indent_default + } + }; + } + match component { + CheckErrorComponent::Message(msg) => writeln!(f, "{}{msg}", indent!())?, + CheckErrorComponent::Error(err) => { + let mut cfg = cfg.clone(); + cfg.indent_start.push_str("│"); + cfg.indent_default.push_str("│"); + cfg.indent_end.push_str("└"); + err.human_readable(f, src, &cfg)?; + } + CheckErrorComponent::Source(highlights) => { + if let Some(src) = src { + let start = highlights.iter().map(|v| v.0.start.pos()).min(); + let end = highlights.iter().map(|v| v.0.end.pos()).max(); + if let (Some(start_in_line), Some(end_in_line)) = (start, end) { + let start = src.get_line_start(start_in_line); + let end = src.get_line_end(end_in_line); + let (start_with_comments, end_with_comments) = ( + src.pos_in_og(start_in_line, true), + src.pos_in_og(end_in_line, false), + ); + let (mut start, mut end) = if cfg.show_comments { + (src.pos_in_og(start, true), src.pos_in_og(end, false)) + } else { + (start, end) + }; + let mut first_line_start = 0; + let first_line_nr = src + .src_og() + .line_spans() + .take_while(|l| { + if l.start() <= start_with_comments { + first_line_start = l.start(); + true + } else { + false + } + }) + .count(); + if cfg.show_comments && first_line_start < start { + start = first_line_start; + } + let mut last_line_start = 0; + let last_line_nr = src + .src_og() + .line_spans() + .take_while(|l| { + if l.start() <= end_with_comments { + last_line_start = l.start(); + if cfg.show_comments && l.end() > end { + end = l.end(); + } + true + } else { + false + } + }) + .count(); + if first_line_nr == last_line_nr { + writeln!( + f, + "{}Line {first_line_nr} ({}..{})", + indent!(), + start_with_comments + 1 - first_line_start, + end_with_comments - last_line_start, + )?; + } else { + writeln!( + f, + "{}Lines {first_line_nr}-{last_line_nr} ({}..{})", + indent!(), + start_with_comments + 1 - first_line_start, + end_with_comments - last_line_start, + )?; + } + let lines = if cfg.show_comments { + src.src_og()[start..end].line_spans().collect::>() + } else { + src.src()[start..end].line_spans().collect::>() + }; + for line in lines { + let line_start = line.start(); + let line_end = line.end(); + let line = line.as_str(); + writeln!(f, "{} {line}", indent!())?; + let mut right = 0; + for (pos, color) in highlights { + if let Some(color) = color { + let (highlight_start, highlight_end) = if cfg.show_comments + { + ( + src.pos_in_og(pos.start.pos(), true), + src.pos_in_og(pos.end.pos(), false), + ) + } else { + (pos.start.pos(), pos.end.pos()) + }; + let highlight_start = highlight_start - start; + let highlight_end = highlight_end - start; + if highlight_start < line_end && highlight_end > line_start + { + // where the highlight starts in this line + let hl_start = + highlight_start.saturating_sub(line_start); + // highlight would be further left than cursor, so we need a new line + if hl_start < right { + right = 0; + writeln!(f)?; + } + // length of the highlight + let hl_len = highlight_end + .saturating_sub(line_start) + .saturating_sub(hl_start); + let hl_space = hl_start - right; + let print_indent = right == 0; + let hl_len = hl_len.min(line.len() - right); + right += hl_space + hl_len; + if print_indent && right != 0 { + write!(f, "{} ", indent!())?; + } + write!( + f, + "{}{}", + " ".repeat(hl_space), + "~".repeat(hl_len).color(*color) + )?; + } + } + } + if right != 0 { + writeln!(f)?; + } + } + } + } + } + } + } + Ok(()) + } +} +impl From for CheckError { + fn from(value: String) -> Self { + Self::new().msg(value) + } +} diff --git a/mers_lib/src/lib.rs b/mers_lib/src/lib.rs index 66eb48f..6c98ef4 100755 --- a/mers_lib/src/lib.rs +++ b/mers_lib/src/lib.rs @@ -1,5 +1,7 @@ /// data and types in mers pub mod data; +/// struct to represent errors the user may face +pub mod errors; /// shared code handling scopes to guarantee that compiler and runtime scopes match pub mod info; /// parser implementation. diff --git a/mers_lib/src/parsing/errors.rs b/mers_lib/src/parsing/errors.rs deleted file mode 100755 index 8b13789..0000000 --- a/mers_lib/src/parsing/errors.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index be92b1d..ce05167 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -1,8 +1,10 @@ use std::sync::Arc; -use crate::program::{self, parsed::block::Block, run::CheckError}; +use crate::{ + errors::{CheckError, SourcePos}, + program::{self, parsed::block::Block}, +}; -pub mod errors; pub mod statements; pub mod types; @@ -20,7 +22,7 @@ pub struct Source { src_raw_len: usize, src_og: String, src: String, - /// (start, content) of each comment, including start/end (//, \n, /* and */) + /// (start, content) of each comment, including start/end (//, /* and */), but NOT newline after // comments: Vec<(usize, String)>, i: usize, sections: Vec, @@ -36,13 +38,15 @@ impl Source { if let Some((i, ch)) = chars.next() { match in_comment { Some(false) => { - comment.1.push(ch); if ch == '\n' { + src.push('\n'); in_comment = None; comments.push(( comment.0, std::mem::replace(&mut comment.1, String::new()), )); + } else { + comment.1.push(ch); } } Some(true) => { @@ -223,6 +227,20 @@ impl Source { } o } + + pub fn pos_in_og(&self, mut pos: usize, inclusive: bool) -> usize { + for (start, comment) in &self.comments { + if *start < pos || (inclusive && *start == pos) { + pos += comment.len(); + } else { + break; + } + } + pos + } + pub fn src_og(&self) -> &String { + &self.src_og + } } impl Drop for Source { @@ -261,14 +279,3 @@ impl SectionMarker { } } } - -#[derive(Clone, Copy, Debug)] -pub struct SourcePos(usize); -impl SourcePos { - pub fn pos(&self) -> usize { - self.0 - } - fn diff(&self, rhs: &Self) -> usize { - rhs.0 - self.0 - } -} diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 1cb6072..df5bb43 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -3,11 +3,8 @@ use std::fs; use super::{Source, SourcePos}; use crate::{ data::Data, - program::{ - self, - parsed::MersStatement, - run::{error_colors, CheckError}, - }, + errors::{error_colors, CheckError}, + program::{self, parsed::MersStatement}, }; pub fn parse( @@ -19,6 +16,7 @@ pub fn parse( } else { return Ok(None); }; + let mut pos_after_first = src.get_pos(); src.skip_whitespace(); match src.peek_word() { ":=" => { @@ -68,15 +66,15 @@ pub fn parse( }); } _ => loop { - let pos_in_src = src.get_pos(); src.skip_whitespace(); + let dot_in_src = src.get_pos(); if let Some('.') = src.peek_char() { src.next_char(); let chained = match parse_no_chain(src) { Ok(Some(v)) => v, Ok(None) => { return Err(CheckError::new() - .src(vec![((pos_in_src, src.get_pos()).into(), None)]) + .src(vec![((dot_in_src, src.get_pos()).into(), None)]) .msg(format!("EOF after `.`"))) } Err(e) => return Err(e), @@ -91,11 +89,13 @@ pub fn parse( }); } first = Box::new(program::parsed::chain::Chain { - pos_in_src: (pos_in_src, src.get_pos()).into(), + pos_in_src: (first.source_range().start(), src.get_pos()).into(), first, chained, }); + pos_after_first = src.get_pos(); } else { + src.set_pos(pos_after_first); break; } }, diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index c8345ec..2553b47 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -5,7 +5,8 @@ use std::{ use crate::{ data::{self, Data, MersType, Type}, - program::run::{CheckError, CheckInfo, Info}, + errors::CheckError, + program::run::{CheckInfo, Info}, }; use super::Config; diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index 2db4aca..521adeb 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -9,10 +9,8 @@ use crate::{ function::{Function, FunctionT}, Data, MersData, MersType, Type, }, - program::{ - self, - run::{CheckError, CheckInfo}, - }, + errors::CheckError, + program::{self, run::CheckInfo}, }; use super::Config; @@ -140,13 +138,6 @@ impl Config { } } -fn iter_out( - a: &Type, - name: &str, - func: impl Fn(&FunctionT) -> ItersT + Sync + Send, -) -> Result { - iter_out_arg(a, name, func) -} fn iter_out_arg( a: &Type, name: &str, diff --git a/mers_lib/src/program/configs/with_list.rs b/mers_lib/src/program/configs/with_list.rs index 0e75ed6..017725e 100755 --- a/mers_lib/src/program/configs/with_list.rs +++ b/mers_lib/src/program/configs/with_list.rs @@ -135,7 +135,7 @@ impl Config { Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, i| { + out: Arc::new(|a, _i| { if let Some(v) = a.iterable() { Ok(Type::new(ListT(v))) } else { diff --git a/mers_lib/src/program/configs/with_math.rs b/mers_lib/src/program/configs/with_math.rs index 22d1f15..e017b5c 100755 --- a/mers_lib/src/program/configs/with_math.rs +++ b/mers_lib/src/program/configs/with_math.rs @@ -2,10 +2,8 @@ use std::sync::{Arc, Mutex}; use crate::{ data::{self, Data, MersType, Type}, - program::{ - self, - run::{CheckError, CheckInfo}, - }, + errors::CheckError, + program::{self, run::CheckInfo}, }; use super::Config; diff --git a/mers_lib/src/program/configs/with_multithreading.rs b/mers_lib/src/program/configs/with_multithreading.rs index 84f43ce..f0b4711 100755 --- a/mers_lib/src/program/configs/with_multithreading.rs +++ b/mers_lib/src/program/configs/with_multithreading.rs @@ -6,10 +6,8 @@ use std::{ use crate::{ data::{self, Data, MersData, MersType, Type}, - program::{ - self, - run::{CheckError, CheckInfo}, - }, + errors::CheckError, + program::{self, run::CheckInfo}, }; use super::Config; diff --git a/mers_lib/src/program/configs/with_stdio.rs b/mers_lib/src/program/configs/with_stdio.rs index a612e2f..4580c1c 100755 --- a/mers_lib/src/program/configs/with_stdio.rs +++ b/mers_lib/src/program/configs/with_stdio.rs @@ -3,8 +3,11 @@ use std::{ sync::{Arc, Mutex}, }; +use colored::Colorize; + use crate::{ data::{self, Data, Type}, + errors::error_colors, program::{self, run::CheckInfo}, }; @@ -23,7 +26,17 @@ impl Config { Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, i| Ok(Type::new(data::string::StringT))), + out: Arc::new(|a, _i| { + if a.is_zero_tuple() { + Ok(Type::new(data::string::StringT)) + } else { + Err(format!( + "expected (), got {}", + a.to_string().color(error_colors::FunctionArgument) + ) + .into()) + } + }), run: Arc::new(|_a, _i| { let mut line = String::new(); _ = std::io::stdin().read_line(&mut line); @@ -36,7 +49,7 @@ impl Config { Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, i| Ok(Type::empty_tuple())), + out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), run: Arc::new(|a, _i| { eprintln!("{:#?}", a.get()); Data::empty_tuple() @@ -48,10 +61,10 @@ impl Config { Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, i| Ok(Type::empty_tuple())), + out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), run: Arc::new(|a, _i| { eprint!("{}", a.get()); - std::io::stderr().lock().flush(); + _ = std::io::stderr().lock().flush(); Data::empty_tuple() }), }), @@ -61,7 +74,7 @@ impl Config { Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, i| Ok(Type::empty_tuple())), + out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), run: Arc::new(|a, _i| { eprintln!("{}", a.get()); Data::empty_tuple() @@ -73,10 +86,10 @@ impl Config { Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, i| Ok(Type::empty_tuple())), + out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), run: Arc::new(|a, _i| { print!("{}", a.get()); - std::io::stdout().lock().flush(); + _ = std::io::stdout().lock().flush(); Data::empty_tuple() }), }), @@ -86,7 +99,7 @@ impl Config { Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, i| Ok(Type::empty_tuple())), + out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), run: Arc::new(|a, _i| { println!("{}", a.get()); Data::empty_tuple() diff --git a/mers_lib/src/program/parsed/assign_to.rs b/mers_lib/src/program/parsed/assign_to.rs index 6a94343..ce9628a 100755 --- a/mers_lib/src/program/parsed/assign_to.rs +++ b/mers_lib/src/program/parsed/assign_to.rs @@ -1,6 +1,6 @@ -use crate::program::{ - self, - run::{CheckError, SourceRange}, +use crate::{ + errors::{CheckError, SourceRange}, + program::{self}, }; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/parsed/block.rs b/mers_lib/src/program/parsed/block.rs index 282b9f8..c905432 100755 --- a/mers_lib/src/program/parsed/block.rs +++ b/mers_lib/src/program/parsed/block.rs @@ -1,9 +1,7 @@ use crate::{ + errors::{CheckError, SourceRange}, info, - program::{ - self, - run::{CheckError, SourceRange}, - }, + program::{self}, }; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/parsed/chain.rs b/mers_lib/src/program/parsed/chain.rs index 34243e4..a62832b 100755 --- a/mers_lib/src/program/parsed/chain.rs +++ b/mers_lib/src/program/parsed/chain.rs @@ -1,5 +1,7 @@ -use crate::program::run::{CheckError, SourceRange}; -use crate::{info, program}; +use crate::{ + errors::{CheckError, SourceRange}, + info, program, +}; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/parsed/function.rs b/mers_lib/src/program/parsed/function.rs index eaf4b54..eb02e4c 100755 --- a/mers_lib/src/program/parsed/function.rs +++ b/mers_lib/src/program/parsed/function.rs @@ -1,8 +1,8 @@ -use crate::program::run::{CheckError, SourceRange}; use std::sync::{Arc, Mutex}; use crate::{ data, + errors::{CheckError, SourceRange}, program::{self, run::CheckInfo}, }; @@ -29,8 +29,8 @@ impl MersStatement for Function { let arg_target = Arc::new(self.arg.compile(info, comp)?); comp.is_init = false; let run = Arc::new(self.run.compile(info, comp)?); - let arg2 = Arc::clone(&arg_target); - let run2 = Arc::clone(&run); + let arg2: Arc> = Arc::clone(&arg_target); + let run2: Arc> = Arc::clone(&run); Ok(Box::new(program::run::function::Function { pos_in_src: self.pos_in_src, func_no_info: data::function::Function { diff --git a/mers_lib/src/program/parsed/if.rs b/mers_lib/src/program/parsed/if.rs index bbbde1a..6bc4790 100755 --- a/mers_lib/src/program/parsed/if.rs +++ b/mers_lib/src/program/parsed/if.rs @@ -1,6 +1,6 @@ -use crate::program::{ - self, - run::{CheckError, SourceRange}, +use crate::{ + errors::{CheckError, SourceRange}, + program::{self}, }; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/parsed/include_mers.rs b/mers_lib/src/program/parsed/include_mers.rs index 246b1cc..85da98f 100644 --- a/mers_lib/src/program/parsed/include_mers.rs +++ b/mers_lib/src/program/parsed/include_mers.rs @@ -4,11 +4,9 @@ use colored::Colorize; use crate::{ data::{self, Data}, + errors::{error_colors, CheckError, SourceRange}, info::{self, Local}, - program::{ - self, - run::{error_colors, CheckError, SourceRange}, - }, + program::{self}, }; use super::{CompInfo, MersStatement}; @@ -27,7 +25,7 @@ impl MersStatement for IncludeMers { info: &mut info::Info, comp: CompInfo, ) -> Result, CheckError> { - let compiled = match self.include.compile(info, comp) { + let compiled: Arc> = match self.include.compile(info, comp) { Ok(v) => Arc::new(v), Err(e) => { return Err(CheckError::new() diff --git a/mers_lib/src/program/parsed/init_to.rs b/mers_lib/src/program/parsed/init_to.rs index ce893dd..fd14e93 100755 --- a/mers_lib/src/program/parsed/init_to.rs +++ b/mers_lib/src/program/parsed/init_to.rs @@ -1,5 +1,7 @@ -use crate::program::run::SourceRange; -use crate::program::{self, run::CheckError}; +use crate::{ + errors::{CheckError, SourceRange}, + program::{self}, +}; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/parsed/mod.rs b/mers_lib/src/program/parsed/mod.rs index a706722..22aafff 100755 --- a/mers_lib/src/program/parsed/mod.rs +++ b/mers_lib/src/program/parsed/mod.rs @@ -1,8 +1,9 @@ use std::{collections::HashMap, fmt::Debug}; -use crate::info; - -use super::run::{CheckError, SourceRange}; +use crate::{ + errors::{CheckError, SourceRange}, + info, +}; #[cfg(feature = "parse")] pub mod assign_to; diff --git a/mers_lib/src/program/parsed/tuple.rs b/mers_lib/src/program/parsed/tuple.rs index 591ceb3..6e675d6 100755 --- a/mers_lib/src/program/parsed/tuple.rs +++ b/mers_lib/src/program/parsed/tuple.rs @@ -1,9 +1,7 @@ use crate::{ + errors::{CheckError, SourceRange}, info, - program::{ - self, - run::{CheckError, SourceRange}, - }, + program::{self}, }; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/parsed/value.rs b/mers_lib/src/program/parsed/value.rs index 2edf10e..dbb1f67 100755 --- a/mers_lib/src/program/parsed/value.rs +++ b/mers_lib/src/program/parsed/value.rs @@ -1,5 +1,8 @@ -use crate::program::run::{CheckError, SourceRange}; -use crate::{data::Data, program}; +use crate::{ + data::Data, + errors::{CheckError, SourceRange}, + program, +}; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index 590fc63..35fb165 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -1,9 +1,7 @@ use crate::{ + errors::{error_colors, CheckError, SourceRange}, info::Local, - program::{ - self, - run::{error_colors, CheckError, SourceRange}, - }, + program::{self}, }; use super::{CompInfo, MersStatement}; diff --git a/mers_lib/src/program/run/assign_to.rs b/mers_lib/src/program/run/assign_to.rs index 0db87a8..3aa8de8 100755 --- a/mers_lib/src/program/run/assign_to.rs +++ b/mers_lib/src/program/run/assign_to.rs @@ -1,8 +1,11 @@ use colored::Colorize; -use crate::data::{self, Data, MersType, Type}; +use crate::{ + data::{self, Data, MersType, Type}, + errors::{error_colors, CheckError, SourceRange}, +}; -use super::{error_colors, CheckError, CheckInfo, MersStatement, SourceRange}; +use super::{CheckInfo, MersStatement}; #[derive(Debug)] pub struct AssignTo { diff --git a/mers_lib/src/program/run/block.rs b/mers_lib/src/program/run/block.rs index af15de3..8765917 100755 --- a/mers_lib/src/program/run/block.rs +++ b/mers_lib/src/program/run/block.rs @@ -1,6 +1,9 @@ -use crate::data::Type; +use crate::{ + data::Type, + errors::{CheckError, SourceRange}, +}; -use super::{MersStatement, SourceRange}; +use super::{CheckInfo, MersStatement}; #[derive(Debug)] pub struct Block { @@ -10,9 +13,9 @@ pub struct Block { impl MersStatement for Block { fn check_custom( &self, - info: &mut super::CheckInfo, + info: &mut CheckInfo, init_to: Option<&Type>, - ) -> Result { + ) -> Result { if init_to.is_some() { return Err("can't init to statement type Block".to_string().into()); } diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index 8a20cf9..397d952 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -2,9 +2,12 @@ use std::sync::Arc; use colored::Colorize; -use crate::data::{Data, Type}; +use crate::{ + data::{Data, Type}, + errors::{error_colors, CheckError, SourceRange}, +}; -use super::{error_colors, CheckError, MersStatement, SourceRange}; +use super::MersStatement; #[derive(Debug)] pub struct Chain { diff --git a/mers_lib/src/program/run/function.rs b/mers_lib/src/program/run/function.rs index 2609653..c8d770a 100755 --- a/mers_lib/src/program/run/function.rs +++ b/mers_lib/src/program/run/function.rs @@ -1,8 +1,11 @@ use std::sync::Arc; -use crate::data::{self, Data, MersData, Type}; +use crate::{ + data::{self, Data, MersData, Type}, + errors::{CheckError, SourceRange}, +}; -use super::{MersStatement, SourceRange}; +use super::MersStatement; #[derive(Debug)] pub struct Function { @@ -15,7 +18,7 @@ impl MersStatement for Function { &self, info: &mut super::CheckInfo, init_to: Option<&Type>, - ) -> Result { + ) -> Result { if init_to.is_some() { return Err("can't init to statement type Function".to_string().into()); } diff --git a/mers_lib/src/program/run/if.rs b/mers_lib/src/program/run/if.rs index 0220d77..51406d9 100755 --- a/mers_lib/src/program/run/if.rs +++ b/mers_lib/src/program/run/if.rs @@ -2,9 +2,12 @@ use std::sync::Arc; use colored::Colorize; -use crate::data::{self, Data, MersType, Type}; +use crate::{ + data::{self, Data, MersType, Type}, + errors::{error_colors, CheckError, SourceRange}, +}; -use super::{error_colors, CheckError, MersStatement, SourceRange}; +use super::MersStatement; #[derive(Debug)] pub struct If { @@ -19,7 +22,7 @@ impl MersStatement for If { &self, info: &mut super::CheckInfo, init_to: Option<&Type>, - ) -> Result { + ) -> Result { if init_to.is_some() { return Err("can't init to statement type If".to_string().into()); } diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 93e87d1..71de367 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -1,15 +1,12 @@ use std::{ - fmt::{Debug, Display}, + fmt::Debug, sync::{Arc, RwLock}, }; -use colored::Colorize; -use line_span::LineSpanExt; - use crate::{ data::{self, Data, Type}, + errors::{CheckError, SourceRange}, info, - parsing::{Source, SourcePos}, }; #[cfg(feature = "run")] @@ -61,208 +58,6 @@ pub trait MersStatement: Debug + Send + Sync { fn source_range(&self) -> SourceRange; } -#[derive(Clone, Copy, Debug)] -pub struct SourceRange { - start: SourcePos, - end: SourcePos, -} -impl From<(SourcePos, SourcePos)> for SourceRange { - fn from(value: (SourcePos, SourcePos)) -> Self { - SourceRange { - start: value.0, - end: value.1, - } - } -} -impl SourceRange { - pub fn start(&self) -> SourcePos { - self.start - } - pub fn end(&self) -> SourcePos { - self.end - } -} -#[derive(Clone, Debug)] -pub struct CheckError(Vec); -#[allow(non_upper_case_globals)] -pub mod error_colors { - use colored::Color; - - pub const UnknownVariable: Color = Color::Red; - - 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; - - pub const BackslashEscapeUnknown: Color = Color::Red; - pub const BackslashEscapeEOF: Color = Color::Red; - pub const StringEOF: Color = Color::Red; - - pub const IfConditionNotBool: Color = Color::Red; - pub const ChainWithNonFunction: Color = Color::Yellow; - - pub const Function: Color = Color::BrightMagenta; - pub const FunctionArgument: Color = Color::BrightBlue; - - 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; -} -#[derive(Clone, Debug)] -enum CheckErrorComponent { - Message(String), - Error(CheckError), - Source(Vec<(SourceRange, Option)>), -} -#[derive(Clone)] -pub struct CheckErrorHRConfig { - indent_start: String, - indent_default: String, - indent_end: String, -} -pub struct CheckErrorDisplay<'a> { - e: &'a CheckError, - src: Option<&'a Source>, -} -impl Display for CheckErrorDisplay<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.e.human_readable( - f, - self.src, - &CheckErrorHRConfig { - indent_start: String::new(), - indent_default: String::new(), - indent_end: String::new(), - }, - ) - } -} -impl CheckError { - pub fn new() -> Self { - CheckError(vec![]) - } - fn add(mut self, v: CheckErrorComponent) -> Self { - self.0.push(v); - self - } - pub(crate) fn msg(self, s: String) -> Self { - self.add(CheckErrorComponent::Message(s)) - } - pub(crate) fn err(self, e: Self) -> Self { - self.add(CheckErrorComponent::Error(e)) - } - pub(crate) fn src(self, s: Vec<(SourceRange, Option)>) -> Self { - self.add(CheckErrorComponent::Source(s)) - } - pub fn display<'a>(&'a self, src: &'a Source) -> CheckErrorDisplay<'a> { - CheckErrorDisplay { - e: self, - src: Some(src), - } - } - pub fn display_no_src<'a>(&'a self) -> CheckErrorDisplay<'a> { - CheckErrorDisplay { e: self, src: None } - } - // will, unless empty, end in a newline - fn human_readable( - &self, - f: &mut std::fmt::Formatter<'_>, - src: Option<&Source>, - cfg: &CheckErrorHRConfig, - ) -> std::fmt::Result { - let len = self.0.len(); - for (i, component) in self.0.iter().enumerate() { - macro_rules! indent { - () => { - if i + 1 == len { - &cfg.indent_end - } else if i == 0 { - &cfg.indent_start - } else { - &cfg.indent_default - } - }; - } - match component { - CheckErrorComponent::Message(msg) => writeln!(f, "{}{msg}", indent!())?, - CheckErrorComponent::Error(err) => { - let mut cfg = cfg.clone(); - cfg.indent_start.push_str("│"); - cfg.indent_default.push_str("│"); - cfg.indent_end.push_str("└"); - err.human_readable(f, src, &cfg)?; - } - CheckErrorComponent::Source(highlights) => { - if let Some(src) = src { - let start = highlights.iter().map(|v| v.0.start.pos()).min(); - let end = highlights.iter().map(|v| v.0.start.pos()).max(); - if let (Some(start), Some(end)) = (start, end) { - writeln!(f, "{}Line(s) [?] ({start}..{end})", indent!())?; - let start = src.get_line_start(start); - let end = src.get_line_end(end); - let lines = src.src()[start..end].line_spans().collect::>(); - for line in lines { - let line_start = line.start(); - let line_end = line.end(); - let line = line.as_str(); - writeln!(f, "{} {line}", indent!())?; - let mut right = 0; - for (pos, color) in highlights { - if let Some(color) = color { - let highlight_start = pos.start.pos() - start; - let highlight_end = pos.end.pos() - start; - if highlight_start < line_end && highlight_end > line_start - { - // where the highlight starts in this line - let hl_start = - highlight_start.saturating_sub(line_start); - // highlight would be further left than cursor, so we need a new line - if hl_start < right { - right = 0; - writeln!(f)?; - } - // length of the highlight - let hl_len = highlight_end - .saturating_sub(line_start) - .saturating_sub(hl_start); - let hl_space = hl_start - right; - let print_indent = right == 0; - let hl_len = hl_len.min(line.len() - right); - right += hl_space + hl_len; - if print_indent && right != 0 { - write!(f, "{} ", indent!())?; - } - write!( - f, - "{}{}", - " ".repeat(hl_space), - "~".repeat(hl_len).color(*color) - )?; - } - } - } - if right != 0 { - writeln!(f)?; - } - } - } - } - } - } - } - Ok(()) - } -} -impl From for CheckError { - fn from(value: String) -> Self { - Self::new().msg(value) - } -} - pub type Info = info::Info; pub type CheckInfo = info::Info; diff --git a/mers_lib/src/program/run/tuple.rs b/mers_lib/src/program/run/tuple.rs index b12a68c..f082072 100755 --- a/mers_lib/src/program/run/tuple.rs +++ b/mers_lib/src/program/run/tuple.rs @@ -2,9 +2,12 @@ use std::{collections::VecDeque, sync::Arc}; use colored::Colorize; -use crate::data::{self, tuple::TupleT, Data, Type}; +use crate::{ + data::{self, tuple::TupleT, Data, Type}, + errors::{error_colors, SourceRange}, +}; -use super::{error_colors, MersStatement, SourceRange}; +use super::MersStatement; #[derive(Debug)] pub struct Tuple { diff --git a/mers_lib/src/program/run/value.rs b/mers_lib/src/program/run/value.rs index 706b7b3..85f118b 100755 --- a/mers_lib/src/program/run/value.rs +++ b/mers_lib/src/program/run/value.rs @@ -1,6 +1,9 @@ -use crate::data::{Data, Type}; +use crate::{ + data::{Data, Type}, + errors::SourceRange, +}; -use super::{MersStatement, SourceRange}; +use super::{CheckInfo, MersStatement}; #[derive(Debug)] pub struct Value { @@ -14,7 +17,7 @@ impl MersStatement for Value { } fn check_custom( &self, - info: &mut super::CheckInfo, + _info: &mut CheckInfo, init_to: Option<&Type>, ) -> Result { if init_to.is_some() { diff --git a/mers_lib/src/program/run/variable.rs b/mers_lib/src/program/run/variable.rs index 2917087..dc0bcea 100755 --- a/mers_lib/src/program/run/variable.rs +++ b/mers_lib/src/program/run/variable.rs @@ -1,8 +1,11 @@ use std::sync::{Arc, RwLock}; -use crate::data::{self, Data, Type}; +use crate::{ + data::{self, Data, Type}, + errors::SourceRange, +}; -use super::{MersStatement, SourceRange}; +use super::MersStatement; #[derive(Debug)] pub struct Variable {