use std::sync::Arc;
use sim_kernel::{
AbiVersion, ClassId, DefaultFactory, Dependency, Export, Expr, Factory, Lib, LibManifest,
LibTarget, Linker, NumberDomain, NumberLiteral, Object, Result, Symbol, Value,
ValuePromotionRule, Version,
};
use sim_lib_numbers_cas::CasExpr;
use sim_lib_numbers_core::{DomainNumberValueShape, domains};
use sim_shape::shape_value;
use super::function::{
CallFunction, FnBuilder, GradFunction, build_func_class, call_symbol, fn_symbol, grad_symbol,
};
use super::value::{Func, FuncMetadata, build_constant_func_value, build_func_value};
pub fn func_domain_symbol() -> Symbol {
domains::func()
}
pub fn func_class_symbol() -> Symbol {
domains::domain("Func")
}
pub fn value_shape_symbol() -> Symbol {
sim_lib_numbers_core::value_shape_symbol(&func_domain_symbol())
}
#[sim_citizen_derive::non_citizen(
reason = "numbers/func number-domain marker; reconstruct by loading the function number lib",
kind = "marker"
)]
pub struct FuncNumberDomain;
impl NumberDomain for FuncNumberDomain {
fn symbol(&self) -> Symbol {
func_domain_symbol()
}
fn parse_priority(&self) -> i32 {
-100
}
fn parse_literal(&self, _cx: &mut sim_kernel::Cx, _text: &str) -> Result<Option<Value>> {
Ok(None)
}
fn encode_literal(
&self,
_cx: &mut sim_kernel::Cx,
_value: Value,
) -> Result<Option<NumberLiteral>> {
Ok(None)
}
}
impl Object for FuncNumberDomain {
fn display(&self, _cx: &mut sim_kernel::Cx) -> Result<String> {
Ok("#<number-domain numbers/func>".to_owned())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl sim_kernel::ObjectCompat for FuncNumberDomain {
fn class(&self, cx: &mut sim_kernel::Cx) -> Result<sim_kernel::ClassRef> {
sim_lib_numbers_core::number_domain_class_stub(cx)
}
fn as_expr(&self, _cx: &mut sim_kernel::Cx) -> Result<Expr> {
Ok(Expr::Symbol(func_domain_symbol()))
}
fn as_table(&self, cx: &mut sim_kernel::Cx) -> Result<Value> {
let value_shape = cx
.registry()
.shape_by_symbol(&value_shape_symbol())
.cloned()
.unwrap_or(cx.factory().symbol(value_shape_symbol())?);
let func_class = cx
.registry()
.class_by_symbol(&func_class_symbol())
.cloned()
.unwrap_or(cx.factory().symbol(func_class_symbol())?);
cx.factory().table(vec![
(
Symbol::new("symbol"),
cx.factory().symbol(func_domain_symbol())?,
),
(
Symbol::new("kind"),
cx.factory().string("number-domain".to_owned())?,
),
(
Symbol::new("numeric-family"),
cx.factory().string("function".to_owned())?,
),
(
Symbol::new("canonical-form"),
cx.factory()
.string("callable symbolic function".to_owned())?,
),
(
Symbol::new("parse-priority"),
cx.factory().string("-100".to_owned())?,
),
(Symbol::new("constructor-class"), func_class),
(Symbol::new("value-shape"), value_shape),
(Symbol::new("builder"), cx.factory().symbol(fn_symbol())?),
(Symbol::new("call"), cx.factory().symbol(call_symbol())?),
(Symbol::new("grad"), cx.factory().symbol(grad_symbol())?),
])
}
fn as_number_domain(&self) -> Option<&dyn NumberDomain> {
Some(self)
}
}
pub struct FuncNumbersLib;
impl FuncNumbersLib {
pub fn new() -> Self {
Self
}
}
impl Default for FuncNumbersLib {
fn default() -> Self {
Self::new()
}
}
impl Lib for FuncNumbersLib {
fn manifest(&self) -> LibManifest {
LibManifest {
id: func_domain_symbol(),
version: Version(env!("CARGO_PKG_VERSION").to_owned()),
abi: AbiVersion { major: 0, minor: 1 },
target: LibTarget::HostRegistered,
requires: Vec::<Dependency>::new(),
capabilities: Vec::new(),
exports: vec![
Export::NumberDomain {
symbol: func_domain_symbol(),
number_domain_id: None,
},
Export::Class {
symbol: func_class_symbol(),
class_id: None,
},
Export::Shape {
symbol: value_shape_symbol(),
shape_id: None,
},
Export::Function {
symbol: fn_symbol(),
function_id: None,
},
Export::Function {
symbol: call_symbol(),
function_id: None,
},
Export::Function {
symbol: grad_symbol(),
function_id: None,
},
],
}
}
fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
let value_shape = Arc::new(DomainNumberValueShape::new(
func_domain_symbol(),
"FuncValue",
[
"callable number value in the numbers/func domain",
"accepts any NumberValue where domain == numbers/func",
],
));
linker.number_domain_value(
func_domain_symbol(),
DefaultFactory
.opaque(Arc::new(FuncNumberDomain))
.expect("number domain should be boxable"),
)?;
register_func_value_class(linker)?;
linker.shape_value(
value_shape_symbol(),
shape_value(value_shape_symbol(), value_shape),
)?;
for (symbol, value) in [
(
fn_symbol(),
DefaultFactory
.opaque(Arc::new(FnBuilder))
.expect("fn builder should be boxable"),
),
(
call_symbol(),
DefaultFactory
.opaque(Arc::new(CallFunction))
.expect("call helper should be boxable"),
),
(
grad_symbol(),
DefaultFactory
.opaque(Arc::new(GradFunction))
.expect("grad helper should be boxable"),
),
] {
linker.function_value(symbol, value)?;
}
for from_domain in promoted_domains() {
linker.value_promotion_rule(ValuePromotionRule {
from_domain,
to_domain: func_domain_symbol(),
cost: 1,
convert: promote_value_to_func,
});
}
super::value::register_value_ops(linker);
Ok(())
}
}
fn register_func_value_class(linker: &mut Linker<'_>) -> Result<ClassId> {
let func_class = build_func_class();
let class_id = linker.class_value(
func_class_symbol(),
DefaultFactory
.opaque(func_class.clone())
.expect("function class should be boxable"),
)?;
func_class.set_id(class_id);
Ok(class_id)
}
fn install_func_value_citizen(linker: &mut Linker<'_>) -> Result<()> {
register_func_value_class(linker).map(|_| ())
}
fn conformance_func_value_citizen(cx: &mut sim_kernel::Cx) -> Result<()> {
let var = Symbol::new("x");
let value = build_func_value(
cx,
Func::new(
vec![var.clone()],
Some(CasExpr::Var(var)),
None,
FuncMetadata::default(),
),
)?;
sim_citizen::check_value_fixture(cx, value)
}
sim_citizen::inventory::submit! {
sim_citizen::CitizenInfo {
symbol: "numbers/Func",
version: 0,
crate_name: env!("CARGO_PKG_NAME"),
arity: 2,
install: install_func_value_citizen,
conformance: conformance_func_value_citizen,
}
}
fn promoted_domains() -> Vec<Symbol> {
vec![
domains::bool(),
domains::f32(),
domains::f64(),
domains::i64(),
domains::bigint(),
domains::rational(),
domains::complex(),
domains::cas(),
domains::continued_fraction(),
]
}
fn promote_value_to_func(cx: &mut sim_kernel::Cx, value: Value) -> Result<Value> {
build_constant_func_value(cx, value)
}