better errors, colors, and fix bug where x := x would panic

This commit is contained in:
Mark 2023-11-16 13:09:17 +01:00
parent 39951a59e9
commit c39e784939
11 changed files with 159 additions and 68 deletions

View File

@ -12,7 +12,7 @@ pub mod prelude_compile {
pub use crate::parsing::parse; pub use crate::parsing::parse;
pub use crate::parsing::Source; pub use crate::parsing::Source;
pub use crate::program::configs::Config; pub use crate::program::configs::Config;
pub use crate::program::parsed::MersStatement as ParsedMersStatement; pub use crate::program::parsed::{CompInfo, MersStatement as ParsedMersStatement};
pub use crate::program::run::MersStatement as RunMersStatement; pub use crate::program::run::MersStatement as RunMersStatement;
} }

View File

@ -1,11 +1,13 @@
use std::fs; use std::fs;
use colored::{Color, Colorize};
use super::{Source, SourcePos}; use super::{Source, SourcePos};
use crate::{ use crate::{
data::Data, data::Data,
program::{self, parsed::MersStatement, run::CheckError}, program::{
self,
parsed::MersStatement,
run::{error_colors, CheckError},
},
}; };
pub fn parse( pub fn parse(
@ -24,11 +26,8 @@ pub fn parse(
src.next_word(); src.next_word();
let source = parse(src)?.ok_or_else(|| { let source = parse(src)?.ok_or_else(|| {
CheckError::new() CheckError::new()
.src(vec![( .src(vec![((pos_in_src, src.get_pos()).into(), None)])
(pos_in_src, src.get_pos()).into(), .msg(format!("EOF after `:=`"))
Some(Color::BrightBlack),
)])
.msg(format!("EOF after :="))
})?; })?;
first = Box::new(program::parsed::init_to::InitTo { first = Box::new(program::parsed::init_to::InitTo {
pos_in_src: (pos_in_src, src.get_pos()).into(), pos_in_src: (pos_in_src, src.get_pos()).into(),
@ -41,11 +40,8 @@ pub fn parse(
src.next_word(); src.next_word();
let source = parse(src)?.ok_or_else(|| { let source = parse(src)?.ok_or_else(|| {
CheckError::new() CheckError::new()
.src(vec![( .src(vec![((pos_in_src, src.get_pos()).into(), None)])
(pos_in_src, src.get_pos()).into(), .msg(format!("EOF after `=`"))
Some(Color::BrightBlack),
)])
.msg(format!("EOF after ="))
})?; })?;
first = Box::new(program::parsed::assign_to::AssignTo { first = Box::new(program::parsed::assign_to::AssignTo {
pos_in_src: (pos_in_src, src.get_pos()).into(), pos_in_src: (pos_in_src, src.get_pos()).into(),
@ -56,7 +52,15 @@ pub fn parse(
"->" => { "->" => {
let pos_in_src = src.get_pos(); let pos_in_src = src.get_pos();
src.next_word(); src.next_word();
let run = parse(src)?.expect("err: bad eof, fn needs some statement"); let run = match parse(src) {
Ok(Some(v)) => v,
Ok(None) => {
return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos()).into(), None)])
.msg(format!("EOF after `->`")))
}
Err(e) => return Err(e),
};
first = Box::new(program::parsed::function::Function { first = Box::new(program::parsed::function::Function {
pos_in_src: (pos_in_src, src.get_pos()).into(), pos_in_src: (pos_in_src, src.get_pos()).into(),
arg: first, arg: first,
@ -68,7 +72,15 @@ pub fn parse(
src.skip_whitespace(); src.skip_whitespace();
if let Some('.') = src.peek_char() { if let Some('.') = src.peek_char() {
src.next_char(); src.next_char();
let chained = parse_no_chain(src)?.expect("err: EOF instead of chain"); 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)])
.msg(format!("EOF after `.`")))
}
Err(e) => return Err(e),
};
// allow a.f(b, c) syntax (but not f(a, b, c)) // allow a.f(b, c) syntax (but not f(a, b, c))
if let Some('(') = src.peek_char() { if let Some('(') = src.peek_char() {
src.next_char(); src.next_char();
@ -124,13 +136,16 @@ pub fn parse_no_chain(
src.next_char(); src.next_char();
if src.peek_char().is_none() { if src.peek_char().is_none() {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos()).into(), Some(Color::Red))]) .src(vec![((pos_in_src, src.get_pos()).into(), None)])
.msg(format!("EOF after #"))); .msg(format!("EOF after #")));
} }
if src.peek_char().is_some_and(|ch| ch.is_whitespace()) { if src.peek_char().is_some_and(|ch| ch.is_whitespace()) {
src.skip_whitespace(); src.skip_whitespace();
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos()).into(), Some(Color::Red))]) .src(vec![(
(pos_in_src, src.get_pos()).into(),
Some(error_colors::WhitespaceAfterHashtag),
)])
.msg(format!("Whitespace after #"))); .msg(format!("Whitespace after #")));
} }
match src.next_word() { match src.next_word() {
@ -153,7 +168,10 @@ pub fn parse_no_chain(
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
((pos_in_src, end_in_src).into(), None), ((pos_in_src, end_in_src).into(), None),
((string_in_src, src.get_pos()).into(), Some(Color::Red)), (
(string_in_src, src.get_pos()).into(),
Some(error_colors::HashIncludeCantLoadFile),
),
]) ])
.msg(format!("Can't load file '{s}': {e}"))); .msg(format!("Can't load file '{s}': {e}")));
} }
@ -161,8 +179,8 @@ pub fn parse_no_chain(
} else { } else {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
((pos_in_src, end_in_src).into(), Some(Color::BrightBlack)), ((pos_in_src, end_in_src).into(), None),
((string_in_src, src.get_pos()).into(), Some(Color::Red)), ((string_in_src, src.get_pos()).into(), Some(error_colors::HashIncludeNotAString)),
]) ])
.msg(format!( .msg(format!(
"#include must be followed by a string literal like \"file.mers\" (\" expected)." "#include must be followed by a string literal like \"file.mers\" (\" expected)."
@ -172,7 +190,10 @@ pub fn parse_no_chain(
other => { other => {
let msg = format!("Unknown #statement: {other}"); let msg = format!("Unknown #statement: {other}");
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos()).into(), Some(Color::Red))]) .src(vec![(
(pos_in_src, src.get_pos()).into(),
Some(error_colors::HashUnknown),
)])
.msg(msg)); .msg(msg));
} }
} }
@ -211,14 +232,39 @@ pub fn parse_no_chain(
Ok(Some(match src.next_word() { Ok(Some(match src.next_word() {
"if" => { "if" => {
src.section_begin("if".to_string()); src.section_begin("if".to_string());
let condition = parse(src)?.expect("err: EOF instead of condition"); src.skip_whitespace();
let on_true = parse(src)?.expect("err: EOF instead of on_true"); let condition = match parse(src) {
Ok(Some(v)) => v,
Ok(None) => {
return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos()).into(), None)])
.msg(format!("EOF in `if`")))
}
Err(e) => return Err(e),
};
let on_true = match parse(src) {
Ok(Some(v)) => v,
Ok(None) => {
return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos()).into(), None)])
.msg(format!("EOF after `if <condition>`")))
}
Err(e) => return Err(e),
};
let on_false = { let on_false = {
src.skip_whitespace(); src.skip_whitespace();
if src.peek_word() == "else" { if src.peek_word() == "else" {
src.section_begin("else".to_string()); src.section_begin("else".to_string());
src.next_word(); src.next_word();
Some(parse(src)?.expect("err: EOF instead of on_false after else")) Some(match parse(src) {
Ok(Some(v)) => v,
Ok(None) => {
return Err(CheckError::new()
.src(vec![((pos_in_src, src.get_pos()).into(), None)])
.msg(format!("EOF after `else`")))
}
Err(e) => return Err(e),
})
} else { } else {
None None
} }
@ -300,7 +346,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String,
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(backslash_in_src, src.get_pos()).into(), (backslash_in_src, src.get_pos()).into(),
Some(Color::Red), Some(error_colors::BackslashEscapeUnknown),
)]) )])
.msg(format!("unknown backslash escape '\\{o}'"))); .msg(format!("unknown backslash escape '\\{o}'")));
} }
@ -308,7 +354,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String,
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(backslash_in_src, src.get_pos()).into(), (backslash_in_src, src.get_pos()).into(),
Some(Color::Red), Some(error_colors::BackslashEscapeEOF),
)]) )])
.msg(format!("EOF in backslash escape"))); .msg(format!("EOF in backslash escape")));
} }
@ -322,7 +368,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String,
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(double_quote, src.get_pos()).into(), (double_quote, src.get_pos()).into(),
Some(Color::BrightBlack), Some(error_colors::StringEOF),
)]) )])
.msg(format!("EOF in string literal"))); .msg(format!("EOF in string literal")));
} }

