Reshape the extent trait to a written extent_at and a derived composite

This commit is contained in:
Dennis Kobert
2026-08-14 09:10:04 +00:00
parent a3e9074955
commit 98e6b97072
5 changed files with 45 additions and 48 deletions

View File

@@ -176,6 +176,26 @@ impl Extent {
(Extent::Exactly(n), Extent::Exactly(m)) => GPoll::fallback(Extent::Exactly(n.min(m)), "extent mismatch"),
})
}
/// The product of two extents, used to compose nested-level counts; an
/// unbounded operand leaves the product unbounded.
pub fn mul(a: GPoll<Extent>, b: GPoll<Extent>) -> GPoll<Extent> {
a.zip(b).map(|(a, b)| match (a, b) {
(Extent::Exactly(n), Extent::Exactly(m)) => Extent::Exactly(n * m),
_ => Extent::Free,
})
}
}
/// A query over a node's nesting levels: one level, the product below or above
/// it, or the whole domain. The composite [`Node::extent`](crate::node::Node::extent)
/// derives these from the per-level [`extent_at`](crate::node::Node::extent_at).
#[derive(Clone, Copy, Debug)]
pub enum Level {
At(u8),
Below(u8),
Above(u8),
Total,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]

View File

@@ -1,5 +1,5 @@
use crate::context::InjectIndex;
use crate::gpoll::{Extent, Finality, GPoll, GraphError, Interrupt};
use crate::gpoll::{Extent, Finality, GPoll, GraphError, Interrupt, Level};
use std::cell::Cell;
use std::mem::MaybeUninit;
use std::ops::Range;
@@ -177,18 +177,27 @@ pub trait Node<Input> {
fn eval(&self, input: &Input) -> GPoll<Self::Output>;
fn extent(&self, _input: &Input) -> GPoll<Extent> {
GPoll::Final(Extent::Free)
}
/// The count of items at one absolute nesting level (innermost `0`). The
/// leveled primitive a structure node overrides to report a pushed level's
/// size; the scalar base is one item at every level. Uncertainty rides the
/// `GPoll` status axis, as with [`extent`](Node::extent).
/// `GPoll` status axis.
fn extent_at(&self, _input: &Input, _level: u8) -> GPoll<Extent> {
GPoll::Final(Extent::Exactly(1))
}
/// The composite domain query derived from [`extent_at`](Node::extent_at):
/// one level, the product of the levels below or above it, or the whole
/// domain's flat count. Consumers query this; nodes only write `extent_at`.
fn extent(&self, input: &Input, at: Level) -> GPoll<Extent> {
let product = |range: core::ops::Range<u8>| range.fold(GPoll::Final(Extent::Exactly(1)), |acc, level| Extent::mul(acc, self.extent_at(input, level)));
match at {
Level::At(level) => self.extent_at(input, level),
Level::Below(level) => product(0..level),
Level::Above(level) => product((level + 1)..self.depth()),
Level::Total => product(0..self.depth()),
}
}
/// The node's domain depth (number of nesting levels; `0` = scalar), baked
/// into the record layout at wiring.
fn depth(&self) -> u8 {
@@ -262,10 +271,6 @@ where
(**self).eval(input)
}
fn extent(&self, input: &Input) -> GPoll<Extent> {
(**self).extent(input)
}
fn extent_at(&self, input: &Input, level: u8) -> GPoll<Extent> {
(**self).extent_at(input, level)
}
@@ -296,10 +301,6 @@ where
(**self).eval(input)
}
fn extent(&self, input: &Input) -> GPoll<Extent> {
(**self).extent(input)
}
fn extent_at(&self, input: &Input, level: u8) -> GPoll<Extent> {
(**self).extent_at(input, level)
}
@@ -330,10 +331,6 @@ where
(**self).eval(input)
}
fn extent(&self, input: &Input) -> GPoll<Extent> {
(**self).extent(input)
}
fn extent_at(&self, input: &Input, level: u8) -> GPoll<Extent> {
(**self).extent_at(input, level)
}

View File

@@ -165,11 +165,6 @@ where
result
}
fn extent(&self, input: &Input) -> crate::gpoll::GPoll<crate::gpoll::Extent> {
// SAFETY: as in eval.
unsafe { self.ptr.as_ref() }.extent(input)
}
fn extent_at(&self, input: &Input, level: u8) -> crate::gpoll::GPoll<crate::gpoll::Extent> {
// SAFETY: as in eval.
unsafe { self.ptr.as_ref() }.extent_at(input, level)