nicer errors

This commit is contained in:
Mark 2024-06-21 15:50:41 +02:00
parent b11e4017ed
commit 688e28c171
13 changed files with 833 additions and 397 deletions

14
TODO.md Normal file
View File

@ -0,0 +1,14 @@
# Objects
```
o := {
c: 12
x: 5.0
y: 3.2
coords: o -> o.c
println: o -> "aaa".println
}
{ println: println } := o
o.println
```

12
learn_mers/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "learn_mers"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.4.8", features = ["derive"] }
colored = "2.0.4"
mers_lib = { path = "../mers_lib" }
notify = "6.1.1"

65
learn_mers/src/main.rs Normal file
View File

@ -0,0 +1,65 @@
mod tasks;
use colored::Colorize;
use std::{fs, path::PathBuf, time::Duration};
use clap::Parser;
use notify::Watcher;
use mers_lib::prelude_compile::*;
#[derive(Parser)]
pub(crate) struct Args {
file: PathBuf,
/// Skip this many tasks, i.e. start at task #<skip>, where 0 is `Hello, World`.
#[arg(long, default_value_t = 0)]
skip: usize,
}
fn main() {
let args = Args::parse();
let (s, r) = std::sync::mpsc::channel();
let mut file_watcher = notify::recommended_watcher(move |e: Result<notify::Event, _>| {
let e = e.unwrap();
match e.kind {
notify::EventKind::Modify(k) => match k {
notify::event::ModifyKind::Data(_) => {
s.send(()).unwrap();
}
_ => {}
},
_ => {}
}
})
.unwrap();
fs::write(&args.file, "\"Hello, World!\".println\n").unwrap();
file_watcher
.watch(&args.file, notify::RecursiveMode::NonRecursive)
.unwrap();
for (task_i, (init_msg, cfg, mut task)) in
tasks::tasks(&args).into_iter().enumerate().skip(args.skip)
{
eprintln!("\n\n\n\n\n{}", format!(" -= {task_i} =-").bright_black());
eprintln!("{}", init_msg);
loop {
// wait for file change
r.recv().unwrap();
std::thread::sleep(Duration::from_millis(50));
_ = r.try_iter().count();
let code = fs::read_to_string(&args.file).unwrap();
let mut src = Source::new(code);
let v = parse(&mut src).and_then(|v| {
let config = cfg();
let (mut i1, i2, mut i3) = config.infos();
v.compile(&mut i1, CompInfo::default())
.and_then(|v| v.check(&mut i3, None).map(|t| (t, v, i1, i2, i3)))
});
eprintln!("\n{}\n", "- - - - - - - - - -".bright_black());
if task(v, src) {
break;
}
}
}
eprintln!("\nThere are no more tasks here. Feel free to suggest new tasks or changes to existing ones, and thanks for taking the time to check out mers :)");
}

202
learn_mers/src/tasks.rs Normal file
View File

@ -0,0 +1,202 @@
use std::sync::{Arc, Mutex};
use mers_lib::{
data::{self, Data, MersType, Type},
errors::CheckError,
prelude_compile::{RunMersStatement, Source},
prelude_extend_config::Config,
};
use crate::Args;
pub(crate) fn tasks(
args: &Args,
) -> Vec<(
String,
Box<dyn Fn() -> Config>,
Box<
dyn FnMut(
Result<
(
Type,
Box<dyn RunMersStatement>,
mers_lib::program::parsed::Info,
mers_lib::program::run::Info,
mers_lib::program::run::CheckInfo,
),
CheckError,
>,
Source,
) -> bool,
>,
)> {
let file = args.file.to_string_lossy();
vec![
(
format!(
" Hello, World!
---------------
Hello! I've saved a file at {file}.
You can always use `mers run '{file}'` to actually run it, but for this introduction, just open the file in the editor of your choice.
You should see mers' version of the \"Hello, World!\" program. Your task is to cause an error - to write code that doesn't work.
When done, save the file and you should get feedback right here."
),
Box::new(|| Config::new().with_stdio()),
Box::new(|v, s| match v {
Ok(..) => {
eprintln!("nope, that still compiles.");
false
}
Err(e) => {
eprintln!("{}", e.display(&s));
eprintln!("Nice! Just for fun, the error you created can be seen above.");
true
}
}),
),
(
format!(
" Hello, Function!
------------------
Functions in mers are created using `->`:
`arg -> arg`
This is a function that does nothing, it just returns its argument.
`n -> (n, 1).sum`
This function returns `n + 1`.
Your task is to create any function."
),
Box::new(|| Config::new().with_math()),
Box::new(|v, s| match v {
Ok((t, _, _, _, _)) => {
if t.types
.iter()
.all(|t| t.as_any().is::<data::function::FunctionT>())
{
eprintln!(
"Nice! Note that, even though you didn't see an error, your function may not have been correct. This will be explained later."
);
true
} else {
eprintln!("This expression has type {t}, which isn't a function");
false
}
}
Err(e) => {
eprintln!("{}", e.display(&s));
false
}
}),
),
(
format!(
" Using Functions
-----------------
Mers functions require exactly one argument.
To use a function, use the `<argument>.<function>` syntax:
`\"Hi\".println`
(`println` is a function, `\"Hi\"` is the argument)
In this task, `func` will be a function. Your task is to use it.
`func` works with any argument, so this should be quite easy."
),
Box::new(|| {
Config::new().with_stdio().add_var(
"func".to_string(),
Data::new(data::function::Function {
info: Arc::new(mers_lib::info::Info::neverused()),
info_check: Arc::new(Mutex::new(mers_lib::info::Info::neverused())),
out: Arc::new(|_, _| Ok(Type::empty())),
run: Arc::new(|_, _| Data::empty_tuple()),
}),
)
}),
Box::new(|v, s| match v {
Ok((t, _, _, _, _)) => {
if t.types.is_empty() {
eprintln!(
"Nice! You successfully achieved nothing by using a function that does nothing."
);
true
} else {
eprintln!("Hm, doesn't look like using `func` is the last thing your program does...");
false
}
}
Err(e) => {
eprintln!("{}", e.display(&s));
false
}
}),
),(
format!(
" Hello, Variables!
------------------
To create a new variable in mers, use `:=`:
`greeting := \"Hello\"`
You can use variables by just writing their name:
`greeting.println`
In this task, I'll add the variable `im_done`.
To move on to the next task, return the value stored in it by writing `im_done` at the end of your file.
You can also store the value in another variable first:
`value := im_done`
`value`"
),
Box::new(|| Config::new().add_var("im_done".to_string(), Data::one_tuple(Data::empty_tuple()))),
Box::new(|v, s| match v {
Ok((t, _, _, _, _)) => {
if t.one_tuple_content().is_some_and(|c| c.is_zero_tuple())
{
true
} else {
eprintln!("You returned a value of type {t}, which isn't the type of `im_done`.");
false
}
}
Err(e) => {
eprintln!("{}", e.display(&s));
false
}
}),
),
(
format!(
" Functions with multiple arguments
-----------------------------------
Mers functions only have one argument. To give a function multiple values, we have to use tuples:
`(\"Hello, \", \"World!\").concat`
(`concat` joins the two strings together, creating `\"Hello, World!\"`.)
When writing your own functions, you can destructure these tuples:
`(a, b) -> (b, a).concat`
This creates a function which can only be called with a 2-long tuple.
Your task is to assign this function to a variable `swapped`:
`swapped := (a, b) -> (b, a).concat`
Then, try to call the function with a wrong argument:
`\"hi\".swapped`
`().swapped`
`(\"a\", \"b\", \"c\").swapped`
To complete the task, use the function in a way that won't cause any errors."
),
Box::new(|| Config::new().with_string()),
Box::new(|v, s| match v {
Ok((t, _, _, _, _)) => {
if t.is_included_in(&data::string::StringT) {
true
} else {
eprintln!("Whatever you did compiles, but doesn't seem to use the `swapped` function...");
false
}
}
Err(e) => {
eprintln!("{}", e.display(&s));
false
}
}),
),
]
}

