Skip to content

Commit

Permalink
Merge pull request #51 from lumen/subtract_list_2
Browse files Browse the repository at this point in the history
--/2
  • Loading branch information
bitwalker committed May 21, 2019
2 parents 651ee1b + 8a53f60 commit fca3c4f
Show file tree
Hide file tree
Showing 10 changed files with 755 additions and 31 deletions.
2 changes: 2 additions & 0 deletions lumen_runtime/src/lib.rs
Expand Up @@ -12,6 +12,8 @@
#![feature(try_reserve)]
// for `lumen_runtime::term::Term`
#![feature(untagged_unions)]
// for `lumen_runtime::list::Cons::subtract`.
#![feature(vec_remove_item)]

#[macro_use]
extern crate cfg_if;
Expand Down
118 changes: 89 additions & 29 deletions lumen_runtime/src/list.rs
Expand Up @@ -2,6 +2,7 @@

use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::iter::FusedIterator;

use crate::exception::{self, Exception};
use crate::process::{DebugInProcess, IntoProcess, OrderInProcess, Process, TryIntoInProcess};
Expand All @@ -28,13 +29,30 @@ impl Cons {
}

pub fn concatenate(&self, term: Term, mut process: &mut Process) -> exception::Result {
let vec: Vec<Term> = self.to_vec(&mut process)?;
match self.into_iter().collect::<Result<Vec<Term>, _>>() {
Ok(vec) => Ok(Term::vec_to_list(&vec, term, &mut process)),
Err(ImproperList { .. }) => Err(bad_argument!(&mut process)),
}
}

pub fn is_proper(&self) -> bool {
self.into_iter().all(|item| item.is_ok())
}

let final_list = vec.iter().rfold(term, |acc, element| {
Term::cons(element.clone(), acc, &mut process)
});
pub fn subtract(&self, subtrahend: &Cons, mut process: &mut Process) -> exception::Result {
match self.into_iter().collect::<Result<Vec<Term>, _>>() {
Ok(mut self_vec) => {
for result in subtrahend.into_iter() {
match result {
Ok(subtrahend_element) => self_vec.remove_item(&subtrahend_element),
Err(ImproperList { .. }) => return Err(bad_argument!(&mut process)),
};
}

Ok(final_list)
Ok(Term::vec_to_list(&self_vec, Term::EMPTY_LIST, &mut process))
}
Err(ImproperList { .. }) => Err(bad_argument!(&mut process)),
}
}

pub fn to_pid(&self, mut process: &mut Process) -> exception::Result {
Expand Down Expand Up @@ -68,7 +86,10 @@ impl Cons {
}

pub fn to_tuple(&self, mut process: &mut Process) -> exception::Result {
let vec: Vec<Term> = self.to_vec(&mut process)?;
let vec: Vec<Term> = self
.into_iter()
.collect::<Result<Vec<Term>, _>>()
.map_err(|_| bad_argument!(&mut process))?;

Ok(Term::slice_to_tuple(vec.as_slice(), &mut process))
}
Expand Down Expand Up @@ -158,29 +179,6 @@ impl Cons {
Err(bad_argument!(&mut process))
}
}

fn to_vec(&self, mut process: &mut Process) -> Result<Vec<Term>, Exception> {
let mut vec: Vec<Term> = Vec::new();
let mut head = self.head;
let mut tail = self.tail;

loop {
vec.push(head);

match tail.tag() {
EmptyList => break,
List => {
let cons: &Cons = unsafe { tail.as_ref_cons_unchecked() };

head = cons.head;
tail = cons.tail;
}
_ => return Err(bad_argument!(&mut process)),
}
}

Ok(vec)
}
}

impl DebugInProcess for Cons {
Expand All @@ -202,6 +200,68 @@ impl Hash for Cons {
}
}

#[derive(Clone)]
pub struct ImproperList {
tail: Term,
}

