use std::{cell::RefCell, collections::HashMap, rc::Rc}; use talc_lang::{ exception::{exception, Result}, lformat, parser::{parse_float, parse_int}, symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, HashValue, Rational64, 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("symbol_name", symbol_name().into()); vm.set_global_name("symbol_of", symbol_of().into()); vm.set_global_name("symbol_exists", symbol_exists().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_take().into()); vm.set_global_name("func_state", func_state().into()); vm.set_global_name("func_arity", func_arity().into()); vm.set_global_name("func_name", func_name().into()); vm.set_global_name("apply", apply().into()); vm.set_global_name("compile", compile().into()); } // // types // #[native_func(1, "type")] 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, "as")] 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_bytes()) { (_, b"nil") => Ok(Value::Nil), (v, b"string") => Ok(Value::String(lformat!("{v}").into())), (v, b"bool") => Ok(Value::Bool(v.truthy())), (Value::Symbol(s), b"int") => Ok(Value::Int(s.id() as i64)), (Value::Int(x), b"ratio") => Ok(Value::Ratio(x.into())), (Value::Int(x), b"float") => Ok(Value::Float(x as f64)), (Value::Int(x), b"complex") => Ok(Value::Complex((x as f64).into())), (Value::Ratio(x), b"int") => Ok(Value::Int(x.to_integer())), (Value::Ratio(x), b"float") => Ok(Value::Float(x.to_f64())), (Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())), (Value::Float(x), b"int") => Ok(Value::Int(x as i64)), (Value::Float(x), b"ratio") => { let r = Rational64::approximate_float(x).ok_or_else(|| { exception!( *SYM_VALUE_ERROR, "float {x:?} could not be converted to ratio" ) })?; Ok(Value::Ratio(r)) } (Value::Float(x), b"complex") => Ok(Value::Complex(x.into())), (Value::String(s), b"int") => parse_int(s.as_ref(), 10) .map(i64::into) .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")), (Value::String(s), b"float") => parse_float(s.as_ref()) .map(f64::into) .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")), (v, _) => throw!( *SYM_TYPE_ERROR, "cannot convert value of type {} to type {}", v.get_type().name(), ty.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 = Rc::unwrap_or_clone(c).take(); let c = copy_inner(c)?; Ok(RefCell::new(c).into()) } Value::List(l) => { let l = Rc::unwrap_or_clone(l).take(); let v: Result> = l.into_iter().map(copy_inner).collect(); Ok(v?.into()) } Value::Table(t) => { let t = Rc::unwrap_or_clone(t).take(); let v: Result> = t .into_iter() .map(|(k, v)| copy_inner(v).map(|v| (k, v))) .collect(); Ok(v?.into()) } Value::Native(ref n) => match n.copy_value()? { Some(x) => Ok(x), None => throw!( *SYM_TYPE_ERROR, "cannot copy value of type {}", value.get_type().name() ), }, _ => 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, "str")] pub fn str_(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); Ok(lformat!("{val}").into()) } #[native_func(1)] pub fn repr(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); Ok(lformat!("{val:#}").into()) } // // symbols // #[native_func(1)] pub fn symbol_name(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); let Value::Symbol(s) = val else { throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol") }; Ok(s.name().into()) } #[native_func(1)] pub fn symbol_of(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); let Value::String(s) = val else { throw!(*SYM_TYPE_ERROR, "symbol_of: expected string") }; Ok(Symbol::get(s.as_ref()).into()) } #[native_func(1)] pub fn symbol_exists(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); let Value::String(s) = val else { throw!(*SYM_TYPE_ERROR, "symbol_of: expected string") }; match Symbol::try_get(s.as_ref()) { Some(s) => Ok(s.into()), None => Ok(Value::Nil), } } // // 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, "uncell: 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, "cell_replace: 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, "cell_take: value is not a cell") }; Ok(cell.replace(Value::Nil)) } // // functions // #[native_func(1)] pub fn func_state(_: &mut Vm, args: Vec) -> Result { let [_, func] = unpack_args!(args); match func { Value::NativeFunc(_) => Ok(Value::Nil), Value::Function(f) => { let l: Vec = f.state.iter().map(|v| Value::Cell(v.clone())).collect(); Ok(l.into()) } _ => throw!( *SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function" ), } } #[native_func(1)] pub fn func_arity(_: &mut Vm, args: Vec) -> Result { let [_, func] = unpack_args!(args); let Some(attrs) = func.func_attrs() else { throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function") }; Ok((attrs.arity as i64).into()) } #[native_func(1)] pub fn func_name(_: &mut Vm, args: Vec) -> Result { let [_, func] = unpack_args!(args); let Some(attrs) = func.func_attrs() else { throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function") }; if let Some(name) = attrs.name { Ok(name.into()) } else { Ok(Value::Nil) } } #[native_func(2)] pub fn apply(vm: &mut Vm, args: Vec) -> Result { let [_, func, lst] = unpack_args!(args); if func.func_attrs().is_none() { throw!( *SYM_TYPE_ERROR, "apply: first argument must be a function, found {func:#}" ) } let Value::List(l) = lst else { throw!( *SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}" ) }; let mut args = l.borrow().clone(); args.insert(0, func.clone()); vm.call_value(func, args) } #[native_func(1)] pub fn compile(_: &mut Vm, args: Vec) -> Result { let [_, src] = unpack_args!(args); let Value::String(src) = src else { throw!( *SYM_TYPE_ERROR, "compile: argument must be a string, found {src:#}" ) }; let src = src.to_str().map_err(|e| { exception!( *SYM_VALUE_ERROR, "compile: argument must be valid unicode ({e})" ) })?; let ast = talc_lang::parser::parse(src).map_err(|e| exception!(symbol!("parse_error"), "{e}"))?; let func = talc_lang::compiler::compile(&ast, None); Ok(func.into()) }