Refactor: Use Value::Closure enum variant
Previously, compiled closures were stored as `Value::Object` and then downcasted. This commit introduces a dedicated `Value::Closure` enum variant to represent compiled closures. This improves type safety and simplifies handling of closures throughout the compiler and VM. This change involves: - Updating type definitions to use `Value::Closure`. - Adjusting downcasting logic to directly access the `Closure` struct. - Modifying `run_with_args` and `run_with_args_observed` to expect `Value::Closure` for tail-call targets. - Refining the handling of closures in the `Optimizer` and `Inliner` for better type clarity.
This commit is contained in:
@@ -3,7 +3,7 @@ use crate::ast::nodes::{
|
|||||||
IdentifierBinding, LambdaBinding, Node, NodeKind, UpvalueIdx, VirtualId,
|
IdentifierBinding, LambdaBinding, Node, NodeKind, UpvalueIdx, VirtualId,
|
||||||
};
|
};
|
||||||
use crate::ast::types::{Purity, Value};
|
use crate::ast::types::{Purity, Value};
|
||||||
use crate::ast::closure::Closure;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@@ -343,9 +343,9 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let NodeKind::Constant(Value::Object(ref obj)) = callee_opt.kind
|
if let NodeKind::Constant(Value::Closure(ref closure_rc)) = callee_opt.kind
|
||||||
&& path.inlining_depth < 5
|
&& path.inlining_depth < 5
|
||||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
&& let closure = closure_rc.as_ref()
|
||||||
&& (closure.upvalues.is_empty()
|
&& (closure.upvalues.is_empty()
|
||||||
|| closure.function_node.ty.purity >= Purity::SideEffectFree)
|
|| closure.function_node.ty.purity >= Purity::SideEffectFree)
|
||||||
&& !closure.function_node.ty.is_recursive
|
&& !closure.function_node.ty.is_recursive
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::ast::nodes::{
|
|||||||
Address, AnalyzedNode, IdentifierBinding, NodeKind, VirtualId,
|
Address, AnalyzedNode, IdentifierBinding, NodeKind, VirtualId,
|
||||||
};
|
};
|
||||||
use crate::ast::types::{Purity, Value};
|
use crate::ast::types::{Purity, Value};
|
||||||
use crate::ast::closure::Closure;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -35,12 +35,8 @@ impl<'a> Inliner<'a> {
|
|||||||
| Value::Keyword(_)
|
| Value::Keyword(_)
|
||||||
| Value::Record(_, _)
|
| Value::Record(_, _)
|
||||||
| Value::DateTime(_) => true,
|
| Value::DateTime(_) => true,
|
||||||
Value::Object(obj) => {
|
Value::Closure(closure_rc) => {
|
||||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
closure_rc.upvalues.is_empty() && !closure_rc.function_node.ty.is_recursive
|
||||||
closure.upvalues.is_empty() && !closure.function_node.ty.is_recursive
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::ast::nodes::{
|
|||||||
NodeKind, VirtualId,
|
NodeKind, VirtualId,
|
||||||
};
|
};
|
||||||
use crate::ast::types::{Identity, Value};
|
use crate::ast::types::{Identity, Value};
|
||||||
use crate::ast::closure::Closure;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
// --- PathTracker ---
|
// --- PathTracker ---
|
||||||
@@ -56,12 +56,10 @@ impl UsageInfo {
|
|||||||
self.collect(value);
|
self.collect(value);
|
||||||
}
|
}
|
||||||
NodeKind::Constant(v) => {
|
NodeKind::Constant(v) => {
|
||||||
if let Value::Object(obj) = v
|
if let Value::Closure(closure_rc) = v {
|
||||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
|
||||||
{
|
|
||||||
self.used_identities
|
self.used_identities
|
||||||
.insert(closure.function_node.identity.clone());
|
.insert(closure_rc.function_node.identity.clone());
|
||||||
self.collect(&closure.function_node);
|
self.collect(&closure_rc.function_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeKind::Identifier { binding, .. } => {
|
NodeKind::Identifier { binding, .. } => {
|
||||||
|
|||||||
+3
-12
@@ -668,13 +668,11 @@ impl Environment {
|
|||||||
info.positional_count,
|
info.positional_count,
|
||||||
node.ty.stack_size,
|
node.ty.stack_size,
|
||||||
));
|
));
|
||||||
let closure_obj: Rc<dyn Object> = closure;
|
|
||||||
|
|
||||||
return Rc::new(NativeFunction {
|
return Rc::new(NativeFunction {
|
||||||
purity: Purity::Impure,
|
purity: Purity::Impure,
|
||||||
func: Rc::new(move |args| {
|
func: Rc::new(move |args| {
|
||||||
let mut vm = VM::new(root_values.clone());
|
let mut vm = VM::new(root_values.clone());
|
||||||
match vm.run_with_args(closure_obj.clone(), args) {
|
match vm.run_with_args(closure.clone(), args) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => panic!("Myc Runtime Error: {}", e),
|
Err(e) => panic!("Myc Runtime Error: {}", e),
|
||||||
}
|
}
|
||||||
@@ -692,12 +690,7 @@ impl Environment {
|
|||||||
Err(e) => panic!("Myc Runtime Error: {}", e),
|
Err(e) => panic!("Myc Runtime Error: {}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Value::Object(obj) = &res
|
if let Value::Closure(obj) = &res {
|
||||||
&& obj
|
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<Closure>()
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
match vm.run_with_args(obj.clone(), args) {
|
match vm.run_with_args(obj.clone(), args) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => panic!("Myc Runtime Error (Closure): {}", e),
|
Err(e) => panic!("Myc Runtime Error (Closure): {}", e),
|
||||||
@@ -837,9 +830,7 @@ impl Environment {
|
|||||||
// 2. Execute the root closure immediately to get the actual script result.
|
// 2. Execute the root closure immediately to get the actual script result.
|
||||||
// All Myc scripts are wrapped in a parameterless lambda for consistency.
|
// All Myc scripts are wrapped in a parameterless lambda for consistency.
|
||||||
let mut final_result = result;
|
let mut final_result = result;
|
||||||
if let Ok(Value::Object(obj)) = &final_result
|
if let Ok(Value::Closure(obj)) = &final_result {
|
||||||
&& let Some(_closure) = obj.as_any().downcast_ref::<Closure>()
|
|
||||||
{
|
|
||||||
final_result = vm.run_with_args_observed(&mut observer, obj.clone(), &[]);
|
final_result = vm.run_with_args_observed(&mut observer, obj.clone(), &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -432,8 +432,8 @@ pub fn register(env: &Environment) {
|
|||||||
panic!("create-ticker expects exactly 1 argument (the condition closure)");
|
panic!("create-ticker expects exactly 1 argument (the condition closure)");
|
||||||
}
|
}
|
||||||
|
|
||||||
let closure_obj = if let Value::Object(obj) = &args[0] {
|
let closure_obj = if let Value::Closure(rc) = &args[0] {
|
||||||
obj.clone()
|
rc.clone()
|
||||||
} else {
|
} else {
|
||||||
panic!("create-ticker expects a closure as its argument");
|
panic!("create-ticker expects a closure as its argument");
|
||||||
};
|
};
|
||||||
|
|||||||
+6
-1
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::ast::closure::Closure;
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@@ -257,7 +258,8 @@ pub enum Value {
|
|||||||
Record(std::sync::Arc<RecordLayout>, ValueList),
|
Record(std::sync::Arc<RecordLayout>, ValueList),
|
||||||
FieldAccessor(Keyword),
|
FieldAccessor(Keyword),
|
||||||
Function(Rc<NativeFunction>),
|
Function(Rc<NativeFunction>),
|
||||||
Object(Rc<dyn Object>), // For compiled Closures and other opaque types
|
Closure(Rc<Closure>), // Compiled function with captured upvalues
|
||||||
|
Object(Rc<dyn Object>), // For Series, Streams, SyntaxNodes, and custom extensions
|
||||||
Cell(Rc<RefCell<Value>>), // Boxed value for captures
|
Cell(Rc<RefCell<Value>>), // Boxed value for captures
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,6 +279,7 @@ impl PartialEq for Value {
|
|||||||
}
|
}
|
||||||
(Value::FieldAccessor(a), Value::FieldAccessor(b)) => a == b,
|
(Value::FieldAccessor(a), Value::FieldAccessor(b)) => a == b,
|
||||||
(Value::Function(a), Value::Function(b)) => Rc::ptr_eq(a, b),
|
(Value::Function(a), Value::Function(b)) => Rc::ptr_eq(a, b),
|
||||||
|
(Value::Closure(a), Value::Closure(b)) => Rc::ptr_eq(a, b),
|
||||||
(Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b),
|
(Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b),
|
||||||
(Value::Cell(a), Value::Cell(b)) => Rc::ptr_eq(a, b),
|
(Value::Cell(a), Value::Cell(b)) => Rc::ptr_eq(a, b),
|
||||||
_ => false,
|
_ => false,
|
||||||
@@ -617,6 +620,7 @@ impl Value {
|
|||||||
Value::Record(layout, _) => StaticType::Record(layout.clone()),
|
Value::Record(layout, _) => StaticType::Record(layout.clone()),
|
||||||
Value::FieldAccessor(k) => StaticType::FieldAccessor(*k),
|
Value::FieldAccessor(k) => StaticType::FieldAccessor(*k),
|
||||||
Value::Function(_) => StaticType::Any, // Dynamic function
|
Value::Function(_) => StaticType::Any, // Dynamic function
|
||||||
|
Value::Closure(_) => StaticType::Object("closure"),
|
||||||
Value::Object(o) => StaticType::Object(o.type_name()),
|
Value::Object(o) => StaticType::Object(o.type_name()),
|
||||||
Value::Cell(c) => c.borrow().static_type(),
|
Value::Cell(c) => c.borrow().static_type(),
|
||||||
}
|
}
|
||||||
@@ -660,6 +664,7 @@ impl fmt::Display for Value {
|
|||||||
}
|
}
|
||||||
Value::FieldAccessor(k) => write!(f, ".{}", k.name()),
|
Value::FieldAccessor(k) => write!(f, ".{}", k.name()),
|
||||||
Value::Function(f_meta) => write!(f, "<native fn, {:?}>", f_meta.purity),
|
Value::Function(f_meta) => write!(f, "<native fn, {:?}>", f_meta.purity),
|
||||||
|
Value::Closure(_) => write!(f, "<closure>"),
|
||||||
Value::Object(o) => write!(f, "<{:?}>", o),
|
Value::Object(o) => write!(f, "<{:?}>", o),
|
||||||
Value::Cell(c) => write!(f, "{}", c.borrow()),
|
Value::Cell(c) => write!(f, "{}", c.borrow()),
|
||||||
}
|
}
|
||||||
|
|||||||
+113
-110
@@ -2,7 +2,7 @@ use crate::ast::closure::Closure;
|
|||||||
use crate::ast::nodes::{Address, ExecNode, IdentifierBinding, NodeKind, StackOffset};
|
use crate::ast::nodes::{Address, ExecNode, IdentifierBinding, NodeKind, StackOffset};
|
||||||
use crate::ast::rtl::series::{RecordSeries, SeriesView};
|
use crate::ast::rtl::series::{RecordSeries, SeriesView};
|
||||||
use crate::ast::rtl::streams::{build_map_stream, build_pipeline_node, StreamNode};
|
use crate::ast::rtl::streams::{build_map_stream, build_pipeline_node, StreamNode};
|
||||||
use crate::ast::types::{Object, PipeFn, Value};
|
use crate::ast::types::{PipeFn, Value};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ pub struct VM {
|
|||||||
frames: Vec<CallFrame>,
|
frames: Vec<CallFrame>,
|
||||||
/// Side-channel for tail-call signaling. Set by `eval_internal` when a tail-call
|
/// Side-channel for tail-call signaling. Set by `eval_internal` when a tail-call
|
||||||
/// is requested; consumed by `resolve_tail_calls` and the inline TCO loop.
|
/// is requested; consumed by `resolve_tail_calls` and the inline TCO loop.
|
||||||
tail_call: Option<(Rc<dyn Object>, Vec<Value>)>,
|
tail_call: Option<(Value, Vec<Value>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VM {
|
impl VM {
|
||||||
@@ -111,60 +111,68 @@ impl VM {
|
|||||||
mut result: Result<Value, String>,
|
mut result: Result<Value, String>,
|
||||||
) -> Result<Value, String> {
|
) -> Result<Value, String> {
|
||||||
loop {
|
loop {
|
||||||
if let Some((next_obj, next_args)) = self.tail_call.take() {
|
if let Some((next_val, next_args)) = self.tail_call.take() {
|
||||||
if let Ok(closure_rc) = next_obj.clone().into_rc_any().downcast::<Closure>() {
|
match next_val {
|
||||||
self.stack.clear();
|
Value::Closure(closure_rc) => {
|
||||||
// frames should be empty here since we popped before entering the loop
|
self.stack.clear();
|
||||||
self.frames.push(CallFrame {
|
// frames should be empty here since we popped before entering the loop
|
||||||
stack_base: 0,
|
self.frames.push(CallFrame {
|
||||||
closure: Some(closure_rc.clone()),
|
stack_base: 0,
|
||||||
});
|
closure: Some(closure_rc.clone()),
|
||||||
let closure = closure_rc.as_ref();
|
});
|
||||||
if let Some(count) = closure.positional_count
|
let closure = closure_rc.as_ref();
|
||||||
&& next_args.len() == count as usize
|
if let Some(count) = closure.positional_count
|
||||||
{
|
&& next_args.len() == count as usize
|
||||||
self.stack.extend(next_args);
|
{
|
||||||
} else {
|
self.stack.extend(next_args);
|
||||||
self.unpack(closure.parameter_node.as_ref(), &next_args, &mut 0)?;
|
} else {
|
||||||
}
|
self.unpack(closure.parameter_node.as_ref(), &next_args, &mut 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
// PRE-ALLOCATION
|
// PRE-ALLOCATION
|
||||||
let current_stack = self.stack.len() as u32;
|
let current_stack = self.stack.len() as u32;
|
||||||
if closure.stack_size > current_stack {
|
if closure.stack_size > current_stack {
|
||||||
self.stack.resize(closure.stack_size as usize, Value::Void);
|
self.stack.resize(closure.stack_size as usize, Value::Void);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = self.eval_observed(observer, &closure.exec_node);
|
result = self.eval_observed(observer, &closure.exec_node);
|
||||||
self.frames.pop();
|
self.frames.pop();
|
||||||
} else if let Some(series) = next_obj.as_series() {
|
|
||||||
if next_args.len() != 1 {
|
|
||||||
return Err(format!(
|
|
||||||
"{} indexer expects exactly 1 argument (the lookback index), got {}",
|
|
||||||
next_obj.type_name(),
|
|
||||||
next_args.len()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if let Value::Int(idx) = &next_args[0] {
|
Value::Object(obj) => {
|
||||||
if *idx < 0 {
|
if let Some(series) = obj.as_series() {
|
||||||
|
if next_args.len() != 1 {
|
||||||
|
return Err(format!(
|
||||||
|
"{} indexer expects exactly 1 argument (the lookback index), got {}",
|
||||||
|
obj.type_name(),
|
||||||
|
next_args.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Value::Int(idx) = &next_args[0] {
|
||||||
|
if *idx < 0 {
|
||||||
|
return Err(format!(
|
||||||
|
"{} lookback index cannot be negative: {}",
|
||||||
|
obj.type_name(),
|
||||||
|
idx
|
||||||
|
));
|
||||||
|
}
|
||||||
|
result = Ok(series.get_item(*idx as usize).unwrap_or(Value::Void));
|
||||||
|
} else {
|
||||||
|
return Err(format!(
|
||||||
|
"{} index must be an integer, got {}",
|
||||||
|
obj.type_name(),
|
||||||
|
next_args[0]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"{} lookback index cannot be negative: {}",
|
"Tail call target is not callable: {}",
|
||||||
next_obj.type_name(),
|
obj.type_name()
|
||||||
idx
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
result = Ok(series.get_item(*idx as usize).unwrap_or(Value::Void));
|
|
||||||
} else {
|
|
||||||
return Err(format!(
|
|
||||||
"{} index must be an integer, got {}",
|
|
||||||
next_obj.type_name(),
|
|
||||||
next_args[0]
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
} else {
|
other => {
|
||||||
return Err(format!(
|
return Err(format!("Tail call target is not callable: {}", other));
|
||||||
"Tail call target is not callable: {}",
|
}
|
||||||
next_obj.type_name()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return result;
|
return result;
|
||||||
@@ -174,23 +182,18 @@ impl VM {
|
|||||||
|
|
||||||
pub fn run_with_args(
|
pub fn run_with_args(
|
||||||
&mut self,
|
&mut self,
|
||||||
closure_obj: Rc<dyn Object>,
|
closure_rc: Rc<Closure>,
|
||||||
args: &[Value],
|
args: &[Value],
|
||||||
) -> Result<Value, String> {
|
) -> Result<Value, String> {
|
||||||
self.run_with_args_observed(&mut NoOpObserver, closure_obj, args)
|
self.run_with_args_observed(&mut NoOpObserver, closure_rc, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_with_args_observed<O: VMObserver>(
|
pub fn run_with_args_observed<O: VMObserver>(
|
||||||
&mut self,
|
&mut self,
|
||||||
observer: &mut O,
|
observer: &mut O,
|
||||||
closure_obj: Rc<dyn Object>,
|
closure_rc: Rc<Closure>,
|
||||||
args: &[Value],
|
args: &[Value],
|
||||||
) -> Result<Value, String> {
|
) -> Result<Value, String> {
|
||||||
let closure_rc = closure_obj
|
|
||||||
.into_rc_any()
|
|
||||||
.downcast::<Closure>()
|
|
||||||
.map_err(|_| "Object is not a closure".to_string())?;
|
|
||||||
|
|
||||||
self.stack.clear();
|
self.stack.clear();
|
||||||
self.frames.clear();
|
self.frames.clear();
|
||||||
|
|
||||||
@@ -416,8 +419,8 @@ impl VM {
|
|||||||
let mut pipe_vm = VM::new(self.globals.clone());
|
let mut pipe_vm = VM::new(self.globals.clone());
|
||||||
|
|
||||||
let executor: Box<PipeFn> = match lambda_val {
|
let executor: Box<PipeFn> = match lambda_val {
|
||||||
Value::Object(obj) => {
|
Value::Closure(rc) => {
|
||||||
let my_closure = obj.clone();
|
let my_closure = rc.clone();
|
||||||
Box::new(move |args: &[Value]| -> Value {
|
Box::new(move |args: &[Value]| -> Value {
|
||||||
match pipe_vm.run_with_args(my_closure.clone(), args) {
|
match pipe_vm.run_with_args(my_closure.clone(), args) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
@@ -465,7 +468,7 @@ impl VM {
|
|||||||
info.positional_count,
|
info.positional_count,
|
||||||
stack_size,
|
stack_size,
|
||||||
);
|
);
|
||||||
Ok(Value::Object(Rc::new(closure)))
|
Ok(Value::Closure(Rc::new(closure)))
|
||||||
}
|
}
|
||||||
NodeKind::Call { callee, args } => {
|
NodeKind::Call { callee, args } => {
|
||||||
let func_val = self.eval_internal(obs, callee)?;
|
let func_val = self.eval_internal(obs, callee)?;
|
||||||
@@ -481,8 +484,8 @@ impl VM {
|
|||||||
self.stack.truncate(base);
|
self.stack.truncate(base);
|
||||||
|
|
||||||
match func_val {
|
match func_val {
|
||||||
Value::Object(obj) => {
|
Value::Closure(_) | Value::Object(_) => {
|
||||||
self.tail_call = Some((obj, arg_vals));
|
self.tail_call = Some((func_val, arg_vals));
|
||||||
return Ok(Value::Void);
|
return Ok(Value::Void);
|
||||||
}
|
}
|
||||||
Value::Function(f) => return Ok((f.func)(&arg_vals)),
|
Value::Function(f) => return Ok((f.func)(&arg_vals)),
|
||||||
@@ -612,57 +615,56 @@ impl VM {
|
|||||||
self.stack.truncate(base);
|
self.stack.truncate(base);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
Value::Object(obj) => {
|
Value::Closure(closure_rc) => {
|
||||||
if let Ok(closure_rc) =
|
self.frames.push(CallFrame {
|
||||||
obj.clone().into_rc_any().downcast::<Closure>()
|
stack_base: base,
|
||||||
|
closure: Some(closure_rc.clone()),
|
||||||
|
});
|
||||||
|
let closure = closure_rc.as_ref();
|
||||||
|
|
||||||
|
let unpack_res = if let Some(count) = closure.positional_count
|
||||||
|
&& (self.stack.len() - base) == count as usize
|
||||||
{
|
{
|
||||||
self.frames.push(CallFrame {
|
Ok(())
|
||||||
stack_base: base,
|
} else {
|
||||||
closure: Some(closure_rc.clone()),
|
let args_for_unpack = self.stack[base..].to_vec();
|
||||||
});
|
self.stack.truncate(base);
|
||||||
let closure = closure_rc.as_ref();
|
if let NodeKind::Tuple { elements } =
|
||||||
|
&closure.parameter_node.kind
|
||||||
let unpack_res = if let Some(count) = closure.positional_count
|
|
||||||
&& (self.stack.len() - base) == count as usize
|
|
||||||
{
|
{
|
||||||
Ok(())
|
let mut offset = 0;
|
||||||
|
let mut res = Ok(());
|
||||||
|
for el in elements {
|
||||||
|
if let Err(e) =
|
||||||
|
self.unpack(el.as_ref(), &args_for_unpack, &mut offset)
|
||||||
|
{
|
||||||
|
res = Err(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
} else {
|
} else {
|
||||||
let args_for_unpack = self.stack[base..].to_vec();
|
self.unpack(closure.parameter_node.as_ref(), &args_for_unpack, &mut 0)
|
||||||
self.stack.truncate(base);
|
}
|
||||||
if let NodeKind::Tuple { elements } =
|
};
|
||||||
&closure.parameter_node.kind
|
|
||||||
{
|
|
||||||
let mut offset = 0;
|
|
||||||
let mut res = Ok(());
|
|
||||||
for el in elements {
|
|
||||||
if let Err(e) =
|
|
||||||
self.unpack(el.as_ref(), &args_for_unpack, &mut offset)
|
|
||||||
{
|
|
||||||
res = Err(e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
} else {
|
|
||||||
self.unpack(closure.parameter_node.as_ref(), &args_for_unpack, &mut 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = match unpack_res {
|
let res = match unpack_res {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// PRE-ALLOCATION
|
// PRE-ALLOCATION
|
||||||
let current_stack = (self.stack.len() - base) as u32;
|
let current_stack = (self.stack.len() - base) as u32;
|
||||||
if closure.stack_size > current_stack {
|
if closure.stack_size > current_stack {
|
||||||
self.stack.resize(base + closure.stack_size as usize, Value::Void);
|
self.stack.resize(base + closure.stack_size as usize, Value::Void);
|
||||||
}
|
|
||||||
self.eval_internal(obs, &closure.exec_node)
|
|
||||||
}
|
}
|
||||||
Err(e) => Err(e),
|
self.eval_internal(obs, &closure.exec_node)
|
||||||
};
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
self.frames.pop();
|
self.frames.pop();
|
||||||
res
|
res
|
||||||
} else if let Some(series) = obj.as_series() {
|
}
|
||||||
|
Value::Object(obj) => {
|
||||||
|
if let Some(series) = obj.as_series() {
|
||||||
let arg_len = self.stack.len() - base;
|
let arg_len = self.stack.len() - base;
|
||||||
let res = if arg_len != 1 {
|
let res = if arg_len != 1 {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
@@ -707,6 +709,7 @@ impl VM {
|
|||||||
Value::Record(_, _) => "Record",
|
Value::Record(_, _) => "Record",
|
||||||
Value::FieldAccessor(_) => "FieldAccessor",
|
Value::FieldAccessor(_) => "FieldAccessor",
|
||||||
Value::Function(_) => "Function",
|
Value::Function(_) => "Function",
|
||||||
|
Value::Closure(_) => "Closure",
|
||||||
Value::Object(_) => "Object",
|
Value::Object(_) => "Object",
|
||||||
Value::Cell(_) => "Cell",
|
Value::Cell(_) => "Cell",
|
||||||
};
|
};
|
||||||
@@ -714,8 +717,8 @@ impl VM {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((next_obj, next_args)) = self.tail_call.take() {
|
if let Some((next_val, next_args)) = self.tail_call.take() {
|
||||||
current_func = Value::Object(next_obj);
|
current_func = next_val;
|
||||||
self.stack.truncate(base);
|
self.stack.truncate(base);
|
||||||
self.stack.extend(next_args);
|
self.stack.extend(next_args);
|
||||||
continue;
|
continue;
|
||||||
@@ -736,7 +739,7 @@ impl VM {
|
|||||||
|
|
||||||
let frame = self.frames.last().ok_or("No call frame for 'again'")?;
|
let frame = self.frames.last().ok_or("No call frame for 'again'")?;
|
||||||
if let Some(closure_obj) = &frame.closure {
|
if let Some(closure_obj) = &frame.closure {
|
||||||
self.tail_call = Some((closure_obj.clone() as Rc<dyn Object>, arg_vals));
|
self.tail_call = Some((Value::Closure(closure_obj.clone()), arg_vals));
|
||||||
Ok(Value::Void)
|
Ok(Value::Void)
|
||||||
} else {
|
} else {
|
||||||
Err("'again' called outside of a closure".to_string())
|
Err("'again' called outside of a closure".to_string())
|
||||||
|
|||||||
Reference in New Issue
Block a user