pub mod data; pub use data::*; use std::rc::Rc; use crate::ast::environment::Environment; use crate::ast::types::{Purity, RecordLayout, Signature, StaticType, Value}; // ============================================================================ // 5. Script Integration (RTL Registration) // ============================================================================ pub fn register(env: &Environment) { // (series lookback_limit template_or_type_keyword) -> Series env.register_native_fn( "series", StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Any]), ret: StaticType::Any, })), Purity::Impure, |args: &[Value]| { let lookback_limit = args[0].as_int().unwrap_or(0) as usize; let arg = &args[1]; match arg { Value::Keyword(k) => { match k.name().to_lowercase().as_str() { "float" => Value::Series(Rc::new(ScalarSeries::::new("FloatSeries", lookback_limit))), "int" | "datetime" => Value::Series(Rc::new(ScalarSeries::::new("IntSeries", lookback_limit))), "bool" => Value::Series(Rc::new(ScalarSeries::::new("BoolSeries", lookback_limit))), "text" => Value::Series(Rc::new(ValueSeries::new(lookback_limit))), _ => panic!("Unknown or unsupported series type keyword: :{}", k.name()), } } Value::Record(schema_layout, schema_values) => { // Interpret the record as a schema: { :field :type_keyword } let mut fields = Vec::with_capacity(schema_layout.fields.len()); for (i, (name, _)) in schema_layout.fields.iter().enumerate() { let type_keyword = match &schema_values[i] { Value::Keyword(tk) => tk.name().to_lowercase(), _ => panic!("Field :{} in series schema must be a type keyword (e.g., :float, :int)", name.name()), }; let ty = match type_keyword.as_str() { "float" => StaticType::Float, "int" => StaticType::Int, "bool" => StaticType::Bool, "datetime" => StaticType::DateTime, "text" => StaticType::Text, _ => panic!("Unsupported type keyword :{} for series field :{}", type_keyword, name.name()), }; fields.push((*name, ty)); } let final_layout = RecordLayout::get_or_create(fields); Value::Series(Rc::new(RecordSeries::new(final_layout, lookback_limit))) } _ => panic!("series expects a lookback_limit and a type keyword (:float, :int...) or a schema record (e.g., {{:price :float}})"), } }, ).doc("Creates a new series (ring buffer) with a defined layout and lookback limit.") .description("First arg is the lookback limit (int), second is either a type keyword (:float, :int, :bool, :text, :datetime) or a schema record ({:field :type}).") .examples(&[ "(series 100 :float)", "(series 200 {:price :float :volume :int})", ]); // (push series value) -> Void env.register_native_fn( "push", StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Any, StaticType::Any]), ret: StaticType::Void, })), Purity::Impure, |args: &[Value]| { let (series_val, val) = (&args[0], &args[1]); if let Value::Series(s) = series_val { match s.as_pushable() { Some(p) => p.push_value(val.clone()), None => panic!("push expects a mutable series, got read-only {}", s.series_type_name()), } return Value::Void; } panic!("push expects a series as the first argument") }, ).doc("Pushes a new value into a series. After push, index 0 returns this value.") .examples(&[ "(push my-ticks {:price 10.5 :volume 100})", "(push prices 42.0)", ]); // (len series) -> Int env.register_native_fn( "len", StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Any]), ret: StaticType::Int, })), Purity::Pure, |args: &[Value]| { if let Value::Series(s) = &args[0] { return Value::Int(s.len() as i64); } Value::Int(0) }, ).doc("Returns the current number of items stored in a series.") .examples(&["(len my-ticks)"]); }