Refactor function signatures to use slices
This commit is contained in:
@@ -88,7 +88,7 @@ impl Optimizer {
|
||||
if let Some(val) = sub.get_value(&addr)
|
||||
&& inliner.is_inlinable_value(val, addr)
|
||||
{
|
||||
return Rc::new(folder.make_constant_node(val.clone(), &node));
|
||||
return Rc::new(folder.make_constant_node(val.clone(), node));
|
||||
}
|
||||
|
||||
// 2. Try inlining from AST substitution map (pure expressions)
|
||||
@@ -104,7 +104,7 @@ impl Optimizer {
|
||||
if let Some(val) = globals.get(idx.0 as usize)
|
||||
&& inliner.is_inlinable_value(val, addr)
|
||||
{
|
||||
return Rc::new(folder.make_constant_node(val.clone(), &node));
|
||||
return Rc::new(folder.make_constant_node(val.clone(), node));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ impl Optimizer {
|
||||
if let BoundKind::Constant(Value::Record(layout, values)) = &rec_opt.kind
|
||||
&& let Some(idx) = layout.index_of(*field)
|
||||
{
|
||||
return Rc::new(folder.make_constant_node(values[idx].clone(), &node));
|
||||
return Rc::new(folder.make_constant_node(values[idx].clone(), node));
|
||||
}
|
||||
|
||||
if Rc::ptr_eq(&rec_opt, rec) {
|
||||
@@ -395,7 +395,7 @@ impl Optimizer {
|
||||
} else if let Some(else_node) = else_br {
|
||||
return self.visit_node(else_node.clone(), sub, path);
|
||||
} else {
|
||||
return Rc::new(folder.make_nop_node(&node));
|
||||
return Rc::new(folder.make_nop_node(node));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,7 +517,7 @@ impl Optimizer {
|
||||
|
||||
if self.enabled {
|
||||
if new_exprs.is_empty() {
|
||||
return Rc::new(folder.make_nop_node(&node));
|
||||
return Rc::new(folder.make_nop_node(node));
|
||||
} else if new_exprs.len() == 1 {
|
||||
return new_exprs.pop().unwrap();
|
||||
}
|
||||
@@ -533,7 +533,7 @@ impl Optimizer {
|
||||
positional_count,
|
||||
} => {
|
||||
let mut info = UsageInfo::default();
|
||||
info.collect(&node);
|
||||
info.collect(node);
|
||||
|
||||
let mut new_upvalues = Vec::new();
|
||||
let mut mapping = Vec::new();
|
||||
@@ -643,7 +643,7 @@ impl Optimizer {
|
||||
}
|
||||
|
||||
if self.enabled
|
||||
&& let Some(folded) = folder.try_fold_record(layout, &mapped_values, &node)
|
||||
&& let Some(folded) = folder.try_fold_record(layout, &mapped_values, node)
|
||||
{
|
||||
return Rc::new(folded);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ impl<'a> Folder<'a> {
|
||||
_ => return None,
|
||||
};
|
||||
let result = match func_val {
|
||||
Value::Function(f) => (f.func)(arg_values),
|
||||
Value::Function(f) => (f.func)(&arg_values),
|
||||
_ => return None,
|
||||
};
|
||||
Some(self.make_constant_node(result, callee))
|
||||
|
||||
@@ -229,7 +229,7 @@ impl Environment {
|
||||
name: &str,
|
||||
ty: StaticType,
|
||||
purity_level: Purity,
|
||||
func: impl Fn(Vec<Value>) -> Value + 'static,
|
||||
func: impl Fn(&[Value]) -> Value + 'static,
|
||||
) {
|
||||
self.register_native(
|
||||
name,
|
||||
@@ -501,7 +501,7 @@ impl Environment {
|
||||
pub fn run_script_compiled(&self, compiled: TypedNode) -> Result<Value, String> {
|
||||
let linked = self.link(compiled);
|
||||
let func = self.instantiate(linked);
|
||||
let res = (func.func)(vec![]);
|
||||
let res = (func.func)(&[]);
|
||||
self.run_pipeline();
|
||||
Ok(res)
|
||||
}
|
||||
@@ -526,7 +526,7 @@ impl Environment {
|
||||
.downcast_ref::<crate::ast::vm::Closure>()
|
||||
.is_some()
|
||||
{
|
||||
result = vm.run_with_args_observed(&mut observer, next_obj, next_args);
|
||||
result = vm.run_with_args_observed(&mut observer, next_obj, &next_args);
|
||||
} else {
|
||||
result = Err(format!(
|
||||
"Tail call target is not a closure: {}",
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ fn register_arithmetic(env: &Environment) {
|
||||
let mut acc = 0.0;
|
||||
for arg in args {
|
||||
if let Value::Int(i) = arg {
|
||||
acc += i as f64;
|
||||
acc += *i as f64;
|
||||
} else if let Value::Float(f) = arg {
|
||||
acc += f;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ fn register_arithmetic(env: &Environment) {
|
||||
let mut acc = 1.0;
|
||||
for arg in args {
|
||||
if let Value::Int(i) = arg {
|
||||
acc *= i as f64;
|
||||
acc *= *i as f64;
|
||||
} else if let Value::Float(f) = arg {
|
||||
acc *= f;
|
||||
}
|
||||
|
||||
+1
-1
@@ -168,7 +168,7 @@ pub fn register(env: &Environment) {
|
||||
let prng = std::rc::Rc::new(std::cell::RefCell::new(rng));
|
||||
|
||||
Value::Function(std::rc::Rc::new(crate::ast::types::NativeFunction {
|
||||
func: std::rc::Rc::new(move |_| Value::Float(prng.borrow_mut().f64())),
|
||||
func: std::rc::Rc::new(move |_args| Value::Float(prng.borrow_mut().f64())),
|
||||
purity: Purity::Impure,
|
||||
}))
|
||||
},
|
||||
|
||||
@@ -503,7 +503,7 @@ pub fn register(env: &Environment) {
|
||||
ret: StaticType::Any, // In reality: Object(RecordSeries)
|
||||
})),
|
||||
Purity::Impure,
|
||||
|args: std::vec::Vec<Value>| {
|
||||
|args: &[Value]| {
|
||||
if args.len() != 1 {
|
||||
panic!("create-series expects exactly 1 argument (a record)");
|
||||
}
|
||||
@@ -525,7 +525,7 @@ pub fn register(env: &Environment) {
|
||||
ret: StaticType::Void,
|
||||
})),
|
||||
Purity::Impure,
|
||||
|args: std::vec::Vec<Value>| {
|
||||
|args: &[Value]| {
|
||||
if args.len() != 2 {
|
||||
panic!("push-series expects exactly 2 arguments (series, record)");
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,4 +1,4 @@
|
||||
use crate::ast::types::Value;
|
||||
use crate::ast::types::{PipeFn, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -162,8 +162,8 @@ pub struct PipeStream {
|
||||
current_values: Vec<Value>,
|
||||
/// The current output signal of this pipe.
|
||||
current_signal: RefCell<Option<Signal>>,
|
||||
/// The executable closure representing the Lambda. Expects a Vec of arguments.
|
||||
pub executor: Option<Box<dyn FnMut(Vec<Value>) -> Value>>,
|
||||
/// The executable closure representing the Lambda. Expects a slice of arguments.
|
||||
pub executor: Option<Box<PipeFn>>,
|
||||
/// Observers of THIS pipe.
|
||||
observers: RefCell<Vec<Rc<RefCell<dyn Observer>>>>,
|
||||
}
|
||||
@@ -172,7 +172,7 @@ impl PipeStream {
|
||||
pub fn new(
|
||||
name: String,
|
||||
input_count: usize,
|
||||
executor: Option<Box<dyn FnMut(Vec<Value>) -> Value>>,
|
||||
executor: Option<Box<PipeFn>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
@@ -210,8 +210,8 @@ impl Observer for PipeStream {
|
||||
};
|
||||
|
||||
if barrier_reached {
|
||||
// 1. Prepare Arguments for Lambda (Current values of all inputs)
|
||||
let args = self.current_values.clone();
|
||||
// 1. Prepare Arguments for Lambda (Current values of all inputs) - NO CLONE NEEDED!
|
||||
let args = &self.current_values;
|
||||
|
||||
// 2. Execute Lambda using the encapsulated VM executor
|
||||
let result = if let Some(exec) = &mut self.executor {
|
||||
@@ -291,7 +291,7 @@ impl Observer for RecordPusher {
|
||||
/// This keeps the VM "dumb" and moves the buffer selection logic to the RTL.
|
||||
pub fn build_pipeline_node(
|
||||
inputs: Vec<Rc<dyn ObservableStream>>,
|
||||
executor: Box<dyn FnMut(Vec<Value>) -> Value>,
|
||||
executor: Box<PipeFn>,
|
||||
out_type: &StaticType,
|
||||
) -> Rc<PipelineNode> {
|
||||
use crate::ast::rtl::series::*;
|
||||
@@ -442,7 +442,7 @@ pub fn register(env: &Environment) {
|
||||
ret: StaticType::Series(Box::new(StaticType::Record(ohlc_layout.clone()))),
|
||||
})),
|
||||
Purity::Impure, // Modifies global generator registry
|
||||
move |args: std::vec::Vec<Value>| {
|
||||
move |args: &[Value]| {
|
||||
if args.len() != 2 {
|
||||
panic!("create-random-ohlc expects exactly 2 arguments (seed, limit)");
|
||||
}
|
||||
@@ -527,7 +527,7 @@ pub fn register(env: &Environment) {
|
||||
ret: StaticType::Any, // Returns StreamNode
|
||||
})),
|
||||
Purity::Impure,
|
||||
move |args: std::vec::Vec<Value>| {
|
||||
move |args: &[Value]| {
|
||||
if args.len() != 1 {
|
||||
panic!("create-ticker expects exactly 1 argument (the condition closure)");
|
||||
}
|
||||
@@ -550,7 +550,7 @@ pub fn register(env: &Environment) {
|
||||
|
||||
// 3. Create generator
|
||||
let generator = move || -> bool {
|
||||
match ticker_vm.run_with_args(my_closure.clone(), vec![]) {
|
||||
match ticker_vm.run_with_args(my_closure.clone(), &[]) {
|
||||
Ok(Value::Bool(b)) => {
|
||||
if b {
|
||||
// Ticker pulses with a simple `true` value or `Void`
|
||||
@@ -578,7 +578,7 @@ pub fn register(env: &Environment) {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::types::Value;
|
||||
use crate::ast::types::{PipeFn, Value};
|
||||
|
||||
#[test]
|
||||
fn test_root_to_pipe_flow() {
|
||||
|
||||
@@ -53,10 +53,10 @@ impl TypeRegistry {
|
||||
pub fn create_factory<T, F>(factory_func: F) -> Value
|
||||
where
|
||||
T: Scriptable,
|
||||
F: Fn(Vec<Value>) -> Result<T, String> + 'static,
|
||||
F: Fn(&[Value]) -> Result<T, String> + 'static,
|
||||
{
|
||||
// The factory is a script function that calls the Rust factory, gets T, then wraps it.
|
||||
let closure = move |args: Vec<Value>| -> Value {
|
||||
let closure = move |args: &[Value]| -> Value {
|
||||
match factory_func(args) {
|
||||
Ok(instance) => instance.wrap(),
|
||||
Err(msg) => {
|
||||
@@ -90,7 +90,7 @@ impl RecordBuilder {
|
||||
|
||||
pub fn method<F>(mut self, name: &str, func: F) -> Self
|
||||
where
|
||||
F: Fn(Vec<Value>) -> Value + 'static,
|
||||
F: Fn(&[Value]) -> Value + 'static,
|
||||
{
|
||||
let key = Keyword::intern(name);
|
||||
self.fields
|
||||
@@ -101,9 +101,9 @@ impl RecordBuilder {
|
||||
// Helper for methods that return Result (propagating panics for now)
|
||||
pub fn method_checked<F>(self, name: &str, func: F) -> Self
|
||||
where
|
||||
F: Fn(Vec<Value>) -> Result<Value, String> + 'static,
|
||||
F: Fn(&[Value]) -> Result<Value, String> + 'static,
|
||||
{
|
||||
let closure = move |args: Vec<Value>| match func(args) {
|
||||
let closure = move |args: &[Value]| match func(args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("Method call error: {}", e),
|
||||
};
|
||||
@@ -218,7 +218,7 @@ mod tests {
|
||||
let greet_fn = &values[idx];
|
||||
|
||||
if let Value::Function(f) = greet_fn {
|
||||
let res = (f.func)(vec![]);
|
||||
let res = (f.func)(&[]);
|
||||
if let Value::Text(s) = res {
|
||||
assert_eq!(&*s, "Hello Alice");
|
||||
} else {
|
||||
@@ -234,7 +234,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_factory() {
|
||||
let factory_val = TypeRegistry::create_factory(|args: Vec<Value>| {
|
||||
let factory_val = TypeRegistry::create_factory(|args: &[Value]| {
|
||||
if args.len() != 2 {
|
||||
return Err("Expected 2 args".to_string());
|
||||
}
|
||||
@@ -250,13 +250,13 @@ mod tests {
|
||||
});
|
||||
|
||||
if let Value::Function(f) = factory_val {
|
||||
let instance = (f.func)(vec![Value::Text("Bob".into()), Value::Int(40)]);
|
||||
let instance = (f.func)(&[Value::Text("Bob".into()), Value::Int(40)]);
|
||||
if let Value::Record(layout, values) = instance {
|
||||
let idx = layout.index_of(Keyword::intern("greet")).unwrap();
|
||||
let greet_fn = &values[idx];
|
||||
|
||||
if let Value::Function(gf) = greet_fn {
|
||||
let res = (gf.func)(vec![]);
|
||||
let res = (gf.func)(&[]);
|
||||
if let Value::Text(s) = res {
|
||||
assert_eq!(&*s, "Hello Bob");
|
||||
} else {
|
||||
|
||||
+5
-2
@@ -204,9 +204,12 @@ pub enum Purity {
|
||||
Pure,
|
||||
}
|
||||
|
||||
pub type NativeFn = dyn Fn(&[Value]) -> Value;
|
||||
pub type PipeFn = dyn FnMut(&[Value]) -> Value;
|
||||
|
||||
/// A native host function with metadata.
|
||||
pub struct NativeFunction {
|
||||
pub func: Rc<dyn Fn(Vec<Value>) -> Value>,
|
||||
pub func: Rc<NativeFn>,
|
||||
pub purity: Purity,
|
||||
}
|
||||
|
||||
@@ -503,7 +506,7 @@ impl Value {
|
||||
Value::Record(layout, Rc::new(values))
|
||||
}
|
||||
|
||||
pub fn make_function(purity: Purity, func: impl Fn(Vec<Value>) -> Value + 'static) -> Self {
|
||||
pub fn make_function(purity: Purity, func: impl Fn(&[Value]) -> Value + 'static) -> Self {
|
||||
Value::Function(Rc::new(NativeFunction {
|
||||
func: Rc::new(func),
|
||||
purity,
|
||||
|
||||
+219
-169
@@ -180,7 +180,7 @@ impl VM {
|
||||
pub fn run_with_args(
|
||||
&mut self,
|
||||
closure_obj: Rc<dyn Object>,
|
||||
args: Vec<Value>,
|
||||
args: &[Value],
|
||||
) -> Result<Value, String> {
|
||||
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
|
||||
self.stack.clear();
|
||||
@@ -192,9 +192,9 @@ impl VM {
|
||||
if let Some(count) = closure.positional_count
|
||||
&& args.len() == count as usize
|
||||
{
|
||||
self.stack.extend(args);
|
||||
self.stack.extend_from_slice(args);
|
||||
} else {
|
||||
self.unpack(&closure.parameter_node, &args, &mut 0)?;
|
||||
self.unpack(&closure.parameter_node, args, &mut 0)?;
|
||||
}
|
||||
self.eval(&closure.exec_node)
|
||||
}
|
||||
@@ -203,7 +203,7 @@ impl VM {
|
||||
&mut self,
|
||||
observer: &mut O,
|
||||
closure_obj: Rc<dyn Object>,
|
||||
args: Vec<Value>,
|
||||
args: &[Value],
|
||||
) -> Result<Value, String> {
|
||||
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
|
||||
self.stack.clear();
|
||||
@@ -215,9 +215,9 @@ impl VM {
|
||||
if let Some(count) = closure.positional_count
|
||||
&& args.len() == count as usize
|
||||
{
|
||||
self.stack.extend(args);
|
||||
self.stack.extend_from_slice(args);
|
||||
} else {
|
||||
self.unpack(&closure.parameter_node, &args, &mut 0)?;
|
||||
self.unpack(&closure.parameter_node, args, &mut 0)?;
|
||||
}
|
||||
self.eval_observed(observer, &closure.exec_node)
|
||||
}
|
||||
@@ -419,8 +419,8 @@ impl VM {
|
||||
// Create the persistent execution closure for the PipeStream
|
||||
let mut pipe_vm = crate::ast::vm::VM::new(self.globals.clone());
|
||||
let my_closure = lambda_obj.clone();
|
||||
let executor: Box<dyn FnMut(Vec<Value>) -> Value> =
|
||||
Box::new(move |args: Vec<Value>| -> Value {
|
||||
let executor: Box<crate::ast::types::PipeFn> =
|
||||
Box::new(move |args: &[Value]| -> Value {
|
||||
match pipe_vm.run_with_args(my_closure.clone(), args) {
|
||||
Ok(res) => res,
|
||||
Err(e) => panic!("Pipeline lambda execution failed: {}", e),
|
||||
@@ -462,32 +462,21 @@ impl VM {
|
||||
BoundKind::Call { callee, args } => {
|
||||
let mut func_val = self.eval_internal(obs, callee)?;
|
||||
|
||||
let mut arg_vals = match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut vals = Vec::with_capacity(elements.len());
|
||||
let mut is_complex = false;
|
||||
for e in elements {
|
||||
if matches!(e.kind, BoundKind::Tuple { .. }) {
|
||||
is_complex = true;
|
||||
break;
|
||||
}
|
||||
vals.push(self.eval_internal(obs, e)?);
|
||||
}
|
||||
if is_complex {
|
||||
self.prepare_args_internal(obs, args)?
|
||||
} else {
|
||||
vals
|
||||
}
|
||||
}
|
||||
_ => self.prepare_args_internal(obs, args)?,
|
||||
};
|
||||
let base = self.stack.len();
|
||||
if let Err(e) = self.eval_args_to_stack(obs, args) {
|
||||
self.stack.truncate(base);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if node.ty.is_tail {
|
||||
let arg_vals = self.stack[base..].to_vec();
|
||||
self.stack.truncate(base);
|
||||
|
||||
match func_val {
|
||||
Value::Object(obj) => {
|
||||
return Ok(Value::TailCallRequest(Box::new((obj, arg_vals))));
|
||||
}
|
||||
Value::Function(f) => return Ok((f.func)(arg_vals)),
|
||||
Value::Function(f) => return Ok((f.func)(&arg_vals)),
|
||||
Value::FieldAccessor(k) => {
|
||||
if arg_vals.len() != 1 {
|
||||
return Err(format!(
|
||||
@@ -547,148 +536,166 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
// Standard Call Path
|
||||
let mut current_func = func_val;
|
||||
loop {
|
||||
match func_val {
|
||||
Value::Function(f) => break Ok((f.func)(arg_vals)),
|
||||
let result = match ¤t_func {
|
||||
Value::Function(f) => {
|
||||
let res = (f.func)(&self.stack[base..]);
|
||||
self.stack.truncate(base);
|
||||
return Ok(res);
|
||||
}
|
||||
Value::FieldAccessor(k) => {
|
||||
if arg_vals.len() != 1 {
|
||||
break Err(format!(
|
||||
let arg_len = self.stack.len() - base;
|
||||
let res = if arg_len != 1 {
|
||||
Err(format!(
|
||||
"Field accessor .{} expects exactly 1 argument, got {}",
|
||||
k.name(),
|
||||
arg_vals.len()
|
||||
));
|
||||
}
|
||||
let rec = &arg_vals[0];
|
||||
if let Value::Record(layout, values) = rec {
|
||||
if let Some(idx) = layout.index_of(k) {
|
||||
break Ok(values[idx].clone());
|
||||
} else {
|
||||
break Err(format!("Record does not have field :{}", k.name()));
|
||||
}
|
||||
} else if let Value::Object(obj) = rec {
|
||||
// Polymorphic Field Access: Allow `.field` on a RecordSeries
|
||||
if let Some(rs) = obj
|
||||
.as_any()
|
||||
.downcast_ref::<crate::ast::rtl::series::RecordSeries>()
|
||||
{
|
||||
if let Some(field_series) = rs.field(k) {
|
||||
let view = crate::ast::rtl::series::SeriesView::new(
|
||||
field_series,
|
||||
k,
|
||||
);
|
||||
break Ok(Value::Object(std::rc::Rc::new(view)));
|
||||
} else {
|
||||
break Err(format!(
|
||||
"RecordSeries does not have field :{}",
|
||||
k.name()
|
||||
));
|
||||
}
|
||||
}
|
||||
break Err(format!(
|
||||
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||
k.name(),
|
||||
obj.type_name()
|
||||
));
|
||||
arg_len
|
||||
))
|
||||
} else {
|
||||
break Err(format!(
|
||||
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||
k.name(),
|
||||
rec
|
||||
));
|
||||
}
|
||||
let rec = &self.stack[base];
|
||||
if let Value::Record(layout, values) = rec {
|
||||
if let Some(idx) = layout.index_of(*k) {
|
||||
Ok(values[idx].clone())
|
||||
} else {
|
||||
Err(format!("Record does not have field :{}", k.name()))
|
||||
}
|
||||
} else if let Value::Object(obj) = rec {
|
||||
if let Some(rs) = obj
|
||||
.as_any()
|
||||
.downcast_ref::<crate::ast::rtl::series::RecordSeries>()
|
||||
{
|
||||
if let Some(field_series) = rs.field(*k) {
|
||||
let view = crate::ast::rtl::series::SeriesView::new(
|
||||
field_series,
|
||||
*k,
|
||||
);
|
||||
Ok(Value::Object(std::rc::Rc::new(view)))
|
||||
} else {
|
||||
Err(format!(
|
||||
"RecordSeries does not have field :{}",
|
||||
k.name()
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(format!(
|
||||
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||
k.name(),
|
||||
obj.type_name()
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(format!(
|
||||
"Field accessor .{} expects a record or RecordSeries, got {}",
|
||||
k.name(),
|
||||
rec
|
||||
))
|
||||
}
|
||||
};
|
||||
self.stack.truncate(base);
|
||||
return res;
|
||||
}
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
let old_stack_top = self.stack.len();
|
||||
self.frames.push(CallFrame {
|
||||
stack_base: old_stack_top,
|
||||
stack_base: base,
|
||||
closure: Some(obj.clone()),
|
||||
});
|
||||
if let Some(count) = closure.positional_count
|
||||
&& arg_vals.len() == count as usize
|
||||
|
||||
let unpack_res = if let Some(count) = closure.positional_count
|
||||
&& (self.stack.len() - base) == count as usize
|
||||
{
|
||||
self.stack.extend(arg_vals);
|
||||
Ok(())
|
||||
} else {
|
||||
// Map each parameter pattern to the provided arguments
|
||||
let args_for_unpack = self.stack[base..].to_vec();
|
||||
self.stack.truncate(base);
|
||||
if let BoundKind::Tuple { elements } =
|
||||
&closure.parameter_node.kind
|
||||
{
|
||||
let mut offset = 0;
|
||||
let mut res = Ok(());
|
||||
for el in elements {
|
||||
self.unpack(el, &arg_vals, &mut offset)?;
|
||||
if let Err(e) =
|
||||
self.unpack(el, &args_for_unpack, &mut offset)
|
||||
{
|
||||
res = Err(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
res
|
||||
} else {
|
||||
self.unpack(&closure.parameter_node, &arg_vals, &mut 0)?;
|
||||
self.unpack(&closure.parameter_node, &args_for_unpack, &mut 0)
|
||||
}
|
||||
}
|
||||
let result = self.eval_internal(obs, &closure.exec_node);
|
||||
};
|
||||
|
||||
let res = match unpack_res {
|
||||
Ok(_) => self.eval_internal(obs, &closure.exec_node),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
self.frames.pop();
|
||||
self.stack.truncate(old_stack_top);
|
||||
match result {
|
||||
Ok(Value::TailCallRequest(payload)) => {
|
||||
let (next_obj, next_args) = *payload;
|
||||
func_val = Value::Object(next_obj);
|
||||
arg_vals = next_args;
|
||||
continue;
|
||||
}
|
||||
res => break res,
|
||||
}
|
||||
res
|
||||
} else if let Some(series) = obj.as_series() {
|
||||
// Unified Series Access (Step 1 of Dual Series Architecture)
|
||||
// This handles RecordSeries, SeriesView, and future SharedSeries polymorphically.
|
||||
if arg_vals.len() != 1 {
|
||||
break Err(format!(
|
||||
let arg_len = self.stack.len() - base;
|
||||
let res = if arg_len != 1 {
|
||||
Err(format!(
|
||||
"{} indexer expects exactly 1 argument (the lookback index)",
|
||||
obj.type_name()
|
||||
));
|
||||
}
|
||||
if let Value::Int(idx) = arg_vals[0] {
|
||||
))
|
||||
} else if let Value::Int(idx) = self.stack[base] {
|
||||
if idx < 0 {
|
||||
break Err(format!(
|
||||
Err(format!(
|
||||
"{} lookback index cannot be negative",
|
||||
obj.type_name()
|
||||
));
|
||||
}
|
||||
if let Some(val) = series.get_item(idx as usize) {
|
||||
break Ok(val);
|
||||
))
|
||||
} else if let Some(val) = series.get_item(idx as usize) {
|
||||
Ok(val)
|
||||
} else {
|
||||
// Out of bounds / not enough data yet
|
||||
break Ok(Value::Void);
|
||||
Ok(Value::Void)
|
||||
}
|
||||
} else {
|
||||
break Err(format!(
|
||||
Err(format!(
|
||||
"{} index must be an integer",
|
||||
obj.type_name()
|
||||
));
|
||||
}
|
||||
))
|
||||
};
|
||||
self.stack.truncate(base);
|
||||
return res;
|
||||
} else {
|
||||
break Err(format!("Object is not callable: {}", obj.type_name()));
|
||||
self.stack.truncate(base);
|
||||
return Err(format!("Object is not callable: {}", obj.type_name()));
|
||||
}
|
||||
}
|
||||
_ => break Err(format!("Attempt to call non-function: {}", func_val)),
|
||||
_ => {
|
||||
self.stack.truncate(base);
|
||||
return Err(format!("Attempt to call non-function: {}", current_func));
|
||||
}
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(Value::TailCallRequest(payload)) => {
|
||||
let (next_obj, next_args) = *payload;
|
||||
current_func = Value::Object(next_obj);
|
||||
self.stack.truncate(base);
|
||||
self.stack.extend(next_args);
|
||||
continue;
|
||||
}
|
||||
res => {
|
||||
self.stack.truncate(base);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BoundKind::Again { args } => {
|
||||
let arg_vals = match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut vals = Vec::with_capacity(elements.len());
|
||||
let mut is_complex = false;
|
||||
for e in elements {
|
||||
if matches!(e.kind, BoundKind::Tuple { .. }) {
|
||||
is_complex = true;
|
||||
break;
|
||||
}
|
||||
vals.push(self.eval_internal(obs, e)?);
|
||||
}
|
||||
if is_complex {
|
||||
self.prepare_args_internal(obs, args)?
|
||||
} else {
|
||||
vals
|
||||
}
|
||||
}
|
||||
_ => self.prepare_args_internal(obs, args)?,
|
||||
};
|
||||
let base = self.stack.len();
|
||||
if let Err(e) = self.eval_args_to_stack(obs, args) {
|
||||
self.stack.truncate(base);
|
||||
return Err(e);
|
||||
}
|
||||
let arg_vals = self.stack[base..].to_vec();
|
||||
self.stack.truncate(base);
|
||||
|
||||
let frame = self.frames.last().ok_or("No call frame for 'again'")?;
|
||||
if let Some(closure_obj) = &frame.closure {
|
||||
@@ -717,7 +724,19 @@ impl VM {
|
||||
std::rc::Rc::new(evaluated_values),
|
||||
))
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => self.eval_internal(obs, bound_expanded),
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
let mut curr = bound_expanded;
|
||||
if !O::ACTIVE {
|
||||
while let BoundKind::Expansion {
|
||||
bound_expanded: next,
|
||||
..
|
||||
} = &curr.kind
|
||||
{
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
self.eval_internal(obs, curr)
|
||||
}
|
||||
BoundKind::Extension(ext) => Err(format!(
|
||||
"Execution of extension '{}' not implemented yet",
|
||||
ext.display_name()
|
||||
@@ -730,7 +749,7 @@ impl VM {
|
||||
while let Value::TailCallRequest(payload) = result {
|
||||
let (next_obj, next_args) = *payload;
|
||||
if next_obj.as_any().downcast_ref::<Closure>().is_some() {
|
||||
result = match self.run_with_args(next_obj, next_args) {
|
||||
result = match self.run_with_args(next_obj, &next_args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("Myc Runtime Error (TailCall): {}", e),
|
||||
};
|
||||
@@ -744,6 +763,74 @@ impl VM {
|
||||
result
|
||||
}
|
||||
|
||||
fn eval_args_to_stack<O: VMObserver>(
|
||||
&mut self,
|
||||
obs: &mut O,
|
||||
args: &ExecNode,
|
||||
) -> Result<(), String> {
|
||||
match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
for e in elements {
|
||||
let mut curr = e.as_ref();
|
||||
if !O::ACTIVE {
|
||||
while let BoundKind::Expansion {
|
||||
bound_expanded: next,
|
||||
..
|
||||
} = &curr.kind
|
||||
{
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
|
||||
match &curr.kind {
|
||||
BoundKind::Constant(v) => self.stack.push(v.clone()),
|
||||
_ => {
|
||||
let val = self.eval_internal(obs, curr)?;
|
||||
self.stack.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
BoundKind::Constant(v) => {
|
||||
if let Some(slice) = v.as_slice() {
|
||||
self.stack.extend_from_slice(slice);
|
||||
} else {
|
||||
self.stack.push(v.clone());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
let mut curr = bound_expanded;
|
||||
if !O::ACTIVE {
|
||||
while let BoundKind::Expansion {
|
||||
bound_expanded: next,
|
||||
..
|
||||
} = &curr.kind
|
||||
{
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
let val = self.eval_internal(obs, curr)?;
|
||||
if let Some(slice) = val.as_slice() {
|
||||
self.stack.extend_from_slice(slice);
|
||||
} else {
|
||||
self.stack.push(val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
let val = self.eval_internal(obs, args)?;
|
||||
if let Some(slice) = val.as_slice() {
|
||||
self.stack.extend_from_slice(slice);
|
||||
} else {
|
||||
self.stack.push(val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> {
|
||||
match addr {
|
||||
Address::Local(slot) => {
|
||||
@@ -865,43 +952,6 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
fn flatten_value(val: Value, into: &mut Vec<Value>) {
|
||||
if let Some(values) = val.as_slice() {
|
||||
into.extend_from_slice(values);
|
||||
} else {
|
||||
into.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_args_internal<O: VMObserver>(
|
||||
&mut self,
|
||||
obs: &mut O,
|
||||
args: &ExecNode,
|
||||
) -> Result<Vec<Value>, String> {
|
||||
let mut arg_vals = Vec::new();
|
||||
match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
self.eval_and_flatten_internal(obs, elements, &mut arg_vals)?;
|
||||
}
|
||||
_ => {
|
||||
VM::flatten_value(self.eval_internal(obs, args)?, &mut arg_vals);
|
||||
}
|
||||
}
|
||||
Ok(arg_vals)
|
||||
}
|
||||
|
||||
fn eval_and_flatten_internal<O: VMObserver>(
|
||||
&mut self,
|
||||
obs: &mut O,
|
||||
elements: &[Rc<ExecNode>],
|
||||
into: &mut Vec<Value>,
|
||||
) -> Result<(), String> {
|
||||
for e in elements {
|
||||
into.push(self.eval_internal(obs, e)?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unpack<T>(
|
||||
&mut self,
|
||||
pattern: &Node<BoundKind<T>, T>,
|
||||
|
||||
@@ -75,7 +75,7 @@ mod tests {
|
||||
.expect("Failed to compile");
|
||||
let linked = env.link(compiled);
|
||||
let func = env.instantiate(linked);
|
||||
let result: Result<Value, String> = Ok((func.func)(vec![]));
|
||||
let result: Result<Value, String> = Ok((func.func)(&[]));
|
||||
|
||||
match result {
|
||||
Ok(Value::Int(20)) => (),
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ pub fn run_benchmarks(update: bool, filter: Option<&str>) -> Vec<BenchmarkResult
|
||||
let mut total = Duration::ZERO;
|
||||
for _ in 0..n {
|
||||
let start = Instant::now();
|
||||
let _ = (func.func)(vec![]);
|
||||
let _ = (func.func)(&[]);
|
||||
total += start.elapsed();
|
||||
}
|
||||
Ok(total)
|
||||
|
||||
Reference in New Issue
Block a user