From 09e3f5e75dc199d5a7b87a64687eab6e2fb62316 Mon Sep 17 00:00:00 2001 From: Glint Dev Date: Wed, 22 Jul 2026 14:00:56 +0300 Subject: [PATCH] Phase 6: Add action_cache and expr_cache to RheiContext + cache-first eval/execute methods --- src/interpreter/rhei.rs | 138 ++++++++++++++++++++++++++++++++++------ 1 file changed, 119 insertions(+), 19 deletions(-) diff --git a/src/interpreter/rhei.rs b/src/interpreter/rhei.rs index 89ce5e6..2afc7ab 100644 --- a/src/interpreter/rhei.rs +++ b/src/interpreter/rhei.rs @@ -11,10 +11,18 @@ pub struct RheiContext { engine: Engine, init_ast: AST, scope: RefCell>, + action_cache: RefCell>, + expr_cache: RefCell>, } impl RheiContext { pub fn new(scripts: &[String]) -> Self { + let ctx = Self::new_empty(scripts); + ctx.precompile_scripts(scripts); + ctx + } + + fn new_empty(scripts: &[String]) -> Self { let mut engine = Engine::new(); engine.on_print(|s| println!("[rhei] {s}")); @@ -46,6 +54,8 @@ impl RheiContext { engine, init_ast: combined, scope: RefCell::new(Scope::new()), + action_cache: RefCell::new(HashMap::new()), + expr_cache: RefCell::new(HashMap::new()), } } @@ -86,12 +96,18 @@ impl RheiContext { self.sync_scope(variables); let mut scope = self.scope.borrow_mut(); - match self.engine.eval_expression_with_scope::(&mut *scope, expr) { - Ok(val) => dynamic_to_value(&val), - Err(e) => { - eprintln!("⚠️ Rhei eval_expr error: {e}"); - Value::None + let ast = self.get_or_compile_expr(expr); + match ast { + Some(ast) => { + match self.engine.eval_ast_with_scope::(&mut *scope, &ast) { + Ok(val) => dynamic_to_value(&val), + Err(e) => { + eprintln!("⚠️ Rhei eval_expr error: {e}"); + Value::None + } + } } + None => Value::None, } } @@ -99,12 +115,18 @@ impl RheiContext { self.sync_scope(variables); let mut scope = self.scope.borrow_mut(); - match self.engine.eval_expression_with_scope::(&mut *scope, expr) { - Ok(b) => b, - Err(e) => { - eprintln!("⚠️ Rhei eval_condition error: {e}"); - false + let ast = self.get_or_compile_expr(expr); + match ast { + Some(ast) => { + match self.engine.eval_ast_with_scope::(&mut *scope, &ast) { + Ok(b) => b, + Err(e) => { + eprintln!("⚠️ Rhei eval_condition error: {e}"); + false + } + } } + None => false, } } @@ -112,14 +134,10 @@ impl RheiContext { self.sync_scope(variables); let mut scope = self.scope.borrow_mut(); - match self.engine.compile(script) { - Ok(action_ast) => { - if let Err(e) = self.engine.run_ast_with_scope(&mut *scope, &action_ast) { - eprintln!("⚠️ Rhei action execution error: {e}"); - } - } - Err(e) => { - eprintln!("⚠️ Rhei action compilation error: {e}"); + let action_ast = self.get_or_compile_action(script); + if let Some(action_ast) = action_ast { + if let Err(e) = self.engine.run_ast_with_scope(&mut *scope, &action_ast) { + eprintln!("⚠️ Rhei action execution error: {e}"); } } @@ -130,6 +148,81 @@ impl RheiContext { } } } + + fn get_or_compile_action(&self, script: &str) -> Option { + let mut cache = self.action_cache.borrow_mut(); + if let Some(ast) = cache.get(script) { + return Some(ast.clone()); + } + match self.engine.compile(script) { + Ok(ast) => { + cache.insert(script.to_string(), ast.clone()); + Some(ast) + } + Err(e) => { + eprintln!("⚠️ Rhei action compilation error: {e}"); + None + } + } + } + + fn get_or_compile_expr(&self, expr: &str) -> Option { + let mut cache = self.expr_cache.borrow_mut(); + if let Some(ast) = cache.get(expr) { + return Some(ast.clone()); + } + match self.engine.compile_expression(expr) { + Ok(ast) => { + cache.insert(expr.to_string(), ast.clone()); + Some(ast) + } + Err(e) => { + eprintln!("⚠️ Rhei expression compilation error: {e}"); + None + } + } + } + + pub fn precompile_scripts(&self, scripts: &[String]) { + for script in scripts { + self.get_or_compile_action(script); + } + } + + pub fn precompile_actions(&self, actions: &[String]) { + for action in actions { + self.get_or_compile_action(action); + } + } + + pub fn precompile_exprs(&self, exprs: &[String]) { + for expr in exprs { + self.get_or_compile_expr(expr); + } + } + + pub fn precompile_all_from_doc(&self, doc: &super::Document) { + for script in &doc.rhei_scripts { + self.get_or_compile_action(script); + } + for root in &doc.roots { + Self::collect_and_precompile(root, self); + } + } + + fn collect_and_precompile(el: &super::Element, ctx: &RheiContext) { + for (k, v) in &el.properties { + if k.starts_with("__on:") && !v.is_empty() { + ctx.get_or_compile_action(v); + } + if let Some(expr) = v.strip_prefix(RHEI_PREFIX) { + ctx.get_or_compile_expr(expr); + } + } + for child in &el.children { + Self::collect_and_precompile(child, ctx); + } + } } impl Default for RheiContext { @@ -143,7 +236,7 @@ pub fn value_to_dynamic(v: &Value) -> Dynamic { Value::Int(i) => Dynamic::from(*i), Value::Float(f) => Dynamic::from(*f), Value::Bool(b) => Dynamic::from(*b), - Value::Str(s) => Dynamic::from(s.to_string()), + Value::Str(s) => str_to_dynamic(s), Value::None => Dynamic::UNIT, Value::Array(arr) => { let d: rhai::Dynamic = arr.iter().map(value_to_dynamic).collect(); @@ -152,6 +245,13 @@ pub fn value_to_dynamic(v: &Value) -> Dynamic { } } +pub fn str_to_dynamic(s: &str) -> Dynamic { + if let Ok(i) = s.parse::() { return Dynamic::from(i); } + if let Ok(f) = s.parse::() { return Dynamic::from(f); } + if let Ok(b) = s.parse::() { return Dynamic::from(b); } + Dynamic::from(s.to_owned()) +} + pub fn dynamic_to_value(d: &Dynamic) -> Value { if d.is_string() { return Value::Str(CompactString::new(d.clone().into_string().unwrap_or_default()));