View File

@ -8,8 +8,8 @@ use super::{CompInfo, MersStatement};
#[derive(Debug)] #[derive(Debug)]
pub struct AssignTo { pub struct AssignTo {
pub pos_in_src: SourceRange, pub pos_in_src: SourceRange,
pub target: Box<dyn MersStatement>,
pub source: Box<dyn MersStatement>, pub source: Box<dyn MersStatement>,
pub target: Box<dyn MersStatement>,
} }
impl MersStatement for AssignTo { impl MersStatement for AssignTo {
@ -24,8 +24,8 @@ impl MersStatement for AssignTo {
Ok(Box::new(program::run::assign_to::AssignTo { Ok(Box::new(program::run::assign_to::AssignTo {
pos_in_src: self.pos_in_src, pos_in_src: self.pos_in_src,
is_init: false, is_init: false,
target: self.target.compile(info, comp)?,
source: self.source.compile(info, comp)?, source: self.source.compile(info, comp)?,
target: self.target.compile(info, comp)?,
})) }))
} }
fn source_range(&self) -> SourceRange { fn source_range(&self) -> SourceRange {

View File

@ -7,7 +7,7 @@ use crate::{
info::{self, Local}, info::{self, Local},
program::{ program::{
self, self,
run::{CheckError, SourceRange}, run::{error_colors, CheckError, SourceRange},
}, },
}; };
@ -31,8 +31,8 @@ impl MersStatement for IncludeMers {
Ok(v) => Arc::new(v), Ok(v) => Arc::new(v),
Err(e) => { Err(e) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![(self.pos_in_src, Some(colored::Color::Red))]) .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)".red().to_string()) .msg("Error in inner mers statement! (note: inner errors may refer to a different file)".color(error_colors::HashIncludeErrorInIncludedFile).to_string())
.err(e)) .err(e))
} }
}; };

