Refactor function signatures to use slices

This commit is contained in:
Michael Schimmel
2026-03-03 18:13:20 +01:00
parent 7c38dee243
commit 8c4db9a5ba
12 changed files with 262 additions and 209 deletions
+7 -7
View File
@@ -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);
}
+1 -1
View File
@@ -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))
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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,
}))
},
+2 -2
View File
@@ -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
View File
@@ -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() {
+9 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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 &current_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>,