mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
nicer errors
This commit is contained in:
parent
b11e4017ed
commit
688e28c171
14
TODO.md
Normal file
14
TODO.md
Normal 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
12
learn_mers/Cargo.toml
Normal 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
65
learn_mers/src/main.rs
Normal 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
202
learn_mers/src/tasks.rs
Normal 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
4
main.mers
Normal 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
|
@ -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
19
mers/RuntimeErrors.md
Normal 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)
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user