added better error messages inspired by rustc/cargo

but not as good
This commit is contained in:
Mark 2023-10-23 21:48:15 +02:00
parent 62ed8fc2bd
commit ea95a16c30
30 changed files with 557 additions and 260 deletions

27
mers/Cargo.lock generated
View File

@ -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"

View File

@ -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);
}
};

View File

@ -8,3 +8,7 @@ edition = "2021"
default = ["parse"]
parse = ["run"]
run = []
[dependencies]
colored = "2.0.4"
line-span = "0.1.5"

View File

@ -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
}

View File

@ -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(),
})

View File

@ -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()

View File

@ -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| {

View File

@ -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| {

View File

@ -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())

View File

@ -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| {

View File

@ -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)
})
}

View File

@ -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() {

View File

@ -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>,
}

View File

@ -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 {

View File

@ -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>,
}

View File

@ -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>,
}

View File

@ -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>>,

View File

@ -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>,
}

View File

@ -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 {

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}