Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce stricter recyling rules #201

Merged
merged 13 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/numerics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ fn main() {

println!("Example 1:\nRecycled add of Vec<bool> and Vec<i32>");
println!("{} + {}", x, y);
println!("{}\n", x + y);
println!("{}\n", (x + y).unwrap());

use r::object::OptionNA::*; // Some() and NA
let x = Vector::from(vec![Some(5_i32), NA, Some(1_i32)]);
let y = Vector::from(vec![1_f64, 2_f64, 3_f64]);

println!("Example 2:\nAdd of Vec<i32/NA> and Vec<f64>");
println!("{} + {}", x, y);
println!("{}\n", x + y);
println!("{}\n", (x + y).unwrap());
}
8 changes: 8 additions & 0 deletions src/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
* Named vectors were added and can e.g. be constructed via `[a = 1, b = 2]`
* The `is_null()` primitive was added
* Setting a list value to `null` actually sets it to `null` and does not remove it.
* Stricter recycling rule are enforced (@98):
Vectorized operations on two vectors `v1` and `v2` now requires either of:
* One of the vectors has length 1 and the other vector's length is not zero.
* The vectors have the same length.

Otherwise an error is thrown.
* The `typeof()` primitive was added
* Type stability for numeric operations (@69)

## Internals

Expand Down
1 change: 1 addition & 0 deletions src/callable/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub static BUILTIN: LazyLock<HashMap<&'static str, Box<dyn Builtin>>> = LazyLock
("runif", Box::new(PrimitiveRunif) as Box<dyn Builtin>),
("substitute", Box::new(PrimitiveSubstitute) as Box<dyn Builtin>),
("sum", Box::new(PrimitiveSum) as Box<dyn Builtin>),
("typeof", Box::new(PrimitiveTypeOf) as Box<dyn Builtin>),
// builtins end
])
});
2 changes: 2 additions & 0 deletions src/callable/primitive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ mod length;
pub use length::PrimitiveLength;
mod is_null;
pub use is_null::PrimitiveIsNull;
mod type_reflection;
pub use type_reflection::PrimitiveTypeOf;
46 changes: 46 additions & 0 deletions src/callable/primitive/type_reflection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use r_derive::*;

use crate::callable::core::*;
use crate::formals;
use crate::lang::*;
use crate::object::*;

#[doc(alias = "typeof")]
#[builtin(sym = "typeof")]
#[derive(Debug, Clone, PartialEq)]
pub struct PrimitiveTypeOf;

formals!(PrimitiveTypeOf, "(x,)");

impl Callable for PrimitiveTypeOf {
fn call_matched(&self, args: List, _ellipsis: List, stack: &mut CallStack) -> EvalResult {
let mut args = Obj::List(args);
let x = args.try_get_named("x")?.force(stack)?;

let t = match x {
Obj::Null => "null",
Obj::Vector(v) => match v {
Vector::Character(_) => "character",
Vector::Integer(_) => "integer",
Vector::Double(_) => "double",
Vector::Logical(_) => "logical",
},
Obj::List(_) => "list",
Obj::Expr(_) => "expression",
Obj::Promise(..) => "promise",
Obj::Function(..) => "function",
Obj::Environment(..) => "environment",
};
EvalResult::Ok(Obj::Vector(Vector::Character(vec![t.to_string()].into())))
}
}

#[cfg(test)]

