diff --git a/src/ast/environment.rs b/src/ast/environment.rs index e13e77a..607c1d6 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -15,7 +15,7 @@ use crate::ast::compiler::bound_nodes::{ }; use crate::ast::compiler::dumper::Dumper; use crate::ast::compiler::lambda_collector::LambdaCollector; -use crate::ast::compiler::lowering::{ExecNode, Lowering}; +use crate::ast::compiler::lowering::{Lowering, ExecNode}; use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry}; use crate::ast::compiler::optimizer::Optimizer; use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer}; diff --git a/src/ast/vm.rs b/src/ast/vm.rs index cf3b616..787dede 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -335,7 +335,7 @@ impl VM { val.clone() }; - self.set_value(*addr, store_val)?; + self.set_value(addr.clone(), store_val)?; // Define always evaluates to the unwrapped value for immediate use. Ok(val) } @@ -351,18 +351,14 @@ impl VM { } Ok(val) } - BoundKind::Get { addr, .. } => self.get_value(*addr), + BoundKind::Get { addr, .. } => self.get_value(addr.clone()), - BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(*k)), + BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(k.clone())), BoundKind::GetField { rec, field } => { let rec_val = self.eval_internal(obs, rec)?; - // In Rust, pattern matching (`match`) is the idiomatic way to handle variants safely. - // Previously, this only handled `Value::Record`. Now, we handle objects (like `RecordSeries`) polymorphically. match rec_val { - // Case 1: The classic Record. - // This is a struct-like tuple containing an Arc and a Vec. Value::Record(layout, values) => { if let Some(idx) = layout.index_of(*field) { Ok(values[idx].clone()) @@ -370,28 +366,14 @@ impl VM { Err(format!("Record does not have field :{}", field.name())) } } - - // Case 2: A dynamic Object (our SoA / Struct-of-Arrays optimization). - // `Value::Object` holds an `Rc` - a reference-counted trait object (type-erased). Value::Object(obj) => { - // 1. We get the raw `&dyn Any` reference (Rust's standard mechanism for runtime type reflection). let any_ptr = obj.as_any(); - - // 2. Downcast! We check at runtime if the pointer actually points to a `RecordSeries`. - // `downcast_ref` is very fast (essentially an O(1) type ID comparison under the hood). if let Some(record_series) = any_ptr.downcast_ref::() { - // 3. We call our highly performant 0-copy method on the series. - // It returns an `Rc>`, which is a shared pointer - // to the concrete column array (e.g., a `ScalarSeries`). if let Some(field_series) = record_series.field(*field) { - // 4. We wrap this RefCell in our `SeriesView` struct. - // The `SeriesView` acts as a pure `Object` for the VM, holding the reference. - // CRITICAL: No array elements are copied here! This is pure, fast pointer juggling. - // This single operation turns a SoA `RecordSeries` into a high-speed `FloatSeries` view. let view = - crate::ast::rtl::series::SeriesView::new(field_series, *field); + crate::ast::rtl::series::SeriesView::new(field_series, field.clone()); return Ok(Value::Object(std::rc::Rc::new(view))); } else { return Err(format!( @@ -400,18 +382,14 @@ impl VM { )); } } else if let Some(sn) = obj.as_any().downcast_ref::() { - let mapped = crate::ast::rtl::streams::build_map_stream(sn.inner.clone(), *field); + let mapped = crate::ast::rtl::streams::build_map_stream(sn.inner.clone(), field.clone()); return Ok(Value::Object(Rc::new(mapped))); } - - // Fallback if it's another type of object that is not a RecordSeries. Err(format!( "Attempt to access field on non-record object: {}", obj.type_name() )) } - - // Error handling for primitives (Int, Float, etc.). _ => Err(format!( "Attempt to access field on non-record: {}", rec_val @@ -421,7 +399,7 @@ impl VM { BoundKind::Set { addr, value } => { let val = self.eval_internal(obs, value)?; - self.set_value(*addr, val.clone())?; + self.set_value(addr.clone(), val.clone())?; Ok(val) } BoundKind::If {