From c39e784939b24b3da4a2f6c0419ef6b490c8c086 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 16 Nov 2023 13:09:17 +0100 Subject: [PATCH] better errors, colors, and fix bug where x := x would panic --- mers_lib/src/lib.rs | 2 +- mers_lib/src/parsing/statements.rs | 100 ++++++++++++++------ mers_lib/src/program/parsed/assign_to.rs | 4 +- mers_lib/src/program/parsed/include_mers.rs | 6 +- mers_lib/src/program/parsed/init_to.rs | 8 +- mers_lib/src/program/parsed/variable.rs | 4 +- mers_lib/src/program/run/assign_to.rs | 30 +++--- mers_lib/src/program/run/chain.rs | 19 ++-- mers_lib/src/program/run/if.rs | 10 +- mers_lib/src/program/run/mod.rs | 28 ++++++ mers_lib/src/program/run/tuple.rs | 16 +++- 11 files changed, 159 insertions(+), 68 deletions(-) diff --git a/mers_lib/src/lib.rs b/mers_lib/src/lib.rs index 0a09e88..66eb48f 100755 --- a/mers_lib/src/lib.rs +++ b/mers_lib/src/lib.rs @@ -12,7 +12,7 @@ pub mod prelude_compile { pub use crate::parsing::parse; pub use crate::parsing::Source; 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; } diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 7b23943..1cb6072 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -1,11 +1,13 @@ use std::fs; -use colored::{Color, Colorize}; - use super::{Source, SourcePos}; use crate::{ data::Data, - program::{self, parsed::MersStatement, run::CheckError}, + program::{ + self, + parsed::MersStatement, + run::{error_colors, CheckError}, + }, }; pub fn parse( @@ -24,11 +26,8 @@ pub fn parse( src.next_word(); let source = parse(src)?.ok_or_else(|| { CheckError::new() - .src(vec![( - (pos_in_src, src.get_pos()).into(), - Some(Color::BrightBlack), - )]) - .msg(format!("EOF after :=")) + .src(vec![((pos_in_src, src.get_pos()).into(), None)]) + .msg(format!("EOF after `:=`")) })?; first = Box::new(program::parsed::init_to::InitTo { pos_in_src: (pos_in_src, src.get_pos()).into(), @@ -41,11 +40,8 @@ pub fn parse( src.next_word(); let source = parse(src)?.ok_or_else(|| { CheckError::new() - .src(vec![( - (pos_in_src, src.get_pos()).into(), - Some(Color::BrightBlack), - )]) - .msg(format!("EOF after =")) + .src(vec![((pos_in_src, src.get_pos()).into(), None)]) + .msg(format!("EOF after `=`")) })?; first = Box::new(program::parsed::assign_to::AssignTo { pos_in_src: (pos_in_src, src.get_pos()).into(), @@ -56,7 +52,15 @@ pub fn parse( "->" => { let pos_in_src = src.get_pos(); 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 { pos_in_src: (pos_in_src, src.get_pos()).into(), arg: first, @@ -68,7 +72,15 @@ pub fn parse( src.skip_whitespace(); if let Some('.') = src.peek_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)) if let Some('(') = src.peek_char() { src.next_char(); @@ -124,13 +136,16 @@ pub fn parse_no_chain( src.next_char(); if src.peek_char().is_none() { 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 #"))); } 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()).into(), Some(Color::Red))]) + .src(vec![( + (pos_in_src, src.get_pos()).into(), + Some(error_colors::WhitespaceAfterHashtag), + )]) .msg(format!("Whitespace after #"))); } match src.next_word() { @@ -153,7 +168,10 @@ pub fn parse_no_chain( return Err(CheckError::new() .src(vec![ ((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}"))); } @@ -161,8 +179,8 @@ pub fn parse_no_chain( } else { return Err(CheckError::new() .src(vec![ - ((pos_in_src, end_in_src).into(), Some(Color::BrightBlack)), - ((string_in_src, src.get_pos()).into(), Some(Color::Red)), + ((pos_in_src, end_in_src).into(), None), + ((string_in_src, src.get_pos()).into(), Some(error_colors::HashIncludeNotAString)), ]) .msg(format!( "#include must be followed by a string literal like \"file.mers\" (\" expected)." @@ -172,7 +190,10 @@ pub fn parse_no_chain( other => { let msg = format!("Unknown #statement: {other}"); 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)); } } @@ -211,14 +232,39 @@ pub fn parse_no_chain( Ok(Some(match src.next_word() { "if" => { src.section_begin("if".to_string()); - let condition = parse(src)?.expect("err: EOF instead of condition"); - let on_true = parse(src)?.expect("err: EOF instead of on_true"); + src.skip_whitespace(); + 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 `"))) + } + Err(e) => return Err(e), + }; let on_false = { src.skip_whitespace(); if src.peek_word() == "else" { src.section_begin("else".to_string()); 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 { None } @@ -300,7 +346,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result Result Result, pub source: Box, + pub target: Box, } impl MersStatement for AssignTo { @@ -24,8 +24,8 @@ impl MersStatement for AssignTo { Ok(Box::new(program::run::assign_to::AssignTo { pos_in_src: self.pos_in_src, is_init: false, - target: self.target.compile(info, comp)?, source: self.source.compile(info, comp)?, + target: self.target.compile(info, comp)?, })) } 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 52e8c61..246b1cc 100644 --- a/mers_lib/src/program/parsed/include_mers.rs +++ b/mers_lib/src/program/parsed/include_mers.rs @@ -7,7 +7,7 @@ use crate::{ info::{self, Local}, program::{ self, - run::{CheckError, SourceRange}, + run::{error_colors, CheckError, SourceRange}, }, }; @@ -31,8 +31,8 @@ impl MersStatement for IncludeMers { Ok(v) => Arc::new(v), Err(e) => { return Err(CheckError::new() - .src(vec![(self.pos_in_src, Some(colored::Color::Red))]) - .msg("Error in inner mers statement! (note: inner errors may refer to a different file)".red().to_string()) + .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)) } }; diff --git a/mers_lib/src/program/parsed/init_to.rs b/mers_lib/src/program/parsed/init_to.rs index 890c969..ce893dd 100755 --- a/mers_lib/src/program/parsed/init_to.rs +++ b/mers_lib/src/program/parsed/init_to.rs @@ -6,8 +6,8 @@ use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct InitTo { pub pos_in_src: SourceRange, - pub target: Box, pub source: Box, + pub target: Box, } impl MersStatement for InitTo { @@ -19,15 +19,17 @@ impl MersStatement for InitTo { info: &mut crate::info::Info, mut comp: CompInfo, ) -> Result, CheckError> { + // source must be compiled BEFORE target! + comp.is_init = false; + let source = self.source.compile(info, comp)?; comp.is_init = true; let target = self.target.compile(info, comp)?; comp.is_init = false; - let source = self.source.compile(info, comp)?; Ok(Box::new(program::run::assign_to::AssignTo { pos_in_src: self.pos_in_src, is_init: true, - target, source, + target, })) } fn source_range(&self) -> SourceRange { diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index 1ceea16..590fc63 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -2,7 +2,7 @@ use crate::{ info::Local, program::{ self, - run::{CheckError, SourceRange}, + run::{error_colors, CheckError, SourceRange}, }, }; @@ -41,7 +41,7 @@ impl MersStatement for Variable { *v } else { 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))); }, })) diff --git a/mers_lib/src/program/run/assign_to.rs b/mers_lib/src/program/run/assign_to.rs index 1edda59..0db87a8 100755 --- a/mers_lib/src/program/run/assign_to.rs +++ b/mers_lib/src/program/run/assign_to.rs @@ -2,7 +2,7 @@ use colored::Colorize; use crate::data::{self, Data, MersType, Type}; -use super::{CheckError, CheckInfo, MersStatement, SourceRange}; +use super::{error_colors, CheckError, CheckInfo, MersStatement, SourceRange}; #[derive(Debug)] pub struct AssignTo { @@ -28,11 +28,8 @@ impl MersStatement for AssignTo { return Err(CheckError::new() .src(vec![ (self.pos_in_src, None), - ( - self.target.source_range(), - Some(colored::Color::BrightYellow), - ), - (self.source.source_range(), Some(colored::Color::BrightCyan)), + (self.target.source_range(), Some(error_colors::InitFrom)), + (self.source.source_range(), Some(error_colors::InitTo)), ]) .msg(format!("Cannot initialize:")) .err(e)) @@ -44,16 +41,13 @@ impl MersStatement for AssignTo { return Err(CheckError::new() .src(vec![ (self.pos_in_src, None), - ( - self.target.source_range(), - Some(colored::Color::BrightYellow), - ), - (self.source.source_range(), Some(colored::Color::BrightCyan)), + (self.target.source_range(), Some(error_colors::AssignTo)), + (self.source.source_range(), Some(error_colors::AssignFrom)), ]) .msg(format!( "can't assign {} to {} because it isn't included in {}", - source.to_string().bright_cyan(), - target.to_string().bright_yellow(), + source.to_string().color(error_colors::AssignFrom), + target.to_string().color(error_colors::AssignTo), t ))); } @@ -61,9 +55,15 @@ impl MersStatement for AssignTo { return Err(CheckError::new() .src(vec![ (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()) diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index 3668459..8a20cf9 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -4,7 +4,7 @@ use colored::Colorize; use crate::data::{Data, Type}; -use super::{CheckError, MersStatement, SourceRange}; +use super::{error_colors, CheckError, MersStatement, SourceRange}; #[derive(Debug)] pub struct Chain { @@ -35,16 +35,16 @@ impl MersStatement for Chain { return Err(CheckError::new() .src(vec![ (self.pos_in_src, None), - (self.first.source_range(), Some(colored::Color::BrightCyan)), ( - self.chained.source_range(), - Some(colored::Color::BrightMagenta), + 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".bright_magenta(), - arg.to_string().bright_cyan() + "this function".color(error_colors::Function), + arg.to_string().color(error_colors::FunctionArgument) )) .err(e)) } @@ -53,11 +53,14 @@ impl MersStatement for Chain { return Err(CheckError::new() .src(vec![ (self.pos_in_src, None), - (self.chained.source_range(), Some(colored::Color::BrightRed)), + ( + self.chained.source_range(), + Some(error_colors::ChainWithNonFunction), + ), ]) .msg(format!( "cannot chain with a non-function ({})", - func.to_string().bright_red() + func.to_string().color(error_colors::ChainWithNonFunction) ))); } } diff --git a/mers_lib/src/program/run/if.rs b/mers_lib/src/program/run/if.rs index a346403..0220d77 100755 --- a/mers_lib/src/program/run/if.rs +++ b/mers_lib/src/program/run/if.rs @@ -4,7 +4,7 @@ use colored::Colorize; use crate::data::{self, Data, MersType, Type}; -use super::{CheckError, MersStatement, SourceRange}; +use super::{error_colors, CheckError, MersStatement, SourceRange}; #[derive(Debug)] pub struct If { @@ -30,13 +30,15 @@ impl MersStatement for If { (self.pos_in_src, None), ( self.condition.source_range(), - Some(colored::Color::BrightRed), + Some(error_colors::IfConditionNotBool), ), ]) .msg(format!( "The {} in an if-statement must return bool, not {}", - "condition".red(), - cond_return_type.to_string().bright_red(), + "condition".color(error_colors::IfConditionNotBool), + cond_return_type + .to_string() + .color(error_colors::IfConditionNotBool), ))); } let mut t = self.on_true.check(info, None)?; diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 4994395..93e87d1 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -84,6 +84,34 @@ impl SourceRange { } #[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), diff --git a/mers_lib/src/program/run/tuple.rs b/mers_lib/src/program/run/tuple.rs index d32c68c..b12a68c 100755 --- a/mers_lib/src/program/run/tuple.rs +++ b/mers_lib/src/program/run/tuple.rs @@ -4,7 +4,7 @@ use colored::Colorize; use crate::data::{self, tuple::TupleT, Data, Type}; -use super::{MersStatement, SourceRange}; +use super::{error_colors, MersStatement, SourceRange}; #[derive(Debug)] pub struct Tuple { @@ -21,6 +21,7 @@ impl MersStatement for Tuple { let mut vec = (0..self.elems.len()) .map(|_| Type::empty()) .collect::>(); + let print_is_part_of = init_to.types.len() > 1; for t in init_to.types.iter() { if let Some(t) = t.as_any().downcast_ref::() { if t.0.len() == self.elems.len() { @@ -29,10 +30,19 @@ impl MersStatement for Tuple { } } else { 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 { - 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)