Add the folded subject to LayoutMeta and derive reducer depth from it

This commit is contained in:
Dennis Kobert
2026-08-19 21:43:14 +00:00
parent 58a65c69e9
commit 7ffc9b9e3b
4 changed files with 36 additions and 1 deletions

View File

@@ -284,6 +284,10 @@ pub struct LayoutMeta {
/// The depth change the node applies: `0` for elementwise and flip nodes,
/// `+1` for a creator, `-1` for a reducer.
pub level_delta: i8,
/// The materialized subject a reducer folds, as `(input, levels)`: the
/// output keeps the subject's levels above the folded ones, so the depth
/// derives from its layout even though it contributes no fields.
pub folded: Option<(u8, u8)>,
}
/// The attributes a node reads from one input, recorded on [`LayoutMeta`] for
@@ -314,6 +318,7 @@ impl LayoutMeta {
writes: Vec::new(),
removes: Vec::new(),
level_delta: 0,
folded: None,
}
}
@@ -328,7 +333,13 @@ impl LayoutMeta {
sources => Layout::union(sources),
}
.without(&self.removes);
let depth = (base.depth as i8 + self.level_delta).max(0) as u8;
let depth = match self.folded {
Some((input, levels)) => {
let subject = inputs[input as usize].expect("layout fold folded input has no layout");
(subject.depth.saturating_sub(levels) as i8 + self.level_delta).max(0) as u8
}
None => (base.depth as i8 + self.level_delta).max(0) as u8,
};
let element = match &self.element {
ElementSpec::Concrete(element) => *element,
ElementSpec::Carried => base.element,

View File

@@ -236,6 +236,10 @@ pub(crate) fn layout_meta_tokens(node: &Node, element_spec: TokenStream2, core_t
quote!((<#marker as #core_types::attribute::Attribute>::NAME, #level))
});
let level_delta = level_delta(node);
let folded = match folded_subject(node) {
Some((index, levels)) => quote!(::core::option::Option::Some((#index, #levels))),
None => quote!(::core::option::Option::None),
};
quote! {
#core_types::record::LayoutMeta {
sources: ::std::vec![#(#sources),*],
@@ -244,10 +248,20 @@ pub(crate) fn layout_meta_tokens(node: &Node, element_spec: TokenStream2, core_t
writes: ::std::vec![#(#writes),*],
removes: ::std::vec![#(#removes),*],
level_delta: #level_delta,
folded: #folded,
}
}
}
/// The materialized subject a node folds, as `(input, levels)`.
pub(crate) fn folded_subject(node: &Node) -> Option<(u8, u8)> {
node.inputs
.iter()
.enumerate()
.find(|(index, input)| input.subject && materialized_levels(node, *index) > 0)
.map(|(index, _)| (index as u8, materialized_levels(node, index)))
}
fn field_writes(attrs: &[LevelAttr], core_types: &TokenStream2) -> Vec<TokenStream2> {
attrs
.iter()

View File

@@ -636,6 +636,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 1,
folded: None,
};
let node = install(
RepeatNode::new(RecordSource::new(bare_source(&base, 7.), &base, &base), count_edge, reverse_edge, &base, &count_layout, &reverse_layout),
@@ -671,6 +672,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 1,
folded: None,
};
let repeat = install(
RepeatNode::new(RecordSource::new(IndexSourceNode { layout: base.clone() }, &base, &base), count_edge, reverse_edge, &base, &count_layout, &reverse_layout),
@@ -712,6 +714,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 1,
folded: None,
};
let repeat = install(
RepeatNode::new(RecordSource::new(IndexSourceNode { layout: base.clone() }, &base, &base), count_edge, reverse_edge, &base, &count_layout, &reverse_layout),
@@ -755,6 +758,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 1,
folded: None,
};
let repeat = install(
RepeatNode::new(RecordSource::new(content, &leveled_content, &leveled_content), count_edge, reverse_edge, &leveled_content, &count_layout, &reverse_layout),
@@ -854,6 +858,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 0,
folded: None,
};
let node = install(
ExtendNode::new(RecordSource::new(base, &base_layout, &union), RecordSource::new(new, &new_layout, &union), &union),
@@ -903,6 +908,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 0,
folded: None,
};
let build = |index: f64| {
let content = LeveledSourceNode {
@@ -958,6 +964,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 0,
folded: None,
};
install(
IndexElementsNode::new(RecordSource::new(content, &layout, &layout), index_edge, &layout, &index_layout),
@@ -1174,6 +1181,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 1,
folded: None,
};
let nested = install(
RepeatNode::new(RecordSource::new(content, &leveled_content, &leveled_content), count_edge, reverse_edge, &leveled_content, &count_layout, &reverse_layout),
@@ -1234,6 +1242,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta: 1,
folded: None,
};
let repeat = install(
RepeatNode::new(RecordSource::new(IndexSourceNode { layout: base.clone() }, &base, &base), count_edge, reverse_edge, &base, &count_layout, &reverse_layout),

View File

@@ -409,6 +409,7 @@ mod tests {
writes: vec![],
removes: vec![],
level_delta,
folded: None,
}
}