Skip to content

Commit

Permalink
Merge pull request #48 from lumen/tuple_to_list_1
Browse files Browse the repository at this point in the history
tuple_to_list/1
  • Loading branch information
bitwalker committed May 21, 2019
2 parents 6b56030 + 8d21af1 commit 4ba30b1
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 0 deletions.
18 changes: 18 additions & 0 deletions lumen_runtime/src/otp/erlang.rs
Expand Up @@ -713,6 +713,24 @@ pub fn tuple_size_1(tuple: Term, mut process: &mut Process) -> Result {
}
}

pub fn tuple_to_list_1(tuple: Term, mut process: &mut Process) -> Result {
match tuple.tag() {
Boxed => {
let unboxed: &Term = tuple.unbox_reference();

match unboxed.tag() {
Arity => {
let tuple: &Tuple = tuple.unbox_reference();

Ok(tuple.to_list(&mut process))
}
_ => Err(bad_argument!(&mut process)),
}
}
_ => Err(bad_argument!(&mut process)),
}
}

// Private Functions

fn binary_existence_to_atom(
Expand Down
1 change: 1 addition & 0 deletions lumen_runtime/src/otp/erlang/tests.rs
Expand Up @@ -49,6 +49,7 @@ mod self_0;
mod size_1;
mod tl_1;
mod tuple_size_1;
mod tuple_to_list_1;

fn list_term(mut process: &mut Process) -> Term {
let head_term = Term::str_to_atom("head", DoNotCare, &mut process).unwrap();
Expand Down
197 changes: 197 additions & 0 deletions lumen_runtime/src/otp/erlang/tests/tuple_to_list_1.rs
@@ -0,0 +1,197 @@
use super::*;

use num_traits::Num;

use std::sync::{Arc, RwLock};

use crate::environment::{self, Environment};

#[test]
fn with_atom_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let tuple = Term::str_to_atom("atom", DoNotCare, &mut process).unwrap();

assert_bad_argument!(erlang::tuple_to_list_1(tuple, &mut process), &mut process);
}

#[test]
fn with_local_reference_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let tuple = Term::local_reference(&mut process);

assert_bad_argument!(erlang::tuple_to_list_1(tuple, &mut process), &mut process);
}

#[test]
fn with_empty_list_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let tuple = Term::EMPTY_LIST;

assert_bad_argument!(erlang::tuple_to_list_1(tuple, &mut process), &mut process);
}

#[test]
fn with_list_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let tuple = Term::cons(1.into_process(&mut process), Term::EMPTY_LIST, &mut process);

assert_bad_argument!(erlang::tuple_to_list_1(tuple, &mut process), &mut process);
}

#[test]
fn with_small_integer_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let small_integer_term = 0.into_process(&mut process);

assert_bad_argument!(
erlang::tuple_to_list_1(small_integer_term, &mut process),
&mut process
);
}

#[test]
fn with_big_integer_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let big_integer_term = <BigInt as Num>::from_str_radix("576460752303423489", 10)
.unwrap()
.into_process(&mut process);

assert_bad_argument!(
erlang::tuple_to_list_1(big_integer_term, &mut process),
&mut process
);
}

#[test]
fn with_float_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let float_term = 1.0.into_process(&mut process);

assert_bad_argument!(
erlang::tuple_to_list_1(float_term, &mut process),
&mut process
);
}

#[test]
fn with_local_pid_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let local_pid_term = Term::local_pid(0, 0, &mut process).unwrap();

assert_bad_argument!(
erlang::tuple_to_list_1(local_pid_term, &mut process),
&mut process
);
}

#[test]
fn with_external_pid_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let external_pid_term = Term::external_pid(1, 0, 0, &mut process).unwrap();

assert_bad_argument!(
erlang::tuple_to_list_1(external_pid_term, &mut process),
&mut process
);
}

#[test]
fn with_empty_tuple_is_empty_list() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let tuple_term = Term::slice_to_tuple(&[], &mut process);

assert_eq_in_process!(
erlang::tuple_to_list_1(tuple_term, &mut process),
Ok(Term::EMPTY_LIST),
&mut process
);
}

#[test]
fn with_nonempty_tuple_is_list() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let first_element = 1.into_process(&mut process);
let second_element = 2.into_process(&mut process);
let third_element = 3.into_process(&mut process);
let tuple_term = Term::slice_to_tuple(
&[first_element, second_element, third_element],
&mut process,
);

assert_eq_in_process!(
erlang::tuple_to_list_1(tuple_term, &mut process),
Ok(Term::cons(
first_element,
Term::cons(
second_element,
Term::cons(third_element, Term::EMPTY_LIST, &mut process),
&mut process,
),
&mut process,
)),
&mut process
);
}

#[test]
fn with_map_errors_badarg() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let map_term = Term::slice_to_map(&[], &mut process);

assert_bad_argument!(
erlang::tuple_to_list_1(map_term, &mut process),
&mut process
);
}

#[test]
fn with_heap_binary_is_false() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let heap_binary_term = Term::slice_to_binary(&[], &mut process);

assert_bad_argument!(
erlang::tuple_to_list_1(heap_binary_term, &mut process),
&mut process
);
}

#[test]
fn with_subbinary_is_false() {
let environment_rw_lock: Arc<RwLock<Environment>> = Default::default();
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let binary_term =
Term::slice_to_binary(&[0b0000_00001, 0b1111_1110, 0b1010_1011], &mut process);
let subbinary_term = Term::subbinary(binary_term, 0, 7, 2, 1, &mut process);

assert_bad_argument!(
erlang::tuple_to_list_1(subbinary_term, &mut process),
&mut process
);
}
21 changes: 21 additions & 0 deletions lumen_runtime/src/tuple.rs
Expand Up @@ -192,6 +192,12 @@ impl Tuple {
// while `arity` is tagged as an `arity` to mark the beginning of a tuple.
self.arity.arity_to_usize().into()
}

pub fn to_list(&self, mut process: &mut Process) -> Term {
self.iter().rfold(Term::EMPTY_LIST, |acc, element| {
Term::cons(element, acc, &mut process)
})
}
}

impl DebugInProcess for Tuple {
Expand Down Expand Up @@ -243,6 +249,21 @@ pub struct Iter {
limit: *const Term,
}

impl DoubleEndedIterator for Iter {
fn next_back(&mut self) -> Option<Term> {
if self.pointer == self.limit {
None
} else {
unsafe {
// limit is +1 past he actual elements, so pre-decrement unlike `next`, which
// post-decrements
self.limit = self.limit.offset(-1);
self.limit.as_ref().map(|r| *r)
}
}
}
}

impl Iterator for Iter {
type Item = Term;

Expand Down

0 comments on commit 4ba30b1

Please sign in to comment.