From 923652eca9c7c7c26e07df8a9ee52559207b19a0 Mon Sep 17 00:00:00 2001 From: Mark <> Date: Thu, 7 Nov 2024 16:53:15 +0100 Subject: [PATCH] add iterable ranges (range_inc / range_exc) --- mers/Cargo.toml | 2 +- mers_lib/Cargo.toml | 2 +- mers_lib/src/data/float.rs | 2 +- mers_lib/src/data/int.rs | 2 +- mers_lib/src/program/configs/with_iters.rs | 190 ++++++++++++++++++++- 5 files changed, 193 insertions(+), 5 deletions(-) diff --git a/mers/Cargo.toml b/mers/Cargo.toml index e23f57c..5589f51 100644 --- a/mers/Cargo.toml +++ b/mers/Cargo.toml @@ -15,7 +15,7 @@ default = ["colored-output"] colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"] [dependencies] -mers_lib = "0.9.14" +mers_lib = "0.9.15" # 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 480ebf0..80f354f 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers_lib" -version = "0.9.14" +version = "0.9.15" 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/float.rs b/mers_lib/src/data/float.rs index 073440e..d590872 100755 --- a/mers_lib/src/data/float.rs +++ b/mers_lib/src/data/float.rs @@ -4,7 +4,7 @@ use crate::info::DisplayInfo; use super::{MersData, MersType, Type}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Float(pub f64); impl MersData for Float { diff --git a/mers_lib/src/data/int.rs b/mers_lib/src/data/int.rs index b448c0e..1c9e60c 100755 --- a/mers_lib/src/data/int.rs +++ b/mers_lib/src/data/int.rs @@ -41,7 +41,7 @@ pub const INT32U_MAX: isize = if isize::BITS > u32::BITS { isize::MAX }; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Int(pub isize); impl MersData for Int { diff --git a/mers_lib/src/program/configs/with_iters.rs b/mers_lib/src/program/configs/with_iters.rs index 565c7b2..db64dc3 100755 --- a/mers_lib/src/program/configs/with_iters.rs +++ b/mers_lib/src/program/configs/with_iters.rs @@ -7,7 +7,7 @@ use crate::{ data::{ self, function::{Function, FunctionT}, - int::INT_MAX, + int::{Int, IntT, INT_MAX}, Data, MersData, MersType, MersTypeWInfo, Type, }, errors::CheckError, @@ -28,8 +28,95 @@ impl Config { /// `enumerate: fn` transforms an iterator over T into one over (Int, T), where Int is the index of the element /// `any: fn` returns true if any element of the iterator are true /// `all: fn` returns true if all elements of the iterator are true + /// `range_inc: fn` returns an iterable `Range` starting at the first argument, counting up to the second one (inclusive). + /// `range_exc: fn` returns an iterable `Range` starting at the first argument, counting up to the second one (exclusive). pub fn with_iters(self) -> Self { self + .add_type("Range".to_owned(), Err(Arc::new(|str, _i| { + if let Some((val, end)) = str.split_once("..") { + if let (Ok(val), Ok(end)) = (val.trim().parse(), end.trim().parse()) { + Ok(RangeT(val, end)) + } else { + Err(CheckError::from(format!("bad Range type, got <{str}> but expected where start and end are Ints"))) + } + } else { + Err(CheckError::from(format!("bad Range type, got <{str}> but expected "))) + }.map(|v| Arc::new(Type::new(v))) + }))) + .add_var("range_inc", Function::new_generic( + |a, i| { + let mut o = Type::empty(); + for a in &a.types { + let a = a.as_any().downcast_ref::().ok_or_else(|| CheckError::from(format!("expected 2- or 3-tuple, but found {}", a.with_info(i))))?; + if a.0.len() == 2 { + let mut min = None; + let mut max = None; + for v in a.0[0].types.iter().chain(a.0[1].types.iter()) { + let v = v.as_any().downcast_ref::().ok_or_else(|| CheckError::from(format!("expected int as first argument, but got {}", v.with_info(i))))?; + if min.is_none_or(|min| min > v.0) { + min = Some(v.0); + } + if max.is_none_or(|max| max < v.1) { + max = Some(v.1); + } + } + if let (Some(min), Some(max)) = (min, max) { + o.add(Arc::new(RangeT(min, max))); + } + } else { + return Err(CheckError::from(format!("expected 2-tuple, but found {}", a.with_info(i)))); + } + } + Ok(o) + }, |a, _| { + let a = a.get(); + let a = a.as_any().downcast_ref::().unwrap(); + let (v, e) = (a.0[0].get(), a.0[1].get()); + let (v, e) = (v.as_any().downcast_ref::().unwrap(), e.as_any().downcast_ref::().unwrap()); + Ok(Data::new(Range(v.0, e.0))) + } + )) + .add_var("range_exc", Function::new_generic( + |a, i| { + let mut o = Type::empty(); + for a in &a.types { + let a = a.as_any().downcast_ref::().ok_or_else(|| CheckError::from(format!("expected 2- or 3-tuple, but found {}", a.with_info(i))))?; + if a.0.len() == 2 { + let mut min = None; + let mut max = None; + for v in a.0[0].types.iter().chain(a.0[1].types.iter()) { + let v = v.as_any().downcast_ref::().ok_or_else(|| CheckError::from(format!("expected int as first argument, but got {}", v.with_info(i))))?; + if min.is_none_or(|min| min > v.0) { + min = Some(v.0); + } + if max.is_none_or(|max| max < v.1) { + max = Some(v.1); + } + } + if let (Some(min), Some(max)) = (min, max) { + if let Some(max) = max.checked_sub(1) { + o.add(Arc::new(RangeT(min, max))); + } else { + o.add(Arc::new(RangeT(min.saturating_add(1), max))); + } + } + } else { + return Err(CheckError::from(format!("expected 2-tuple, but found {}", a.with_info(i)))); + } + } + Ok(o) + }, |a, _| { + let a = a.get(); + let a = a.as_any().downcast_ref::().unwrap(); + let (v, e) = (a.0[0].get(), a.0[1].get()); + let (v, e) = (v.as_any().downcast_ref::().unwrap(), e.as_any().downcast_ref::().unwrap()); + if let Some(e) = e.0.checked_sub(1) { + Ok(Data::new(Range(v.0, e))) + } else { + Ok(Data::new(Range(v.0.saturating_add(1), e.0))) + } + } + )) .add_var("any", genfunc_iter_in_val_out("all".to_string(), data::bool::bool_type(), data::bool::bool_type(), |a, i| { for v in a.get().iterable(&i.global).unwrap().map(|v| v.map(|v| v.get().as_any().downcast_ref::().is_some_and(|v| v.0))) { if v? { @@ -586,3 +673,104 @@ fn genfunc_iter_in_val_out( inner_statements: None, } } + +#[derive(Clone, Debug, PartialEq)] +pub struct Range(isize, isize); +#[derive(Clone, Debug, PartialEq)] +pub struct RangeT(isize, isize); +impl MersData for Range { + fn display(&self, _info: &DisplayInfo, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}.range_inc({})", self.0, self.1) + } + fn iterable( + &self, + _gi: &crate::program::run::RunLocalGlobalInfo, + ) -> Option>>> { + Some(Box::new( + RangeInt(self.0, self.1, false).map(|v| Ok(Data::new(Int(v)))), + )) + } + fn clone(&self) -> Box { + Box::new(Clone::clone(self)) + } + fn is_eq(&self, other: &dyn MersData) -> bool { + other + .as_any() + .downcast_ref::() + .is_some_and(|other| other == self) + } + fn as_type(&self) -> Type { + Type::new(RangeT(self.0, self.1)) + } + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn mut_any(&mut self) -> &mut dyn std::any::Any { + self + } + fn to_any(self) -> Box { + Box::new(self) + } +} +impl MersType for RangeT { + fn display( + &self, + _info: &crate::info::DisplayInfo<'_>, + f: &mut std::fmt::Formatter, + ) -> std::fmt::Result { + write!(f, "Range<{}..{}>", self.0, self.1) + } + fn iterable(&self) -> Option { + Some(Type::new(IntT(self.0, self.0.max(self.1)))) + } + fn is_same_type_as(&self, other: &dyn MersType) -> bool { + other + .as_any() + .downcast_ref::() + .is_some_and(|other| *self == *other) + } + fn is_included_in(&self, target: &dyn MersType) -> bool { + target + .as_any() + .downcast_ref::() + .is_some_and(|target| { + // prolly good + self.is_empty() || (!target.is_empty() && self.0 >= target.0 && self.1 <= target.1) + }) + } + fn subtypes(&self, acc: &mut Type) { + acc.add(Arc::new(Clone::clone(self))) + } + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn mut_any(&mut self) -> &mut dyn std::any::Any { + self + } + fn to_any(self) -> Box { + Box::new(self) + } +} +impl RangeT { + pub fn is_empty(&self) -> bool { + self.1 < self.0 + } +} + +struct RangeInt(isize, isize, bool); +impl Iterator for RangeInt { + type Item = isize; + fn next(&mut self) -> Option { + if !self.2 { + let o = self.0; + if self.0 < self.1 { + self.0 += 1; + } else { + self.2 = true; + } + Some(o) + } else { + None + } + } +}