use std::collections::HashMap; use std::rc::Rc; use super::ast::{Expr, Op, Stmt}; use super::parse::Parser; use super::token::Token; use super::value::{ExternHandle, Value}; use super::Interpreter; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DisplayFormat { Inline, Table, Tree, } pub trait InterpreterHook { fn name(&self) -> &str; fn priority(&self) -> i32 { 0 } fn before(&self) -> &'static [&'static str] { &[] } fn after(&self) -> &'static [&'static str] { &[] } fn rewrite_tokens(&self, _toks: &mut Vec) {} fn try_parse_stmt(&self, _p: &mut Parser) -> Option> { None } fn try_call(&self, _i: &mut Interpreter, _name: &str, _args: &[Expr], _depth: u32) -> Option> { None } fn after_exec(&self, _i: &Interpreter, _stmt: &Stmt, _result: &Value) {} fn display_extern(&self, _h: &ExternHandle, _fmt: DisplayFormat) -> Option { None } fn clone_extern(&self, _h: &ExternHandle) -> Option { None } fn extern_field_get(&self, _h: &ExternHandle, _field: &str) -> Option { None } fn extern_field_set(&self, _h: &ExternHandle, _field: &str, _v: Value) -> Option> { None } fn extern_method_call(&self, _i: &mut Interpreter, _h: &ExternHandle, _method: &str, _args: &[Value], _depth: u32) -> Option> { None } fn extern_eq(&self, _a: &ExternHandle, _b: &ExternHandle) -> Option { None } fn extern_binop(&self, _i: &mut Interpreter, _op: Op, _left: &Value, _right: &Value, _depth: u32) -> Option> { None } fn extern_unop(&self, _i: &mut Interpreter, _op: Op, _operand: &Value, _depth: u32) -> Option> { None } } pub(crate) struct HookList { pub(crate) hooks: Vec>, } impl HookList { pub fn new() -> Self { Self { hooks: Vec::new() } } pub fn register(&mut self, hook: Rc) -> Result<(), String> { let name = hook.name().to_string(); if self.hooks.iter().any(|h| h.name() == name) { return Err(format!("hook '{}' already registered", name)); } self.hooks.push(hook); self.resolve_order() } /// Kahn-style topo sort with priority tie-breaking on the resolved free set. fn resolve_order(&mut self) -> Result<(), String> { let n = self.hooks.len(); if n <= 1 { return Ok(()); } let names: Vec = self.hooks.iter().map(|h| h.name().to_string()).collect(); let name_idx: HashMap = names.iter().enumerate() .map(|(i, s)| (s.clone(), i)).collect(); let mut edges: Vec<(usize, usize)> = Vec::new(); for (i, h) in self.hooks.iter().enumerate() { for after_name in h.after() { if let Some(&j) = name_idx.get(*after_name) { edges.push((j, i)); } } for before_name in h.before() { if let Some(&j) = name_idx.get(*before_name) { edges.push((i, j)); } } } let mut indeg = vec![0usize; n]; for &(_, v) in &edges { indeg[v] += 1; } let mut placed = vec![false; n]; let mut order = Vec::with_capacity(n); for _ in 0..n { let mut best: Option = None; for i in 0..n { if placed[i] || indeg[i] > 0 { continue; } best = Some(match best { None => i, Some(b) if self.hooks[i].priority() > self.hooks[b].priority() => i, Some(b) => b, }); } let chosen = best.ok_or_else(|| "hook ordering has a cycle".to_string())?; placed[chosen] = true; order.push(chosen); for &(u, v) in &edges { if u == chosen { indeg[v] = indeg[v].saturating_sub(1); } } } let new_hooks: Vec> = order.iter() .map(|&i| self.hooks[i].clone()).collect(); self.hooks = new_hooks; Ok(()) } }