#![no_std]
extern crate alloc;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
pub use tishlang_core::{
js_number_to_string_into, to_int32, to_int32_value, to_number_value, to_uint32,
to_uint32_value, ArcStr, NumArrayBacking, ObjectData, ObjectMap, PropMap, TishStruct, Value,
VmReadGuard, VmRef, VmWriteGuard,
};
pub use tishlang_core::Arc;
pub type Fixed = agb::fixnum::Num<i32, 8>;
pub use tishlang_core::{
has_pending_throw, set_pending_throw, stack_overflow_error, take_pending_throw, CallDepthGuard,
};
unsafe extern "C" {
static __iwram_end: u8;
}
const GBA_STACK_MARGIN: usize = 2 * 1024;
#[inline]
pub fn stack_low() -> bool {
let anchor = 0u8;
let sp = &anchor as *const u8 as usize;
let floor = (&raw const __iwram_end) as usize + GBA_STACK_MARGIN;
sp < floor
}
#[inline]
pub fn enter_call_guarded() -> Option<CallDepthGuard> {
if stack_low() {
set_pending_throw(stack_overflow_error());
return None;
}
tishlang_core::enter_call_guarded()
}
#[cold]
#[inline(never)]
pub fn recursion_tripped_f64() -> f64 {
set_pending_throw(stack_overflow_error());
f64::NAN
}
#[derive(Debug, Clone)]
pub enum TishError {
Throw(Value),
Return(Value),
}
impl core::fmt::Display for TishError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
TishError::Throw(v) => write!(f, "{}", v.to_display_string()),
TishError::Return(v) => write!(f, "return {}", v.to_display_string()),
}
}
}
impl core::error::Error for TishError {}
pub fn fn_unwind(e: Box<dyn core::error::Error>) -> Value {
if let Some(te) = e.downcast_ref::<TishError>() {
match te {
TishError::Throw(v) | TishError::Return(v) => return v.clone(),
}
}
Value::String(e.to_string().into())
}
pub fn value_call(callee: &Value, args: &[Value]) -> Value {
if has_pending_throw() {
return Value::Null;
}
tishlang_core::value_call(callee, args)
}
#[inline]
pub fn push_value_str(buf: &mut String, v: &Value) {
match v {
Value::String(s) => buf.push_str(s),
Value::Number(n) => js_number_to_string_into(buf, *n),
Value::Bool(b) => buf.push_str(if *b { "true" } else { "false" }),
Value::Null => buf.push_str("null"),
other => buf.push_str(&other.to_js_string()),
}
}
#[inline]
pub fn vm_read<T: Clone>(cell: &VmRef<T>) -> T {
(*cell.borrow()).clone()
}
pub fn normalize_for_of(v: Value) -> Value {
if let Value::NumberArray(arr) = &v {
let items: Vec<Value> = arr.borrow().to_values();
return Value::Array(VmRef::new(items));
}
match tishlang_core::drain_iterator(&v) {
Some(items) => Value::Array(VmRef::new(items)),
None => v,
}
}
pub fn in_operator(key: &Value, obj: &Value) -> Value {
match obj {
Value::Object(_) => Value::Bool(tishlang_core::object_has(obj, key)),
Value::Array(arr) => Value::Bool(array_in(key, arr.borrow().len())),
Value::NumberArray(arr) => Value::Bool(array_in(key, arr.borrow().len())),
Value::Struct(s) => {
Value::Bool(tishlang_core::object_has(&s.borrow().tish_to_object(), key))
}
_ => Value::Bool(false),
}
}
pub fn object_spread_props(v: &Value) -> Option<PropMap> {
match v {
Value::Object(o) => Some(o.borrow().strings.clone()),
Value::Struct(s) => match s.borrow().tish_to_object() {
Value::Object(o) => Some(o.borrow().strings.clone()),
_ => None,
},
_ => None,
}
}
fn array_in(key: &Value, len: usize) -> bool {
let key_str: Arc<str> = match key {
Value::String(s) => Arc::from(s.as_str()),
Value::Number(n) => n.to_string().into(),
_ => return false,
};
key_str.as_ref() == "length"
|| key_str.parse::<usize>().ok().map(|i| i < len).unwrap_or(false)
}
use tishlang_builtins::collections::collection_size;
use tishlang_builtins::helpers::extract_num;
pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
let key = key.as_ref();
match obj {
Value::Object(map) => {
if key == "size" {
if let Some(n) = collection_size(obj) {
return Value::Number(n);
}
}
map.borrow().strings.get(key).cloned().unwrap_or(Value::Null)
}
Value::Array(arr) => {
if key == "length" {
Value::Number(arr.borrow().len() as f64)
} else if let Ok(idx) = key.parse::<usize>() {
arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
} else {
Value::Null
}
}
Value::NumberArray(arr) => {
if key == "length" {
Value::Number(arr.borrow().len() as f64)
} else if let Ok(idx) = key.parse::<usize>() {
arr.borrow()
.get(idx)
.map(|v| match v {
Value::Number(n) if n.is_nan() => Value::Null,
other => other,
})
.unwrap_or(Value::Null)
} else {
Value::Null
}
}
Value::String(s) => {
if key == "length" {
Value::Number(tishlang_builtins::string::char_count(s) as f64)
} else {
Value::Null
}
}
Value::Opaque(o) => o.get_method(key).map(Value::Function).unwrap_or(Value::Null),
Value::Struct(s) => s.borrow().tish_get(key),
Value::Null => {
tishlang_core::set_pending_throw(tishlang_core::cannot_read_property_error(key));
Value::Null
}
_ => Value::Null,
}
}
pub fn get_index(obj: &Value, index: &Value) -> Value {
match obj {
Value::Array(arr) => {
let idx = match index {
Value::Number(n) => *n as usize,
Value::String(s) => match tishlang_core::str_to_array_index(s) {
Some(i) => i,
None => return Value::Null,
},
_ => return Value::Null,
};
arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
}
Value::NumberArray(arr) => {
let idx = match index {
Value::Number(n) => *n as usize,
Value::String(s) => match tishlang_core::str_to_array_index(s) {
Some(i) => i,
None => return Value::Null,
},
_ => return Value::Null,
};
arr.borrow()
.get(idx)
.map(|v| match v {
Value::Number(n) if n.is_nan() => Value::Null,
other => other,
})
.unwrap_or(Value::Null)
}
Value::String(s) => match index {
Value::Number(n) if *n >= 0.0 && n.fract() == 0.0 => {
tishlang_builtins::string::nth_char(s, *n as usize)
.map(|c| Value::String(c.to_string().into()))
.unwrap_or(Value::Null)
}
_ => Value::Null,
},
Value::Object(_) => tishlang_core::object_get(obj, index).unwrap_or(Value::Null),
Value::Struct(s) => {
let key = match index {
Value::String(k) => k.to_string(),
other => other.to_js_string(),
};
s.borrow().tish_get(&key)
}
Value::Null => {
let key = match index {
Value::String(s) => s.to_string(),
other => other.to_js_string(),
};
tishlang_core::set_pending_throw(tishlang_core::cannot_read_property_error(&key));
Value::Null
}
_ => Value::Null,
}
}
#[inline]
pub fn str_index(s: &str, idx: &Value) -> Value {
match idx {
Value::Number(n) if *n >= 0.0 && n.fract() == 0.0 => match s.chars().nth(*n as usize) {
Some(c) => Value::String(c.to_string().into()),
None => Value::Null,
},
_ => Value::Null,
}
}
#[inline]
pub fn str_char_code_at(s: &str, idx: &Value) -> Value {
let i = match idx {
Value::Number(n) if *n >= 0.0 && n.fract() == 0.0 => *n as usize,
_ => return Value::Number(f64::NAN),
};
match s.chars().nth(i) {
Some(c) => Value::Number(c as u32 as f64),
None => Value::Number(f64::NAN),
}
}
#[inline]
pub fn str_char_at(s: &str, idx: &Value) -> Value {
let i = match idx {
Value::Number(n) if *n >= 0.0 && n.fract() == 0.0 => *n as usize,
_ => return Value::String("".into()),
};
match s.chars().nth(i) {
Some(c) => Value::String(c.to_string().into()),
None => Value::String("".into()),
}
}
pub fn delete_property(obj: &Value, key: &Value) -> Value {
match obj {
Value::Object(m) => {
let key_s = match key {
Value::String(s) => s.to_string(),
other => other.to_js_string(),
};
m.borrow_mut().strings.remove(key_s.as_str());
}
Value::Array(arr) => {
if let Value::Number(n) = key {
let n = *n;
if n >= 0.0 && n.fract() == 0.0 {
let i = n as usize;
let mut a = arr.borrow_mut();
if i < a.len() {
a[i] = Value::Null;
}
}
}
}
_ => {}
}
Value::Bool(true)
}
fn array_index(n: f64) -> Option<usize> {
if n >= 0.0 && n.fract() == 0.0 && n <= 4_294_967_294.0 {
Some(n as usize)
} else {
None
}
}
fn grow_or_throw<T: Clone>(v: &mut alloc::vec::Vec<T>, index: usize, fill: T) -> bool {
if index >= v.len() {
let extra = index - v.len() + 1;
if v.try_reserve(extra).is_err() {
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
"array index {index} too large to allocate"
)));
return false;
}
v.resize(index + 1, fill);
}
true
}
pub fn set_prop(obj: &Value, key: &str, val: Value) -> Value {
match obj {
Value::Object(map) => {
let mut m = map.borrow_mut();
if m.frozen {
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
"Cannot assign to read only property '{key}' of a frozen object"
)));
return val;
}
if let Some(slot) = m.strings.get_mut(key) {
*slot = val.clone();
} else {
m.strings.insert(Arc::from(key), val.clone());
}
val
}
Value::Array(arr) if key == "length" => {
let n = extract_num(Some(&val)).unwrap_or(f64::NAN);
if n.is_nan() || n < 0.0 || n.fract() != 0.0 || n > 4_294_967_295.0 {
tishlang_core::set_pending_throw(tishlang_core::type_error("Invalid array length"));
return val;
}
let len = n as usize;
let mut arr_mut = arr.borrow_mut();
if len > arr_mut.len() {
let _ = grow_or_throw(&mut arr_mut, len - 1, Value::Null);
} else {
arr_mut.truncate(len);
}
val
}
Value::Struct(s) => {
s.borrow_mut().tish_set(key, val.clone());
val
}
_ => {
tishlang_core::set_pending_throw(tishlang_core::type_error("Cannot assign property on a non-object"));
val
}
}
}
pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
let arr_index = |idx: &Value| -> Option<usize> {
match idx {
Value::Number(n) => array_index(*n),
Value::String(s) => tishlang_core::str_to_array_index(s),
_ => None,
}
};
match obj {
Value::Array(arr) => {
let Some(index) = arr_index(idx) else {
return val;
};
let mut arr_mut = arr.borrow_mut();
if !grow_or_throw(&mut arr_mut, index, Value::Null) {
return val;
}
arr_mut[index] = val.clone();
val
}
Value::NumberArray(arr) => {
let Some(index) = arr_index(idx) else {
return val;
};
let mut b = arr.borrow_mut();
match (b.as_packed_mut(), val.as_number()) {
(Some(nums), Some(n)) => {
if grow_or_throw(nums, index, f64::NAN) {
nums[index] = n;
}
}
_ => {
let boxed = b.deopt();
if grow_or_throw(boxed, index, Value::Null) {
boxed[index] = val.clone();
}
}
}
val
}
Value::Object(map) => {
if map.borrow().frozen {
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
"Cannot assign to read only property '{}' of a frozen object",
idx.to_display_string()
)));
return val;
}
if tishlang_core::object_set(obj, idx, val.clone()).is_err() {
tishlang_core::set_pending_throw(tishlang_core::type_error(format!(
"cannot use {} as an object key",
idx.to_display_string()
)));
}
val
}
_ => {
tishlang_core::set_pending_throw(tishlang_core::type_error("Cannot index-assign on a non-array/object"));
val
}
}
}
fn console_line(args: &[Value]) -> String {
tishlang_core::format_values_for_console(args, false)
}
pub fn console_log(args: &[Value]) {
agb::println!("{}", console_line(args));
}
pub fn console_info(args: &[Value]) {
agb::println!("{}", console_line(args));
}
pub fn console_debug(args: &[Value]) {
agb::println!("{}", console_line(args));
}
pub fn console_warn(args: &[Value]) {
agb::println!("{}", console_line(args));
}
pub fn console_error(args: &[Value]) {
agb::println!("{}", console_line(args));
}
pub fn json_parse(args: &[Value]) -> Value {
let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
tishlang_core::json_parse(&s).unwrap_or(Value::Null)
}
pub fn json_stringify(args: &[Value]) -> Value {
let v = args.first().cloned().unwrap_or(Value::Null);
Value::String(tishlang_core::json_stringify(&v).into())
}
pub mod json {
pub use tishlang_core::write_json_number;
pub fn escape_into(buf: &mut alloc::string::String, s: &str) {
use core::fmt::Write;
let bytes = s.as_bytes();
let mut start = 0usize;
for (i, &b) in bytes.iter().enumerate() {
if b < 0x20 || b == b'"' || b == b'\\' {
if start < i {
buf.push_str(&s[start..i]);
}
match b {
b'"' => buf.push_str("\\\""),
b'\\' => buf.push_str("\\\\"),
b'\n' => buf.push_str("\\n"),
b'\r' => buf.push_str("\\r"),
b'\t' => buf.push_str("\\t"),
b'\x08' => buf.push_str("\\b"),
b'\x0c' => buf.push_str("\\f"),
_ => {
let _ = write!(buf, "\\u{:04x}", b as u32);
}
}
start = i + 1;
}
}
if start < bytes.len() {
buf.push_str(&s[start..]);
}
}
}
pub fn object_map_from<const N: usize>(items: [(Arc<str>, Value); N]) -> ObjectMap {
let mut m = ObjectMap::default();
for (k, v) in items {
m.insert(k, v);
}
m
}
pub use tishlang_core::FloatExt;
pub use tishlang_core::SingleCore;
pub mod ops {
use crate::Value;
use alloc::string::String;
#[inline]
pub fn add(left: &Value, right: &Value) -> Value {
match (left, right) {
(Value::Number(a), Value::Number(b)) => Value::Number(a + b),
(Value::String(a), Value::String(b)) => {
let mut s = String::with_capacity(a.len() + b.len());
s.push_str(a);
s.push_str(b);
Value::String(s.into())
}
(Value::String(a), b) => {
let b_str = b.to_js_string();
let mut s = String::with_capacity(a.len() + b_str.len());
s.push_str(a);
s.push_str(&b_str);
Value::String(s.into())
}
(a, Value::String(b)) => {
let a_str = a.to_js_string();
let mut s = String::with_capacity(a_str.len() + b.len());
s.push_str(&a_str);
s.push_str(b);
Value::String(s.into())
}
(a, b) => Value::Number(
a.as_number().unwrap_or(f64::NAN) + b.as_number().unwrap_or(f64::NAN),
),
}
}
macro_rules! num_op {
($name:ident, $op:tt) => {
#[inline]
pub fn $name(left: &Value, right: &Value) -> Value {
match (left, right) {
(Value::Number(a), Value::Number(b)) => Value::Number(a $op b),
(a, b) => Value::Number(
a.as_number().unwrap_or(f64::NAN) $op b.as_number().unwrap_or(f64::NAN),
),
}
}
};
}
num_op!(sub, -);
num_op!(mul, *);
num_op!(div, /);
num_op!(modulo, %);
macro_rules! cmp_op {
($name:ident, $op:tt) => {
#[inline]
pub fn $name(left: &Value, right: &Value) -> Value {
let b = match (left, right) {
(Value::Number(a), Value::Number(b)) => a $op b,
(Value::String(a), Value::String(b)) => a.as_str() $op b.as_str(),
_ => false,
};
Value::Bool(b)
}
};
}
cmp_op!(lt, <);
cmp_op!(le, <=);
cmp_op!(gt, >);
cmp_op!(ge, >=);
}
pub use tishlang_builtins::globals::{
array_from, array_is_array, array_of, boolean, decode_uri, decode_uri_component, encode_uri,
encode_uri_component, is_finite, is_nan, number_convert, number_is_finite, number_is_integer,
number_is_nan, number_is_safe_integer, object_assign, object_entries, object_freeze,
object_from_entries, object_has_own, object_is, object_is_frozen, object_keys, object_values,
parse_float, parse_int, string_convert, string_from_char_code, structured_clone,
};
pub use tishlang_builtins::string::escape_html as string_escape_html_impl;
pub use tishlang_builtins::string::{
at as string_at_impl, char_at as string_char_at_impl,
char_code_at as string_char_code_at_impl, ends_with as string_ends_with_impl,
includes as string_includes_impl, index_of as string_index_of_impl,
slice as string_slice_impl, starts_with as string_starts_with_impl,
substr as string_substr_impl, substring as string_substring_impl,
to_lower_case as string_to_lower_case, to_upper_case as string_to_upper_case,
trim as string_trim, trim_end as string_trim_end, trim_start as string_trim_start,
};
#[inline]
pub fn string_index_of(s: &Value, search: &Value, from: &Value) -> Value {
string_index_of_impl(s, search, Some(from))
}
#[inline]
pub fn string_includes(s: &Value, search: &Value, from: &Value) -> Value {
string_includes_impl(s, search, Some(from))
}
#[inline]
pub fn string_slice(s: &Value, start: &Value, end: &Value) -> Value {
string_slice_impl(s, start, end)
}
#[inline]
pub fn string_substring(s: &Value, start: &Value, end: &Value) -> Value {
string_substring_impl(s, start, end)
}
#[inline]
pub fn string_substr(s: &Value, start: &Value, length: &Value) -> Value {
string_substr_impl(s, start, length)
}
#[inline]
pub fn string_starts_with(s: &Value, search: &Value, position: Option<&Value>) -> Value {
string_starts_with_impl(s, search, position)
}
#[inline]
pub fn string_ends_with(s: &Value, search: &Value, end_position: Option<&Value>) -> Value {
string_ends_with_impl(s, search, end_position)
}
#[inline]
pub fn string_char_at(s: &Value, idx: &Value) -> Value {
string_char_at_impl(s, idx)
}
#[inline]
pub fn string_at(s: &Value, idx: &Value) -> Value {
string_at_impl(s, idx)
}
#[inline]
pub fn string_char_code_at(s: &Value, idx: &Value) -> Value {
string_char_code_at_impl(s, idx)
}
pub use tishlang_builtins::construct::{
array_construct, audio_context_constructor_value as tish_audio_context_constructor,
construct as tish_construct, error_constructor_value as tish_error_constructor,
};
pub use tishlang_builtins::date::date_constructor_value as tish_date_constructor;
pub use tishlang_builtins::collections::{
map_constructor_value as tish_map_constructor, map_get, map_has, map_set, map_values,
set_constructor_value as tish_set_constructor,
};
pub use tishlang_builtins::array::{
at as array_at, concat as array_concat_impl, every as array_every, filter as array_filter,
find as array_find, find_index as array_find_index, find_last as array_find_last,
find_last_index as array_find_last_index, flat as array_flat_impl, flat_map as array_flat_map,
for_each as array_for_each, includes as array_includes_impl, index_of as array_index_of_impl,
join as array_join_impl, map as array_map, pop as array_pop, push as array_push_impl,
fill as array_fill, last_index_of as array_last_index_of, copy_within as array_copy_within,
reduce as array_reduce, reduce_right as array_reduce_right, reverse as array_reverse,
keys as array_keys, values as array_values, entries as array_entries, shift as array_shift,
slice as array_slice_impl, snapshot_values as array_snapshot_values,
as_f64_snapshot as array_as_f64_snapshot, some as array_some,
sort_by_keys as array_sort_by_keys, sort_default as array_sort_default,
sort_numeric_asc as array_sort_numeric_asc, sort_numeric_desc as array_sort_numeric_desc,
sort_with_comparator as array_sort_with_comparator, splice as array_splice_impl,
to_reversed as array_to_reversed, to_sorted as array_to_sorted,
to_spliced as array_to_spliced, with as array_with, unshift as array_unshift_impl,
};
#[inline]
pub fn array_push(arr: &Value, args: &[Value]) -> Value { array_push_impl(arr, args) }
#[inline]
pub fn array_unshift(arr: &Value, args: &[Value]) -> Value { array_unshift_impl(arr, args) }
#[inline]
pub fn array_index_of(arr: &Value, search: &Value, from: Option<&Value>) -> Value {
array_index_of_impl(arr, search, from)
}
#[inline]
pub fn array_includes(arr: &Value, search: &Value, from: &Value) -> Value {
array_includes_impl(arr, search, Some(from))
}
#[inline]
pub fn array_join(arr: &Value, sep: &Value) -> Value { array_join_impl(arr, sep) }
#[inline]
pub fn array_splice(arr: &Value, start: &Value, delete_count: Option<&Value>, items: &[Value]) -> Value {
array_splice_impl(arr, start, delete_count, items)
}
#[inline]
pub fn array_slice(arr: &Value, start: &Value, end: &Value) -> Value {
array_slice_impl(arr, start, end)
}
#[inline]
pub fn array_concat(arr: &Value, args: &[Value]) -> Value { array_concat_impl(arr, args) }
#[inline]
pub fn array_flat(arr: &Value, depth: &Value) -> Value { array_flat_impl(arr, depth) }
#[inline]
pub fn array_sort(arr: &Value, comparator: Option<&Value>) -> Value {
match comparator {
Some(cmp) => array_sort_with_comparator(arr, cmp),
None => array_sort_default(arr),
}
}
#[inline]
pub fn value_at(recv: &Value, idx: &Value) -> Value {
match recv {
Value::String(_) => string_at_impl(recv, idx),
_ => array_at(recv, idx),
}
}
pub use tishlang_builtins::symbol::symbol_object;
pub use tishlang_builtins::typedarrays::{
float32_array_constructor_value as tish_float32_array_constructor,
float64_array_constructor_value as tish_float64_array_constructor,
int16_array_constructor_value as tish_int16_array_constructor,
int32_array_constructor_value as tish_int32_array_constructor,
int8_array_constructor_value as tish_int8_array_constructor,
uint16_array_constructor_value as tish_uint16_array_constructor,
uint32_array_constructor_value as tish_uint32_array_constructor,
uint8_array_constructor_value as tish_uint8_array_constructor,
uint8_clamped_array_constructor_value as tish_uint8_clamped_array_constructor,
};
macro_rules! math_fwd {
($($name:ident => $path:path),* $(,)?) => {
$(
#[inline]
pub fn $name(args: &[Value]) -> Value { $path(args) }
)*
};
}
math_fwd! {
math_abs => tishlang_builtins::math::abs,
math_ceil => tishlang_builtins::math::ceil,
math_floor => tishlang_builtins::math::floor,
math_round => tishlang_builtins::math::round,
math_sqrt => tishlang_builtins::math::sqrt,
math_sin => tishlang_builtins::math::sin,
math_cos => tishlang_builtins::math::cos,
math_tan => tishlang_builtins::math::tan,
math_asin => tishlang_builtins::math::asin,
math_acos => tishlang_builtins::math::acos,
math_atan => tishlang_builtins::math::atan,
math_atan2 => tishlang_builtins::math::atan2,
math_log => tishlang_builtins::math::log,
math_log2 => tishlang_builtins::math::log2,
math_log10 => tishlang_builtins::math::log10,
math_exp => tishlang_builtins::math::exp,
math_expm1 => tishlang_builtins::math::expm1,
math_log1p => tishlang_builtins::math::log1p,
math_cbrt => tishlang_builtins::math::cbrt,
math_trunc => tishlang_builtins::math::trunc,
math_sign => tishlang_builtins::math::sign,
math_pow => tishlang_builtins::math::pow,
math_max => tishlang_builtins::math::max,
math_min => tishlang_builtins::math::min,
math_hypot => tishlang_builtins::math::hypot,
math_imul => tishlang_builtins::math::imul,
math_clz32 => tishlang_builtins::math::clz32,
math_fround => tishlang_builtins::math::fround,
math_random => tishlang_builtins::math::random,
}
macro_rules! math_unary_libm {
($($name:ident => $m:ident),* $(,)?) => {
$(
#[inline]
pub fn $name(args: &[Value]) -> Value {
let n = match args.first() { Some(Value::Number(n)) => *n, _ => f64::NAN };
Value::Number(n.$m())
}
)*
};
}
math_unary_libm! {
math_sinh => sinh,
math_cosh => cosh,
math_tanh => tanh,
math_asinh => asinh,
math_acosh => acosh,
math_atanh => atanh,
}
pub mod gba;