use std::{cell::RefCell, collections::HashMap, rc::Rc}; use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{exception::Result, HashValue, Value}, Vm}; use talc_macros::native_func; use crate::unpack_args; pub fn load(vm: &mut Vm) { vm.set_global_name("type", type_().into()); vm.set_global_name("is", is().into()); vm.set_global_name("as", as_().into()); vm.set_global_name("copy", copy().into()); vm.set_global_name("str", str_().into()); vm.set_global_name("repr", repr().into()); vm.set_global_name("cell", cell().into()); vm.set_global_name("uncell", uncell().into()); vm.set_global_name("cell_replace", cell_replace().into()); vm.set_global_name("cell_take", cell_replace().into()); } // // types // #[native_func(1)] pub fn type_(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); Ok(val.get_type().into()) } #[native_func(2)] pub fn is(_: &mut Vm, args: Vec) -> Result { let [_, val, ty] = unpack_args!(args); let Value::Symbol(ty) = ty else { throw!(*SYM_TYPE_ERROR, "type expected symbol, got {ty:#}") }; Ok((val.get_type() == ty).into()) } #[native_func(2)] pub fn as_(_: &mut Vm, args: Vec) -> Result { let [_, val, ty] = unpack_args!(args); let Value::Symbol(ty) = ty else { throw!(*SYM_TYPE_ERROR, "type expected symbol, got {ty:#}") }; if val.get_type() == ty { return Ok(val) } match (val, ty.name().as_ref()) { (_, "nil") => Ok(Value::Nil), (v, "string") => Ok(Value::String(v.to_string().into())), (v, "bool") => Ok(Value::Bool(v.truthy())), (Value::Symbol(s), "int") => Ok(Value::Int(s.id() as i64)), (Value::Int(x), "ratio") => Ok(Value::Ratio(x.into())), (Value::Int(x), "float") => Ok(Value::Float(x as f64)), (Value::Int(x), "complex") => Ok(Value::Complex((x as f64).into())), (Value::Ratio(x), "float") => Ok(Value::Float(*x.numer() as f64 / *x.denom() as f64)), (Value::Ratio(x), "complex") => Ok(Value::Complex((*x.numer() as f64 / *x.denom() as f64).into())), (Value::Float(x), "complex") => Ok(Value::Complex(x.into())), (Value::String(s), "int") => parse_int(&s, 10) .map(|v| v.into()) .map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as integer")), (Value::String(s), "float") => parse_float(&s) .map(|v| v.into()) .map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as float")), (v, t) => throw!(*SYM_TYPE_ERROR, "cannot convert value of type {} to type {t}", v.get_type().name()) } } pub fn copy_inner(value: Value) -> Result { match value { Value::Nil | Value::Bool(_) | Value::Symbol(_) | Value::Int(_) | Value::Ratio(_) | Value::Float(_) | Value::Complex(_) | Value::Range(_) | Value::String(_) => Ok(value), Value::Cell(c) => { let c = copy_inner(talc_lang::value::cell_take(c))?; Ok(RefCell::new(c).into()) }, Value::List(l) => { let l = talc_lang::value::cell_take(l); let v: Result> = l.into_iter() .map(copy_inner) .collect(); Ok(v?.into()) }, Value::Table(t) => { let t = talc_lang::value::cell_take(t); let v: Result> = t.into_iter() .map(|(k, v)| copy_inner(v).map(|v| (k, v))) .collect(); Ok(v?.into()) }, _ => throw!(*SYM_TYPE_ERROR, "cannot copy value of type {}", value.get_type().name()) } } #[native_func(1)] pub fn copy(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); copy_inner(val) } // // strings // #[native_func(1)] pub fn str_(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); Ok(val.to_string().into()) } #[native_func(1)] pub fn repr(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); Ok(format!("{val:#}").into()) } // // cells // #[native_func(1)] pub fn cell(_: &mut Vm, args: Vec) -> Result { let [_, value] = unpack_args!(args); Ok(RefCell::new(value).into()) } #[native_func(1)] pub fn uncell(_: &mut Vm, args: Vec) -> Result { let [_, cell] = unpack_args!(args); let Value::Cell(cell) = cell else { throw!(*SYM_TYPE_ERROR, "value is not a cell") }; Ok(Rc::unwrap_or_clone(cell).into_inner()) } #[native_func(2)] pub fn cell_replace(_: &mut Vm, args: Vec) -> Result { let [_, cell, value] = unpack_args!(args); let Value::Cell(cell) = cell else { throw!(*SYM_TYPE_ERROR, "value is not a cell") }; Ok(cell.replace(value)) } #[native_func(1)] pub fn cell_take(_: &mut Vm, args: Vec) -> Result { let [_, cell] = unpack_args!(args); let Value::Cell(cell) = cell else { throw!(*SYM_TYPE_ERROR, "value is not a cell") }; Ok(cell.replace(Value::Nil)) }