talc/talc-std/src/value.rs

184 lines
5.1 KiB
Rust

use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{exception::{exception, Result}, lformat, parse_float, parse_int, symbol::SYM_TYPE_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("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_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_TYPE_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_TYPE_ERROR, "could not parse {s:#} as integer")),
(Value::String(s), b"float")
=> parse_float(s.as_ref())
.map(f64::into)
.map_err(|_| exception!(*SYM_TYPE_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<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())
},
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<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(lformat!("{val}").into())
}
#[native_func(1)]
pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
Ok(lformat!("{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))
}