171 lines
4.6 KiB
Rust
171 lines
4.6 KiB
Rust
|
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<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)
|
||
|
}
|
||
|
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<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) => {
|
||
|
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<Vec<Value>> = l.into_iter()
|
||
|
.map(copy_inner)
|
||
|
.collect();
|
||
|
Ok(v?.into())
|
||
|
},
|
||
|
Value::Table(t) => {
|
||
|
let t = talc_lang::value::cell_take(t);
|
||
|
let v: Result<HashMap<HashValue, Value>> = 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<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);
|
||
|
Ok(val.to_string().into())
|
||
|
}
|
||
|
|
||
|
#[native_func(1)]
|
||
|
pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||
|
let [_, val] = unpack_args!(args);
|
||
|
Ok(format!("{val:#}").into())
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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))
|
||
|
}
|
||
|
|