View File

@ -6,8 +6,8 @@ use super::{CompInfo, MersStatement};
#[derive(Debug)] #[derive(Debug)]
pub struct InitTo { pub struct InitTo {
pub pos_in_src: SourceRange, pub pos_in_src: SourceRange,
pub target: Box<dyn MersStatement>,
pub source: Box<dyn MersStatement>, pub source: Box<dyn MersStatement>,
pub target: Box<dyn MersStatement>,
} }
impl MersStatement for InitTo { impl MersStatement for InitTo {
@ -19,15 +19,17 @@ impl MersStatement for InitTo {
info: &mut crate::info::Info<super::Local>, info: &mut crate::info::Info<super::Local>,
mut comp: CompInfo, mut comp: CompInfo,
) -> Result<Box<dyn crate::program::run::MersStatement>, CheckError> { ) -> Result<Box<dyn crate::program::run::MersStatement>, CheckError> {
// source must be compiled BEFORE target!
comp.is_init = false;
let source = self.source.compile(info, comp)?;
comp.is_init = true; comp.is_init = true;
let target = self.target.compile(info, comp)?; let target = self.target.compile(info, comp)?;
comp.is_init = false; comp.is_init = false;
let source = self.source.compile(info, comp)?;
Ok(Box::new(program::run::assign_to::AssignTo { Ok(Box::new(program::run::assign_to::AssignTo {
pos_in_src: self.pos_in_src, pos_in_src: self.pos_in_src,
is_init: true, is_init: true,
target,
source, source,
target,
})) }))
} }
fn source_range(&self) -> SourceRange { fn source_range(&self) -> SourceRange {

View File

@ -2,7 +2,7 @@ use crate::{
info::Local, info::Local,
program::{ program::{
self, self,
run::{CheckError, SourceRange}, run::{error_colors, CheckError, SourceRange},
}, },
}; };
@ -41,7 +41,7 @@ impl MersStatement for Variable {
*v *v
} else { } else {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![(self.pos_in_src, Some(colored::Color::Red))]) .src(vec![(self.pos_in_src, Some(error_colors::UnknownVariable))])
.msg(format!("No variable named '{}' found!", self.var))); .msg(format!("No variable named '{}' found!", self.var)));
}, },
})) }))

