feat: Add SharedValueSeries and PipelineNode

Introduces `SharedValueSeries` for read-only access to generic `Value`
buffers, and `PipelineNode` which combines an `ObservableStream` with a
`Series` view. This is a foundational step for implementing the `pipe`
operator.
This commit is contained in:
Michael Schimmel
2026-03-01 23:12:09 +01:00
parent 2579e2b1fd
commit b177aa8854
3 changed files with 138 additions and 9 deletions
+30
View File
@@ -386,6 +386,36 @@ impl<T: ScalarValue> crate::ast::types::Series for SharedSeries<T> {
}
}
/// A read-only series for generic Values that shares its buffer with the pipeline.
#[derive(Clone)]
pub struct SharedValueSeries {
pub buffer: Rc<std::cell::RefCell<RingBuffer<Value>>>,
}
impl Debug for SharedValueSeries {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SharedValueSeries[len: {}]", self.buffer.borrow().len())
}
}
impl Object for SharedValueSeries {
fn type_name(&self) -> &'static str {
"SharedValueSeries"
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_series(&self) -> Option<&dyn crate::ast::types::Series> {
Some(self)
}
}
impl crate::ast::types::Series for SharedValueSeries {
fn get_item(&self, index: usize) -> Option<Value> {
self.buffer.borrow().get(index).cloned()
}
}
/// A read-only series for Records that shares its buffers with the pipeline.
#[derive(Clone)]
pub struct SharedRecordSeries {
+53 -2
View File
@@ -29,6 +29,12 @@ pub trait ObservableStream {
fn add_observer(&self, observer: Rc<RefCell<dyn Observer>>);
}
impl<T: ObservableStream> ObservableStream for std::cell::RefCell<T> {
fn add_observer(&self, observer: Rc<RefCell<dyn Observer>>) {
self.borrow().add_observer(observer);
}
}
/// A generic wrapper to pass ANY ObservableStream (Root, Pipe, etc.) as an Object to the VM.
#[derive(Clone)]
pub struct StreamNode {
@@ -50,6 +56,32 @@ impl crate::ast::types::Object for StreamNode {
}
}
/// A node that represents the result of a `pipe` statement.
/// It acts as a Stream (to connect downstream pipes) AND as a Series (for script lookbacks).
#[derive(Clone)]
pub struct PipelineNode {
pub stream: Rc<dyn ObservableStream>,
pub series: Rc<dyn crate::ast::types::Series>,
}
impl std::fmt::Debug for PipelineNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PipelineNode")
}
}
impl crate::ast::types::Object for PipelineNode {
fn type_name(&self) -> &'static str {
"PipelineNode"
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_series(&self) -> Option<&dyn crate::ast::types::Series> {
Some(self.series.as_ref())
}
}
/// The RootStream is the "Clock" and data source of the entire pipeline.
/// It generates the monotonic `cycle_id` and triggers the observers.
pub struct RootStream {
@@ -160,13 +192,20 @@ impl Observer for PipeStream {
let args = vec![value]; // Simplified for now
// 2. Execute Lambda (This requires a VM instance!)
// TODO: Actually execute the lambda using a VM context.
// For now, we mock the result to be the input value itself.
let result = args[0].clone();
// 3. Update Current Signal
// 3. Handle Void case! (Filter pattern)
if matches!(result, Value::Void) {
return; // Act as a filter: do not emit, do not push.
}
// 4. Update Current Signal
let new_signal = Signal { cycle_id, value: result };
*self.current_signal.borrow_mut() = Some(new_signal.clone());
// 4. Notify Observers (Always at source_index 0 of the NEXT pipe)
// 5. Notify Observers (Always at source_index 0 of the NEXT pipe)
let obs_list = self.observers.borrow();
for obs in obs_list.iter() {
obs.borrow_mut().notify(0, cycle_id, new_signal.value.clone());
@@ -187,6 +226,18 @@ impl<T: crate::ast::rtl::series::ScalarValue> Observer for SeriesPusher<T> {
}
}
/// A specialized observer that pushes incoming signals into a generic SharedValueSeries buffer.
pub struct ValuePusher {
pub buffer: Rc<RefCell<crate::ast::rtl::series::RingBuffer<Value>>>,
pub lookback: Option<usize>,
}
impl Observer for ValuePusher {
fn notify(&mut self, _source_index: usize, _cycle_id: u64, value: Value) {
self.buffer.borrow_mut().push(value, self.lookback);
}
}
// ============================================================================
// Script Integration (RTL Registration)
// ============================================================================