use std::collections::HashSet;
use crate::scope_kernel::ScopeKernel;
use polydat::iteration::comprehension::pre_evaluate_clause;
use polydat::kernel::ManifestEntry;
use super::cascade::{CascadeInputs, CascadeOutputs, cascade_parent_into_source};
use super::helpers::{collect_leaf_placeholders, scan_one};
#[allow(clippy::too_many_arguments)]
pub fn build_for_each_scope_kernel(
bindings: &[(String, String)],
parent_manifest: &[ManifestEntry],
parent_kernel: &ScopeKernel,
workload_params: &std::collections::HashMap<String, String>,
polydat_lib_paths: Vec<std::path::PathBuf>,
workload_dir: Option<&std::path::Path>,
strict: bool,
context: &str,
phase_bindings: Option<&str>,
) -> Result<ScopeKernel, String> {
let iter_vars: Vec<String> = bindings.iter().map(|(v, _)| v.clone()).collect();
let spec_exprs: Vec<String> = bindings.iter().map(|(_, e)| e.clone()).collect();
let mut source = String::new();
let mut emitted: HashSet<String> = HashSet::new();
let mut inherited_names: Vec<String> = Vec::new();
let mut probes: std::collections::HashMap<String, String> = std::collections::HashMap::new();
let mut all_referenced: HashSet<String> = collect_leaf_placeholders(&spec_exprs);
for (idx, var) in iter_vars.iter().enumerate() {
if emitted.contains(var) {
continue;
}
let spec_text = spec_exprs.get(idx).map(String::as_str).unwrap_or("");
let values = pre_evaluate_clause(spec_text, parent_kernel, workload_params, &probes)
.unwrap_or_default();
let detected_type = values
.first()
.map(polydat::iteration::comprehension::value_to_polydat_type_name)
.unwrap_or("String");
source.push_str(&format!("extern {var}: {detected_type}\n"));
emitted.insert(var.clone());
for v in &values {
let v_str = v.to_display_string();
for next_spec in &spec_exprs[idx + 1..] {
let mut substituted = next_spec.clone();
substituted = substituted.replace(&format!("{{{var}}}"), &v_str);
let mut emergent = HashSet::new();
scan_one(&substituted, &mut emergent);
all_referenced.extend(emergent);
}
}
if let Some(first) = values.into_iter().next() {
probes.insert(var.clone(), first.to_display_string());
}
}
let pre_emitted: HashSet<String> = iter_vars.iter().cloned().collect();
cascade_parent_into_source(
CascadeInputs {
parent_kernel,
workload_params,
parent_manifest,
referenced: &all_referenced,
pre_emitted: &pre_emitted,
shadow_names: &pre_emitted,
include_referenced_cascade: true,
},
CascadeOutputs {
source: &mut source,
emitted: &mut emitted,
inherited_names: &mut inherited_names,
},
);
if let Some(body) = phase_bindings {
let trimmed = body.trim();
if !trimmed.is_empty() {
if !source.ends_with('\n') && !source.is_empty() {
source.push('\n');
}
source.push_str(body);
if !source.ends_with('\n') {
source.push('\n');
}
}
}
if source.is_empty() {
source.push_str("const __empty := 0\n");
}
let compile_options = polydat::kernel::subcontext::CompileOptions {
workload_dir: workload_dir.map(|p| p.to_path_buf()),
polydat_lib_paths,
strict,
required_outputs: Vec::new(),
context_label: Some(context.to_string()),
cursor_limit: None,
..Default::default()
};
crate::scope_kernel::ScopeKernel::synthesize_under(
parent_kernel,
crate::scope_kernel::SourceMatter::source(context, source, compile_options)
.inherited(inherited_names),
)
.map_err(|e| format!("{context}: for_each scope synthesis: {e}"))
}