use crate::ast::PolydatNode;
use crate::compile::assembly::WireRef;
use crate::library::identity::ConstU64;
use crate::dsl::registry;
#[derive(Clone)]
pub enum ConstArg {
Int(u64),
Float(f64),
Str(String),
List(Vec<ConstArg>),
Opaque(std::sync::Arc<dyn std::any::Any + Send + Sync>),
}
impl std::fmt::Debug for ConstArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConstArg::Int(v) => f.debug_tuple("Int").field(v).finish(),
ConstArg::Float(v) => f.debug_tuple("Float").field(v).finish(),
ConstArg::Str(s) => f.debug_tuple("Str").field(s).finish(),
ConstArg::List(l) => f.debug_tuple("List").field(l).finish(),
ConstArg::Opaque(_) => f.write_str("Opaque(..)"),
}
}
}
impl ConstArg {
pub fn as_opaque<T: std::any::Any + Send + Sync>(&self) -> Option<std::sync::Arc<T>> {
match self {
ConstArg::Opaque(v) => v.clone().downcast::<T>().ok(),
_ => None,
}
}
pub fn as_u64(&self) -> u64 {
match self {
ConstArg::Int(v) => *v,
_ => 0,
}
}
pub fn as_f64(&self) -> f64 {
match self {
ConstArg::Float(v) => *v,
ConstArg::Int(v) => *v as f64,
_ => 0.0,
}
}
pub fn as_str(&self) -> &str {
match self {
ConstArg::Str(s) => s,
_ => "",
}
}
}
#[derive(Clone, Debug, Default)]
pub struct BuildContext {
bindings: Vec<String>,
resources: crate::resource::ResourceScope,
}
impl BuildContext {
pub fn new(bindings: Vec<String>, resources: crate::resource::ResourceScope) -> Self {
Self {
bindings,
resources,
}
}
pub fn with_binding(name: impl Into<String>) -> Self {
Self::new(vec![name.into()], crate::resource::ResourceScope::new())
}
pub fn binding(&self) -> Option<&str> {
self.bindings.last().map(String::as_str)
}
pub fn bindings(&self) -> &[String] {
&self.bindings
}
pub fn resources(&self) -> &crate::resource::ResourceScope {
&self.resources
}
}
pub fn build_node(
ctx: &BuildContext,
func: &str,
wires: &[WireRef],
wire_types: &[crate::ast::PortType],
consts: &[ConstArg],
) -> Result<Box<dyn PolydatNode>, String> {
for factory in registry::factories() {
let Some(sig) = factory.signatures().iter().find(|s| s.name == func) else {
continue;
};
if let Err(msg) = check_param_constraints(sig, consts) {
return Err(format!("bad constant {func}: {msg}"));
}
if let Err(reason) = factory.validate(func, consts) {
return Err(format!("bad constant {func}: {reason}"));
}
return factory.build(ctx, func, wires, wire_types, consts);
}
let mut msg = format!("unknown function: '{func}'\n");
if let Some(suggestion) = registry::suggest_function(func) {
msg.push_str(&format!("\n Did you mean '{suggestion}'?"));
}
msg.push_str("\n\n This function is not registered in the wiring function library.");
msg.push_str("\n See the registered wiring functions for the available names.");
Err(msg)
}
pub(crate) fn variadic_node(
sig: ®istry::FuncSig,
wire_count: usize,
) -> Result<Box<dyn PolydatNode>, String> {
let func = sig.name;
if !sig.is_variadic() {
return Err(format!(
"'{func}' is registered, but its registration builds no node for it"
));
}
if wire_count == 0 {
return match sig.identity {
Some(id) => Ok(Box::new(ConstU64::new(id))),
None => Err(format!(
"variadic function '{func}' requires at least one input"
)),
};
}
match sig.variadic_ctor {
Some(ctor) => Ok(ctor(wire_count)),
None => Err(format!(
"variadic function '{func}' is registered without a variadic constructor"
)),
}
}
fn check_param_constraints(
sig: &crate::dsl::registry::FuncSig,
consts: &[ConstArg],
) -> Result<(), String> {
use crate::ast::SlotType;
let mut const_idx = 0usize;
for spec in sig.params {
if matches!(spec.slot_type, SlotType::Wire) {
continue;
}
if let Some(constraint) = &spec.constraint
&& let Some(arg) = consts.get(const_idx)
{
constraint.check(arg, spec.name)?;
}
const_idx += 1;
}
Ok(())
}