2024-02-27 00:18:43 -05:00
|
|
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|
|
|
|
2024-03-30 12:21:09 -04:00
|
|
|
use talc_lang::{exception::{exception, Result}, lformat, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm};
|
2024-02-27 00:18:43 -05:00
|
|
|
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<Value>) -> Result<Value> {
|
|
|
|
let [_, val] = unpack_args!(args);
|
|
|
|
Ok(val.get_type().into())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[native_func(2)]
|
|
|
|
pub fn is(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
|
|
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<Value>) -> Result<Value> {
|
|
|
|
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)
|
|
|
|
}
|
2024-03-30 12:21:09 -04:00
|
|
|
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") => {
|
2024-02-28 11:47:15 -05:00
|
|
|
let r = Rational64::approximate_float(x)
|
|
|
|
.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "float {x:?} could not be converted to ratio"))?;
|
|
|
|
Ok(Value::Ratio(r))
|
|
|
|
}
|
2024-03-30 12:21:09 -04:00
|
|
|
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
|
2024-02-27 00:18:43 -05:00
|
|
|
|
2024-03-30 12:21:09 -04:00
|
|
|
(Value::String(s), b"int")
|
|
|
|
=> parse_int(s.as_ref(), 10)
|
|
|
|
.map(i64::into)
|
2024-02-27 00:18:43 -05:00
|
|
|
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as integer")),
|
|
|
|
|
2024-03-30 12:21:09 -04:00
|
|
|
(Value::String(s), b"float")
|
|
|
|
=> parse_float(s.as_ref())
|
|
|
|
.map(f64::into)
|
2024-02-27 00:18:43 -05:00
|
|
|
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as float")),
|
|
|
|
|
2024-03-30 12:21:09 -04:00
|
|
|
(v, _) => throw!(*SYM_TYPE_ERROR,
|
|
|
|
"cannot convert value of type {} to type {}", v.get_type().name(), ty.name())
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn copy_inner(value: Value) -> Result<Value> {
|
|
|
|
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) => {
|
2024-02-28 11:47:15 -05:00
|
|
|
let c = Rc::unwrap_or_clone(c).take();
|
2024-02-28 10:46:42 -05:00
|
|
|
let c = copy_inner(c)?;
|
2024-02-27 00:18:43 -05:00
|
|
|
Ok(RefCell::new(c).into())
|
|
|
|
},
|
|
|
|
Value::List(l) => {
|
2024-02-28 11:47:15 -05:00
|
|
|
let l = Rc::unwrap_or_clone(l).take();
|
2024-02-27 00:18:43 -05:00
|
|
|
let v: Result<Vec<Value>> = l.into_iter()
|
|
|
|
.map(copy_inner)
|
|
|
|
.collect();
|
|
|
|
Ok(v?.into())
|
|
|
|
},
|
|
|
|
Value::Table(t) => {
|
2024-02-28 11:47:15 -05:00
|
|
|
let t = Rc::unwrap_or_clone(t).take();
|
2024-02-27 00:18:43 -05:00
|
|
|
let v: Result<HashMap<HashValue, Value>> = t.into_iter()
|
|
|
|
.map(|(k, v)| copy_inner(v).map(|v| (k, v)))
|
|
|
|
.collect();
|
|
|
|
Ok(v?.into())
|
|
|
|
},
|
2024-03-30 12:21:09 -04:00
|
|
|
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())
|
|
|
|
}
|
2024-02-27 00:18:43 -05:00
|
|
|
_ => throw!(*SYM_TYPE_ERROR,
|
|
|
|
"cannot copy value of type {}", value.get_type().name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[native_func(1)]
|
|
|
|
pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
|
|
let [_, val] = unpack_args!(args);
|
|
|
|
copy_inner(val)
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// strings
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#[native_func(1)]
|
|
|
|
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
|
|
let [_, val] = unpack_args!(args);
|
2024-03-30 12:21:09 -04:00
|
|
|
Ok(lformat!("{val}").into())
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[native_func(1)]
|
|
|
|
pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
|
|
let [_, val] = unpack_args!(args);
|
2024-03-30 12:21:09 -04:00
|
|
|
Ok(lformat!("{val:#}").into())
|
2024-02-27 00:18:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// cells
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#[native_func(1)]
|
|
|
|
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
|
|
let [_, value] = unpack_args!(args);
|
|
|
|
Ok(RefCell::new(value).into())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[native_func(1)]
|
|
|
|
pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
|
|
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<Value>) -> Result<Value> {
|
|
|
|
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<Value>) -> Result<Value> {
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
|