mod tests {
use crate::{r, r_expect};
#[test]
fn character() {
r_expect!(typeof("a") == "character")
}
}
5 changes: 5 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum Error {
CannotBeCoercedTo(&'static str),
InvalidRange,

NonRecyclableLengths(usize, usize),

// destructuring
CannotBeDestructuredIntoList,

Expand Down Expand Up @@ -76,6 +78,9 @@ impl Error {
Error::NotInterpretableAsLogical => {
"argument is not interpretable as logical".to_string()
}
Error::NonRecyclableLengths(l, r) => {
format!("Vector lengths {l} and {r} cannot be recycled.")
}
Error::ConditionIsNotScalar => "the condition has length > 1".to_string(),
Error::CannotBeCoercedToCharacter => {
"object cannot be coerced to type 'character'".to_string()
Expand Down
70 changes: 35 additions & 35 deletions src/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,20 @@ impl Obj {
Obj::List(mut l) => {
match value.clone() {
Obj::List(r) => {
l.assign(r);
l.assign(r)?;
}
Obj::Vector(r) => match r {
Vector::Integer(r) => {
l.assign(r);
l.assign(r)?;
}
Vector::Character(r) => {
l.assign(r);
l.assign(r)?;
}
Vector::Logical(r) => {
l.assign(r);
l.assign(r)?;
}
Vector::Double(r) => {
l.assign(r);
l.assign(r)?;
}
},
_ => return Err(err.into()),
Expand Down Expand Up @@ -423,9 +423,9 @@ fn display_list(x: &List, f: &mut fmt::Formatter<'_>, bc: Option<String>) -> fmt
impl std::ops::Add for Obj {
type Output = EvalResult;

fn add(self, rhs: Self) -> Self::Output {
match (self.as_double()?, rhs.as_double()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l + r)),
fn add(self, rhs: Self) -> EvalResult {
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l + r)?)),
_ => internal_err!(),
}
}
Expand All @@ -434,9 +434,9 @@ impl std::ops::Add for Obj {
impl std::ops::Sub for Obj {
type Output = EvalResult;

fn sub(self, rhs: Self) -> Self::Output {
match (self.as_double()?, rhs.as_double()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l - r)),
fn sub(self, rhs: Self) -> EvalResult {
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l - r)?)),
_ => internal_err!(),
}
}
Expand All @@ -446,8 +446,8 @@ impl std::ops::Neg for Obj {
type Output = EvalResult;

fn neg(self) -> Self::Output {
match self.as_double()? {
Obj::Vector(x) => Ok(Obj::Vector(-x)),
match self {
Obj::Vector(x) => Ok(Obj::Vector((-x)?)),
_ => internal_err!(),
}
}
Expand All @@ -457,8 +457,8 @@ impl std::ops::Not for Obj {
type Output = EvalResult;

fn not(self) -> Self::Output {
match self.as_logical()? {
Obj::Vector(x) => Ok(Obj::Vector(!x)),
match self {
Obj::Vector(x) => Ok(Obj::Vector((!x)?)),
_ => internal_err!(),
}
}
Expand All @@ -468,8 +468,8 @@ impl std::ops::Mul for Obj {
type Output = EvalResult;

fn mul(self, rhs: Self) -> Self::Output {
match (self.as_double()?, rhs.as_double()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l * r)),
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l * r)?)),
_ => internal_err!(),
}
}
Expand All @@ -479,8 +479,8 @@ impl std::ops::Div for Obj {
type Output = EvalResult;

fn div(self, rhs: Self) -> Self::Output {
match (self.as_double()?, rhs.as_double()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l / r)),
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l / r)?)),
_ => internal_err!(),
}
}
Expand All @@ -490,8 +490,8 @@ impl super::object::Pow<Obj> for Obj {
type Output = EvalResult;

fn power(self, rhs: Self) -> Self::Output {
match (self.as_double()?, rhs.as_double()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.power(r))),
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.power(r)?)),
_ => internal_err!(),
}
}
Expand All @@ -501,8 +501,8 @@ impl std::ops::Rem for Obj {
type Output = EvalResult;

fn rem(self, rhs: Self) -> Self::Output {
match (self.as_double()?, rhs.as_double()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l % r)),
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l % r)?)),
_ => internal_err!(),
}
}
Expand All @@ -512,8 +512,8 @@ impl std::ops::BitOr for Obj {
type Output = EvalResult;

fn bitor(self, rhs: Self) -> Self::Output {
match (self.as_logical()?, rhs.as_logical()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l | r)),
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l | r)?)),
_ => internal_err!(),
}
}
Expand All @@ -523,8 +523,8 @@ impl std::ops::BitAnd for Obj {
type Output = EvalResult;

fn bitand(self, rhs: Self) -> Self::Output {
match (self.as_logical()?, rhs.as_logical()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l & r)),
match (self, rhs) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l & r)?)),
_ => internal_err!(),
}
}
Expand All @@ -534,28 +534,28 @@ impl VecPartialCmp<Obj> for Obj {
type Output = EvalResult;
fn vec_gt(self, rhs: Self) -> Self::Output {
match (self.as_vector()?, rhs.as_vector()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gt(r))),
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gt(r)?)),
_ => internal_err!(),
}
}

fn vec_gte(self, rhs: Self) -> Self::Output {
match (self.as_vector()?, rhs.as_vector()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gte(r))),
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gte(r)?)),
_ => internal_err!(),
}
}

fn vec_lt(self, rhs: Self) -> Self::Output {
match (self.as_vector()?, rhs.as_vector()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lt(r))),
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lt(r)?)),
_ => internal_err!(),
}
}

fn vec_lte(self, rhs: Self) -> Self::Output {
match (self.as_vector()?, rhs.as_vector()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lte(r))),
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lte(r)?)),
_ => internal_err!(),
}
}
Expand All @@ -567,7 +567,7 @@ impl VecPartialCmp<Obj> for Obj {
(lhs @ Obj::Function(..), rhs @ Obj::Function(..)) => Ok((lhs == rhs).into()),
(lhs @ Obj::Environment(_), rhs @ Obj::Environment(_)) => Ok((lhs == rhs).into()),
(lhs, rhs) => match (lhs.as_vector()?, rhs.as_vector()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_eq(r))),
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_eq(r)?)),
_ => internal_err!(),
},
}
Expand All @@ -580,7 +580,7 @@ impl VecPartialCmp<Obj> for Obj {
(lhs @ Obj::Function(..), rhs @ Obj::Function(..)) => Ok((lhs != rhs).into()),
(lhs @ Obj::Environment(_), rhs @ Obj::Environment(_)) => Ok((lhs != rhs).into()),
(lhs, rhs) => match (lhs.as_vector()?, rhs.as_vector()?) {
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_neq(r))),
(Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_neq(r)?)),
_ => internal_err!(),
},
}
Expand Down Expand Up @@ -1266,7 +1266,7 @@ mod test {
r_expect! {{"
f = fn(x) x
a = f((y = 2))
a == 2
a == 2
"}}
}

Expand All @@ -1275,7 +1275,7 @@ mod test {
r_expect! {{"
f = fn(x) x
f((y = 2))
y == 2
y == 2
"}}
}

Expand Down
14 changes: 0 additions & 14 deletions src/object/vector/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,20 +328,6 @@ pub trait CommonCmp: Sized {
fn into_common(self) -> (Self::Common, Self::Common);
}

impl<T, U, V> CommonCmp for (OptionNA<U>, OptionNA<V>)
where
(U, V): CommonCmp<Common = OptionNA<T>>,
{
type Common = OptionNA<T>;
fn into_common(self) -> (OptionNA<T>, OptionNA<T>) {
use OptionNA::*;
match self {
(Some(l), Some(r)) => (l, r).into_common(),
_ => (NA, NA),
}
}
}

#[macro_export]
macro_rules! register {
(
Expand Down
Loading
Loading