View File

@ -2,7 +2,7 @@ use colored::Colorize;
use crate::data::{self, Data, MersType, Type}; use crate::data::{self, Data, MersType, Type};
use super::{CheckError, CheckInfo, MersStatement, SourceRange}; use super::{error_colors, CheckError, CheckInfo, MersStatement, SourceRange};
#[derive(Debug)] #[derive(Debug)]
pub struct AssignTo { pub struct AssignTo {
@ -28,11 +28,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(), (self.source.source_range(), Some(error_colors::InitTo)),
Some(colored::Color::BrightYellow),
),
(self.source.source_range(), Some(colored::Color::BrightCyan)),
]) ])
.msg(format!("Cannot initialize:")) .msg(format!("Cannot initialize:"))
.err(e)) .err(e))
@ -44,16 +41,13 @@ 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::AssignTo)),
self.target.source_range(), (self.source.source_range(), Some(error_colors::AssignFrom)),
Some(colored::Color::BrightYellow),
),
(self.source.source_range(), Some(colored::Color::BrightCyan)),
]) ])
.msg(format!( .msg(format!(
"can't assign {} to {} because it isn't included in {}", "can't assign {} to {} because it isn't included in {}",
source.to_string().bright_cyan(), source.to_string().color(error_colors::AssignFrom),
target.to_string().bright_yellow(), target.to_string().color(error_colors::AssignTo),
t t
))); )));
} }
@ -61,9 +55,15 @@ 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(colored::Color::Red)), (
self.target.source_range(),
Some(error_colors::AssignTargetNonReference),
),
]) ])
.msg(format!("can't assign to non-reference!"))); .msg(format!(
"can't assign to {}",
"non-reference!".color(error_colors::AssignTargetNonReference)
)));
} }
} }
Ok(Type::empty_tuple()) Ok(Type::empty_tuple())

View File

@ -4,7 +4,7 @@ use colored::Colorize;
use crate::data::{Data, Type}; use crate::data::{Data, Type};
use super::{CheckError, MersStatement, SourceRange}; use super::{error_colors, CheckError, MersStatement, SourceRange};
#[derive(Debug)] #[derive(Debug)]
pub struct Chain { pub struct Chain {
@ -35,16 +35,16 @@ impl MersStatement for Chain {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
(self.pos_in_src, None), (self.pos_in_src, None),
(self.first.source_range(), Some(colored::Color::BrightCyan)),
( (
self.chained.source_range(), self.first.source_range(),
Some(colored::Color::BrightMagenta), Some(error_colors::FunctionArgument),
), ),
(self.chained.source_range(), Some(error_colors::Function)),
]) ])
.msg(format!( .msg(format!(
"Can't call {} with an argument of type {}:", "Can't call {} with an argument of type {}:",
"this function".bright_magenta(), "this function".color(error_colors::Function),
arg.to_string().bright_cyan() arg.to_string().color(error_colors::FunctionArgument)
)) ))
.err(e)) .err(e))
} }
@ -53,11 +53,14 @@ impl MersStatement for Chain {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![ .src(vec![
(self.pos_in_src, None), (self.pos_in_src, None),
(self.chained.source_range(), Some(colored::Color::BrightRed)), (
self.chained.source_range(),
Some(error_colors::ChainWithNonFunction),
),
]) ])
.msg(format!( .msg(format!(
"cannot chain with a non-function ({})", "cannot chain with a non-function ({})",
func.to_string().bright_red() func.to_string().color(error_colors::ChainWithNonFunction)
))); )));
} }
} }

