use super::{EffectContext, Effect, LiftingConfig, EffectResult, GenerationalEnvManager};
use crate::diagnostics::Result;
use crate::eval::Value;
#[derive(Debug, Clone)]
pub struct EffectSystem {
context: EffectContext,
env_manager: GenerationalEnvManager,
lifting_config: LiftingConfig,
}
impl EffectSystem {
pub fn new() -> Self {
Self {
context: EffectContext::pure(),
env_manager: GenerationalEnvManager::new(),
lifting_config: LiftingConfig::default(),
}
}
pub fn with_config(config: LiftingConfig) -> Self {
Self {
context: EffectContext::pure(),
env_manager: GenerationalEnvManager::new(),
lifting_config: config,
}
}
pub fn context(&self) -> &EffectContext {
&self.context
}
pub fn context_mut(&mut self) -> &mut EffectContext {
&mut self.context
}
pub fn env_manager(&self) -> &GenerationalEnvManager {
&self.env_manager
}
pub fn env_manager_mut(&mut self) -> &mut GenerationalEnvManager {
&mut self.env_manager
}
pub fn lifting_config(&self) -> &LiftingConfig {
&self.lifting_config
}
pub fn enter_context(&mut self, effects: Vec<Effect>) -> EffectContext {
let old_context = self.context.clone();
for effect in effects {
self.context.add_effect(effect);
}
old_context
}
pub fn exit_context(&mut self, old_context: EffectContext) {
self.context = old_context;
}
pub fn handle_effect(&self, effect: &Effect, args: &[Value]) -> Result<EffectResult> {
if let Some(handler_ref) = self.context.find_handler(effect) {
handler_ref.handler().handle(effect, args)
} else {
Ok(EffectResult::Unhandled)
}
}
pub fn should_lift(&self, operation: &str, current_effects: &[Effect]) -> Option<Effect> {
if let Some(effect) = self.lifting_config.check_custom_rules(operation, current_effects) {
return Some(effect);
}
match operation {
"display" | "write" | "newline" | "read" | "open-input-file" | "open-output-file"
if self.lifting_config.auto_lift_io() => Some(Effect::IO),
"set!" | "vector-set!" | "string-set!"
if self.lifting_config.auto_lift_state() => Some(Effect::State),
"error" | "raise" | "throw"
if self.lifting_config.auto_lift_error() => Some(Effect::Error),
_ => None,
}
}
}
impl Default for EffectSystem {
fn default() -> Self {
Self::new()
}
}