convert more stdlib functions and add rounding

converted with_base and with_math
This commit is contained in:
Mark 2024-07-03 11:59:22 +02:00
parent 50928cca1d
commit 08a82733e9
7 changed files with 404 additions and 713 deletions

View File

@ -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::<data::string::String>()
.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(())
}

View File

@ -40,25 +40,25 @@ impl Clone for Function {
impl Function {
pub fn new_static(
out: Vec<(Type, Type)>,
run: impl Fn(Data) -> Result<Data, CheckError> + Send + Sync + 'static,
run: impl Fn(Data, &mut Info) -> Result<Data, CheckError> + 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<Type, CheckError> + Send + Sync + 'static,
run: impl Fn(Data) -> Result<Data, CheckError> + Send + Sync + 'static,
run: impl Fn(Data, &mut Info) -> Result<Data, CheckError> + 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,
}
}

View File

@ -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<I: FromMersData + 'static, O: ToMersData + 'static>(
f: fn(I, &mut crate::program::run::Info) -> Result<O, CheckError>,
) -> impl StaticMersFunc {
Box::new(f)
}
pub fn func<I: FromMersData + 'static, O: ToMersData + 'static>(
f: fn(I) -> Result<O, CheckError>,
f: fn(I, &mut crate::program::run::Info) -> Result<O, CheckError>,
) -> data::function::Function {
Box::new(f).mers_func()
}
pub fn func_end<I: FromMersData + 'static>(
f: fn(I, &mut crate::program::run::Info) -> !,
) -> data::function::Function {
Box::new(f).mers_func()
}
pub fn func_err<I: FromMersData + 'static>(
f: fn(I, &mut crate::program::run::Info) -> CheckError,
) -> data::function::Function {
Box::new(f).mers_func()
}
@ -31,10 +46,14 @@ pub fn func<I: FromMersData + 'static, O: ToMersData + 'static>(
pub trait StaticMersFunc: Sized + 'static + Send + Sync {
fn types() -> Vec<(Type, Type)>;
fn run(&self, a: &(impl MersData + ?Sized)) -> Option<Result<Data, CheckError>>;
fn run(
&self,
a: &(impl MersData + ?Sized),
info: &mut crate::program::run::Info,
) -> Option<Result<Data, CheckError>>;
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<A: StaticMersFunc, B: StaticMersFunc>(pub A, pub B);
pub trait Func: Send + Sync + 'static {
type I: FromMersData;
type O: ToMersData;
fn run_func(&self, i: Self::I) -> Result<Self::O, CheckError>;
fn run_func(
&self,
i: Self::I,
info: &mut crate::program::run::Info,
) -> Result<Self::O, CheckError>;
}
impl<I: FromMersData + 'static, O: ToMersData + 'static> Func for fn(I) -> Result<O, CheckError> {
impl<I: FromMersData + 'static, O: ToMersData + 'static> Func
for fn(I, &mut crate::program::run::Info) -> Result<O, CheckError>
{
type I = I;
type O = O;
fn run_func(&self, i: Self::I) -> Result<Self::O, CheckError> {
self(i)
fn run_func(
&self,
i: Self::I,
info: &mut crate::program::run::Info,
) -> Result<Self::O, CheckError> {
self(i, info)
}
}
pub struct UnreachableDontConstruct(PhantomData<Self>);
impl ToMersData for UnreachableDontConstruct {
fn as_type_to() -> Type {
Type::empty()
}
fn represent(self) -> Data {
unreachable!()
}
}
impl<I: FromMersData + 'static> 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::O, CheckError> {
self(i, info);
}
}
impl<I: FromMersData + 'static> 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<Self::O, CheckError> {
Err(self(i, info))
}
}
@ -66,8 +127,14 @@ impl<F: Func + ?Sized> StaticMersFunc for Box<F> {
fn types() -> Vec<(Type, Type)> {
vec![(F::I::as_type_from(), F::O::as_type_to())]
}
fn run(&self, a: &(impl MersData + ?Sized)) -> Option<Result<Data, CheckError>> {
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<Result<Data, CheckError>> {
F::I::try_represent(a, |v| {
v.map(|v| self.run_func(v, info).map(|v| v.represent()))
})
}
}
@ -84,7 +151,11 @@ impl<A: StaticMersFunc, B: StaticMersFunc> StaticMersFunc for TwoFuncs<A, B> {
}
o
}
fn run(&self, a: &(impl MersData + ?Sized)) -> Option<Result<Data, CheckError>> {
self.0.run(a).or_else(|| self.1.run(a))
fn run(
&self,
a: &(impl MersData + ?Sized),
info: &mut crate::program::run::Info,
) -> Option<Result<Data, CheckError>> {
self.0.run(a, info).or_else(|| self.1.run(a, info))
}
}

View File

