diff --git a/mers_lib/examples/02_own_variable.rs b/mers_lib/examples/02_own_variable.rs deleted file mode 100644 index 2fa302e..0000000 --- a/mers_lib/examples/02_own_variable.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::sync::Arc; - -use mers_lib::{ - data::{self, Data, Type}, - errors::CheckError, - prelude_compile::{parse, Config, Source}, - program::parsed::CompInfo, -}; - -fn main() { - eprintln!("This is valid:"); - run("my_custom_var.debug.rust_func.debug".to_owned()).unwrap(); - eprintln!(); - - eprintln!("This is not:"); - let e = run("5.rust_func".to_owned()).err().unwrap(); - eprintln!("{e:?}"); -} -fn run(src: String) -> Result<(), CheckError> { - let mut source = Source::new_from_string(src); - let srca = Arc::new(source.clone()); - let parsed = parse(&mut source, &srca)?; - - // Add our custom variables to the `Config` - let (mut i1, mut i2, mut i3) = Config::new() - .bundle_std() - .add_var( - "my_custom_var".to_owned(), - Data::new(data::string::String(format!("my custom value!"))), - ) - .add_var( - "rust_func".to_owned(), - Data::new(data::function::Function::new_generic( - |arg| { - // If the input is a string, the output is a string. - // Otherwise, the function is used incorrectly. - if arg.is_included_in_single(&data::string::StringT) { - Ok(Type::new(data::string::StringT)) - } else { - // Wrong argument type. The code won't compile and this is the error message shown to the user. - Err(format!("Can't call rust_func with non-string argument {arg}!").into()) - } - }, - |arg| { - let arg = arg.get(); - let arg = &arg - .as_any() - .downcast_ref::() - .unwrap() - .0; - Ok(Data::new(data::string::String(arg.chars().rev().collect()))) - }, - )), - ) - .infos(); - - let compiled = parsed.compile(&mut i1, CompInfo::default())?; - compiled.check(&mut i3, None)?; - compiled.run(&mut i2)?; - Ok(()) -} diff --git a/mers_lib/src/data/function.rs b/mers_lib/src/data/function.rs index fefeb29..768d44c 100755 --- a/mers_lib/src/data/function.rs +++ b/mers_lib/src/data/function.rs @@ -40,25 +40,25 @@ impl Clone for Function { impl Function { pub fn new_static( out: Vec<(Type, Type)>, - run: impl Fn(Data) -> Result + Send + Sync + 'static, + run: impl Fn(Data, &mut Info) -> Result + Send + Sync + 'static, ) -> Self { Self { info: crate::info::Info::neverused(), info_check: Arc::new(Mutex::new(crate::info::Info::neverused())), out: Err(Arc::new(out)), - run: Arc::new(move |a, _| run(a)), + run: Arc::new(run), inner_statements: None, } } pub fn new_generic( out: impl Fn(&Type) -> Result + Send + Sync + 'static, - run: impl Fn(Data) -> Result + Send + Sync + 'static, + run: impl Fn(Data, &mut Info) -> Result + Send + Sync + 'static, ) -> Self { Self { info: crate::info::Info::neverused(), info_check: Arc::new(Mutex::new(crate::info::Info::neverused())), out: Ok(Arc::new(move |a, _| out(a))), - run: Arc::new(move |a, _| run(a)), + run: Arc::new(run), inner_statements: None, } } diff --git a/mers_lib/src/program/configs/gen/function.rs b/mers_lib/src/program/configs/gen/function.rs index ff5c54f..c5be0db 100644 --- a/mers_lib/src/program/configs/gen/function.rs +++ b/mers_lib/src/program/configs/gen/function.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{marker::PhantomData, sync::Arc}; use crate::{ data::{self, Data, MersData, Type}, @@ -7,8 +7,23 @@ use crate::{ use super::{FromMersData, ToMersData}; +pub fn fun( + f: fn(I, &mut crate::program::run::Info) -> Result, +) -> impl StaticMersFunc { + Box::new(f) +} pub fn func( - f: fn(I) -> Result, + f: fn(I, &mut crate::program::run::Info) -> Result, +) -> data::function::Function { + Box::new(f).mers_func() +} +pub fn func_end( + f: fn(I, &mut crate::program::run::Info) -> !, +) -> data::function::Function { + Box::new(f).mers_func() +} +pub fn func_err( + f: fn(I, &mut crate::program::run::Info) -> CheckError, ) -> data::function::Function { Box::new(f).mers_func() } @@ -31,10 +46,14 @@ pub fn func( pub trait StaticMersFunc: Sized + 'static + Send + Sync { fn types() -> Vec<(Type, Type)>; - fn run(&self, a: &(impl MersData + ?Sized)) -> Option>; + fn run( + &self, + a: &(impl MersData + ?Sized), + info: &mut crate::program::run::Info, + ) -> Option>; fn mers_func(self) -> data::function::Function { - data::function::Function::new_static(Self::types(), move |a| { - match self.run(a.get().as_ref()) { + data::function::Function::new_static(Self::types(), move |a, i| { + match self.run(a.get().as_ref(), i) { Some(Ok(v)) => Ok(v), Some(Err(e)) => Err(e), None => Err(CheckError::from(format!( @@ -52,13 +71,55 @@ pub struct TwoFuncs(pub A, pub B); pub trait Func: Send + Sync + 'static { type I: FromMersData; type O: ToMersData; - fn run_func(&self, i: Self::I) -> Result; + fn run_func( + &self, + i: Self::I, + info: &mut crate::program::run::Info, + ) -> Result; } -impl Func for fn(I) -> Result { +impl Func + for fn(I, &mut crate::program::run::Info) -> Result +{ type I = I; type O = O; - fn run_func(&self, i: Self::I) -> Result { - self(i) + fn run_func( + &self, + i: Self::I, + info: &mut crate::program::run::Info, + ) -> Result { + self(i, info) + } +} + +pub struct UnreachableDontConstruct(PhantomData); +impl ToMersData for UnreachableDontConstruct { + fn as_type_to() -> Type { + Type::empty() + } + fn represent(self) -> Data { + unreachable!() + } +} +impl Func for fn(I, &mut crate::program::run::Info) -> ! { + type I = I; + type O = UnreachableDontConstruct; + fn run_func( + &self, + i: Self::I, + info: &mut crate::program::run::Info, + ) -> Result { + self(i, info); + } +} +impl Func for fn(I, &mut crate::program::run::Info) -> CheckError { + type I = I; + type O = UnreachableDontConstruct; + fn run_func( + &self, + i: Self::I, + info: &mut crate::program::run::Info, + ) -> Result { + Err(self(i, info)) } } @@ -66,8 +127,14 @@ impl StaticMersFunc for Box { fn types() -> Vec<(Type, Type)> { vec![(F::I::as_type_from(), F::O::as_type_to())] } - fn run(&self, a: &(impl MersData + ?Sized)) -> Option> { - F::I::try_represent(a, |v| v.map(|v| self.run_func(v).map(|v| v.represent()))) + fn run( + &self, + a: &(impl MersData + ?Sized), + info: &mut crate::program::run::Info, + ) -> Option> { + F::I::try_represent(a, |v| { + v.map(|v| self.run_func(v, info).map(|v| v.represent())) + }) } } @@ -84,7 +151,11 @@ impl StaticMersFunc for TwoFuncs { } o } - fn run(&self, a: &(impl MersData + ?Sized)) -> Option> { - self.0.run(a).or_else(|| self.1.run(a)) + fn run( + &self, + a: &(impl MersData + ?Sized), + info: &mut crate::program::run::Info, + ) -> Option> { + self.0.run(a, info).or_else(|| self.1.run(a, info)) } } diff --git a/mers_lib/src/program/configs/util.rs b/mers_lib/src/program/configs/util.rs index 8bd077a..2f56490 100644 --- a/mers_lib/src/program/configs/util.rs +++ b/mers_lib/src/program/configs/util.rs @@ -41,7 +41,7 @@ pub fn to_mers_func_with_in_out_types( out_type: Type, run: impl Fn(Data) -> Result + Send + Sync + 'static, ) -> data::function::Function { - data::function::Function::new_static(vec![(in_type, out_type)], run) + data::function::Function::new_static(vec![(in_type, out_type)], move |a, _| run(a)) } pub fn to_mers_func_concrete_string_to_any( diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index de571bf..d121110 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -9,23 +9,24 @@ use crate::{ program::run::{CheckInfo, Info}, }; -use super::Config; +use super::{ + gen::{ + function::{func, func_end, func_err}, + OneOf, + }, + Config, +}; impl Config { /// `deref: fn` clones the value from a reference /// `mkref: fn` returns a reference to a copy of the value /// `eq: fn` returns true if all the values are equal, otherwise false. - /// `loop: fn` runs a function until it returns (T) instead of (), then returns T. Also works with ((), f) instead of f for ().loop(() -> { ... }) syntax, which may be more readable - /// `try: fn` runs the first valid function with the argument. usage: (arg, (f1, f2, f3)).try - /// NOTE: try's return type may miss some types that can actually happen when using it on tuples, so... don't do ((a, b), (f1, any -> ())).try unless f1 also returns () /// `len: fn` gets the length of strings or tuples /// `sleep: fn` sleeps for n seconds (pauses the current thread) /// `panic: fn` exits the program with the given exit code /// `lock_update: fn` locks the value of a reference so you can exclusively modify it: &var.lock_update(v -> (v, 1).sum) pub fn with_base(self) -> Self { self - // .add_var("try".to_string(), get_try(false)) - // .add_var("try_allow_unused".to_string(), get_try(true)) .add_var("lock_update".to_string(), Data::new(data::function::Function { info: Info::neverused(), info_check: Arc::new(Mutex::new(CheckInfo::neverused())), @@ -74,68 +75,24 @@ impl Config { }), inner_statements: None, })) - .add_var("sleep".to_string(), Data::new(data::function::Function { - info: Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(Arc::new(|a, _i| if a.is_included_in(&Type::newm(vec![ - Arc::new(data::int::IntT), - Arc::new(data::float::FloatT), - ])) { - Ok(Type::empty_tuple()) - } else { - Err(format!("cannot call sleep with non-int or non-float argument.").into()) - })), - run: Arc::new(|a, i| { - let a = a.get(); - let mut sleep_dur = if let Some(data::int::Int(n)) = a.as_any().downcast_ref() { - Duration::from_secs(*n as _) - } else if let Some(data::float::Float(n)) = a.as_any().downcast_ref() { - Duration::from_secs_f64(*n) - } else { - return Err("sleep called on non-int/non-float".into()); - }; - // limit how long sleep can take - if let Some(cutoff) = i.global.limit_runtime { - sleep_dur = sleep_dur.min(cutoff.saturating_duration_since(Instant::now())); - } - std::thread::sleep(sleep_dur); - Ok(Data::empty_tuple()) - }), - inner_statements: None, - })) - .add_var("exit".to_string(), Data::new(data::function::Function { - info: Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(Arc::new(|a, _i| if a.is_included_in_single(&data::int::IntT) { - Ok(Type::empty()) - } else { - Err(format!("cannot call exit with non-int argument").into()) - })), - run: Arc::new(|a, _i| { - std::process::exit(a.get().as_any().downcast_ref::().map(|i| i.0 as _).unwrap_or(1)); - }), - inner_statements: None, - })) - .add_var("panic".to_string(), Data::new(data::function::Function { - info: Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(Arc::new(|a, _i| if a.is_included_in_single(&data::string::StringT) { - Ok(Type::empty()) - } else { - Err(format!("cannot call panic with non-string argument").into()) - })), - run: Arc::new(|a, _i| { - Err( - a - .get() - .as_any() - .downcast_ref::() - .map(|i| i.0.to_owned()) - .unwrap_or_else(String::new).into() - ) - }), - inner_statements: None, - })) + .add_var("sleep".to_owned(), Data::new(func(|dur: OneOf, i| { + let mut sleep_dur = match dur { + OneOf::A(dur) => Duration::from_secs(dur.max(0).try_into().unwrap_or(u64::MAX)), + OneOf::B(dur) => Duration::from_secs_f64(dur.max(0.0)), + }; + // limit how long sleep can take + if let Some(cutoff) = i.global.limit_runtime { + sleep_dur = sleep_dur.min(cutoff.saturating_duration_since(Instant::now())); + } + std::thread::sleep(sleep_dur); + Ok(()) + }))) + .add_var("exit".to_string(), Data::new(func_end(|code: isize, _| { + std::process::exit(code.try_into().unwrap_or(255)); + }))) + .add_var("panic".to_string(), Data::new(func_err(|message: &str, _| { + CheckError::from(message) + }))) .add_var( "len".to_string(), Data::new(data::function::Function { @@ -155,8 +112,8 @@ impl Config { } else if let Some(s) = a.get().as_any().downcast_ref::() { s.0.len() as _ } else if let Some(i) = a.get().iterable() { - // -1 if more elements than isize can represent - i.take(isize::MAX as usize + 1).count() as isize + // -1 if more elements than isize can represent + i.take(isize::MAX as usize + 1).count().try_into().unwrap_or(-1) } else { return Err("called len on {a:?}, which isn't a tuple or a string".into()); }))) @@ -235,171 +192,3 @@ impl Config { ) } } - -// fn get_try(allow_unused_functions: bool) -> Data { -// Data::new(data::function::Function { -// info: Info::neverused(), -// info_check: Arc::new(Mutex::new(CheckInfo::neverused())), -// out: Arc::new(move |a, _i| { -// let mut out = Type::empty(); -// for t in a.types.iter() { -// if let Some(outer_tuple) = t.as_any().downcast_ref::() { -// if outer_tuple.0.len() != 2 { -// return Err(format!( -// "cannot use try with tuple argument where len != 2 (got len {})", -// outer_tuple.0.len() -// ) -// .into()); -// } -// let arg_type = &outer_tuple.0[0]; -// let functions = &outer_tuple.0[1]; -// let mut used_functions_and_errors = vec![]; -// for arg_type in arg_type.subtypes_type().types.iter() { -// let arg_type = Type::newm(vec![arg_type.clone()]); -// // possibilities for the tuple (f1, f2, f3, ..., fn) -// for (fti, ft) in functions.types.iter().enumerate() { -// if used_functions_and_errors.len() <= fti { -// used_functions_and_errors.push(vec![]); -// } -// let mut tuple_fallible = true; -// let mut tuple_possible = false; -// if let Some(ft) = ft.as_any().downcast_ref::() { -// // f1, f2, f3, ..., fn -// let mut func_errors = vec![]; -// let mut skip_checks = false; -// for (fi, ft) in ft.0.iter().enumerate() { -// if used_functions_and_errors[fti].len() <= fi { -// used_functions_and_errors[fti].push(vec![]); -// } -// let mut func_fallible = false; -// // possibilities for f_ -// for (fvi, ft) in ft.types.iter().enumerate() { -// if let Some(ft) = -// ft.as_any().downcast_ref::() -// { -// if used_functions_and_errors[fti][fi].len() <= fvi { -// used_functions_and_errors[fti][fi] -// .push(Some(vec![])); -// } -// if !skip_checks { -// func_errors.push(( -// fvi, -// match ft.o(&arg_type) { -// Err(e) => { -// func_fallible = true; -// if let Some(errs) = -// &mut used_functions_and_errors[fti] -// [fi][fvi] -// { -// errs.push(e.clone()); -// } -// Some(e) -// } -// Ok(o) => { -// used_functions_and_errors[fti][fi] -// [fvi] = None; -// tuple_possible = true; -// for t in o.types { -// out.add(t); -// } -// None -// } -// }, -// )); -// } -// } else { -// return Err(format!( -// "try: arguments f1-fn must be functions" -// ) -// .into()); -// } -// } -// // found a function that won't fail for this arg_type! -// if !func_fallible { -// tuple_fallible = false; -// if tuple_possible { -// skip_checks = true; -// } -// } -// } -// if tuple_fallible || !tuple_possible { -// // if the argument is {arg_type}, there is no infallible function. add a fallback function to handle this case! -// let mut e = CheckError::new() -// .msg(format!("if the argument is {arg_type}, there is no infallible function.")) -// .msg(format!("Add a function to handle this case!")); -// for (i, err) in func_errors.into_iter() { -// if let Some(err) = err { -// e = e -// .msg(format!("Error for function #{}:", i + 1)) -// .err(err); -// } -// } -// return Err(e); -// } -// } else { -// return Err(format!( -// "try: argument must be (arg, (f1, f2, f3, ..., fn))" -// ) -// .into()); -// } -// } -// } -// // check for unused functions -// if !allow_unused_functions { -// for (functions_posibility_index, functions_possibility) in -// used_functions_and_errors.into_iter().enumerate() -// { -// for (func_index, func_possibilities) in -// functions_possibility.into_iter().enumerate() -// { -// for (func_possibility_index, errors_if_unused) in -// func_possibilities.into_iter().enumerate() -// { -// if let Some(errs) = errors_if_unused { -// let mut e = CheckError::new().msg(format!("try: For the argument {t}:\nFunction #{}{} is never used. (use `try_allow_unused` to avoid this error){}", -// func_index + 1, -// if functions_posibility_index != 0 || func_possibility_index != 0 { -// format!(" (func-tuple possibility {}, function possibility {})", functions_posibility_index + 1, func_possibility_index + 1) -// } else { -// format!("") -// }, -// if errs.is_empty() { "" } else { " Errors:" })); -// for err in errs { -// e = e.err(err); -// } -// return Err(e); -// } -// } -// } -// } -// } -// } else { -// return Err(format!("cannot use try with non-tuple argument").into()); -// } -// } -// Ok(out) -// }), -// run: Arc::new(|a, _i| { -// let tuple = a.get(); -// let tuple = tuple -// .as_any() -// .downcast_ref::() -// .expect("try: not a tuple"); -// let arg = &tuple.0[0]; -// let funcs = tuple.0[1].get(); -// let funcs = funcs.as_any().downcast_ref::().unwrap(); -// for func in funcs.0.iter() { -// let func = func.get(); -// let func = func -// .as_any() -// .downcast_ref::() -// .unwrap(); -// if func.check(&arg.get().as_type()).is_ok() { -// return func.run(arg.clone()); -// } -// } -// unreacha ble!("try: no function found") -// }), -// inner_statements: None, -// }) -// } diff --git a/mers_lib/src/program/configs/with_math.rs b/mers_lib/src/program/configs/with_math.rs index 4516f3a..db67944 100755 --- a/mers_lib/src/program/configs/with_math.rs +++ b/mers_lib/src/program/configs/with_math.rs @@ -1,4 +1,7 @@ -use std::sync::{Arc, Mutex}; +use std::{ + ops::Rem, + sync::{Arc, Mutex}, +}; use crate::{ data::{self, Data, Type}, @@ -6,15 +9,23 @@ use crate::{ program::{self, run::CheckInfo}, }; -use super::Config; +use super::{ + gen::{ + function::{fun, func, StaticMersFunc, TwoFuncs}, + OneOf, OneOrNone, + }, + Config, +}; impl Config { - /// `sum: fn` returns the sum of all the numbers in the tuple - /// `minus: fn` returns the first number minus all the others - /// `product: fn` returns the product of all the numbers in the tuple + /// `add: fn` returns the sum of all the numbers in the tuple + /// `sub: fn` returns the first number minus all the others + /// `mul: 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. + /// `remainder: fn` returns a % b + /// `modulo: fn` returns a % b, where a % b >= 0 + /// `abs: fn` returns the absolute value of a, abs(a) or |a|, which is a for a >= 0 and -a for a < 0. For a==isize::MIN, returns isize::MAX, which is one less than the theoretical absolute value of isize::MIN /// `pow: fn` returns a^b or 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) /// `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. /// `gt: fn` returns true if the input keeps decreasing, that is, for (a, b), a > b, for (a, b, c), a > b > c, and so on. @@ -27,199 +38,88 @@ impl Config { /// `round: fn` rounds the float and returns an int /// `ceil: fn` rounds the float [?] and returns an int /// `floor: fn` rounds the float [?] and returns an int + /// `round_as_float: fn` round ties (x.5) away from zero, return the result as a float + /// `ceil_as_float: fn` round all numbers towards +infty, return the result as a float + /// `floor_as_float: fn` round all numbers towards -infty, return the result as a float + /// `truncate_as_float: fn` round all numbers towards 0, return the result as a float + /// `round_ties_even_as_float: fn` round ties (x.5) to the nearest even number, return the result as a float + /// `round_to_int: fn` round ties (x.5) away from zero, return the result as an Int (saturates at the Int boundaries, hence the to_int instead of as_int) + /// `ceil_to_int: fn` round all numbers towards +infty, return the result as an Int (saturates at the Int boundaries, hence the to_int instead of as_int) + /// `floor_to_int: fn` round all numbers towards -infty, return the result as an Int (saturates at the Int boundaries, hence the to_int instead of as_int) + /// `truncate_to_int: fn` round all numbers towards 0, return the result as an Int (saturates at the Int boundaries, hence the to_int instead of as_int) + /// `round_ties_even_to_int: fn` round ties (x.5) to the nearest even number, return the result as an Int (saturates at the Int boundaries, hence the to_int instead of as_int) pub fn with_math(self) -> Self { self.add_var( + "parse_float".to_owned(), + Data::new(func(|n: &str, _| Ok(OneOrNone(n.parse::().ok())))), + ) + .add_var( + "parse_int".to_owned(), + Data::new(func(|n: &str, _| Ok(OneOrNone(n.parse::().ok())))), + ) + .add_var( "lt".to_string(), - Data::new(ltgtoe_function("lt".to_string(), |l, r| match (l, r) { - (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, - (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l < 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::Float(r)) => l < r, + Data::new(func(|v: (OneOf, OneOf), _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a < b, + (OneOf::A(a), OneOf::B(b)) => (a as f64) < b, + (OneOf::B(a), OneOf::A(b)) => a < (b as f64), + (OneOf::B(a), OneOf::B(b)) => a < b, + }) })), ) .add_var( "gt".to_string(), - Data::new(ltgtoe_function("gt".to_string(), |l, r| match (l, r) { - (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, - (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l > 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::Float(r)) => l > r, + Data::new(func(|v: (OneOf, OneOf), _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a > b, + (OneOf::A(a), OneOf::B(b)) => (a as f64) > b, + (OneOf::B(a), OneOf::A(b)) => a > (b as f64), + (OneOf::B(a), OneOf::B(b)) => a > b, + }) })), ) .add_var( "ltoe".to_string(), - Data::new(ltgtoe_function("ltoe".to_string(), |l, r| match (l, r) { - (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, - (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l <= 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::Float(r)) => l <= r, + Data::new(func(|v: (OneOf, OneOf), _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a <= b, + (OneOf::A(a), OneOf::B(b)) => (a as f64) <= b, + (OneOf::B(a), OneOf::A(b)) => a <= (b as f64), + (OneOf::B(a), OneOf::B(b)) => a <= b, + }) })), ) .add_var( "gtoe".to_string(), - Data::new(ltgtoe_function("gtoe".to_string(), |l, r| match (l, r) { - (IntOrFloatOrNothing::Nothing, _) | (_, IntOrFloatOrNothing::Nothing) => true, - (IntOrFloatOrNothing::Int(l), IntOrFloatOrNothing::Int(r)) => l >= 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::Float(r)) => l >= r, + Data::new(func(|v: (OneOf, OneOf), _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a >= b, + (OneOf::A(a), OneOf::B(b)) => (a as f64) >= b, + (OneOf::B(a), OneOf::A(b)) => a >= (b as f64), + (OneOf::B(a), OneOf::B(b)) => a >= b, + }) })), ) - .add_var( - "parse_float".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(Arc::new(|a, _i| { - if a.is_included_in(&Type::new(data::string::StringT)) { - Ok(Type::newm(vec![ - Arc::new(data::tuple::TupleT(vec![Type::new(data::float::FloatT)])), - Arc::new(data::tuple::TupleT(vec![])), - ])) - } else { - Err(format!("parse_float called on non-string type").into()) - } - })), - run: Arc::new(|a, _i| { - Ok( - if let Ok(n) = a - .get() - .as_any() - .downcast_ref::() - .unwrap() - .0 - .parse() - { - Data::one_tuple(Data::new(data::float::Float(n))) - } else { - Data::empty_tuple() - }, - ) - }), - inner_statements: None, - }), - ) - .add_var( - "parse_int".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(Arc::new(|a, _i| { - if a.is_included_in(&Type::new(data::string::StringT)) { - Ok(Type::newm(vec![ - Arc::new(data::tuple::TupleT(vec![Type::new(data::int::IntT)])), - Arc::new(data::tuple::TupleT(vec![])), - ])) - } else { - Err(format!("parse_float called on non-string type").into()) - } - })), - run: Arc::new(|a, _i| { - Ok( - if let Ok(n) = a - .get() - .as_any() - .downcast_ref::() - .unwrap() - .0 - .parse() - { - Data::one_tuple(Data::new(data::int::Int(n))) - } else { - Data::empty_tuple() - }, - ) - }), - inner_statements: None, - }), - ) .add_var( "signum".to_string(), - Data::new(data::function::Function { - info: program::run::Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(Arc::new(|a, _i| { - if a.is_included_in(&Type::newm(vec![ - Arc::new(data::int::IntT), - Arc::new(data::float::FloatT), - ])) { - Ok(Type::new(data::int::IntT)) - } else { - Err(format!("signum called on non-number type").into()) - } - })), - run: Arc::new(|a, _i| { - Ok(Data::new(data::int::Int( - if let Some(n) = a.get().as_any().downcast_ref::() { - n.0.signum() - } else if let Some(n) = - a.get().as_any().downcast_ref::() - { - if n.0 > 0.0 { - 1 - } else if n.0 < 0.0 { - -1 - } else { - 0 - } + Data::new(func(|n: OneOf, _| { + Ok(match n { + OneOf::A(n) => n.signum(), + OneOf::B(n) => { + if n > 0.0 { + 1 + } else if n < 0.0 { + -1 } else { - return Err("called signum on non-number type".into()); - }, - ))) - }), - inner_statements: None, - }), + 0 + } + } + }) + })), ) .add_var( - "div".to_string(), - Data::new(two_num_tuple_to_num( - "div", - |l, r| { - l.checked_div(r) - .ok_or_else(|| CheckError::from("attempted to divide by zero")) - }, - |l, r| Ok(l as f64 / r), - |l, r| Ok(l / r as f64), - |l, r| Ok(l / r), - )), - ) - .add_var( - "pow".to_string(), - Data::new(two_num_tuple_to_num( - "pow", - |l, r| Ok(l.pow(r.try_into().unwrap_or(u32::MAX))), - |l, r| Ok((l as f64).powf(r)), - |l, r| { - Ok(if let Ok(r) = r.try_into() { - l.powi(r) - } else { - l.powf(r as f64) - }) - }, - |l, r| Ok(l.powf(r)), - )), - ) - .add_var( - "modulo".to_string(), - Data::new(two_num_tuple_to_num( - "modulo", - |l, r| { - l.checked_rem(r).ok_or_else(|| { - 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( - "sum".to_string(), + "add".to_string(), Data::new(num_iter_to_num("sum", Ok(0), |a, v| match (a, v) { (Ok(a), Ok(v)) => Ok(a + v), (Ok(a), Err(v)) => Err(a as f64 + v), @@ -228,17 +128,27 @@ impl Config { })), ) .add_var( - "subtract".to_string(), - Data::new(two_num_tuple_to_num( - "subtract", - |l, r| Ok(l - r), - |l, r| Ok(l as f64 - r), - |l, r| Ok(l - r as f64), - |l, r| Ok(l - r), - )), + "sub".to_string(), + Data::new( + TwoFuncs( + fun(|(n, d): (isize, isize), _| Ok(n.wrapping_sub(d))), + fun(|(n, d): (OneOf, OneOf), _| { + let n = match n { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + let d = match d { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + Ok(n - d) + }), + ) + .mers_func(), + ), ) .add_var( - "product".to_string(), + "mul".to_string(), 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), @@ -246,6 +156,189 @@ impl Config { (Err(a), Err(v)) => Err(a * v), })), ) + .add_var( + "div".to_string(), + Data::new( + TwoFuncs( + fun(|(n, d): (isize, isize), _| { + n.checked_div(d) + .ok_or_else(|| CheckError::from("attempted to divide by zero")) + }), + fun(|(n, d): (OneOf, OneOf), _| { + let n = match n { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + let d = match d { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + Ok(n / d) + }), + ) + .mers_func(), + ), + ) + .add_var( + "remainder".to_string(), + Data::new( + TwoFuncs( + fun(|(n, d): (isize, isize), _| { + n.checked_rem(d).ok_or_else(|| { + CheckError::from( + "attempted to calculate remainder with zero, or overflow occured", + ) + }) + }), + fun(|(n, d): (OneOf, OneOf), _| { + let n = match n { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + let d = match d { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + Ok(n.rem(d)) + }), + ) + .mers_func(), + ), + ) + .add_var( + "modulo".to_string(), + Data::new( + TwoFuncs( + fun(|(n, d): (isize, isize), _| { + n.checked_rem_euclid(d).ok_or_else(|| { + CheckError::from( + "attempted to perform modulo with zero, or overflow occured", + ) + }) + }), + fun(|(n, d): (OneOf, OneOf), _| { + let n = match n { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + let d = match d { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + Ok(n.rem_euclid(d)) + }), + ) + .mers_func(), + ), + ) + .add_var( + "abs".to_string(), + Data::new( + TwoFuncs( + fun(|v: isize, _| Ok(v.saturating_abs())), + fun(|v: f64, _| Ok(v.abs())), + ) + .mers_func(), + ), + ) + .add_var( + "pow".to_string(), + Data::new( + TwoFuncs( + fun(|(l, r): (isize, isize), _| Ok(l.pow(r.try_into().unwrap_or(u32::MAX)))), + fun(|(l, r): (OneOf, OneOf), _| { + let l = match l { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }; + Ok(match r { + OneOf::A(r) => { + if let Ok(r) = r.try_into() { + l.powi(r) + } else { + l.powf(r as f64) + } + } + OneOf::B(r) => l.powf(r), + }) + }), + ) + .mers_func(), + ), + ) + .add_var( + "as_float".to_owned(), + Data::new(func(|v: OneOf, _| { + Ok(match v { + OneOf::A(v) => v as f64, + OneOf::B(v) => v, + }) + })), + ) + .add_var( + "round_as_float".to_owned(), + Data::new(func(|v: f64, _| -> Result { Ok(v.round()) })), + ) + .add_var( + "ceil_as_float".to_owned(), + Data::new(func(|v: f64, _| -> Result { Ok(v.ceil()) })), + ) + .add_var( + "floor_as_float".to_owned(), + Data::new(func(|v: f64, _| -> Result { Ok(v.floor()) })), + ) + .add_var( + "truncate_as_float".to_owned(), + Data::new(func(|v: f64, _| -> Result { Ok(v.trunc()) })), + ) + .add_var( + "round_ties_even_as_float".to_owned(), + Data::new(func(|v: f64, _| -> Result { + Ok(v.round_ties_even()) + })), + ) + .add_var( + "round_to_int".to_owned(), + Data::new(func(|v: f64, _| -> Result { + Ok(isize_from(v.round())) + })), + ) + .add_var( + "ceil_to_int".to_owned(), + Data::new(func(|v: f64, _| -> Result { + Ok(isize_from(v.ceil())) + })), + ) + .add_var( + "floor_to_int".to_owned(), + Data::new(func(|v: f64, _| -> Result { + Ok(isize_from(v.floor())) + })), + ) + .add_var( + "truncate_to_int".to_owned(), + Data::new(func(|v: f64, _| -> Result { + Ok(isize_from(v.trunc())) + })), + ) + .add_var( + "round_ties_even_to_int".to_owned(), + Data::new(func(|v: f64, _| -> Result { + Ok(isize_from(v.round_ties_even())) + })), + ) + } +} + +const ISIZE_MAX_F: f64 = isize::MAX as _; +const ISIZE_MIN_F: f64 = isize::MIN as _; +fn isize_from(v: f64) -> isize { + if v >= ISIZE_MAX_F { + isize::MAX + } else if v <= ISIZE_MIN_F { + isize::MIN + } else { + v as isize } } @@ -307,150 +400,3 @@ fn num_iter_to_num( inner_statements: None, } } - -/// (int, int) -> int -/// (int, float) -> float -/// (float, int) -> float -/// (float, float) -> float -fn two_num_tuple_to_num( - func_name: &'static str, - func_ii: impl Fn(isize, isize) -> Result + Send + Sync + 'static, - func_if: impl Fn(isize, f64) -> Result + Send + Sync + 'static, - func_fi: impl Fn(f64, isize) -> Result + Send + Sync + 'static, - func_ff: impl Fn(f64, f64) -> Result + Send + Sync + 'static, -) -> data::function::Function { - data::function::Function { - info: program::run::Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(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 { - let mut float = false; - for t in &a.types { - if let Some(t) = t.as_any().downcast_ref::() { - if t.0.len() != 2 { - return Err(format!("Called {func_name} with a tuple where len != 2").into()); - } - for (t, side) in [(&t.0[0], "left"), (&t.0[1], "right")] { - for t in t.types.iter() { - if t.as_any().is::() { - float = true; - } else if !t.as_any().is::() { - return Err(format!("Called {func_name}, but the {side} side of the tuple had type {t}, which isn't Int/Float.").into()); - } - } - } - } else { - return Err(format!("Called {func_name} on a non-tuple").into()); - } - } - Ok(if a.types.is_empty() { - Type::empty() - } else if float { - Type::new(data::float::FloatT) - } else { - Type::new(data::int::IntT) - }) -} -fn two_tuple_to_num_impl_run( - a: Data, - func_name: &'static str, - func_ii: &(impl Fn(isize, isize) -> Result + Send + Sync), - func_if: &(impl Fn(isize, f64) -> Result + Send + Sync), - func_fi: &(impl Fn(f64, isize) -> Result + Send + Sync), - func_ff: &(impl Fn(f64, f64) -> Result + Send + Sync), -) -> Result { - if let Some(t) = a.get().as_any().downcast_ref::() { - let left = t.0[0].get(); - let right = t.0[1].get(); - let (left, right) = (left.as_any(), right.as_any()); - Ok( - match ( - left.downcast_ref::(), - left.downcast_ref::(), - right.downcast_ref::(), - right.downcast_ref::(), - ) { - (Some(data::int::Int(l)), None, Some(data::int::Int(r)), None) => { - Data::new(data::int::Int(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( - func_name: String, - op: impl Fn(IntOrFloatOrNothing, IntOrFloatOrNothing) -> bool + Send + Sync + 'static, -) -> data::function::Function { - data::function::Function { - info: program::run::Info::neverused(), - info_check: Arc::new(Mutex::new(CheckInfo::neverused())), - out: Ok(Arc::new(move |a, _i| { - if let Some(iter_type) = a.iterable() { - let iter_required_type = Type::newm(vec![ - Arc::new(data::int::IntT), - Arc::new(data::float::FloatT), - ]); - if iter_type.is_included_in(&iter_required_type) { - Ok(Type::new(data::bool::BoolT)) - } else { - Err(CheckError::from(format!("Cannot use {func_name} on iterator over type {iter_type} (has to be at most {iter_required_type})."))) - } - } else { - Err(CheckError::from(format!("Cannot use {func_name}"))) - } - })), - run: Arc::new(move |a, _i| { - let mut prev = IntOrFloatOrNothing::Nothing; - for item in a.get().iterable().unwrap() { - let item = item?; - let item = item.get(); - let new = if let Some(data::int::Int(v)) = item.as_any().downcast_ref() { - IntOrFloatOrNothing::Int(*v) - } else if let Some(data::float::Float(v)) = item.as_any().downcast_ref() { - IntOrFloatOrNothing::Float(*v) - } else { - return Err( - "one of the (l/g)t[oe] function argument iterator elements were neither int nor float".into(), - ); - }; - if op(prev, new) { - prev = new; - } else { - return Ok(Data::new(data::bool::Bool(false))); - } - } - Ok(Data::new(data::bool::Bool(true))) - }), - inner_statements: None, - } -} -#[derive(Clone, Copy)] -enum IntOrFloatOrNothing { - Nothing, - Int(isize), - Float(f64), -} diff --git a/mers_lib/src/program/configs/with_string.rs b/mers_lib/src/program/configs/with_string.rs index 9f6b25a..58fc648 100755 --- a/mers_lib/src/program/configs/with_string.rs +++ b/mers_lib/src/program/configs/with_string.rs @@ -20,45 +20,39 @@ impl Config { pub fn with_string(self) -> Self { self.add_var( "trim".to_string(), - Data::new(func(|v: &str| Ok(v.trim().to_owned()))), + Data::new(func(|v: &str, _| Ok(v.trim().to_owned()))), ) - // .add_var("index_of".to_string(), Data::new(util::to_mers_func_concrete_string_string_to_opt_int(|v, p| v.find(p).map(|v| v as _)))) .add_var( "index_of".to_string(), - Data::new(func(|(v, p): (&str, &str)| { + Data::new(func(|(v, p): (&str, &str), _| { Ok(OneOrNone(v.find(p).map(|v| v as isize))) })), ) - // .add_var("index_of_rev".to_string(), Data::new(util::to_mers_func_concrete_string_string_to_opt_int(|v, p| v.rfind(p).map(|v| v as _) ))) .add_var( "index_of_rev".to_string(), - Data::new(func(|(v, p): (&str, &str)| { + Data::new(func(|(v, p): (&str, &str), _| { Ok(OneOrNone(v.rfind(p).map(|v| v as isize))) })), ) - // .add_var("starts_with".to_string(), Data::new(util::to_mers_func_concrete_string_string_to_bool(|v, p| v.starts_with(p)))) .add_var( "starts_with".to_string(), - Data::new(func(|(v, p): (&str, &str)| Ok(v.starts_with(p)))), + Data::new(func(|(v, p): (&str, &str), _| Ok(v.starts_with(p)))), ) - // .add_var("ends_with".to_string(), Data::new(util::to_mers_func_concrete_string_string_to_bool(|v, p| v.ends_with(p)))) .add_var( "ends_with".to_string(), - Data::new(func(|(v, p): (&str, &str)| Ok(v.ends_with(p)))), + Data::new(func(|(v, p): (&str, &str), _| Ok(v.ends_with(p)))), ) - // .add_var("str_split_once".to_string(), Data::new(util::to_mers_func_concrete_string_string_to_opt_string_string(|v, p| v.split_once(p).map(|(a, b)| (a.to_owned(), b.to_owned()))))) .add_var( "str_split_once".to_string(), - Data::new(func(|(v, p): (&str, &str)| { + Data::new(func(|(v, p): (&str, &str), _| { Ok(AnyOrNone( v.split_once(p).map(|(a, b)| (a.to_owned(), b.to_owned())), )) })), ) - // .add_var("str_split_once_rev".to_string(), Data::new(util::to_mers_func_concrete_string_string_to_opt_string_string(|v, p| v.rsplit_once(p).map(|(a, b)| (a.to_owned(), b.to_owned()))))) .add_var( "str_split_once_rev".to_string(), - Data::new(func(|(v, p): (&str, &str)| { + Data::new(func(|(v, p): (&str, &str), _| { Ok(AnyOrNone( v.rsplit_once(p).map(|(a, b)| (a.to_owned(), b.to_owned())), )) @@ -66,11 +60,10 @@ impl Config { ) .add_var( "str_split".to_string(), - Data::new(func(|(v, p): (&str, &str)| { + Data::new(func(|(v, p): (&str, &str), _| { Ok(IterToList(v.split(p).map(|v| v.to_owned()))) })), ) - // .add_var("str_split".to_string(), Data::new(util::to_mers_func_concrete_string_string_to_any(Type::new(super::with_list::ListT(Type::new(data::string::StringT))), |v, p| Ok(Data::new(super::with_list::List(v.split(p).map(|v| Arc::new(RwLock::new(Data::new(data::string::String(v.to_owned()))))).collect())))))) .add_var( "concat".to_string(), Data::new(util::to_mers_func( @@ -99,56 +92,9 @@ impl Config { |a| Ok(Data::new(data::string::String(a.get().to_string()))), )), ) - // .add_var("substring".to_string(), Data::new(util::to_mers_func( - // |a| { - // for t in a.types.iter() { - // if let Some(t) = t.as_any().downcast_ref::() { - // if t.0.len() != 2 && t.0.len() != 3 { - // return Err(format!("cannot call substring with tuple argument of len != 3").into()); - // } - // if !t.0[0].is_included_in_single(&data::string::StringT) { - // return Err(format!("cannot call substring with tuple argument that isn't (*string*, int, int)").into()); - // } - // if !t.0[1].is_included_in_single(&data::int::IntT) { - // return Err(format!("cannot call substring with tuple argument that isn't (string, *int*, int)").into()); - // } - // if t.0.len() > 2 && !t.0[2].is_included_in_single(&data::int::IntT) { - // return Err(format!("cannot call substring with tuple argument that isn't (string, int, *int*)").into()); - // } - // } else { - // return Err(format!("cannot call substring with non-tuple argument.").into()); - // } - // } - // Ok(if a.types.is_empty() { - // Type::empty() - // } else { - // Type::new(data::string::StringT) - // }) - // }, - // |a| { - // let tuple = a.get(); - // let tuple = tuple.as_any().downcast_ref::().expect("called substring with non-tuple arg"); - // let (s, start, end) = (&tuple.0[0], &tuple.0[1], tuple.0.get(2)); - // let s = s.get(); - // let s = &s.as_any().downcast_ref::().unwrap().0; - // let start = start.get(); - // let start = start.as_any().downcast_ref::().unwrap().0; - // let start = if start < 0 { s.len().saturating_sub(start.abs() as usize) } else { start as usize }; - // let end = end - // .map(|end| end.get()) - // .map(|end| end.as_any().downcast_ref::().unwrap().0) - // .map(|i| if i < 0 { s.len().saturating_sub(i.abs() as usize) } else { i as usize }) - // .unwrap_or(usize::MAX); - // let end = end.min(s.len()); - // if end < start { - // return Ok(Data::new(data::string::String(String::new()))); - // } - // Ok(Data::new(data::string::String(s[start..end].to_owned()))) - // }) - // )) .add_var( "substring".to_string(), - Data::new(func(|v: OneOf<(&str, isize), (&str, isize, isize)>| { + Data::new(func(|v: OneOf<(&str, isize), (&str, isize, isize)>, _| { let (s, start, end) = match v { OneOf::A((t, s)) => (t, s, None), OneOf::B((t, s, e)) => (t, s, Some(e)),