talc/talc-std/src/value.rs

179 lines
5.0 KiB
Rust

use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{HashValue, Rational64, Value}, Vm, exception::Result};
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), "int") => Ok(Value::Int(*x.numer() / *x.denom())),
(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), "int") => Ok(Value::Int(x as i64)),
(Value::Float(x), "ratio") => {
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))
}
(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 = 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<Vec<Value>> = l.into_iter()
.map(copy_inner)
.collect();
Ok(v?.into())
},
Value::Table(t) => {
let t = Rc::unwrap_or_clone(t).take();
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))
}