improve/fix errors in #include

This commit is contained in:
Mark 2023-11-17 10:09:44 +01:00
parent 12925fed67
commit b6d708db3d
9 changed files with 193 additions and 51 deletions

View File

@ -69,17 +69,14 @@ fn main() {
}); });
let (mut info_parsed, mut info_run, mut info_check) = config.infos(); let (mut info_parsed, mut info_run, mut info_check) = config.infos();
let mut source = match args.command { let mut source = match args.command {
Command::Run { file } => { Command::Run { file } => match Source::new_from_file(PathBuf::from(&file)) {
let str = match fs::read_to_string(&file) { Ok(s) => s,
Ok(s) => s, Err(e) => {
Err(e) => { eprintln!("Can't read file {file:?}: {e}");
eprintln!("Can't read file {file:?}: {e}"); exit(10);
exit(10); }
} },
}; Command::Exec { source } => Source::new_from_string(source),
Source::new(str)
}
Command::Exec { source } => Source::new(source),
}; };
let parsed = match parse(&mut source) { let parsed = match parse(&mut source) {
Ok(v) => v, Ok(v) => v,

View File

@ -1,4 +1,4 @@
use std::fmt::Display; use std::fmt::{Debug, Display};
use colored::Colorize; use colored::Colorize;
use line_span::LineSpans; use line_span::LineSpans;
@ -35,7 +35,6 @@ impl SourceRange {
self.end self.end
} }
} }
#[derive(Clone, Debug)]
pub struct CheckError(Vec<CheckErrorComponent>); pub struct CheckError(Vec<CheckErrorComponent>);
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
pub mod error_colors { pub mod error_colors {
@ -65,10 +64,10 @@ pub mod error_colors {
pub const AssignTo: Color = InitTo; pub const AssignTo: Color = InitTo;
pub const AssignTargetNonReference: Color = Color::BrightYellow; pub const AssignTargetNonReference: Color = Color::BrightYellow;
} }
#[derive(Clone, Debug)]
enum CheckErrorComponent { enum CheckErrorComponent {
Message(String), Message(String),
Error(CheckError), Error(CheckError),
ErrorWithSrc(CheckErrorWithSrc),
Source(Vec<(SourceRange, Option<colored::Color>)>), Source(Vec<(SourceRange, Option<colored::Color>)>),
} }
#[derive(Clone)] #[derive(Clone)]
@ -86,6 +85,12 @@ pub struct CheckErrorDisplay<'a> {
pub show_comments: bool, pub show_comments: bool,
} }
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub struct CheckErrorWithSrc {
e: CheckError,
src: Source,
pub show_comments: bool,
}
#[cfg(feature = "parse")]
impl<'a> CheckErrorDisplay<'a> { impl<'a> CheckErrorDisplay<'a> {
pub fn show_comments(mut self, show_comments: bool) -> Self { pub fn show_comments(mut self, show_comments: bool) -> Self {
self.show_comments = show_comments; self.show_comments = show_comments;
@ -93,6 +98,13 @@ impl<'a> CheckErrorDisplay<'a> {
} }
} }
#[cfg(feature = "parse")] #[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<'_> { impl Display for CheckErrorDisplay<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.e.human_readable( 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 { impl CheckError {
pub fn new() -> Self { pub fn new() -> Self {
CheckError(vec![]) CheckError(vec![])
@ -121,6 +148,13 @@ impl CheckError {
pub(crate) fn err(self, e: Self) -> Self { pub(crate) fn err(self, e: Self) -> Self {
self.add(CheckErrorComponent::Error(e)) 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<colored::Color>)>) -> Self { pub(crate) fn src(self, s: Vec<(SourceRange, Option<colored::Color>)>) -> Self {
self.add(CheckErrorComponent::Source(s)) self.add(CheckErrorComponent::Source(s))
} }
@ -148,6 +182,8 @@ impl CheckError {
src: Option<&Source>, src: Option<&Source>,
cfg: &CheckErrorHRConfig, cfg: &CheckErrorHRConfig,
) -> std::fmt::Result { ) -> std::fmt::Result {
use crate::parsing::SourceFrom;
let len = self.0.len(); let len = self.0.len();
for (i, component) in self.0.iter().enumerate() { for (i, component) in self.0.iter().enumerate() {
macro_rules! indent { macro_rules! indent {
@ -170,6 +206,14 @@ impl CheckError {
cfg.indent_end.push_str(""); cfg.indent_end.push_str("");
err.human_readable(f, src, &cfg)?; 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) => { CheckErrorComponent::Source(highlights) => {
if let Some(src) = src { if let Some(src) = src {
let start = highlights.iter().map(|v| v.0.start.pos()).min(); let start = highlights.iter().map(|v| v.0.start.pos()).min();
@ -218,21 +262,27 @@ impl CheckError {
} }
}) })
.count(); .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 { if first_line_nr == last_line_nr {
writeln!( writeln!(
f, f,
"{}Line {first_line_nr} ({}..{})", "{}Line {first_line_nr} ({}..{}){}",
indent!(), indent!(),
start_with_comments + 1 - first_line_start, start_with_comments + 1 - first_line_start,
end_with_comments - last_line_start, end_with_comments - last_line_start,
src_from,
)?; )?;
} else { } else {
writeln!( writeln!(
f, f,
"{}Lines {first_line_nr}-{last_line_nr} ({}..{})", "{}Lines {first_line_nr}-{last_line_nr} ({}..{}){}",
indent!(), indent!(),
start_with_comments + 1 - first_line_start, start_with_comments + 1 - first_line_start,
end_with_comments - last_line_start, end_with_comments - last_line_start,
src_from,
)?; )?;
} }
let lines = if cfg.show_comments { let lines = if cfg.show_comments {
@ -306,3 +356,13 @@ impl From<String> for CheckError {
Self::new().msg(value) 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())
}
}

View File

@ -1,4 +1,4 @@
use std::sync::Arc; use std::{fmt::Debug, path::PathBuf, sync::Arc};
use crate::{ use crate::{
errors::{CheckError, SourcePos}, errors::{CheckError, SourcePos},
@ -19,6 +19,7 @@ pub fn parse(src: &mut Source) -> Result<Box<dyn program::parsed::MersStatement>
} }
pub struct Source { pub struct Source {
src_from: SourceFrom,
src_raw_len: usize, src_raw_len: usize,
src_og: String, src_og: String,
src: String, src: String,
@ -27,8 +28,38 @@ pub struct Source {
i: usize, i: usize,
sections: Vec<SectionMarker>, sections: Vec<SectionMarker>,
} }
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 { impl Source {
pub fn new(source: String) -> Self { pub fn new_from_file(path: PathBuf) -> std::io::Result<Self> {
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 src = String::with_capacity(source.len());
let mut comment = (0, String::new()); let mut comment = (0, String::new());
let mut comments = Vec::new(); let mut comments = Vec::new();
@ -88,6 +119,7 @@ impl Source {
} }
} }
Self { Self {
src_from,
src_raw_len: source.len(), src_raw_len: source.len(),
src_og: source, src_og: source,
src, src,
@ -228,6 +260,10 @@ impl Source {
o o
} }
pub fn src_from(&self) -> &SourceFrom {
&self.src_from
}
pub fn pos_in_og(&self, mut pos: usize, inclusive: bool) -> usize { pub fn pos_in_og(&self, mut pos: usize, inclusive: bool) -> usize {
for (start, comment) in &self.comments { for (start, comment) in &self.comments {
if *start < pos || (inclusive && *start == pos) { if *start < pos || (inclusive && *start == pos) {

View File

@ -1,4 +1,4 @@
use std::fs; use std::path::PathBuf;
use super::{Source, SourcePos}; use super::{Source, SourcePos};
use crate::{ use crate::{
@ -154,13 +154,21 @@ pub fn parse_no_chain(
src.skip_whitespace(); src.skip_whitespace();
let string_in_src = src.get_pos(); let string_in_src = src.get_pos();
if src.next_char() == Some('"') { if src.next_char() == Some('"') {
let s = parse_string(src, string_in_src)?; let file_path = parse_string(src, string_in_src)?;
match fs::read_to_string(&s) { match Source::new_from_file(PathBuf::from(&file_path)) {
Ok(s) => { Ok(mut inner_src) => {
return Ok(Some(Box::new( return Ok(Some(Box::new(
program::parsed::include_mers::IncludeMers { program::parsed::include_mers::IncludeMers {
pos_in_src: (pos_in_src, src.get_pos()).into(), 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), Some(error_colors::HashIncludeCantLoadFile),
), ),
]) ])
.msg(format!("Can't load file '{s}': {e}"))); .msg(format!("Can't load file '{file_path}': {e}")));
} }
} }
} else { } else {

View File

@ -24,6 +24,7 @@ impl MersStatement for Chain {
pos_in_src: self.pos_in_src, pos_in_src: self.pos_in_src,
first: self.first.compile(info, comp)?, first: self.first.compile(info, comp)?,
chained: self.chained.compile(info, comp)?, chained: self.chained.compile(info, comp)?,
as_part_of_include: None,
})) }))
} }
fn source_range(&self) -> SourceRange { fn source_range(&self) -> SourceRange {

View File

@ -6,6 +6,7 @@ use crate::{
data::{self, Data}, data::{self, Data},
errors::{error_colors, CheckError, SourceRange}, errors::{error_colors, CheckError, SourceRange},
info::{self, Local}, info::{self, Local},
parsing::Source,
program::{self}, program::{self},
}; };
@ -15,6 +16,7 @@ use super::{CompInfo, MersStatement};
pub struct IncludeMers { pub struct IncludeMers {
pub pos_in_src: SourceRange, pub pos_in_src: SourceRange,
pub include: Box<dyn MersStatement>, pub include: Box<dyn MersStatement>,
pub inner_src: Source,
} }
impl MersStatement for IncludeMers { impl MersStatement for IncludeMers {
fn has_scope(&self) -> bool { fn has_scope(&self) -> bool {
@ -25,15 +27,23 @@ impl MersStatement for IncludeMers {
info: &mut info::Info<super::Local>, info: &mut info::Info<super::Local>,
comp: CompInfo, comp: CompInfo,
) -> Result<Box<dyn program::run::MersStatement>, CheckError> { ) -> Result<Box<dyn program::run::MersStatement>, CheckError> {
let compiled: Arc<Box<dyn crate::program::run::MersStatement>> = match self.include.compile(info, comp) { let compiled: Arc<Box<dyn crate::program::run::MersStatement>> =
Ok(v) => Arc::new(v), match self.include.compile(info, comp) {
Err(e) => { Ok(v) => Arc::new(v),
return Err(CheckError::new() Err(e) => {
.src(vec![(self.pos_in_src, Some(error_colors::HashIncludeErrorInIncludedFile))]) return Err(CheckError::new()
.msg("Error in inner mers statement! (note: inner errors may refer to a different file)".color(error_colors::HashIncludeErrorInIncludedFile).to_string()) .src(vec![(
.err(e)) 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); let compiled2 = Arc::clone(&compiled);
Ok(Box::new(program::run::chain::Chain { Ok(Box::new(program::run::chain::Chain {
pos_in_src: self.pos_in_src, 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())), run: Arc::new(move |_, i| compiled2.run(&mut i.duplicate())),
}, },
}), }),
as_part_of_include: Some(self.inner_src.clone()),
})) }))
} }
fn source_range(&self) -> SourceRange { fn source_range(&self) -> SourceRange {

View File

@ -31,8 +31,8 @@ impl MersStatement for AssignTo {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
(self.pos_in_src, None), (self.pos_in_src, None),
(self.target.source_range(), Some(error_colors::InitFrom)), (self.target.source_range(), Some(error_colors::InitTo)),
(self.source.source_range(), Some(error_colors::InitTo)), (self.source.source_range(), Some(error_colors::InitFrom)),
]) ])
.msg(format!("Cannot initialize:")) .msg(format!("Cannot initialize:"))
.err(e)) .err(e))

View File

@ -5,6 +5,7 @@ use colored::Colorize;
use crate::{ use crate::{
data::{Data, Type}, data::{Data, Type},
errors::{error_colors, CheckError, SourceRange}, errors::{error_colors, CheckError, SourceRange},
parsing::Source,
}; };
use super::MersStatement; use super::MersStatement;
@ -14,6 +15,7 @@ pub struct Chain {
pub pos_in_src: SourceRange, pub pos_in_src: SourceRange,
pub first: Box<dyn MersStatement>, pub first: Box<dyn MersStatement>,
pub chained: Box<dyn MersStatement>, pub chained: Box<dyn MersStatement>,
pub as_part_of_include: Option<Source>,
} }
impl MersStatement for Chain { impl MersStatement for Chain {
fn check_custom( fn check_custom(
@ -35,21 +37,35 @@ impl MersStatement for Chain {
match (func.0)(&arg) { match (func.0)(&arg) {
Ok(t) => o.add(Arc::new(t)), Ok(t) => o.add(Arc::new(t)),
Err(e) => { Err(e) => {
return Err(CheckError::new() return Err(if let Some(inner_src) = &self.as_part_of_include {
.src(vec![ CheckError::new()
(self.pos_in_src, None), .src(vec![(
( self.pos_in_src,
self.first.source_range(), Some(error_colors::HashIncludeErrorInIncludedFile),
Some(error_colors::FunctionArgument), )])
), .msg(
(self.chained.source_range(), Some(error_colors::Function)), "Error in #include:"
]) .color(error_colors::HashIncludeErrorInIncludedFile)
.msg(format!( .to_string(),
"Can't call {} with an argument of type {}:", )
"this function".color(error_colors::Function), .err_with_src(e, inner_src.clone())
arg.to_string().color(error_colors::FunctionArgument) } else {
)) CheckError::new()
.err(e)) .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 { } else {

View File

@ -45,7 +45,20 @@ impl MersStatement for Tuple {
).into()); ).into());
} }
} else { } 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) Some(vec)