use crate::atom::Atom;
use crate::native::ProcessContext;
use crate::term::Term;
use crate::term::boxed::{Cons, Map, Tuple};
use crate::term::compare;
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 process = context.process_mut().ok_or_else(badarg)?;
process.set_x_reg(0, *input);
}
let map_words = 2 + pairs.len() * 2;
context.ensure_heap_space(map_words)?;
let input = context.process_mut().ok_or_else(badarg)?.x_reg(0);
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));
}
}
let atom_table = context.atom_table().ok_or_else(badarg)?;
entries.sort_by(|(a, _), (b, _)| compare::cmp(*a, *b, atom_table));
let keys: Vec<Term> = entries.iter().map(|(k, _)| *k).collect();
let values: Vec<Term> = entries.iter().map(|(_, v)| *v).collect();
context.alloc_map_prereserved(&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 entry_count = map1.len() + map2.len();
{
let process = context.process_mut().ok_or_else(badarg)?;
process.set_x_reg(0, *map1_term);
process.set_x_reg(1, *map2_term);
}
context.ensure_heap_space(2 + entry_count * 2)?;
let (map1_term, map2_term) = {
let process = context.process_mut().ok_or_else(badarg)?;
(process.x_reg(0), process.x_reg(1))
};
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));
}
}
}
let atom_table = context.atom_table().ok_or_else(badarg)?;
entries.sort_by(|(a, _), (b, _)| compare::cmp(*a, *b, atom_table));
let keys: Vec<Term> = entries.iter().map(|(k, _)| *k).collect();
let values: Vec<Term> = entries.iter().map(|(_, v)| *v).collect();
context.alloc_map_prereserved(&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 process = context.process_mut().ok_or_else(badarg)?;
process.set_x_reg(0, *key_term);
process.set_x_reg(1, *map_term);
}
context.ensure_heap_space(2 + map.len() * 2)?;
let (key_term, map_term) = {
let process = context.process_mut().ok_or_else(badarg)?;
(process.x_reg(0), process.x_reg(1))
};
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);
}
}
context.alloc_map_prereserved(&keys, &values)
}
pub fn bif_lists_reverse(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let count = list_length(*input)?;
if count == 0 {
return Ok(Term::NIL);
}
{
let process = context.process_mut().ok_or_else(badarg)?;
process.set_x_reg(0, *input);
}
context.ensure_heap_space(count * 2)?;
let input = context.process_mut().ok_or_else(badarg)?.x_reg(0);
build_reversed_list(context, input, count)
}
pub fn bif_maps_map(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
super::maps_bifs::bif_maps_map(args, context)
}
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();
}
}
pub(super) fn list_length(term: Term) -> Result<usize, Term> {
let mut count = 0;
let mut current = term;
loop {
if current.is_nil() {
return Ok(count);
}
let cons = Cons::new(current).ok_or_else(badarg)?;
count += 1;
current = cons.tail();
}
}
fn build_reversed_list(
context: &mut ProcessContext,
list: Term,
count: usize,
) -> Result<Term, Term> {
use crate::term::boxed::write_cons;
let mut result = Term::NIL;
let mut current = list;
for _ in 0..count {
let cons = Cons::new(current).ok_or_else(badarg)?;
let head = cons.head();
current = cons.tail();
let process = context.process_mut().ok_or_else(badarg)?;
let heap = process.heap_mut().alloc_slice(2).map_err(|_| badarg())?;
result = write_cons(heap, head, result).ok_or_else(badarg)?;
}
Ok(result)
}
fn badarg() -> Term {
Term::atom(Atom::BADARG)
}