Rename pipe-buffered to pipe-series

The `pipe-buffered` function has been renamed to `pipe-series` to better
reflect its functionality. This change involves updating the function
name in the example file `examples/data_stream_pipe_buffered.myc` and
across the Rust source code in `src/ast/rtl/streams.rs`. Additionally,
test cases and documentation have been updated accordingly.
This commit is contained in:
2026-03-30 14:16:04 +02:00
parent 24d36a08a6
commit 3676eb51e5
3 changed files with 27 additions and 27 deletions
@@ -5,7 +5,7 @@
(def cnt 1) (def cnt 1)
(pipe-buffered 2 [EURUSD GER40] (pipe-series 2 [EURUSD GER40]
(fn [eu ge] (fn [eu ge]
(do (do
(def dax (.close (ge 0))) (def dax (.close (ge 0)))
+14 -14
View File
@@ -529,10 +529,10 @@ fn pipe_arg_hint_resolver(
} }
// ============================================================================ // ============================================================================
// pipe-buffered: Pipe with automatic value accumulation into Series // pipe-series: Pipe with automatic value accumulation into Series
// ============================================================================ // ============================================================================
/// Compiler hook for `(pipe-buffered lookback inputs lambda)`. /// Compiler hook for `(pipe-series lookback inputs lambda)`.
/// ///
/// - **post_call:** Validates 3 arguments (Int, Streams, Lambda) and checks that /// - **post_call:** Validates 3 arguments (Int, Streams, Lambda) and checks that
/// the input stream count matches the lambda parameter count. /// the input stream count matches the lambda parameter count.
@@ -551,7 +551,7 @@ impl RtlCompilerHook for LookbackPipeHook {
_ctx: &dyn InferenceAccess, _ctx: &dyn InferenceAccess,
diag: &mut Diagnostics, diag: &mut Diagnostics,
) -> StaticType { ) -> StaticType {
// Validate: (pipe-buffered Int Streams Lambda) — 3 elements // Validate: (pipe-series Int Streams Lambda) — 3 elements
let NodeKind::Tuple { elements } = &args.kind else { return ret_ty }; let NodeKind::Tuple { elements } = &args.kind else { return ret_ty };
if elements.len() != 3 || !matches!(elements[0].ty, StaticType::Int) { if elements.len() != 3 || !matches!(elements[0].ty, StaticType::Int) {
return ret_ty; return ret_ty;
@@ -572,7 +572,7 @@ impl RtlCompilerHook for LookbackPipeHook {
if actual != expected { if actual != expected {
diag.push_error( diag.push_error(
format!( format!(
"pipe-buffered: lambda expects {} parameter(s) but {} input stream(s) provided", "pipe-series: lambda expects {} parameter(s) but {} input stream(s) provided",
actual, expected actual, expected
), ),
Some(elements[2].identity.clone()), Some(elements[2].identity.clone()),
@@ -655,7 +655,7 @@ fn build_buffered_wrapper(
}) })
} }
/// Extracts input element types from the typed AST for `pipe-buffered`. /// Extracts input element types from the typed AST for `pipe-series`.
/// Args layout: `Tuple([Int, inputs, Lambda])` — inputs at index 1. /// Args layout: `Tuple([Int, inputs, Lambda])` — inputs at index 1.
fn extract_input_element_types(args: &TypedNode) -> Vec<StaticType> { fn extract_input_element_types(args: &TypedNode) -> Vec<StaticType> {
let NodeKind::Tuple { elements } = &args.kind else { return vec![] }; let NodeKind::Tuple { elements } = &args.kind else { return vec![] };
@@ -694,7 +694,7 @@ fn extract_runtime_input_types(val: &Value) -> Vec<StaticType> {
} }
} }
/// Resolves the return type of `pipe-buffered` from its argument types. /// Resolves the return type of `pipe-series` from its argument types.
/// Argument layout: `Tuple([Int, inputs, Lambda])`. /// Argument layout: `Tuple([Int, inputs, Lambda])`.
/// Returns `Stream<U>`, unwrapping `Optional<U>` to `U` (filter pattern). /// Returns `Stream<U>`, unwrapping `Optional<U>` to `U` (filter pattern).
fn buffered_pipe_type_resolver(args_ty: &StaticType) -> Option<StaticType> { fn buffered_pipe_type_resolver(args_ty: &StaticType) -> Option<StaticType> {
@@ -712,7 +712,7 @@ fn buffered_pipe_type_resolver(args_ty: &StaticType) -> Option<StaticType> {
Some(StaticType::Stream(Box::new(inner))) Some(StaticType::Stream(Box::new(inner)))
} }
/// Provides expected lambda parameter types for `pipe-buffered`. /// Provides expected lambda parameter types for `pipe-series`.
/// Lambda is at arg index 2. Parameters are wrapped as `Series<T>` instead of raw `T`. /// Lambda is at arg index 2. Parameters are wrapped as `Series<T>` instead of raw `T`.
fn buffered_pipe_arg_hint_resolver( fn buffered_pipe_arg_hint_resolver(
arg_index: usize, arg_index: usize,
@@ -939,21 +939,21 @@ pub fn register(env: &Environment) {
"(pipe [stream-a stream-b] (fn [a b] (+ a b)))", "(pipe [stream-a stream-b] (fn [a b] (+ a b)))",
]); ]);
// (pipe-buffered lookback inputs lambda) -> StreamNode // (pipe-series lookback inputs lambda) -> StreamNode
// Like pipe, but accumulates values into internal Series with the given lookback // Like pipe, but accumulates values into internal Series with the given lookback
// depth. The lambda receives Series objects and only fires once all series have // depth. The lambda receives Series objects and only fires once all series have
// at least `lookback` elements (fill gate). // at least `lookback` elements (fill gate).
let fn_globals = env.global_store(); let fn_globals = env.global_store();
let hook_globals = fn_globals.clone(); let hook_globals = fn_globals.clone();
env.register_native_fn( env.register_native_fn(
"pipe-buffered", "pipe-series",
StaticType::PolymorphicFn { StaticType::PolymorphicFn {
resolve_return: buffered_pipe_type_resolver, resolve_return: buffered_pipe_type_resolver,
resolve_arg_hints: Some(buffered_pipe_arg_hint_resolver), resolve_arg_hints: Some(buffered_pipe_arg_hint_resolver),
}, },
Purity::Impure, Purity::Impure,
move |args: &[Value]| { move |args: &[Value]| {
assert!(args.len() == 3, "pipe-buffered expects 3 arguments (lookback, inputs, lambda)"); assert!(args.len() == 3, "pipe-series expects 3 arguments (lookback, inputs, lambda)");
let lookback = args[0].as_int().unwrap() as usize; let lookback = args[0].as_int().unwrap() as usize;
let obs = extract_obs_streams(&args[1]); let obs = extract_obs_streams(&args[1]);
let input_types: Vec<StaticType> = extract_runtime_input_types(&args[1]); let input_types: Vec<StaticType> = extract_runtime_input_types(&args[1]);
@@ -965,8 +965,8 @@ pub fn register(env: &Environment) {
.doc("Like pipe, but accumulates values into Series before firing the lambda.") .doc("Like pipe, but accumulates values into Series before firing the lambda.")
.description("The lambda only fires once all input series have at least N elements (fill gate). Lambda parameters are Series objects with lookback indexing (0 = newest).") .description("The lambda only fires once all input series have at least N elements (fill gate). Lambda parameters are Series objects with lookback indexing (0 = newest).")
.examples(&[ .examples(&[
"(pipe-buffered 20 ohlc (fn [bars] (- (.close (bars 0)) (.close (bars 19)))))", "(pipe-series 20 ohlc (fn [bars] (- (.close (bars 0)) (.close (bars 19)))))",
"(pipe-buffered 2 [stream-a stream-b] (fn [a b] (+ (a 0) (b 0))))", "(pipe-series 2 [stream-a stream-b] (fn [a b] (+ (a 0) (b 0))))",
]); ]);
} }
@@ -1034,7 +1034,7 @@ mod tests {
assert_eq!(sig.cycle_id, 1); assert_eq!(sig.cycle_id, 1);
} }
/// Validates the wrapper-executor pattern for `pipe-buffered`: /// Validates the wrapper-executor pattern for `pipe-series`:
/// A closure wraps push + fill gate + user lambda, reusing standard PipeStream. /// A closure wraps push + fill gate + user lambda, reusing standard PipeStream.
#[test] #[test]
fn test_buffered_pipe_fill_gate() { fn test_buffered_pipe_fill_gate() {
@@ -1043,7 +1043,7 @@ mod tests {
let root = RootStream::new(); let root = RootStream::new();
// Internal series with lookback 3 (simulates what pipe-buffered creates) // Internal series with lookback 3 (simulates what pipe-series creates)
let series: Rc<dyn SeriesStorage> = let series: Rc<dyn SeriesStorage> =
Rc::new(ScalarSeries::<f64>::new("FloatSeries", 3)); Rc::new(ScalarSeries::<f64>::new("FloatSeries", 3));
let series_clone = Rc::clone(&series); let series_clone = Rc::clone(&series);
+12 -12
View File
@@ -146,57 +146,57 @@ fn test_pipeline_optional_type() {
} }
} }
/// Verifies that `pipe-buffered` accumulates values into Series and only fires /// Verifies that `pipe-series` accumulates values into Series and only fires
/// the lambda once all series have reached the lookback depth (fill gate). /// the lambda once all series have reached the lookback depth (fill gate).
/// Lambda parameters must be Series objects, not individual values. /// Lambda parameters must be Series objects, not individual values.
#[test] #[test]
fn test_pipe_buffered_fill_gate() { fn test_pipe_series_fill_gate() {
let env = Environment::new(); let env = Environment::new();
// create-random-ohlc produces 10 ticks. With lookback 3, the lambda // create-random-ohlc produces 10 ticks. With lookback 3, the lambda
// fires from tick 3 onwards (8 times). Each call returns the difference // fires from tick 3 onwards (8 times). Each call returns the difference
// between the current and previous close: (.close (bars 0)) - (.close (bars 1)) // between the current and previous close: (.close (bars 0)) - (.close (bars 1))
let source = "(do let source = "(do
(def ohlc (create-random-ohlc 42 10)) (def ohlc (create-random-ohlc 42 10))
(def result (pipe-buffered 3 [ohlc] (def result (pipe-series 3 [ohlc]
(fn [bars] (fn [bars]
(- (.close (bars 0)) (.close (bars 1)))))) (- (.close (bars 0)) (.close (bars 1))))))
result result
)"; )";
let res = env.run_script(source); let res = env.run_script(source);
assert!(res.is_ok(), "pipe-buffered script failed: {:?}", res.err()); assert!(res.is_ok(), "pipe-series script failed: {:?}", res.err());
let val = res.unwrap(); let val = res.unwrap();
if let Value::Stream(s) = val { if let Value::Stream(s) = val {
assert_eq!(s.stream_type_name(), "stream"); assert_eq!(s.stream_type_name(), "stream");
} else { } else {
panic!("Expected Stream from pipe-buffered, got: {:?}", val); panic!("Expected Stream from pipe-series, got: {:?}", val);
} }
} }
/// Verifies that pipe-buffered with multiple inputs waits for all series to fill. /// Verifies that pipe-series with multiple inputs waits for all series to fill.
#[test] #[test]
fn test_pipe_buffered_multi_input() { fn test_pipe_series_multi_input() {
let env = Environment::new(); let env = Environment::new();
let source = "(do let source = "(do
(def src1 (create-random-ohlc 42 10)) (def src1 (create-random-ohlc 42 10))
(def src2 (create-random-ohlc 99 10)) (def src2 (create-random-ohlc 99 10))
(def result (pipe-buffered 2 [src1 src2] (def result (pipe-series 2 [src1 src2]
(fn [a b] (fn [a b]
(+ (.close (a 0)) (.close (b 0)))))) (+ (.close (a 0)) (.close (b 0))))))
result result
)"; )";
let res = env.run_script(source); let res = env.run_script(source);
assert!(res.is_ok(), "pipe-buffered multi-input failed: {:?}", res.err()); assert!(res.is_ok(), "pipe-series multi-input failed: {:?}", res.err());
} }
/// Verifies that pipe-buffered lambda params are typed as Series, not raw values. /// Verifies that pipe-series lambda params are typed as Series, not raw values.
/// The arg hint resolver must wrap inner types in Series<T>. /// The arg hint resolver must wrap inner types in Series<T>.
#[test] #[test]
fn test_pipe_buffered_lambda_params_are_series() { fn test_pipe_series_lambda_params_are_series() {
let env = Environment::new(); let env = Environment::new();
let source = "(do let source = "(do
(def ohlc (create-random-ohlc 42 5)) (def ohlc (create-random-ohlc 42 5))
(pipe-buffered 3 [ohlc] (fn [bars] (.close (bars 0)))) (pipe-series 3 [ohlc] (fn [bars] (.close (bars 0))))
)"; )";
let dump = env.dump_ast(source).expect("dump_ast failed"); let dump = env.dump_ast(source).expect("dump_ast failed");