@ -41,7 +41,7 @@ pub fn to_mers_func_with_in_out_types(
out_type: Type,
run: impl Fn(Data) -> Result<Data, CheckError> + 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(

View File

@ -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());
.add_var("sleep".to_owned(), Data::new(func(|dur: OneOf<isize, f64>, 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(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::<data::int::Int>().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::<data::string::String>()
.map(|i| i.0.to_owned())
.unwrap_or_else(String::new).into()
)
}),
inner_statements: None,
}))
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 {
@ -156,7 +113,7 @@ impl Config {
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
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::<data::tuple::TupleT>() {
// 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::<data::tuple::TupleT>() {
// // 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::<data::function::FunctionT>()
// {
// 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::<data::tuple::Tuple>()
// .expect("try: not a tuple");
// let arg = &tuple.0[0];
// let funcs = tuple.0[1].get();
// let funcs = funcs.as_any().downcast_ref::<data::tuple::Tuple>().unwrap();
// for func in funcs.0.iter() {
// let func = func.get();
// let func = func
// .as_any()
// .downcast_ref::<data::function::Function>()
// .unwrap();
// if func.check(&arg.get().as_type()).is_ok() {
// return func.run(arg.clone());
// }
// }
// unreacha ble!("try: no function found")
// }),
// inner_statements: None,
// })
// }

View File

@ -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::<f64>().ok())))),
)
.add_var(
"parse_int".to_owned(),
Data::new(func(|n: &str, _| Ok(OneOrNone(n.parse::<isize>().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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, OneOf<isize, f64>), _| {
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::<data::string::String>()
.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::<data::string::String>()
.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::<data::int::Int>() {
n.0.signum()
} else if let Some(n) =
a.get().as_any().downcast_ref::<data::float::Float>()
{
if n.0 > 0.0 {
Data::new(func(|n: OneOf<isize, f64>, _| {
Ok(match n {
OneOf::A(n) => n.signum(),
OneOf::B(n) => {
if n > 0.0 {
1
} else if n.0 < 0.0 {
} else if n < 0.0 {
-1
} else {
0
}
} else {
return Err("called signum on non-number type".into());
},
)))
}),
inner_statements: None,
}),
)
.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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, OneOf<isize, f64>), _| {
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<isize, f64>, _| {
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<f64, _> { Ok(v.round()) })),
)
.add_var(
"ceil_as_float".to_owned(),
Data::new(func(|v: f64, _| -> Result<f64, _> { Ok(v.ceil()) })),
)
.add_var(
"floor_as_float".to_owned(),
Data::new(func(|v: f64, _| -> Result<f64, _> { Ok(v.floor()) })),
)
.add_var(
"truncate_as_float".to_owned(),
Data::new(func(|v: f64, _| -> Result<f64, _> { Ok(v.trunc()) })),
)
.add_var(
"round_ties_even_as_float".to_owned(),
Data::new(func(|v: f64, _| -> Result<f64, _> {
Ok(v.round_ties_even())
})),
)
.add_var(
"round_to_int".to_owned(),
Data::new(func(|v: f64, _| -> Result<isize, _> {
Ok(isize_from(v.round()))
})),
)
.add_var(
"ceil_to_int".to_owned(),
Data::new(func(|v: f64, _| -> Result<isize, _> {
Ok(isize_from(v.ceil()))
})),
)
.add_var(
"floor_to_int".to_owned(),
Data::new(func(|v: f64, _| -> Result<isize, _> {
Ok(isize_from(v.floor()))
})),
)
.add_var(
"truncate_to_int".to_owned(),
Data::new(func(|v: f64, _| -> Result<isize, _> {
Ok(isize_from(v.trunc()))
})),
)
.add_var(
"round_ties_even_to_int".to_owned(),
Data::new(func(|v: f64, _| -> Result<isize, _> {
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<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: 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<Type, CheckError> {
let mut float = false;
for t in &a.types {
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
if t.0.len() != 2 {
return Err(format!("Called {func_name} with a tuple where len != 2").into());
}
for (t, side) in [(&t.0[0], "left"), (&t.0[1], "right")] {
for t in t.types.iter() {
if t.as_any().is::<data::float::FloatT>() {
float = true;
} else if !t.as_any().is::<data::int::IntT>() {
return Err(format!("Called {func_name}, but the {side} side of the tuple had type {t}, which isn't Int/Float.").into());
}
}
}
} else {
return Err(format!("Called {func_name} on a non-tuple").into());
}
}
Ok(if a.types.is_empty() {
Type::empty()
} else if float {
Type::new(data::float::FloatT)
} else {
Type::new(data::int::IntT)
})
}
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(
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),
}

View File

@ -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::<data::tuple::TupleT>() {
// 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::<data::tuple::Tuple>().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::<data::string::String>().unwrap().0;
// let start = start.get();
// let start = start.as_any().downcast_ref::<data::int::Int>().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::<data::int::Int>().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)),