diff --git a/src/ast/rtl/series.rs b/src/ast/rtl/series.rs index c0a4c38..1b5d91e 100644 --- a/src/ast/rtl/series.rs +++ b/src/ast/rtl/series.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use std::sync::Arc; use crate::ast::environment::Environment; -use crate::ast::types::{Keyword, Object, RecordLayout, Series, StaticType, Value}; +use crate::ast::types::{Keyword, Object, PushableSeries, RecordLayout, Series, StaticType, Value}; use crate::ast::types::{Purity, Signature}; // ============================================================================ @@ -144,14 +144,20 @@ impl Object for ScalarSeries { fn into_rc_any(self: Rc) -> Rc { self } + fn as_series(&self) -> Option<&dyn Series> { + Some(self) + } + fn as_pushable_series(&self) -> Option<&dyn PushableSeries> { + Some(self) + } +} + +impl PushableSeries for ScalarSeries { fn push_value(&self, value: Value) { if let Some(v) = T::from_value(value) { self.data.borrow_mut().push(v); } } - fn as_series(&self) -> Option<&dyn Series> { - Some(self) - } } impl Series for ScalarSeries { @@ -200,12 +206,18 @@ impl Object for ValueSeries { fn into_rc_any(self: Rc) -> Rc { self } - fn push_value(&self, value: Value) { - self.data.borrow_mut().push(value); - } fn as_series(&self) -> Option<&dyn Series> { Some(self) } + fn as_pushable_series(&self) -> Option<&dyn PushableSeries> { + Some(self) + } +} + +impl PushableSeries for ValueSeries { + fn push_value(&self, value: Value) { + self.data.borrow_mut().push(value); + } } impl Series for ValueSeries { @@ -224,10 +236,10 @@ impl Series for ValueSeries { // 3. RecordSeries (SoA - Struct of Arrays) // ============================================================================ -/// Marker supertrait combining `Object` and `Series` for RecordSeries field buffers. -/// Ensures each field buffer can be stored as a typed series AND used as a dynamic object -/// (including `push_value` via the `Object` supertrait). -pub trait SeriesMember: Object + Series {} +/// Marker supertrait for RecordSeries field buffers. +/// Ensures each field buffer supports indexed access (Series), dynamic dispatch (Object), +/// and value insertion (PushableSeries). +pub trait SeriesMember: PushableSeries {} impl SeriesMember for ScalarSeries {} impl SeriesMember for ScalarSeries {} @@ -321,6 +333,15 @@ impl Object for RecordSeries { fn into_rc_any(self: Rc) -> Rc { self } + fn as_series(&self) -> Option<&dyn Series> { + Some(self) + } + fn as_pushable_series(&self) -> Option<&dyn PushableSeries> { + Some(self) + } +} + +impl PushableSeries for RecordSeries { fn push_value(&self, value: Value) { if let Value::Record(_, values) = value { for (i, v) in values.iter().enumerate() { @@ -332,9 +353,6 @@ impl Object for RecordSeries { panic!("push to RecordSeries expects a record"); } } - fn as_series(&self) -> Option<&dyn Series> { - Some(self) - } } impl Series for RecordSeries { @@ -622,8 +640,11 @@ pub fn register(env: &Environment) { let (series_val, val) = (&args[0], &args[1]); if let Value::Object(obj) = series_val { - obj.push_value(val.clone()); - return Value::Void; + if let Some(p) = obj.as_pushable_series() { + p.push_value(val.clone()); + return Value::Void; + } + panic!("push expects a pushable series, got: {}", obj.type_name()); } panic!("push expects a series object as the first argument") }, diff --git a/src/ast/types.rs b/src/ast/types.rs index e63d14b..c85c3c7 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -176,7 +176,7 @@ impl RecordLayout { } } -/// Interface for custom objects (Closures, Series, Streams) +/// Interface for custom objects (Series, Streams, SyntaxNodes, and custom extensions). pub trait Object: fmt::Debug { fn type_name(&self) -> &'static str; fn as_any(&self) -> &dyn Any; @@ -185,17 +185,21 @@ pub trait Object: fmt::Debug { /// `Rc` via `Rc::downcast::()`. Requires `arbitrary_self_types` (stable since 1.81). fn into_rc_any(self: Rc) -> Rc; - /// Pushes a value into this object if it is a mutable series. - /// The default implementation panics — only series types override this. - fn push_value(&self, _val: Value) { - panic!("{} does not support push_value", self.type_name()); - } - - /// Optional optimization: If this object behaves like a time series (indexable lookbacks), - /// it can return a reference to its Series trait implementation. + /// If this object supports indexed lookback access, returns a `Series` reference. fn as_series(&self) -> Option<&dyn Series> { None } + + /// If this object supports pushing new values, returns a `PushableSeries` reference. + fn as_pushable_series(&self) -> Option<&dyn PushableSeries> { + None + } +} + +/// Extension of `Object + Series` for types that also accept new values via `push_value`. +/// Only series types that are mutable (ScalarSeries, ValueSeries, RecordSeries) implement this. +pub trait PushableSeries: Object + Series { + fn push_value(&self, val: Value); } /// Unified interface for all series types (Local RecordSeries, SeriesViews, and future SharedSeries).