impl IntoIterator for &Cons {
type Item = Result<Term, ImproperList>;
type IntoIter = Iter;

fn into_iter(self) -> Iter {
Iter {
head: Some(Ok(self.head)),
tail: Some(self.tail),
}
}
}

pub struct Iter {
head: Option<Result<Term, ImproperList>>,
tail: Option<Term>,
}

impl FusedIterator for Iter {}

impl Iterator for Iter {
type Item = Result<Term, ImproperList>;

fn next(&mut self) -> Option<Self::Item> {
let next = self.head.clone();

match next {
None => (),
Some(Err(_)) => {
self.head = None;
self.tail = None;
}
_ => {
let tail = self.tail.unwrap();

match tail.tag() {
EmptyList => {
self.head = None;
self.tail = None;
}
List => {
let cons: &Cons = unsafe { tail.as_ref_cons_unchecked() };

self.head = Some(Ok(cons.head));
self.tail = Some(cons.tail);
}
_ => {
self.head = Some(Err(ImproperList { tail }));
self.tail = None;
}
}
}
}

next
}
}

impl OrderInProcess for Cons {
fn cmp_in_process(&self, other: &Cons, process: &Process) -> Ordering {
match self.head.cmp_in_process(&other.head, process) {
Expand Down
23 changes: 23 additions & 0 deletions lumen_runtime/src/otp/erlang.rs
Expand Up @@ -711,6 +711,29 @@ pub fn size_1(binary_or_tuple: Term, mut process: &mut Process) -> Result {
.map(|integer| integer.into_process(&mut process))
}

pub fn subtract_list_2(minuend: Term, subtrahend: Term, mut process: &mut Process) -> Result {
match (minuend.tag(), subtrahend.tag()) {
(EmptyList, EmptyList) => Ok(minuend),
(EmptyList, List) => {
let subtrahend_cons: &Cons = unsafe { subtrahend.as_ref_cons_unchecked() };

if subtrahend_cons.is_proper() {
Ok(minuend)
} else {
Err(bad_argument!(&mut process))
}
}
(List, EmptyList) => Ok(minuend),
(List, List) => {
let minuend_cons: &Cons = unsafe { minuend.as_ref_cons_unchecked() };
let subtrahend_cons: &Cons = unsafe { subtrahend.as_ref_cons_unchecked() };

minuend_cons.subtract(subtrahend_cons, &mut process)
}
_ => Err(bad_argument!(&mut process)),
}
}

pub fn tl_1(list: Term, process: &mut Process) -> Result {
let cons: &Cons = list.try_into_in_process(process)?;

Expand Down
1 change: 1 addition & 0 deletions lumen_runtime/src/otp/erlang/tests.rs
Expand Up @@ -49,6 +49,7 @@ mod make_ref_0;
mod self_0;
mod setelement_3;
mod size_1;
mod subtract_list_2;
mod tl_1;
mod tuple_size_1;
mod tuple_to_list_1;
Expand Down
Expand Up @@ -96,7 +96,7 @@ fn with_float_returns_float() {
let process_rw_lock = environment::process(Arc::clone(&environment_rw_lock));
let mut process = process_rw_lock.write().unwrap();
let list = Term::EMPTY_LIST;
let term = Term::EMPTY_LIST;
let term = 1.0.into_process(&mut process);

assert_eq_in_process!(
erlang::concatenate_2(list, term, &mut process),
Expand Down
Expand Up @@ -99,7 +99,7 @@ fn with_float_returns_improper_list_with_float_as_tail() {
let mut process = process_rw_lock.write().unwrap();
let element = 0.into_process(&mut process);
let list = Term::cons(element, Term::EMPTY_LIST, &mut process);
let term = Term::cons(0.into_process(&mut process), Term::EMPTY_LIST, &mut process);
let term = 1.0.into_process(&mut process);

assert_eq_in_process!(
erlang::concatenate_2(list, term, &mut process),
Expand Down

0 comments on commit fca3c4f

Please sign in to comment.