use crate::ast::{PortType, Value};
use crate::kernel::{Construction, Dataflow, Metadata, PolydatKernel};
impl Metadata for PolydatKernel {
#[inline]
fn find_input(&self, name: &str) -> Option<usize> {
self.program().find_input(name)
}
#[inline]
fn input_names(&self) -> Vec<String> {
self.program().input_names()
}
#[inline]
fn output_names(&self) -> Vec<String> {
self.program()
.output_names()
.iter()
.map(|s| s.to_string())
.collect()
}
#[inline]
fn coord_count(&self) -> usize {
self.program().coord_count()
}
#[inline]
fn input_port_type(&self, name: &str) -> Option<PortType> {
self.program().input_port_type(name)
}
#[inline]
fn input_port_type_by_idx(&self, idx: usize) -> Option<PortType> {
self.program().input_port_type_by_idx(idx)
}
#[inline]
fn output_port_type(&self, name: &str) -> Option<PortType> {
self.program().output_port_type(name)
}
}
impl Dataflow for PolydatKernel {
fn set_wire_idx(
&mut self,
idx: usize,
value: Value,
) -> Result<(), crate::kernel::api::WriteError> {
use crate::kernel::api::WriteError;
let slot_type = match self.program().input_port_type_by_idx(idx) {
Some(t) => t,
None => {
return Err(WriteError::UnknownWire {
key: format!("wire[{idx}]"),
});
}
};
let slot_name = self
.program()
.input_name_by_idx(idx)
.map(|s| s.to_string())
.unwrap_or_else(|| format!("wire[{idx}]"));
let got = value.port_type();
let adapted = crate::kernel::state::adapt_boundary_value(&slot_name, slot_type, value);
if !adapted.satisfies_slot(slot_type) {
return Err(WriteError::TypeMismatch {
slot: slot_name,
expected: slot_type,
got,
});
}
self.state().set_input(idx, adapted);
Ok(())
}
#[inline]
fn get_wire_idx(&self, idx: usize) -> Value {
self.state_ref().get_input(idx)
}
}
impl Construction for PolydatKernel {
type Error = crate::kernel::subcontext::ContractViolation;
fn root(matter: crate::kernel::subcontext::PolydatMatter<'_>) -> Result<Self, Self::Error> {
use crate::kernel::subcontext::PolydatMatterInner;
match matter.inner {
PolydatMatterInner::Source(s) => {
let options = crate::dsl::compile::CompileOptions {
source_dir: s.options.workload_dir.clone(),
lib_paths: s.options.polydat_lib_paths,
required_outputs: s.options.required_outputs.clone(),
strict: s.options.strict,
context: s
.options
.context_label
.clone()
.unwrap_or_else(|| s.label.clone()),
cursor_limit: s.options.cursor_limit,
ledger: None,
};
crate::dsl::compile::compile_polydat_with_options(&s.body, &options, None)
.map_err(crate::kernel::subcontext::ContractViolation::Compile)
}
PolydatMatterInner::Statements(s) => {
let file = crate::dsl::ast::PolydatFile {
statements: s.statements,
};
let options = crate::dsl::compile::CompileOptions {
source_dir: s.options.workload_dir.clone(),
lib_paths: s.options.polydat_lib_paths,
required_outputs: s.options.required_outputs.clone(),
strict: s.options.strict,
context: s
.options
.context_label
.clone()
.unwrap_or_else(|| s.label.clone()),
cursor_limit: None,
ledger: None,
};
crate::dsl::compile::compile_ast_with_options(&file, "", &options, None)
.map_err(crate::kernel::subcontext::ContractViolation::Compile)
}
PolydatMatterInner::Program(p) => {
let mut k = PolydatKernel::from_program(p.program);
for (var, value) in p.iter_bindings {
if let Some(idx) = k.program().find_input(var) {
k.state().set_input(idx, value.clone());
}
}
Ok(k)
}
}
}
fn subscope(
&self,
matter: crate::kernel::subcontext::PolydatMatter<'_>,
) -> Result<Self, Self::Error> {
PolydatKernel::build_subscope(self, matter)
}
}
impl crate::kernel::Kernel for PolydatKernel {
fn engine(&self) -> crate::compile::select::Engine {
crate::compile::select::Engine::Interpreter(self.program().cone_mode())
}
fn set_inputs(&mut self, coords: &[u64]) {
PolydatKernel::set_inputs(self, coords);
}
fn set_input(&mut self, name: &str, value: Value) -> Result<(), String> {
PolydatKernel::set_input(self, name, value)
}
fn set_cursor(
&mut self,
name: &str,
partition: &crate::iteration::cursor_partition::Partition,
) -> Result<(), String> {
PolydatKernel::set_cursor(self, name, partition)
}
fn eval(&mut self) {
for name in Metadata::output_names(self) {
let _ = PolydatKernel::pull(self, &name);
}
}
fn pull(&mut self, name: &str) -> Value {
PolydatKernel::pull(self, name).clone()
}
fn input_names(&self) -> Vec<String> {
Metadata::input_names(self)
}
fn output_names(&self) -> Vec<String> {
Metadata::output_names(self)
}
fn output_type(&self, name: &str) -> Option<PortType> {
Metadata::output_port_type(self, name)
}
fn externs(&self) -> Vec<(String, PortType)> {
let program = self.program();
Metadata::input_names(self)
.into_iter()
.enumerate()
.filter(|(i, _)| program.input_kind(*i) != Some(crate::kernel::InputKind::Coordinate))
.filter_map(|(i, name)| Metadata::input_port_type_by_idx(self, i).map(|t| (name, t)))
.collect()
}
fn cursor_schemas(&self) -> &[crate::iteration::source::SourceSchema] {
self.program().cursor_schemas()
}
fn input_value(&self, name: &str) -> Option<Value> {
let idx = self.program().find_input(name)?;
Some(self.state_ref().get_input(idx))
}
fn input_index(&self, name: &str) -> Option<usize> {
self.program().find_input(name)
}
fn set_input_at(&mut self, index: usize, value: Value) -> Result<(), String> {
PolydatKernel::set_input_at(self, index, value)
}
fn output_index(&self, name: &str) -> Option<usize> {
self.program().output_index(name)
}
fn pull_at(&mut self, index: usize) -> Value {
PolydatKernel::pull_by_index(self, index).clone()
}
fn traversals(&self) -> &[crate::dsl::traversal::Traversal] {
self.program().traversals()
}
fn plan(&self) -> crate::EnginePlan {
self.program().engine_plan()
}
fn traverse(&mut self, index: usize) -> Result<crate::kernel::TraversalStream, String> {
PolydatKernel::traverse(self, index)
}
fn invalidate_all(&mut self) {
self.state().invalidate_all();
}
fn shared_cells(&self) -> Vec<crate::kernel::SharedCellEntry> {
self.shared_cells_in_scope()
}
fn attach_shared_cell(
&mut self,
name: &str,
cell: crate::kernel::SharedCell,
) -> Result<(), String> {
let program = self.program().clone();
let shared = program.shared_outputs();
let idx = program.find_input(name).filter(|_| shared.contains(&name));
let Some(idx) = idx else {
return Err(format!(
"no `shared` binding named '{name}'; this kernel's shared bindings are {shared:?}"
));
};
self.state().attach_shared_cell(idx, cell);
Ok(())
}
fn into_program(self: Box<Self>) -> std::sync::Arc<dyn crate::kernel::KernelProgram> {
PolydatKernel::into_program(*self)
}
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
self.program().ledger()
}
}
impl crate::kernel::KernelInternals for PolydatKernel {
fn set_traversals(
&mut self,
traversals: Vec<crate::dsl::traversal::Traversal>,
producers: Vec<crate::dsl::traversal::Producer>,
) {
PolydatKernel::set_traversals(self, traversals, producers);
}
fn folded_value(&self, name: &str) -> Option<Value> {
self.get_constant(name).cloned()
}
fn set_cursor_extent(&mut self, index: usize, extent: u64) {
let mut schemas = self.program().cursor_schemas().to_vec();
if let Some(schema) = schemas.get_mut(index) {
schema.extent = Some(extent);
self.set_cursor_schemas(schemas);
}
}
}
impl crate::kernel::KernelProgram for crate::kernel::PolydatProgram {
fn engine(&self) -> crate::compile::select::Engine {
crate::compile::select::Engine::Interpreter(self.cone_mode())
}
fn as_interpreter(self: std::sync::Arc<Self>) -> Option<std::sync::Arc<Self>> {
Some(self)
}
fn create_kernel(self: std::sync::Arc<Self>) -> Box<dyn crate::kernel::Kernel> {
Box::new(PolydatKernel::from_program(self))
}
fn ledger(&self) -> &std::sync::Arc<crate::kernel::CompileLedger> {
crate::kernel::PolydatProgram::ledger(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dsl::compile::compile_polydat;
#[test]
fn dataflow_indexed_set_get() {
let mut k = compile_polydat("input cycle: u64\nconst x := 7\n").unwrap();
k.set_wire(0_usize, Value::U64(42)).expect("typed write");
assert_eq!(k.get_wire(0_usize), Some(Value::U64(42)));
}
#[test]
fn dataflow_named_set_get() {
let mut k = compile_polydat("input cycle: u64\nextern n: u64\n").unwrap();
k.set_wire("n", Value::U64(5)).expect("typed write");
match k.get_wire("n") {
Some(Value::U64(5)) => {}
other => panic!("expected U64(5), got {other:?}"),
}
}
#[test]
fn dataflow_string_key() {
let mut k = compile_polydat("input cycle: u64\nextern n: u64\n").unwrap();
let name = String::from("n");
k.set_wire(&name, Value::U64(99)).expect("typed write");
assert_eq!(k.get_wire(name.clone()), Some(Value::U64(99)));
}
#[test]
fn dataflow_unknown_name_safe() {
let mut k = compile_polydat("input cycle: u64\n").unwrap();
let err = k.set_wire("nonexistent", Value::U64(1)).unwrap_err();
assert!(matches!(
err,
crate::kernel::api::WriteError::UnknownWire { .. }
));
assert!(k.get_wire("nonexistent").is_none());
}
#[test]
fn dataflow_type_mismatch_rejected() {
let mut k = compile_polydat("input cycle: u64\nextern n: u64\n").unwrap();
let err = k
.set_wire(
"n",
Value::VecF32(crate::ast::SliceArc::from_vec(vec![1.0_f32, 2.0])),
)
.unwrap_err();
match err {
crate::kernel::api::WriteError::TypeMismatch {
slot,
expected,
got,
} => {
assert_eq!(slot, "n");
assert_eq!(expected, PortType::U64);
assert_eq!(got, PortType::VecF32);
}
other => panic!("expected TypeMismatch, got {other:?}"),
}
}
#[test]
fn vec_to_scalar_diagnostic_mentions_explicit_helpers() {
let err = crate::kernel::api::WriteError::TypeMismatch {
slot: "score".into(),
expected: PortType::F64,
got: PortType::VecF32,
};
let msg = err.to_string();
assert!(
msg.contains("reduction node"),
"missing reduction hint: {msg}"
);
assert!(msg.contains("vec_dot"), "missing vec_dot hint: {msg}");
}
#[test]
fn dataflow_healable_mismatch_adapts() {
let mut k = compile_polydat("input cycle: u64\nextern x: f64\n").unwrap();
k.set_wire("x", Value::U64(42))
.expect("u64→f64 boundary adapter");
match k.get_wire("x") {
Some(Value::F64(42.0)) => {}
other => panic!("expected adapted F64(42.0), got {other:?}"),
}
}
#[test]
fn dataflow_none_passes_through_any_slot() {
let mut k = compile_polydat("input cycle: u64\nextern n: u64\n").unwrap();
k.set_wire("n", Value::None).expect("None always permitted");
}
#[test]
fn metadata_listings() {
let k = compile_polydat("input (cycle: u64, thread: u64)\nextern n: u64\nconst x := 7\n")
.unwrap();
let inputs: Vec<String> = k.input_names();
assert!(inputs.iter().any(|s| s == "cycle"));
assert!(inputs.iter().any(|s| s == "n"));
assert_eq!(k.coord_count(), 2); assert!(k.find_input("n").is_some());
assert_eq!(k.input_port_type("n"), Some(PortType::U64));
}
#[test]
fn construction_symmetric_paths() {
let root_opts = crate::kernel::subcontext::CompileOptions {
workload_dir: None,
polydat_lib_paths: Vec::new(),
strict: false,
required_outputs: Vec::new(),
context_label: Some("root".to_string()),
cursor_limit: None,
..Default::default()
};
let root_matter = crate::kernel::subcontext::PolydatMatter::builder()
.label("root")
.source("input cycle: u64\nshared flag := 0\n")
.options(root_opts)
.build()
.expect("matter build");
let root =
<PolydatKernel as Construction>::root(root_matter).expect("root from source matter");
let sub_opts = crate::kernel::subcontext::CompileOptions {
workload_dir: None,
polydat_lib_paths: Vec::new(),
strict: false,
required_outputs: Vec::new(),
context_label: Some("sub".to_string()),
cursor_limit: None,
..Default::default()
};
let sub_matter = crate::kernel::subcontext::PolydatMatter::builder()
.label("sub")
.source("input cycle: u64\n")
.options(sub_opts)
.build()
.expect("matter build");
let _sub = root
.subscope(sub_matter)
.expect("subscope from source matter");
}
#[test]
fn construction_root_from_program() {
let template = compile_polydat("input cycle: u64\nextern n: u64\n").unwrap();
let program = template.program().clone();
let matter = crate::kernel::subcontext::PolydatMatter::builder()
.program(program)
.build()
.expect("matter build");
let mut root =
<PolydatKernel as Construction>::root(matter).expect("root from program matter");
root.set_wire("n", Value::U64(13)).expect("set_wire");
assert_eq!(root.get_wire("n"), Some(Value::U64(13)));
}
#[test]
fn builder_rejects_multiple_forms() {
let template = compile_polydat("input cycle: u64\n").unwrap();
match crate::kernel::subcontext::PolydatMatter::builder()
.source("input cycle: u64\n")
.program(template.program().clone())
.build()
{
Err(msg) => assert!(
msg.contains("multiple"),
"expected multiple-forms error, got: {msg}"
),
Ok(_) => panic!("multiple forms must error"),
}
}
#[test]
fn builder_rejects_empty() {
match crate::kernel::subcontext::PolydatMatter::builder().build() {
Err(msg) => assert!(
msg.contains("no input form"),
"expected no-form error, got: {msg}"
),
Ok(_) => panic!("empty matter must error"),
}
}
}