Phase 6: Add action_cache and expr_cache to RheiContext + cache-first eval/execute methods

This commit is contained in:
Glint Dev
2026-07-22 14:00:56 +03:00
parent 4eec003939
commit 09e3f5e75d

View File

@@ -11,10 +11,18 @@ pub struct RheiContext {
engine: Engine, engine: Engine,
init_ast: AST, init_ast: AST,
scope: RefCell<Scope<'static>>, scope: RefCell<Scope<'static>>,
action_cache: RefCell<HashMap<String, AST>>,
expr_cache: RefCell<HashMap<String, AST>>,
} }
impl RheiContext { impl RheiContext {
pub fn new(scripts: &[String]) -> Self { 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(); let mut engine = Engine::new();
engine.on_print(|s| println!("[rhei] {s}")); engine.on_print(|s| println!("[rhei] {s}"));
@@ -46,6 +54,8 @@ impl RheiContext {
engine, engine,
init_ast: combined, init_ast: combined,
scope: RefCell::new(Scope::new()), scope: RefCell::new(Scope::new()),
action_cache: RefCell::new(HashMap::new()),
expr_cache: RefCell::new(HashMap::new()),
} }
} }
@@ -86,7 +96,10 @@ impl RheiContext {
self.sync_scope(variables); self.sync_scope(variables);
let mut scope = self.scope.borrow_mut(); let mut scope = self.scope.borrow_mut();
match self.engine.eval_expression_with_scope::<Dynamic>(&mut *scope, expr) { let ast = self.get_or_compile_expr(expr);
match ast {
Some(ast) => {
match self.engine.eval_ast_with_scope::<Dynamic>(&mut *scope, &ast) {
Ok(val) => dynamic_to_value(&val), Ok(val) => dynamic_to_value(&val),
Err(e) => { Err(e) => {
eprintln!("⚠️ Rhei eval_expr error: {e}"); eprintln!("⚠️ Rhei eval_expr error: {e}");
@@ -94,12 +107,18 @@ impl RheiContext {
} }
} }
} }
None => Value::None,
}
}
pub fn eval_condition(&self, expr: &str, variables: &HashMap<String, Value>) -> bool { pub fn eval_condition(&self, expr: &str, variables: &HashMap<String, Value>) -> bool {
self.sync_scope(variables); self.sync_scope(variables);
let mut scope = self.scope.borrow_mut(); let mut scope = self.scope.borrow_mut();
match self.engine.eval_expression_with_scope::<bool>(&mut *scope, expr) { let ast = self.get_or_compile_expr(expr);
match ast {
Some(ast) => {
match self.engine.eval_ast_with_scope::<bool>(&mut *scope, &ast) {
Ok(b) => b, Ok(b) => b,
Err(e) => { Err(e) => {
eprintln!("⚠️ Rhei eval_condition error: {e}"); eprintln!("⚠️ Rhei eval_condition error: {e}");
@@ -107,21 +126,20 @@ impl RheiContext {
} }
} }
} }
None => false,
}
}
pub fn execute_action(&self, script: &str, variables: &mut HashMap<String, Value>) { pub fn execute_action(&self, script: &str, variables: &mut HashMap<String, Value>) {
self.sync_scope(variables); self.sync_scope(variables);
let mut scope = self.scope.borrow_mut(); let mut scope = self.scope.borrow_mut();
match self.engine.compile(script) { let action_ast = self.get_or_compile_action(script);
Ok(action_ast) => { if let Some(action_ast) = action_ast {
if let Err(e) = self.engine.run_ast_with_scope(&mut *scope, &action_ast) { if let Err(e) = self.engine.run_ast_with_scope(&mut *scope, &action_ast) {
eprintln!("⚠️ Rhei action execution error: {e}"); eprintln!("⚠️ Rhei action execution error: {e}");
} }
} }
Err(e) => {
eprintln!("⚠️ Rhei action compilation error: {e}");
}
}
for (name, _, val) in scope.iter_raw() { for (name, _, val) in scope.iter_raw() {
let s_val = dynamic_to_value(&val); let s_val = dynamic_to_value(&val);
@@ -130,6 +148,81 @@ impl RheiContext {
} }
} }
} }
fn get_or_compile_action(&self, script: &str) -> Option<AST> {
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<AST> {
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 { impl Default for RheiContext {
@@ -143,7 +236,7 @@ pub fn value_to_dynamic(v: &Value) -> Dynamic {
Value::Int(i) => Dynamic::from(*i), Value::Int(i) => Dynamic::from(*i),
Value::Float(f) => Dynamic::from(*f), Value::Float(f) => Dynamic::from(*f),
Value::Bool(b) => Dynamic::from(*b), 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::None => Dynamic::UNIT,
Value::Array(arr) => { Value::Array(arr) => {
let d: rhai::Dynamic = arr.iter().map(value_to_dynamic).collect(); 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::<i64>() { return Dynamic::from(i); }
if let Ok(f) = s.parse::<f64>() { return Dynamic::from(f); }
if let Ok(b) = s.parse::<bool>() { return Dynamic::from(b); }
Dynamic::from(s.to_owned())
}
pub fn dynamic_to_value(d: &Dynamic) -> Value { pub fn dynamic_to_value(d: &Dynamic) -> Value {
if d.is_string() { if d.is_string() {
return Value::Str(CompactString::new(d.clone().into_string().unwrap_or_default())); return Value::Str(CompactString::new(d.clone().into_string().unwrap_or_default()));