use crate::atom::Atom;
use crate::native::ProcessContext;
use crate::term::Term;
use crate::term::boxed::{Cons, Map, Tuple, write_cons, write_map};
pub fn bif_maps_from_list(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let pairs = list_of_2tuples(*input)?;
let mut entries: Vec<(Term, Term)> = Vec::with_capacity(pairs.len());
for (key, value) in pairs {
if let Some(existing) = entries.iter_mut().find(|(k, _)| *k == key) {
existing.1 = value;
} else {
entries.push((key, value));
}
}
entries.sort_by(|(a, _), (b, _)| a.cmp(b));
let keys: Vec<Term> = entries.iter().map(|(k, _)| *k).collect();
let values: Vec<Term> = entries.iter().map(|(_, v)| *v).collect();
make_leaked_map(&keys, &values)
}
pub fn bif_maps_merge(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [map1_term, map2_term] = args else {
return Err(badarg());
};
let map1 = Map::new(*map1_term).ok_or_else(badarg)?;
let map2 = Map::new(*map2_term).ok_or_else(badarg)?;
let mut entries: Vec<(Term, Term)> = Vec::with_capacity(map1.len() + map2.len());
for i in 0..map1.len() {
if let (Some(k), Some(v)) = (map1.key(i), map1.value(i)) {
entries.push((k, v));
}
}
for i in 0..map2.len() {
if let (Some(k), Some(v)) = (map2.key(i), map2.value(i)) {
if let Some(existing) = entries.iter_mut().find(|(ek, _)| *ek == k) {
existing.1 = v;
} else {
entries.push((k, v));
}
}
}
entries.sort_by(|(a, _), (b, _)| a.cmp(b));
let keys: Vec<Term> = entries.iter().map(|(k, _)| *k).collect();
let values: Vec<Term> = entries.iter().map(|(_, v)| *v).collect();
make_leaked_map(&keys, &values)
}
pub fn bif_maps_remove(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [key_term, map_term] = args else {
return Err(badarg());
};
let map = Map::new(*map_term).ok_or_else(badarg)?;
let mut keys = Vec::with_capacity(map.len());
let mut values = Vec::with_capacity(map.len());
for i in 0..map.len() {
if let (Some(k), Some(v)) = (map.key(i), map.value(i))
&& k != *key_term
{
keys.push(k);
values.push(v);
}
}
make_leaked_map(&keys, &values)
}
pub fn bif_lists_reverse(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let elements = list_to_vec(*input)?;
let mut tail = Term::NIL;
for element in elements {
let cell = Box::leak(Box::new([0u64; 2]));
tail = write_cons(cell, element, tail).ok_or_else(badarg)?;
}
Ok(tail)
}
pub fn bif_maps_map(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [_fun, _map] = args else {
return Err(badarg());
};
Err(badarg())
}
pub fn bif_timer_sleep(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [ms_term] = args else {
return Err(badarg());
};
let ms = ms_term
.as_small_int()
.and_then(|v| u64::try_from(v).ok())
.ok_or_else(badarg)?;
std::thread::sleep(std::time::Duration::from_millis(ms));
Ok(Term::atom(Atom::OK))
}
fn list_of_2tuples(term: Term) -> Result<Vec<(Term, Term)>, Term> {
let mut pairs = Vec::new();
let mut current = term;
loop {
if current.is_nil() {
return Ok(pairs);
}
let cons = Cons::new(current).ok_or_else(badarg)?;
let head = cons.head();
let tuple = Tuple::new(head).ok_or_else(badarg)?;
if tuple.arity() != 2 {
return Err(badarg());
}
let key = tuple.get(0).ok_or_else(badarg)?;
let value = tuple.get(1).ok_or_else(badarg)?;
pairs.push((key, value));
current = cons.tail();
}
}
fn list_to_vec(term: Term) -> Result<Vec<Term>, Term> {
let mut elements = Vec::new();
let mut current = term;
loop {
if current.is_nil() {
return Ok(elements);
}
let cons = Cons::new(current).ok_or_else(badarg)?;
elements.push(cons.head());
current = cons.tail();
}
}
fn make_leaked_map(keys: &[Term], values: &[Term]) -> Result<Term, Term> {
let total_words = 2 + keys.len() + values.len();
let heap: &mut [u64] = Box::leak(vec![0u64; total_words].into_boxed_slice());
write_map(heap, keys, values).ok_or_else(badarg)
}
fn badarg() -> Term {
Term::atom(Atom::BADARG)
}