use std::sync::Arc;
use arc_gc::gc::GC;
use crate::{
lambda::runnable::{Runnable, RuntimeError, StepResult},
types::{
object::{OnionObject, OnionObjectCell},
pair::OnionPair,
},
};
pub struct Scheduler {
runnable_stack: Vec<Box<dyn Runnable>>,
}
impl Scheduler {
pub fn new(runnable_stack: Vec<Box<dyn Runnable>>) -> Self {
Scheduler { runnable_stack }
}
}
impl Runnable for Scheduler {
fn step(&mut self, gc: &mut GC<OnionObjectCell>) -> StepResult {
if let Some(runnable) = self.runnable_stack.last_mut() {
match runnable.step(gc) {
StepResult::Continue => StepResult::Continue,
v @ StepResult::SpawnRunnable(_) => return v,
StepResult::NewRunnable(new_runnable) => {
self.runnable_stack.push(new_runnable);
StepResult::Continue
}
StepResult::ReplaceRunnable(new_runnable) => {
self.runnable_stack.last_mut().map(|r| *r = new_runnable);
StepResult::Continue
}
StepResult::Return(ref result) => {
self.runnable_stack.pop();
if let Some(top_runnable) = self.runnable_stack.last_mut() {
match top_runnable.receive(&StepResult::Return(result.clone()), gc) {
Ok(_) => {}
Err(RuntimeError::CustomValue(ref e)) => {
return StepResult::Return(
OnionPair::new_static(
&OnionObject::Boolean(false).stabilize(),
&e,
)
.into(),
);
}
Err(e) => {
return StepResult::Return(
OnionPair::new_static(
&OnionObject::Boolean(false).stabilize(),
&OnionObject::String(Arc::from(e.to_string())).stabilize(),
)
.into(),
);
}
};
StepResult::Continue
} else {
StepResult::Return(
OnionPair::new_static(
&OnionObject::Boolean(true).stabilize(),
result.as_ref(),
)
.into(),
)
}
}
StepResult::Error(ref error) => {
if let RuntimeError::Pending = error {
return StepResult::Error(RuntimeError::Pending);
}
return StepResult::Return(
OnionPair::new_static(
&OnionObject::Boolean(false).stabilize(),
&match error {
RuntimeError::CustomValue(v) => v.as_ref().clone(),
_ => OnionObject::Undefined(Some(error.to_string().into()))
.stabilize(),
},
)
.into(),
);
}
}
} else {
StepResult::Error(RuntimeError::DetailedError(
"No runnable in stack".into(),
))
}
}
fn receive(
&mut self,
step_result: &StepResult,
gc: &mut GC<OnionObjectCell>,
) -> Result<(), RuntimeError> {
if let Some(runnable) = self.runnable_stack.last_mut() {
runnable.receive(&step_result, gc)
} else {
Err(RuntimeError::DetailedError(
"No runnable in stack".into(),
))
}
}
fn format_context(&self) -> String {
if self.runnable_stack.is_empty() {
return "Scheduler: No active runnables.".to_string();
}
let contexts: Vec<String> = self
.runnable_stack
.iter()
.rev() .enumerate() .map(|(index, runnable)| {
let header = format!("--- Frame #{} ---", index);
let inner_context = runnable.format_context();
format!("{}\n{}", header, inner_context)
})
.collect();
contexts.join("\n\n")
}
}