diff --git a/mers/Cargo.toml b/mers/Cargo.toml index 84b10eb..35dc740 100644 --- a/mers/Cargo.toml +++ b/mers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers" -version = "0.9.3" +version = "0.9.4" edition = "2021" license = "MIT OR Apache-2.0" description = "dynamically typed but type-checked programming language" @@ -15,7 +15,7 @@ default = ["colored-output"] colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"] [dependencies] -mers_lib = "0.9.3" +mers_lib = "0.9.4" # mers_lib = { path = "../mers_lib" } clap = { version = "4.3.19", features = ["derive"] } colored = { version = "2.1.0", optional = true } diff --git a/mers_lib/Cargo.toml b/mers_lib/Cargo.toml index 7ddcaa4..a672730 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers_lib" -version = "0.9.3" +version = "0.9.4" edition = "2021" license = "MIT OR Apache-2.0" description = "library to use the mers language in other projects" diff --git a/mers_lib/src/data/function.rs b/mers_lib/src/data/function.rs index 5efcc5d..b5621f5 100755 --- a/mers_lib/src/data/function.rs +++ b/mers_lib/src/data/function.rs @@ -51,13 +51,13 @@ impl Function { } } pub fn new_generic( - out: impl Fn(&Type) -> Result + Send + Sync + 'static, + out: impl Fn(&Type, &mut CheckInfo) -> 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))), + out: Ok(Arc::new(move |a, i| out(a, i))), run: Arc::new(run), inner_statements: None, } diff --git a/mers_lib/src/data/int.rs b/mers_lib/src/data/int.rs index 2d72e14..e908df7 100755 --- a/mers_lib/src/data/int.rs +++ b/mers_lib/src/data/int.rs @@ -4,6 +4,43 @@ use crate::info::DisplayInfo; use super::{MersData, MersType, Type}; +/// The smallest representable integer. +/// Depends on the system for which mers is being compiled, as mers uses pointer-sized signed integers. +/// `-2^W`, `W` is the bit-width of a pointer on the system, often `32` or `64`. +pub const INT_MIN: isize = isize::MIN; +/// The largest representable integer. +/// Depends on the system for which mers is being compiled, as mers uses pointer-sized signed integers. +/// `2^W-1`, `W` is the bit-width of a pointer on the system, often `32` or `64`. +pub const INT_MAX: isize = isize::MAX; +/// The smallest integer representable by mers and by a signed 32-bit number. +/// `max(INT_MIN, -2^31)` +pub const INT32S_MIN: isize = if isize::BITS > i32::BITS { + i32::MIN as isize +} else { + isize::MIN +}; +/// The largest integer representable by mers and by a signed 32-bit number. +/// `min(INT_MAX, 2^31-1)` +pub const INT32S_MAX: isize = if isize::BITS > i32::BITS { + i32::MAX as isize +} else { + isize::MAX +}; +/// The smallest integer representable by mers and by an unsigned 32-bit number, assuming its value was negative. +/// `max(INT_MIN, -(2^32-1))` +pub const INT32U_MIN: isize = if isize::BITS > u32::BITS { + -(u32::MAX as isize) +} else { + isize::MIN +}; +/// The largest integer representable by mers and by an unsigned 32-bit number. +/// `min(INT_MAX, 2^32-1)` +pub const INT32U_MAX: isize = if isize::BITS > u32::BITS { + u32::MAX as isize +} else { + isize::MAX +}; + #[derive(Debug, Clone, Copy)] pub struct Int(pub isize); @@ -22,7 +59,7 @@ impl MersData for Int { Box::new(Clone::clone(self)) } fn as_type(&self) -> super::Type { - Type::new(IntT) + Type::new(IntT(self.0, self.0)) } fn as_any(&self) -> &dyn Any { self @@ -35,8 +72,8 @@ impl MersData for Int { } } -#[derive(Debug, Clone)] -pub struct IntT; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IntT(pub isize, pub isize); impl MersType for IntT { fn display( &self, @@ -46,13 +83,35 @@ impl MersType for IntT { write!(f, "{self}") } fn is_same_type_as(&self, other: &dyn MersType) -> bool { - other.as_any().downcast_ref::().is_some() + if let Some(other) = other.as_any().downcast_ref::() { + self == other + } else { + false + } } fn is_included_in(&self, target: &dyn MersType) -> bool { - self.is_same_type_as(target) + if let Some(target) = target.as_any().downcast_ref::() { + target.0 <= self.0 && self.1 <= target.1 + } else { + false + } } fn subtypes(&self, acc: &mut Type) { acc.add(Arc::new(self.clone())); + // INT_MIN .. INT32U_MIN .. INT32S_MIN .. 0 .. INT32S_MAX .. INT32U_MAX .. INT_MAX + let mut add_range = |min, max| { + // the range is non-empty, self starts before or where the range ends, and self ends after or where the range starts. + if min <= max && self.0 <= max && self.1 >= min { + acc.add(Arc::new(IntT(self.0.max(min), self.1.min(max)))); + } + }; + add_range(INT_MIN, INT32U_MIN - 1); + add_range(INT32U_MIN, INT32S_MIN - 1); + add_range(INT32S_MIN, -1); + add_range(0, 0); + add_range(1, INT32S_MAX); + add_range(INT32S_MAX + 1, INT32U_MAX); + add_range(INT32U_MAX + 1, INT_MAX); } fn as_any(&self) -> &dyn Any { self @@ -72,6 +131,12 @@ impl Display for Int { } impl Display for IntT { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Int") + match (self.0, self.1) { + (isize::MIN, isize::MAX) => write!(f, "Int"), + (val, val2) if val == val2 => write!(f, "Int<{val}>"), + (min, isize::MAX) => write!(f, "Int<{min}..>"), + (isize::MIN, max) => write!(f, "Int<..{max}>"), + (min, max) => write!(f, "Int<{min}..{max}>"), + } } } diff --git a/mers_lib/src/data/mod.rs b/mers_lib/src/data/mod.rs index aa2795e..198d622 100755 --- a/mers_lib/src/data/mod.rs +++ b/mers_lib/src/data/mod.rs @@ -413,6 +413,39 @@ impl Type { let n = new.as_any(); if let Some(s) = n.downcast_ref::() { self.add_all(s); + } else if let Some(n) = n.downcast_ref::() { + let n = n.clone(); + let mut newt = None; + for a in &self.types { + if let Some(t) = a.as_any().downcast_ref::() { + if t.0 <= n.0 && n.1 <= t.1 { + // we are included in this type + return; + } + if t.0 <= n.1.saturating_add(1) && n.0.saturating_sub(1) <= t.1 { + // this type will be added instead of the original `new`, and `t` will be removed from `self.types`. + newt = Some(crate::data::int::IntT(t.0.min(n.0), t.1.max(n.1))); + break; + } + } + } + let newt2 = newt.is_some(); + // remove types that are included in `self` before adding `self` + let newt = newt.unwrap_or(n); + let mut rmstack = vec![]; + for (i, a) in self.types.iter().enumerate() { + if let Some(t) = a.as_any().downcast_ref::() { + if newt.0 <= t.0 && t.1 <= newt.1 { + rmstack.push(i); + } + } + } + for i in rmstack.into_iter().rev() { + self.types.remove(i); + } + if !newt2 { + self.types.push(new); + } } else { if !self.types.iter().any(|t| new.is_included_in(t.as_ref())) { self.types.push(new); diff --git a/mers_lib/src/parsing/types.rs b/mers_lib/src/parsing/types.rs index 64a96d7..2d5f141 100755 --- a/mers_lib/src/parsing/types.rs +++ b/mers_lib/src/parsing/types.rs @@ -277,11 +277,11 @@ pub fn type_from_parsed( .find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v)) { Some(Ok(t)) => as_type.add_all(&*t), - Some(Err(_)) => { - return Err(CheckError::new().msg_str(format!( - "Type: specified type without info, but type needs additional info" - ))) - } + Some(Err(f)) => as_type.add_all(&*f("", info).map_err(|e| { + e.msg_str(format!( + "Note: specified type `{name}` without info, but type needs additional info: `{name}<...>`" + )) + })?), None => return Err(CheckError::new().msg_str(format!("Unknown type '{name}'"))), }, ParsedType::TypeWithInfo(name, additional_info) => match info diff --git a/mers_lib/src/program/configs/gen/mod.rs b/mers_lib/src/program/configs/gen/mod.rs index e771753..5502d2f 100644 --- a/mers_lib/src/program/configs/gen/mod.rs +++ b/mers_lib/src/program/configs/gen/mod.rs @@ -379,27 +379,33 @@ impl ToMersData for u8 { } } -impl FromMersData for isize { +/// An integer within the range `N..=M` +pub struct IntR(pub isize); +impl FromMersData for IntR { fn as_type_from() -> Type { Self::as_type_to() } fn can_represent(t: &Type) -> bool { - t.is_included_in(&Type::new(data::int::IntT)) + t.is_included_in(&Type::new(data::int::IntT(N, M))) } fn try_represent) -> O>(d: &(impl MersData + ?Sized), f: F) -> O { if let Some(v) = d.as_any().downcast_ref::() { - f(Some(v.0)) + if N <= v.0 && v.0 <= M { + f(Some(Self(v.0))) + } else { + f(None) + } } else { f(None) } } } -impl ToMersData for isize { +impl ToMersData for IntR { fn as_type_to() -> Type { - Type::new(data::int::IntT) + Type::new(data::int::IntT(N, M)) } fn represent(self) -> Data { - Data::new(data::int::Int(self)) + Data::new(data::int::Int(self.0)) } } diff --git a/mers_lib/src/program/configs/mod.rs b/mers_lib/src/program/configs/mod.rs index 741d911..a80f7cf 100755 --- a/mers_lib/src/program/configs/mod.rs +++ b/mers_lib/src/program/configs/mod.rs @@ -1,7 +1,11 @@ use std::sync::{Arc, RwLock}; use crate::{ - data::{self, Data, MersData, Type}, + data::{ + self, + int::{INT_MAX, INT_MIN}, + Data, MersData, Type, + }, errors::CheckError, info::Local, program::run::{CheckInfo, CheckLocalGlobalInfo, RunLocalGlobalInfo}, @@ -92,7 +96,42 @@ impl Config { .types .insert("Bool".to_owned(), Ok(Arc::new(data::bool::bool_type()))); init_d!(data::byte::ByteT); - init_d!(data::int::IntT); + info_check.scopes.last_mut().unwrap().types.insert( + "Int".to_owned(), + // Ok(Arc::new(data::Type::new(data::int::INT_T_ALL))), + Err(Arc::new(|range, _| { + Ok(Arc::new(Type::new({ + let range = range.trim(); + if range.is_empty() { + data::int::IntT(INT_MIN, INT_MAX) + } else if let Some((min, max)) = range.split_once("..") { + let (min, max) = (min.trim(), max.trim()); + let min = if min.is_empty() { + data::int::INT_MIN + } else if let Ok(v) = min.parse() { + v + } else { + return Err(CheckError::new().msg_str(format!("In type `Int<{min}..{max}>`: min was present but not a valid integer."))); + }; + let max = if max.is_empty() { + data::int::INT_MAX + } else if let Ok(v) = max.parse() { + v + } else { + return Err(CheckError::new().msg_str(format!("In type `Int<{min}..{max}>`: max was present but not a valid integer."))); + }; + if min > max { + return Err(CheckError::new().msg_str(format!("In type `Int<{min}..{max}>`: min ({min}) must be smaller than or equal to max ({max}). Did you mean `Int<{max}..{min}>`?"))); + } + crate::data::int::IntT(min, max) + } else if let Ok(v) = range.parse() { + crate::data::int::IntT(v, v) + } else { + return Err(CheckError::new().msg_str(format!("In type `Int<{range}>`: Invalid range. Either use `Int` (or `Int<>` or `Int<..>`) for the entire integer range, `Int` for a specific number, `Int` for all numbers `>=n`, `Int<..m>` for all numbers `<=m`, or `Int` for all numbers `>=n` and `<= m`."))); + } + }))) + })), + ); init_d!(data::float::FloatT); init_d!(data::string::StringT); Self { diff --git a/mers_lib/src/program/configs/with_base.rs b/mers_lib/src/program/configs/with_base.rs index 06ec6e8..b9eb4a9 100755 --- a/mers_lib/src/program/configs/with_base.rs +++ b/mers_lib/src/program/configs/with_base.rs @@ -4,7 +4,12 @@ use std::{ }; use crate::{ - data::{self, bool::bool_type, Data, MersTypeWInfo, Type}, + data::{ + self, + bool::bool_type, + int::{INT_MAX, INT_MIN}, + Data, MersTypeWInfo, Type, + }, errors::CheckError, program::run::{CheckInfo, Info}, }; @@ -12,7 +17,7 @@ use crate::{ use super::{ gen::{ function::{func, func_end, func_err}, - OneOf, + IntR, OneOf, }, Config, }; @@ -75,9 +80,9 @@ impl Config { }), inner_statements: None, }) - .add_var("sleep", func(|dur: OneOf, i| { + .add_var("sleep", func(|dur: OneOf, f64>, i| { let mut sleep_dur = match dur { - OneOf::A(dur) => Duration::from_secs(dur.max(0).try_into().unwrap_or(u64::MAX)), + OneOf::A(dur) => Duration::from_secs(dur.0.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 @@ -87,8 +92,8 @@ impl Config { std::thread::sleep(sleep_dur); Ok(()) })) - .add_var("exit", func_end(|code: isize, _| { - std::process::exit(code.try_into().unwrap_or(255)); + .add_var("exit", func_end(|code: IntR, _| { + std::process::exit(code.0.try_into().unwrap_or(255)); })) .add_var("panic", func_err(|message: &str, _| { CheckError::from(message) @@ -104,18 +109,17 @@ impl Config { return Err(format!("cannot get length of {} (must be a tuple, string or iterable)", t.with_info(i)).into()); } } - Ok(Type::new(data::int::IntT)) + Ok(Type::new(data::int::IntT(0, INT_MAX))) })), run: Arc::new(|a, _i| { Ok(Data::new(data::int::Int(if let Some(t) = a.get().as_any().downcast_ref::() { - t.0.len() as _ + t.0.len().try_into().unwrap_or(INT_MAX) } else if let Some(s) = a.get().as_any().downcast_ref::() { - s.0.len() as _ + s.0.len().try_into().unwrap_or(INT_MAX) } else if let Some(i) = a.get().iterable() { - // -1 if more elements than isize can represent - i.take(isize::MAX as usize + 1).count().try_into().unwrap_or(-1) + i.count().try_into().unwrap_or(INT_MAX) } else { - return Err("called len on {a:?}, which isn't a tuple or a string".into()); + return Err("called len on {a:?}, which isn't a tuple, a string, or something iterable.".into()); }))) }), inner_statements: None, diff --git a/mers_lib/src/program/configs/with_command_running.rs b/mers_lib/src/program/configs/with_command_running.rs index 4596f9a..f779582 100755 --- a/mers_lib/src/program/configs/with_command_running.rs +++ b/mers_lib/src/program/configs/with_command_running.rs @@ -6,7 +6,12 @@ use std::{ }; use crate::{ - data::{self, object::ObjectFieldsMap, Data, MersData, MersDataWInfo, MersType, Type}, + data::{ + self, + int::{INT_MAX, INT_MIN}, + object::ObjectFieldsMap, + Data, MersData, MersDataWInfo, MersType, Type, + }, errors::CheckError, info::DisplayInfo, program::{self, run::CheckInfo}, @@ -34,7 +39,7 @@ impl Config { if a.types.iter().all(|t| t.as_any().downcast_ref::().is_some_and(|t| t.0.len() == 2 && t.0[0].is_included_in_single(&data::string::StringT) && t.0[1].iterable().is_some_and(|t| t.is_included_in_single(&data::string::StringT)))) { Ok(Type::newm(vec![ Arc::new(data::tuple::TupleT(vec![ - Type::newm(vec![Arc::new(data::int::IntT), Arc::new(data::bool::TrueT), Arc::new(data::bool::FalseT)]), + Type::newm(vec![Arc::new(data::int::IntT(INT_MIN, INT_MAX)), Arc::new(data::bool::TrueT), Arc::new(data::bool::FalseT)]), Type::new(data::string::StringT), Type::new(data::string::StringT), ])), @@ -160,7 +165,7 @@ impl Config { out: Ok(Arc::new(|a, i| { if a.is_included_in_single(&ChildProcessT) { Ok(Type::newm(vec![ - Arc::new(data::int::IntT), + Arc::new(data::int::IntT(INT_MIN, INT_MAX)), Arc::new(data::bool::TrueT), Arc::new(data::bool::FalseT), Arc::new(data::tuple::TupleT(vec![])), diff --git a/mers_lib/src/program/configs/with_get.rs b/mers_lib/src/program/configs/with_get.rs index d11c1f5..d091ce3 100755 --- a/mers_lib/src/program/configs/with_get.rs +++ b/mers_lib/src/program/configs/with_get.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex}; use crate::{ - data::{self, Data, MersTypeWInfo, Type}, + data::{self, int::INT_MAX, Data, MersTypeWInfo, Type}, program::{self, run::CheckInfo}, }; @@ -22,7 +22,7 @@ impl Config { if t.0.len() != 2 { return Err(format!("called get on tuple with len != 2").into()); } - if !t.0[1].is_included_in_single(&data::int::IntT) { + if !t.0[1].is_included_in_single(&data::int::IntT(0, INT_MAX)) { return Err(format!( "called get with non-int index of type {}", t.0[1].with_info(i) diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index b1b8301..895ecdf 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -7,6 +7,7 @@ use crate::{ data::{ self, function::{Function, FunctionT}, + int::INT_MAX, Data, MersData, MersType, MersTypeWInfo, Type, }, errors::CheckError, @@ -128,8 +129,8 @@ impl Config { genfunc_iter_and_func("map_while", ItersT::MapWhile, Iters::MapWhile), ) .add_var("take", genfunc_iter_and_arg("take", |_: &data::int::IntT| ItersT::Take, |v: &data::int::Int| { - Iters::Take(v.0.max(0) as _) - }, &data::int::IntT)) + Iters::Take(v.0.max(0).try_into().unwrap_or(usize::MAX)) + }, &data::int::IntT(0, INT_MAX))) .add_var( "enumerate", data::function::Function { @@ -463,7 +464,7 @@ impl IterT { } ItersT::Take => data.clone(), ItersT::Enumerate => Type::new(data::tuple::TupleT(vec![ - Type::new(data::int::IntT), + Type::new(data::int::IntT(0, INT_MAX)), data.clone(), ])), ItersT::Chained => { diff --git a/mers_lib/src/program/configs/with_list.rs b/mers_lib/src/program/configs/with_list.rs index 6a6b05d..57819a8 100755 --- a/mers_lib/src/program/configs/with_list.rs +++ b/mers_lib/src/program/configs/with_list.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex, RwLock}; use crate::{ - data::{self, Data, MersData, MersType, MersTypeWInfo, Type}, + data::{self, int::INT_MAX, Data, MersData, MersType, MersTypeWInfo, Type}, errors::CheckError, info::DisplayInfo, parsing::{statements::to_string_literal, Source}, @@ -41,7 +41,7 @@ impl Config { "get_mut: argument must be a 2-tuple `(&List<_>, Int)`." ).into()); } - if t.0[1].is_included_in_single(&data::int::IntT) { + if t.0[1].is_included_in_single(&data::int::IntT(0, INT_MAX)) { if let Some(t) = t.0[0].dereference() { for t in t.types.iter() { if let Some(t) = t.as_any().downcast_ref::() { diff --git a/mers_lib/src/program/configs/with_math.rs b/mers_lib/src/program/configs/with_math.rs index fa902ad..700d7f6 100755 --- a/mers_lib/src/program/configs/with_math.rs +++ b/mers_lib/src/program/configs/with_math.rs @@ -1,18 +1,16 @@ -use std::{ - ops::Rem, - sync::{Arc, Mutex}, -}; +use std::{ops::Rem, sync::Arc}; -use crate::{ - data::{self, Data, Type}, - errors::CheckError, - program::{self, run::CheckInfo}, +use crate::data::{ + self, + function::Function, + int::{INT_MAX, INT_MIN}, + Data, MersTypeWInfo, Type, }; use super::{ gen::{ function::{fun, func, Funcs, StaticMersFunc}, - OneOf, OneOrNone, + IntR, OneOf, OneOrNone, }, Config, }; @@ -55,57 +53,85 @@ impl Config { ) .add_var( "parse_int", - func(|n: &str, _| Ok(OneOrNone(n.parse::().ok()))), + func(|n: &str, _| { + Ok(OneOrNone( + n.parse::().ok().map(IntR::), + )) + }), ) .add_var( "lt", - 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, - }) - }), + func( + |v: ( + OneOf, f64>, + OneOf, f64>, + ), + _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a.0 < b.0, + (OneOf::A(a), OneOf::B(b)) => (a.0 as f64) < b, + (OneOf::B(a), OneOf::A(b)) => a < (b.0 as f64), + (OneOf::B(a), OneOf::B(b)) => a < b, + }) + }, + ), ) .add_var( "gt", - 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, - }) - }), + func( + |v: ( + OneOf, f64>, + OneOf, f64>, + ), + _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a.0 > b.0, + (OneOf::A(a), OneOf::B(b)) => (a.0 as f64) > b, + (OneOf::B(a), OneOf::A(b)) => a > (b.0 as f64), + (OneOf::B(a), OneOf::B(b)) => a > b, + }) + }, + ), ) .add_var( "ltoe", - 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, - }) - }), + func( + |v: ( + OneOf, f64>, + OneOf, f64>, + ), + _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a.0 <= b.0, + (OneOf::A(a), OneOf::B(b)) => (a.0 as f64) <= b, + (OneOf::B(a), OneOf::A(b)) => a <= (b.0 as f64), + (OneOf::B(a), OneOf::B(b)) => a <= b, + }) + }, + ), ) .add_var( "gtoe", - 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, - }) - }), + func( + |v: ( + OneOf, f64>, + OneOf, f64>, + ), + _| { + Ok(match v { + (OneOf::A(a), OneOf::A(b)) => a.0 >= b.0, + (OneOf::A(a), OneOf::B(b)) => (a.0 as f64) >= b, + (OneOf::B(a), OneOf::A(b)) => a >= (b.0 as f64), + (OneOf::B(a), OneOf::B(b)) => a >= b, + }) + }, + ), ) .add_var( "signum", - func(|n: OneOf, _| { - Ok(match n { - OneOf::A(n) => n.signum(), + func(|n: OneOf, f64>, _| { + Ok(IntR::<-1, 1>(match n { + OneOf::A(n) => n.0.signum(), OneOf::B(n) => { if n > 0.0 { 1 @@ -115,118 +141,270 @@ impl Config { 0 } } - }) + })) }), ) .add_var( "add", - 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), - (Err(a), Ok(v)) => Err(a + v as f64), - (Err(a), Err(v)) => Err(a + v), - }), + func_math_op( + "add", + |a, b| Some(a + b), + false, + |a, b| a.checked_add(b), + |a, b| { + if a.0.checked_add(b.0).is_some() + || a.0.checked_add(b.1).is_some() + || a.1.checked_add(b.0).is_some() + || a.1.checked_add(b.1).is_some() + { + Some(( + vec![Arc::new(data::int::IntT( + a.0.saturating_add(b.0), + a.1.saturating_add(b.1), + ))], + a.0.checked_add(b.0).is_none() || a.1.checked_add(b.1).is_none(), + )) + } else { + None + } + }, + None, + ), ) .add_var( "sub", - Funcs( - 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(), + func_math_op( + "sub", + |a, b| Some(a - b), + false, + |a, b| a.checked_sub(b), + |a, b| { + if a.0.checked_sub(b.0).is_some() + || a.0.checked_sub(b.1).is_some() + || a.1.checked_sub(b.0).is_some() + || a.1.checked_sub(b.1).is_some() + { + Some(( + vec![Arc::new(data::int::IntT( + a.0.saturating_sub(b.1), + a.1.saturating_sub(b.0), + ))], + a.0.checked_sub(b.1).is_none() || a.1.checked_sub(b.0).is_none(), + )) + } else { + None + } + }, + None, + ), ) .add_var( "mul", - 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), - }), + func_math_op( + "mul", + |a, b| Some(a * b), + false, + |a, b| a.checked_mul(b), + |a, b| { + let (mut min, mut max, mut fails) = (None, None, false); + let mut minmax = |v| { + if min.is_none() || v < min.unwrap() { + min = Some(v); + } + if max.is_none() || v > max.unwrap() { + max = Some(v); + } + }; + let mut trymul = |a: isize, b: isize| { + a.checked_mul(b).unwrap_or_else(|| { + fails = true; + match a.signum() * b.signum() { + 0 => 0, + ..=-1 => isize::MIN, + 1.. => isize::MAX, + } + }) + }; + minmax(trymul(a.0, b.0)); + minmax(trymul(a.0, b.1)); + minmax(trymul(a.1, b.0)); + minmax(trymul(a.1, b.1)); + if let (Some(min), Some(max)) = (min, max) { + Some((vec![Arc::new(data::int::IntT(min, max))], fails)) + } else { + None + } + }, + None, + ), ) .add_var( "div", - Funcs( - 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", - Funcs( - 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(), + func_math_op( + "div", + |a, b| Some(a / b), + false, + |a, b| a.checked_div(b), + |a, b| { + let mut minmax = (None, None, None, None, b.0 <= 0 && 0 <= b.1); + if a.0 < 0 { + for_a(a.0, b, &mut minmax); + for_a(a.1.min(-1), b, &mut minmax); + } + if a.1 > 0 { + for_a(a.1, b, &mut minmax); + for_a(a.0.max(1), b, &mut minmax); + } + if a.0 <= 0 && 0 <= a.1 { + for_a(0, b, &mut minmax); + } + fn for_a( + a: isize, + b: &data::int::IntT, + minmax: &mut ( + Option, + Option, + Option, + Option, + bool, + ), + ) { + if b.0 < 0 { + for_ab(a, b.0, minmax); + for_ab(a, b.1.min(-1), minmax); + } + if b.1 >= 0 { + for_ab(a, b.1, minmax); + for_ab(a, b.0.max(1), minmax); + } + } + fn for_ab( + a: isize, + b: isize, + minmax: &mut ( + Option, + Option, + Option, + Option, + bool, + ), + ) { + if a.checked_div(b).is_none() { + minmax.4 = true; + } + if let Some(v) = a.checked_div(b).or_else(|| { + if b != 0 { + Some(a.saturating_div(b)) + } else { + None + } + }) { + if v <= 0 { + for_mm(v, &mut minmax.0, &mut minmax.1); + } + if v >= 0 { + for_mm(v, &mut minmax.2, &mut minmax.3); + } + } else { + } + } + fn for_mm(v: isize, min: &mut Option, max: &mut Option) { + if min.is_none() || v < min.unwrap() { + *min = Some(v); + } + if max.is_none() || v > max.unwrap() { + *max = Some(v); + } + } + match minmax { + (Some(w), Some(x), Some(y), Some(z), f) if x == y => { + Some((vec![Arc::new(data::int::IntT(w, z))], f)) + } + (Some(w), Some(x), Some(y), Some(z), f) => Some(( + vec![ + Arc::new(data::int::IntT(w, x)), + Arc::new(data::int::IntT(y, z)), + ], + f, + )), + (Some(w), Some(x), _, _, f) => { + Some((vec![Arc::new(data::int::IntT(w, x))], f)) + } + (_, _, Some(y), Some(z), f) => { + Some((vec![Arc::new(data::int::IntT(y, z))], f)) + } + (_, _, _, _, _) => None, + } + }, + None, + ), ) .add_var( "modulo", - Funcs( - 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(), + func_math_op( + "modulo", + |a, b| Some(a.rem_euclid(b)), + false, + |a, b| a.checked_rem_euclid(b), + |a, b| { + if b.0 == 0 && b.1 == 0 { + None + } else { + let can_fail = + b.0 <= 0 && 0 <= b.1 || (a.0 == isize::MIN && b.0 <= -1 && -1 <= b.1); + Some(( + vec![Arc::new(data::int::IntT(0, { + let mut max = b.1.abs(); + if max > 0 { + max -= 1; + } + if a.0 < 0 { + max + } else { + max.min(a.1) + } + }))], + can_fail, + )) + } + }, + None, + ), + ) + .add_var( + "remainder", + func_math_op( + "remainder", + |a, b| Some(a.rem(b)), + false, + |a, b| a.checked_rem(b), + |a, b| { + if b.0 == 0 && b.1 == 0 { + None + } else { + let can_fail = + b.0 <= 0 && 0 <= b.1 || (a.0 == isize::MIN && b.0 <= -1 && -1 <= b.1); + Some(( + vec![Arc::new({ + let mut max = b.1.abs(); + if max > 0 { + max -= 1; + } + data::int::IntT(-max, max) + })], + can_fail, + )) + } + }, + None, + ), ) .add_var( "abs", Funcs( - fun(|v: isize, _| Ok(v.saturating_abs())), + fun(|v: IntR, _| { + Ok(IntR::(v.0.saturating_abs())) + }), fun(|v: f64, _| Ok(v.abs())), ) .mers_func(), @@ -234,31 +412,201 @@ impl Config { .add_var( "pow", Funcs( - 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), - }) + fun(|(l, r): (IntR, IntR<0, INT_MAX>), _| { + Ok(IntR::( + l.0.saturating_pow(r.0.try_into().unwrap_or(u32::MAX)), + )) }), + fun( + |(l, r): ( + OneOf, f64>, + OneOf, f64>, + ), + _| { + let l = match l { + OneOf::A(v) => v.0 as f64, + OneOf::B(v) => v, + }; + Ok(match r { + OneOf::A(r) => { + if let Ok(r) = r.0.try_into() { + l.powi(r) + } else { + l.powf(r.0 as f64) + } + } + OneOf::B(r) => l.powf(r), + }) + }, + ), ) .mers_func(), ) + .add_var( + "min", + Function::new_generic( + |a, i| { + let mut o = Type::empty(); + for a in &a.types { + if let Some(t) = a.as_any().downcast_ref::().filter(|v| v.0.len() == 2) { + let (a, b) = (&t.0[0], &t.0[1]); + let mut float = false; + for a in &a.types { + if let Some(a) = a.as_any().downcast_ref::().map(Ok).or_else(|| a.as_any().downcast_ref::().map(Err)) { + for b in &b.types { + if let Some(b) = b.as_any().downcast_ref::().map(Ok).or_else(|| b.as_any().downcast_ref::().map(Err)) { + match (a, b) { + (Ok(a), Ok(b)) => { + o.add(Arc::new(data::int::IntT(a.0.min(b.0), a.1.min(b.1)))); + }, + _ => float = true, + } + } else { + return Err(format!("called `min` on a 2-tuple, `{}`, containing a non-`Int/Float` second element.", t.with_info(i)).into()); + } + } + } else { + return Err(format!("called `min` on a 2-tuple, `{}`, containing a non-`Int/Float` first element.", t.with_info(i)).into()); + } + } + if float { + o.add(Arc::new(data::float::FloatT)); + } + } else if let Some(a) = a.iterable() { + let mut int = false; + let mut float = false; + for e in &a.types { + if e.as_any().is::() { + int = true; + } else if e.as_any().is::() { + float = true; + } else { + return Err(format!("called `min` on an iterator over elements of type {}, which is not `Int/Float`", a.with_info(i)).into()); + } + } + if int { + o.add(Arc::new(data::int::IntT(INT_MIN, INT_MAX))); + } + if float { + o.add(Arc::new(data::float::FloatT)); + } + o.add(Arc::new(data::tuple::TupleT(vec![]))); + } else { + return Err(format!( + "cannot call `min` on non-iterable type {}", + a.with_info(i) + ) + .into()); + } + } + Ok(o) + }, + |a, _| { + let mut min_int = None; + let mut min_float = None; + for a in a.get().iterable().expect("called `min` on non-itereable") { + let a = a?; + let a = a.get(); + let a = a.as_any().downcast_ref::().map(|v| Ok(v.0)).or_else(|| a.as_any().downcast_ref::().map(|v| Err(v.0))).expect("found non-Int/Float element in argument to `min`"); + match a { + Ok(a) => if min_int.is_none() || a < min_int.unwrap() { min_int = Some(a); }, + Err(a) => if min_float.is_none() || a < min_float.unwrap() { min_float = Some(a); }, + } + } + Ok(match (min_float, min_int) { + (Some(a), Some(b)) => if a < b as f64 { Data::new(data::float::Float(a)) } else { Data::new(data::int::Int(b))}, + (Some(a), None) => Data::new(data::float::Float(a)), + (None, Some(b)) => Data::new(data::int::Int(b)), + (None, None) => Data::empty_tuple(), + }) + }, + ), + ) + .add_var( + "max", + Function::new_generic( + |a, i| { + let mut o = Type::empty(); + for a in &a.types { + if let Some(t) = a.as_any().downcast_ref::().filter(|v| v.0.len() == 2) { + let (a, b) = (&t.0[0], &t.0[1]); + let mut float = false; + for a in &a.types { + if let Some(a) = a.as_any().downcast_ref::().map(Ok).or_else(|| a.as_any().downcast_ref::().map(Err)) { + for b in &b.types { + if let Some(b) = b.as_any().downcast_ref::().map(Ok).or_else(|| b.as_any().downcast_ref::().map(Err)) { + match (a, b) { + (Ok(a), Ok(b)) => { + o.add(Arc::new(data::int::IntT(a.0.max(b.0), a.1.max(b.1)))); + }, + _ => float = true, + } + } else { + return Err(format!("called `max` on a 2-tuple, `{}`, containing a non-`Int/Float` second element.", t.with_info(i)).into()); + } + } + } else { + return Err(format!("called `max` on a 2-tuple, `{}`, containing a non-`Int/Float` first element.", t.with_info(i)).into()); + } + } + if float { + o.add(Arc::new(data::float::FloatT)); + } + } else if let Some(a) = a.iterable() { + let mut int = false; + let mut float = false; + for e in &a.types { + if e.as_any().is::() { + int = true; + } else if e.as_any().is::() { + float = true; + } else { + return Err(format!("called `max` on an iterator over elements of type {}, which is not `Int/Float`", a.with_info(i)).into()); + } + } + if int { + o.add(Arc::new(data::int::IntT(INT_MIN, INT_MAX))); + } + if float { + o.add(Arc::new(data::float::FloatT)); + } + o.add(Arc::new(data::tuple::TupleT(vec![]))); + } else { + return Err(format!( + "cannot call `max` on non-iterable type {}", + a.with_info(i) + ) + .into()); + } + } + Ok(o) + }, + |a, _| { + let mut min_int = None; + let mut min_float = None; + for a in a.get().iterable().expect("called `min` on non-itereable") { + let a = a?; + let a = a.get(); + let a = a.as_any().downcast_ref::().map(|v| Ok(v.0)).or_else(|| a.as_any().downcast_ref::().map(|v| Err(v.0))).expect("found non-Int/Float element in argument to `min`"); + match a { + Ok(a) => if min_int.is_none() || a < min_int.unwrap() { min_int = Some(a); }, + Err(a) => if min_float.is_none() || a < min_float.unwrap() { min_float = Some(a); }, + } + } + Ok(match (min_float, min_int) { + (Some(a), Some(b)) => if a < b as f64 { Data::new(data::float::Float(a)) } else { Data::new(data::int::Int(b))}, + (Some(a), None) => Data::new(data::float::Float(a)), + (None, Some(b)) => Data::new(data::int::Int(b)), + (None, None) => Data::empty_tuple(), + }) + }, + ), + ) .add_var( "as_float", - func(|v: OneOf, _| { + func(|v: OneOf, f64>, _| { Ok(match v { - OneOf::A(v) => v as f64, + OneOf::A(v) => v.0 as f64, OneOf::B(v) => v, }) }), @@ -285,94 +633,243 @@ impl Config { ) .add_var( "round_to_int", - func(|v: f64, _| -> Result { Ok(isize_from(v.round())) }), + func(|v: f64, _| -> Result, _> { Ok(isize_from(v.round())) }), ) .add_var( "ceil_to_int", - func(|v: f64, _| -> Result { Ok(isize_from(v.ceil())) }), + func(|v: f64, _| -> Result, _> { Ok(isize_from(v.ceil())) }), ) .add_var( "floor_to_int", - func(|v: f64, _| -> Result { Ok(isize_from(v.floor())) }), + func(|v: f64, _| -> Result, _> { Ok(isize_from(v.floor())) }), ) .add_var( "truncate_to_int", - func(|v: f64, _| -> Result { Ok(isize_from(v.trunc())) }), + func(|v: f64, _| -> Result, _> { Ok(isize_from(v.trunc())) }), ) .add_var( "round_ties_even_to_int".to_owned(), - func(|v: f64, _| -> Result { Ok(isize_from(v.round_ties_even())) }), + 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 { +fn isize_from(v: f64) -> IntR { + IntR(if v >= ISIZE_MAX_F { isize::MAX } else if v <= ISIZE_MIN_F { isize::MIN } else { v as isize - } + }) } -fn num_iter_to_num( - func_name: &'static str, - init: Result, - func: impl Fn(Result, Result) -> 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(move |a, i| { - if let Some(a) = a.iterable() { - let int_type = Type::new(data::int::IntT); - if a.is_included_in(&int_type) { - Ok(int_type) - } else { - 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.with_info(i)).into()) +/// for math operations which are fallible for integers (and maybe floats), like `+`, `-`, ... +/// +/// iter_version, if some, is +/// - initial value. if `None`, `()` will be returned for empty iterators. +/// - op_int_may_fail. if `true` and the iterator may contain ints, adds `()` to the return type. this is the int equivalent to `op_float_may_fail`, which, for 2-tuples, is part of `op_int_ranges_with_may_fail_flag`. +fn func_math_op( + funcname: &'static str, + op_float: impl Fn(f64, f64) -> Option + Send + Sync + 'static, + op_float_may_fail: bool, + op_int: impl Fn(isize, isize) -> Option + Send + Sync + 'static, + op_int_ranges_with_may_fail_flag: impl Fn( + &data::int::IntT, + &data::int::IntT, + ) -> Option<(Vec>, bool)> + + Send + + Sync + + 'static, + iter_version: Option<(Option>, bool)>, +) -> Function { + Function::new_generic( + move |a, i| { + let mut o = Type::empty(); + let mut may_fail = false; + for a in &a.types { + 'pre_iter: { + if let Some(a) = a.as_any().downcast_ref::() { + if a.0.len() == 2 { + let (a, b) = (&a.0[0], &a.0[1]); + for a in &a.types { + for b in &b.types { + let a = a.as_any().downcast_ref::().map(Ok).or_else(|| a.as_any().downcast_ref::().map(Err)).ok_or_else(|| format!("`{funcname}`: first argument must be `Int/Float`, but was `{}`.", a.with_info(i)))?; + let b = b.as_any().downcast_ref::().map(Ok).or_else(|| b.as_any().downcast_ref::().map(Err)).ok_or_else(|| format!("`{funcname}`: second argument must be `Int/Float`, but was `{}`.", b.with_info(i)))?; + match (a, b) { + (Ok(a), Ok(b)) => { + if let Some((range, fail)) = + op_int_ranges_with_may_fail_flag(a, b) + { + if fail { + may_fail = true; + } + o.add_all(&Type::newm(range)); + } else { + may_fail = true; + // always fails, no need to add any types + } + } + _ => { + if op_float_may_fail { + may_fail = true; + } + o.add(Arc::new(data::float::FloatT)); + } + } + } + } + break 'pre_iter; } } + // ITER VERSION + if let Some((init, f_int)) = &iter_version { + if let Some(it) = a.iterable() { + let mut is_int = false; + let mut is_float = false; + for a in &it.types { + if a.as_any().is::() { + is_int = true; + } else if a.as_any().is::() { + is_float = true; + } else { + return Err(format!( + "cannot call `{funcname}` on an iterator over type `{}`, because it contains the non-`Int/Float` type {}.", it.with_info(i), a.with_info(i) + ) + .into()); + } + } + let (o_int, o_float) = match init { + None => { + // if the iterator is empty + may_fail = true; + (is_int, is_float) + } + Some(Ok(_)) => { + if is_int && *f_int { + may_fail = true; + } + if is_float && op_float_may_fail { + may_fail = true; + } + (true, is_float) + } + Some(Err(_)) => { + if is_float && op_float_may_fail { + may_fail = true; + } + (false, true) + } + }; + if o_int { + o.add(Arc::new(data::tuple::TupleT(vec![Type::new( + data::int::IntT(INT_MIN, INT_MAX), + )]))); + } + if o_float { + o.add(Arc::new(data::tuple::TupleT(vec![Type::new( + data::float::FloatT, + )]))); + } + } else { + return Err(format!( + "cannot call `{funcname}` on non-iterable type `{}`", + a.with_info(i) + ) + .into()); + } + } else { + return Err(format!( + "cannot call `{funcname}` on non-2-tuple type `{}`", + a.with_info(i) + ) + .into()); + } } - } else { - Err(format!("argument passed to {func_name} must be an iterator").into()) } - })), - run: Arc::new(move |a, _i| { - let mut out = init; - for v in a.get().iterable().unwrap() { - let v = v?; - let v = v.get(); - let v = v.as_any(); - let v = v - .downcast_ref::() - .map(|v| Ok(v.0)) - .unwrap_or_else(|| { - Err(v - .downcast_ref::() - .expect("value used in num-iterator function was not a number") - .0) - }); - out = func(out, v); + if may_fail { + o.add(Arc::new(data::tuple::TupleT(vec![]))); } - Ok(match out { - Ok(v) => Data::new(data::int::Int(v)), - Err(v) => Data::new(data::float::Float(v)), - }) - }), - inner_statements: None, - } + Ok(o) + }, + move |a, _| { + let a = a.get(); + Ok( + if let Some(a) = &a + .as_any() + .downcast_ref::() + .map(|v| &v.0) + .filter(|v| v.len() == 2) + { + let (a, b) = (&a[0], &a[1]); + let (a, b) = (a.get(), b.get()); + let a = a + .as_any() + .downcast_ref::() + .map(Ok) + .or_else(|| a.as_any().downcast_ref::().map(Err)) + .unwrap(); + let b = b + .as_any() + .downcast_ref::() + .map(Ok) + .or_else(|| b.as_any().downcast_ref::().map(Err)) + .unwrap(); + if let Some(v) = match (a, b) { + (Ok(a), Ok(b)) => op_int(a.0, b.0).map(data::int::Int).map(Data::new), + (Ok(a), Err(b)) => op_float(a.0 as f64, b.0) + .map(data::float::Float) + .map(Data::new), + (Err(a), Ok(b)) => op_float(a.0, b.0 as f64) + .map(data::float::Float) + .map(Data::new), + (Err(a), Err(b)) => { + op_float(a.0, b.0).map(data::float::Float).map(Data::new) + } + } { + v + } else { + Data::empty_tuple() + } + } else { + let (mut acc, _) = iter_version + .expect("no iter version for this math op, but argument not a 2-tuple..."); + for a in a + .iterable() + .expect("math op with iter version called on non-iterable") + { + let a = a?; + let a = a.get(); + let a = a + .as_any() + .downcast_ref::() + .map(Ok) + .or_else(|| a.as_any().downcast_ref::().map(Err)) + .unwrap(); + acc = if let Some(acc) = acc { + match (acc, a) { + (Ok(a), Ok(b)) => op_int(a, b.0).map(Ok), + (Ok(a), Err(b)) => op_float(a as f64, b.0).map(Err), + (Err(a), Ok(b)) => op_float(a, b.0 as f64).map(Err), + (Err(a), Err(b)) => op_float(a, b.0).map(Err), + } + } else { + Some(a.map(|v| v.0).map_err(|v| v.0)) + }; + } + match acc { + None => Data::empty_tuple(), + Some(v) => match v { + Ok(v) => Data::new(data::int::Int(v)), + Err(v) => Data::new(data::float::Float(v)), + }, + } + }, + ) + }, + ) } diff --git a/mers_lib/src/program/configs/with_string.rs b/mers_lib/src/program/configs/with_string.rs index 69b7d35..b236df9 100755 --- a/mers_lib/src/program/configs/with_string.rs +++ b/mers_lib/src/program/configs/with_string.rs @@ -1,7 +1,11 @@ -use crate::data::{self, Data, MersDataWInfo, Type}; +use crate::data::{ + self, + int::{INT_MAX, INT_MIN}, + Data, MersDataWInfo, Type, +}; use super::{ - gen::{function::func, AnyOrNone, IterToList, OneOf, OneOrNone}, + gen::{function::func, AnyOrNone, IntR, IterToList, OneOf, OneOrNone}, util, Config, }; @@ -21,11 +25,17 @@ impl Config { self.add_var("trim", func(|v: &str, _| Ok(v.trim().to_owned()))) .add_var( "index_of", - func(|(v, p): (&str, &str), _| Ok(OneOrNone(v.find(p).map(|v| v as isize)))), + func(|(v, p): (&str, &str), _| { + Ok(OneOrNone(v.find(p).map(|v| IntR::<0, INT_MAX>(v as isize)))) + }), ) .add_var( "index_of_rev", - func(|(v, p): (&str, &str), _| Ok(OneOrNone(v.rfind(p).map(|v| v as isize)))), + func(|(v, p): (&str, &str), _| { + Ok(OneOrNone( + v.rfind(p).map(|v| IntR::<0, INT_MAX>(v as isize)), + )) + }), ) .add_var( "starts_with", @@ -92,31 +102,37 @@ impl Config { ) .add_var( "substring", - 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)), - }; - let start = if start < 0 { - s.len().saturating_sub(start.abs() as usize) - } else { - start as usize - }; - let end = end - .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(String::new()); - } - Ok(s[start..end].to_owned()) - }), + func( + |v: OneOf< + (&str, IntR), + (&str, IntR, IntR), + >, + _| { + let (s, start, end) = match v { + OneOf::A((t, s)) => (t, s.0, None), + OneOf::B((t, s, e)) => (t, s.0, Some(e.0)), + }; + let start = if start < 0 { + s.len().saturating_sub(start.abs() as usize) + } else { + start as usize + }; + let end = end + .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(String::new()); + } + Ok(s[start..end].to_owned()) + }, + ), ) } } diff --git a/mers_lib/tests/lang.rs b/mers_lib/tests/lang.rs index cf5aad2..b4e9631 100644 --- a/mers_lib/tests/lang.rs +++ b/mers_lib/tests/lang.rs @@ -15,8 +15,8 @@ fn variable() -> Res { assert_eq!( run_code(Config::new(), format!("x := {n}, x"))?, TypedData( - Type::new(data::int::IntT), - Data::new(data::int::Int(n as _)), + Type::new(data::int::IntT(n, n)), + Data::new(data::int::Int(n)), mers_lib::info::Info::neverused(), ) ); @@ -27,9 +27,9 @@ fn variable() -> Res { #[test] fn mutating_a_variable() -> Res { assert_eq!( - run_code(Config::new(), "x := 5, &x = 2, x")?, + run_code(Config::new(), "x := [Int<2..5>] 5, &x = 2, x")?, TypedData( - Type::new(data::int::IntT), + Type::new(data::int::IntT(2, 5)), Data::new(data::int::Int(2)), mers_lib::info::Info::neverused() ), @@ -40,9 +40,9 @@ fn mutating_a_variable() -> Res { #[test] fn variable_shadowing() -> Res { assert_eq!( - run_code(Config::new(), "x := 5, { x := 2, &x = 3 }, x")?, + run_code(Config::new(), "x := 5, { x := 2, &x = 2 }, x")?, TypedData( - Type::new(data::int::IntT), + Type::new(data::int::IntT(5, 5)), Data::new(data::int::Int(5)), mers_lib::info::Info::neverused() ) @@ -55,7 +55,7 @@ fn identity_function() -> Res { assert_eq!( run_code(Config::new(), "id := x -> x, 4.id")?, TypedData( - Type::new(data::int::IntT), + Type::new(data::int::IntT(4, 4)), Data::new(data::int::Int(4)), mers_lib::info::Info::neverused() )