4
main.mers Normal file
View File

@ -0,0 +1,4 @@
add_one := x -> x.sum(1)
do_twice := func -> x -> x.func.func
add_two := [(Int -> Int, Float -> Float)] add_one.do_twice
"not a number".add_two

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mers" name = "mers"
version = "0.8.11" version = "0.8.12"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "dynamically typed but type-checked programming language" description = "dynamically typed but type-checked programming language"
@ -11,7 +11,7 @@ repository = "https://github.com/Dummi26/mers"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
mers_lib = "0.8.11" # mers_lib = "0.8.12"
# mers_lib = { path = "../mers_lib" } mers_lib = { path = "../mers_lib" }
clap = { version = "4.3.19", features = ["derive"] } clap = { version = "4.3.19", features = ["derive"] }
colored = "2.1.0" colored = "2.1.0"

19
mers/RuntimeErrors.md Normal file
View File

@ -0,0 +1,19 @@
# Runtime Errors in Mers
This is a list of <small><small><small><small>(hopefully)</small></small></small></small>
all runtime errors that can occur when running a mers program.
## Explicit
- Calling `panic` with a `String`-type argument will cause a runtime error, using the argument as the error message.
- Calling `exit` will exit with the given (`Int`) exit code. Usually, a nonzero exit code is considered a program failure. While this isn't really a runtime error, `exit` terminate the program just like an error would.
## Integer-Integer math
Some math functions fail under certain conditions when called with two integer arguments:
- `x.div(0)` will fail because you cannot divide by zero. If at least one argument is a `Float`, this will return Infinity or Not A Number.
- `x.modulo(0)` will fail
- ...
## ... (TODO)

View File

