111 lines
4.1 KiB
Rust
111 lines
4.1 KiB
Rust
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<Token>) {}
|
|
fn try_parse_stmt(&self, _p: &mut Parser) -> Option<Result<Stmt, String>> { None }
|
|
fn try_call(&self, _i: &mut Interpreter, _name: &str, _args: &[Expr], _depth: u32)
|
|
-> Option<Result<Value, String>> { None }
|
|
fn after_exec(&self, _i: &Interpreter, _stmt: &Stmt, _result: &Value) {}
|
|
|
|
fn display_extern(&self, _h: &ExternHandle, _fmt: DisplayFormat) -> Option<Value> { None }
|
|
fn clone_extern(&self, _h: &ExternHandle) -> Option<Value> { None }
|
|
fn extern_field_get(&self, _h: &ExternHandle, _field: &str) -> Option<Value> { None }
|
|
fn extern_field_set(&self, _h: &ExternHandle, _field: &str, _v: Value)
|
|
-> Option<Result<(), String>> { None }
|
|
fn extern_method_call(&self, _i: &mut Interpreter, _h: &ExternHandle,
|
|
_method: &str, _args: &[Value], _depth: u32) -> Option<Result<Value, String>> { None }
|
|
fn extern_eq(&self, _a: &ExternHandle, _b: &ExternHandle) -> Option<bool> { None }
|
|
|
|
fn extern_binop(&self, _i: &mut Interpreter, _op: Op, _left: &Value, _right: &Value, _depth: u32)
|
|
-> Option<Result<Value, String>> { None }
|
|
fn extern_unop(&self, _i: &mut Interpreter, _op: Op, _operand: &Value, _depth: u32)
|
|
-> Option<Result<Value, String>> { None }
|
|
}
|
|
|
|
pub(crate) struct HookList {
|
|
pub(crate) hooks: Vec<Rc<dyn InterpreterHook>>,
|
|
}
|
|
|
|
impl HookList {
|
|
pub fn new() -> Self { Self { hooks: Vec::new() } }
|
|
|
|
pub fn register(&mut self, hook: Rc<dyn InterpreterHook>) -> 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<String> = self.hooks.iter().map(|h| h.name().to_string()).collect();
|
|
let name_idx: HashMap<String, usize> = 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<usize> = 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<Rc<dyn InterpreterHook>> = order.iter()
|
|
.map(|&i| self.hooks[i].clone()).collect();
|
|
self.hooks = new_hooks;
|
|
Ok(())
|
|
}
|
|
}
|