use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use crate::kernel::{GkKernel, SharedCell};
use crate::node::{PortType, Value};
use super::builder::SubcontextBuilder;
use super::error::{ContractViolation, SourceContext};
use super::module::{ScopeModule, WriteThroughBinding};
use super::name::ChildName;
use super::pull::RegisteredPullConsumer;
#[derive(Debug)]
pub struct RootMarker;
#[derive(Debug)]
pub struct Child<P>(PhantomData<fn() -> P>);
#[derive(Debug)]
struct ChildEntry {
site: SourceContext,
}
pub struct ScopeKernel<M> {
name: ChildName,
inner: Arc<Mutex<GkKernel>>,
site: SourceContext,
children: Mutex<HashMap<ChildName, ChildEntry>>,
consumers: Mutex<Vec<RegisteredPullConsumer>>,
write_throughs: Vec<WriteThroughBinding>,
_module: PhantomData<fn() -> M>,
}
impl<M> std::fmt::Debug for ScopeKernel<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScopeKernel")
.field("name", &self.name)
.field("site", &self.site)
.finish()
}
}
#[derive(Clone)]
pub struct SharedCellInScope {
pub name: String,
pub port_type: PortType,
pub cell: SharedCell,
}
impl std::fmt::Debug for SharedCellInScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SharedCellInScope")
.field("name", &self.name)
.field("port_type", &self.port_type)
.finish()
}
}
impl<M> ScopeKernel<M> {
pub fn shared_cells_in_scope(&self) -> Vec<SharedCellInScope> {
let inner = self.lock_inner();
inner
.shared_cells_in_scope()
.into_iter()
.map(|e| SharedCellInScope {
name: e.name,
port_type: e.port_type,
cell: e.cell,
})
.collect()
}
pub(crate) fn new_internal(
name: ChildName,
kernel: GkKernel,
site: SourceContext,
consumers: Vec<RegisteredPullConsumer>,
) -> Self {
Self::new_with_write_throughs(name, kernel, site, consumers, Vec::new())
}
pub(crate) fn new_with_write_throughs(
name: ChildName,
kernel: GkKernel,
site: SourceContext,
consumers: Vec<RegisteredPullConsumer>,
write_throughs: Vec<WriteThroughBinding>,
) -> Self {
Self {
name,
inner: Arc::new(Mutex::new(kernel)),
site,
children: Mutex::new(HashMap::new()),
consumers: Mutex::new(consumers),
write_throughs,
_module: PhantomData,
}
}
pub fn name(&self) -> &ChildName {
&self.name
}
pub fn site(&self) -> &SourceContext {
&self.site
}
pub fn lock_inner(&self) -> std::sync::MutexGuard<'_, GkKernel> {
self.inner.lock().expect("ScopeKernel inner kernel poisoned")
}
pub fn consumers(&self) -> Vec<RegisteredPullConsumer> {
self.consumers
.lock()
.expect("ScopeKernel consumers poisoned")
.clone()
}
pub fn has_child(&self, name: &ChildName) -> bool {
self.children
.lock()
.expect("ScopeKernel children registry poisoned")
.contains_key(name)
}
pub fn release_child(&self, name: &ChildName) {
self.children
.lock()
.expect("ScopeKernel children registry poisoned")
.remove(name);
}
pub fn subcontext_builder(self: Arc<Self>) -> SubcontextBuilder<M> {
SubcontextBuilder::new(self)
}
pub fn spawn(
self: &Arc<Self>,
name: ChildName,
artifact: ScopeModule<Child<M>>,
) -> Result<ScopeKernel<Child<M>>, ContractViolation> {
{
let mut children = self
.children
.lock()
.expect("ScopeKernel children registry poisoned");
if let Some(prior) = children.get(&name) {
return Err(ContractViolation::DuplicateChild {
name: name.clone(),
prior_site: prior.site.clone(),
this_site: artifact.context.clone(),
});
}
children.insert(
name.clone(),
ChildEntry {
site: artifact.context.clone(),
},
);
}
let parent_inner = self.lock_inner();
let child_kernel = parent_inner.materialize_subscope(artifact.program.clone(), &[]);
drop(parent_inner);
let child_site = artifact.context.clone();
let child_consumers = artifact.consumers.clone();
let child_write_throughs = artifact.write_throughs.clone();
Ok(ScopeKernel::new_with_write_throughs(
name,
child_kernel,
child_site,
child_consumers,
child_write_throughs,
))
}
pub fn write_throughs(&self) -> &[WriteThroughBinding] {
&self.write_throughs
}
pub fn commit_write_throughs(&self) {
if self.write_throughs.is_empty() {
return;
}
let mut inner = self.lock_inner();
let mut pending: Vec<(usize, Value)> = Vec::with_capacity(self.write_throughs.len());
for wt in &self.write_throughs {
let Some(idx) = inner.program().find_input(&wt.export_name) else {
continue;
};
let value = inner.pull(&wt.source_output).clone();
pending.push((idx, value));
}
for (idx, value) in pending {
inner.state().set_input(idx, value);
}
}
}
pub(crate) fn wrap_root_kernel(kernel: GkKernel, label: impl Into<String>) -> Arc<ScopeKernel<RootMarker>> {
let label = label.into();
let name = ChildName::from_segments([label.clone()]);
let site = SourceContext::new(label);
Arc::new(ScopeKernel::new_internal(name, kernel, site, Vec::new()))
}
pub struct GkMatter<'a> {
pub(crate) inner: GkMatterInner<'a>,
}
pub(crate) enum GkMatterInner<'a> {
Source(SourceMatter),
Statements(StatementsMatter),
Program(ProgramMatter<'a>),
}
pub(crate) struct SourceMatter {
pub(crate) label: String,
pub(crate) body: String,
pub(crate) result_bindings: Option<String>,
pub(crate) inherited_outputs: Vec<String>,
pub(crate) options: super::builder::CompileOptions,
}
pub(crate) struct StatementsMatter {
pub(crate) label: String,
pub(crate) statements: Vec<crate::dsl::ast::Statement>,
pub(crate) result_bindings: Option<String>,
pub(crate) inherited_outputs: Vec<String>,
pub(crate) options: super::builder::CompileOptions,
}
pub(crate) struct ProgramMatter<'a> {
pub(crate) program: Arc<crate::kernel::GkProgram>,
pub(crate) iter_bindings: &'a [(String, Value)],
}
impl<'a> GkMatter<'a> {
#[inline]
pub fn builder() -> GkMatterBuilder<'a> {
GkMatterBuilder::new()
}
}
#[derive(Default)]
pub struct GkMatterBuilder<'a> {
label: Option<String>,
body: Option<String>,
statements: Option<Vec<crate::dsl::ast::Statement>>,
program: Option<Arc<crate::kernel::GkProgram>>,
iter_bindings: &'a [(String, Value)],
result_bindings: Option<String>,
inherited_outputs: Vec<String>,
options: super::builder::CompileOptions,
}
impl<'a> GkMatterBuilder<'a> {
fn new() -> Self {
Self::default()
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn source(mut self, body: impl Into<String>) -> Self {
self.body = Some(body.into());
self
}
pub fn statements(mut self, stmts: Vec<crate::dsl::ast::Statement>) -> Self {
self.statements = Some(stmts);
self
}
pub fn program(mut self, program: Arc<crate::kernel::GkProgram>) -> Self {
self.program = Some(program);
self
}
pub fn iter_bindings(mut self, bindings: &'a [(String, Value)]) -> Self {
self.iter_bindings = bindings;
self
}
pub fn result_bindings(mut self, src: impl Into<String>) -> Self {
self.result_bindings = Some(src.into());
self
}
pub fn inherited_outputs(mut self, names: Vec<String>) -> Self {
self.inherited_outputs = names;
self
}
pub fn options(mut self, options: super::builder::CompileOptions) -> Self {
self.options = options;
self
}
pub fn build(self) -> Result<GkMatter<'a>, String> {
let forms = [self.body.is_some(), self.statements.is_some(), self.program.is_some()];
let count = forms.iter().filter(|x| **x).count();
if count == 0 {
return Err("GkMatter::builder: no input form set (use .source / .statements / .program)".into());
}
if count > 1 {
return Err("GkMatter::builder: multiple input forms set; choose exactly one".into());
}
let label = self.label.unwrap_or_else(|| "(matter)".to_string());
let inner = if let Some(body) = self.body {
GkMatterInner::Source(SourceMatter {
label,
body,
result_bindings: self.result_bindings,
inherited_outputs: self.inherited_outputs,
options: self.options,
})
} else if let Some(stmts) = self.statements {
GkMatterInner::Statements(StatementsMatter {
label,
statements: stmts,
result_bindings: self.result_bindings,
inherited_outputs: self.inherited_outputs,
options: self.options,
})
} else {
GkMatterInner::Program(ProgramMatter {
program: self.program.expect("program form set per count above"),
iter_bindings: self.iter_bindings,
})
};
Ok(GkMatter { inner })
}
}
impl GkKernel {
pub fn build_subscope(
&self,
matter: GkMatter<'_>,
) -> Result<GkKernel, ContractViolation> {
use super::module::BodyFragment;
match matter.inner {
GkMatterInner::Program(p) => {
Ok(self.materialize_subscope(p.program, p.iter_bindings))
}
GkMatterInner::Source(s) => {
let transient = self.transient_typed_parent(&s.label);
let mut builder = transient.clone().subcontext_builder();
builder
.context(SourceContext::new(s.label.clone()))
.mark_inherited_outputs(s.inherited_outputs)
.with_compile_options(s.options)
.body(BodyFragment::GkSource(s.body));
if let Some(src) = s.result_bindings {
builder.add_result_bindings(&src)?;
}
let module = builder.finalize()?;
let child = self.materialize_subscope(module.program.clone(), &[]);
drop(transient);
Ok(child)
}
GkMatterInner::Statements(s) => {
let transient = self.transient_typed_parent(&s.label);
let mut builder = transient.clone().subcontext_builder();
builder
.context(SourceContext::new(s.label.clone()))
.mark_inherited_outputs(s.inherited_outputs)
.with_compile_options(s.options)
.body(BodyFragment::Statements(s.statements));
if let Some(src) = s.result_bindings {
builder.add_result_bindings(&src)?;
}
let module = builder.finalize()?;
let child = self.materialize_subscope(module.program.clone(), &[]);
drop(transient);
Ok(child)
}
}
}
fn transient_typed_parent(&self, label: &str) -> Arc<ScopeKernel<RootMarker>> {
wrap_root_kernel(self.snapshot_with_cells(), format!("{label}__transient"))
}
}