1use crate::kernel::{Dataflow, PolydatKernel, Metadata, Construction};
14use crate::ast::{PortType, Value};
15
16impl Metadata for PolydatKernel {
17 #[inline]
18 fn find_input(&self, name: &str) -> Option<usize> {
19 self.program().find_input(name)
20 }
21
22 #[inline]
23 fn input_names(&self) -> Vec<String> {
24 self.program().input_names()
25 }
26
27 #[inline]
28 fn output_names(&self) -> Vec<String> {
29 self.program().output_names().iter().map(|s| s.to_string()).collect()
30 }
31
32 #[inline]
33 fn coord_count(&self) -> usize {
34 self.program().coord_count()
35 }
36
37 #[inline]
38 fn input_port_type(&self, name: &str) -> Option<PortType> {
39 self.program().input_port_type(name)
40 }
41
42 #[inline]
43 fn input_port_type_by_idx(&self, idx: usize) -> Option<PortType> {
44 self.program().input_port_type_by_idx(idx)
45 }
46
47 #[inline]
48 fn output_port_type(&self, name: &str) -> Option<PortType> {
49 self.program().output_port_type(name)
50 }
51}
52
53impl Dataflow for PolydatKernel {
54 fn set_wire_idx(&mut self, idx: usize, value: Value) -> Result<(), crate::kernel::api::WriteError> {
55 use crate::kernel::api::WriteError;
56
57 let slot_type = match self.program().input_port_type_by_idx(idx) {
63 Some(t) => t,
64 None => return Err(WriteError::UnknownWire { key: format!("wire[{idx}]") }),
65 };
66
67 let slot_name = self.program()
68 .input_name_by_idx(idx)
69 .map(|s| s.to_string())
70 .unwrap_or_else(|| format!("wire[{idx}]"));
71
72 let got = value.port_type();
82 let adapted = crate::kernel::state::adapt_boundary_value(&slot_name, slot_type, value);
83 if !adapted.satisfies_slot(slot_type) {
94 return Err(WriteError::TypeMismatch {
95 slot: slot_name,
96 expected: slot_type,
97 got,
98 });
99 }
100 self.state().set_input(idx, adapted);
101 Ok(())
102 }
103
104 #[inline]
105 fn get_wire_idx(&self, idx: usize) -> Value {
106 self.state_ref().get_input(idx)
107 }
108}
109
110impl Construction for PolydatKernel {
111 type Error = crate::kernel::subcontext::ContractViolation;
112
113 fn root(matter: crate::kernel::subcontext::PolydatMatter<'_>) -> Result<Self, Self::Error> {
114 use crate::kernel::subcontext::PolydatMatterInner;
115 match matter.inner {
116 PolydatMatterInner::Source(s) => {
117 crate::dsl::compile::compile_polydat_with_libs_and_limit(
118 &s.body,
119 s.options.workload_dir.as_deref(),
120 s.options.polydat_lib_paths,
121 &s.options.required_outputs,
122 s.options.strict,
123 s.options.context_label.as_deref().unwrap_or(&s.label),
124 s.options.cursor_limit,
125 )
126 .map_err(crate::kernel::subcontext::ContractViolation::Compile)
127 }
128 PolydatMatterInner::Statements(s) => {
129 let file = crate::dsl::ast::PolydatFile { statements: s.statements };
134 crate::dsl::compile::compile_ast_with_libs(
135 &file,
136 s.options.workload_dir.as_deref(),
137 s.options.polydat_lib_paths,
138 &s.options.required_outputs,
139 s.options.strict,
140 s.options.context_label.as_deref().unwrap_or(&s.label),
141 )
142 .map_err(crate::kernel::subcontext::ContractViolation::Compile)
143 }
144 PolydatMatterInner::Program(p) => {
145 let mut k = PolydatKernel::from_program(p.program);
146 for (var, value) in p.iter_bindings {
147 if let Some(idx) = k.program().find_input(var) {
148 k.state().set_input(idx, value.clone());
149 }
150 }
151 Ok(k)
152 }
153 }
154 }
155
156 fn subscope(
157 &self,
158 matter: crate::kernel::subcontext::PolydatMatter<'_>,
159 ) -> Result<Self, Self::Error> {
160 PolydatKernel::build_subscope(self, matter)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::dsl::compile::compile_polydat;
169
170 #[test]
172 fn dataflow_indexed_set_get() {
173 let mut k = compile_polydat(
174 "input cycle: u64\nconst x := 7\n"
175 ).unwrap();
176 k.set_wire(0_usize, Value::U64(42)).expect("typed write");
178 assert_eq!(k.get_wire(0_usize), Some(Value::U64(42)));
179 }
180
181 #[test]
183 fn dataflow_named_set_get() {
184 let mut k = compile_polydat(
185 "input cycle: u64\nextern n: u64\n"
186 ).unwrap();
187 k.set_wire("n", Value::U64(5)).expect("typed write");
188 match k.get_wire("n") {
189 Some(Value::U64(5)) => {}
190 other => panic!("expected U64(5), got {other:?}"),
191 }
192 }
193
194 #[test]
196 fn dataflow_string_key() {
197 let mut k = compile_polydat(
198 "input cycle: u64\nextern n: u64\n"
199 ).unwrap();
200 let name = String::from("n");
201 k.set_wire(&name, Value::U64(99)).expect("typed write");
202 assert_eq!(k.get_wire(name.clone()), Some(Value::U64(99)));
203 }
204
205 #[test]
207 fn dataflow_unknown_name_safe() {
208 let mut k = compile_polydat("input cycle: u64\n").unwrap();
209 let err = k.set_wire("nonexistent", Value::U64(1)).unwrap_err();
210 assert!(matches!(err, crate::kernel::api::WriteError::UnknownWire { .. }));
211 assert!(k.get_wire("nonexistent").is_none());
212 }
213
214 #[test]
223 fn dataflow_type_mismatch_rejected() {
224 let mut k = compile_polydat(
225 "input cycle: u64\nextern n: u64\n"
226 ).unwrap();
227 let err = k.set_wire(
228 "n",
229 Value::VecF32(crate::ast::SliceArc::from_vec(vec![1.0_f32, 2.0])),
230 ).unwrap_err();
231 match err {
232 crate::kernel::api::WriteError::TypeMismatch { slot, expected, got } => {
233 assert_eq!(slot, "n");
234 assert_eq!(expected, PortType::U64);
235 assert_eq!(got, PortType::VecF32);
236 }
237 other => panic!("expected TypeMismatch, got {other:?}"),
238 }
239 }
240
241 #[test]
246 fn vec_to_scalar_diagnostic_mentions_explicit_helpers() {
247 let err = crate::kernel::api::WriteError::TypeMismatch {
248 slot: "score".into(),
249 expected: PortType::F64,
250 got: PortType::VecF32,
251 };
252 let msg = err.to_string();
253 assert!(msg.contains("vec_len"), "missing vec_len hint: {msg}");
254 assert!(msg.contains("vec_first"), "missing vec_first hint: {msg}");
255 }
256
257 #[test]
260 fn dataflow_healable_mismatch_adapts() {
261 let mut k = compile_polydat(
262 "input cycle: u64\nextern x: f64\n"
263 ).unwrap();
264 k.set_wire("x", Value::U64(42)).expect("u64→f64 boundary adapter");
267 match k.get_wire("x") {
268 Some(Value::F64(42.0)) => {}
269 other => panic!("expected adapted F64(42.0), got {other:?}"),
270 }
271 }
272
273 #[test]
277 fn dataflow_none_passes_through_any_slot() {
278 let mut k = compile_polydat(
279 "input cycle: u64\nextern n: u64\n"
280 ).unwrap();
281 k.set_wire("n", Value::None).expect("None always permitted");
282 }
283
284 #[test]
286 fn metadata_listings() {
287 let k = compile_polydat(
288 "input (cycle: u64, thread: u64)\nextern n: u64\nconst x := 7\n"
289 ).unwrap();
290 let inputs: Vec<String> = k.input_names();
291 assert!(inputs.iter().any(|s| s == "cycle"));
292 assert!(inputs.iter().any(|s| s == "n"));
293 assert_eq!(k.coord_count(), 2); assert!(k.find_input("n").is_some());
295 assert_eq!(k.input_port_type("n"), Some(PortType::U64));
296 }
297
298 #[test]
302 fn construction_symmetric_paths() {
303 let root_opts = crate::kernel::subcontext::CompileOptions {
304 workload_dir: None,
305 polydat_lib_paths: Vec::new(),
306 strict: false,
307 required_outputs: Vec::new(),
308 context_label: Some("root".to_string()),
309 cursor_limit: None,
310 ..Default::default()
311 };
312 let root_matter = crate::kernel::subcontext::PolydatMatter::builder()
313 .label("root")
314 .source("input cycle: u64\nshared flag := 0\n")
315 .options(root_opts)
316 .build()
317 .expect("matter build");
318 let root = <PolydatKernel as Construction>::root(root_matter)
319 .expect("root from source matter");
320
321 let sub_opts = crate::kernel::subcontext::CompileOptions {
322 workload_dir: None,
323 polydat_lib_paths: Vec::new(),
324 strict: false,
325 required_outputs: Vec::new(),
326 context_label: Some("sub".to_string()),
327 cursor_limit: None,
328 ..Default::default()
329 };
330 let sub_matter = crate::kernel::subcontext::PolydatMatter::builder()
331 .label("sub")
332 .source("input cycle: u64\n")
333 .options(sub_opts)
334 .build()
335 .expect("matter build");
336 let _sub = root
337 .subscope(sub_matter)
338 .expect("subscope from source matter");
339 }
340
341 #[test]
345 fn construction_root_from_program() {
346 let template = compile_polydat("input cycle: u64\nextern n: u64\n").unwrap();
347 let program = template.program().clone();
348 let matter = crate::kernel::subcontext::PolydatMatter::builder()
349 .program(program)
350 .build()
351 .expect("matter build");
352 let mut root = <PolydatKernel as Construction>::root(matter)
353 .expect("root from program matter");
354 root.set_wire("n", Value::U64(13)).expect("set_wire");
355 assert_eq!(root.get_wire("n"), Some(Value::U64(13)));
356 }
357
358 #[test]
360 fn builder_rejects_multiple_forms() {
361 let template = compile_polydat("input cycle: u64\n").unwrap();
362 match crate::kernel::subcontext::PolydatMatter::builder()
363 .source("input cycle: u64\n")
364 .program(template.program().clone())
365 .build()
366 {
367 Err(msg) => assert!(msg.contains("multiple"), "expected multiple-forms error, got: {msg}"),
368 Ok(_) => panic!("multiple forms must error"),
369 }
370 }
371
372 #[test]
374 fn builder_rejects_empty() {
375 match crate::kernel::subcontext::PolydatMatter::builder().build() {
376 Err(msg) => assert!(msg.contains("no input form"), "expected no-form error, got: {msg}"),
377 Ok(_) => panic!("empty matter must error"),
378 }
379 }
380}