Files
Glint-Runtime/src/interpreter/rhei.rs

174 lines
5.3 KiB
Rust

use rhai::{Dynamic, Engine, Scope, AST, Module};
use std::collections::HashMap;
use std::cell::RefCell;
use super::types::Value;
use compact_str::CompactString;
pub const RHEI_PREFIX: &str = "__rhei:";
pub struct RheiContext {
engine: Engine,
init_ast: AST,
scope: RefCell<Scope<'static>>,
}
impl RheiContext {
pub fn new(scripts: &[String]) -> Self {
let mut engine = Engine::new();
engine.on_print(|s| println!("[rhei] {s}"));
engine.on_debug(|s, src, pos| {
eprintln!("[rhei debug @ {src:?}:{pos}] {s}");
});
let mut combined = AST::empty();
for (i, script) in scripts.iter().enumerate() {
match engine.compile(script) {
Ok(ast) => combined = combined.merge(&ast),
Err(e) => eprintln!("⚠️ Rhei compile error (block #{i}): {e}"),
}
}
let mut fn_ast = combined.clone();
fn_ast.clear_statements();
match Module::eval_ast_as_new(Scope::new(), &fn_ast, &engine) {
Ok(module) => {
engine.register_global_module(module.into());
}
Err(e) => {
eprintln!("⚠️ Rhei module creation error: {e}");
}
}
Self {
engine,
init_ast: combined,
scope: RefCell::new(Scope::new()),
}
}
pub fn sync_scope(&self, variables: &HashMap<String, Value>) {
let mut scope = self.scope.borrow_mut();
for (k, v) in variables.iter() {
if scope.contains(k) {
if let Some(old_val) = scope.get_value::<Dynamic>(k) {
if dynamic_to_value(&old_val) == *v {
continue;
}
}
scope.set_value(k, value_to_dynamic(v));
} else {
scope.push_dynamic(k.clone(), value_to_dynamic(v));
}
}
}
pub fn initialize(&self, variables: &mut HashMap<String, Value>) {
self.sync_scope(variables);
let mut scope = self.scope.borrow_mut();
if let Err(e) = self.engine.run_ast_with_scope(&mut *scope, &self.init_ast) {
eprintln!("⚠️ Rhei initialization error: {e}");
}
for (name, _, val) in scope.iter_raw() {
let s_val = dynamic_to_value(&val);
if variables.get(name).map(|v| v != &s_val).unwrap_or(true) {
variables.insert(name.to_string(), s_val);
}
}
}
pub fn eval_expr(&self, expr: &str, variables: &HashMap<String, Value>) -> Value {
self.sync_scope(variables);
let mut scope = self.scope.borrow_mut();
match self.engine.eval_expression_with_scope::<Dynamic>(&mut *scope, expr) {
Ok(val) => dynamic_to_value(&val),
Err(e) => {
eprintln!("⚠️ Rhei eval_expr error: {e}");
Value::None
}
}
}
pub fn eval_condition(&self, expr: &str, variables: &HashMap<String, Value>) -> bool {
self.sync_scope(variables);
let mut scope = self.scope.borrow_mut();
match self.engine.eval_expression_with_scope::<bool>(&mut *scope, expr) {
Ok(b) => b,
Err(e) => {
eprintln!("⚠️ Rhei eval_condition error: {e}");
false
}
}
}
pub fn execute_action(&self, script: &str, variables: &mut HashMap<String, Value>) {
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}");
}
}
for (name, _, val) in scope.iter_raw() {
let s_val = dynamic_to_value(&val);
if variables.get(name).map(|v| v != &s_val).unwrap_or(true) {
variables.insert(name.to_string(), s_val);
}
}
}
}
impl Default for RheiContext {
fn default() -> Self {
Self::new(&[])
}
}
pub fn value_to_dynamic(v: &Value) -> Dynamic {
match v {
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::None => Dynamic::UNIT,
Value::Array(arr) => {
let d: rhai::Dynamic = arr.iter().map(value_to_dynamic).collect();
d
}
}
}
pub fn dynamic_to_value(d: &Dynamic) -> Value {
if d.is_string() {
return Value::Str(CompactString::new(d.clone().into_string().unwrap_or_default()));
}
if d.is_int() {
return Value::Int(d.as_int().unwrap_or(0));
}
if d.is_float() {
return Value::Float(d.as_float().unwrap_or(0.0));
}
if d.is_bool() {
return Value::Bool(d.as_bool().unwrap_or(false));
}
if d.is_array() {
let arr = d.clone().into_array().unwrap_or_default();
return Value::Array(arr.iter().map(dynamic_to_value).collect());
}
Value::None
}