use std::sync::{Arc, Mutex, PoisonError};
use tatara_lisp::Span;
use crate::error::EvalError;
use crate::value::Value;
#[derive(Clone, Copy, Debug)]
pub struct Call<'a> {
pub name: Option<&'a Arc<str>>,
pub source: Span,
pub at: Span,
}
#[derive(Clone, Copy, Debug)]
pub enum Exit<'a> {
Returned(&'a Value),
TailCalled,
Raised(&'a EvalError),
Supplied(&'a Value),
}
pub trait CallObserver: Send + Sync {
fn enter(&self, call: &Call<'_>, args: &[Value]) -> Option<Value>;
fn leave(&self, call: &Call<'_>, exit: Exit<'_>);
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TraceFrame {
pub name: Option<Arc<str>>,
pub source: Span,
pub at: Span,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Trace {
pub frames: Vec<TraceFrame>,
pub depth: usize,
}
pub const TRACE_FRAMES: usize = 256;
#[derive(Default)]
pub(crate) struct Unwind(Mutex<Trace>);
impl Unwind {
pub(crate) fn push(&self, call: &Call<'_>) {
let mut t = self.0.lock().unwrap_or_else(PoisonError::into_inner);
t.depth += 1;
if t.frames.len() < TRACE_FRAMES {
t.frames.push(TraceFrame {
name: call.name.cloned(),
source: call.source,
at: call.at,
});
}
}
pub(crate) fn clear(&self) {
let mut t = self.0.lock().unwrap_or_else(PoisonError::into_inner);
if t.depth > 0 {
*t = Trace::default();
}
}
pub(crate) fn take(&self) -> Trace {
std::mem::take(&mut *self.0.lock().unwrap_or_else(PoisonError::into_inner))
}
}