diff --git a/mers/Cargo.lock b/mers/Cargo.lock index 3e3741b..f1c480b 100755 --- a/mers/Cargo.lock +++ b/mers/Cargo.lock @@ -110,6 +110,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "colored" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +dependencies = [ + "is-terminal", + "lazy_static", + "windows-sys", +] + [[package]] name = "errno" version = "0.3.1" @@ -154,12 +165,24 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "line-span" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fc123b2f6600099ca18248f69e3ee02b09c4188c0d98e9a690d90dec3e408a" + [[package]] name = "linux-raw-sys" version = "0.4.3" @@ -177,6 +200,10 @@ dependencies = [ [[package]] name = "mers_lib" version = "0.3.0" +dependencies = [ + "colored", + "line-span", +] [[package]] name = "once_cell" diff --git a/mers/src/main.rs b/mers/src/main.rs index ef4c46f..b2a7f43 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -88,7 +88,7 @@ fn main() { let return_type = match run.check(&mut info_check, None) { Ok(v) => v, Err(e) => { - eprintln!("check failed: {e}"); + eprint!("{}", e.display(&source)); std::process::exit(36); } }; diff --git a/mers_lib/Cargo.toml b/mers_lib/Cargo.toml index 94e18f4..40d256b 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -8,3 +8,7 @@ edition = "2021" default = ["parse"] parse = ["run"] run = [] + +[dependencies] +colored = "2.0.4" +line-span = "0.1.5" diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index 703247e..2e126b1 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -8,15 +8,17 @@ pub mod types; pub fn parse(src: &mut Source) -> Result, ()> { let pos_in_src = src.get_pos(); + let statements = statements::parse_multiple(src, "")?; let block = Block { - pos_in_src, - statements: statements::parse_multiple(src, "")?, + pos_in_src: (pos_in_src, src.get_pos()).into(), + statements, }; Ok(Box::new(block)) } pub struct Source { src_raw_len: usize, + src_og: String, src: String, /// (start, content) of each comment, including start/end (//, \n, /* and */) comments: Vec<(usize, String)>, @@ -83,6 +85,7 @@ impl Source { } Self { src_raw_len: source.len(), + src_og: source, src, comments, i: 0, @@ -90,6 +93,13 @@ impl Source { } } + pub fn src(&self) -> &String { + &self.src + } + pub fn comments(&self) -> &Vec<(usize, String)> { + &self.comments + } + pub fn skip_whitespace(&mut self) { if let Some(i) = self.src[self.i..].char_indices().find_map(|(i, ch)| { if !ch.is_whitespace() { @@ -172,6 +182,17 @@ impl Source { &self.sections } + pub fn get_line_start(&self, pos: usize) -> usize { + self.src[0..pos].rfind("\n").map(|i| i + 1).unwrap_or(0) + } + pub fn get_line_end(&self, pos: usize) -> usize { + // TODO: If the newline is preceded by `\r`s, remove those too since they are part of the newline `\r\n` sequence + self.src[pos..] + .find("\n") + .map(|i| i + pos) + .unwrap_or(self.src.len()) + } + pub fn format(&self, insertions: &Vec<(usize, bool, String)>) -> String { let mut o = String::with_capacity(self.src_raw_len); let mut insertions = insertions.iter().peekable(); @@ -244,6 +265,9 @@ 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 4c98868..643cff7 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -16,28 +16,31 @@ pub fn parse(src: &mut Source) -> Result" => { let pos_in_src = src.get_pos(); src.next_word(); + let run = parse(src)?.expect("err: bad eof, fn needs some statement"); first = Box::new(program::parsed::function::Function { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), arg: first, - run: parse(src)?.expect("err: bad eof, fn needs some statement"), + run, }); } _ => loop { @@ -47,7 +50,7 @@ pub fn parse(src: &mut Source) -> Result { let pos_in_src = src.get_pos(); src.next_char(); + let statements = parse_multiple(src, "}")?; return Ok(Some(Box::new(program::parsed::block::Block { - pos_in_src, - statements: parse_multiple(src, "}")?, + pos_in_src: (pos_in_src, src.get_pos()).into(), + statements, }))); } Some('(') => { let pos_in_src = src.get_pos(); src.next_char(); + let elems = parse_multiple(src, ")")?; return Ok(Some(Box::new(program::parsed::tuple::Tuple { - pos_in_src, - elems: parse_multiple(src, ")")?, + pos_in_src: (pos_in_src, src.get_pos()).into(), + elems, }))); } Some('"') => { @@ -127,7 +132,7 @@ pub fn parse_no_chain( } } return Ok(Some(Box::new(program::parsed::value::Value { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), data: Data::new(crate::data::string::String(s)), }))); } @@ -137,28 +142,31 @@ 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"); + 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")) + } else { + None + } + }; Box::new(program::parsed::r#if::If { - pos_in_src, - condition: parse(src)?.expect("err: EOF instead of condition"), - on_true: parse(src)?.expect("err: EOF instead of on_true"), - 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")) - } else { - None - } - }, + pos_in_src: (pos_in_src, src.get_pos()).into(), + condition, + on_true, + on_false, }) } "true" => Box::new(program::parsed::value::Value { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), data: Data::new(crate::data::bool::Bool(true)), }), "false" => Box::new(program::parsed::value::Value { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), data: Data::new(crate::data::bool::Bool(false)), }), "" => return Ok(None), @@ -171,32 +179,32 @@ pub fn parse_no_chain( src.next_char(); if let Ok(num) = format!("{o}.{}", src.next_word()).parse() { Box::new(program::parsed::value::Value { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), data: Data::new(crate::data::float::Float(num)), }) } else { src.set_pos(here); Box::new(program::parsed::value::Value { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), data: Data::new(crate::data::int::Int(n)), }) } } else { Box::new(program::parsed::value::Value { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), data: Data::new(crate::data::int::Int(n)), }) } } else { if let Some('&') = o.chars().next() { Box::new(program::parsed::variable::Variable { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), is_ref: true, var: o[1..].to_string(), }) } else { Box::new(program::parsed::variable::Variable { - pos_in_src, + pos_in_src: (pos_in_src, src.get_pos()).into(), is_ref: false, var: o.to_string(), }) diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index 4f0deb3..c3ab507 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -24,7 +24,7 @@ impl Config { for t in a.types.iter() { if let Some(t) = t.as_any().downcast_ref::() { if t.0.len() != 2 { - return Err(CheckError(format!("cannot use try with tuple argument where len != 2 (got len {})", t.0.len()))); + return Err(format!("cannot use try with tuple argument where len != 2 (got len {})", t.0.len()).into()); } let arg_type = &t.0[0]; let functions = &t.0[1]; @@ -56,7 +56,7 @@ impl Config { }, }); } else { - return Err(CheckError(format!("try: arguments f1-fn must be functions"))); + return Err(format!("try: arguments f1-fn must be functions").into()); } } // found a function that won't fail for this arg_type! @@ -68,18 +68,26 @@ impl Config { } } if tuple_fallible || !tuple_possible { - return Err(CheckError(format!("try: if the argument is {arg_type}, there is no infallible function. add a fallback function to handle this case! Errors for all functions: {}", func_errors.iter().enumerate().map(|(i, v)| match v { - Some(e) => format!("\n{i}: {}", e.0), - None => "\n({i}: no error)".to_owned(), - }).collect::()))); + // if the argument is {arg_type}, there is no infallible function. add a fallback function to handle this case! + let mut e = CheckError::new() + .msg(format!("if the argument is {arg_type}, there is no infallible function.")) + .msg(format!("Add a fallback function to handle this case!")); + for (i, err) in func_errors.into_iter().enumerate() { + if let Some(err) = err { + e = e + .msg(format!("Error for function #{i}:")) + .err(err); + } + } + return Err(e); } } else { - return Err(CheckError(format!("try: argument must be (arg, (f1, f2, f3, ..., fn))"))); + return Err(format!("try: argument must be (arg, (f1, f2, f3, ..., fn))").into()); } } } } else { - return Err(CheckError(format!("cannot use try with non-tuple argument"))); + return Err(format!("cannot use try with non-tuple argument").into()); } } Ok(out) @@ -106,7 +114,7 @@ impl Config { out: Arc::new(|a, _i| if a.is_included_in(&data::int::IntT) { Ok(Type::empty()) } else { - Err(CheckError(format!("cannot call exit with non-int argument"))) + Err(format!("cannot call exit with non-int argument").into()) }), run: Arc::new(|a, _i| { std::process::exit(a.get().as_any().downcast_ref::().map(|i| i.0 as _).unwrap_or(1)); @@ -120,7 +128,7 @@ impl Config { out: Arc::new(|a, _i| { for t in &a.types { if t.as_any().downcast_ref::().is_none() && t.as_any().downcast_ref::().is_none() { - return Err(crate::program::run::CheckError(format!("cannot get length of {t} (must be a tuple or a string)"))); + return Err(format!("cannot get length of {t} (must be a tuple or a string)").into()); } } Ok(Type::new(data::int::IntT)) @@ -147,16 +155,16 @@ impl Config { for t in (t.0)(&Type::empty_tuple())?.types { if let Some(t) = t.as_any().downcast_ref::() { if t.0.len() > 1 { - return Err(crate::program::run::CheckError(format!("called loop with funcion that might return a tuple of length > 1"))); + return Err(format!("called loop with funcion that might return a tuple of length > 1").into()); } else if let Some(v) = t.0.first() { o.add(Arc::new(v.clone())) } } else { - return Err(crate::program::run::CheckError(format!("called loop with funcion that might return something other than a tuple"))); + return Err(format!("called loop with funcion that might return something other than a tuple").into()); } } } else { - return Err(crate::program::run::CheckError(format!("called loop on a non-function"))); + return Err(format!("called loop on a non-function").into()); } } Ok(o) @@ -182,7 +190,7 @@ impl Config { out: Arc::new(|a, _i| { for t in &a.types { if t.iterable().is_none() { - return Err(crate::program::run::CheckError(format!("called eq on non-iterable"))) + return Err(format!("called eq on non-iterable").into()) } } Ok(Type::new(data::bool::BoolT)) @@ -212,7 +220,8 @@ impl Config { Data::new(data::function::Function { info: Arc::new(Info::neverused()), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Arc::new(|a, _i| if let Some(v) = a.dereference() { Ok(v) } else { Err(crate::program::run::CheckError(format!("cannot dereference type {a}")))}), + out: Arc::new(|a, _i| if let Some(v) = a.dereference() { Ok(v) } else { Err(format!("cannot dereference type {a}").into()) + }), run: Arc::new(|a, _i| { if let Some(r) = a .get() diff --git a/mers_lib/src/program/configs/with_command_running.rs b/mers_lib/src/program/configs/with_command_running.rs index d5555e3..55c2b69 100755 --- a/mers_lib/src/program/configs/with_command_running.rs +++ b/mers_lib/src/program/configs/with_command_running.rs @@ -6,10 +6,7 @@ use std::{ use crate::{ data::{self, Data, MersData, MersType, Type}, - program::{ - self, - run::{CheckError, CheckInfo}, - }, + program::{self, run::CheckInfo}, }; use super::Config; @@ -37,7 +34,7 @@ impl Config { Arc::new(RunCommandErrorT) ])) } else { - return Err(CheckError(format!("run_command called with invalid arguments (must be (String, Iter))"))); + return Err(format!("run_command called with invalid arguments (must be (String, Iter))").into()); } }), run: Arc::new(|a, _i| { diff --git a/mers_lib/src/program/configs/with_get.rs b/mers_lib/src/program/configs/with_get.rs index fbc5007..11f4b68 100755 --- a/mers_lib/src/program/configs/with_get.rs +++ b/mers_lib/src/program/configs/with_get.rs @@ -19,9 +19,7 @@ impl Config { if let Some(v) = a.get() { Ok(v) } else { - Err(program::run::CheckError(format!( - "called get on non-gettable type {a}" - ))) + Err(format!("called get on non-gettable type {a}").into()) } }), run: Arc::new(|a, _i| { diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index 1c2bf65..a184d29 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -5,10 +5,7 @@ use std::{ use crate::{ data::{self, Data, MersData, MersType, Type}, - program::{ - self, - run::{CheckError, CheckInfo}, - }, + program::{self, run::CheckInfo}, }; use super::Config; @@ -42,24 +39,22 @@ impl Config { for f in f { let ret = f.0(&iter)?; if !ret.is_zero_tuple() { - return Err(CheckError(format!( - "for_each function must return (), not {ret}" - ))); + return Err(format!("for_each function must return (), not {ret}").into()); } } } else { - return Err(CheckError(format!( + return Err(format!( "for_each called on tuple not containing iterable and function: {v} is {}", if v.iterable().is_some() { "iterable" } else { "not iterable" }, - ))); + ).into()); } } else { - return Err(CheckError(format!( + return Err(format!( "for_each called on tuple with len < 2" - ))); + ).into()); } } else { - return Err(CheckError(format!("for_each called on non-tuple"))); + return Err(format!("for_each called on non-tuple").into()); } } Ok(Type::empty_tuple()) diff --git a/mers_lib/src/program/configs/with_list.rs b/mers_lib/src/program/configs/with_list.rs index 155efa4..530b49b 100755 --- a/mers_lib/src/program/configs/with_list.rs +++ b/mers_lib/src/program/configs/with_list.rs @@ -5,10 +5,7 @@ use std::{ use crate::{ data::{self, Data, MersData, MersType, Type}, - program::{ - self, - run::{CheckError, CheckInfo}, - }, + program::{self, run::CheckInfo}, }; use super::Config; @@ -36,14 +33,14 @@ impl Config { if let Some(t) = t.as_any().downcast_ref::() { out.add(Arc::new(t.0.clone())); } else { - return Err(CheckError(format!( + return Err(format!( "pop: found a reference to {t}, which is not a list" - ))); + ).into()); } } Ok(out) } else { - return Err(CheckError(format!("pop: not a reference: {a}"))); + return Err(format!("pop: not a reference: {a}").into()); } }), run: Arc::new(|a, _i| { @@ -77,9 +74,9 @@ impl Config { for t in a.types.iter() { if let Some(t) = t.as_any().downcast_ref::() { if t.0.len() != 2 { - return Err(CheckError(format!( + return Err(format!( "push: tuple must have length 2" - ))); + ).into()); } let a = &t.0[0]; let new = &t.0[1]; @@ -87,23 +84,24 @@ impl Config { for t in a.types.iter() { if let Some(t) = t.as_any().downcast_ref::() { if !new.is_included_in(&t.0) { - return Err(CheckError(format!( + return Err(format!( "push: found a reference to {t}, which is a list which can't contain elements of type {new}" - ))); + ).into()); } } else { - return Err(CheckError(format!( - "push: found a reference to {t}, which is not a list" - ))); + return Err(format!( + "push: found a reference to {t}, which is not a list" + ).into()); } } } else { - return Err(CheckError(format!( + return Err(format!( "push: first element in tuple not a reference: {a}" - ))); + ).into()); } } else { - return Err(CheckError(format!("push: not a tuple: {t}"))); + return Err(format!("push: not a tuple: {t}") + .into()); } } Ok(Type::empty_tuple()) @@ -138,9 +136,9 @@ impl Config { if let Some(v) = a.iterable() { Ok(Type::new(ListT(v))) } else { - Err(program::run::CheckError(format!( + Err(format!( "cannot iterate over type {a}" - ))) + ).into()) } }), run: Arc::new(|a, _i| { diff --git a/mers_lib/src/program/configs/with_math.rs b/mers_lib/src/program/configs/with_math.rs index fac020a..afed35b 100755 --- a/mers_lib/src/program/configs/with_math.rs +++ b/mers_lib/src/program/configs/with_math.rs @@ -14,6 +14,8 @@ impl Config { /// `sum: fn` returns the sum of all the numbers in the tuple /// `diff: fn` returns b - a /// `product: fn` returns the product of all the numbers in the tuple + /// `div: fn` returns a / b. Performs integer division if a and b are both integers. + /// `modulo: fn` returns a % b /// `signum: fn` returns 1 for positive numbers, -1 for negative ones and 0 for 0 (always returns an Int, even when input is Float) /// `parse_int: fn` parses a string to an int, returns () on failure /// `parse_float: fn` parses a string to an int, returns () on failure @@ -22,8 +24,6 @@ impl Config { /// `round: fn` rounds the float and returns an int /// `ceil: fn` rounds the float [?] and returns an int /// `floor: fn` rounds the float [?] and returns an int - /// `div: fn` returns a / b. Performs integer division if a and b are both integers. - /// `modulo: fn` returns a % b pub fn with_math(self) -> Self { self.add_var("parse_float".to_string(), Data::new(data::function::Function { info: Arc::new(program::run::Info::neverused()), @@ -35,7 +35,7 @@ impl Config { Arc::new(data::tuple::TupleT(vec![])), ])) } else { - Err(CheckError(format!("parse_float called on non-string type"))) + Err(format!("parse_float called on non-string type").into()) } }), run: Arc::new(|a, _i| { @@ -55,7 +55,7 @@ impl Config { Arc::new(data::tuple::TupleT(vec![])), ])) } else { - Err(CheckError(format!("parse_float called on non-string type"))) + Err(format!("parse_float called on non-string type").into()) } }), run: Arc::new(|a, _i| { @@ -72,7 +72,7 @@ impl Config { if a.is_included_in(&Type::newm(vec![Arc::new(data::int::IntT), Arc::new(data::float::FloatT)])) { Ok(Type::new(data::int::IntT)) } else { - Err(CheckError(format!("signum called on non-number type"))) + Err(format!("signum called on non-number type").into()) } }), run: Arc::new(|a, _i| { @@ -88,38 +88,47 @@ impl Config { } } else { unreachable!("called signum on non-number type")})) }) - })) + })) .add_var("div".to_string(), 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| two_tuple_to_num(a, "div")), + run: Arc::new(|a, _i| if let Some(t) = a.get().as_any().downcast_ref::() { + let left = t.0[0].get(); + let right = t.0[1].get(); + let (left, right) = (left.as_any(), right.as_any()); + match (left.downcast_ref::(), left.downcast_ref::(), + right.downcast_ref::(), right.downcast_ref::() + ) { + (Some(data::int::Int(l)), None, Some(data::int::Int(r)), None) => Data::new(data::int::Int(l / r)), + (Some(data::int::Int(l)), None, None, Some(data::float::Float(r))) => Data::new(data::float::Float(*l as f64 / r)), + (None, Some(data::float::Float(l)), Some(data::int::Int(r)), None) => Data::new(data::float::Float(l / *r as f64)), + (None, Some(data::float::Float(l)), None, Some(data::float::Float(r))) => Data::new(data::float::Float(l / r)), + _ => unreachable!(), + } + } else { unreachable!() }), + })).add_var("modulo".to_string(), 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| two_tuple_to_num(a, "modulo")), + run: Arc::new(|a, _i| if let Some(t) = a.get().as_any().downcast_ref::() { + let left = t.0[0].get(); + let right = t.0[1].get(); + let (left, right) = (left.as_any(), right.as_any()); + match (left.downcast_ref::(), left.downcast_ref::(), + right.downcast_ref::(), right.downcast_ref::() + ) { + (Some(data::int::Int(l)), None, Some(data::int::Int(r)), None) => Data::new(data::int::Int(l % r)), + (Some(data::int::Int(l)), None, None, Some(data::float::Float(r))) => Data::new(data::float::Float(*l as f64 % r)), + (None, Some(data::float::Float(l)), Some(data::int::Int(r)), None) => Data::new(data::float::Float(l % *r as f64)), + (None, Some(data::float::Float(l)), None, Some(data::float::Float(r))) => Data::new(data::float::Float(l % r)), + _ => unreachable!(), + } + } else { unreachable!() }), + })) .add_var("diff".to_string(), 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| { - let mut float = false; - for t in &a.types { - if let Some(t) = t.as_any().downcast_ref::() { - if t.0.len() != 2 { - return Err(CheckError(format!("Called diff with a tuple where len != 2"))); - } - for (t, side) in [(&t.0[0], "left"), (&t.0[1], "right")] { - for t in t.types.iter() { - if t.as_any().is::() { - float = true; - } else if !t.as_any().is::() { - return Err(CheckError(format!("Called diff, but the {side} side of the tuple had type {t}, which isn't Int/Float."))); - } - } - } - } else { - return Err(CheckError(format!("Called diff on a non-tuple"))); - } - } - Ok(if a.types.is_empty() { - Type::empty() - } else if float { - Type::new(data::float::FloatT) - } else { - Type::new(data::int::IntT) - }) - }), + out: Arc::new(|a, _i| two_tuple_to_num(a, "diff")), run: Arc::new(|a, _i| if let Some(t) = a.get().as_any().downcast_ref::() { let left = t.0[0].get(); let right = t.0[1].get(); @@ -156,12 +165,12 @@ impl Config { }) { floats = true; } else { - return Err(CheckError(format!("cannot get sum of iterator over type {i} because it contains types that aren't int/float"))) + return Err(format!("cannot get sum of iterator over type {i} because it contains types that aren't int/float").into()) } } else { - return Err(CheckError(format!( + return Err(format!( "cannot get sum of non-iterable type {a}" - ))); + ).into()); } } Ok(match (ints, floats) { @@ -217,12 +226,12 @@ impl Config { }) { floats = true; } else { - return Err(CheckError(format!("cannot get product of iterator over type {i} because it contains types that aren't int/float"))) + return Err(format!("cannot get product of iterator over type {i} because it contains types that aren't int/float").into()) } } else { - return Err(CheckError(format!( + return Err(format!( "cannot get product of non-iterable type {a}" - ))); + ).into()); } } Ok(match (ints, floats) { @@ -259,3 +268,36 @@ impl Config { ) } } + +/// (int, int) -> int +/// (int, float) -> float +/// (float, int) -> float +/// (float, float) -> float +fn two_tuple_to_num(a: &Type, func_name: &str) -> Result { + let mut float = false; + for t in &a.types { + if let Some(t) = t.as_any().downcast_ref::() { + if t.0.len() != 2 { + return Err(format!("Called {func_name} with a tuple where len != 2").into()); + } + for (t, side) in [(&t.0[0], "left"), (&t.0[1], "right")] { + for t in t.types.iter() { + if t.as_any().is::() { + float = true; + } else if !t.as_any().is::() { + return Err(format!("Called {func_name}, but the {side} side of the tuple had type {t}, which isn't Int/Float.").into()); + } + } + } + } else { + return Err(format!("Called {func_name} on a non-tuple").into()); + } + } + Ok(if a.types.is_empty() { + Type::empty() + } else if float { + Type::new(data::float::FloatT) + } else { + Type::new(data::int::IntT) + }) +} diff --git a/mers_lib/src/program/configs/with_string.rs b/mers_lib/src/program/configs/with_string.rs index 3c861ac..90fe9fe 100644 --- a/mers_lib/src/program/configs/with_string.rs +++ b/mers_lib/src/program/configs/with_string.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex}; use crate::{ data::{self, Data, MersType, Type}, - program::run::{CheckError, CheckInfo, Info}, + program::run::{CheckInfo, Info}, }; use super::Config; @@ -21,7 +21,7 @@ impl Config { out: Arc::new(|a, _i| if a.is_included_in(&data::string::StringT) { Ok(Type::new(data::string::StringT)) } else { - Err(CheckError(format!("cannot call trim on non-strings"))) + Err(format!("cannot call trim on non-strings").into()) }), run: Arc::new(|a, _i| { Data::new(data::string::String(a.get().as_any().downcast_ref::().unwrap().0.trim().to_owned())) @@ -32,7 +32,7 @@ impl Config { out: Arc::new(|a, _i| if a.iterable().is_some() { Ok(Type::new(data::string::StringT)) } else { - Err(CheckError(format!("concat called on non-iterable type {a}"))) + Err(format!("concat called on non-iterable type {a}").into()) }), run: Arc::new(|a, _i| Data::new(data::string::String(a.get().iterable().unwrap().map(|v| v.get().to_string()).collect()))), })).add_var("to_string".to_string(), Data::new(data::function::Function { @@ -49,7 +49,7 @@ impl Config { Arc::new(data::int::IntT), ])) } else { - Err(CheckError(format!("wrong args for index_of: must be (string, string)"))) + Err(format!("wrong args for index_of: must be (string, string)").into()) }), run: Arc::new(|a, _i| index_of(a, false)), })).add_var("index_of_rev".to_string(), Data::new(data::function::Function { @@ -61,7 +61,7 @@ impl Config { Arc::new(data::int::IntT), ])) } else { - Err(CheckError(format!("wrong args for index_of: must be (string, string)"))) + Err(format!("wrong args for index_of: must be (string, string)").into()) }), run: Arc::new(|a, _i| index_of(a, true)), })).add_var("substring".to_string(), Data::new(data::function::Function { @@ -71,19 +71,19 @@ impl Config { for t in a.types.iter() { if let Some(t) = t.as_any().downcast_ref::() { if t.0.len() != 2 && t.0.len() != 3 { - return Err(CheckError(format!("cannot call substring with tuple argument of len != 3"))); + return Err(format!("cannot call substring with tuple argument of len != 3").into()); } if !t.0[0].is_included_in(&data::string::StringT) { - return Err(CheckError(format!("cannot call substring with tuple argument that isn't (*string*, int, int)"))); + return Err(format!("cannot call substring with tuple argument that isn't (*string*, int, int)").into()); } if !t.0[1].is_included_in(&data::int::IntT) { - return Err(CheckError(format!("cannot call substring with tuple argument that isn't (string, *int*, int)"))); + return Err(format!("cannot call substring with tuple argument that isn't (string, *int*, int)").into()); } if t.0.len() > 2 && !t.0[2].is_included_in(&data::int::IntT) { - return Err(CheckError(format!("cannot call substring with tuple argument that isn't (string, int, *int*)"))); + return Err(format!("cannot call substring with tuple argument that isn't (string, int, *int*)").into()); } } else { - return Err(CheckError(format!("cannot call substring with non-tuple argument."))); + return Err(format!("cannot call substring with non-tuple argument.").into()); } } Ok(if a.types.is_empty() { diff --git a/mers_lib/src/program/parsed/assign_to.rs b/mers_lib/src/program/parsed/assign_to.rs index 4013b0e..cd10563 100755 --- a/mers_lib/src/program/parsed/assign_to.rs +++ b/mers_lib/src/program/parsed/assign_to.rs @@ -1,10 +1,10 @@ -use crate::{parsing::SourcePos, program}; +use crate::program::{self, run::SourceRange}; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct AssignTo { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub target: Box, pub source: Box, } diff --git a/mers_lib/src/program/parsed/block.rs b/mers_lib/src/program/parsed/block.rs index b98a421..0612637 100755 --- a/mers_lib/src/program/parsed/block.rs +++ b/mers_lib/src/program/parsed/block.rs @@ -1,10 +1,13 @@ -use crate::{info, parsing::SourcePos, program}; +use crate::{ + info, + program::{self, run::SourceRange}, +}; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct Block { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub statements: Vec>, } impl MersStatement for Block { diff --git a/mers_lib/src/program/parsed/chain.rs b/mers_lib/src/program/parsed/chain.rs index 9f5c116..7fde8a8 100755 --- a/mers_lib/src/program/parsed/chain.rs +++ b/mers_lib/src/program/parsed/chain.rs @@ -1,11 +1,11 @@ -use crate::parsing::SourcePos; +use crate::program::run::SourceRange; use crate::{info, program}; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct Chain { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub first: Box, pub chained: Box, } diff --git a/mers_lib/src/program/parsed/function.rs b/mers_lib/src/program/parsed/function.rs index 9276a10..b9d06a9 100755 --- a/mers_lib/src/program/parsed/function.rs +++ b/mers_lib/src/program/parsed/function.rs @@ -1,4 +1,4 @@ -use crate::parsing::SourcePos; +use crate::program::run::SourceRange; use std::sync::{Arc, Mutex}; use crate::{ @@ -10,7 +10,7 @@ use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct Function { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub arg: Box, pub run: Box, } diff --git a/mers_lib/src/program/parsed/if.rs b/mers_lib/src/program/parsed/if.rs index 51751f0..ea8ad16 100755 --- a/mers_lib/src/program/parsed/if.rs +++ b/mers_lib/src/program/parsed/if.rs @@ -1,10 +1,10 @@ -use crate::{parsing::SourcePos, program}; +use crate::program::{self, run::SourceRange}; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct If { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub condition: Box, pub on_true: Box, pub on_false: Option>, diff --git a/mers_lib/src/program/parsed/init_to.rs b/mers_lib/src/program/parsed/init_to.rs index 256f996..faa2964 100755 --- a/mers_lib/src/program/parsed/init_to.rs +++ b/mers_lib/src/program/parsed/init_to.rs @@ -1,11 +1,11 @@ -use crate::parsing::SourcePos; use crate::program; +use crate::program::run::SourceRange; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct InitTo { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub target: Box, pub source: Box, } diff --git a/mers_lib/src/program/parsed/tuple.rs b/mers_lib/src/program/parsed/tuple.rs index 5a33a72..b4479dd 100755 --- a/mers_lib/src/program/parsed/tuple.rs +++ b/mers_lib/src/program/parsed/tuple.rs @@ -1,10 +1,13 @@ -use crate::{info, parsing::SourcePos, program}; +use crate::{ + info, + program::{self, run::SourceRange}, +}; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct Tuple { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub elems: Vec>, } impl MersStatement for Tuple { diff --git a/mers_lib/src/program/parsed/value.rs b/mers_lib/src/program/parsed/value.rs index f4dbbf9..260f321 100755 --- a/mers_lib/src/program/parsed/value.rs +++ b/mers_lib/src/program/parsed/value.rs @@ -1,11 +1,11 @@ -use crate::parsing::SourcePos; +use crate::program::run::SourceRange; use crate::{data::Data, program}; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct Value { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub data: Data, } diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index e035d50..47c4650 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -1,10 +1,13 @@ -use crate::{info::Local, parsing::SourcePos, program}; +use crate::{ + info::Local, + program::{self, run::SourceRange}, +}; use super::{CompInfo, MersStatement}; #[derive(Debug)] pub struct Variable { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub is_ref: bool, pub var: String, } diff --git a/mers_lib/src/program/run/assign_to.rs b/mers_lib/src/program/run/assign_to.rs index dd01605..1edda59 100755 --- a/mers_lib/src/program/run/assign_to.rs +++ b/mers_lib/src/program/run/assign_to.rs @@ -1,13 +1,12 @@ -use crate::{ - data::{self, Data, MersType, Type}, - parsing::SourcePos, -}; +use colored::Colorize; -use super::{CheckError, CheckInfo, MersStatement}; +use crate::data::{self, Data, MersType, Type}; + +use super::{CheckError, CheckInfo, MersStatement, SourceRange}; #[derive(Debug)] pub struct AssignTo { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub is_init: bool, pub target: Box, pub source: Box, @@ -20,21 +19,51 @@ impl MersStatement for AssignTo { init_to: Option<&Type>, ) -> Result { if init_to.is_some() { - return Err(CheckError( - "can't init to statement type AssignTo".to_string(), - )); + return Err("can't init to statement type AssignTo".to_string().into()); } let source = self.source.check(info, None)?; - let target = self.target.check(info, Some(&source))?; + let target = match self.target.check(info, Some(&source)) { + Ok(v) => v, + Err(e) => { + 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)), + ]) + .msg(format!("Cannot initialize:")) + .err(e)) + } + }; if !self.is_init { if let Some(t) = target.dereference() { if !source.is_included_in(&t) { - return Err(CheckError(format!( - "can't assign {source} to {target} because it isn't included in {t}!" - ))); + 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)), + ]) + .msg(format!( + "can't assign {} to {} because it isn't included in {}", + source.to_string().bright_cyan(), + target.to_string().bright_yellow(), + t + ))); } } else { - return Err(CheckError(format!("can't assign to non-reference!"))); + return Err(CheckError::new() + .src(vec![ + (self.pos_in_src, None), + (self.target.source_range(), Some(colored::Color::Red)), + ]) + .msg(format!("can't assign to non-reference!"))); } } Ok(Type::empty_tuple()) @@ -48,7 +77,7 @@ impl MersStatement for AssignTo { fn has_scope(&self) -> bool { false } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } } diff --git a/mers_lib/src/program/run/block.rs b/mers_lib/src/program/run/block.rs index 3ced007..af15de3 100755 --- a/mers_lib/src/program/run/block.rs +++ b/mers_lib/src/program/run/block.rs @@ -1,10 +1,10 @@ -use crate::{data::Type, parsing::SourcePos}; +use crate::data::Type; -use super::{CheckError, MersStatement}; +use super::{MersStatement, SourceRange}; #[derive(Debug)] pub struct Block { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub statements: Vec>, } impl MersStatement for Block { @@ -14,7 +14,7 @@ impl MersStatement for Block { init_to: Option<&Type>, ) -> Result { if init_to.is_some() { - return Err(CheckError("can't init to statement type Block".to_string())); + return Err("can't init to statement type Block".to_string().into()); } let mut o = Type::empty_tuple(); for s in &self.statements { @@ -32,7 +32,7 @@ impl MersStatement for Block { fn has_scope(&self) -> bool { true } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } } diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index 224ef15..3668459 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -1,15 +1,14 @@ use std::sync::Arc; -use crate::{ - data::{Data, Type}, - parsing::SourcePos, -}; +use colored::Colorize; -use super::{CheckError, MersStatement}; +use crate::data::{Data, Type}; + +use super::{CheckError, MersStatement, SourceRange}; #[derive(Debug)] pub struct Chain { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub first: Box, pub chained: Box, } @@ -20,7 +19,7 @@ impl MersStatement for Chain { init_to: Option<&Type>, ) -> Result { if init_to.is_some() { - return Err(CheckError("can't init to statement type Chain".to_string())); + return Err("can't init to statement type Chain".to_string().into()); } let arg = self.first.check(info, None)?; let func = self.chained.check(info, None)?; @@ -32,15 +31,34 @@ impl MersStatement for Chain { { match (func.0)(&arg) { Ok(t) => o.add(Arc::new(t)), - Err(e) => - return Err(CheckError(format!( - "cannot run this function with this argument (type: {arg}), because it would cause the following error:\n{e}" - ))), + Err(e) => { + 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), + ), + ]) + .msg(format!( + "Can't call {} with an argument of type {}:", + "this function".bright_magenta(), + arg.to_string().bright_cyan() + )) + .err(e)) + } } } else { - return Err(CheckError(format!( - "cannot chain with a non-function ({func})" - ))); + return Err(CheckError::new() + .src(vec![ + (self.pos_in_src, None), + (self.chained.source_range(), Some(colored::Color::BrightRed)), + ]) + .msg(format!( + "cannot chain with a non-function ({})", + func.to_string().bright_red() + ))); } } Ok(o) @@ -58,7 +76,7 @@ impl MersStatement for Chain { fn has_scope(&self) -> bool { false } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } } diff --git a/mers_lib/src/program/run/function.rs b/mers_lib/src/program/run/function.rs index 651e238..2609653 100755 --- a/mers_lib/src/program/run/function.rs +++ b/mers_lib/src/program/run/function.rs @@ -1,15 +1,12 @@ use std::sync::Arc; -use crate::{ - data::{self, Data, MersData, Type}, - parsing::SourcePos, -}; +use crate::data::{self, Data, MersData, Type}; -use super::{CheckError, MersStatement}; +use super::{MersStatement, SourceRange}; #[derive(Debug)] pub struct Function { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub func_no_info: data::function::Function, } @@ -20,9 +17,7 @@ impl MersStatement for Function { init_to: Option<&Type>, ) -> Result { if init_to.is_some() { - return Err(CheckError( - "can't init to statement type Function".to_string(), - )); + return Err("can't init to statement type Function".to_string().into()); } self.func_no_info.with_info_check(info.clone()); Ok(self.func_no_info.as_type()) @@ -33,7 +28,7 @@ impl MersStatement for Function { fn has_scope(&self) -> bool { true } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } } diff --git a/mers_lib/src/program/run/if.rs b/mers_lib/src/program/run/if.rs index a4a275f..a346403 100755 --- a/mers_lib/src/program/run/if.rs +++ b/mers_lib/src/program/run/if.rs @@ -1,15 +1,14 @@ use std::sync::Arc; -use crate::{ - data::{self, Data, MersType, Type}, - parsing::SourcePos, -}; +use colored::Colorize; -use super::{CheckError, MersStatement}; +use crate::data::{self, Data, MersType, Type}; + +use super::{CheckError, MersStatement, SourceRange}; #[derive(Debug)] pub struct If { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub condition: Box, pub on_true: Box, pub on_false: Option>, @@ -22,16 +21,23 @@ impl MersStatement for If { init_to: Option<&Type>, ) -> Result { if init_to.is_some() { - return Err(CheckError("can't init to statement type If".to_string())); + return Err("can't init to statement type If".to_string().into()); } - if !self - .condition - .check(info, None)? - .is_included_in(&data::bool::BoolT) - { - return Err(CheckError(format!( - "condition in an if-statement must return bool" - ))); + let cond_return_type = self.condition.check(info, None)?; + if !cond_return_type.is_included_in(&data::bool::BoolT) { + return Err(CheckError::new() + .src(vec![ + (self.pos_in_src, None), + ( + self.condition.source_range(), + Some(colored::Color::BrightRed), + ), + ]) + .msg(format!( + "The {} in an if-statement must return bool, not {}", + "condition".red(), + cond_return_type.to_string().bright_red(), + ))); } let mut t = self.on_true.check(info, None)?; if let Some(f) = &self.on_false { @@ -59,7 +65,7 @@ impl MersStatement for If { fn has_scope(&self) -> bool { true } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } } diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 5db0447..611a723 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -3,10 +3,13 @@ use std::{ sync::{Arc, Mutex}, }; +use colored::Colorize; +use line_span::LineSpanExt; + use crate::{ data::{self, Data, Type}, info, - parsing::SourcePos, + parsing::{Source, SourcePos}, }; #[cfg(feature = "run")] @@ -35,7 +38,6 @@ pub trait MersStatement: Debug + Send + Sync { fn run_custom(&self, info: &mut Info) -> Data; /// if true, local variables etc. will be contained inside their own scope. fn has_scope(&self) -> bool; - fn pos_in_src(&self) -> &SourcePos; fn check(&self, info: &mut CheckInfo, assign: Option<&Type>) -> Result { if self.has_scope() { info.create_scope(); @@ -56,13 +58,159 @@ pub trait MersStatement: Debug + Send + Sync { } o } + fn source_range(&self) -> SourceRange; } -#[derive(Clone, Debug)] -pub struct CheckError(pub String); -impl Display for CheckError { +#[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, + } + } +} +#[derive(Clone)] +pub struct CheckError(Vec); +#[derive(Clone)] +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: &'a Source, +} +impl Display for CheckErrorDisplay<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) + 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 } + } + // will, unless empty, end in a newline + fn human_readable( + &self, + f: &mut std::fmt::Formatter<'_>, + src: &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) => { + 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 { + let hl_start = highlight_start.saturating_sub(line_start); + if hl_start < right { + right = 0; + writeln!(f)?; + } + let hl_len = highlight_end + .saturating_sub(line_start) + .saturating_sub(hl_start); + let hl_space = hl_start - right; + let print_indent = right == 0; + right += hl_space + hl_len; + let hl_len = hl_len.min(highlight_end - highlight_start); + 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/program/run/tuple.rs b/mers_lib/src/program/run/tuple.rs index e7ff5ac..a1304c2 100755 --- a/mers_lib/src/program/run/tuple.rs +++ b/mers_lib/src/program/run/tuple.rs @@ -1,15 +1,14 @@ use std::sync::Arc; -use crate::{ - data::{self, tuple::TupleT, Data, Type}, - parsing::SourcePos, -}; +use colored::Colorize; -use super::{CheckError, MersStatement}; +use crate::data::{self, tuple::TupleT, Data, Type}; + +use super::{MersStatement, SourceRange}; #[derive(Debug)] pub struct Tuple { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub elems: Vec>, } impl MersStatement for Tuple { @@ -29,14 +28,11 @@ impl MersStatement for Tuple { vec[i].add(Arc::new(e.clone())); } } else { - return Err(CheckError( - format!("can't init to 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()), - )); + 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()); } } else { - return Err(CheckError( - format!("can't init to statement type Tuple with value type {t}, which is part of {init_to} - only tuples can be assigned to tuples"), - )); + 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()); } } Some(vec) @@ -68,7 +64,7 @@ impl MersStatement for Tuple { fn has_scope(&self) -> bool { false } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } } diff --git a/mers_lib/src/program/run/value.rs b/mers_lib/src/program/run/value.rs index 21b9cdf..706b7b3 100755 --- a/mers_lib/src/program/run/value.rs +++ b/mers_lib/src/program/run/value.rs @@ -1,13 +1,10 @@ -use crate::{ - data::{Data, Type}, - parsing::SourcePos, -}; +use crate::data::{Data, Type}; -use super::{CheckError, MersStatement}; +use super::{MersStatement, SourceRange}; #[derive(Debug)] pub struct Value { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub val: Data, } @@ -21,14 +18,14 @@ impl MersStatement for Value { init_to: Option<&Type>, ) -> Result { if init_to.is_some() { - return Err(CheckError("can't init to statement type Value".to_string())); + return Err("can't init to statement type Value".to_string().into()); } Ok(self.val.get().as_type()) } fn run_custom(&self, _info: &mut super::Info) -> Data { self.val.clone() } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } } diff --git a/mers_lib/src/program/run/variable.rs b/mers_lib/src/program/run/variable.rs index 1bed306..3f4454d 100755 --- a/mers_lib/src/program/run/variable.rs +++ b/mers_lib/src/program/run/variable.rs @@ -1,15 +1,12 @@ use std::sync::{Arc, Mutex}; -use crate::{ - data::{self, Data, Type}, - parsing::SourcePos, -}; +use crate::data::{self, Data, Type}; -use super::MersStatement; +use super::{MersStatement, SourceRange}; #[derive(Debug)] pub struct Variable { - pub pos_in_src: SourcePos, + pub pos_in_src: SourceRange, pub is_init: bool, pub is_ref: bool, pub var: (usize, usize), @@ -60,7 +57,7 @@ impl MersStatement for Variable { .clone() } } - fn pos_in_src(&self) -> &SourcePos { - &self.pos_in_src + fn source_range(&self) -> SourceRange { + self.pos_in_src } }