mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
the math does math... at compile time, in types
This commit is contained in:
parent
c357e192a0
commit
754dd5df40
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mers"
|
name = "mers"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "dynamically typed but type-checked programming language"
|
description = "dynamically typed but type-checked programming language"
|
||||||
@ -15,7 +15,7 @@ default = ["colored-output"]
|
|||||||
colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"]
|
colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mers_lib = "0.9.3"
|
mers_lib = "0.9.4"
|
||||||
# mers_lib = { path = "../mers_lib" }
|
# mers_lib = { path = "../mers_lib" }
|
||||||
clap = { version = "4.3.19", features = ["derive"] }
|
clap = { version = "4.3.19", features = ["derive"] }
|
||||||
colored = { version = "2.1.0", optional = true }
|
colored = { version = "2.1.0", optional = true }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mers_lib"
|
name = "mers_lib"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "library to use the mers language in other projects"
|
description = "library to use the mers language in other projects"
|
||||||
|
@ -51,13 +51,13 @@ impl Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_generic(
|
pub fn new_generic(
|
||||||
out: impl Fn(&Type) -> Result<Type, CheckError> + Send + Sync + 'static,
|
out: impl Fn(&Type, &mut CheckInfo) -> Result<Type, CheckError> + Send + Sync + 'static,
|
||||||
run: impl Fn(Data, &mut Info) -> Result<Data, CheckError> + Send + Sync + 'static,
|
run: impl Fn(Data, &mut Info) -> Result<Data, CheckError> + Send + Sync + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
info: crate::info::Info::neverused(),
|
info: crate::info::Info::neverused(),
|
||||||
info_check: Arc::new(Mutex::new(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),
|
run: Arc::new(run),
|
||||||
inner_statements: None,
|
inner_statements: None,
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,43 @@ use crate::info::DisplayInfo;
|
|||||||
|
|
||||||
use super::{MersData, MersType, Type};
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Int(pub isize);
|
pub struct Int(pub isize);
|
||||||
|
|
||||||
@ -22,7 +59,7 @@ impl MersData for Int {
|
|||||||
Box::new(Clone::clone(self))
|
Box::new(Clone::clone(self))
|
||||||
}
|
}
|
||||||
fn as_type(&self) -> super::Type {
|
fn as_type(&self) -> super::Type {
|
||||||
Type::new(IntT)
|
Type::new(IntT(self.0, self.0))
|
||||||
}
|
}
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
@ -35,8 +72,8 @@ impl MersData for Int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct IntT;
|
pub struct IntT(pub isize, pub isize);
|
||||||
impl MersType for IntT {
|
impl MersType for IntT {
|
||||||
fn display(
|
fn display(
|
||||||
&self,
|
&self,
|
||||||
@ -46,13 +83,35 @@ impl MersType for IntT {
|
|||||||
write!(f, "{self}")
|
write!(f, "{self}")
|
||||||
}
|
}
|
||||||
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
fn is_same_type_as(&self, other: &dyn MersType) -> bool {
|
||||||
other.as_any().downcast_ref::<Self>().is_some()
|
if let Some(other) = other.as_any().downcast_ref::<Self>() {
|
||||||
|
self == other
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn is_included_in(&self, target: &dyn MersType) -> bool {
|
fn is_included_in(&self, target: &dyn MersType) -> bool {
|
||||||
self.is_same_type_as(target)
|
if let Some(target) = target.as_any().downcast_ref::<Self>() {
|
||||||
|
target.0 <= self.0 && self.1 <= target.1
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn subtypes(&self, acc: &mut Type) {
|
fn subtypes(&self, acc: &mut Type) {
|
||||||
acc.add(Arc::new(self.clone()));
|
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 {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
@ -72,6 +131,12 @@ impl Display for Int {
|
|||||||
}
|
}
|
||||||
impl Display for IntT {
|
impl Display for IntT {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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}>"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,6 +413,39 @@ impl Type {
|
|||||||
let n = new.as_any();
|
let n = new.as_any();
|
||||||
if let Some(s) = n.downcast_ref::<Self>() {
|
if let Some(s) = n.downcast_ref::<Self>() {
|
||||||
self.add_all(s);
|
self.add_all(s);
|
||||||
|
} else if let Some(n) = n.downcast_ref::<crate::data::int::IntT>() {
|
||||||
|
let n = n.clone();
|
||||||
|
let mut newt = None;
|
||||||
|
for a in &self.types {
|
||||||
|
if let Some(t) = a.as_any().downcast_ref::<crate::data::int::IntT>() {
|
||||||
|
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::<crate::data::int::IntT>() {
|
||||||
|
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 {
|
} else {
|
||||||
if !self.types.iter().any(|t| new.is_included_in(t.as_ref())) {
|
if !self.types.iter().any(|t| new.is_included_in(t.as_ref())) {
|
||||||
self.types.push(new);
|
self.types.push(new);
|
||||||
|
@ -277,11 +277,11 @@ pub fn type_from_parsed(
|
|||||||
.find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v))
|
.find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v))
|
||||||
{
|
{
|
||||||
Some(Ok(t)) => as_type.add_all(&*t),
|
Some(Ok(t)) => as_type.add_all(&*t),
|
||||||
Some(Err(_)) => {
|
Some(Err(f)) => as_type.add_all(&*f("", info).map_err(|e| {
|
||||||
return Err(CheckError::new().msg_str(format!(
|
e.msg_str(format!(
|
||||||
"Type: specified type without info, but type needs additional info"
|
"Note: specified type `{name}` without info, but type needs additional info: `{name}<...>`"
|
||||||
)))
|
))
|
||||||
}
|
})?),
|
||||||
None => return Err(CheckError::new().msg_str(format!("Unknown type '{name}'"))),
|
None => return Err(CheckError::new().msg_str(format!("Unknown type '{name}'"))),
|
||||||
},
|
},
|
||||||
ParsedType::TypeWithInfo(name, additional_info) => match info
|
ParsedType::TypeWithInfo(name, additional_info) => match info
|
||||||
|
@ -379,27 +379,33 @@ impl ToMersData for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromMersData for isize {
|
/// An integer within the range `N..=M`
|
||||||
|
pub struct IntR<const N: isize, const M: isize>(pub isize);
|
||||||
|
impl<const N: isize, const M: isize> FromMersData for IntR<N, M> {
|
||||||
fn as_type_from() -> Type {
|
fn as_type_from() -> Type {
|
||||||
Self::as_type_to()
|
Self::as_type_to()
|
||||||
}
|
}
|
||||||
fn can_represent(t: &Type) -> bool {
|
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, F: FnOnce(Option<Self>) -> O>(d: &(impl MersData + ?Sized), f: F) -> O {
|
fn try_represent<O, F: FnOnce(Option<Self>) -> O>(d: &(impl MersData + ?Sized), f: F) -> O {
|
||||||
if let Some(v) = d.as_any().downcast_ref::<data::int::Int>() {
|
if let Some(v) = d.as_any().downcast_ref::<data::int::Int>() {
|
||||||
f(Some(v.0))
|
if N <= v.0 && v.0 <= M {
|
||||||
|
f(Some(Self(v.0)))
|
||||||
|
} else {
|
||||||
|
f(None)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
f(None)
|
f(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ToMersData for isize {
|
impl<const N: isize, const M: isize> ToMersData for IntR<N, M> {
|
||||||
fn as_type_to() -> Type {
|
fn as_type_to() -> Type {
|
||||||
Type::new(data::int::IntT)
|
Type::new(data::int::IntT(N, M))
|
||||||
}
|
}
|
||||||
fn represent(self) -> Data {
|
fn represent(self) -> Data {
|
||||||
Data::new(data::int::Int(self))
|
Data::new(data::int::Int(self.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{self, Data, MersData, Type},
|
data::{
|
||||||
|
self,
|
||||||
|
int::{INT_MAX, INT_MIN},
|
||||||
|
Data, MersData, Type,
|
||||||
|
},
|
||||||
errors::CheckError,
|
errors::CheckError,
|
||||||
info::Local,
|
info::Local,
|
||||||
program::run::{CheckInfo, CheckLocalGlobalInfo, RunLocalGlobalInfo},
|
program::run::{CheckInfo, CheckLocalGlobalInfo, RunLocalGlobalInfo},
|
||||||
@ -92,7 +96,42 @@ impl Config {
|
|||||||
.types
|
.types
|
||||||
.insert("Bool".to_owned(), Ok(Arc::new(data::bool::bool_type())));
|
.insert("Bool".to_owned(), Ok(Arc::new(data::bool::bool_type())));
|
||||||
init_d!(data::byte::ByteT);
|
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<n>` for a specific number, `Int<n..>` for all numbers `>=n`, `Int<..m>` for all numbers `<=m`, or `Int<n..m>` for all numbers `>=n` and `<= m`.")));
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
})),
|
||||||
|
);
|
||||||
init_d!(data::float::FloatT);
|
init_d!(data::float::FloatT);
|
||||||
init_d!(data::string::StringT);
|
init_d!(data::string::StringT);
|
||||||
Self {
|
Self {
|
||||||
|
@ -4,7 +4,12 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
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,
|
errors::CheckError,
|
||||||
program::run::{CheckInfo, Info},
|
program::run::{CheckInfo, Info},
|
||||||
};
|
};
|
||||||
@ -12,7 +17,7 @@ use crate::{
|
|||||||
use super::{
|
use super::{
|
||||||
gen::{
|
gen::{
|
||||||
function::{func, func_end, func_err},
|
function::{func, func_end, func_err},
|
||||||
OneOf,
|
IntR, OneOf,
|
||||||
},
|
},
|
||||||
Config,
|
Config,
|
||||||
};
|
};
|
||||||
@ -75,9 +80,9 @@ impl Config {
|
|||||||
}),
|
}),
|
||||||
inner_statements: None,
|
inner_statements: None,
|
||||||
})
|
})
|
||||||
.add_var("sleep", func(|dur: OneOf<isize, f64>, i| {
|
.add_var("sleep", func(|dur: OneOf<IntR<0, INT_MAX>, f64>, i| {
|
||||||
let mut sleep_dur = match dur {
|
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)),
|
OneOf::B(dur) => Duration::from_secs_f64(dur.max(0.0)),
|
||||||
};
|
};
|
||||||
// limit how long sleep can take
|
// limit how long sleep can take
|
||||||
@ -87,8 +92,8 @@ impl Config {
|
|||||||
std::thread::sleep(sleep_dur);
|
std::thread::sleep(sleep_dur);
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
}))
|
||||||
.add_var("exit", func_end(|code: isize, _| {
|
.add_var("exit", func_end(|code: IntR<INT_MIN, INT_MAX>, _| {
|
||||||
std::process::exit(code.try_into().unwrap_or(255));
|
std::process::exit(code.0.try_into().unwrap_or(255));
|
||||||
}))
|
}))
|
||||||
.add_var("panic", func_err(|message: &str, _| {
|
.add_var("panic", func_err(|message: &str, _| {
|
||||||
CheckError::from(message)
|
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());
|
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| {
|
run: Arc::new(|a, _i| {
|
||||||
Ok(Data::new(data::int::Int(if let Some(t) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
Ok(Data::new(data::int::Int(if let Some(t) = a.get().as_any().downcast_ref::<data::tuple::Tuple>() {
|
||||||
t.0.len() as _
|
t.0.len().try_into().unwrap_or(INT_MAX)
|
||||||
} else if let Some(s) = a.get().as_any().downcast_ref::<data::string::String>() {
|
} else if let Some(s) = a.get().as_any().downcast_ref::<data::string::String>() {
|
||||||
s.0.len() as _
|
s.0.len().try_into().unwrap_or(INT_MAX)
|
||||||
} else if let Some(i) = a.get().iterable() {
|
} else if let Some(i) = a.get().iterable() {
|
||||||
// -1 if more elements than isize can represent
|
i.count().try_into().unwrap_or(INT_MAX)
|
||||||
i.take(isize::MAX as usize + 1).count().try_into().unwrap_or(-1)
|
|
||||||
} else {
|
} 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,
|
inner_statements: None,
|
||||||
|
@ -6,7 +6,12 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
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,
|
errors::CheckError,
|
||||||
info::DisplayInfo,
|
info::DisplayInfo,
|
||||||
program::{self, run::CheckInfo},
|
program::{self, run::CheckInfo},
|
||||||
@ -34,7 +39,7 @@ impl Config {
|
|||||||
if a.types.iter().all(|t| t.as_any().downcast_ref::<data::tuple::TupleT>().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)))) {
|
if a.types.iter().all(|t| t.as_any().downcast_ref::<data::tuple::TupleT>().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![
|
Ok(Type::newm(vec![
|
||||||
Arc::new(data::tuple::TupleT(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),
|
||||||
Type::new(data::string::StringT),
|
Type::new(data::string::StringT),
|
||||||
])),
|
])),
|
||||||
@ -160,7 +165,7 @@ impl Config {
|
|||||||
out: Ok(Arc::new(|a, i| {
|
out: Ok(Arc::new(|a, i| {
|
||||||
if a.is_included_in_single(&ChildProcessT) {
|
if a.is_included_in_single(&ChildProcessT) {
|
||||||
Ok(Type::newm(vec![
|
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::TrueT),
|
||||||
Arc::new(data::bool::FalseT),
|
Arc::new(data::bool::FalseT),
|
||||||
Arc::new(data::tuple::TupleT(vec![])),
|
Arc::new(data::tuple::TupleT(vec![])),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{self, Data, MersTypeWInfo, Type},
|
data::{self, int::INT_MAX, Data, MersTypeWInfo, Type},
|
||||||
program::{self, run::CheckInfo},
|
program::{self, run::CheckInfo},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ impl Config {
|
|||||||
if t.0.len() != 2 {
|
if t.0.len() != 2 {
|
||||||
return Err(format!("called get on tuple with len != 2").into());
|
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!(
|
return Err(format!(
|
||||||
"called get with non-int index of type {}",
|
"called get with non-int index of type {}",
|
||||||
t.0[1].with_info(i)
|
t.0[1].with_info(i)
|
||||||
|
@ -7,6 +7,7 @@ use crate::{
|
|||||||
data::{
|
data::{
|
||||||
self,
|
self,
|
||||||
function::{Function, FunctionT},
|
function::{Function, FunctionT},
|
||||||
|
int::INT_MAX,
|
||||||
Data, MersData, MersType, MersTypeWInfo, Type,
|
Data, MersData, MersType, MersTypeWInfo, Type,
|
||||||
},
|
},
|
||||||
errors::CheckError,
|
errors::CheckError,
|
||||||
@ -128,8 +129,8 @@ impl Config {
|
|||||||
genfunc_iter_and_func("map_while", ItersT::MapWhile, Iters::MapWhile),
|
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| {
|
.add_var("take", genfunc_iter_and_arg("take", |_: &data::int::IntT| ItersT::Take, |v: &data::int::Int| {
|
||||||
Iters::Take(v.0.max(0) as _)
|
Iters::Take(v.0.max(0).try_into().unwrap_or(usize::MAX))
|
||||||
}, &data::int::IntT))
|
}, &data::int::IntT(0, INT_MAX)))
|
||||||
.add_var(
|
.add_var(
|
||||||
"enumerate",
|
"enumerate",
|
||||||
data::function::Function {
|
data::function::Function {
|
||||||
@ -463,7 +464,7 @@ impl IterT {
|
|||||||
}
|
}
|
||||||
ItersT::Take => data.clone(),
|
ItersT::Take => data.clone(),
|
||||||
ItersT::Enumerate => Type::new(data::tuple::TupleT(vec![
|
ItersT::Enumerate => Type::new(data::tuple::TupleT(vec![
|
||||||
Type::new(data::int::IntT),
|
Type::new(data::int::IntT(0, INT_MAX)),
|
||||||
data.clone(),
|
data.clone(),
|
||||||
])),
|
])),
|
||||||
ItersT::Chained => {
|
ItersT::Chained => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{self, Data, MersData, MersType, MersTypeWInfo, Type},
|
data::{self, int::INT_MAX, Data, MersData, MersType, MersTypeWInfo, Type},
|
||||||
errors::CheckError,
|
errors::CheckError,
|
||||||
info::DisplayInfo,
|
info::DisplayInfo,
|
||||||
parsing::{statements::to_string_literal, Source},
|
parsing::{statements::to_string_literal, Source},
|
||||||
@ -41,7 +41,7 @@ impl Config {
|
|||||||
"get_mut: argument must be a 2-tuple `(&List<_>, Int)`."
|
"get_mut: argument must be a 2-tuple `(&List<_>, Int)`."
|
||||||
).into());
|
).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() {
|
if let Some(t) = t.0[0].dereference() {
|
||||||
for t in t.types.iter() {
|
for t in t.types.iter() {
|
||||||
if let Some(t) = t.as_any().downcast_ref::<ListT>() {
|
if let Some(t) = t.as_any().downcast_ref::<ListT>() {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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::{
|
use super::{
|
||||||
gen::{function::func, AnyOrNone, IterToList, OneOf, OneOrNone},
|
gen::{function::func, AnyOrNone, IntR, IterToList, OneOf, OneOrNone},
|
||||||
util, Config,
|
util, Config,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,11 +25,17 @@ impl Config {
|
|||||||
self.add_var("trim", func(|v: &str, _| Ok(v.trim().to_owned())))
|
self.add_var("trim", func(|v: &str, _| Ok(v.trim().to_owned())))
|
||||||
.add_var(
|
.add_var(
|
||||||
"index_of",
|
"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(
|
.add_var(
|
||||||
"index_of_rev",
|
"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(
|
.add_var(
|
||||||
"starts_with",
|
"starts_with",
|
||||||
@ -92,10 +102,15 @@ impl Config {
|
|||||||
)
|
)
|
||||||
.add_var(
|
.add_var(
|
||||||
"substring",
|
"substring",
|
||||||
func(|v: OneOf<(&str, isize), (&str, isize, isize)>, _| {
|
func(
|
||||||
|
|v: OneOf<
|
||||||
|
(&str, IntR<INT_MIN, INT_MAX>),
|
||||||
|
(&str, IntR<INT_MIN, INT_MAX>, IntR<INT_MIN, INT_MAX>),
|
||||||
|
>,
|
||||||
|
_| {
|
||||||
let (s, start, end) = match v {
|
let (s, start, end) = match v {
|
||||||
OneOf::A((t, s)) => (t, s, None),
|
OneOf::A((t, s)) => (t, s.0, None),
|
||||||
OneOf::B((t, s, e)) => (t, s, Some(e)),
|
OneOf::B((t, s, e)) => (t, s.0, Some(e.0)),
|
||||||
};
|
};
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
s.len().saturating_sub(start.abs() as usize)
|
s.len().saturating_sub(start.abs() as usize)
|
||||||
@ -116,7 +131,8 @@ impl Config {
|
|||||||
return Ok(String::new());
|
return Ok(String::new());
|
||||||
}
|
}
|
||||||
Ok(s[start..end].to_owned())
|
Ok(s[start..end].to_owned())
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ fn variable() -> Res {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_code(Config::new(), format!("x := {n}, x"))?,
|
run_code(Config::new(), format!("x := {n}, x"))?,
|
||||||
TypedData(
|
TypedData(
|
||||||
Type::new(data::int::IntT),
|
Type::new(data::int::IntT(n, n)),
|
||||||
Data::new(data::int::Int(n as _)),
|
Data::new(data::int::Int(n)),
|
||||||
mers_lib::info::Info::neverused(),
|
mers_lib::info::Info::neverused(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -27,9 +27,9 @@ fn variable() -> Res {
|
|||||||
#[test]
|
#[test]
|
||||||
fn mutating_a_variable() -> Res {
|
fn mutating_a_variable() -> Res {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_code(Config::new(), "x := 5, &x = 2, x")?,
|
run_code(Config::new(), "x := [Int<2..5>] 5, &x = 2, x")?,
|
||||||
TypedData(
|
TypedData(
|
||||||
Type::new(data::int::IntT),
|
Type::new(data::int::IntT(2, 5)),
|
||||||
Data::new(data::int::Int(2)),
|
Data::new(data::int::Int(2)),
|
||||||
mers_lib::info::Info::neverused()
|
mers_lib::info::Info::neverused()
|
||||||
),
|
),
|
||||||
@ -40,9 +40,9 @@ fn mutating_a_variable() -> Res {
|
|||||||
#[test]
|
#[test]
|
||||||
fn variable_shadowing() -> Res {
|
fn variable_shadowing() -> Res {
|
||||||
assert_eq!(
|
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(
|
TypedData(
|
||||||
Type::new(data::int::IntT),
|
Type::new(data::int::IntT(5, 5)),
|
||||||
Data::new(data::int::Int(5)),
|
Data::new(data::int::Int(5)),
|
||||||
mers_lib::info::Info::neverused()
|
mers_lib::info::Info::neverused()
|
||||||
)
|
)
|
||||||
@ -55,7 +55,7 @@ fn identity_function() -> Res {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_code(Config::new(), "id := x -> x, 4.id")?,
|
run_code(Config::new(), "id := x -> x, 4.id")?,
|
||||||
TypedData(
|
TypedData(
|
||||||
Type::new(data::int::IntT),
|
Type::new(data::int::IntT(4, 4)),
|
||||||
Data::new(data::int::Int(4)),
|
Data::new(data::int::Int(4)),
|
||||||
mers_lib::info::Info::neverused()
|
mers_lib::info::Info::neverused()
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user