use crate::ast::{Expr, Spanned};
use crate::diagnostics::{Result, Span};
use crate::eval::{Value, Environment};
use std::collections::VecDeque;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct EvaluationContext {
frames: VecDeque<ContextFrame>,
captured_environment: std::sync::Arc<super::value::ThreadSafeEnvironment>,
context_id: ContextId,
span: Option<Span>,
}
#[derive(Debug, Clone)]
pub enum ContextFrame {
ApplicationOperator {
operands: Vec<Spanned<Expr>>,
environment: Rc<Environment>,
span: Span,
},
ApplicationOperand {
procedure: Value,
evaluated_args: Vec<Value>,
pending_args: Vec<Spanned<Expr>>,
environment: Rc<Environment>,
span: Span,
},
Conditional {
then_branch: Spanned<Expr>,
else_branch: Box<Option<Spanned<Expr>>>,
environment: Rc<Environment>,
span: Span,
},
Assignment {
variable: String,
environment: Rc<Environment>,
span: Span,
},
Sequence {
evaluated_exprs: Vec<Value>,
pending_exprs: Vec<Spanned<Expr>>,
environment: Rc<Environment>,
span: Span,
},
LambdaBody {
procedure_name: Option<String>,
environment: Rc<Environment>,
span: Span,
},
LetBinding {
bound_vars: Vec<(String, Value)>,
current_var: String,
pending_bindings: Vec<(String, Spanned<Expr>)>,
body: Vec<Spanned<Expr>>,
environment: Rc<Environment>,
span: Span,
},
CallCC {
procedure: Value,
environment: Rc<Environment>,
span: Span,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ContextId(u64);
static CONTEXT_ID_COUNTER: std::sync::atomic::AtomicU64 =
std::sync::atomic::AtomicU64::new(1);
fn next_context_id() -> ContextId {
ContextId(CONTEXT_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst))
}
#[derive(Debug, Clone)]
pub struct Redex {
pub expression: Spanned<Expr>,
pub environment: Rc<Environment>,
pub metadata: RedexMetadata,
}
#[derive(Debug, Clone)]
pub struct RedexMetadata {
pub is_tail_position: bool,
pub stack_depth: usize,
pub generation: u64,
}
#[derive(Debug, Clone)]
pub struct ComputationState {
pub context: EvaluationContext,
pub redex: Redex,
pub machine_state: MachineState,
}
#[derive(Debug, Clone)]
pub struct MachineState {
pub generation: u64,
pub in_tail_position: bool,
pub stack_depth: usize,
}
impl EvaluationContext {
pub fn empty(environment: Rc<Environment>) -> Self {
Self {
frames: VecDeque::new(),
captured_environment: super::value::ThreadSafeEnvironment::from_legacy(&environment),
context_id: next_context_id(),
span: None,
}
}
pub fn single_frame(frame: ContextFrame, environment: Rc<Environment>) -> Self {
let mut frames = VecDeque::new();
frames.push_back(frame);
Self {
frames,
captured_environment: super::value::ThreadSafeEnvironment::from_legacy(&environment),
context_id: next_context_id(),
span: None,
}
}
pub fn environment(&self) -> &std::sync::Arc<super::value::ThreadSafeEnvironment> {
&self.captured_environment
}
pub fn environment_legacy(&self) -> Rc<Environment> {
self.captured_environment.to_legacy()
}
pub fn push_frame(&mut self, frame: ContextFrame) {
self.frames.push_back(frame);
}
pub fn pop_frame(&mut self) -> Option<ContextFrame> {
self.frames.pop_back()
}
pub fn peek_frame(&self) -> Option<&ContextFrame> {
self.frames.back()
}
pub fn is_empty(&self) -> bool {
self.frames.is_empty()
}
pub fn depth(&self) -> usize {
self.frames.len()
}
pub fn compose(mut self, inner: EvaluationContext) -> EvaluationContext {
for frame in inner.frames {
self.frames.push_back(frame);
}
self
}
pub fn to_continuation(&self) -> crate::eval::value::Continuation {
use crate::eval::value::Continuation;
use std::sync::Arc;
let stack = self.frames.iter()
.map(|frame| self.context_frame_to_stack_frame(frame))
.collect();
Continuation::new(
stack,
self.captured_environment.clone(),
self.context_id.0,
None, )
}
pub fn apply_to_value(&self, value: Value) -> Result<ComputationState> {
if self.is_empty() {
return Ok(ComputationState {
context: EvaluationContext::empty(self.environment_legacy()),
redex: Redex {
expression: Spanned {
inner: Expr::Quote(Box::new(Spanned {
inner: value.to_expr()?,
span: Span::default(),
})),
span: Span::default(),
},
environment: self.environment_legacy(),
metadata: RedexMetadata {
is_tail_position: true,
stack_depth: 0,
generation: 0,
},
},
machine_state: MachineState {
generation: 0,
in_tail_position: true,
stack_depth: 0,
},
});
}
let mut new_context = self.clone();
let top_frame = new_context.pop_frame().unwrap();
let (new_expr, new_env) = self.fill_frame_with_value(&top_frame, value)?;
Ok(ComputationState {
context: new_context,
redex: Redex {
expression: new_expr,
environment: new_env,
metadata: RedexMetadata {
is_tail_position: self.frames.len() == 1, stack_depth: self.frames.len(),
generation: 0, },
},
machine_state: MachineState {
generation: 0,
in_tail_position: self.frames.len() == 1,
stack_depth: self.frames.len(),
},
})
}
fn context_frame_to_stack_frame(&self, frame: &ContextFrame) -> crate::eval::value::Frame {
use crate::eval::value::Frame;
use std::sync::Arc;
Frame::CallCC {
environment: Arc::new(crate::eval::value::ThreadSafeEnvironment::default()),
source: crate::diagnostics::Span::new(0, 0),
}
}
fn fill_frame_with_value(
&self,
frame: &ContextFrame,
value: Value
) -> Result<(Spanned<Expr>, Rc<Environment>)> {
match frame {
ContextFrame::ApplicationOperator { operands, environment, span } => {
Ok((
Spanned {
inner: Expr::Application {
operator: Box::new(Spanned {
inner: value.to_expr()?,
span: *span,
}),
operands: operands.clone(),
},
span: *span,
},
environment.clone(),
))
}
ContextFrame::ApplicationOperand {
procedure,
evaluated_args,
pending_args,
environment,
span
} => {
let mut new_evaluated = evaluated_args.clone();
new_evaluated.push(value);
if pending_args.is_empty() {
Ok((
Spanned {
inner: Expr::Application {
operator: Box::new(Spanned {
inner: procedure.to_expr()?,
span: *span,
}),
operands: new_evaluated.into_iter()
.map(|v| Spanned {
inner: v.to_expr().unwrap_or(Expr::Literal(crate::ast::Literal::Nil)),
span: *span,
})
.collect(),
},
span: *span,
},
environment.clone(),
))
} else {
let next_arg = pending_args[0].clone();
Ok((next_arg, environment.clone()))
}
}
ContextFrame::Conditional { then_branch, else_branch, environment, span } => {
if value.is_truthy() {
Ok((then_branch.clone(), environment.clone()))
} else if let Some(else_expr) = else_branch.as_ref() {
Ok((else_expr.clone(), environment.clone()))
} else {
Ok((
Spanned {
inner: Expr::Literal(crate::ast::Literal::Unspecified),
span: *span,
},
environment.clone()
))
}
}
_ => {
Ok((
Spanned {
inner: value.to_expr()?,
span: Span::default(),
},
self.environment_legacy(),
))
}
}
}
pub fn id(&self) -> ContextId {
self.context_id
}
}
impl ComputationState {
pub fn new(context: EvaluationContext, redex: Redex) -> Self {
let machine_state = MachineState {
generation: redex.metadata.generation,
in_tail_position: redex.metadata.is_tail_position,
stack_depth: redex.metadata.stack_depth,
};
Self {
context,
redex,
machine_state,
}
}
pub fn is_tail_position(&self) -> bool {
self.machine_state.in_tail_position
}
pub fn stack_depth(&self) -> usize {
self.machine_state.stack_depth
}
}
trait ValueToExpr {
fn to_expr(&self) -> Result<Expr>;
}
impl ValueToExpr for Value {
fn to_expr(&self) -> Result<Expr> {
match self {
Value::Literal(lit) => Ok(Expr::Literal(lit.clone())),
Value::Symbol(sym) => Ok(Expr::Identifier(format!("symbol_{}", sym.id()))),
Value::Pair(car, cdr) => {
Ok(Expr::Literal(crate::ast::Literal::Nil)) }
_ => {
Ok(Expr::Quote(Box::new(Spanned {
inner: Expr::Literal(crate::ast::Literal::Nil), span: Span::default(),
})))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_context() {
let env = Rc::new(Environment::new(None, 0));
let ctx = EvaluationContext::empty(env.clone());
assert!(ctx.is_empty());
assert_eq!(ctx.depth(), 0);
assert!(ctx.peek_frame().is_none());
}
#[test]
fn test_context_composition() {
let env = Rc::new(Environment::new(None, 0));
let frame1 = ContextFrame::Conditional {
then_branch: Spanned {
inner: Expr::Literal(crate::ast::Literal::Number(42.0)),
span: Span::default(),
},
else_branch: Box::new(None),
environment: env.clone(),
span: Span::default(),
};
let ctx1 = EvaluationContext::single_frame(frame1, env.clone());
let ctx2 = EvaluationContext::empty(env.clone());
let composed = ctx2.compose(ctx1);
assert_eq!(composed.depth(), 1);
}
#[test]
fn test_context_id_uniqueness() {
let env = Rc::new(Environment::new(None, 0));
let ctx1 = EvaluationContext::empty(env.clone());
let ctx2 = EvaluationContext::empty(env.clone());
assert_ne!(ctx1.id(), ctx2.id());
}
}