From ebf4bc4668f877b2c78c8151380583c6c59fef9b Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Wed, 9 Oct 2024 18:16:20 +0200 Subject: [PATCH] refactor internal vector representation --- src/CHANGELOG.md | 22 + src/callable/builtins.rs | 1 + src/callable/core.rs | 85 ++- src/callable/operators.rs | 60 +- src/callable/primitive/c.rs | 201 +++++-- src/callable/primitive/callstack.rs | 2 +- src/callable/primitive/is_null.rs | 63 ++ src/callable/primitive/length.rs | 9 +- src/callable/primitive/list.rs | 6 +- src/callable/primitive/mod.rs | 2 + src/callable/primitive/names.rs | 48 +- src/callable/primitive/paste.rs | 7 +- src/context/core.rs | 44 +- src/lang.rs | 148 ++--- src/object/core.rs | 14 +- src/object/cow.rs | 6 +- src/object/environment.rs | 20 +- src/object/list.rs | 324 ++--------- src/object/vector/core.rs | 106 +++- src/object/vector/rep.rs | 384 ++++++++++--- src/object/vector/reptype.rs | 855 +++++++++++++++++++++++++--- src/object/vector/subset.rs | 19 +- src/object/vector/subsets.rs | 14 +- src/object/vector/types.rs | 144 +++++ 24 files changed, 1892 insertions(+), 692 deletions(-) create mode 100644 src/callable/primitive/is_null.rs diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 7f2ae52b..60deb53e 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -2,6 +2,28 @@ ## Changes +* 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. + +## Internals + +* The `List` is now represented as a `Rep`, unifying heterogenous and atomic vectors. + This included a considerable refactor. +* Iterating over references of a `Rep` was made much simpler and new methods were added + and unused ones removed. + +## Notable Bugs Addressed + +* Concatenating `list`s now works as expected (#177). +* `names()` now works correctly when the vector is subset, e.g. + `names(list(a = 1, b = 2)[1])` (#181). +* `[[.<-` assignment has been fixed for lists (#179) + +# 0.4.0 + +## Changes + * Added German (`ger`) localization. * Digits can now be separated by an underscore to improve readability. diff --git a/src/callable/builtins.rs b/src/callable/builtins.rs index 6751c2e6..cb84727c 100644 --- a/src/callable/builtins.rs +++ b/src/callable/builtins.rs @@ -42,6 +42,7 @@ pub static BUILTIN: LazyLock>> = LazyLock ("callstack", Box::new(PrimitiveCallstack) as Box), ("environment", Box::new(PrimitiveEnvironment) as Box), ("eval", Box::new(PrimitiveEval) as Box), + ("is_null", Box::new(PrimitiveIsNull) as Box), ("length", Box::new(PrimitiveLength) as Box), ("list", Box::new(PrimitiveList) as Box), ("names", Box::new(PrimitiveNames) as Box), diff --git a/src/callable/core.rs b/src/callable/core.rs index 1b74fc01..43334a40 100644 --- a/src/callable/core.rs +++ b/src/callable/core.rs @@ -3,8 +3,8 @@ extern crate r_derive; use crate::callable::dyncompare::*; use crate::cli::Experiment; use crate::context::Context; -use crate::object::List; -use crate::object::{Expr, ExprList, Obj}; +use crate::object::types::{Character, Integer}; +use crate::object::{Expr, ExprList, List, Obj, Subset}; use crate::{internal_err, lang::*}; impl std::fmt::Debug for Box { @@ -38,30 +38,27 @@ pub trait CallableFormals { pub trait Callable: CallableFormals { fn match_args(&self, args: List, stack: &mut CallStack) -> Result<(List, List), Signal> { let mut formals = self.formals(); - let ellipsis: List = vec![].into(); - let matched_args: List = vec![].into(); + let ellipsis: List = List::new(); + let matched_args: List = List::new(); // assign named args to corresponding formals - let mut i: usize = 0; - 'outer: while i < args.values.len() { - 'inner: { - // check argname with immutable borrow, but drop scope. If - // found, drop borrow so we can mutably assign it - if let (Some(argname), _) = &args.values.borrow()[i] { - if let Some((Some(_), _)) = formals.remove_named(argname) { - break 'inner; - } - } - i += 1; - continue 'outer; - } + let mut indices: Vec = Vec::new(); - matched_args - .values - .with_inner_mut(|v| v.push(args.values.with_inner_mut(|x| x.remove(i)))) + for (i, (maybe_name, value)) in args.pairs_ref().iter().enumerate() { + if let Character::Some(name) = maybe_name { + if let Some((Some(_), _)) = formals.remove_named(name) { + matched_args.push_named(Character::Some(name.clone()), value.clone()); + continue; + } + } + indices.push(i as i32); } + let indices: Vec = indices.into_iter().map(Integer::Some).collect(); + let subset = Subset::Indices(indices.into()); + let args = args.subset(subset).materialize(); + // TODO(bug): need to evaluate trailing unassigned params that have // a default value before popping off remaining trailing params @@ -69,27 +66,18 @@ pub trait Callable: CallableFormals { let remainder = formals.pop_trailing(); // backfill unnamed args, populating ellipsis with overflow - let argsiter = args.values.into_iter(); - for (key, value) in argsiter { + for (key, value) in args.iter_pairs() { match key { // named args go directly to ellipsis, they did not match a formal - Some(arg) => { - ellipsis - .values - .with_inner_mut(|v| v.push((Some(arg), value))); - } + Character::Some(arg) => ellipsis.push_named(Character::Some(arg), value), // unnamed args populate next formal, or ellipsis if formals exhausted - None => { + Character::NA => { let next_unassigned_formal = formals.remove(0); if let Some((Some(param), _)) = next_unassigned_formal { - matched_args - .values - .with_inner_mut(|vals| vals.push((Some(param), value))); + matched_args.push_named(Character::Some(param), value); } else { - ellipsis - .values - .with_inner_mut(|vals| vals.push((None, value))); + ellipsis.push_named(Character::NA, value); } } } @@ -97,22 +85,19 @@ pub trait Callable: CallableFormals { // add back in parameter defaults that weren't filled with args for (param, default) in formals.into_iter() { - matched_args.values.with_inner_mut(|v| { - v.push(( - param, - Obj::Promise(None, default, stack.last_frame().env().clone()), - )); - }) + matched_args.push_named( + param.into(), + Obj::Promise(None, default, stack.last_frame().env().clone()), + ) } if let Some(Expr::Ellipsis(Some(name))) = remainder.get(0) { - matched_args - .values - .with_inner_mut(|v| v.push((Some(name), Obj::List(ellipsis.clone())))) + matched_args.push_named(Character::Some(name), Obj::List(ellipsis.clone())); } else if !remainder.is_empty() { - matched_args - .values - .with_inner_mut(|v| v.push((Some("...".to_string()), Obj::List(ellipsis.clone())))) + matched_args.push_named( + Character::Some("...".to_string()), + Obj::List(ellipsis.clone()), + ); } Ok((matched_args, ellipsis)) @@ -276,14 +261,10 @@ where impl CallableFormals for String {} impl Builtin for String {} -pub fn force_promises( - vals: List, - stack: &mut CallStack, -) -> Result, Obj)>, Signal> { +pub fn force_promises(vals: List, stack: &mut CallStack) -> Result, Signal> { // Force any closures that were created during call. This helps with using // variables as argument for sep and collapse parameters. - vals.values - .into_iter() + vals.iter_pairs() .map(|(k, v)| match (k, v.force(stack)) { (k, Ok(v)) => Ok((k, v)), (_, Err(e)) => Err(e), diff --git a/src/callable/operators.rs b/src/callable/operators.rs index 3bc7f519..c2391207 100644 --- a/src/callable/operators.rs +++ b/src/callable/operators.rs @@ -362,7 +362,7 @@ impl Callable for InfixDollar { let mut what = stack.eval(what)?; match index { - Expr::String(s) | Expr::Symbol(s) => what.try_get_named(s.as_str()), + Expr::String(s) | Expr::Symbol(s) => what.try_get_named(&s), _ => Ok(Obj::Null), } } @@ -381,7 +381,7 @@ impl Callable for InfixDollar { let mut what = stack.eval_mut(what)?; match index { - Expr::String(s) | Expr::Symbol(s) => what.try_get_named(s.as_str()), + Expr::String(s) | Expr::Symbol(s) => what.try_get_named_mut(s.as_str()), _ => Ok(Obj::Null), } } @@ -401,10 +401,7 @@ impl Callable for InfixDollar { let mut what = stack.eval_mut(what)?; match name { - Expr::String(s) | Expr::Symbol(s) => { - what.set_named(s.as_str(), value)?; - Ok(what) - } + Expr::String(s) | Expr::Symbol(s) => what.try_set_named(&s, value), _ => unimplemented!(), } } @@ -436,6 +433,30 @@ impl Callable for PostfixIndex { let index = stack.eval(x.1)?; what.try_get_inner_mut(index) } + + fn call_assign(&self, value: Expr, args: ExprList, stack: &mut CallStack) -> EvalResult { + let mut argstream = args.into_iter(); + + let Some((_, what)) = argstream.next() else { + unreachable!(); + }; + + let Some((_, index)) = argstream.next() else { + unreachable!(); + }; + + let value = stack.eval(value)?; + let what = stack.eval_mut(what)?; + let index = stack.eval(index)?; + + let subset = index.try_into()?; + + Ok(match what { + Obj::List(mut v) => v.set_subset(subset, value)?, + Obj::Vector(mut v) => v.set_subset(subset, value).map(Obj::Vector)?, + _ => unimplemented!(), + }) + } } #[derive(Debug, Clone, PartialEq)] @@ -460,7 +481,7 @@ impl Callable for PostfixVecIndex { mod tests { use crate::error::Error; use crate::lang::{EvalResult, Signal}; - use crate::r; + use crate::{r, r_expect}; #[test] fn colon_operator() { assert_eq!(EvalResult::Err(Signal::Error(Error::InvalidRange)), r!(1:0)); @@ -468,4 +489,29 @@ mod tests { assert_eq!(r!([1]), r!(1:1)); assert_eq!(r!(1:-2:-3), r!([1, -1, -3])); } + + #[test] + fn dollar_assign() { + r_expect! {{" + l = (a = 1, ) + x = (l$a = 2) + l$a == 2 & x == 2 + "}} + } + #[test] + fn dollar_assign_nested() { + r_expect! {{" + l = (a = (b = 1,),) + x = (l$a$b = 2) + l$a$b == 2 & x == 2 + "}} + } + + #[test] + fn dollar_access() { + r_expect! {{" + l = (a = 1, ) + l$a == 1 + "}} + } } diff --git a/src/callable/primitive/c.rs b/src/callable/primitive/c.rs index b83eceb4..91b57320 100644 --- a/src/callable/primitive/c.rs +++ b/src/callable/primitive/c.rs @@ -55,17 +55,28 @@ impl Callable for PrimitiveC { // lets first see what we're aiming to build. let ty: u8 = vals - .values - .borrow() + .pairs_ref() .iter() .map(|(_, v)| match v { Obj::Null => 0, Obj::Vector(_) => 1, - Obj::List(_) => 2, - _ => 0, + _ => 2, }) .fold(0, std::cmp::max); + // if the output will have names + // either an argument was passed via a name or it has names itself + let named = vals.pairs_ref().iter().any(|(n, o)| { + if matches!(n, OptionNA::Some(_)) { + return true; + } + match o { + Obj::Vector(v) => v.is_named(), + Obj::List(l) => l.is_named(), + _ => false, + } + }); + // most complex type was NULL if ty == 0 { return Ok(Obj::Null); @@ -73,15 +84,69 @@ impl Callable for PrimitiveC { // most complex type was List if ty == 2 { - return Ok(Obj::List(vals)); + // TODO: We should use size hints here. + let list = List::new(); + for (name1, value1) in vals.iter_pairs() { + match value1 { + Obj::List(x) => { + for (name2, value2) in x.iter_pairs() { + let name = match (&name1, name2) { + (OptionNA::Some(x1), OptionNA::Some(x2)) => { + OptionNA::Some(format!("{x1}.{x2}")) + } + (OptionNA::NA, OptionNA::Some(x2)) => OptionNA::Some(x2), + (OptionNA::Some(_), OptionNA::NA) => name1.clone(), + (OptionNA::NA, OptionNA::NA) => OptionNA::NA, + }; + + list.push_named(name, value2) + } + } + _ => list.push_named(name1, value1), + } + } + return Ok(Obj::List(list)); } + let names: Option> = if named { + let mut pairs = vals.pairs_ref(); + let nms = pairs.iter().flat_map(|(name, obj)| { + let maybe_prefix = name.clone().as_option().clone(); + + if let Obj::Vector(v) = obj { + let maybe_names_iter = v.iter_names(); + + let x: Vec = match (maybe_prefix, maybe_names_iter) { + (None, None) => std::iter::repeat(Character::NA).take(v.len()).collect(), + (Some(prefix), None) => std::iter::repeat(Character::Some(prefix.clone())) + .take(v.len()) + .collect(), + (None, Some(names_iter)) => names_iter.collect(), + (Some(prefix), Some(names_iter)) => names_iter + .map(|maybe_name| { + if let OptionNA::Some(name) = maybe_name { + Character::Some(format!("{}.{}", prefix, name)) + } else { + Character::Some(prefix.clone()) + } + }) + .collect(), + }; + x + } else { + unreachable!("if we are not building a list, all elements are vectors") + } + }); + Some(nms.collect()) + } else { + None + }; + // otherwise, try to collapse vectors into same type let ret = vals - .values - .borrow() + .values_ref() .iter() - .map(|(_, r)| match r { + .map(|r| match r { Obj::Vector(Vector::Logical(_)) => Vector::from(Vec::::new()), Obj::Vector(Vector::Integer(_)) => Vector::from(Vec::::new()), Obj::Vector(Vector::Double(_)) => Vector::from(Vec::::new()), @@ -99,59 +164,121 @@ impl Callable for PrimitiveC { }); // consume values and merge into a new collection - match ret { - Vector::Character(_) => Ok(Obj::Vector(Vector::from( + let v = match ret { + Vector::Character(_) => Vector::from( Vec::>::new() .into_iter() - .chain( - vals.values - .into_iter() - .flat_map(|(_, i)| match i.as_character() { - Ok(Obj::Vector(Vector::Character(v))) => v.into_iter(), - _ => unreachable!(), - }), - ) + .chain(vals.iter_values().flat_map(|i| match i.as_character() { + Ok(Obj::Vector(Vector::Character(v))) => v.iter_values(), + _ => unreachable!(), + })) .collect::>(), - ))), - Vector::Double(_) => Ok(Obj::Vector(Vector::from( + ), + Vector::Double(_) => Vector::from( Vec::>::new() .into_iter() .chain( - vals.values - .into_iter() - .flat_map(|(_, i)| match i.as_double() { - Ok(Obj::Vector(Vector::Double(v))) => v.into_iter(), + vals.iter_values() + .flat_map(|i| match i.clone().as_double() { + Ok(Obj::Vector(Vector::Double(v))) => v.iter_values(), _ => unreachable!(), }), ) .collect::>(), - ))), - Vector::Integer(_) => Ok(Obj::Vector(Vector::from( + ), + Vector::Integer(_) => Vector::from( Vec::>::new() .into_iter() .chain( - vals.values - .into_iter() - .flat_map(|(_, i)| match i.as_integer() { - Ok(Obj::Vector(Vector::Integer(v))) => v.into_iter(), + vals.iter_values() + .flat_map(|i| match i.clone().as_integer() { + Ok(Obj::Vector(Vector::Integer(v))) => v.iter_values(), _ => unreachable!(), }), ) .collect::>(), - ))), - Vector::Logical(_) => Ok(Obj::Vector(Vector::from( + ), + Vector::Logical(_) => Vector::from( Vec::>::new() .into_iter() .chain( - vals.values - .into_iter() - .flat_map(|(_, i)| match i.as_logical() { - Ok(Obj::Vector(Vector::Logical(v))) => v.into_iter(), + vals.iter_values() + .flat_map(|i| match i.clone().as_logical() { + Ok(Obj::Vector(Vector::Logical(v))) => v.iter_values(), _ => unreachable!(), }), ) .collect::>(), - ))), + ), + }; + + if let Some(names) = names { + v.set_names(names.into()) } + Ok(Obj::Vector(v)) + } +} + +#[cfg(test)] + +mod tests { + use crate::{r, r_expect}; + #[test] + fn list_empty() { + assert_eq!(r!(list()), r!(c(list()))) + } + #[test] + fn list_list() { + r_expect! {{" + l = c(list(1), list(2)) + l[[1]] == 1 & l[[2]] == 2 + "}} + } + #[test] + fn list_vec() { + r_expect! {{" + l = c(list(1), 2:3) + l[[1]] == 1 & l[[2]][1] == 2 & l[[2]][2] == 3 + "}} + } + #[test] + fn list_fn() { + r_expect! {{" + l = c(list(1), fn() 2) + l[[1]] == 1 & l[[2]]() == 2 + "}} + } + #[test] + fn function() { + r_expect! {{" + l = c(fn() 2) + l[[1]]() == 2 + "}} + } + #[test] + fn list_names_outer() { + r_expect! {{" + c(a = list(1))$a == 1 + "}} + } + #[test] + fn list_names_inner() { + r_expect! {{" + c(list(a = 1))$a == 1 + "}} + } + #[test] + fn list_names_both() { + r_expect! {{" + c(a = list(b = 1))$a.b == 1 + "}} + } + + #[test] + fn vector_names() { + r_expect! {{r#" + x = [a = 1] + names(x) == "a" + "#}} } } diff --git a/src/callable/primitive/callstack.rs b/src/callable/primitive/callstack.rs index 49072ca8..9aa1fb29 100644 --- a/src/callable/primitive/callstack.rs +++ b/src/callable/primitive/callstack.rs @@ -43,7 +43,7 @@ impl Callable for PrimitiveCallstack { .frames .iter() .skip(1) // skip global frame - .map(|f| (None, Obj::Expr(f.call.clone()))) + .map(|f| (OptionNA::NA, Obj::Expr(f.call.clone()))) .collect::>(), ))) } diff --git a/src/callable/primitive/is_null.rs b/src/callable/primitive/is_null.rs new file mode 100644 index 00000000..8464d62a --- /dev/null +++ b/src/callable/primitive/is_null.rs @@ -0,0 +1,63 @@ +use r_derive::*; + +use crate::callable::core::*; +use crate::formals; +use crate::lang::*; +use crate::object::types::Logical; +use crate::object::*; + +/// Is an object `null` +/// +/// Checks whether an object is null +/// +/// # In-Language +/// +/// ## Usage +/// +/// ```custom,{class=r} +/// is_null(x) +/// ``` +/// +/// ## Arguments +/// +/// `x`: Object to check. +/// +/// ## Examples +/// +/// ```custom,{class=r-repl} +/// is_null(null) +/// is_null(0) +/// ``` +#[doc(alias = "is_null")] +#[builtin(sym = "is_null")] +#[derive(Debug, Clone, PartialEq)] +pub struct PrimitiveIsNull; + +formals!(PrimitiveIsNull, "(x)"); + +impl Callable for PrimitiveIsNull { + fn call(&self, args: ExprList, stack: &mut CallStack) -> EvalResult { + let (args, _ellipsis) = self.match_arg_exprs(args, stack)?; + let mut args = Obj::List(args); + + let x = args.try_get_named("x")?.force(stack)?; + + EvalResult::Ok(Obj::Vector(Vector::from(vec![Logical::Some(matches!( + x, + Obj::Null + ))]))) + } +} + +#[cfg(test)] +mod tests { + use crate::{r, r_expect}; + #[test] + fn is_null() { + r_expect!(is_null(null)) + } + #[test] + fn is_not_null() { + r_expect!(!is_null(1:2)) + } +} diff --git a/src/callable/primitive/length.rs b/src/callable/primitive/length.rs index 6c43d4fc..93c89c83 100644 --- a/src/callable/primitive/length.rs +++ b/src/callable/primitive/length.rs @@ -47,7 +47,7 @@ impl Callable for PrimitiveLength { Vector::Logical(rep) => rep.len(), Vector::Character(rep) => rep.len(), }, - Obj::List(_) => todo!("Not implemented yet"), + Obj::List(rep) => rep.len(), Obj::Environment(env) => env.len(), _ => return Error::Other("Argument 'x' does not have a length".into()).into(), }; @@ -110,4 +110,11 @@ mod tests { EvalResult::Err(Error::Other("Argument 'x' does not have a length".to_string()).into()) ) } + + #[test] + fn list() { + r_expect! {{" + length(list(1)) == 1 && length(list()) == 0 + "}} + } } diff --git a/src/callable/primitive/list.rs b/src/callable/primitive/list.rs index 05715e62..24cb79f9 100644 --- a/src/callable/primitive/list.rs +++ b/src/callable/primitive/list.rs @@ -26,6 +26,11 @@ use crate::object::*; /// /// `...`: Arguments to collect into a `list`. /// +/// ## Differences to the R implementation +/// +/// Setting a list value to `null` does not remove the element but +/// sets it to the value `null`. +/// /// ## Examples /// /// ```custom,{class=r-repl} @@ -45,7 +50,6 @@ use crate::object::*; /// ```custom,{class=r-repl} /// (1,) /// ``` -/// #[doc(alias = "list")] #[builtin(sym = "list")] #[derive(Debug, Clone, PartialEq)] diff --git a/src/callable/primitive/mod.rs b/src/callable/primitive/mod.rs index 4b638978..3e698957 100644 --- a/src/callable/primitive/mod.rs +++ b/src/callable/primitive/mod.rs @@ -30,3 +30,5 @@ mod sum; pub use sum::PrimitiveSum; mod length; pub use length::PrimitiveLength; +mod is_null; +pub use is_null::PrimitiveIsNull; diff --git a/src/callable/primitive/names.rs b/src/callable/primitive/names.rs index 47dc872f..d0885b58 100644 --- a/src/callable/primitive/names.rs +++ b/src/callable/primitive/names.rs @@ -63,20 +63,16 @@ impl Callable for PrimitiveNames { match x { Null => Ok(Null), Promise(..) => Ok(Null), - Vector(..) => Ok(Null), // named vectors currently not supported... - Expr(..) => Ok(Null), // handle arg lists? + Vector(v) => match v.names() { + Some(n) => Ok(Obj::Vector(n.into())), + None => Ok(Null), + }, + Expr(..) => Ok(Null), // handle arg lists? Function(..) => Ok(Null), // return formals? - List(x) => { - Ok(x.values - .borrow() - .iter() - .map(|(k, _)| match k { - Some(name) => OptionNA::Some(name.clone()), - None => OptionNA::NA, // unlike R, unnamed elements are NAs - }) - .collect::>>() - .into()) - } + List(l) => match l.names() { + Some(n) => Ok(Obj::Vector(n.into())), + None => Ok(Null), + }, Environment(e) => { let mut names = e.values.borrow().keys().cloned().collect::>(); names.sort(); @@ -89,14 +85,11 @@ impl Callable for PrimitiveNames { #[cfg(test)] mod test { use crate::error::Error; - use crate::r; + use crate::{r, r_expect}; #[test] fn no_args() { - assert_eq!( - r! { names() }, - Error::ArgumentMissing(String::from("x")).into() - ) + assert_eq!(r! { names() }, Error::Missing.into()) } #[test] @@ -111,4 +104,23 @@ mod test { r! { c("a", "b", NA, "d") } ) } + #[test] + fn subset() { + r_expect! {{r#" + names([a = 1, b = 2][1]) == "a" + "#}} + } + + #[test] + fn unnamed_atomic() { + r_expect! {{r#" + is_null(names([1, 2])) + "#}} + } + #[test] + fn named_atomic() { + r_expect! {{r#" + names([a = 1]) == "a" + "#}} + } } diff --git a/src/callable/primitive/paste.rs b/src/callable/primitive/paste.rs index 2e41b6c0..98a68c4f 100644 --- a/src/callable/primitive/paste.rs +++ b/src/callable/primitive/paste.rs @@ -2,6 +2,7 @@ use crate::callable::core::*; use crate::error::*; use crate::formals; use crate::lang::*; +use crate::object::types::Character; use crate::object::*; use r_derive::*; @@ -52,6 +53,7 @@ impl Callable for PrimitivePaste { let (args, ellipsis) = self.match_arg_exprs(args, stack)?; let ellipsis = force_promises(ellipsis, stack)?; + let args = force_promises(args, stack)?; let mut sep = String::from(" "); @@ -60,7 +62,7 @@ impl Callable for PrimitivePaste { // remove named sep and collapse args from our arguments and populate values for (k, v) in args.iter().rev() { - let Some(k) = k else { continue }; + let Character::Some(k) = k else { continue }; match (k.as_str(), v) { ("sep", Obj::Vector(v)) => { @@ -122,7 +124,6 @@ impl Callable for PrimitivePaste { if should_collapse { output = vec![output.join(&collapse)]; } - Ok(Obj::Vector(output.into())) } } @@ -130,7 +131,7 @@ impl Callable for PrimitivePaste { #[cfg(test)] mod test { use super::*; - use crate::object::types::*; + use crate::r; #[test] diff --git a/src/context/core.rs b/src/context/core.rs index edb1b616..954d0ddf 100644 --- a/src/context/core.rs +++ b/src/context/core.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use crate::lang::{EvalResult, Signal}; +use crate::object::types::Character; use crate::object::*; use crate::{error::*, internal_err}; @@ -75,34 +76,42 @@ pub trait Context: std::fmt::Debug + std::fmt::Display { .map(|pair| match pair { (_, Expr::Ellipsis(None)) => { if let Ok(Obj::List(ellipsis)) = self.get_ellipsis() { - Ok(ellipsis.values.into_iter()) + Ok(ellipsis.iter_pairs()) } else { - Ok(CowObj::from(vec![]).into_iter()) + Ok(List::new().iter_pairs()) } } (_, Expr::Ellipsis(Some(name))) => { if let Ok(Obj::List(more)) = self.get(name) { - Ok(more.values.into_iter()) + Ok(more.iter_pairs()) } else { internal_err!() } } // Avoid creating a new closure just to point to another, just reuse it (k, Expr::Symbol(s)) => match self.env().get(s.clone()) { - Ok(c @ Obj::Promise(..)) => Ok(CowObj::from(vec![(k, c)]).into_iter()), - _ => Ok(CowObj::from(vec![( - k, - Obj::Promise(None, Expr::Symbol(s), self.env()), - )]) - .into_iter()), + Ok(c @ Obj::Promise(..)) => { + let k = k.map_or(OptionNA::NA, OptionNA::Some); + Ok(List::from(vec![(k, c)]).iter_pairs()) + } + _ => { + let k = k.map_or(OptionNA::NA, OptionNA::Some); + Ok(List::from(vec![( + k, + Obj::Promise(None, Expr::Symbol(s), self.env()), + )]) + .iter_pairs()) + } }, (k, c @ Expr::Call(..)) => { + let k = k.map_or(OptionNA::NA, OptionNA::Some); let elem = vec![(k, Obj::Promise(None, c, self.env()))]; - Ok(CowObj::from(elem).into_iter()) + Ok(List::from(elem).iter_pairs()) } (k, v) => { + let k = k.map_or(OptionNA::NA, OptionNA::Some); if let Ok(elem) = self.eval(v) { - Ok(CowObj::from(vec![(k, elem)]).into_iter()) + Ok(List::from(vec![(k, elem)]).iter_pairs()) } else { internal_err!() } @@ -121,20 +130,23 @@ pub trait Context: std::fmt::Debug + std::fmt::Display { .map(|pair| match pair { (_, Expr::Ellipsis(None)) => { if let Ok(Obj::List(ellipsis)) = self.get_ellipsis() { - Ok(ellipsis.values.into_iter()) + Ok(ellipsis.iter_pairs()) } else { - Ok(CowObj::from(vec![]).into_iter()) + Ok(List::from(Vec::<(Character, Obj)>::new()).iter_pairs()) } } (_, Expr::Ellipsis(Some(name))) => { if let Ok(Obj::List(more)) = self.get(name) { - Ok(more.values.into_iter()) + Ok(more.iter_pairs()) } else { - Ok(CowObj::from(vec![]).into_iter()) + Ok(List::from(Vec::<(Character, Obj)>::new()).iter_pairs()) } } (k, v) => match self.eval_and_finalize(v) { - Ok(elem) => Ok(CowObj::from(vec![(k, elem)]).into_iter()), + Ok(elem) => { + let k = k.map_or(OptionNA::NA, OptionNA::Some); + Ok(List::from(vec![(k, elem)]).iter_pairs()) + } Err(e) => Err(e), }, }) diff --git a/src/lang.rs b/src/lang.rs index 187e4985..82607da4 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -5,11 +5,12 @@ use crate::context::Context; use crate::error::*; use crate::internal_err; use crate::object::types::*; +use crate::object::List; use crate::object::*; use crate::parser::LocalizedParser; use crate::parser::ParseResult; use crate::session::{Session, SessionParserConfig}; -use std::collections::HashSet; +use hashbrown::HashSet; use core::fmt; use std::fmt::Display; @@ -70,11 +71,7 @@ impl ViewMut for Obj { Vector::Logical(v) => Vector::Logical(v.view_mut()), }), - Obj::List(List { names, values, subsets }) => Obj::List(List { - names: (*names).view_mut(), - values: (*values).view_mut(), - subsets: (*subsets).clone(), - }), + Obj::List(l) => Obj::List(l.view_mut()), // FIXME: this needs to be implemented for all objects that can be mutated x => x.clone(), } @@ -98,6 +95,7 @@ impl Obj { } } + // this is vectorized assignment. pub fn assign(self, value: Obj) -> EvalResult { // TODO(ERROR) cleanup let err = Error::Other("Invalid target for assignment".to_string()); @@ -108,7 +106,27 @@ impl Obj { Ok(value) } Obj::List(mut l) => { - l.assign(value.clone())?; + match value.clone() { + Obj::List(r) => { + l.assign(r); + } + Obj::Vector(r) => match r { + Vector::Integer(r) => { + l.assign(r); + } + Vector::Character(r) => { + l.assign(r); + } + Vector::Logical(r) => { + l.assign(r); + } + Vector::Double(r) => { + l.assign(r); + } + }, + _ => return Err(err.into()), + }; + Ok(value) } _ => Err(err.into()), @@ -117,7 +135,7 @@ impl Obj { pub fn as_list(&self) -> EvalResult { match self { - Obj::Null => Ok(Obj::List(List::from(vec![]))), + Obj::Null => Ok(Obj::List(List::new())), Obj::Vector(_v) => internal_err!(), Obj::List(l) => Ok(Obj::List(l.clone())), Obj::Expr(e) => match e { @@ -213,8 +231,8 @@ impl Obj { pub fn get(&self, index: usize) -> Option { match self { Obj::Vector(v) => v.get(index).map(Obj::Vector), + Obj::List(v) => v.get(index).map(Obj::List), Obj::Null => None, - Obj::List(l) => l.values.borrow().get(index).map(|x| x.1.clone()), Obj::Expr(..) => None, Obj::Promise(..) => None, Obj::Function(..) => None, @@ -222,77 +240,79 @@ impl Obj { } } - pub fn get_named(&mut self, name: &str) -> Option { + pub fn environment(&self) -> Option> { match self { - Obj::List(v) => v - .values - .borrow() - .iter() - .find(|(k, _)| *k == Some(String::from(name))) - .map(|(_, v)| v.clone()), - Obj::Environment(e) => match e.get(String::from(name)) { - Ok(v) => Some(v), - Err(_) => None, - }, + Obj::Promise(.., e) | Obj::Function(.., e) | Obj::Environment(e) => Some(e.clone()), _ => None, } } - pub fn set_named(&mut self, name: &str, value: Obj) -> EvalResult { + /// Used for `$`-assignment. + pub fn try_set_named(&mut self, name: &str, value: Obj) -> EvalResult { match self { - Obj::List(v) => { - let loc = v - .values - .iter() - .enumerate() - .find(|(_, (k, _))| *k == Some(name.into())) - .map(|(i, _)| i); - - v.values.with_inner_mut(|vb| match loc { - Some(i) => vb[i].1 = value.clone(), - None => vb.push((Some(name.into()), value.clone())), - }); - - Ok(value) + Obj::List(l) => { + let subset = Subset::Names(vec![Character::Some(name.to_string())].into()); + Ok(l.set_subset(subset, value)?) } Obj::Environment(e) => { e.values.borrow_mut().insert(name.into(), value.clone()); Ok(value) } - _ => Ok(Obj::Null), + _ => internal_err!(), } } - pub fn environment(&self) -> Option> { + /// Used for `$`-assignment. + pub fn try_get_named_mut(&mut self, name: &str) -> EvalResult { match self { - Obj::Promise(.., e) | Obj::Function(.., e) | Obj::Environment(e) => Some(e.clone()), - _ => None, + Obj::List(l) => { + let subset = Subset::Names(vec![Character::Some(name.to_string())].into()); + Ok(l.try_get_inner_mut(subset)?) + } + Obj::Environment(e) => match e.get_mut(String::from(name)) { + Ok(v) => Ok(v), + Err(_) => Err(Error::VariableNotFound(name.into()).into()), + }, + _ => internal_err!(), } } + /// Used for `$`-access. pub fn try_get_named(&mut self, name: &str) -> EvalResult { - use Error::{ArgumentMissing, VariableNotFound}; - match self.get_named(name) { - Some(Obj::Promise(_, Expr::Missing, _)) => Err(ArgumentMissing(name.into()).into()), - Some(x) => Ok(x), - None => Err(VariableNotFound(name.into()).into()), + match self { + Obj::List(l) => { + let subset = Subset::Names(vec![Character::Some(name.to_string())].into()); + Ok(l.try_get_inner(subset)?) + } + Obj::Environment(e) => match e.get(String::from(name)) { + Ok(v) => Ok(v), + Err(_) => Err(Error::VariableNotFound(name.into()).into()), + }, + _ => internal_err!(), } } // Used for [ ] syntax pub fn try_get(&self, index: Obj) -> EvalResult { + let index = index.as_vector()?; match self { Obj::Vector(v) => v.try_get(index), - Obj::List(l) => l.try_get(index), + Obj::List(l) => { + let subset = Subset::try_from(index)?; + let x = l.subset(subset); + EvalResult::Ok(Obj::List(List::from(x))) + } obj => obj.as_list()?.try_get(index), } } - // Used for [[ ]] syntax + // Used for `[[`-access. pub fn try_get_inner(&self, index: Obj) -> EvalResult { match self { Obj::Vector(v) => v.try_get(index), - Obj::List(l) => l.try_get_inner(index), + Obj::List(l) => EvalResult::Ok(l.try_get_inner(index.try_into()?)?), + // To access environments use try_get_named + Obj::Environment(_) => internal_err!(), obj => obj.as_list()?.try_get_inner(index), } } @@ -301,7 +321,7 @@ impl Obj { pub fn try_get_inner_mut(&self, index: Obj) -> EvalResult { match self { Obj::Vector(v) => v.try_get(index), - Obj::List(l) => l.try_get_inner_mut(index), + Obj::List(l) => EvalResult::Ok(l.try_get_inner_mut(index.try_into()?)?), obj => obj.as_list()?.try_get_inner_mut(index), } } @@ -372,29 +392,12 @@ impl Display for Obj { } fn display_list(x: &List, f: &mut fmt::Formatter<'_>, bc: Option) -> fmt::Result { - let v = x.values.borrow(); - let s = x.subsets.clone(); - - for (i, (_, si)) in s - .bind_names(x.names.clone()) - .into_iter() - .take(v.len()) - .enumerate() - { - let name; - let value; - - if let Some(i) = si { - (name, value) = v[i].clone(); - } else { - return write!(f, "{}", Obj::Null); - } - + for (i, (maybe_name, value)) in x.pairs_ref().iter().enumerate() { if i > 0 { writeln!(f)? } - let bc_elem = if let Some(name) = name { + let bc_elem = if let Character::Some(name) = maybe_name { format!("${}", name) } else { format!("[[{}]]", i + 1) @@ -408,7 +411,7 @@ fn display_list(x: &List, f: &mut fmt::Formatter<'_>, bc: Option) -> fmt match value { Obj::List(nested_values) => { writeln!(f, "{}", breadcrumbs)?; - display_list(&nested_values, f, Some(breadcrumbs))? + display_list(nested_values, f, Some(breadcrumbs))? } _ => write!(f, "{}\n{}\n", breadcrumbs, value)?, } @@ -828,14 +831,15 @@ impl Context for CallStack { match item { (None, Expr::String(s) | Expr::Symbol(s)) => { let index = Obj::Vector(Vector::from(vec![i])); - let value = args.try_get_inner(index)?; + let value = args.try_get_inner(index.try_into()?)?; self.assign(Expr::Symbol(s), value)?; i += 1; } // TODO(feature): allow arbitrary right-side expressions // evaluated with list as additional data-frame (Some(n), Expr::String(s) | Expr::Symbol(s)) => { - let value = args.try_get_inner(Obj::Vector(Vector::from(vec![s])))?; + let value = + args.try_get_inner(Obj::Vector(Vector::from(vec![s])).try_into()?)?; self.assign(Expr::Symbol(n), value)?; } _ => return internal_err!(), @@ -1073,7 +1077,7 @@ impl Context for Obj { fn get(&mut self, name: String) -> EvalResult { match self { - Obj::List(l) => l.try_get_inner(Obj::Vector(Vector::from(vec![name]))), + Obj::List(l) => Ok(l.try_get_inner(Obj::Vector(Vector::from(vec![name])).try_into()?)?), Obj::Environment(e) => e.get(name), _ => unimplemented!(), } @@ -1166,7 +1170,7 @@ pub fn assert_formals(session: &Session, formals: ExprList) -> Result bool { match (self, other) { (Obj::Null, Obj::Null) => true, - (Obj::List(l), Obj::List(r)) => { - let lb = l.values.borrow(); - let rb = r.values.borrow(); - let liter = lb.iter(); - let riter = rb.iter(); - liter - .zip(riter) - .all(|((lk, lv), (rk, rv))| lk == rk && lv == rv) - } + (Obj::List(l), Obj::List(r)) => l + .pairs_ref() + .iter() + .zip(r.pairs_ref().iter()) + .all(|((lk, lv), (rk, rv))| lk == rk && lv == rv), (Obj::Expr(l), Obj::Expr(r)) => l == r, (Obj::Promise(None, lc, lenv), Obj::Promise(None, rc, renv)) => { lc == rc && lenv == renv diff --git a/src/object/cow.rs b/src/object/cow.rs index 0bfdcfdc..9001f426 100644 --- a/src/object/cow.rs +++ b/src/object/cow.rs @@ -11,7 +11,7 @@ pub trait ViewMut { /// Internal Data representation for copy-on-write semantics. #[derive(Debug, PartialEq, Default)] -pub struct CowObj(Rc>>); +pub struct CowObj(pub Rc>>); impl Clone for CowObj { fn clone(&self) -> Self { @@ -59,6 +59,10 @@ impl CowObj { CowObj(x) } + pub fn inner_rc(&self) -> Rc { + self.borrow().clone() + } + /// Get mutable access to the internal vector. /// In case more than one reference to the internal data exists, /// the vector is cloned. diff --git a/src/object/environment.rs b/src/object/environment.rs index 33918f3f..735d0403 100644 --- a/src/object/environment.rs +++ b/src/object/environment.rs @@ -8,6 +8,7 @@ use crate::callable::builtins::BUILTIN; use crate::context::Context; use crate::error::Error; use crate::lang::{EvalResult, Signal}; +use crate::object::types::Character; use crate::object::ViewMut; use super::{Expr, ExprList, List, Obj}; @@ -55,8 +56,8 @@ impl Environment { } pub fn append(&self, l: List) { - for (key, value) in l.values.borrow().iter() { - if let Some(name) = key { + for (key, value) in l.pairs_ref().iter() { + if let Character::Some(name) = key { self.values.borrow_mut().insert(name.clone(), value.clone()); } } @@ -144,3 +145,18 @@ impl Display for Environment { Ok(()) } } + +#[cfg(test)] + +mod tests { + use crate::{r, r_expect}; + + #[test] + fn dollar() { + r_expect! {{" + e = environment() + e$x = 1 + e$x == 1 + "}} + } +} diff --git a/src/object/list.rs b/src/object/list.rs index 7cf4144c..dfc3e5d8 100644 --- a/src/object/list.rs +++ b/src/object/list.rs @@ -1,279 +1,7 @@ -use hashbrown::HashMap; +use crate::object::rep::Rep; +use crate::object::Obj; -use crate::error::Error; -use crate::lang::EvalResult; - -use super::*; - -type ListNameMap = HashMap>; - -#[derive(Debug, Clone, PartialEq, Default)] -pub struct List { - pub names: CowObj, - pub values: CowObj, Obj)>>, - pub subsets: Subsets, -} - -impl From, Obj)>> for List { - fn from(value: Vec<(Option, Obj)>) -> Self { - let mut result = List { values: CowObj::from(value), ..Default::default() }; - - result.reindex(); - result - } -} - -impl List { - pub fn reindex(&mut self) { - self.names.with_inner_mut(|names| { - names.drain(); - - for (i, (k, _)) in self.values.borrow().iter().enumerate() { - if let Some(name) = k { - let indices = names.entry(name.clone()).or_default(); - if !indices.contains(&i) { - indices.push(i) - } - } - } - }) - } - - pub fn subset(&self, by: Subset) -> List { - let Subsets(mut inner) = self.subsets.clone(); - inner.push(by); - List { - names: self.names.clone(), - values: self.values.view_mut(), - subsets: Subsets(inner), - } - } - - pub fn assign(&mut self, value: Obj) -> EvalResult { - match value { - // remove elements from list - Obj::Null => { - let n = self.values.len(); - let indices = self - .subsets - .clone() - .bind_names(self.names.clone()) - .into_iter() - .take(n); - - self.values.with_inner_mut(|values| { - for (i, _) in indices { - values.remove(i); - } - }); - - self.reindex(); - - // TODO(feat): need to return list with NULL elements when - // index is NA - - Ok(Obj::List(List { - names: self.names.clone(), - values: self.values.clone(), - subsets: self.subsets.clone(), - })) - } - - // any single length R value - any if any.len() == Some(1) => { - let n = self.values.len(); - let indices = self - .subsets - .clone() - .bind_names(self.names.clone()) - .into_iter() - .take(n); - - self.values.with_inner_mut(|v| { - // first check to see if we need to extend - if let Some(max) = self - .subsets - .clone() - .bind_names(self.names.clone()) - .into_iter() - .map(|(i, _)| i) - .max() - { - v.reserve(max.saturating_sub(n)) - } - - // then assign to indices - for (_, i) in indices { - if let Some(i) = i { - v[i].1 = any.clone() - } - } - }); - - Ok(Obj::List(List { - names: self.names.clone(), - values: self.values.clone(), - subsets: self.subsets.clone(), - })) - } - // vectorized assignment - // TODO(feature): warn when index recycling does not cycle evenly - any if any.len() == Some(self.len()) => { - let n = self.values.len(); - let indices = self - .subsets - .clone() - .bind_names(self.names.clone()) - .into_iter() - .take(n); - - self.values.with_inner_mut(|v| { - // first check to see if we need to extend - if let Some(max) = self - .subsets - .clone() - .bind_names(self.names.clone()) - .into_iter() - .map(|(i, _)| i) - .max() - { - v.reserve(max.saturating_sub(n)) - } - - // then assign to indices - for (any_i, (_, i)) in indices.enumerate() { - if let (Some(value), Some(i)) = (any.get(any_i), i) { - v[i].1 = value; - } - } - }); - - Ok(Obj::List(List { - names: self.names.clone(), - values: self.values.clone(), - subsets: self.subsets.clone(), - })) - } - other => { - let n = self.values.len(); - let indices = self - .subsets - .clone() - .bind_names(self.names.clone()) - .into_iter() - .take(n); - - self.values.with_inner_mut(|v| { - // first check to see if we need to extend - if let Some(max) = self - .subsets - .clone() - .bind_names(self.names.clone()) - .into_iter() - .map(|(i, _)| i) - .max() - { - v.reserve(max.saturating_sub(n)) - } - - // then assign to indices - for (_, i) in indices { - if let Some(i) = i { - v[i].1 = other.clone() - } - } - }); - - Ok(Obj::List(List { - names: self.names.clone(), - values: self.values.clone(), - subsets: self.subsets.clone(), - })) - } - } - } - - pub fn try_get(&self, index: Obj) -> EvalResult { - let err = Error::Other("Cannot use object for indexing".to_string()); - match index.as_vector()? { - Obj::Vector(v) => Ok(Obj::List(self.subset(v.try_into()?))), - _ => Err(err.into()), - } - } - - pub fn try_get_inner_mut(&self, index: Obj) -> EvalResult { - let err_invalid = Error::Other("Cannot use object for indexing".to_string()); - let err_index_invalid = Error::Other("Index out of bounds".to_string()); - - match index.as_vector()? { - Obj::Vector(v) if v.len() == 1 => { - let Subsets(mut subsets) = self.subsets.clone(); - subsets.push(v.try_into()?); - - if let Some((i, _)) = Subsets(subsets) - .bind_names(self.names.clone()) - .into_iter() - .next() - { - self.values.with_inner_mut(|v| { - v.get_mut(i) - .map_or(Err(err_index_invalid.into()), |(_, i)| Ok(i.view_mut())) - }) - } else { - Ok(Obj::Null) - } - } - _ => Err(err_invalid.into()), - } - } - - pub fn try_get_inner(&self, index: Obj) -> EvalResult { - #[allow(clippy::map_clone)] - self.try_get_inner_mut(index).map(|v| v.clone()) - } - - pub fn dedup_last(self) -> Self { - self.names.with_inner_mut(|names| { - let mut dups: Vec = names - .iter() - .flat_map(|(_, indices)| { - indices - .split_last() - .map_or(vec![], |(_, leading_dups)| leading_dups.to_vec()) - }) - .collect(); - - dups.sort(); - - self.values.with_inner_mut(|vs| { - for i in dups.into_iter().rev() { - vs.remove(i); - } - }); - }); - - self.names.with_inner_mut(|names| { - for (_, indices) in names.iter_mut() { - indices.drain(0..indices.len()); - } - }); - - self - } - - pub fn len(&self) -> usize { - let Subsets(inner) = &self.subsets; - match inner.as_slice() { - [] => self.values.borrow().len(), - [.., last] => std::cmp::min(self.values.borrow().len(), last.len()), - } - } - - #[must_use] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} +pub type List = Rep; #[cfg(test)] mod tests { @@ -296,7 +24,16 @@ mod tests { "}} } #[test] - fn copy_on_write_bracket_names() { + fn copy_on_write_double_bracket_names() { + r_expect! {{r#" + l1 = (a = 1,) + l2 = l1 + l1[["a"]] = 2 + l1$a == 2 & l2$a == 1 + "#}} + } + #[test] + fn copy_on_write_single_bracket_names() { r_expect! {{r#" l1 = (a = 1,) l2 = l1 @@ -309,7 +46,7 @@ mod tests { r_expect! {{r#" l = (a = 1, b = 2, c = 3) l1 = l - l1[c("a", "b")] = c(10, 20) + l1[c("a", "b")] = (10, 20) l1$a == 10 && l1$b == 20 & l$a == 1 & l$b == 2 "#}} @@ -319,7 +56,7 @@ mod tests { r_expect! {{" l = (1, 2) l1 = l - l1[1:2] = [10, 20] + l1[1:2] = (10, 20) l1[[1]] == 10 && l1[[2]] == 20 & l[[1]] == 1 & l[[2]] == 2 "}} } @@ -369,4 +106,35 @@ mod tests { l[[1]] == 10 & l[[2]] == 20 "#}} } + #[test] + fn list_assign() { + r_expect! {{r#" + l = (function() null, ) + l[[1]] = 1 + l[[1]] == 1 + "#}} + } + #[test] + fn assign_atomic_to_list_slice() { + r_expect! {{r#" + l = (1, 2, 3) + l[1:2] = [3, 4] + l[[1]] == 3 & l[[2]] == 4 + "#}} + } + #[test] + fn assign_null() { + r_expect! {{r#" + l = (1, ) + l[[1]] = null + is_null(l[[1]]) + "#}} + } + #[test] + #[should_panic] + fn index_length_one() { + r_expect! {{r#" + list(1, 2)[[c(1L, 2L)]] + "#}} + } } diff --git a/src/object/vector/core.rs b/src/object/vector/core.rs index 8e9b25d2..8784799c 100644 --- a/src/object/vector/core.rs +++ b/src/object/vector/core.rs @@ -3,10 +3,13 @@ use std::fmt::Display; use crate::error::Error; use crate::lang::EvalResult; +use crate::lang::Signal; +use crate::object::CowObj; use crate::object::Obj; use super::coercion::CoercibleInto; use super::rep::Rep; +use super::reptype::IterableValues; use super::reptype::RepType; use super::subset::Subset; use super::types::*; @@ -40,6 +43,13 @@ impl OptionNA { OptionNA::NA => OptionNA::NA, } } + + pub fn as_option(self) -> Option { + match self { + OptionNA::Some(x) => Option::Some(x), + OptionNA::NA => Option::None, + } + } } #[derive(Debug, PartialEq)] @@ -52,12 +62,24 @@ pub enum Vector { // Raw(Raw), } +// TODO: Implement vector more like Rep +// I.e. the conversion from and to objects should be handled via TryFrom/From +// and .into() calls impl Clone for Vector { fn clone(&self) -> Self { - self.lazy_copy() + match self { + Vector::Double(v) => Vector::Double(v.clone()), + Vector::Character(v) => Vector::Character(v.clone()), + Vector::Integer(v) => Vector::Integer(v.clone()), + Vector::Logical(v) => Vector::Logical(v.clone()), + } } } +// TODO: Ensure that Vector API does not go beyond Rep unless it is really +// necessary. + +/// See [`Rep`] for the documentation on the methods. impl Vector { pub fn get(&self, index: usize) -> Option { use Vector::*; @@ -69,18 +91,63 @@ impl Vector { } } - /// Create a lazy copy of the vector. - /// When mutating vectors, the internal data only needs to be copied when there is more than - /// one such lazy copy. - pub fn lazy_copy(&self) -> Self { + pub fn set_subset(&mut self, subset: Subset, value: Obj) -> Result { + use Vector::*; match self { - Vector::Double(v) => Vector::Double(v.clone()), - Vector::Character(v) => Vector::Character(v.clone()), - Vector::Integer(v) => Vector::Integer(v.clone()), - Vector::Logical(v) => Vector::Logical(v.clone()), + Double(x) => x + .set_subset(subset, value.try_into()?) + .map(|x| Double(Rep::from(vec![x]))), + Integer(x) => x + .set_subset(subset, value.try_into()?) + .map(|x| Integer(Rep::from(vec![x]))), + Character(x) => x + .set_subset(subset, value.try_into()?) + .map(|x| Character(Rep::from(vec![x]))), + Logical(x) => x + .set_subset(subset, value.try_into()?) + .map(|x| Logical(Rep::from(vec![x]))), + } + } + + pub fn iter_names(&self) -> Option> { + use Vector::*; + match self { + Double(x) => x.iter_names(), + Integer(x) => x.iter_names(), + Logical(x) => x.iter_names(), + Character(x) => x.iter_names(), + } + } + + pub fn is_named(&self) -> bool { + use Vector::*; + match self { + Double(x) => x.is_named(), + Integer(x) => x.is_named(), + Logical(x) => x.is_named(), + Character(x) => x.is_named(), + } + } + + pub fn names(&self) -> Option>> { + use Vector::*; + match self { + Double(x) => x.names(), + Integer(x) => x.names(), + Logical(x) => x.names(), + Character(x) => x.names(), } } + pub fn set_names(&self, names: CowObj>) { + match self { + Vector::Character(x) => x.set_names(names), + Vector::Logical(x) => x.set_names(names), + Vector::Integer(x) => x.set_names(names), + Vector::Double(x) => x.set_names(names), + }; + } + pub fn try_get(&self, index: Obj) -> EvalResult { let err = Error::Other("Vector index cannot be coerced into a valid indexing type.".to_string()); @@ -244,6 +311,12 @@ impl TryInto for Vector { } } +impl From>> for Vector { + fn from(x: CowObj>) -> Self { + Vector::Character(x.into()) + } +} + impl From> for Vector { fn from(x: RepType) -> Self { Vector::Double(x.into()) @@ -912,3 +985,18 @@ impl std::ops::BitAnd for Vector { } } } + +#[cfg(test)] + +mod tests { + use crate::{r, r_expect}; + + #[test] + fn double_brackets_assign() { + r_expect! {{" + x = c(1, 2) + x[[1]] = 10 + x[[1]] == 10 & x[[2]] == 2 + "}} + } +} diff --git a/src/object/vector/rep.rs b/src/object/vector/rep.rs index 7ce94f9c..8fec7372 100644 --- a/src/object/vector/rep.rs +++ b/src/object/vector/rep.rs @@ -1,15 +1,17 @@ -use std::cell::{Ref, RefCell}; +use std::cell::{Ref, RefCell, RefMut}; use std::fmt::{Debug, Display}; use super::coercion::{AtomicMode, CoercibleInto, CommonCmp, CommonNum, MinimallyNumeric}; use super::iterators::{map_common_numeric, zip_recycle}; -use super::reptype::RepType; -use super::reptype::RepTypeIter; +use super::reptype::{ + IntoIterableRefNames, IntoIterableRefPairs, IntoIterableRefValues, IterablePairs, + IterableValues, Naming, RepType, +}; use super::subset::Subset; use super::types::*; use super::{OptionNA, Pow, VecPartialCmp}; -use crate::object::CowObj; -use crate::object::ViewMut; +use crate::lang::Signal; +use crate::object::{CowObj, Obj, Subsets, ViewMut}; /// Vector Representation /// @@ -18,27 +20,68 @@ use crate::object::ViewMut; #[derive(Debug, PartialEq)] pub struct Rep(pub RefCell>); -impl Clone for Rep { +impl Clone for Rep { fn clone(&self) -> Self { match self.borrow().clone() { - RepType::Subset(v, s) => Rep(RefCell::new(RepType::Subset(v.clone(), s.clone()))), + RepType::Subset(v, s, n) => Rep(RefCell::new(RepType::Subset( + v.clone(), + s.clone(), + n.clone(), + ))), } } } -impl ViewMut for Rep { +impl ViewMut for Rep { fn view_mut(&self) -> Self { Self(RefCell::new(self.borrow().view_mut())) } } +impl Rep { + /// Get the inner value mutably. + /// This is used for assignments like `list(1)[[1]] = 10`. + pub fn try_get_inner_mut(&self, subset: Subset) -> Result { + self.borrow().try_get_inner_mut(subset) + } + + /// Get a cloned version of the inner value. + /// This is used for accessing inner values like `list(1)[[1]]`. + pub fn try_get_inner(&self, subset: Subset) -> Result { + #[allow(clippy::map_clone)] + self.try_get_inner_mut(subset).map(|x| x.clone()) + } +} + +impl Rep { + /// Iterate over the owned names and values of the vector. + pub fn iter_pairs(&self) -> IterablePairs { + self.0.borrow().clone().iter_pairs() + } +} + impl Rep where - T: AtomicMode + Clone + Default, + T: Clone + Default, { - fn borrow(&self) -> Ref> { + pub fn borrow(&self) -> Ref> { self.0.borrow() } + + pub fn borrow_mut(&mut self) -> RefMut> { + self.0.borrow_mut() + } + + /// Iterate over the (owned) values of the vector. + pub fn iter_values(&self) -> IterableValues { + self.0.borrow().iter_values() + } + + /// Iterate over the names of the vector (if they exist). + pub fn iter_names(&self) -> Option> { + self.0.borrow().iter_names() + } + fn materialize_inplace(&self) -> &Self { // TODO: Rewrite this to avoid copying unnecessarily let new_repr = { self.borrow().materialize() }; @@ -47,6 +90,86 @@ where self } + /// Reindex the mapping from names to indices using the names vector from the `Naming`. + pub fn reindex(&mut self) { + self.borrow_mut().reindex() + } + + /// Set the names of the vector. + pub fn set_names(&self, names: CowObj>>) { + let new_repr = self.borrow().materialize().set_names(names); + self.0.replace(new_repr); + } + + /// Whether the vector representation has names. + pub fn is_named(&self) -> bool { + matches!(*self.borrow(), RepType::Subset(.., Some(_))) + } + + /// Return the names of the vector if there are any. + pub fn names(&self) -> Option>> { + match self.borrow().clone() { + RepType::Subset(_, s, n) => { + if s.is_empty() { + n.map(|n| n.clone().names) + } else if n.is_some() { + Some( + self.iter_names() + .expect("checked that names exist") + .collect::>() + .into(), + ) + } else { + None + } + } + } + } + + pub fn dedup_last(self) -> Self { + self.0.into_inner().dedup_last().into() + } + + /// Constructs a new, empty `Rep` with at least the specified `capacity`. + /// Names are only include if `names` is true. + pub fn with_capacity(capacity: usize, names: bool) -> Self { + let naming = if names { + Some(Naming::with_capacity(capacity)) + } else { + None + }; + Self(RefCell::new(RepType::Subset( + CowObj::from(Vec::with_capacity(capacity)), + Subsets::default(), + naming, + ))) + } + + /// Get an `RepTypeIntoIterablePairs` which in turn can be converted into an iterator over + /// pairs of references (&name, &value). + /// + /// Directly getting an iterator is not possible due to lifetime issues. + pub fn pairs_ref(&self) -> IntoIterableRefPairs { + self.0.borrow().pairs_ref() + } + + /// Get an `Option>` which in turn can be converted into an iterator over + /// references to the values. + /// The `None` variant is returned if the `Rep` is not named. + /// + /// Directly getting an iterator is not possible due to lifetime issues. + pub fn values_ref(&self) -> IntoIterableRefValues { + self.0.borrow().values_ref() + } + + /// Get an `RepTypeIntoIterableValues` which in turn can be converted into an iterator over + /// references to the names. + /// + /// Directly getting an iterator is not possible due to lifetime issues. + pub fn names_ref(&self) -> Option { + self.0.borrow().names_ref() + } + pub fn materialize(&self) -> Self { self.borrow().materialize().into() } @@ -101,7 +224,23 @@ where x.map(|x| x.into()) } - pub fn assign(&mut self, value: Self) -> Self { + /// Change a value at the location given by `subset` to the provided `value`. + /// If the `subset` does not have length `1`, an error is returned. + pub fn set_subset(&mut self, subset: Subset, value: T) -> Result { + // Used for `[[`-assignment. + self.0.borrow_mut().set_subset(subset, value) + } + + /// Push a named `value` with a given `name` onto the `Rep`. + pub fn push_named(&self, name: OptionNA, value: T) { + self.borrow().push_named(name, value) + } + + pub fn assign(&mut self, value: Rep) -> Self + where + T: From + Clone, + R: Clone + Default, + { self.0.borrow_mut().assign(value.0.into_inner()).into() } /// Test the mode of the internal vector type @@ -109,19 +248,31 @@ where /// Internally, this is defined by the [crate::object::coercion::AtomicMode] /// implementation of the vector's element type. /// - pub fn is_double(&self) -> bool { + pub fn is_double(&self) -> bool + where + T: AtomicMode, + { T::is_double() } /// See [Self::is_double] for more information - pub fn is_logical(&self) -> bool { + pub fn is_logical(&self) -> bool + where + T: AtomicMode, + { T::is_logical() } /// See [Self::is_double] for more information - pub fn is_integer(&self) -> bool { + pub fn is_integer(&self) -> bool + where + T: AtomicMode, + { T::is_integer() } /// See [Self::is_double] for more information - pub fn is_character(&self) -> bool { + pub fn is_character(&self) -> bool + where + T: AtomicMode, + { T::is_character() } @@ -151,7 +302,7 @@ where /// pub fn as_mode(&self) -> Rep where - T: CoercibleInto, + T: CoercibleInto + AtomicMode, Mode: Clone, { Rep(RefCell::new(self.borrow().as_mode())) @@ -160,7 +311,7 @@ where /// See [Self::as_mode] for more information pub fn as_logical(&self) -> Rep where - T: CoercibleInto, + T: CoercibleInto + AtomicMode, { self.as_mode::() } @@ -168,7 +319,7 @@ where /// See [Self::as_mode] for more information pub fn as_integer(&self) -> Rep where - T: CoercibleInto, + T: CoercibleInto + AtomicMode, { self.as_mode::() } @@ -176,7 +327,7 @@ where /// See [Self::as_mode] for more information pub fn as_double(&self) -> Rep where - T: CoercibleInto, + T: CoercibleInto + AtomicMode, { self.as_mode::() } @@ -184,7 +335,7 @@ where /// See [Self::as_mode] for more information pub fn as_character(&self) -> Rep where - T: CoercibleInto, + T: CoercibleInto + AtomicMode, { self.as_mode::() } @@ -209,67 +360,59 @@ where .into_inner() .vectorized_partial_cmp(other.0.into_inner()) } - - fn get_inner(&self, index: usize) -> Option { - self.borrow().get_inner(index) - } } impl Default for Rep where - T: AtomicMode + Clone + Default, + T: Clone + Default, { fn default() -> Self { Rep(RefCell::new(RepType::default())) } } -pub struct RepIter(RepTypeIter); - -impl IntoIterator for Rep -where - T: AtomicMode + Clone + Default, -{ - type Item = T; - type IntoIter = RepIter; - fn into_iter(self) -> Self::IntoIter { - let x = self.0.into_inner(); - RepIter(x.into_iter()) - } -} - -impl Iterator for RepIter +impl From>> for Rep where - T: AtomicMode + Clone + Default, + T: Clone + Default, { - type Item = T; - fn next(&mut self) -> Option { - self.0.next() + fn from(rep: CowObj>) -> Self { + Rep(RefCell::new(rep.into())) } } impl From> for Rep where - T: AtomicMode + Clone + Default, + T: Clone + Default, { fn from(rep: RepType) -> Self { Rep(RefCell::new(rep)) } } +// TODO: I think this should err when rep has length > 1 impl TryInto for Rep> where OptionNA: AtomicMode + Clone + CoercibleInto>, + T: 'static, { type Error = (); fn try_into(self) -> Result { - self.get_inner(0).map_or( - Err(()), - |i| match CoercibleInto::>::coerce_into(i) { - OptionNA::Some(x) => Ok(x), - OptionNA::NA => Err(()), - }, - ) + self.iter_pairs() + .next() + .map(|(_, x)| x) + .map_or( + Err(()), + |i| match CoercibleInto::>::coerce_into(i) { + OptionNA::Some(x) => Ok(x), + OptionNA::NA => Err(()), + }, + ) + } +} + +impl From> for Rep { + fn from(value: Vec<(Character, Obj)>) -> Self { + Rep(RefCell::new(value.into())) } } @@ -321,6 +464,12 @@ impl From> for Rep { } } +impl From, T)>> for Rep { + fn from(value: Vec<(Option, T)>) -> Self { + Rep(RefCell::new(value.into())) + } +} + impl Display for Rep where T: AtomicMode + Debug + Default + Clone, @@ -341,47 +490,104 @@ where return write!(f, "character(0)"); } } - let nlen = format!("{}", n).len(); - // TODO: iteratively calculate when we hit max print so our - // max_len isn't inflated by a value that is omitted - - let xc = self.inner().clone(); - let xb = xc.borrow(); - - let x_strs = xb.iter().map(|xi| format!("{:?}", xi)); - let max_len = x_strs - .clone() - .fold(0, |max_len, xi| std::cmp::max(max_len, xi.len())); - - let mut col = 0; - let gutterlen = 2 + nlen + 1; - - // hard coded max print & console width - let maxprint = 20 * ((80 - gutterlen) / max_len); - - x_strs - .take(maxprint) - .enumerate() - .try_for_each(|(i, x_str)| { - if i == 0 { - col = gutterlen + max_len; - write!(f, "{:>3$}[{}] {:>4$}", "", i + 1, x_str, nlen - 1, max_len) - } else if col + 1 + max_len > 80 { - col = gutterlen + max_len; - let i_str = format!("{}", i + 1); - let gutter = nlen - i_str.len(); - write!(f, "\n{:>3$}[{}] {:>4$}", "", i_str, x_str, gutter, max_len) - } else { - col += 1 + max_len; - write!(f, " {:>1$}", x_str, max_len) + // calculate how many characters are printed per value. + // The iteraror yields the characters needed for a specific item. + fn element_width(iter: impl Iterator) -> usize { + let mut elt_width = 1_usize; + for (i, width) in iter.enumerate() { + elt_width = std::cmp::max(elt_width, width); + if elt_width * (i + 1) >= 20 * 80 { + break; } - })?; - - if n > maxprint { - write!(f, "\n[ omitting {} entries ]", n - maxprint)?; + } + elt_width } + if !self.is_named() { + let elt_width = + element_width(self.values_ref().iter().map(|x| format!("{:?}", x).len())); + + let mut values_ref = self.values_ref(); + let x_strs = values_ref.iter().map(|xi| format!("{:?}", xi)); + + let mut col = 0; + let gutterlen = 2 + nlen + 1; + + // hard coded max print & console width + // we print at most 20 rows + let maxprint = 20 * ((80 - gutterlen) / (elt_width + 1)); + + x_strs + .take(maxprint) + .enumerate() + .try_for_each(|(i, x_str)| { + if i == 0 { + col = gutterlen + elt_width; + write!( + f, + "{:>3$}[{}] {:>4$}", + "", + i + 1, + x_str, + nlen - 1, + elt_width + ) + } else if col + 1 + elt_width > 80 { + col = gutterlen + elt_width; + let i_str = format!("{}", i + 1); + let gutter = nlen - i_str.len(); + write!( + f, + "\n{:>3$}[{}] {:>4$}", + "", i_str, x_str, gutter, elt_width + ) + } else { + col += 1 + elt_width; + write!(f, " {:>1$}", x_str, elt_width) + } + })?; + + if n > maxprint { + write!(f, "\n[ omitting {} entries ]", n - maxprint)?; + } + } else { + let elt_width = element_width( + self.pairs_ref() + .iter() + .map(|x| std::cmp::max(format!("{:}", x.0).len(), format!("{:?}", x.1).len())), + ); + let mut values_ref = self.values_ref(); + let mut names_ref = self + .names_ref() + .expect("already checked existence of names"); + + let mut values_strs = values_ref.iter().map(|x| format!("{:?}", x)); + let mut names_strs = names_ref.iter().map(|x| format!("{:}", x)); + + // hard coded max print & console width + // we print at most 20 rows + let elts_per_line = 80 / (elt_width + 1); + + 'lines: for _ in 1..=20 { + for _ in 1..=elts_per_line { + if let Some(name) = names_strs.next() { + write!(f, "{:}{:>2$}", name, " ", elt_width - name.len())?; + } else { + break; + } + } + writeln!(f)?; + for _ in 1..=elts_per_line { + if let Some(value) = values_strs.next() { + write!(f, "{:}{:>2$}", value, " ", elt_width - value.len())?; + } else { + break 'lines; + } + } + writeln!(f)?; + } + } Ok(()) } } diff --git a/src/object/vector/reptype.rs b/src/object/vector/reptype.rs index 74c5f607..237e6f73 100644 --- a/src/object/vector/reptype.rs +++ b/src/object/vector/reptype.rs @@ -6,13 +6,97 @@ use super::subset::Subset; use super::subsets::Subsets; use super::types::*; use super::{OptionNA, Pow, VecPartialCmp}; +use crate::error::Error; +use crate::lang::Signal; use crate::object::{CowObj, ViewMut}; +use hashbrown::HashMap; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug, Clone, PartialEq, Default)] +pub struct Naming { + // TODO: change this to usize and not Vec (after making names unique) + pub map: CowObj>>, + pub names: CowObj>>, +} + +impl Naming { + /// Create an empty `Naming` + pub fn new() -> Self { + Naming::default() + } + + /// Create a naming with the given `capacity`. + pub fn with_capacity(capacity: usize) -> Self { + Self { + map: HashMap::>::with_capacity(capacity).into(), + names: CowObj::from(Vec::::with_capacity(capacity)), + } + } + + /// Push a new name onto the `Naming`. + pub fn push_name(&self, name: OptionNA) { + self.names.with_inner_mut(|v| v.push(name.clone())); + if let OptionNA::Some(name) = name { + let n = self.names.len() - 1; + self.map.with_inner_mut(|map| { + let indices = map.entry(name.clone()).or_default(); + if !indices.contains(&n) { + indices.push(n); + }; + }); + }; + } + + /// Get mutable access to the internal data (map and names vector) via the passed closure. + pub fn with_inner_mut(&self, f: F) -> R + where + F: FnOnce(&mut HashMap>, &mut Vec>) -> R, + { + self.map + .with_inner_mut(|map| self.names.with_inner_mut(|names| f(map, names))) + } +} + +impl From> for RepType { + fn from(value: Vec<(Character, T)>) -> Self { + let mut names = Vec::with_capacity(value.len()); + let mut values = Vec::with_capacity(value.len()); + for (k, v) in value { + names.push(k); + values.push(v); + } + + RepType::Subset( + CowObj::new(Rc::new(RefCell::new(Rc::new(values)))), + Subsets::default(), + Option::Some(Naming::from(names)), + ) + } +} + +impl From>> for Naming { + fn from(value: CowObj>) -> Self { + let mut map: HashMap> = HashMap::new(); + + value.iter().enumerate().for_each(|(i, maybe_name)| { + if let OptionNA::Some(name) = maybe_name { + let indices = map.entry(name.clone()).or_default(); + if !indices.contains(&i) { + indices.push(i); + }; + }; + }); + + Self { map: map.into(), names: value } + } +} /// Vector #[derive(Debug, PartialEq)] pub enum RepType { // Vector::Subset encompasses a "raw" vector (no subsetting) - Subset(CowObj>, Subsets), + Subset(CowObj>, Subsets, Option), // Iterator includes things like ranges 1:Inf, and lazily computed values // Iter(Box>) } @@ -20,49 +104,158 @@ pub enum RepType { impl Clone for RepType { fn clone(&self) -> Self { match self { - RepType::Subset(v, s) => RepType::Subset(v.view_mut(), s.clone()), + RepType::Subset(v, s, n) => RepType::Subset(v.clone(), s.clone(), n.clone()), } } } -impl Default for RepType { +impl Default for RepType { fn default() -> Self { Self::new() } } -impl IntoIterator for RepType -where - T: AtomicMode + Clone + Default, -{ - type Item = T; - type IntoIter = RepTypeIter; - fn into_iter(self) -> Self::IntoIter { - // FIXME: this might materialize - let n = self.len(); - match self { - RepType::Subset(..) => RepTypeIter::SubsetIter(self, 0, n), +impl RepType { + /// Retrieve the internal data as a mutable view. + /// This is important for lists for things like `l$a[1:2] = c(10, 11)` + pub fn try_get_inner_mut(&self, subset: Subset) -> Result { + let new_subset = self.subset(subset); + match new_subset { + RepType::Subset(..) => { + let mut iter = new_subset.iter_subset_indices(); + + if let Some(i) = iter.next() { + if iter.next().is_some() { + return Error::Other("subset has length > 1".to_string()).into(); + } + + // TODO: subsetting with NA should not be possible. + let i = i.unwrap(); + + Ok(self.with_inner_mut(|values| values[i].view_mut())) + } else { + Error::Other("subset is empty".to_string()).into() + } + } } } } -pub enum RepTypeIter { - SubsetIter(RepType, usize, usize), +pub struct IntoIterableRefNames { + names: Rc>, + na_name: Character, + iter: Box>>, } -impl Iterator for RepTypeIter { - type Item = T; +pub struct RepTypeIterableNames<'a> { + names: &'a [Character], + na_name: &'a Character, + iter: &'a mut Box>>, +} + +impl IntoIterableRefNames { + pub fn iter(&mut self) -> RepTypeIterableNames<'_> { + let names = &self.names[..]; + RepTypeIterableNames { + names, + na_name: &self.na_name, + iter: &mut self.iter, + } + } +} + +impl<'a> Iterator for RepTypeIterableNames<'a> { + type Item = &'a Character; + fn next(&mut self) -> Option { - match self { - RepTypeIter::SubsetIter(rep, i, len) => { - if i < len { - let x = Some(rep.get_atom(*i)); - *i += 1; - x - } else { - None - } + if let Some(i) = self.iter.next()? { + Some(&self.names[i]) + } else { + Some(self.na_name) + } + } +} + +pub struct IntoIterableRefValues { + values: Rc>, + na_value: T, + iter: Box>>, +} + +impl IntoIterableRefValues { + pub fn iter(&mut self) -> IterableRefValues<'_, T> { + let values = &self.values[..]; + + IterableRefValues { + values, + na_value: &self.na_value, + iter: &mut self.iter, + } + } +} + +pub struct IntoIterableRefPairs { + values: Rc>, + names: Option>>, + na_value: T, + na_name: Character, + iter: Box>>, +} + +impl IntoIterableRefPairs { + pub fn iter(&mut self) -> IterableRefPairs<'_, T> { + let values = &self.values[..]; + + let names = self.names.as_ref().map(|names| &names[..]); + + IterableRefPairs { + values, + names, + na_value: &self.na_value, + na_name: &self.na_name, + iter: &mut self.iter, + } + } +} + +pub struct IterableRefValues<'a, T: Clone> { + values: &'a [T], + na_value: &'a T, + iter: &'a mut Box>>, +} + +pub struct IterableRefPairs<'a, T: Clone> { + values: &'a [T], + names: Option<&'a [Character]>, + na_value: &'a T, + na_name: &'a Character, + iter: &'a mut Box>>, +} + +impl<'a, T: Clone> Iterator for IterableRefPairs<'a, T> { + type Item = (&'a Character, &'a T); + + fn next(&mut self) -> Option { + if let Some(i) = self.iter.next()? { + if let Some(names) = self.names { + Option::Some((&names[i], &self.values[i])) + } else { + Option::Some((self.na_name, &self.values[i])) } + } else { + Option::Some((self.na_name, self.na_value)) + } + } +} + +impl<'a, T: Clone> Iterator for IterableRefValues<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if let Some(i) = self.iter.next()? { + Some(&self.values[i]) + } else { + Some(self.na_value) } } } @@ -70,12 +263,47 @@ impl Iterator for RepTypeIter { impl ViewMut for RepType { fn view_mut(&self) -> Self { match self { - RepType::Subset(v, s) => RepType::Subset(v.view_mut(), s.clone()), + RepType::Subset(v, s, n) => RepType::Subset(v.view_mut(), s.clone(), n.clone()), } } } -impl RepType { +pub struct IterableValues { + values: Rc>, + iter: Box>>, +} + +impl Iterator for IterableValues { + type Item = T; + fn next(&mut self) -> Option { + // FIXME: Already assumes no indexing with NA + let i = self.iter.next()?.unwrap(); + Some(self.values[i].clone()) + } +} + +pub struct IterablePairs { + values: Rc>, + names: Option>>, + iter: Box>>, +} + +impl Iterator for IterablePairs { + type Item = (Character, T); + fn next(&mut self) -> Option { + // FIXME: Already assumes no indexing with NA + let i = self.iter.next()?.unwrap(); + let value = self.values[i].clone(); + let name = if let Some(names) = &self.names { + names[i].clone() + } else { + Character::NA + }; + Some((name, value)) + } +} + +impl RepType { /// Create an empty vector /// /// The primary use case for this function is to support testing, and there @@ -95,39 +323,242 @@ impl RepType { /// ``` /// pub fn new() -> Self { - RepType::Subset(Vec::new().into(), Subsets(Vec::new())) + RepType::Subset( + Vec::new().into(), + Subsets(Vec::new()), + Some(Naming::default()), + ) + } + + pub fn set_subset(&mut self, subset: Subset, value: T) -> Result { + match &self { + RepType::Subset(..) => { + let err = Error::Other("subset must have length 1".to_string()); + + let mut iter = self.clone().subset(subset).iter_subset_indices(); + let i1 = iter.next(); + + // check that subset has exactly length 1 + // assumes no indexing with NA (unwrap the option) + let i = if let Some(i) = i1 { + if iter.next().is_some() { + return err.into(); + } + i + } else { + return err.into(); + } + .unwrap(); + + self.with_inner_mut(|v| v[i] = value.clone()); + Ok(value.clone()) + } + } + } + + pub fn values_ref(&self) -> IntoIterableRefValues { + match self.clone() { + RepType::Subset(values, ..) => { + let iter = Box::new(self.iter_subset_indices()); + let values = values.inner_rc(); + + IntoIterableRefValues { values, na_value: T::default(), iter } + } + } + } + + pub fn names_ref(&self) -> Option { + match self.clone() { + RepType::Subset(.., naming) => { + let iter = Box::new(self.iter_subset_indices()); + let naming = naming?; + let names = naming.names.inner_rc(); + + Some(IntoIterableRefNames { names, na_name: Character::default(), iter }) + } + } + } + + pub fn pairs_ref(&self) -> IntoIterableRefPairs { + match self.clone() { + RepType::Subset(values, _, maybe_naming) => { + let iter = Box::new(self.iter_subset_indices()); + let values = values.inner_rc(); + let names = maybe_naming.map(|x| x.names.inner_rc()); + + IntoIterableRefPairs { + values, + names, + na_value: T::default(), + na_name: Character::NA, + iter, + } + } + } + } + + pub fn iter_pairs(&self) -> IterablePairs { + match self.clone() { + RepType::Subset(values, _, maybe_naming) => { + let iter = Box::new(self.iter_subset_indices()); + let values = values.inner_rc(); + let names = maybe_naming.map(|x| x.names.inner_rc()); + + IterablePairs { values, names, iter } + } + } + } + + pub fn iter_values(&self) -> IterableValues { + match self.clone() { + RepType::Subset(values, ..) => { + let iter = Box::new(self.iter_subset_indices()); + IterableValues { values: values.inner_rc(), iter } + } + } + } + + pub fn iter_names(&self) -> Option> { + match self.clone() { + RepType::Subset(.., maybe_naming) => { + let iter = Box::new(self.iter_subset_indices()); + let names = maybe_naming.map(|x| x.names.inner_rc())?; + + Some(IterableValues { values: names, iter }) + } + } + } + + pub fn push_value(&self, value: T) { + self.push_named(Character::NA, value); + } + + pub fn push_named(&self, name: OptionNA, value: T) { + match self { + RepType::Subset(values, Subsets(subsets), maybe_naming) => match subsets.as_slice() { + [] => { + values.with_inner_mut(|values| values.push(value)); + if let Some(naming) = maybe_naming { + naming.push_name(name) + } + } + _ => unimplemented!(), + }, + } + } + + pub fn iter_subset_indices(&self) -> Box>> { + match self.clone() { + RepType::Subset(vals, subsets, maybe_naming) => { + if subsets.is_empty() { + return Box::new((0_usize..vals.len()).map(Some)); + } + + if let Some(naming) = maybe_naming { + Box::new(subsets.bind_names(naming.map).into_iter().map(|(_, y)| y)) + } else { + Box::new(subsets.into_iter().map(|(_, y)| y)) + } + } + } + } + + /// Reindex the mapping from names to indices. + pub fn reindex(&mut self) { + if let RepType::Subset(.., Some(naming)) = self { + naming.map.with_inner_mut(|map| { + map.drain(); + + for (i, maybe_name) in naming.names.borrow().iter().enumerate() { + if let OptionNA::Some(name) = maybe_name { + let indices = map.entry(name.clone()).or_default(); + if !indices.contains(&i) { + indices.push(i) + } + } + } + }) + } + } + + pub fn dedup_last(self) -> Self { + match self { + RepType::Subset(values, subsets, Some(naming)) => { + naming.with_inner_mut(|map, names| { + let mut dups: Vec = map + .iter() + .flat_map(|(_, indices)| { + indices + .split_last() + .map_or(vec![], |(_, leading_dups)| leading_dups.to_vec()) + }) + .collect(); + + dups.sort(); + + values.with_inner_mut(|vs| { + for i in dups.into_iter().rev() { + vs.remove(i); + names.remove(i); + } + }); + + for (_, indices) in map.iter_mut() { + indices.drain(0..(indices.len())); + } + }); + RepType::Subset(values, subsets, Some(naming)) + } + RepType::Subset(.., None) => self, + } + } + + pub fn set_names(&self, names: CowObj>) -> Self { + match self { + RepType::Subset(v, s, _) => { + RepType::Subset(v.clone(), s.clone(), Option::Some(names.into())) + } + } } /// Access a lazy copy of the internal vector data pub fn inner(&self) -> CowObj> { match self.materialize() { - RepType::Subset(v, _) => v.clone(), + RepType::Subset(v, ..) => v.clone(), + } + } + + /// Get mutable access to the internal vector through the passed closure. + pub fn with_inner_mut(&self, f: F) -> R + where + F: FnOnce(&mut Vec) -> R, + { + match self { + RepType::Subset(v, ..) => v.with_inner_mut(f), } } /// Subsetting a Vector /// /// Introduce a new subset into the aggregate list of subset indices. - /// pub fn subset(&self, subset: Subset) -> Self { match self { - RepType::Subset(v, Subsets(subsets)) => { + RepType::Subset(v, Subsets(subsets), n) => { let mut subsets = subsets.clone(); subsets.push(subset); - RepType::Subset(v.view_mut(), Subsets(subsets)) + RepType::Subset(v.view_mut(), Subsets(subsets), n.clone()) } } } pub fn len(&self) -> usize { match self { - RepType::Subset(v, Subsets(s)) => match s.as_slice() { + RepType::Subset(v, Subsets(s), _) => match s.as_slice() { [] => v.borrow().len(), _ => unimplemented!(), }, } } - #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 @@ -142,21 +573,15 @@ impl RepType { T: Clone, { match self { - RepType::Subset(v, subsets) => { + RepType::Subset(v, subsets, _) => { let vb = v.borrow(); let index = subsets.get_index_at(index)?; let elem = vb.get(index)?; - Some(RepType::Subset(vec![elem.clone()].into(), Subsets::new())) - } - } - } - - pub fn get_atom(&self, index: usize) -> T { - match self { - RepType::Subset(v, subsets) => { - let vb = v.borrow(); - let index = subsets.get_index_at(index).unwrap(); - vb[index].clone() + Some(RepType::Subset( + vec![elem.clone()].into(), + Subsets::new(), + Option::Some(Naming::new()), + )) } } } @@ -166,30 +591,30 @@ impl RepType { /// Assignment to a vector from another. The aggregate subsetted indices /// are iterated over while performing the assignment. /// - pub fn assign(&mut self, value: Self) -> Self + pub fn assign(&mut self, value: RepType) -> Self where - T: Clone + Default, + T: Clone + Default + From, + R: Default + Clone, { + // TODO(feature): here we should also throw an error if the recycling rules are violated. + let l_indices = self.iter_subset_indices(); + let r_indices = value.iter_subset_indices(); match (self, value) { - (RepType::Subset(lv, ls), RepType::Subset(rv, rs)) => { + (RepType::Subset(lv, ls, ln), RepType::Subset(rv, ..)) => { lv.with_inner_mut(|lvb| { let rvc = rv.clone(); let rvb = rvc.borrow(); - let lv_len = lvb.len(); - let l_indices = ls.clone().into_iter().take_while(|(i, _)| i < &lv_len); - let r_indices = rs.clone().into_iter().take_while(|(i, _)| i < &lv_len); - - for ((_, li), (_, ri)) in l_indices.zip(r_indices) { + for (li, ri) in l_indices.zip(r_indices) { match (li, ri) { (Some(li), None) => lvb[li] = T::default(), - (Some(li), Some(ri)) => lvb[li] = rvb[ri % rvb.len()].clone(), + (Some(li), Some(ri)) => lvb[li] = rvb[ri % rvb.len()].clone().into(), _ => (), } } }); - RepType::Subset(lv.clone(), ls.clone()) + RepType::Subset(lv.clone(), ls.clone(), ln.clone()) } } } @@ -197,44 +622,77 @@ impl RepType { /// Materialize a Vector /// /// Apply subsets and clone values into a new vector. - /// pub fn materialize(&self) -> Self where T: Clone, { match self { - RepType::Subset(v, subsets) => { + RepType::Subset(v, subsets, naming) => { + // early exit when there is nothing to do + match subsets { + Subsets(s) => { + if s.as_slice().is_empty() { + return self.clone(); + } + } + } + let vc = v.clone(); let vb = vc.borrow(); let mut res: Vec = vec![]; let vb_len = vb.len(); + let new_naming = Naming::new(); + let iter = subsets.clone().into_iter().take_while(|(i, _)| i < &vb_len); + for (_, i) in iter { match i { - Some(i) => res.push(vb[i].clone()), - None => res.push(T::default()), + Some(i) => { + res.push(vb[i].clone()); + if let Option::Some(n) = naming { + new_naming.push_name(n.names.borrow()[i].clone()) + }; + } + // default is NA + None => { + res.push(T::default()); + // When we subset with NA, there is no name for this entry; + new_naming.push_name(OptionNA::NA); + } } } - RepType::Subset(res.into(), Subsets(vec![])) + RepType::Subset(res.into(), Subsets(vec![]), Option::None) } } } - pub fn is_double(&self) -> bool { + pub fn is_double(&self) -> bool + where + T: AtomicMode, + { T::is_double() } - pub fn is_logical(&self) -> bool { + pub fn is_logical(&self) -> bool + where + T: AtomicMode, + { T::is_logical() } - pub fn is_integer(&self) -> bool { + pub fn is_integer(&self) -> bool + where + T: AtomicMode, + { T::is_integer() } - pub fn is_character(&self) -> bool { + pub fn is_character(&self) -> bool + where + T: AtomicMode, + { T::is_character() } @@ -244,13 +702,13 @@ impl RepType { Mode: Clone, { match self { - RepType::Subset(v, subsets) => { + RepType::Subset(v, subsets, naming) => { let vc = v.clone(); let vb = vc.borrow(); let num_vec: Vec = vb.iter().map(|i| (*i).clone().coerce_into()).collect(); - RepType::Subset(num_vec.into(), subsets.clone()) + RepType::Subset(num_vec.into(), subsets.clone(), naming.clone()) } } } @@ -309,7 +767,11 @@ impl RepType { pub fn get_inner(&self, index: usize) -> Option { match self { - RepType::Subset(v, subsets) => { + RepType::Subset(v, subsets, maybe_naming) => { + if maybe_naming.is_some() { + // TODO(NOW) + unimplemented!() + } let vb = v.borrow(); let index = subsets.get_index_at(index)?; vb.get(index).cloned() @@ -334,59 +796,88 @@ where } } +impl From> for Naming { + fn from(value: Vec) -> Self { + let naming = Naming::new(); + for k in value { + naming.push_name(k); + } + naming + } +} + +impl From>> for RepType { + fn from(value: CowObj>) -> Self { + RepType::Subset(value, Subsets::default(), Option::None) + } +} + +impl From, T)>> for RepType { + fn from(value: Vec<(Option, T)>) -> Self { + let mut names = Vec::with_capacity(value.len()); + let mut values = Vec::with_capacity(value.len()); + for (k, v) in value.into_iter() { + names.push(k.map_or(Character::NA, Character::Some)); + values.push(v) + } + let naming = Naming::from(names); + RepType::Subset(values.into(), Subsets::default(), Some(naming)) + } +} + impl From>> for RepType { fn from(value: Vec>) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } impl From> for RepType { fn from(value: Vec) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } impl From>> for RepType { fn from(value: Vec>) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } impl From> for RepType { fn from(value: Vec) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } impl From>> for RepType { fn from(value: Vec>) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } impl From> for RepType { fn from(value: Vec) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } impl From>> for RepType { fn from(value: Vec>) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } impl From> for RepType { fn from(value: Vec) -> Self { let value: Vec<_> = value.into_iter().map(|i| i.coerce_into()).collect(); - RepType::Subset(value.into(), Subsets(Vec::new())) + RepType::Subset(value.into(), Subsets(Vec::new()), Option::None) } } @@ -397,7 +888,7 @@ where { fn from(value: (Vec, Subsets)) -> Self { match Self::from(value.0) { - RepType::Subset(v, _) => RepType::Subset(v, value.1), + RepType::Subset(v, ..) => RepType::Subset(v, value.1, Option::None), } } } @@ -742,6 +1233,7 @@ mod test { use super::OptionNA::*; use crate::object::reptype::RepType; use crate::object::{types::*, OptionNA, VecPartialCmp}; + use crate::r; use crate::utils::SameType; #[test] @@ -834,15 +1326,200 @@ mod test { } #[test] - fn iter() { - let x = RepType::from(vec![Some(1), Some(2)]); - let mut xi = x.into_iter(); - assert_eq!(xi.next(), Option::Some(OptionNA::Some(1))); - assert_eq!(xi.next(), Option::Some(OptionNA::Some(2))); - assert_eq!(xi.next(), Option::None); - let xs = RepType::from(vec![Some("a".to_string())]); - let mut xsi = xs.into_iter(); - assert_eq!(xsi.next(), Option::Some(OptionNA::Some("a".to_string()))); - assert_eq!(xsi.next(), Option::None); + fn test_iter_values() { + // Create values as Vec + let values = vec![1, 2, 3, 4, 5]; + + // Create RepType from values + let rep = RepType::from(values.clone()); + + // Use iter_values to get an iterator and collect values + let collected_values: Vec = rep.iter_values().collect(); + + // Expected values as Vec> + let expected_values: Vec = values.into_iter().map(OptionNA::Some).collect(); + + // Assert collected values match expected values + assert_eq!(collected_values, expected_values); + } + + #[test] + fn test_iter_names() { + // Create values with names + let values_with_names = vec![ + (Character::Some(String::from("a")), 1), + (Character::Some(String::from("b")), 2), + (Character::NA, 3), + (Character::Some(String::from("d")), 4), + (Character::NA, 5), + ]; + + // Create RepType from values with names + let rep = RepType::from(values_with_names.clone()); + + // Use iter_names to get an iterator + let names_iter = rep.iter_names(); + + // Ensure iter_names is Some iterator + assert!(names_iter.is_some()); + + // Collect names + let collected_names: Vec = names_iter.unwrap().collect(); + + // Expected names + let expected_names: Vec = values_with_names + .iter() + .map(|(name_opt, _)| match name_opt { + Some(name) => Character::Some(name.clone()), + Character::NA => Character::NA, + }) + .collect(); + + // Assert collected names match expected names + assert_eq!(collected_names, expected_names); + } + + use crate::object::{Obj, Vector}; + // The tests below don't test the subsetting mechanism, which is instead tested in subsets.rs + #[test] + fn iter_pairs_mixed_names() { + let x = r!(c(a = 1, 2)).unwrap(); + + let mut x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().iter_pairs() + } else { + unreachable!() + }; + + assert_eq!( + x.next().unwrap(), + (Character::Some("a".to_string()), Double::Some(1.0)) + ); + assert_eq!(x.next().unwrap(), (Character::NA, Double::Some(2.0))); + assert_eq!(x.next(), None); + } + + #[test] + fn iter_pairs_no_names() { + let x = r!(c(1, 2)).unwrap(); + + let mut x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().iter_pairs() + } else { + unreachable!() + }; + + assert_eq!(x.next().unwrap(), (Character::NA, Double::Some(1.0))); + assert_eq!(x.next().unwrap(), (Character::NA, Double::Some(2.0))); + assert_eq!(x.next(), None); + } + + #[test] + fn iter_values() { + let x = r!(c(1, 2)).unwrap(); + + let mut x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().iter_values() + } else { + unreachable!() + }; + + assert_eq!(x.next().unwrap(), Double::Some(1.0)); + assert_eq!(x.next().unwrap(), Double::Some(2.0)); + assert_eq!(x.next(), None); + } + + #[test] + fn iter_names_none() { + let x = r!(c(1, 2)).unwrap(); + + let x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().iter_names() + } else { + unreachable!() + }; + + assert!(x.is_none()) + } + + #[test] + fn iter_names_some() { + let x = r!(c(1, b = 2)).unwrap(); + + let mut x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().iter_names().unwrap() + } else { + unreachable!() + }; + + assert_eq!(x.next().unwrap(), Character::NA); + assert_eq!(x.next().unwrap(), Character::Some("b".to_string())); + assert_eq!(x.next(), None); + } + + #[test] + fn names_ref_iter_some() { + let x = r!(c(1, b = 2)).unwrap(); + + let mut x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().names_ref().unwrap() + } else { + unreachable!() + }; + + let mut x = x.iter(); + + assert_eq!(x.next().unwrap(), &Character::NA); + assert_eq!(x.next().unwrap(), &Character::Some("b".to_string())); + assert_eq!(x.next(), None); + } + + #[test] + #[should_panic] + fn names_ref_iter_none() { + let x = r!(c(1, 2)).unwrap(); + + if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().names_ref().unwrap() + } else { + unreachable!() + }; + } + + #[test] + fn values_ref_iter() { + let x = r!(c(1, b = 2)).unwrap(); + + let mut x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().values_ref() + } else { + unreachable!() + }; + + let mut x = x.iter(); + + assert_eq!(x.next().unwrap(), &Double::Some(1.0)); + assert_eq!(x.next().unwrap(), &Double::Some(2.0)); + assert_eq!(x.next(), None); + } + + #[test] + fn pairs_ref_iter() { + let x = r!(c(1, b = 2)).unwrap(); + + let mut x = if let Obj::Vector(Vector::Double(r)) = x { + r.borrow().clone().pairs_ref() + } else { + unreachable!() + }; + + let mut x = x.iter(); + + assert_eq!(x.next().unwrap(), (&Character::NA, &Double::Some(1.0))); + assert_eq!( + x.next().unwrap(), + (&Character::Some("b".to_string()), &Double::Some(2.0)) + ); + assert_eq!(x.next(), None); } } diff --git a/src/object/vector/subset.rs b/src/object/vector/subset.rs index 333842e5..243ba739 100644 --- a/src/object/vector/subset.rs +++ b/src/object/vector/subset.rs @@ -2,10 +2,10 @@ use std::cell::RefCell; use std::ops::Range; use std::rc::Rc; -use crate::lang::Signal; -use crate::object::CowObj; - use super::{types::*, OptionNA, Vector}; +use crate::error::Error; +use crate::lang::Signal; +use crate::object::{CowObj, Obj}; /// Subsets /// @@ -187,6 +187,17 @@ impl Subset { } } +impl TryFrom for Subset { + type Error = Signal; + fn try_from(value: Obj) -> Result { + let err = Error::Other("Cannot use object for indexing".to_string()); + match value { + Obj::Vector(v) => Subset::try_from(v), + _ => err.into(), + } + } +} + impl From for Subset { fn from(value: usize) -> Self { Subset::Indices(vec![OptionNA::Some(value as i32)].into()) @@ -217,7 +228,7 @@ impl TryFrom for Subset { value @ Vector::Double(_) => Subset::try_from(value.as_integer()), Vector::Integer(v) => { let y = v - .into_iter() + .iter_values() .map(|i| match i { OptionNA::Some(x) => OptionNA::Some(x - 1), OptionNA::NA => OptionNA::NA, diff --git a/src/object/vector/subsets.rs b/src/object/vector/subsets.rs index ab81e404..f6983464 100644 --- a/src/object/vector/subsets.rs +++ b/src/object/vector/subsets.rs @@ -32,12 +32,20 @@ impl Subsets { Some(index) } - pub fn push(self, subset: T) + pub fn len(&self) -> usize { + self.0.len() + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn push(&mut self, subset: T) where T: Into, { - let Subsets(mut v) = self; - v.push(subset.into()); + self.0.push(subset.into()); } pub fn bind_names(self, names: CowObj>>) -> NamedSubsets { diff --git a/src/object/vector/types.rs b/src/object/vector/types.rs index fec490c9..c8cd9a33 100644 --- a/src/object/vector/types.rs +++ b/src/object/vector/types.rs @@ -1,5 +1,8 @@ use super::coercion::AtomicMode; use super::OptionNA; +use crate::error::Error; +use crate::object::Obj; +use crate::object::Vector; pub type Double = OptionNA; impl AtomicMode for Double { @@ -28,3 +31,144 @@ impl AtomicMode for Character { true } } + +impl From> for OptionNA { + fn from(value: Option) -> Self { + match value { + None => Self::NA, + Some(x) => Self::Some(x), + } + } +} + +impl From> for OptionNA { + fn from(value: Option) -> Self { + match value { + None => Self::NA, + Some(x) => Self::Some(x), + } + } +} + +impl From> for OptionNA { + fn from(value: Option) -> Self { + match value { + None => Self::NA, + Some(x) => Self::Some(x), + } + } +} + +impl From> for OptionNA { + fn from(value: Option) -> Self { + match value { + None => Self::NA, + Some(x) => Self::Some(x), + } + } +} + +impl OptionNA { + pub fn is_na(&self) -> bool { + matches!(self, OptionNA::NA) + } +} + +impl TryFrom for Double { + type Error = Error; + fn try_from(value: Obj) -> Result { + let err = Err(Error::Other( + "Cannot convert object to scalar double.".to_string(), + )); + if let Obj::Vector(Vector::Double(v)) = value { + if v.len() == 1 { + Ok(v.iter_values().next().expect("length is one")) + } else { + err + } + } else { + err + } + } +} + +impl TryFrom for Integer { + type Error = Error; + fn try_from(value: Obj) -> Result { + let err = Err(Error::Other( + "Cannot convert object to scalar integer.".to_string(), + )); + if let Obj::Vector(Vector::Integer(v)) = value { + if v.len() == 1 { + Ok(v.iter_values().next().expect("length is one")) + } else { + err + } + } else { + err + } + } +} + +impl TryFrom for Character { + type Error = Error; + fn try_from(value: Obj) -> Result { + let err = Err(Error::Other( + "Cannot convert object to scalar character.".to_string(), + )); + if let Obj::Vector(Vector::Character(v)) = value { + if v.len() == 1 { + Ok(v.iter_values().next().expect("length is one")) + } else { + err + } + } else { + err + } + } +} + +impl TryFrom for Logical { + type Error = Error; + fn try_from(value: Obj) -> Result { + let err = Err(Error::Other( + "Cannot convert object to scalar logical.".to_string(), + )); + if let Obj::Vector(Vector::Logical(v)) = value { + if v.len() == 1 { + Ok(v.iter_values().next().expect("length is one")) + } else { + err + } + } else { + err + } + } +} + +// TODO: Because From> for Obj is implement this means that +// when converting Character -> Obj we get a character vector. +// Change this once we have scalars. +impl From for Vector { + fn from(value: Character) -> Self { + Vector::Character(vec![value].into()) + } +} + +impl From for Vector { + fn from(value: Logical) -> Self { + Vector::Logical(vec![value].into()) + } +} + +impl From for Vector { + fn from(value: Double) -> Self { + Vector::Double(vec![value].into()) + } +} + +impl From for Vector { + fn from(value: Integer) -> Self { + Vector::Integer(vec![value].into()) + } +}