diff --git a/src/continuation.rs b/src/continuation.rs index 3160586..9dd9d8d 100644 --- a/src/continuation.rs +++ b/src/continuation.rs @@ -15,7 +15,7 @@ use crate::{ use std::{collections::HashMap, sync::Arc}; #[async_trait] -pub trait Resumable: Trace + Send + Sync { +pub trait Resumable: Trace + Send + Sync + std::fmt::Debug { fn min_args(&self) -> usize { 1 } @@ -35,7 +35,7 @@ pub trait Resumable: Trace + Send + Sync { fn clone_stack(&self, cloned: &mut HashMap, Gc>) -> Arc; } -#[derive(Clone, Trace)] +#[derive(Clone, Trace, Debug)] struct DiscardResumeArgs { replacement: Result>, RuntimeError>, } @@ -55,10 +55,11 @@ impl Resumable for DiscardResumeArgs { } } -#[derive(Trace)] +#[derive(Trace, derive_more::Debug)] pub struct Continuation { resume_point: Arc, remaining: Option>, + #[debug(skip)] dynamic_wind: Option, } @@ -98,39 +99,10 @@ impl Continuation { args: Vec>, ) -> Result>, RuntimeError> { if let Some(ref dynamic_wind) = self.dynamic_wind { - let in_cont = Some(Arc::new(Continuation::with_dynamic_wind( - Arc::new(ResumableDynamicWind { - args: args.clone(), - body_thunk: Arc::new(Some(self.clone())), - dynamic_wind: dynamic_wind.clone(), - stage: Stage::In, - }), - &None, - dynamic_wind.clone(), - ))); - - let _ = dynamic_wind - .in_thunk - .call(Vec::new(), &in_cont) - .await? - .eval(&in_cont) - .await?; - - let body_cont = Some(Arc::new(Continuation::with_dynamic_wind( - Arc::new(ResumableDynamicWind { - args: args.clone(), - body_thunk: Arc::new(Some(self.clone())), - dynamic_wind: dynamic_wind.clone(), - stage: Stage::Body, - }), - &None, - dynamic_wind.clone(), - ))); - - self.resume(args, &body_cont).await - } else { - self.resume(args, &None).await + dynamic_wind.in_thunks().call(self).await?; } + + self.resume(args, &None).await } } @@ -144,21 +116,6 @@ impl Resumable for Continuation { if let Some(ref remaining) = self.remaining { let new_cont = Some(Arc::new(Continuation::new(remaining.clone(), cont))); let resume_result = self.resume_point.resume(args, &new_cont).await; - if let Some(out_thunk) = - leaving_dynamic_extent(&self.dynamic_wind, &remaining.dynamic_wind) - { - let out_cont = Some(Arc::new(Continuation::new( - Arc::new(DiscardResumeArgs { - replacement: resume_result.clone(), - }), - &self.remaining, - ))); - let _ = out_thunk - .call(Vec::new(), &out_cont) - .await? - .eval(&out_cont) - .await?; - } remaining.resume(resume_result?, cont).await } else { self.resume_point.resume(args, cont).await @@ -198,7 +155,7 @@ impl Callable for Option> { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableDefineVar { env: Gc, name: Identifier, @@ -284,7 +241,7 @@ impl Resumable for ResumableBody { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableLet { scope: Gc, curr: Identifier, @@ -342,7 +299,7 @@ impl Resumable for ResumableLet { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableIf { env: Gc, success: Arc, @@ -389,7 +346,7 @@ impl Resumable for ResumableIf { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableAnd { env: Gc, args: ArcSlice, @@ -446,7 +403,7 @@ impl Resumable for ResumableAnd { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableOr { env: Gc, args: ArcSlice, @@ -503,7 +460,7 @@ impl Resumable for ResumableOr { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableSet { env: Gc, var: Ref, @@ -539,7 +496,7 @@ impl Resumable for ResumableSet { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableSyntaxCase { env: Gc, transformer: Transformer, @@ -585,7 +542,7 @@ impl Resumable for ResumableSyntaxCase { } } -#[derive(Trace)] +#[derive(Trace, Debug)] pub struct ResumableCall { env: Gc, // TODO: Making this a SmallVec of around 10 would probably be a @@ -681,7 +638,7 @@ pub async fn call_cc( .await } -#[derive(Clone, Trace)] +#[derive(Clone, Trace, Debug)] pub struct CallWithValues { min_args: usize, max_args: Option, @@ -756,45 +713,106 @@ pub async fn call_with_values( .await } -#[derive(Trace, Clone)] +#[derive(Trace, Clone, Debug)] pub struct DynamicWind { - in_thunk: Arc, + in_thunks: Vec>, out_thunk: Arc, } -fn leaving_dynamic_extent<'a>( - curr_wind: &'a Option, - containing_wind: &Option, -) -> Option<&'a dyn Callable> { - if curr_wind.as_ref().map(|wind| Arc::as_ptr(&wind.out_thunk)) - != containing_wind +impl DynamicWind { + pub fn new( + cont: &Option>, + in_thunk: Arc, + out_thunk: Arc, + ) -> Self { + let mut in_thunks = cont .as_ref() - .map(|wind| Arc::as_ptr(&wind.out_thunk)) - { - curr_wind.as_ref().map(|wind| wind.out_thunk.as_ref()) - } else { - None + .and_then(|cont| cont.dynamic_wind.as_ref()) + .map_or_else(Vec::new, |cont| cont.in_thunks.clone()); + in_thunks.push(in_thunk); + Self { + in_thunks, + out_thunk, + } + } + + pub fn in_thunks(&self) -> ResumableListOfThunks { + ResumableListOfThunks { + thunks: ArcSlice::from(self.in_thunks.clone()), + } } } -#[derive(derive_more::Debug, Clone, Trace)] +#[derive(Debug, Clone, Trace)] pub struct ResumableDynamicWind { - args: Vec>, - #[debug(skip)] + in_thunk: Arc, body_thunk: Arc, - #[debug(skip)] - dynamic_wind: DynamicWind, - stage: Stage, + out_thunk: Arc, +} + +#[async_trait] +impl Resumable for ResumableDynamicWind { + fn min_args(&self) -> usize { + 0 + } + + fn max_args(&self) -> Option { + None + } + + async fn resume( + &self, + _args: Vec>, + cont: &Option>, + ) -> Result>, RuntimeError> { + let dynamic_wind = DynamicWind::new(cont, self.in_thunk.clone(), self.out_thunk.clone()); + + // Discard the arguments and call the body thunk + let body_cont = Some(Arc::new(Continuation::with_dynamic_wind( + Arc::new(ResumableDynamicWindBody { + out_thunk: self.out_thunk.clone(), + }), + cont, + dynamic_wind.clone(), + ))); + + let res = self + .body_thunk + .call(Vec::new(), &body_cont) + .await? + .eval(&body_cont) + .await?; + + let out_cont = Some(Arc::new(Continuation::new( + Arc::new(DiscardResumeArgs { + replacement: Ok(res.clone()), + }), + cont, + ))); + + let _ = self + .out_thunk + .call(Vec::new(), &out_cont) + .await? + .eval(&out_cont) + .await?; + + Ok(res) + } + + fn clone_stack(&self, _cloned: &mut HashMap, Gc>) -> Arc { + // Do I need to clone the closures of the dynamic wind? I don't think so, but maybe. + Arc::new(self.clone()) + } } #[derive(Debug, Clone, Trace)] -enum Stage { - In, - Body, +pub struct ResumableDynamicWindBody { + out_thunk: Arc, } #[async_trait] -impl Resumable for ResumableDynamicWind { +impl Resumable for ResumableDynamicWindBody { fn min_args(&self) -> usize { 0 } @@ -808,29 +826,20 @@ impl Resumable for ResumableDynamicWind { args: Vec>, cont: &Option>, ) -> Result>, RuntimeError> { - match self.stage { - Stage::In => { - let mut body = self.clone(); - body.stage = Stage::Body; - let body = Arc::new(body); - - let body_cont = Some(Arc::new(Continuation::with_dynamic_wind( - body.clone(), - cont, - self.dynamic_wind.clone(), - ))); - - let vals = self - .body_thunk - .call(self.args.clone(), &body_cont) - .await? - .eval(&body_cont) - .await?; - - Ok(vals) - } - Stage::Body => Ok(args), - } + let out_cont = Some(Arc::new(Continuation::new( + Arc::new(DiscardResumeArgs { + replacement: Ok(args.clone()), + }), + cont, + ))); + + self.out_thunk + .call(Vec::new(), &out_cont) + .await? + .eval(&out_cont) + .await?; + + Ok(args) } fn clone_stack(&self, _cloned: &mut HashMap, Gc>) -> Arc { @@ -839,6 +848,76 @@ impl Resumable for ResumableDynamicWind { } } +#[derive(Trace, derive_more::Debug)] +pub struct ResumableListOfThunks { + thunks: ArcSlice>, +} + +impl ResumableListOfThunks { + pub fn new(thunks: ArcSlice>) -> Self { + Self { thunks } + } + + async fn call(&self, cont: &Arc) -> Result>, RuntimeError> { + let mut result = Vec::new(); + for (i, (thunk, tail)) in self.thunks.iter().enumerate() { + let in_thunks = self + .thunks + .iter() + .map(|(x, _)| x) + .take(i) + .cloned() + .collect::>(); + let dynamic_wind = DynamicWind { + in_thunks, + out_thunk: cont.dynamic_wind.as_ref().unwrap().out_thunk.clone(), + }; + let cont = Some(Arc::new(Continuation::with_dynamic_wind( + Arc::new(ResumableListOfThunks::new(tail)), + &Some(cont.clone()), + dynamic_wind, + ))); + result = thunk.call(Vec::new(), &cont).await?.eval(&cont).await?; + } + Ok(result) + } +} + +#[async_trait] +impl Resumable for ResumableListOfThunks { + fn min_args(&self) -> usize { + 0 + } + + fn max_args(&self) -> Option { + None + } + + async fn resume( + &self, + args: Vec>, + cont: &Option>, + ) -> Result>, RuntimeError> { + let Some(last) = self.thunks.last() else { + return Ok(args); + }; + for (thunk, tail) in self.thunks.skip_last() { + let cont = Some(Arc::new(Continuation::new( + Arc::new(ResumableListOfThunks::new(tail)), + cont, + ))); + thunk.call(Vec::new(), &cont).await?.eval(&cont).await?; + } + last.call(Vec::new(), cont).await?.eval(cont).await + } + + fn clone_stack(&self, _cloned: &mut HashMap, Gc>) -> Arc { + Arc::new(Self { + thunks: self.thunks.clone(), + }) + } +} + #[builtin("dynamic-wind")] pub async fn dynamic_wind( cont: &Option>, @@ -870,20 +949,15 @@ pub async fn dynamic_wind( .clone() }; - let dynamic_wind = DynamicWind { - in_thunk: in_thunk.clone(), - out_thunk: out_thunk.clone(), - }; + let dynamic_wind = DynamicWind::new(cont, in_thunk.clone(), out_thunk.clone()); - let in_cont = Some(Arc::new(Continuation::with_dynamic_wind( + let in_cont = Some(Arc::new(Continuation::new( Arc::new(ResumableDynamicWind { - args: Vec::new(), + in_thunk: in_thunk.clone(), body_thunk: body_thunk.clone(), - dynamic_wind: dynamic_wind.clone(), - stage: Stage::In, + out_thunk: out_thunk.clone(), }), cont, - dynamic_wind.clone(), ))); let _ = in_thunk @@ -893,11 +967,8 @@ pub async fn dynamic_wind( .await?; let body_cont = Some(Arc::new(Continuation::with_dynamic_wind( - Arc::new(ResumableDynamicWind { - args: Vec::new(), - body_thunk: body_thunk.clone(), - dynamic_wind: dynamic_wind.clone(), - stage: Stage::Body, + Arc::new(ResumableDynamicWindBody { + out_thunk: out_thunk.clone(), }), cont, dynamic_wind.clone(), @@ -909,12 +980,11 @@ pub async fn dynamic_wind( .eval(&body_cont) .await?; - let out_cont = Some(Arc::new(Continuation::with_dynamic_wind( + let out_cont = Some(Arc::new(Continuation::new( Arc::new(DiscardResumeArgs { replacement: Ok(res.clone()), }), cont, - dynamic_wind.clone(), ))); let _ = out_thunk diff --git a/src/proc.rs b/src/proc.rs index f70bc78..11dc3ba 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -44,7 +44,7 @@ impl ValuesOrPreparedCall { } #[async_trait] -pub trait Callable: Send + Sync + 'static { +pub trait Callable: Send + Sync + 'static + std::fmt::Debug { fn min_args(&self) -> usize; fn max_args(&self) -> Option; @@ -62,7 +62,6 @@ pub struct Procedure { pub up: Gc, pub args: Vec, pub remaining: Option, - #[debug(skip)] pub body: Body, pub is_variable_transformer: bool, } diff --git a/tests/r6rs.scm b/tests/r6rs.scm index 0b67827..faef924 100644 --- a/tests/r6rs.scm +++ b/tests/r6rs.scm @@ -257,3 +257,23 @@ (set! n (+ n 4)))))) n) 1) + +(define n 0) + +(dynamic-wind + (lambda () + (set! n (+ n 1)) + (call/cc (lambda (x) (set! h x)))) + (lambda () + (set! n (+ n 2)) + (dynamic-wind + (lambda () (set! n (+ n 3))) + (lambda () (call/cc (lambda (x) (set! g x)))) + (lambda () (set! n (+ n 5))))) + (lambda () (set! n (+ n 7)))) + +(g) +(h) + +(assert-eq n 49) ; 1 + 2 + 3 + 5 + 7 + (1 + 3 + 5 + 7) + (3 + 5 + 7) +