@ -123,7 +123,7 @@ fn main() {
} }
Ok(_) => { Ok(_) => {
if let Err(e) = compiled.run(&mut i2) { if let Err(e) = compiled.run(&mut i2) {
eprintln!("Error while running: {}", e); eprintln!("Error while running:\n{e}");
std::process::exit(1); std::process::exit(1);
} }
} }
@ -149,7 +149,7 @@ fn main() {
} }
Ok(compiled) => { Ok(compiled) => {
if let Err(e) = compiled.run(&mut i2) { if let Err(e) = compiled.run(&mut i2) {
eprintln!("Error while running: {}", e); eprintln!("Error while running:\n{e}");
std::process::exit(1); std::process::exit(1);
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mers_lib" name = "mers_lib"
version = "0.8.11" version = "0.8.12"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "library to use the mers language in other projects" description = "library to use the mers language in other projects"

View File

@ -352,11 +352,7 @@ impl Type {
pub fn iterable(&self) -> Option<Type> { pub fn iterable(&self) -> Option<Type> {
let mut o = Self::empty(); let mut o = Self::empty();
for t in self.types.iter() { for t in self.types.iter() {
if let Some(t) = t.iterable() { o.add_all(&t.iterable()?);
o.add_all(&t);
} else {
return None;
}
} }
Some(o) Some(o)
} }

View File

@ -87,6 +87,9 @@ pub mod error_colors {
pub const TryNotAFunction: Color = Color::Red; pub const TryNotAFunction: Color = Color::Red;
pub const TryUnusedFunction1: Color = Color::Red; pub const TryUnusedFunction1: Color = Color::Red;
pub const TryUnusedFunction2: Color = Color::BrightRed; pub const TryUnusedFunction2: Color = Color::BrightRed;
pub const StacktraceDescend: Color = Color::Yellow;
pub const StacktraceDescendHashInclude: Color = Color::Red;
} }
#[derive(Clone)] #[derive(Clone)]
pub enum CheckErrorComponent { pub enum CheckErrorComponent {
@ -95,15 +98,134 @@ pub enum CheckErrorComponent {
ErrorWithDifferentSource(CheckError), ErrorWithDifferentSource(CheckError),
Source(Vec<(SourceRange, Option<colored::Color>)>), Source(Vec<(SourceRange, Option<colored::Color>)>),
} }
#[derive(Clone)]
pub struct CheckErrorHRConfig { pub struct CheckErrorHRConfig {
color_index: Rc<AtomicUsize>, color_index_ptr: Rc<AtomicUsize>,
indent_start: String, color_index: usize,
indent_default: String, is_inner: bool,
indent_end: String, style: u8,
idt_start: String,
idt_default: String,
idt_end: String,
idt_single: String,
/// if true, shows "original" source code, if false, shows source with comments removed (this is what the parser uses internally) /// if true, shows "original" source code, if false, shows source with comments removed (this is what the parser uses internally)
show_comments: bool, show_comments: bool,
} }
type BorderCharsSet = [[&'static str; 4]; 3];
pub struct IndentStr<'a>(&'a str, ColoredString);
impl Display for IndentStr<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.0, self.1)
}
}
impl CheckErrorHRConfig {
pub const BORDER_STYLE_THIN: u8 = 0;
pub const BORDER_STYLE_THICK: u8 = 1;
pub const BORDER_STYLE_DOUBLE: u8 = 2;
pub const STYLE_DEFAULT: u8 = Self::BORDER_STYLE_THIN;
pub const STYLE_DIFFSRC: u8 = Self::BORDER_STYLE_THICK;
const CHARS_HORIZONTAL: [&'static str; 3] = ["", "", ""];
const CHARS: [BorderCharsSet; 3] = [
[
["", "", "", ""],
["", "", "", ""],
["", "", "", ""],
],
[
["", "", "", ""],
["", "", "", ""],
["", "", "", ""],
],
[
["", "", "", ""],
["", "", "", ""],
["", "", "", ""],
],
];
fn color(&self, s: &str) -> ColoredString {
match self.color_index % 8 {
0 => s.bright_white(),
1 => s.bright_green(),
2 => s.bright_purple(),
3 => s.bright_cyan(),
4 => s.bright_red(),
5 => s.bright_yellow(),
6 => s.bright_magenta(),
_ => s.bright_blue(),
}
}
pub fn indent_start(&self, right: bool) -> IndentStr {
IndentStr(
&self.idt_start,
self.color(
Self::CHARS[self.style as usize][0][self.is_inner as usize * 2 + right as usize],
),
)
}
pub fn indent_default(&self, right: bool) -> IndentStr {
IndentStr(
&self.idt_default,
self.color(Self::CHARS[self.style as usize][1][right as usize]),
)
}
pub fn indent_end(&self, right: bool) -> IndentStr {
IndentStr(
&self.idt_end,
self.color(
Self::CHARS[self.style as usize][2][self.is_inner as usize * 2 + right as usize],
),
)
}
pub fn indent_single(&self, right: bool) -> IndentStr {
IndentStr(
&self.idt_single,
self.color(if self.is_inner {
if right {
Self::CHARS_HORIZONTAL[2] // left+right
} else {
Self::CHARS_HORIZONTAL[1] // left only
}
} else {
if right {
Self::CHARS_HORIZONTAL[0] // right only
} else {
Self::CHARS_HORIZONTAL[1] // left only (so that there is something)
}
}),
)
}
pub fn for_inner(&self, is_first: bool, is_last: bool, style: u8) -> Self {
let color_index = self
.color_index_ptr
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Self {
color_index_ptr: self.color_index_ptr.clone(),
color_index,
is_inner: true,
style,
idt_start: if is_first {
self.indent_start(true)
} else {
self.indent_default(true)
}
.to_string(),
idt_default: self.indent_default(false).to_string(),
idt_end: if is_last {
self.indent_end(true)
} else {
self.indent_default(true)
}
.to_string(),
idt_single: match (is_first, is_last) {
(false, false) => self.indent_default(true),
(false, true) => self.indent_end(true),
(true, false) => self.indent_start(true),
(true, true) => self.indent_single(true),
}
.to_string(),
show_comments: self.show_comments,
}
}
}
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub struct CheckErrorDisplay<'a> { pub struct CheckErrorDisplay<'a> {
e: &'a CheckError, e: &'a CheckError,
@ -122,10 +244,14 @@ impl Display for CheckErrorDisplay<'_> {
self.e.human_readable( self.e.human_readable(
f, f,
&CheckErrorHRConfig { &CheckErrorHRConfig {
color_index: Rc::new(AtomicUsize::new(0)), color_index: 0,
indent_start: String::new(), color_index_ptr: Rc::new(AtomicUsize::new(1)),
indent_default: String::new(), is_inner: false,
indent_end: String::new(), style: CheckErrorHRConfig::STYLE_DEFAULT,
idt_start: String::new(),
idt_default: String::new(),
idt_end: String::new(),
idt_single: String::new(),
show_comments: self.show_comments, show_comments: self.show_comments,
}, },
) )
@ -182,18 +308,21 @@ impl CheckError {
f: &mut std::fmt::Formatter<'_>, f: &mut std::fmt::Formatter<'_>,
cfg: &CheckErrorHRConfig, cfg: &CheckErrorHRConfig,
) -> std::fmt::Result { ) -> std::fmt::Result {
const ADD_RIGHT_BITS: bool = false;
use crate::parsing::SourceFrom; use crate::parsing::SourceFrom;
let len = self.0.len(); let len = self.0.len();
for (i, component) in self.0.iter().enumerate() { for (i, component) in self.0.iter().enumerate() {
let is_first = i == 0;
let is_last = i + 1 == len;
let i = (); // to see if we use `i` anywhere else
macro_rules! indent { macro_rules! indent {
($s:expr, $e:expr) => { ($s:expr, $e:expr, $right:expr) => {
if $e && i + 1 == len { match ($s && is_first, $e && is_last) {
&cfg.indent_end (false, false) => cfg.indent_default($right),
} else if $s && i == 0 { (false, true) => cfg.indent_end($right),
&cfg.indent_start (true, false) => cfg.indent_start($right),
} else { (true, true) => cfg.indent_single($right),
&cfg.indent_default
} }
}; };
} }
@ -202,23 +331,17 @@ impl CheckError {
let lines = msg.lines().collect::<Vec<_>>(); let lines = msg.lines().collect::<Vec<_>>();
let lc = lines.len(); let lc = lines.len();
for (i, line) in lines.into_iter().enumerate() { for (i, line) in lines.into_iter().enumerate() {
writeln!(f, "{}{line}", indent!(i == 0, i + 1 == lc))? let s = i == 0;
let e = i + 1 == lc;
writeln!(f, "{}{line}", indent!(s, e, s && ADD_RIGHT_BITS))?
} }
} }
CheckErrorComponent::Error(err) => { CheckErrorComponent::Error(err) => {
let clr = Self::get_color(&cfg.color_index); let cfg = cfg.for_inner(is_first, is_last, CheckErrorHRConfig::STYLE_DEFAULT);
let mut cfg = cfg.clone();
cfg.indent_start.push_str(&clr("").to_string());
cfg.indent_default.push_str(&clr("").to_string());
cfg.indent_end.push_str(&clr("").to_string());
err.human_readable(f, &cfg)?; err.human_readable(f, &cfg)?;
} }
CheckErrorComponent::ErrorWithDifferentSource(err) => { CheckErrorComponent::ErrorWithDifferentSource(err) => {
let clr = Self::get_color(&cfg.color_index); let cfg = cfg.for_inner(is_first, is_last, CheckErrorHRConfig::STYLE_DIFFSRC);
let mut cfg = cfg.clone();
cfg.indent_start.push_str(&clr("").bold().to_string());
cfg.indent_default.push_str(&clr("").bold().to_string());
cfg.indent_end.push_str(&clr("").bold().to_string());
err.human_readable(f, &cfg)?; err.human_readable(f, &cfg)?;
} }
CheckErrorComponent::Source(highlights) => { CheckErrorComponent::Source(highlights) => {
@ -276,10 +399,10 @@ impl CheckError {
if first_line_nr == last_line_nr { if first_line_nr == last_line_nr {
writeln!( writeln!(
f, f,
"{}", "{}{}",
indent!(true, false, ADD_RIGHT_BITS),
format!( format!(
"{}Line {first_line_nr} ({}..{}){}", "Line {first_line_nr} ({}..{}){}",
indent!(true, false),
start_with_comments + 1 - first_line_start, start_with_comments + 1 - first_line_start,
end_with_comments - last_line_start, end_with_comments - last_line_start,
src_from, src_from,
@ -289,10 +412,10 @@ impl CheckError {
} else { } else {
writeln!( writeln!(
f, f,
"{}", "{}{}",
indent!(true, false, ADD_RIGHT_BITS),
format!( format!(
"{}Lines {first_line_nr}-{last_line_nr} ({}..{}){}", "Lines {first_line_nr}-{last_line_nr} ({}..{}){}",
indent!(true, false),
start_with_comments + 1 - first_line_start, start_with_comments + 1 - first_line_start,
end_with_comments - last_line_start, end_with_comments - last_line_start,
src_from, src_from,
@ -313,16 +436,18 @@ impl CheckError {
let line = line.as_str(); let line = line.as_str();
let mut line_printed = false; let mut line_printed = false;
let mut right = 0; let mut right = 0;
for (pos, color) in highlights { for (highlight_index, (highlight_pos, color)) in
highlights.iter().enumerate()
{
if let Some(color) = color { if let Some(color) = color {
let (highlight_start, highlight_end) = if cfg.show_comments let (highlight_start, highlight_end) = if cfg.show_comments
{ {
( (
src.pos_in_og(pos.start.pos(), true), src.pos_in_og(highlight_pos.start.pos(), true),
src.pos_in_og(pos.end.pos(), false), src.pos_in_og(highlight_pos.end.pos(), false),
) )
} else { } else {
(pos.start.pos(), pos.end.pos()) (highlight_pos.start.pos(), highlight_pos.end.pos())
}; };
let highlight_start = highlight_start - start; let highlight_start = highlight_start - start;
let highlight_end = highlight_end - start; let highlight_end = highlight_end - start;
@ -330,7 +455,11 @@ impl CheckError {
{ {
if !line_printed { if !line_printed {
// this isn't the last line (important for indent) // this isn't the last line (important for indent)
writeln!(f, "{} {line}", indent!(false, false))?; writeln!(
f,
"{} {line}",
indent!(false, false, false)
)?;
line_printed = true; line_printed = true;
} }
// where the highlight starts in this line // where the highlight starts in this line
@ -350,7 +479,35 @@ impl CheckError {
let hl_len = hl_len.min(line.len() - right); let hl_len = hl_len.min(line.len() - right);
right += hl_space + hl_len; right += hl_space + hl_len;
if print_indent && right != 0 { if print_indent && right != 0 {
write!(f, "{} ", indent!(false, false))?; write!(
f,
"{} ",
indent!(
false,
// is end if last_line and
// all following highlights can be put on this line
last_line
&& highlights
.iter()
.skip(highlight_index + 1)
.try_fold(
// accumulator = end position of previous highlight
highlight_pos.end().pos(),
// success if all highlights start only after the previous highlight ended: a < hl.start
|a, hl| if a < hl
.0
.start()
.pos()
{
Some(hl.0.end().pos())
} else {
None
}
)
.is_some(),
false
)
)?;
} }
write!( write!(
f, f,
@ -363,7 +520,7 @@ impl CheckError {
} }
if !line_printed { if !line_printed {
// may be last line (important for indent) // may be last line (important for indent)
writeln!(f, "{} {line}", indent!(false, last_line))?; writeln!(f, "{} {line}", indent!(false, last_line, false))?;
} }
if right != 0 { if right != 0 {
writeln!(f)?; writeln!(f)?;
@ -376,19 +533,6 @@ impl CheckError {
} }
Ok(()) Ok(())
} }
fn get_color(i: &AtomicUsize) -> impl Fn(&str) -> ColoredString {
let i = i.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
move |s| match i % 8 {
0 => s.bright_white(),
1 => s.bright_green(),
2 => s.bright_purple(),
3 => s.bright_cyan(),
4 => s.bright_red(),
5 => s.bright_yellow(),
6 => s.bright_magenta(),
_ => s.bright_blue(),
}
}
} }
impl From<String> for CheckError { impl From<String> for CheckError {
fn from(value: String) -> Self { fn from(value: String) -> Self {

View File

@ -13,6 +13,7 @@ impl Config {
/// `minus: fn` returns the first number minus all the others /// `minus: fn` returns the first number minus all the others
/// `product: fn` returns the product of all the numbers in the tuple /// `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. /// `div: fn` returns a / b. Performs integer division if a and b are both integers.
/// `pow: fn` returns a^b or a**b.
/// `modulo: fn` returns a % b /// `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) /// `signum: fn` returns 1 for positive numbers, -1 for negative ones and 0 for 0 (always returns an Int, even when input is Float)
/// `lt: fn` returns true if the input keeps increasing, that is, for (a, b), a < b, for (a, b, c), a < b < c, and so on. /// `lt: fn` returns true if the input keeps increasing, that is, for (a, b), a < b, for (a, b, c), a < b < c, and so on.
@ -27,44 +28,55 @@ impl Config {
/// `ceil: 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 /// `floor: fn` rounds the float [?] and returns an int
pub fn with_math(self) -> Self { pub fn with_math(self) -> Self {
self self.add_var(
.add_var("lt".to_string(), Data::new(ltgtoe_function("lt".to_string(), |l, r| match (l, r) { "lt".to_string(),
Data::new(ltgtoe_function("lt".to_string(), |l, r| match (l, r) {
(IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l < r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l < r,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) < r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) < r,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l < r as f64, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l < r as f64,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l < r, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l < r,
}))) })),
.add_var("gt".to_string(), Data::new(ltgtoe_function("gt".to_string(), |l, r| match (l, r) { )
.add_var(
"gt".to_string(),
Data::new(ltgtoe_function("gt".to_string(), |l, r| match (l, r) {
(IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l > r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l > r,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) > r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) > r,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l > r as f64, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l > r as f64,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l > r, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l > r,
}))) })),
.add_var("ltoe".to_string(), Data::new(ltgtoe_function("ltoe".to_string(), |l, r| match (l, r) { )
.add_var(
"ltoe".to_string(),
Data::new(ltgtoe_function("ltoe".to_string(), |l, r| match (l, r) {
(IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l <= r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l <= r,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) <= r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) <= r,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l <= r as f64, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l <= r as f64,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l <= r, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l <= r,
}))) })),
.add_var("gtoe".to_string(), Data::new(ltgtoe_function("gtoe".to_string(), |l, r| match (l, r) { )
.add_var(
"gtoe".to_string(),
Data::new(ltgtoe_function("gtoe".to_string(), |l, r| match (l, r) {
(IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l >= r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l >= r,
(IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) >= r, (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Float(r)) => (l as f64) >= r,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l >= r as f64, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Int(r)) => l >= r as f64,
(IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l >= r, (IntOrFloatOrNothing::Float(l), IntOrFloatOrNothing::Float(r)) => l >= r,
}))) })),
.add_var("parse_float".to_string(), Data::new(data::function::Function { )
.add_var(
"parse_float".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()), info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, _i| { out: Arc::new(|a, _i| {
if a.is_included_in(&Type::new(data::string::StringT)) { if a.is_included_in(&Type::new(data::string::StringT)) {
Ok(Type::newm(vec![ Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![ Arc::new(data::tuple::TupleT(vec![Type::new(data::float::FloatT)])),
Type::new(data::float::FloatT),
])),
Arc::new(data::tuple::TupleT(vec![])), Arc::new(data::tuple::TupleT(vec![])),
])) ]))
} else { } else {
@ -72,22 +84,33 @@ impl Config {
} }
}), }),
run: Arc::new(|a, _i| { run: Arc::new(|a, _i| {
Ok(if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() { Ok(
if let Ok(n) = a
.get()
.as_any()
.downcast_ref::<data::string::String>()
.unwrap()
.0
.parse()
{
Data::one_tuple(Data::new(data::float::Float(n))) Data::one_tuple(Data::new(data::float::Float(n)))
} else { } else {
Data::empty_tuple() Data::empty_tuple()
}) },
)
}), }),
inner_statements: None, inner_statements: None,
})).add_var("parse_int".to_string(), Data::new(data::function::Function { }),
)
.add_var(
"parse_int".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()), info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, _i| { out: Arc::new(|a, _i| {
if a.is_included_in(&Type::new(data::string::StringT)) { if a.is_included_in(&Type::new(data::string::StringT)) {
Ok(Type::newm(vec![ Ok(Type::newm(vec![
Arc::new(data::tuple::TupleT(vec![ Arc::new(data::tuple::TupleT(vec![Type::new(data::int::IntT)])),
Type::new(data::int::IntT),
])),
Arc::new(data::tuple::TupleT(vec![])), Arc::new(data::tuple::TupleT(vec![])),
])) ]))
} else { } else {
@ -95,279 +118,193 @@ impl Config {
} }
}), }),
run: Arc::new(|a, _i| { run: Arc::new(|a, _i| {
Ok(if let Ok(n) = a.get().as_any().downcast_ref::<data::string::String>().unwrap().0.parse() { Ok(
if let Ok(n) = a
.get()
.as_any()
.downcast_ref::<data::string::String>()
.unwrap()
.0
.parse()
{
Data::one_tuple(Data::new(data::int::Int(n))) Data::one_tuple(Data::new(data::int::Int(n)))
} else { } else {
Data::empty_tuple() Data::empty_tuple()
}) },
)
}), }),
inner_statements: None, inner_statements: None,
})).add_var("signum".to_string(), Data::new(data::function::Function { }),
)
.add_var(
"signum".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()), info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, _i| { out: Arc::new(|a, _i| {
if a.is_included_in(&Type::newm(vec![Arc::new(data::int::IntT), Arc::new(data::float::FloatT)])) { if a.is_included_in(&Type::newm(vec![
Arc::new(data::int::IntT),
Arc::new(data::float::FloatT),
])) {
Ok(Type::new(data::int::IntT)) Ok(Type::new(data::int::IntT))
} else { } else {
Err(format!("signum called on non-number type").into()) Err(format!("signum called on non-number type").into())
} }
}), }),
run: Arc::new(|a, _i| { run: Arc::new(|a, _i| {
Ok(Data::new(data::int::Int(if let Some(n) = a.get().as_any().downcast_ref::<data::int::Int>() { Ok(Data::new(data::int::Int(
if let Some(n) = a.get().as_any().downcast_ref::<data::int::Int>() {
n.0.signum() n.0.signum()
} else } else if let Some(n) =
if let Some(n) = a.get().as_any().downcast_ref::<data::float::Float>() { a.get().as_any().downcast_ref::<data::float::Float>()
{
if n.0 > 0.0 { if n.0 > 0.0 {
1 1
} else if n.0 < 0.0 { } else if n.0 < 0.0 {
-1 -1
} else { 0 } else {
0
} }
} else { return Err("called signum on non-number type".into()); }))) } else {
return Err("called signum on non-number type".into());
},
)))
}), }),
inner_statements: None, inner_statements: None,
})) .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())), .add_var(
out: Arc::new(|a, _i| two_tuple_to_num(a, "div")), "div".to_string(),
run: Arc::new(|a, _i| if let Some(t) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() { Data::new(two_num_tuple_to_num(
let left = t.0[0].get(); "div",
let right = t.0[1].get(); |l, r| {
let (left, right) = (left.as_any(), right.as_any()); l.checked_div(r)
match (left.downcast_ref::<data::int::Int>(), left.downcast_ref::<data::float::Float>(), .ok_or_else(|| CheckError::from("attempted to divide by zero"))
right.downcast_ref::<data::int::Int>(), right.downcast_ref::<data::float::Float>() },
) { |l, r| Ok(l as f64 / r),
(Some(data::int::Int(l)), None, Some(data::int::Int(r)), None) => Ok(Data::new(data::int::Int(l.checked_div(*r).ok_or_else(|| CheckError::from("attempted to divide by zero"))?))), |l, r| Ok(l / r as f64),
(Some(data::int::Int(l)), None, None, Some(data::float::Float(r))) => Ok(Data::new(data::float::Float(*l as f64 / r))), |l, r| Ok(l / r),
(None, Some(data::float::Float(l)), Some(data::int::Int(r)), None) => Ok(Data::new(data::float::Float(l / *r as f64))), )),
(None, Some(data::float::Float(l)), None, Some(data::float::Float(r))) => Ok(Data::new(data::float::Float(l / r))), )
_ => return Err("at least one of the arguments to div were neither an int nor a float".into()), .add_var(
} "pow".to_string(),
} else { return Err("argument to div was not a tuple".into()); }), Data::new(two_num_tuple_to_num(
inner_statements: None, "pow",
})).add_var("modulo".to_string(), Data::new(data::function::Function { |l, r| Ok(l.pow(r.try_into().unwrap_or(u32::MAX))),
info: Arc::new(program::run::Info::neverused()), |l, r| Ok((l as f64).powf(r)),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |l, r| {
out: Arc::new(|a, _i| two_tuple_to_num(a, "modulo")), Ok(if let Ok(r) = r.try_into() {
run: Arc::new(|a, _i| if let Some(t) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() { l.powi(r)
let left = t.0[0].get(); } else {
let right = t.0[1].get(); l.powf(r as f64)
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>() |l, r| Ok(l.powf(r)),
) { )),
(Some(data::int::Int(l)), None, Some(data::int::Int(r)), None) => Ok(Data::new(data::int::Int(l % r))), )
(Some(data::int::Int(l)), None, None, Some(data::float::Float(r))) => Ok(Data::new(data::float::Float(*l as f64 % r))), .add_var(
(None, Some(data::float::Float(l)), Some(data::int::Int(r)), None) => Ok(Data::new(data::float::Float(l % *r as f64))), "modulo".to_string(),
(None, Some(data::float::Float(l)), None, Some(data::float::Float(r))) => Ok(Data::new(data::float::Float(l % r))), Data::new(two_num_tuple_to_num(
_ => return Err("at least one of the arguments to modulo were neither an int nor a float".into()), "modulo",
} |l, r| {
} else { return Err("argument to modulo was not a tuple".into()) }), l.checked_rem(r).ok_or_else(|| {
inner_statements: None, CheckError::from(
})) "called modulo on two integers, and the second one was zero",
)
})
},
|l, r| Ok(l as f64 % r),
|l, r| Ok(l % r as f64),
|l, r| Ok(l % r),
)),
)
.add_var( .add_var(
"sum".to_string(), "sum".to_string(),
Data::new(data::function::Function { Data::new(num_iter_to_num("sum", Ok(0), |a, v| match (a, v) {
info: Arc::new(program::run::Info::neverused()), (Ok(a), Ok(v)) => Ok(a + v),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), (Ok(a), Err(v)) => Err(a as f64 + v),
out: Arc::new(|a, _i| { (Err(a), Ok(v)) => Err(a + v as f64),
let mut ints = false; (Err(a), Err(v)) => Err(a + v),
let mut floats = false; })),
for a in &a.types {
if let Some(i) = a.iterable() {
if i.types
.iter()
.all(|t| t.as_any().downcast_ref::<data::int::IntT>().is_some())
{
ints = true;
} else if i.types.iter().all(|t| {
t.as_any().downcast_ref::<data::int::IntT>().is_some()
|| t.as_any().downcast_ref::<data::float::FloatT>().is_some()
}) {
floats = true;
} else {
return Err(format!("cannot get sum of iterator over type {i} because it contains types that aren't int/float").into())
}
} else {
return Err(format!(
"cannot get sum of non-iterable type {a}"
).into());
}
}
Ok(match (ints, floats) {
(_, true) => Type::new(data::float::FloatT),
(true, false) => Type::new(data::int::IntT),
(false, false) => Type::empty(),
})
}),
run: Arc::new(|a, _i| {
if let Some(i) = a.get().iterable() {
let mut sumi = 0;
let mut sumf = 0.0;
let mut usef = false;
for val in i {
let val = val?;
let o = if let Some(i) = val.get().as_any().downcast_ref::<data::int::Int>() {
sumi += i.0;
} else if let Some(i) =
val.get().as_any().downcast_ref::<data::float::Float>()
{
sumf += i.0;
usef = true;
};
o
}
Ok(if usef {
Data::new(data::float::Float(sumi as f64 + sumf))
} else {
Data::new(data::int::Int(sumi))
})
} else {
return Err("sum called on non-tuple".into());
}
}),
inner_statements: None,
}),
) )
.add_var( .add_var(
"subtract".to_string(), "subtract".to_string(),
Data::new(data::function::Function { Data::new(two_num_tuple_to_num(
info: Arc::new(program::run::Info::neverused()), "subtract",
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |l, r| Ok(l - r),
out: Arc::new(|a, _i| { |l, r| Ok(l as f64 - r),
let mut ints = false; |l, r| Ok(l - r as f64),
let mut floats = false; |l, r| Ok(l - r),
for a in &a.types { )),
if let Some(i) = a.iterable() {
if i.types
.iter()
.all(|t| t.as_any().downcast_ref::<data::int::IntT>().is_some())
{
ints = true;
} else if i.types.iter().all(|t| {
t.as_any().downcast_ref::<data::int::IntT>().is_some()
|| t.as_any().downcast_ref::<data::float::FloatT>().is_some()
}) {
floats = true;
} else {
return Err(format!("cannot subtract on iterator over type {i} because it contains types that aren't int/float").into())
}
} else {
return Err(format!(
"cannot subtract over non-iterable type {a}"
).into());
}
}
Ok(match (ints, floats) {
(_, true) => Type::new(data::float::FloatT),
(true, false) => Type::new(data::int::IntT),
(false, false) => Type::empty(),
})
}),
run: Arc::new(|a, _i| {
if let Some(i) = a.get().iterable() {
let mut first = true;
let mut sumi = 0;
let mut sumf = 0.0;
let mut usef = false;
for val in i {
let val = val?;
if let Some(i) = val.get().as_any().downcast_ref::<data::int::Int>() {
if first {
sumi = i.0;
} else {
sumi -= i.0;
}
} else if let Some(i) =
val.get().as_any().downcast_ref::<data::float::Float>()
{
if first {
sumf = i.0;
} else {
sumf -= i.0;
}
usef = true;
}
if first {
first = false;
}
}
Ok(if usef {
Data::new(data::float::Float(sumi as f64 + sumf))
} else {
Data::new(data::int::Int(sumi))
})
} else {
return Err("sum called on non-tuple".into());
}
}),
inner_statements: None,
}),
) )
.add_var( .add_var(
"product".to_string(), "product".to_string(),
Data::new(data::function::Function { Data::new(num_iter_to_num("sum", Ok(1), |a, v| match (a, v) {
(Ok(a), Ok(v)) => Ok(a * v),
(Ok(a), Err(v)) => Err(a as f64 * v),
(Err(a), Ok(v)) => Err(a * v as f64),
(Err(a), Err(v)) => Err(a * v),
})),
)
}
}
fn num_iter_to_num(
func_name: &'static str,
init: Result<isize, f64>,
func: impl Fn(Result<isize, f64>, Result<isize, f64>) -> Result<isize, f64> + Send + Sync + 'static,
) -> data::function::Function {
data::function::Function {
info: Arc::new(program::run::Info::neverused()), info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, _i| { out: Arc::new(move |a, _i| {
let mut ints = false; if let Some(a) = a.iterable() {
let mut floats = false; let int_type = Type::new(data::int::IntT);
for a in &a.types { if a.is_included_in(&int_type) {
if let Some(i) = a.iterable() { Ok(int_type)
if i.types
.iter()
.all(|t| t.as_any().downcast_ref::<data::int::IntT>().is_some())
{
ints = true;
} else if i.types.iter().all(|t| {
t.as_any().downcast_ref::<data::int::IntT>().is_some()
|| t.as_any().downcast_ref::<data::float::FloatT>().is_some()
}) {
floats = true;
} else { } else {
return Err(format!("cannot get product of iterator over type {i} because it contains types that aren't int/float").into()) let float_type = Type::new(data::float::FloatT);
if a.is_included_in(&float_type) {
Ok(float_type)
} else {
let int_float_type = Type::newm(vec![
Arc::new(data::int::IntT),
Arc::new(data::float::FloatT),
]);
if a.is_included_in(&int_float_type) {
Ok(int_float_type)
} else {
Err(format!("argument passed to {func_name} must be an iterator over values of type Int/String, but was an iterator over values of type {a}.").into())
}
}
} }
} else { } else {
return Err(format!( Err(format!("argument passed to {func_name} must be an iterator").into())
"cannot get product of non-iterable type {a}"
).into());
} }
}
Ok(match (ints, floats) {
(_, true) => Type::new(data::float::FloatT),
(true, false) => Type::new(data::int::IntT),
(false, false) => Type::empty(),
})
}), }),
run: Arc::new(|a, _i| { run: Arc::new(move |a, _i| {
if let Some(i) = a.get().iterable() { let mut out = init;
let mut prodi = 1; for v in a.get().iterable().unwrap() {
let mut prodf = 1.0; let v = v?;
let mut usef = false; let v = v.get();
for val in i { let v = v.as_any();
let val = val?; let v = v
let o = if let Some(i) = val.get().as_any().downcast_ref::<data::int::Int>() { .downcast_ref::<data::int::Int>()
prodi *= i.0; .map(|v| Ok(v.0))
} else if let Some(i) = .unwrap_or_else(|| {
val.get().as_any().downcast_ref::<data::float::Float>() Err(v
{ .downcast_ref::<data::float::Float>()
prodf *= i.0; .expect("value used in num-iterator function was not a number")
usef = true; .0)
}; });
o out = func(out, v);
} }
Ok(if usef { Ok(match out {
Data::new(data::float::Float(prodi as f64 * prodf)) Ok(v) => Data::new(data::int::Int(v)),
} else { Err(v) => Data::new(data::float::Float(v)),
Data::new(data::int::Int(prodi))
}) })
} else {
return Err("product called on non-tuple".into());
}
}), }),
inner_statements: None, inner_statements: None,
}),
)
} }
} }
@ -375,7 +312,24 @@ impl Config {
/// (int, float) -> float /// (int, float) -> float
/// (float, int) -> float /// (float, int) -> float
/// (float, float) -> float /// (float, float) -> float
fn two_tuple_to_num(a: &Type, func_name: &str) -> Result<Type, CheckError> { fn two_num_tuple_to_num(
func_name: &'static str,
func_ii: impl Fn(isize, isize) -> Result<isize, CheckError> + Send + Sync + 'static,
func_if: impl Fn(isize, f64) -> Result<f64, CheckError> + Send + Sync + 'static,
func_fi: impl Fn(f64, isize) -> Result<f64, CheckError> + Send + Sync + 'static,
func_ff: impl Fn(f64, f64) -> Result<f64, CheckError> + Send + Sync + 'static,
) -> data::function::Function {
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_impl_check(a, func_name)),
run: Arc::new(move |a, _i| {
two_tuple_to_num_impl_run(a, func_name, &func_ii, &func_if, &func_fi, &func_ff)
}),
inner_statements: None,
}
}
fn two_tuple_to_num_impl_check(a: &Type, func_name: &str) -> Result<Type, CheckError> {
let mut float = false; let mut float = false;
for t in &a.types { for t in &a.types {
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() { if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
@ -403,6 +357,49 @@ fn two_tuple_to_num(a: &Type, func_name: &str) -> Result<Type, CheckError> {
Type::new(data::int::IntT) Type::new(data::int::IntT)
}) })
} }
fn two_tuple_to_num_impl_run(
a: Data,
func_name: &'static str,
func_ii: &(impl Fn(isize, isize) -> Result<isize, CheckError> + Send + Sync),
func_if: &(impl Fn(isize, f64) -> Result<f64, CheckError> + Send + Sync),
func_fi: &(impl Fn(f64, isize) -> Result<f64, CheckError> + Send + Sync),
func_ff: &(impl Fn(f64, f64) -> Result<f64, CheckError> + Send + Sync),
) -> Result<Data, CheckError> {
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());
Ok(
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(func_ii(*l, *r)?))
}
(Some(data::int::Int(l)), None, None, Some(data::float::Float(r))) => {
Data::new(data::float::Float(func_if(*l, *r)?))
}
(None, Some(data::float::Float(l)), Some(data::int::Int(r)), None) => {
Data::new(data::float::Float(func_fi(*l, *r)?))
}
(None, Some(data::float::Float(l)), None, Some(data::float::Float(r))) => {
Data::new(data::float::Float(func_ff(*l, *r)?))
}
_ => {
return Err(format!(
"at least one of the arguments to {func_name} were neither an int nor a float"
)
.into())
}
},
)
} else {
return Err(format!("argument to {func_name} was not a tuple").into());
}
}
fn ltgtoe_function( fn ltgtoe_function(
func_name: String, func_name: String,

View File

@ -96,32 +96,15 @@ impl MersStatement for Chain {
match func.run(f) { match func.run(f) {
Ok(v) => Ok(v), Ok(v) => Ok(v),
Err(e) => Err(if let Some(_) = &self.as_part_of_include { Err(e) => Err(if let Some(_) = &self.as_part_of_include {
CheckError::new() CheckError::new().err_with_diff_src(e).src(vec![(
.src(vec![(
self.pos_in_src.clone(), self.pos_in_src.clone(),
Some(error_colors::HashIncludeErrorInIncludedFile), Some(error_colors::StacktraceDescendHashInclude),
)]) )])
.msg(
"Error in #include:"
.color(error_colors::HashIncludeErrorInIncludedFile)
.to_string(),
)
.err_with_diff_src(e)
} else { } else {
CheckError::new() CheckError::new().err(e).src(vec![
.src(vec![
(self.pos_in_src.clone(), None), (self.pos_in_src.clone(), None),
( (self.source_range(), Some(error_colors::StacktraceDescend)),
self.first.source_range(),
Some(error_colors::FunctionArgument),
),
(self.chained.source_range(), Some(error_colors::Function)),
]) ])
.msg(format!(
"Error in {}:",
"this function".color(error_colors::Function)
))
.err(e)
}), }),
} }
} else { } else {