Phase 6: Add action_cache and expr_cache to RheiContext + cache-first eval/execute methods
This commit is contained in:
@@ -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,12 +96,18 @@ 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);
|
||||||
Ok(val) => dynamic_to_value(&val),
|
match ast {
|
||||||
Err(e) => {
|
Some(ast) => {
|
||||||
eprintln!("⚠️ Rhei eval_expr error: {e}");
|
match self.engine.eval_ast_with_scope::<Dynamic>(&mut *scope, &ast) {
|
||||||
Value::None
|
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);
|
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);
|
||||||
Ok(b) => b,
|
match ast {
|
||||||
Err(e) => {
|
Some(ast) => {
|
||||||
eprintln!("⚠️ Rhei eval_condition error: {e}");
|
match self.engine.eval_ast_with_scope::<bool>(&mut *scope, &ast) {
|
||||||
false
|
Ok(b) => b,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("⚠️ Rhei eval_condition error: {e}");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,14 +134,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.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}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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()));
|
||||||
|
|||||||
Reference in New Issue
Block a user