feat: Add tail call optimization
Introduce a TCO pass to the compiler and modify the VM to handle tail calls. This allows for deep recursion without stack overflow.
This commit is contained in:
+51
-24
@@ -114,40 +114,67 @@ impl VM {
|
||||
|
||||
Ok(Value::Object(Rc::new(closure)))
|
||||
},
|
||||
|
||||
BoundKind::TailCall { callee, args } => {
|
||||
let func_val = self.eval(callee)?;
|
||||
let mut arg_vals = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
arg_vals.push(self.eval(arg)?);
|
||||
}
|
||||
|
||||
match func_val {
|
||||
Value::Object(obj) => Ok(Value::TailCallRequest(obj, arg_vals)),
|
||||
// Native functions can't be TCO'd this way (they return value directly),
|
||||
// so we just execute them and return Value.
|
||||
Value::Function(f) => Ok(f(arg_vals)),
|
||||
_ => Err(format!("Tail call target is not a function: {}", func_val)),
|
||||
}
|
||||
},
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
let func_val = self.eval(callee)?;
|
||||
let mut func_val = self.eval(callee)?;
|
||||
|
||||
let mut arg_vals = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
arg_vals.push(self.eval(arg)?);
|
||||
}
|
||||
|
||||
match func_val {
|
||||
Value::Function(f) => Ok(f(arg_vals)),
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
let old_stack_top = self.stack.len();
|
||||
let closure_rc = Rc::new(closure.clone());
|
||||
|
||||
self.stack.extend(arg_vals);
|
||||
|
||||
self.frames.push(CallFrame {
|
||||
stack_base: old_stack_top,
|
||||
closure: Some(closure_rc.clone()),
|
||||
});
|
||||
// Trampoline Loop
|
||||
loop {
|
||||
match func_val {
|
||||
Value::Function(f) => return Ok(f(arg_vals)),
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
let old_stack_top = self.stack.len();
|
||||
let closure_rc = Rc::new(closure.clone());
|
||||
|
||||
self.stack.extend(arg_vals); // Moves arg_vals into stack
|
||||
|
||||
self.frames.push(CallFrame {
|
||||
stack_base: old_stack_top,
|
||||
closure: Some(closure_rc.clone()),
|
||||
});
|
||||
|
||||
let result = self.eval(&closure.function_node);
|
||||
let result = self.eval(&closure.function_node);
|
||||
|
||||
self.frames.pop();
|
||||
self.stack.truncate(old_stack_top);
|
||||
|
||||
result
|
||||
} else {
|
||||
Err(format!("Object is not a closure: {}", obj.type_name()))
|
||||
}
|
||||
},
|
||||
_ => Err(format!("Attempt to call non-function: {}", func_val)),
|
||||
self.frames.pop();
|
||||
self.stack.truncate(old_stack_top);
|
||||
|
||||
match result {
|
||||
Ok(Value::TailCallRequest(next_obj, next_args)) => {
|
||||
func_val = Value::Object(next_obj);
|
||||
arg_vals = next_args;
|
||||
continue;
|
||||
},
|
||||
Ok(val) => return Ok(val),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
return Err(format!("Object is not a closure: {}", obj.type_name()));
|
||||
}
|
||||
},
|
||||
_ => return Err(format!("Attempt to call non-function: {}", func_val)),
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user