View File

@ -4,7 +4,7 @@ use colored::Colorize;
use crate::data::{self, Data, MersType, Type}; use crate::data::{self, Data, MersType, Type};
use super::{CheckError, MersStatement, SourceRange}; use super::{error_colors, CheckError, MersStatement, SourceRange};
#[derive(Debug)] #[derive(Debug)]
pub struct If { pub struct If {
@ -30,13 +30,15 @@ impl MersStatement for If {
(self.pos_in_src, None), (self.pos_in_src, None),
( (
self.condition.source_range(), self.condition.source_range(),
Some(colored::Color::BrightRed), Some(error_colors::IfConditionNotBool),
), ),
]) ])
.msg(format!( .msg(format!(
"The {} in an if-statement must return bool, not {}", "The {} in an if-statement must return bool, not {}",
"condition".red(), "condition".color(error_colors::IfConditionNotBool),
cond_return_type.to_string().bright_red(), cond_return_type
.to_string()
.color(error_colors::IfConditionNotBool),
))); )));
} }
let mut t = self.on_true.check(info, None)?; let mut t = self.on_true.check(info, None)?;

View File

@ -84,6 +84,34 @@ impl SourceRange {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CheckError(Vec<CheckErrorComponent>); pub struct CheckError(Vec<CheckErrorComponent>);
#[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)] #[derive(Clone, Debug)]
enum CheckErrorComponent { enum CheckErrorComponent {
Message(String), Message(String),

View File

@ -4,7 +4,7 @@ use colored::Colorize;
use crate::data::{self, tuple::TupleT, Data, Type}; use crate::data::{self, tuple::TupleT, Data, Type};
use super::{MersStatement, SourceRange}; use super::{error_colors, MersStatement, SourceRange};
#[derive(Debug)] #[derive(Debug)]
pub struct Tuple { pub struct Tuple {
@ -21,6 +21,7 @@ impl MersStatement for Tuple {
let mut vec = (0..self.elems.len()) let mut vec = (0..self.elems.len())
.map(|_| Type::empty()) .map(|_| Type::empty())
.collect::<VecDeque<_>>(); .collect::<VecDeque<_>>();
let print_is_part_of = init_to.types.len() > 1;
for t in init_to.types.iter() { for t in init_to.types.iter() {
if let Some(t) = t.as_any().downcast_ref::<TupleT>() { if let Some(t) = t.as_any().downcast_ref::<TupleT>() {
if t.0.len() == self.elems.len() { if t.0.len() == self.elems.len() {
@ -29,10 +30,19 @@ impl MersStatement for Tuple {
} }
} else { } else {
return Err( return Err(
format!("can't init statement type Tuple with value type {t}, which is part of {init_to} - only tuples with the same length ({}) can be assigned to tuples", self.elems.len()).into()); 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),
if print_is_part_of {
format!(", which is part of {}", init_to.to_string().color(error_colors::InitFrom))
} else {
format!("")
},
self.elems.len()
).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".bright_yellow(), t.to_string().bright_cyan(), init_to.to_string().bright_cyan()).into()); 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());
} }
} }
Some(vec) Some(vec)