Skip to content

Commit

Permalink
adding changelog, fixing broken tests (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgkf authored Aug 26, 2023
1 parent 0e18666 commit 63a50ac
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 94 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "r"
version = "0.1.0"
version = "0.1.1"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
4 changes: 2 additions & 2 deletions examples/numerics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use r::r_vector::vectors::*;
use r::vector::vectors::*;

fn main() {
let x = Vector::from(vec![false, true, false]);
Expand All @@ -8,7 +8,7 @@ fn main() {
println!("{} + {}", x, y);
println!("{}\n", x + y);

use r::r_vector::vectors::OptionNA::*; // Some() and NA
use r::vector::vectors::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]);

Expand Down
54 changes: 54 additions & 0 deletions src/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 0.2.0 "In Bloom"

## Major Changes

* Primitives are now more consistently handled, allowing them to be reassigned
like any other function object. Prior to these enhancements, primitives
would only be used for calls to specificly named symbols.

```r
f <- paste
f("a", c("b", "c"))
# [1] "a b" "a c"
```

* A call stack with proper call frames is now added, paving the way for
improved error messages and the implementation of R's metaprogramming tools.
You can view the call stack by introducing a `callstack()` call:
```r
f <- function(...) list(..., callstack())
f("Hello, World!")
# [[1]]
# [1] "Hello, World!"
#
# [[2]]
# [[2]][[1]]
# f("Hello, World!")
#
# [[2]][[2]]
# list(..., callstack())
#
# [[2]][[3]]
# callstack()
```
* Even more primitives now implemented! This release brings `paste()` and
`callstack()` (akin to R's `sys.calls()`)

## Behind the Scenes

* Primitives are now _all_ implemented as `dyn Primitive` objects, implementing
a `Callable` trait. They still don't have a proper standard library namespace,
and are discovered only if not found in the environment (or its parents),
but this paves the way for treating primitives more like user-defined
functions.
## Thanks to our new contributors!
@armenic (#16)
# 0.1.0 "Why Not?"
Initial release.
141 changes: 54 additions & 87 deletions src/callable/primitive/paste.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ impl PrimitiveSYM for PrimitivePaste {

impl Callable for PrimitivePaste {
fn call(&self, args: ExprList, stack: &mut CallStack) -> EvalResult {
let R::List(mut vals) = stack.parent_frame().eval_list_greedy(args)? else {
let R::List(vals) = stack.parent_frame().eval_list_lazy(args)? else {
unreachable!()
};

let mut vals = force_closures(vals, stack);

let mut sep = String::from(" ");
let mut should_collapse = false;
let mut collapse = String::new();
Expand Down Expand Up @@ -101,142 +103,107 @@ impl Callable for PrimitivePaste {
#[cfg(test)]
mod test_primitive_paste {
use super::*;
use crate::parser::parse_args;
use crate::parser::parse;

#[test]
fn test_primitive_paste_01() {
let mut env = Environment::default();
let args = parse_args("paste(1, 2, collapse = NULL)").unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let mut stack = CallStack::new();
let expr = parse("paste(1, 2, collapse = NULL)").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["1 2"];

assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_02() {
let mut env = Environment::default();
let args = parse_args("paste(null)").unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let mut stack = CallStack::new();
let expr = parse("paste(null)").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<&str> = vec![];

assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_03() {
let mut env = Environment::default();
let args = parse_args("paste(1.1, null, 2, false, 'a', c(1.0, 2.0), sep = '+')").unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["1.1++2+false+a+1", "1.1++2+false+a+2"]
.iter()
.map(|s| s.to_string())
.collect();

let mut stack = CallStack::new();
let expr = parse("paste(1.1, null, 2, false, 'a', c(1.0, 2.0), sep = '+')").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["1.1++2+false+a+1".to_string(), "1.1++2+false+a+2".to_string()];
assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_04() {
let mut env = Environment::default();
let args = parse_args("paste(1.1, null, 2, false, 'a', c(1.0, 2.0))").unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["1.1 2 false a 1", "1.1 2 false a 2"]
.iter()
.map(|s| s.to_string())
.collect();

let mut stack = CallStack::new();
let expr = parse("paste(1.1, null, 2, false, 'a', c(1.0, 2.0))").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["1.1 2 false a 1".to_string(), "1.1 2 false a 2".to_string()];
assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_05() {
let mut env = Environment::default();
let args =
parse_args("paste(c(1, 2, 3, 4, 5), c('st', 'nd', 'rd', c('th', 'th')), sep = '')")
.unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["1st", "2nd", "3rd", "4th", "5th"]
.iter()
.map(|s| s.to_string())
.collect();

let mut stack = CallStack::new();
let expr = parse("paste(c(1, 2, 3, 4, 5), c('st', 'nd', 'rd', c('th', 'th')), sep = '')").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["1st".to_string(), "2nd".to_string(), "3rd".to_string(), "4th".to_string(), "5th".to_string()];
assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_06() {
let mut env = Environment::default();
let args =
parse_args("paste(1.1, null, 2, false, 'a', c(1.0, 2.0), , collapse = '+')").unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["1.1 2 false a 1+1.1 2 false a 2"]
.iter()
.map(|s| s.to_string())
.collect();

let mut stack = CallStack::new();
let expr = parse("paste(1.1, null, 2, false, 'a', c(1.0, 2.0), , collapse = '+')").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["1.1 2 false a 1+1.1 2 false a 2".to_string()];
assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_07() {
let mut env = Environment::default();
let args = parse_args("paste(1, 2, 3, collapse = '+')").unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["1 2 3"].iter().map(|s| s.to_string()).collect();

let mut stack = CallStack::new();
let expr = parse("paste(1, 2, 3, collapse = '+')").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["1 2 3".to_string()];
assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_08() {
let mut env = Environment::default();
let args = parse_args("paste(c(1, 2), 3, 4, 5, sep = '-', collapse = '+')").unwrap();
let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["1-3-4-5+2-3-4-5"]
.iter()
.map(|s| s.to_string())
.collect();

let mut stack = CallStack::new();
let expr = parse("paste(c(1, 2), 3, 4, 5, sep = '-', collapse = '+')").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["1-3-4-5+2-3-4-5".to_string()];
assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_09() {
let mut env = Environment::default();
// Passing x vector to the environment so that paste can use it
env.insert(
"x".to_string(),
R::Vector(Vector::Character(vec![OptionNA::Some(
"<collapse>".to_string(),
)])),
);
let args = parse_args("paste(c('a', 'b'), collapse = x)").unwrap();

let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["a<collapse>b"].iter().map(|s| s.to_string()).collect();

let mut stack = CallStack::new();
let x_val = stack.eval(parse("\"<collapse>\"").unwrap()).unwrap();
stack.last_frame().env.insert("x".to_string(), x_val);
let expr = parse("paste(c('a', 'b'), collapse = x)").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["a<collapse>b".to_string()];
assert_eq!(observed, expected);
}

#[test]
fn test_primitive_paste_10() {
let mut env = Environment::default();
let args = parse_args("paste('a', c('b', 'c'), collapse = '')").unwrap();

let R::Vector(observed) = PrimitivePaste.call(args, &mut env).unwrap() else {unimplemented!()};
let observed: Vec<_> = observed.into();
let expected: Vec<_> = vec!["a ba c"].iter().map(|s| s.to_string()).collect();

let mut stack = CallStack::new();
let expr = parse("paste('a', c('b', 'c'), collapse = '')").unwrap();
let R::Vector(res) = stack.eval(expr).unwrap() else { unimplemented!() };
let observed: Vec<_> = res.into();
let expected: Vec<_> = vec!["a ba c".to_string()];
assert_eq!(observed, expected);
}
}
13 changes: 12 additions & 1 deletion src/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,10 @@ impl CallStack {
error => error
}
}

pub fn new() -> CallStack {
CallStack::from(Frame { call: Expr::Missing, env: Rc::new(Environment::default())})
}
}

impl Display for CallStack {
Expand Down Expand Up @@ -596,7 +600,7 @@ pub trait Context {
}
}
})
.collect(),
.collect()
))
}
}
Expand Down Expand Up @@ -651,6 +655,13 @@ impl Context for CallStack {
self.last_frame().eval(expr)
}
}

// NOTE:
// eval_list_lazy and force_closures are often used together to greedily
// evaluated arguments. This pattern can be specialized in the case of a
// CallStack to cut out the creation of intermediate closures. Need to
// lift EvalResult over Context::eval_list_lazy's flat_map by implementing
// Try.
}

impl Context for &Frame {
Expand Down
2 changes: 1 addition & 1 deletion src/repl/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ where
P: AsRef<Path>,
{
// print session header
println!("R version 0.0.1 -- \"Why Not?\"");
println!("R version 0.2.0 -- \"In Bloom\"");

let line_editor = Reedline::create()
.with_validator(Box::new(RValidator))
Expand Down
2 changes: 1 addition & 1 deletion src/vector/iterators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::vector::coercion::*;
/// recycled even if they do not repeat an even number of times.
///
/// ```rust
/// use r::r_vector::iterators::zip_recycle;
/// use r::vector::iterators::zip_recycle;
///
/// let x = vec![1, 2, 3, 4];
/// let y = vec![2, 4];
Expand Down

0 comments on commit 63a50ac

Please sign in to comment.