pub mod bitwise_bifs;
pub mod collection_bifs;
pub mod encoding_bifs;
pub mod gleam_stdlib_ffi;
pub mod gleam_stdlib_ffi2;
pub mod io_bifs;
pub mod lists_bifs;
pub mod maps_bifs;
pub mod math_bifs;
pub mod sample_support_bifs;
pub mod string_bifs;
pub mod type_conversion_bifs;
pub mod uri_bifs;
use crate::atom::{Atom, AtomTable};
use crate::native::{BifRegistryImpl, NativeFn, NativeRegistrationError, ProcessContext};
use crate::term::Term;
use crate::term::binary::Binary;
use crate::term::boxed::Cons;
use rand::RngExt;
use bitwise_bifs::{bif_band, bif_bnot, bif_bor, bif_bsl, bif_bsr, bif_bxor};
use collection_bifs::{
bif_lists_reverse, bif_maps_from_list, bif_maps_map, bif_maps_merge, bif_maps_remove,
bif_timer_sleep,
};
use encoding_bifs::{
bif_base64_decode, bif_base64_encode, bif_binary_decode_hex, bif_binary_encode_hex,
};
use gleam_stdlib_ffi::{
bif_contains_string, bif_crop_string, bif_inspect, bif_iodata_append, bif_less_than, bif_slice,
bif_string_ends_with, bif_string_pop_grapheme, bif_string_remove_prefix,
bif_string_remove_suffix, bif_string_replace, bif_string_starts_with,
bif_utf_codepoint_list_to_string,
};
use gleam_stdlib_ffi2::{
bif_base16_decode as bif_gleam_base16_decode, bif_base16_encode as bif_gleam_base16_encode,
bif_base64_decode as bif_gleam_base64_decode, bif_base64_encode as bif_gleam_base64_encode,
bif_bit_array as bif_gleam_bit_array, bif_bit_array_concat as bif_gleam_bit_array_concat,
bif_bit_array_pad_to_bytes as bif_gleam_bit_array_pad_to_bytes,
bif_bit_array_slice as bif_gleam_bit_array_slice,
bif_bit_array_to_int_and_size as bif_gleam_bit_array_to_int_and_size, bif_classify_dynamic,
bif_dict, bif_float as bif_gleam_float, bif_float_to_string, bif_index, bif_int,
bif_int_from_base_string, bif_is_null, bif_list as bif_gleam_list, bif_map_get,
bif_parse_float, bif_parse_int, bif_print, bif_print_error, bif_println, bif_println_error,
bif_wrap_list,
};
use io_bifs::{
bif_io_format_3, bif_io_lib_format_2, bif_io_put_chars_1, bif_io_put_chars_2, bif_io_setopts_2,
};
use lists_bifs::{
bif_lists_append_1, bif_lists_append_2, bif_lists_join, bif_lists_map, bif_lists_reverse_2,
bif_lists_seq,
};
use maps_bifs::{
bif_maps_filter, bif_maps_find, bif_maps_fold, bif_maps_keys, bif_maps_merge_with,
bif_maps_put, bif_maps_to_list, bif_maps_update_with, bif_maps_values, bif_maps_with,
bif_maps_without,
};
use math_bifs::{bif_ceil, bif_exp, bif_floor, bif_log, bif_pow};
use sample_support_bifs::{
bif_gleam_list_map, bif_gleam_result_try, bif_gleam_string_repeat, bif_gleam_string_replace,
bif_gleam_string_tree_split, bif_gleeunit_main,
};
use string_bifs::{
bif_equal as bif_string_equal, bif_find as bif_string_find,
bif_is_empty as bif_string_is_empty, bif_length as bif_string_length,
bif_lowercase as bif_string_lowercase, bif_next_grapheme as bif_string_next_grapheme,
bif_pad as bif_string_pad, bif_replace as bif_string_replace4,
bif_reverse as bif_string_reverse, bif_slice as bif_string_slice,
bif_split as bif_string_split, bif_trim as bif_string_trim,
bif_uppercase as bif_string_uppercase,
};
use type_conversion_bifs::{
bif_atom_to_binary, bif_binary_to_float, bif_binary_to_integer, bif_binary_to_integer_radix,
bif_float, bif_integer_to_binary, bif_integer_to_binary_radix, bif_integer_to_list,
bif_iolist_to_binary, bif_list_to_bitstring, bif_list_to_tuple, bif_tuple_to_list,
};
use uri_bifs::{
bif_parse_query, bif_percent_decode, bif_percent_encode, bif_uri_parse,
bif_uri_string_dissect_query, bif_uri_string_parse,
};
type StubBif = (&'static str, &'static str, u8, NativeFn);
const STDLIB_STUBS: &[StubBif] = &[
("erlang", "atom_to_binary", 1, bif_atom_to_binary),
("erlang", "binary_to_float", 1, bif_binary_to_float),
("erlang", "binary_to_integer", 1, bif_binary_to_integer),
(
"erlang",
"binary_to_integer",
2,
bif_binary_to_integer_radix,
),
("erlang", "float", 1, bif_float),
("erlang", "integer_to_binary", 1, bif_integer_to_binary),
(
"erlang",
"integer_to_binary",
2,
bif_integer_to_binary_radix,
),
("erlang", "integer_to_list", 1, bif_integer_to_list),
("erlang", "iolist_to_binary", 1, bif_iolist_to_binary),
("erlang", "list_to_bitstring", 1, bif_list_to_bitstring),
("erlang", "list_to_tuple", 1, bif_list_to_tuple),
("erlang", "tuple_to_list", 1, bif_tuple_to_list),
("erlang", "band", 2, bif_band),
("erlang", "bnot", 1, bif_bnot),
("erlang", "bor", 2, bif_bor),
("erlang", "bsl", 2, bif_bsl),
("erlang", "bsr", 2, bif_bsr),
("erlang", "bxor", 2, bif_bxor),
("math", "ceil", 1, bif_ceil),
("math", "floor", 1, bif_floor),
("math", "exp", 1, bif_exp),
("math", "log", 1, bif_log),
("math", "pow", 2, bif_pow),
("rand", "uniform", 0, bif_rand_uniform),
("logger", "warning", 2, bif_logger_warning),
("unicode", "characters_to_list", 1, bif_characters_to_list),
(
"unicode",
"characters_to_binary",
1,
bif_characters_to_binary,
),
("sys", "debug_options", 1, bif_debug_options),
("gleam_stdlib", "identity", 1, bif_identity),
("gleam_stdlib", "classify_dynamic", 1, bif_classify_dynamic),
("gleam_stdlib", "dict", 1, bif_dict),
("gleam_stdlib", "float", 1, bif_gleam_float),
("gleam_stdlib", "float_to_string", 1, bif_float_to_string),
("gleam_stdlib", "index", 2, bif_index),
("gleam_stdlib", "int", 1, bif_int),
(
"gleam_stdlib",
"int_from_base_string",
2,
bif_int_from_base_string,
),
("gleam_stdlib", "parse_float", 1, bif_parse_float),
("gleam_stdlib", "parse_int", 1, bif_parse_int),
("gleam_stdlib", "is_null", 1, bif_is_null),
("gleam_stdlib", "list", 5, bif_gleam_list),
("gleam_stdlib", "map_get", 2, bif_map_get),
("gleam_stdlib", "print", 1, bif_print),
("gleam_stdlib", "print_error", 1, bif_print_error),
("gleam_stdlib", "println", 1, bif_println),
("gleam_stdlib", "println_error", 1, bif_println_error),
("gleam_stdlib", "wrap_list", 1, bif_wrap_list),
("gleam_stdlib", "parse_query", 1, bif_parse_query),
("gleam_stdlib", "percent_decode", 1, bif_percent_decode),
("gleam_stdlib", "percent_encode", 1, bif_percent_encode),
("gleam_stdlib", "uri_parse", 1, bif_uri_parse),
("gleam_stdlib", "base16_decode", 1, bif_gleam_base16_decode),
("gleam_stdlib", "base16_encode", 1, bif_gleam_base16_encode),
("gleam_stdlib", "base64_decode", 1, bif_gleam_base64_decode),
("gleam_stdlib", "base64_encode", 2, bif_gleam_base64_encode),
("gleam_stdlib", "bit_array", 1, bif_gleam_bit_array),
(
"gleam_stdlib",
"bit_array_concat",
1,
bif_gleam_bit_array_concat,
),
(
"gleam_stdlib",
"bit_array_pad_to_bytes",
1,
bif_gleam_bit_array_pad_to_bytes,
),
(
"gleam_stdlib",
"bit_array_slice",
3,
bif_gleam_bit_array_slice,
),
(
"gleam_stdlib",
"bit_array_to_int_and_size",
1,
bif_gleam_bit_array_to_int_and_size,
),
("gleam_stdlib", "string_replace", 3, bif_string_replace),
("gleam_stdlib", "less_than", 2, bif_less_than),
("gleam_stdlib", "slice", 3, bif_slice),
("gleam_stdlib", "crop_string", 2, bif_crop_string),
("gleam_stdlib", "contains_string", 2, bif_contains_string),
(
"gleam_stdlib",
"string_starts_with",
2,
bif_string_starts_with,
),
("gleam_stdlib", "string_ends_with", 2, bif_string_ends_with),
(
"gleam_stdlib",
"string_pop_grapheme",
1,
bif_string_pop_grapheme,
),
(
"gleam_stdlib",
"utf_codepoint_list_to_string",
1,
bif_utf_codepoint_list_to_string,
),
("gleam_stdlib", "inspect", 1, bif_inspect),
(
"gleam_stdlib",
"string_remove_prefix",
2,
bif_string_remove_prefix,
),
(
"gleam_stdlib",
"string_remove_suffix",
2,
bif_string_remove_suffix,
),
("gleam_stdlib", "iodata_append", 2, bif_iodata_append),
("uri_string", "parse", 1, bif_uri_string_parse),
(
"uri_string",
"dissect_query",
1,
bif_uri_string_dissect_query,
),
("string", "length", 1, bif_string_length),
("string", "reverse", 1, bif_string_reverse),
("string", "lowercase", 1, bif_string_lowercase),
("string", "uppercase", 1, bif_string_uppercase),
("string", "trim", 2, bif_string_trim),
("string", "split", 3, bif_string_split),
("string", "find", 2, bif_string_find),
("string", "next_grapheme", 1, bif_string_next_grapheme),
("string", "pad", 4, bif_string_pad),
("string", "replace", 4, bif_string_replace4),
("string", "slice", 3, bif_string_slice),
("string", "equal", 2, bif_string_equal),
("string", "is_empty", 1, bif_string_is_empty),
("binary", "part", 3, bif_binary_part),
("binary", "encode_hex", 1, bif_binary_encode_hex),
("binary", "decode_hex", 1, bif_binary_decode_hex),
("base64", "encode", 2, bif_base64_encode),
("base64", "decode", 1, bif_base64_decode),
("io", "put_chars", 1, bif_io_put_chars_1),
("io", "put_chars", 2, bif_io_put_chars_2),
("io", "format", 3, bif_io_format_3),
("io", "setopts", 2, bif_io_setopts_2),
("io_lib", "format", 2, bif_io_lib_format_2),
("init", "stop", 1, bif_init_stop),
("maps", "from_list", 1, bif_maps_from_list),
("maps", "merge", 2, bif_maps_merge),
("maps", "remove", 2, bif_maps_remove),
("maps", "map", 2, bif_maps_map),
("maps", "put", 3, bif_maps_put),
("maps", "find", 2, bif_maps_find),
("maps", "keys", 1, bif_maps_keys),
("maps", "values", 1, bif_maps_values),
("maps", "to_list", 1, bif_maps_to_list),
("maps", "fold", 3, bif_maps_fold),
("maps", "filter", 2, bif_maps_filter),
("maps", "merge_with", 3, bif_maps_merge_with),
("maps", "update_with", 4, bif_maps_update_with),
("maps", "with", 2, bif_maps_with),
("maps", "without", 2, bif_maps_without),
("lists", "reverse", 1, bif_lists_reverse),
("lists", "append", 1, bif_lists_append_1),
("lists", "append", 2, bif_lists_append_2),
("lists", "join", 2, bif_lists_join),
("lists", "map", 2, bif_lists_map),
("lists", "reverse", 2, bif_lists_reverse_2),
("lists", "seq", 2, bif_lists_seq),
("timer", "sleep", 1, bif_timer_sleep),
("gleam@list", "map", 2, bif_gleam_list_map),
("gleam@string", "repeat", 2, bif_gleam_string_repeat),
("gleam@string", "replace", 3, bif_gleam_string_replace),
("gleam@string_tree", "split", 2, bif_gleam_string_tree_split),
("gleam@result", "try", 2, bif_gleam_result_try),
("gleeunit", "main", 0, bif_gleeunit_main),
("erlang", "fun_info", 2, bif_fun_info),
("io_lib_format", "fwrite_g", 1, bif_fwrite_g),
];
#[cfg(feature = "json")]
const JSON_STUBS: &[StubBif] = &[
("json", "decode", 1, bif_json_decode),
("json", "encode", 1, bif_json_encode),
];
pub fn register_stdlib_stubs(
registry: &BifRegistryImpl,
atom_table: &AtomTable,
) -> Result<(), NativeRegistrationError> {
for &(module_name, function_name, arity, native_function) in STDLIB_STUBS {
let module = atom_table.intern(module_name);
let function = atom_table.intern(function_name);
registry.register(module, function, arity, native_function)?;
}
#[cfg(feature = "json")]
for &(module_name, function_name, arity, native_function) in JSON_STUBS {
let module = atom_table.intern(module_name);
let function = atom_table.intern(function_name);
registry.register(module, function, arity, native_function)?;
}
Ok(())
}
pub fn bif_logger_warning(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [format_term, args_term] = args else {
return Err(badarg());
};
if let Some(binary) = Binary::new(*format_term) {
let format_str = String::from_utf8_lossy(binary.as_bytes());
eprintln!("[warning] {format_str} {args_term:?}");
} else {
eprintln!("[warning] {format_term:?} {args_term:?}");
}
Ok(Term::atom(Atom::OK))
}
pub fn bif_characters_to_binary(
args: &[Term],
_context: &mut ProcessContext,
) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
if Binary::new(*input).is_some() {
return Ok(*input);
}
if input.is_nil() {
return Ok(make_empty_binary());
}
if input.is_list() {
let mut bytes = Vec::new();
let mut current = *input;
loop {
if current.is_nil() {
break;
}
let cons = Cons::new(current).ok_or_else(badarg)?;
let head = cons.head();
if let Some(code_point) = head.as_small_int() {
let cp = u32::try_from(code_point).map_err(|_| badarg())?;
let ch = char::from_u32(cp).ok_or_else(badarg)?;
let mut buf = [0u8; 4];
let encoded = ch.encode_utf8(&mut buf);
bytes.extend_from_slice(encoded.as_bytes());
} else if let Some(binary) = Binary::new(head) {
bytes.extend_from_slice(binary.as_bytes());
} else {
return Err(badarg());
}
current = cons.tail();
}
return Ok(make_leaked_binary(&bytes));
}
Err(badarg())
}
pub fn bif_characters_to_list(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let binary = Binary::new(*input).ok_or_else(badarg)?;
let bytes = binary.as_bytes();
if bytes.is_empty() {
return Ok(Term::NIL);
}
let text = std::str::from_utf8(bytes).map_err(|_| badarg())?;
let code_points: Vec<i64> = text.chars().map(|ch| i64::from(ch as u32)).collect();
let mut tail = Term::NIL;
for &cp in code_points.iter().rev() {
let int_term = Term::try_small_int(cp).ok_or_else(badarg)?;
let cell = Box::leak(Box::new([0u64; 2]));
tail = crate::term::boxed::write_cons(cell, int_term, tail).ok_or_else(badarg)?;
}
Ok(tail)
}
pub fn bif_binary_part(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let _ = context;
let [binary_term, offset_term, length_term] = args else {
return Err(badarg());
};
let binary = Binary::new(*binary_term).ok_or_else(badarg)?;
let offset = offset_term
.as_small_int()
.and_then(|value| usize::try_from(value).ok())
.ok_or_else(badarg)?;
let length = length_term
.as_small_int()
.and_then(|value| usize::try_from(value).ok())
.ok_or_else(badarg)?;
let end = offset.checked_add(length).ok_or_else(badarg)?;
let bytes = binary.as_bytes();
if end > bytes.len() {
return Err(badarg());
}
Ok(make_leaked_binary(&bytes[offset..end]))
}
pub fn bif_rand_uniform(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let _ = context;
if !args.is_empty() {
return Err(badarg());
}
let value = rand::rng().random_range(0.0..1.0);
let heap = Box::leak(Box::new([0u64; 2]));
crate::term::boxed::write_float(heap, value).ok_or_else(badarg)
}
pub fn bif_init_stop(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [exit_code] = args else {
return Err(badarg());
};
let _code = exit_code.as_small_int().ok_or_else(badarg)?;
context.request_shutdown();
Ok(Term::atom(Atom::OK))
}
pub fn bif_debug_options(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [_options] = args else {
return Err(badarg());
};
Ok(Term::NIL)
}
pub fn bif_identity(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [value] = args else {
return Err(badarg());
};
Ok(*value)
}
pub fn bif_fun_info(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [fun, item] = args else {
return Err(badarg());
};
let item_atom = item.as_atom().ok_or_else(badarg)?;
let at = context.atom_table().ok_or_else(badarg)?;
let item_name = at.resolve(item_atom).unwrap_or("");
let value = match item_name {
"arity" => {
let arity = crate::term::boxed::Closure::new(*fun)
.map_or(0, |c| i64::from(c.arity()));
Term::small_int(arity)
}
"module" | "name" | "type" => make_leaked_binary(item_name.as_bytes()),
"env" => Term::NIL,
_ => Term::atom(Atom::UNDEFINED),
};
let heap: &mut [u64] = Box::leak(vec![0u64; 3].into_boxed_slice());
crate::term::boxed::write_tuple(heap, &[*item, value]).ok_or_else(badarg)
}
pub fn bif_fwrite_g(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [float_term] = args else {
return Err(badarg());
};
let f = if let Some(v) = float_term.as_small_int() {
v as f64
} else if let Some(fl) = crate::term::boxed::Float::new(*float_term) {
fl.value()
} else {
return Err(badarg());
};
Ok(make_leaked_binary(format!("{f}").as_bytes()))
}
fn make_empty_binary() -> Term {
let heap = Box::leak(Box::new([0u64; 2]));
crate::term::binary::write_binary(heap, &[]).unwrap_or(Term::NIL)
}
fn make_leaked_binary(bytes: &[u8]) -> Term {
let data_words = crate::term::binary::packed_word_count(bytes.len());
let total_words = 2 + data_words;
let heap: &mut [u64] = Box::leak(vec![0u64; total_words].into_boxed_slice());
crate::term::binary::write_binary(heap, bytes).unwrap_or(Term::NIL)
}
fn badarg() -> Term {
Term::atom(Atom::BADARG)
}
#[cfg(test)]
mod b033_tests;
#[cfg(test)]
mod b038_tests;
#[cfg(test)]
mod bitwise_bifs_tests;
#[cfg(test)]
mod collection_bifs_tests;
#[cfg(feature = "json")]
fn bif_json_decode(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let binary = Binary::new(*input).ok_or_else(badarg)?;
let json_value: serde_json::Value =
serde_json::from_slice(binary.as_bytes()).map_err(|_| badarg())?;
crate::term::json::value_to_term(&json_value, context).map_err(|_| badarg())
}
#[cfg(feature = "json")]
fn bif_json_encode(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let atom_table = context.atom_table().ok_or_else(badarg)?;
let json_value =
crate::term::json::term_to_value(*input, atom_table).map_err(|_| badarg())?;
let json_bytes = serde_json::to_vec(&json_value).map_err(|_| badarg())?;
Ok(make_leaked_binary(&json_bytes))
}
#[cfg(test)]
mod gleam_stdlib_ffi2_tests;
#[cfg(test)]
mod math_bifs_tests;
#[cfg(test)]
mod rand_bifs_tests;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod type_conversion_bifs_tests;
#[cfg(test)]
mod uri_bifs_tests;