diff --git a/mers/src/main.rs b/mers/src/main.rs index f22e4c0..147fe17 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -69,17 +69,14 @@ fn main() { }); let (mut info_parsed, mut info_run, mut info_check) = config.infos(); let mut source = match args.command { - Command::Run { file } => { - let str = match fs::read_to_string(&file) { - Ok(s) => s, - Err(e) => { - eprintln!("Can't read file {file:?}: {e}"); - exit(10); - } - }; - Source::new(str) - } - Command::Exec { source } => Source::new(source), + Command::Run { file } => match Source::new_from_file(PathBuf::from(&file)) { + Ok(s) => s, + Err(e) => { + eprintln!("Can't read file {file:?}: {e}"); + exit(10); + } + }, + Command::Exec { source } => Source::new_from_string(source), }; let parsed = match parse(&mut source) { Ok(v) => v, diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index 85f8e33..fe7516a 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::fmt::{Debug, Display}; use colored::Colorize; use line_span::LineSpans; @@ -35,7 +35,6 @@ impl SourceRange { self.end } } -#[derive(Clone, Debug)] pub struct CheckError(Vec); #[allow(non_upper_case_globals)] pub mod error_colors { @@ -65,10 +64,10 @@ pub mod error_colors { pub const AssignTo: Color = InitTo; pub const AssignTargetNonReference: Color = Color::BrightYellow; } -#[derive(Clone, Debug)] enum CheckErrorComponent { Message(String), Error(CheckError), + ErrorWithSrc(CheckErrorWithSrc), Source(Vec<(SourceRange, Option)>), } #[derive(Clone)] @@ -86,6 +85,12 @@ pub struct CheckErrorDisplay<'a> { pub show_comments: bool, } #[cfg(feature = "parse")] +pub struct CheckErrorWithSrc { + e: CheckError, + src: 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; @@ -93,6 +98,13 @@ impl<'a> CheckErrorDisplay<'a> { } } #[cfg(feature = "parse")] +impl CheckErrorWithSrc { + 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( @@ -107,6 +119,21 @@ impl Display for CheckErrorDisplay<'_> { ) } } +#[cfg(feature = "parse")] +impl Display for CheckErrorWithSrc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.e.human_readable( + f, + Some(&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![]) @@ -121,6 +148,13 @@ impl CheckError { pub(crate) fn err(self, e: Self) -> Self { self.add(CheckErrorComponent::Error(e)) } + pub(crate) fn err_with_src(self, e: CheckError, src: Source) -> Self { + self.add(CheckErrorComponent::ErrorWithSrc(CheckErrorWithSrc { + e, + src, + show_comments: true, + })) + } pub(crate) fn src(self, s: Vec<(SourceRange, Option)>) -> Self { self.add(CheckErrorComponent::Source(s)) } @@ -148,6 +182,8 @@ impl CheckError { src: Option<&Source>, cfg: &CheckErrorHRConfig, ) -> std::fmt::Result { + use crate::parsing::SourceFrom; + let len = self.0.len(); for (i, component) in self.0.iter().enumerate() { macro_rules! indent { @@ -170,6 +206,14 @@ impl CheckError { cfg.indent_end.push_str("└"); err.human_readable(f, src, &cfg)?; } + CheckErrorComponent::ErrorWithSrc(err) => { + let mut cfg = cfg.clone(); + cfg.indent_start.push_str(&"│".bright_yellow().to_string()); + cfg.indent_default + .push_str(&"│".bright_yellow().to_string()); + cfg.indent_end.push_str(&"└".bright_yellow().to_string()); + err.e.human_readable(f, Some(&err.src), &cfg)?; + } CheckErrorComponent::Source(highlights) => { if let Some(src) = src { let start = highlights.iter().map(|v| v.0.start.pos()).min(); @@ -218,21 +262,27 @@ impl CheckError { } }) .count(); + let src_from = match src.src_from() { + SourceFrom::File(path) => format!(" [{}]", path.to_string_lossy()), + SourceFrom::Unspecified => String::with_capacity(0), + }; if first_line_nr == last_line_nr { writeln!( f, - "{}Line {first_line_nr} ({}..{})", + "{}Line {first_line_nr} ({}..{}){}", indent!(), start_with_comments + 1 - first_line_start, end_with_comments - last_line_start, + src_from, )?; } else { writeln!( f, - "{}Lines {first_line_nr}-{last_line_nr} ({}..{})", + "{}Lines {first_line_nr}-{last_line_nr} ({}..{}){}", indent!(), start_with_comments + 1 - first_line_start, end_with_comments - last_line_start, + src_from, )?; } let lines = if cfg.show_comments { @@ -306,3 +356,13 @@ impl From for CheckError { Self::new().msg(value) } } +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_no_src()) + } +} diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index ce05167..b76995f 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{fmt::Debug, path::PathBuf, sync::Arc}; use crate::{ errors::{CheckError, SourcePos}, @@ -19,6 +19,7 @@ pub fn parse(src: &mut Source) -> Result } pub struct Source { + src_from: SourceFrom, src_raw_len: usize, src_og: String, src: String, @@ -27,8 +28,38 @@ pub struct Source { i: usize, sections: Vec, } +impl Clone for Source { + fn clone(&self) -> Self { + Self { + src_from: self.src_from.clone(), + src_raw_len: self.src_raw_len, + src_og: self.src_og.clone(), + src: self.src.clone(), + comments: self.comments.clone(), + i: self.i, + sections: vec![], + } + } +} +impl Debug for Source { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Src: {:?}", self.src_from) + } +} +#[derive(Clone, Debug)] +pub enum SourceFrom { + File(PathBuf), + Unspecified, +} impl Source { - pub fn new(source: String) -> Self { + pub fn new_from_file(path: PathBuf) -> std::io::Result { + let content = std::fs::read_to_string(&path)?; + Ok(Self::new(SourceFrom::File(path), content)) + } + pub fn new_from_string(source: String) -> Self { + Self::new(SourceFrom::Unspecified, source) + } + pub fn new(src_from: SourceFrom, source: String) -> Self { let mut src = String::with_capacity(source.len()); let mut comment = (0, String::new()); let mut comments = Vec::new(); @@ -88,6 +119,7 @@ impl Source { } } Self { + src_from, src_raw_len: source.len(), src_og: source, src, @@ -228,6 +260,10 @@ impl Source { o } + pub fn src_from(&self) -> &SourceFrom { + &self.src_from + } + pub fn pos_in_og(&self, mut pos: usize, inclusive: bool) -> usize { for (start, comment) in &self.comments { if *start < pos || (inclusive && *start == pos) { diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index df5bb43..8ad4a71 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -1,4 +1,4 @@ -use std::fs; +use std::path::PathBuf; use super::{Source, SourcePos}; use crate::{ @@ -154,13 +154,21 @@ pub fn parse_no_chain( src.skip_whitespace(); let string_in_src = src.get_pos(); if src.next_char() == Some('"') { - let s = parse_string(src, string_in_src)?; - match fs::read_to_string(&s) { - Ok(s) => { + let file_path = parse_string(src, string_in_src)?; + match Source::new_from_file(PathBuf::from(&file_path)) { + Ok(mut inner_src) => { return Ok(Some(Box::new( program::parsed::include_mers::IncludeMers { pos_in_src: (pos_in_src, src.get_pos()).into(), - include: super::parse(&mut Source::new(s))?, + include: match super::parse(&mut inner_src) { + Ok(v) => v, + Err(e) => { + return Err( + CheckError::new().err_with_src(e, inner_src) + ) + } + }, + inner_src, }, ))); } @@ -173,7 +181,7 @@ pub fn parse_no_chain( Some(error_colors::HashIncludeCantLoadFile), ), ]) - .msg(format!("Can't load file '{s}': {e}"))); + .msg(format!("Can't load file '{file_path}': {e}"))); } } } else { diff --git a/mers_lib/src/program/parsed/chain.rs b/mers_lib/src/program/parsed/chain.rs index a62832b..cdac4d0 100755 --- a/mers_lib/src/program/parsed/chain.rs +++ b/mers_lib/src/program/parsed/chain.rs @@ -24,6 +24,7 @@ impl MersStatement for Chain { pos_in_src: self.pos_in_src, first: self.first.compile(info, comp)?, chained: self.chained.compile(info, comp)?, + as_part_of_include: None, })) } fn source_range(&self) -> SourceRange { diff --git a/mers_lib/src/program/parsed/include_mers.rs b/mers_lib/src/program/parsed/include_mers.rs index 85da98f..16dca44 100644 --- a/mers_lib/src/program/parsed/include_mers.rs +++ b/mers_lib/src/program/parsed/include_mers.rs @@ -6,6 +6,7 @@ use crate::{ data::{self, Data}, errors::{error_colors, CheckError, SourceRange}, info::{self, Local}, + parsing::Source, program::{self}, }; @@ -15,6 +16,7 @@ use super::{CompInfo, MersStatement}; pub struct IncludeMers { pub pos_in_src: SourceRange, pub include: Box, + pub inner_src: Source, } impl MersStatement for IncludeMers { fn has_scope(&self) -> bool { @@ -25,15 +27,23 @@ impl MersStatement for IncludeMers { info: &mut info::Info, comp: CompInfo, ) -> Result, CheckError> { - let compiled: Arc> = match self.include.compile(info, comp) { - Ok(v) => Arc::new(v), - Err(e) => { - return Err(CheckError::new() - .src(vec![(self.pos_in_src, Some(error_colors::HashIncludeErrorInIncludedFile))]) - .msg("Error in inner mers statement! (note: inner errors may refer to a different file)".color(error_colors::HashIncludeErrorInIncludedFile).to_string()) - .err(e)) - } - }; + let compiled: Arc> = + match self.include.compile(info, comp) { + Ok(v) => Arc::new(v), + Err(e) => { + return Err(CheckError::new() + .src(vec![( + self.pos_in_src, + Some(error_colors::HashIncludeErrorInIncludedFile), + )]) + .msg( + "Error in #include! (note: inner errors may refer to a different file)" + .color(error_colors::HashIncludeErrorInIncludedFile) + .to_string(), + ) + .err_with_src(e, self.inner_src.clone())) + } + }; let compiled2 = Arc::clone(&compiled); Ok(Box::new(program::run::chain::Chain { pos_in_src: self.pos_in_src, @@ -50,6 +60,7 @@ impl MersStatement for IncludeMers { run: Arc::new(move |_, i| compiled2.run(&mut i.duplicate())), }, }), + as_part_of_include: Some(self.inner_src.clone()), })) } fn source_range(&self) -> SourceRange { diff --git a/mers_lib/src/program/run/assign_to.rs b/mers_lib/src/program/run/assign_to.rs index 3aa8de8..e52bf36 100755 --- a/mers_lib/src/program/run/assign_to.rs +++ b/mers_lib/src/program/run/assign_to.rs @@ -31,8 +31,8 @@ impl MersStatement for AssignTo { return Err(CheckError::new() .src(vec![ (self.pos_in_src, None), - (self.target.source_range(), Some(error_colors::InitFrom)), - (self.source.source_range(), Some(error_colors::InitTo)), + (self.target.source_range(), Some(error_colors::InitTo)), + (self.source.source_range(), Some(error_colors::InitFrom)), ]) .msg(format!("Cannot initialize:")) .err(e)) diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index 397d952..a67fdd0 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -5,6 +5,7 @@ use colored::Colorize; use crate::{ data::{Data, Type}, errors::{error_colors, CheckError, SourceRange}, + parsing::Source, }; use super::MersStatement; @@ -14,6 +15,7 @@ pub struct Chain { pub pos_in_src: SourceRange, pub first: Box, pub chained: Box, + pub as_part_of_include: Option, } impl MersStatement for Chain { fn check_custom( @@ -35,21 +37,35 @@ impl MersStatement for Chain { match (func.0)(&arg) { Ok(t) => o.add(Arc::new(t)), Err(e) => { - return Err(CheckError::new() - .src(vec![ - (self.pos_in_src, None), - ( - self.first.source_range(), - Some(error_colors::FunctionArgument), - ), - (self.chained.source_range(), Some(error_colors::Function)), - ]) - .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)) + return Err(if let Some(inner_src) = &self.as_part_of_include { + CheckError::new() + .src(vec![( + self.pos_in_src, + Some(error_colors::HashIncludeErrorInIncludedFile), + )]) + .msg( + "Error in #include:" + .color(error_colors::HashIncludeErrorInIncludedFile) + .to_string(), + ) + .err_with_src(e, inner_src.clone()) + } else { + CheckError::new() + .src(vec![ + (self.pos_in_src, None), + ( + self.first.source_range(), + Some(error_colors::FunctionArgument), + ), + (self.chained.source_range(), Some(error_colors::Function)), + ]) + .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) + }) } } } else { diff --git a/mers_lib/src/program/run/tuple.rs b/mers_lib/src/program/run/tuple.rs index f082072..6e0a8f9 100755 --- a/mers_lib/src/program/run/tuple.rs +++ b/mers_lib/src/program/run/tuple.rs @@ -45,7 +45,20 @@ impl MersStatement for Tuple { ).into()); } } else { - return Err(format!("can't init a {} with a value of type {}, which is part of {} - only tuples can be assigned to tuples", "tuple".color(error_colors::InitTo), t.to_string().color(error_colors::InitFrom), init_to.to_string().color(error_colors::InitFrom)).into()); + 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 { + format!( + ", which is part of {}", + init_to.to_string().color(error_colors::InitFrom) + ) + } else { + format!("") + } + ) + .into()); } } Some(vec)