mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
added better error messages inspired by rustc/cargo
but not as good
This commit is contained in:
parent
62ed8fc2bd
commit
ea95a16c30
27
mers/Cargo.lock
generated
27
mers/Cargo.lock
generated
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -8,3 +8,7 @@ edition = "2021"
|
||||
default = ["parse"]
|
||||
parse = ["run"]
|
||||
run = []
|
||||
|
||||
[dependencies]
|
||||
colored = "2.0.4"
|
||||
line-span = "0.1.5"
|
||||
|
@ -8,15 +8,17 @@ pub mod types;
|
||||
|
||||
pub fn parse(src: &mut Source) -> Result<Box<dyn program::parsed::MersStatement>, ()> {
|
||||
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
|
||||
}
|
||||
|
@ -16,28 +16,31 @@ pub fn parse(src: &mut Source) -> Result<Option<Box<dyn program::parsed::MersSta
|
||||
":=" => {
|
||||
let pos_in_src = src.get_pos();
|
||||
src.next_word();
|
||||
let source = parse(src)?.expect("todo");
|
||||
first = Box::new(program::parsed::init_to::InitTo {
|
||||
pos_in_src,
|
||||
pos_in_src: (pos_in_src, src.get_pos()).into(),
|
||||
target: first,
|
||||
source: parse(src)?.expect("todo"),
|
||||
source,
|
||||
});
|
||||
}
|
||||
"=" => {
|
||||
let pos_in_src = src.get_pos();
|
||||
src.next_word();
|
||||
let source = parse(src)?.expect("todo");
|
||||
first = Box::new(program::parsed::assign_to::AssignTo {
|
||||
pos_in_src,
|
||||
pos_in_src: (pos_in_src, src.get_pos()).into(),
|
||||
target: first,
|
||||
source: parse(src)?.expect("todo"),
|
||||
source,
|
||||
});
|
||||
}
|
||||
"->" => {
|
||||
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<Option<Box<dyn program::parsed::MersSta
|
||||
src.next_char();
|
||||
let chained = parse_no_chain(src)?.expect("err: EOF instead of chain");
|
||||
first = Box::new(program::parsed::chain::Chain {
|
||||
pos_in_src,
|
||||
pos_in_src: (pos_in_src, src.get_pos()).into(),
|
||||
first,
|
||||
chained,
|
||||
});
|
||||
@ -87,17 +90,19 @@ pub fn parse_no_chain(
|
||||
Some('{') => {
|
||||
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,11 +142,9 @@ pub fn parse_no_chain(
|
||||
Ok(Some(match src.next_word() {
|
||||
"if" => {
|
||||
src.section_begin("if".to_string());
|
||||
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: {
|
||||
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());
|
||||
@ -150,15 +153,20 @@ pub fn parse_no_chain(
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
Box::new(program::parsed::r#if::If {
|
||||
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(),
|
||||
})
|
||||
|
@ -24,7 +24,7 @@ impl Config {
|
||||
for t in a.types.iter() {
|
||||
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
|
||||
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::<String>())));
|
||||
// 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::<data::int::Int>().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::<data::string::StringT>().is_none() && t.as_any().downcast_ref::<data::tuple::TupleT>().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::<data::tuple::TupleT>() {
|
||||
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()
|
||||
|
@ -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<String>))")));
|
||||
return Err(format!("run_command called with invalid arguments (must be (String, Iter<String>))").into());
|
||||
}
|
||||
}),
|
||||
run: Arc::new(|a, _i| {
|
||||
|
@ -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| {
|
||||
|
@ -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())
|
||||
|
@ -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::<ListT>() {
|
||||
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::<data::tuple::TupleT>() {
|
||||
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::<ListT>() {
|
||||
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!(
|
||||
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| {
|
||||
|
@ -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::<data::tuple::Tuple>() {
|
||||
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::<data::int::Int>(), left.downcast_ref::<data::float::Float>(),
|
||||
right.downcast_ref::<data::int::Int>(), right.downcast_ref::<data::float::Float>()
|
||||
) {
|
||||
(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::<data::tuple::Tuple>() {
|
||||
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::<data::int::Int>(), left.downcast_ref::<data::float::Float>(),
|
||||
right.downcast_ref::<data::int::Int>(), right.downcast_ref::<data::float::Float>()
|
||||
) {
|
||||
(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::<data::tuple::TupleT>() {
|
||||
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::<data::float::FloatT>() {
|
||||
float = true;
|
||||
} else if !t.as_any().is::<data::int::IntT>() {
|
||||
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::<data::tuple::Tuple>() {
|
||||
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<Type, CheckError> {
|
||||
let mut float = false;
|
||||
for t in &a.types {
|
||||
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
|
||||
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::<data::float::FloatT>() {
|
||||
float = true;
|
||||
} else if !t.as_any().is::<data::int::IntT>() {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
@ -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::<data::string::String>().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::<data::tuple::TupleT>() {
|
||||
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() {
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub source: Box<dyn MersStatement>,
|
||||
}
|
||||
|
@ -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<Box<dyn MersStatement>>,
|
||||
}
|
||||
impl MersStatement for Block {
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub chained: Box<dyn MersStatement>,
|
||||
}
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub run: Box<dyn MersStatement>,
|
||||
}
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub on_true: Box<dyn MersStatement>,
|
||||
pub on_false: Option<Box<dyn MersStatement>>,
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub source: Box<dyn MersStatement>,
|
||||
}
|
||||
|
@ -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<Box<dyn MersStatement>>,
|
||||
}
|
||||
impl MersStatement for Tuple {
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub source: Box<dyn MersStatement>,
|
||||
@ -20,21 +19,51 @@ impl MersStatement for AssignTo {
|
||||
init_to: Option<&Type>,
|
||||
) -> Result<Type, CheckError> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<Box<dyn MersStatement>>,
|
||||
}
|
||||
impl MersStatement for Block {
|
||||
@ -14,7 +14,7 @@ impl MersStatement for Block {
|
||||
init_to: Option<&Type>,
|
||||
) -> Result<crate::data::Type, super::CheckError> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub chained: Box<dyn MersStatement>,
|
||||
}
|
||||
@ -20,7 +19,7 @@ impl MersStatement for Chain {
|
||||
init_to: Option<&Type>,
|
||||
) -> Result<Type, CheckError> {
|
||||
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,14 +31,33 @@ 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()
|
||||
)));
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<data::Type, super::CheckError> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn MersStatement>,
|
||||
pub on_true: Box<dyn MersStatement>,
|
||||
pub on_false: Option<Box<dyn MersStatement>>,
|
||||
@ -22,15 +21,22 @@ impl MersStatement for If {
|
||||
init_to: Option<&Type>,
|
||||
) -> Result<data::Type, super::CheckError> {
|
||||
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)?;
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<Type, CheckError> {
|
||||
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<CheckErrorComponent>);
|
||||
#[derive(Clone)]
|
||||
enum CheckErrorComponent {
|
||||
Message(String),
|
||||
Error(CheckError),
|
||||
Source(Vec<(SourceRange, Option<colored::Color>)>),
|
||||
}
|
||||
#[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<colored::Color>)>) -> 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::<Vec<_>>();
|
||||
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<String> for CheckError {
|
||||
fn from(value: String) -> Self {
|
||||
Self::new().msg(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Box<dyn MersStatement>>,
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<crate::data::Type, super::CheckError> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user