Skip to content

Commit

Permalink
Enforce stricter recyling rules
Browse files Browse the repository at this point in the history
  • Loading branch information
sebffischer authored Oct 16, 2024
1 parent 0901ac0 commit e3e874c
Show file tree
Hide file tree
Showing 12 changed files with 766 additions and 871 deletions.
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 @@ -428,9 +428,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 @@ -439,9 +439,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 @@ -451,8 +451,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 @@ -462,8 +462,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 @@ -473,8 +473,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 @@ -484,8 +484,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 @@ -495,8 +495,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 @@ -506,8 +506,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 @@ -517,8 +517,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 @@ -528,8 +528,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 @@ -539,28 +539,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 @@ -572,7 +572,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 @@ -585,7 +585,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 @@ -1271,7 +1271,7 @@ mod test {
r_expect! {{"
f = fn(x) x
a = f((y = 2))
a == 2
a == 2
"}}
}

Expand All @@ -1280,7 +1280,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

0 comments on commit e3e874c

Please sign in to comment.