use crate::eval::operational_semantics::{
EvaluationContext, ComputationState, Redex, ContextFrame, MachineState,
RedexMetadata,
};
use crate::eval::{Value, Environment};
use crate::ast::{Expr, Spanned};
use crate::diagnostics::{Result, Error, Span};
use std::sync::Arc;
use std::rc::Rc;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ContinuationCaptureService {
capture_config: CaptureConfiguration,
}
#[derive(Debug, Clone)]
pub struct CaptureConfiguration {
max_capture_depth: usize,
capture_environment: bool,
single_shot_semantics: bool,
optimize_tail_calls: bool,
}
#[derive(Debug, Clone)]
pub struct CapturedContinuation {
pub id: ContinuationId,
pub context: EvaluationContext,
pub metadata: ContinuationMetadata,
pub is_invoked: bool,
pub captured_environment: Arc<super::value::ThreadSafeEnvironment>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ContinuationId(pub u64);
#[derive(Debug, Clone)]
pub struct ContinuationMetadata {
pub capture_location: Span,
pub capture_depth: usize,
pub generation: u64,
pub is_tail_continuation: bool,
pub debug_name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ContinuationApplicationService {
application_config: ApplicationConfiguration,
}
#[derive(Debug, Clone)]
pub struct ApplicationConfiguration {
validate_before_apply: bool,
restore_environments: bool,
max_applications: Option<usize>,
}
#[derive(Debug, Clone)]
pub enum ContinuationApplicationResult {
Success {
new_state: ComputationState,
applied_value: Value,
},
FinalValue {
value: Value,
},
Error {
error: Error,
failed_continuation: CapturedContinuation,
},
}
#[derive(Debug, Clone)]
pub struct ContinuationCompositionService {
composition_config: CompositionConfiguration,
}
#[derive(Debug, Clone)]
pub struct CompositionConfiguration {
max_composition_depth: usize,
optimize_compositions: bool,
}
pub enum CompositionType {
Sequential,
Parallel,
Conditional(Box<dyn Fn(&Value) -> bool + Send + Sync>),
Loop {
condition: Box<dyn Fn(&Value) -> bool + Send + Sync>,
body: Box<CapturedContinuation>,
},
}
impl std::fmt::Debug for CompositionType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CompositionType::Sequential => write!(f, "Sequential"),
CompositionType::Parallel => write!(f, "Parallel"),
CompositionType::Conditional(_) => write!(f, "Conditional(<function>)"),
CompositionType::Loop { body, .. } => write!(f, "Loop {{ condition: <function>, body: {body:?} }}"),
}
}
}
impl Clone for CompositionType {
fn clone(&self) -> Self {
match self {
CompositionType::Sequential => CompositionType::Sequential,
CompositionType::Parallel => CompositionType::Parallel,
CompositionType::Conditional(_) => {
CompositionType::Sequential },
CompositionType::Loop { condition: _, body } => {
CompositionType::Loop {
condition: Box::new(|_| false), body: Box::new(*body.clone()),
}
},
}
}
}
pub trait ContinuationRepository {
fn store(&mut self, continuation: CapturedContinuation) -> Result<ContinuationId>;
fn find_by_id(&self, id: ContinuationId) -> Option<CapturedContinuation>;
fn remove(&mut self, id: ContinuationId) -> Result<()>;
fn list_all(&self) -> Vec<ContinuationId>;
fn garbage_collect(&mut self, current_generation: u64) -> Result<usize>;
}
static CONTINUATION_ID_COUNTER: std::sync::atomic::AtomicU64 =
std::sync::atomic::AtomicU64::new(1);
fn next_continuation_id() -> ContinuationId {
ContinuationId(CONTINUATION_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst))
}
impl Default for ContinuationCaptureService {
fn default() -> Self {
Self::new()
}
}
impl ContinuationCaptureService {
pub fn new() -> Self {
Self {
capture_config: CaptureConfiguration::default(),
}
}
pub fn with_config(config: CaptureConfiguration) -> Self {
Self {
capture_config: config,
}
}
pub fn capture_continuation(
&self,
context: &EvaluationContext,
capture_location: Span,
generation: u64,
) -> Result<CapturedContinuation> {
if context.depth() > self.capture_config.max_capture_depth {
return Err(Box::new(Error::runtime_error(
format!(
"Continuation capture depth {} exceeds maximum {}",
context.depth(),
self.capture_config.max_capture_depth
),
Some(capture_location),
)));
}
let id = next_continuation_id();
let metadata = ContinuationMetadata {
capture_location,
capture_depth: context.depth(),
generation,
is_tail_continuation: context.is_empty(),
debug_name: None,
};
Ok(CapturedContinuation {
id,
context: context.clone(),
metadata,
is_invoked: false,
captured_environment: context.environment().clone(),
})
}
pub fn capture_named_continuation(
&self,
context: &EvaluationContext,
capture_location: Span,
generation: u64,
debug_name: String,
) -> Result<CapturedContinuation> {
let mut continuation = self.capture_continuation(context, capture_location, generation)?;
continuation.metadata.debug_name = Some(debug_name);
Ok(continuation)
}
pub fn can_capture(&self, context: &EvaluationContext) -> bool {
context.depth() <= self.capture_config.max_capture_depth
}
pub fn config(&self) -> &CaptureConfiguration {
&self.capture_config
}
}
impl Default for ContinuationApplicationService {
fn default() -> Self {
Self::new()
}
}
impl ContinuationApplicationService {
pub fn new() -> Self {
Self {
application_config: ApplicationConfiguration::default(),
}
}
pub fn with_config(config: ApplicationConfiguration) -> Self {
Self {
application_config: config,
}
}
pub fn apply_continuation(
&self,
mut continuation: CapturedContinuation,
value: Value,
) -> Result<ContinuationApplicationResult> {
if self.application_config.validate_before_apply {
if let Err(err) = self.validate_continuation(&continuation) {
return Ok(ContinuationApplicationResult::Error {
error: *err,
failed_continuation: continuation,
});
}
}
if continuation.is_invoked {
return Ok(ContinuationApplicationResult::Error {
error: Error::runtime_error(
"Continuation has already been invoked".to_string(),
Some(continuation.metadata.capture_location),
),
failed_continuation: continuation,
});
}
continuation.is_invoked = true;
match continuation.context.apply_to_value(value.clone()) {
Ok(new_state) => {
if new_state.context.is_empty() {
Ok(ContinuationApplicationResult::FinalValue { value })
} else {
Ok(ContinuationApplicationResult::Success {
new_state,
applied_value: value,
})
}
}
Err(error) => Ok(ContinuationApplicationResult::Error {
error: *error,
failed_continuation: continuation,
}),
}
}
fn validate_continuation(&self, continuation: &CapturedContinuation) -> Result<()> {
if continuation.is_invoked {
return Err(Box::new(Error::runtime_error(
"Cannot apply already-invoked continuation".to_string(),
Some(continuation.metadata.capture_location),
)));
}
Ok(())
}
pub fn config(&self) -> &ApplicationConfiguration {
&self.application_config
}
}
impl Default for ContinuationCompositionService {
fn default() -> Self {
Self::new()
}
}
impl ContinuationCompositionService {
pub fn new() -> Self {
Self {
composition_config: CompositionConfiguration::default(),
}
}
pub fn compose_sequential(
&self,
first: CapturedContinuation,
second: CapturedContinuation,
) -> Result<CapturedContinuation> {
let total_depth = first.context.depth() + second.context.depth();
if total_depth > self.composition_config.max_composition_depth {
return Err(Box::new(Error::runtime_error(
format!(
"Composition depth {} exceeds maximum {}",
total_depth,
self.composition_config.max_composition_depth
),
None,
)));
}
let composed_context = first.context.compose(second.context);
let id = next_continuation_id();
let metadata = ContinuationMetadata {
capture_location: first.metadata.capture_location,
capture_depth: composed_context.depth(),
generation: first.metadata.generation.max(second.metadata.generation),
is_tail_continuation: false, debug_name: Some(format!(
"composed({:?}, {:?})",
first.metadata.debug_name.as_deref().unwrap_or("anonymous"),
second.metadata.debug_name.as_deref().unwrap_or("anonymous")
)),
};
Ok(CapturedContinuation {
id,
context: composed_context,
metadata,
is_invoked: false,
captured_environment: first.captured_environment, })
}
pub fn transform_continuation<F>(
&self,
continuation: CapturedContinuation,
transformer: F,
) -> Result<CapturedContinuation>
where
F: FnOnce(EvaluationContext) -> Result<EvaluationContext>,
{
let transformed_context = transformer(continuation.context)?;
let id = next_continuation_id();
let metadata = ContinuationMetadata {
capture_location: continuation.metadata.capture_location,
capture_depth: transformed_context.depth(),
generation: continuation.metadata.generation,
is_tail_continuation: transformed_context.is_empty(),
debug_name: Some(format!(
"transformed({})",
continuation.metadata.debug_name.as_deref().unwrap_or("anonymous")
)),
};
Ok(CapturedContinuation {
id,
context: transformed_context,
metadata,
is_invoked: false,
captured_environment: continuation.captured_environment,
})
}
pub fn config(&self) -> &CompositionConfiguration {
&self.composition_config
}
}
impl Default for CaptureConfiguration {
fn default() -> Self {
Self {
max_capture_depth: 1000,
capture_environment: true,
single_shot_semantics: true,
optimize_tail_calls: true,
}
}
}
impl Default for ApplicationConfiguration {
fn default() -> Self {
Self {
validate_before_apply: true,
restore_environments: true,
max_applications: Some(1), }
}
}
impl Default for CompositionConfiguration {
fn default() -> Self {
Self {
max_composition_depth: 2000,
optimize_compositions: true,
}
}
}
impl CapturedContinuation {
pub fn debug_info(&self) -> String {
let base = format!(
"Continuation({}) [depth: {}, tail: {}, invoked: {}]",
self.id.0,
self.metadata.capture_depth,
self.metadata.is_tail_continuation,
self.is_invoked
);
if let Some(ref name) = self.metadata.debug_name {
format!("{base} '{name}'")
} else {
base
}
}
pub fn is_valid(&self) -> bool {
!self.is_invoked
}
pub fn capture_location(&self) -> Span {
self.metadata.capture_location
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::eval::operational_semantics::EvaluationContext;
#[test]
fn test_continuation_capture() {
let env = Rc::new(Environment::new(None, 0));
let context = EvaluationContext::empty(env);
let capture_service = ContinuationCaptureService::new();
let continuation = capture_service.capture_continuation(
&context,
Span::default(),
0,
).unwrap();
assert!(continuation.is_valid());
assert_eq!(continuation.metadata.capture_depth, 0);
assert!(continuation.metadata.is_tail_continuation);
}
#[test]
fn test_continuation_application() {
let env = Rc::new(Environment::new(None, 0));
let context = EvaluationContext::empty(env);
let capture_service = ContinuationCaptureService::new();
let application_service = ContinuationApplicationService::new();
let continuation = capture_service.capture_continuation(
&context,
Span::default(),
0,
).unwrap();
let value = Value::number(42.0);
let result = application_service.apply_continuation(continuation, value).unwrap();
match result {
ContinuationApplicationResult::FinalValue { value } => {
assert_eq!(value, Value::number(42.0));
}
_ => panic!("Expected final value"),
}
}
#[test]
fn test_continuation_composition() {
let env = Rc::new(Environment::new(None, 0));
let context1 = EvaluationContext::empty(env.clone());
let context2 = EvaluationContext::empty(env.clone());
let capture_service = ContinuationCaptureService::new();
let composition_service = ContinuationCompositionService::new();
let cont1 = capture_service.capture_continuation(
&context1,
Span::default(),
0,
).unwrap();
let cont2 = capture_service.capture_continuation(
&context2,
Span::default(),
0,
).unwrap();
let composed = composition_service.compose_sequential(cont1, cont2).unwrap();
assert!(composed.is_valid());
assert!(composed.metadata.debug_name.is_some());
}
}