use super::{Effect, EffectResult, IOAction, StateAction, ErrorAction, EffectHandler};
use crate::diagnostics::{Error as DiagnosticError, Result};
use crate::eval::value::{ThreadSafeEnvironment, Value, Procedure};
use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, RwLock};
#[derive(Debug, Clone)]
pub struct EffectHandlerImplementation {
name: String,
handled_effects: Vec<Effect>,
handlers: HashMap<Effect, HandlerFunction>,
default_handler: Option<HandlerFunction>,
#[allow(dead_code)]
environment: Arc<ThreadSafeEnvironment>,
}
#[derive(Debug, Clone)]
pub struct HandlerFunction {
procedure: Arc<Procedure>,
arity: HandlerArity,
resumable: bool,
}
#[derive(Debug, Clone)]
pub enum HandlerArity {
Fixed(usize),
Variable(usize),
WithContinuation(usize),
}
#[derive(Debug, Clone)]
pub struct EffectHandlerRegistry {
handlers: HashMap<String, EffectHandlerImplementation>,
default_handlers: HashMap<Effect, EffectHandlerImplementation>,
handler_stack: Vec<EffectHandlerImplementation>,
}
pub struct BuiltinHandlers;
#[derive(Debug, Clone)]
pub struct IOEffectHandler {
#[allow(dead_code)]
name: String,
config: IOHandlerConfig,
}
#[derive(Debug, Clone, Default)]
pub struct IOHandlerConfig {
buffer_output: bool,
#[allow(dead_code)]
echo_input: bool,
#[allow(dead_code)]
redirections: HashMap<String, IORedirection>,
}
#[derive(Debug, Clone)]
pub enum IORedirection {
File(String),
String(Arc<RwLock<String>>),
Procedure(Arc<Procedure>),
Null,
}
#[derive(Debug, Clone)]
pub struct StateEffectHandler {
#[allow(dead_code)]
name: String,
#[allow(dead_code)]
config: StateHandlerConfig,
}
#[derive(Debug, Clone)]
pub struct StateHandlerConfig {
#[allow(dead_code)]
track_changes: bool,
#[allow(dead_code)]
max_snapshots: usize,
#[allow(dead_code)]
validate_consistency: bool,
}
#[derive(Debug, Clone)]
pub struct ErrorEffectHandler {
#[allow(dead_code)]
name: String,
config: ErrorHandlerConfig,
}
#[derive(Debug, Clone)]
pub struct ErrorHandlerConfig {
#[allow(dead_code)]
capture_stack_trace: bool,
allow_recovery: bool,
#[allow(dead_code)]
error_transformations: HashMap<String, Arc<Procedure>>,
}
impl super::EffectHandler for EffectHandlerImplementation {
fn handle(&self, effect: &Effect, args: &[Value]) -> Result<EffectResult> {
if let Some(handler_func) = self.handlers.get(effect) {
self.execute_handler(handler_func, effect, args)
} else if let Some(default_handler) = &self.default_handler {
self.execute_handler(default_handler, effect, args)
} else {
Ok(EffectResult::Unhandled)
}
}
fn effect_name(&self) -> &str {
&self.name
}
fn can_handle(&self, effect: &Effect) -> bool {
self.handled_effects.contains(effect) || self.default_handler.is_some()
}
}
impl EffectHandlerImplementation {
pub fn new(
name: String,
handled_effects: Vec<Effect>,
environment: Arc<ThreadSafeEnvironment>
) -> Self {
Self {
name,
handled_effects,
handlers: HashMap::new(),
default_handler: None,
environment,
}
}
pub fn add_handler(&mut self, effect: Effect, handler: HandlerFunction) {
self.handlers.insert(effect.clone(), handler);
if !self.handled_effects.contains(&effect) {
self.handled_effects.push(effect);
}
}
pub fn set_default_handler(&mut self, handler: HandlerFunction) {
self.default_handler = Some(handler);
}
fn execute_handler(
&self,
handler: &HandlerFunction,
effect: &Effect,
args: &[Value]
) -> Result<EffectResult> {
if let Err(e) = self.check_handler_arity(&handler.arity, args.len()) {
return Ok(EffectResult::Error(*e));
}
let mut handler_args = vec![
Value::string(format!("{effect}")),
];
handler_args.extend_from_slice(args);
match effect {
Effect::Pure => Ok(EffectResult::Value(Value::Unspecified)),
Effect::IO => {
if let Some(value) = args.first() {
print!("{value}");
Ok(EffectResult::Value(Value::Unspecified))
} else {
Ok(EffectResult::Value(Value::Unspecified))
}
},
Effect::State => {
Ok(EffectResult::Value(Value::Unspecified))
},
Effect::Error => {
if let Some(error_val) = args.first() {
let error_msg = format!("Error: {error_val}");
Ok(EffectResult::Error(DiagnosticError::runtime_error(error_msg, None)))
} else {
Ok(EffectResult::Value(Value::Unspecified))
}
},
Effect::Custom(name) => {
Ok(EffectResult::Value(Value::string(format!("Custom effect: {name}"))))
}
}
}
fn check_handler_arity(&self, arity: &HandlerArity, arg_count: usize) -> Result<()> {
match arity {
HandlerArity::Fixed(expected) => {
if arg_count != *expected {
Err(Box::new(DiagnosticError::runtime_error(
format!("Handler expects {expected} arguments, got {arg_count}"),
None,
)))
} else {
Ok(())
}
},
HandlerArity::Variable(min) => {
if arg_count < *min {
Err(Box::new(DiagnosticError::runtime_error(
format!("Handler expects at least {min} arguments, got {arg_count}"),
None,
)))
} else {
Ok(())
}
},
HandlerArity::WithContinuation(expected) => {
if arg_count != expected + 1 {
Err(Box::new(DiagnosticError::runtime_error(
format!("Handler with continuation expects {} arguments, got {}",
expected + 1, arg_count),
None,
)))
} else {
Ok(())
}
}
}
}
}
impl HandlerFunction {
pub fn new(procedure: Arc<Procedure>, arity: HandlerArity) -> Self {
Self {
procedure,
arity,
resumable: false,
}
}
pub fn resumable(procedure: Arc<Procedure>, arity: HandlerArity) -> Self {
Self {
procedure,
arity,
resumable: true,
}
}
pub fn is_resumable(&self) -> bool {
self.resumable
}
pub fn procedure(&self) -> &Arc<Procedure> {
&self.procedure
}
pub fn arity(&self) -> &HandlerArity {
&self.arity
}
}
impl EffectHandlerRegistry {
pub fn new() -> Self {
let mut registry = Self {
handlers: HashMap::new(),
default_handlers: HashMap::new(),
handler_stack: Vec::new(),
};
registry.register_builtin_handlers();
registry
}
pub fn register_handler(&mut self, name: String, handler: EffectHandlerImplementation) {
self.handlers.insert(name, handler);
}
pub fn set_default_handler(&mut self, effect: Effect, handler: EffectHandlerImplementation) {
self.default_handlers.insert(effect, handler);
}
pub fn find_handler(&self, effect: &Effect) -> Option<&EffectHandlerImplementation> {
for handler in self.handler_stack.iter().rev() {
if handler.can_handle(effect) {
return Some(handler);
}
}
self.default_handlers.get(effect)
}
pub fn push_handler(&mut self, handler: EffectHandlerImplementation) {
self.handler_stack.push(handler);
}
pub fn pop_handler(&mut self) -> Option<EffectHandlerImplementation> {
self.handler_stack.pop()
}
pub fn get_handler(&self, name: &str) -> Option<&EffectHandlerImplementation> {
self.handlers.get(name)
}
pub fn handler_names(&self) -> Vec<&String> {
self.handlers.keys().collect()
}
fn register_builtin_handlers(&mut self) {
let io_handler = BuiltinHandlers::create_io_handler();
self.set_default_handler(Effect::IO, io_handler);
let state_handler = BuiltinHandlers::create_state_handler();
self.set_default_handler(Effect::State, state_handler);
let error_handler = BuiltinHandlers::create_error_handler();
self.set_default_handler(Effect::Error, error_handler);
}
}
impl BuiltinHandlers {
pub fn create_io_handler() -> EffectHandlerImplementation {
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
let mut handler = EffectHandlerImplementation::new(
"builtin-io".to_string(),
vec![Effect::IO],
env,
);
let proc = Arc::new(Procedure {
formals: crate::ast::Formals::Variable("args".to_string()),
body: vec![], environment: Arc::new(ThreadSafeEnvironment::new(None, 0)),
name: Some("io-handler".to_string()),
metadata: HashMap::new(),
source: None,
});
let handler_func = HandlerFunction::new(
proc,
HandlerArity::Variable(1),
);
handler.add_handler(Effect::IO, handler_func);
handler
}
pub fn create_state_handler() -> EffectHandlerImplementation {
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
let mut handler = EffectHandlerImplementation::new(
"builtin-state".to_string(),
vec![Effect::State],
env,
);
let proc = Arc::new(Procedure {
formals: crate::ast::Formals::Variable("args".to_string()),
body: vec![],
environment: Arc::new(ThreadSafeEnvironment::new(None, 0)),
name: Some("state-handler".to_string()),
metadata: HashMap::new(),
source: None,
});
let handler_func = HandlerFunction::new(
proc,
HandlerArity::Variable(1),
);
handler.add_handler(Effect::State, handler_func);
handler
}
pub fn create_error_handler() -> EffectHandlerImplementation {
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
let mut handler = EffectHandlerImplementation::new(
"builtin-error".to_string(),
vec![Effect::Error],
env,
);
let proc = Arc::new(Procedure {
formals: crate::ast::Formals::Variable("args".to_string()),
body: vec![],
environment: Arc::new(ThreadSafeEnvironment::new(None, 0)),
name: Some("error-handler".to_string()),
metadata: HashMap::new(),
source: None,
});
let handler_func = HandlerFunction::new(
proc,
HandlerArity::Variable(1),
);
handler.add_handler(Effect::Error, handler_func);
handler
}
}
impl IOEffectHandler {
pub fn new(name: String) -> Self {
Self {
name,
config: IOHandlerConfig::default(),
}
}
pub fn with_config(name: String, config: IOHandlerConfig) -> Self {
Self {
name,
config,
}
}
pub fn handle_io_action(&self, action: &IOAction) -> Result<Value> {
match action {
IOAction::Print(value) => {
if self.config.buffer_output {
Ok(Value::Unspecified)
} else {
print!("{value}");
Ok(Value::Unspecified)
}
},
IOAction::Newline => {
if !self.config.buffer_output {
println!();
}
Ok(Value::Unspecified)
},
IOAction::Return(value) => Ok(value.clone()),
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"IO action not yet implemented".to_string(),
None,
)))
}
}
}
}
impl StateEffectHandler {
pub fn new(name: String) -> Self {
Self {
name,
config: StateHandlerConfig::default(),
}
}
pub fn handle_state_action(&self, action: &StateAction) -> Result<Value> {
match action {
StateAction::Return(value) => Ok(value.clone()),
StateAction::GetVar(name) => {
Err(Box::new(DiagnosticError::runtime_error(
format!("Variable {name} not found in state"),
None,
)))
},
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"State action not yet implemented".to_string(),
None,
)))
}
}
}
}
impl ErrorEffectHandler {
pub fn new(name: String) -> Self {
Self {
name,
config: ErrorHandlerConfig::default(),
}
}
pub fn handle_error_action(&self, action: &ErrorAction) -> Result<Value> {
match action {
ErrorAction::Return(value) => Ok(value.clone()),
ErrorAction::Throw(error) => {
if self.config.allow_recovery {
Ok(Value::string("Error recovered".to_string()))
} else {
Err(Box::new(error.clone()))
}
},
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"Error action not yet implemented".to_string(),
None,
)))
}
}
}
}
impl Default for EffectHandlerRegistry {
fn default() -> Self {
Self::new()
}
}
impl Default for StateHandlerConfig {
fn default() -> Self {
Self {
track_changes: true,
max_snapshots: 100,
validate_consistency: false,
}
}
}
impl Default for ErrorHandlerConfig {
fn default() -> Self {
Self {
capture_stack_trace: true,
allow_recovery: false,
error_transformations: HashMap::new(),
}
}
}
impl fmt::Display for EffectHandlerImplementation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EffectHandler({}, effects={:?})", self.name, self.handled_effects)
}
}
impl fmt::Display for HandlerArity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HandlerArity::Fixed(n) => write!(f, "Fixed({n})"),
HandlerArity::Variable(min) => write!(f, "Variable(min={min})"),
HandlerArity::WithContinuation(n) => write!(f, "WithContinuation({n})"),
}
}
}
unsafe impl Send for EffectHandlerImplementation {}
unsafe impl Sync for EffectHandlerImplementation {}
unsafe impl Send for HandlerFunction {}
unsafe impl Sync for HandlerFunction {}
unsafe impl Send for HandlerArity {}
unsafe impl Sync for HandlerArity {}
unsafe impl Send for EffectHandlerRegistry {}
unsafe impl Sync for EffectHandlerRegistry {}
unsafe impl Send for IOEffectHandler {}
unsafe impl Sync for IOEffectHandler {}
unsafe impl Send for StateEffectHandler {}
unsafe impl Sync for StateEffectHandler {}
unsafe impl Send for ErrorEffectHandler {}
unsafe impl Sync for ErrorEffectHandler {}
unsafe impl Send for IOHandlerConfig {}
unsafe impl Sync for IOHandlerConfig {}
unsafe impl Send for StateHandlerConfig {}
unsafe impl Sync for StateHandlerConfig {}
unsafe impl Send for ErrorHandlerConfig {}
unsafe impl Sync for ErrorHandlerConfig {}
unsafe impl Send for IORedirection {}
unsafe impl Sync for IORedirection {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handler_registry() {
let registry = EffectHandlerRegistry::new();
assert!(registry.find_handler(&Effect::IO).is_some());
assert!(registry.find_handler(&Effect::State).is_some());
assert!(registry.find_handler(&Effect::Error).is_some());
}
#[test]
fn test_handler_arity_checking() {
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
let handler = EffectHandlerImplementation::new(
"test".to_string(),
vec![Effect::IO],
env,
);
let fixed_arity = HandlerArity::Fixed(2);
assert!(handler.check_handler_arity(&fixed_arity, 2).is_ok());
assert!(handler.check_handler_arity(&fixed_arity, 1).is_err());
assert!(handler.check_handler_arity(&fixed_arity, 3).is_err());
let var_arity = HandlerArity::Variable(1);
assert!(handler.check_handler_arity(&var_arity, 1).is_ok());
assert!(handler.check_handler_arity(&var_arity, 2).is_ok());
assert!(handler.check_handler_arity(&var_arity, 0).is_err());
}
}