Files
Glint-Runtime/docs/ru/modules/04-rhei.md

8.2 KiB
Raw Blame History

Модуль Rhai: src/interpreter/rhei.rs

Интеграция скриптового движка Rhai — компиляция, кеширование AST и выполнение выражений/скриптов.


RHEI_PREFIX — префикс Rhai-выражений

pub const RHEI_PREFIX: &str = "__rhei:";

Константа-маркер для свойств элементов, содержимое которых должно интерпретироваться как Rhai-выражение. Используется в collect_and_precompile() для выборки свойств вида !rhei:....


RheiContext — контекст выполнения Rhai

pub struct RheiContext {
    engine: Engine,
    init_ast: AST,
    scope: RefCell<Scope<'static>>,
    action_cache: RefCell<HashMap<String, AST>>,
    expr_cache: RefCell<HashMap<String, AST>>,
}
Поле Назначение
engine Настроенный экземпляр rhai::Engine
init_ast Объединённое AST всех скриптов инициализации (включая определения функций)
scope Общая область видимости (Scope), разделяемая между вызовами; обёрнута в RefCell для interior mutability
action_cache Кеш скомпилированных скриптов (действий), ключ — исходный код
expr_cache Кеш скомпилированных выражений, ключ — исходный код

Конструкторы

new(scripts)

pub fn new(scripts: &[String]) -> Self

Создаёт контекст через new_empty() и сразу прекомпилирует все переданные скрипты вызовом precompile_scripts().

new_empty(scripts)

fn new_empty(scripts: &[String]) -> Self
  1. Создаёт Engine::new().
  2. Настраивает обработчики on_print (вывод в stdout с префиксом [rhei]) и on_debug (вывод в stderr).
  3. Компилирует все скрипты и сливает их AST в единое дерево через merge(). Ошибки компиляции отдельных блоков логируются, но не прерывают процесс.
  4. Из объединённого AST создаёт модуль (Module::eval_ast_as_new) с пустым скопом — в нём регистрируются глобальные функции, определённые в скриптах. Модуль регистрируется в движке как глобальный (register_global_module).
  5. Инициализирует пустой Scope, пустые кеши action_cache и expr_cache.

sync_scope() — синхронизация переменных

pub fn sync_scope(&self, variables: &HashMap<String, Value>)

Синхронизирует значения из внешнего HashMap в Rhai Scope:

  • Если переменная уже есть в скопе и её значение не изменилось — пропускает.
  • Если переменная есть — обновляет через set_value().
  • Если переменной нет — добавляет через push_dynamic().

Преобразование Value → Dynamic выполняется через value_to_dynamic().


initialize() — инициализация

pub fn initialize(&self, variables: &mut HashMap<String, Value>)
  1. Синхронизирует переменные через sync_scope().
  2. Запускает init_ast (объединённое AST всех скриптов) через run_ast_with_scope().
  3. Обходит все переменные скопа через iter_raw() и записывает обратно в HashMap те, чьи значения изменились.

eval_expr() — вычисление выражения

pub fn eval_expr(&self, expr: &str, variables: &HashMap<String, Value>) -> Value
  1. Синхронизирует переменные.
  2. Получает (компилирует или берёт из кеша) AST выражения через get_or_compile_expr().
  3. Выполняет через eval_ast_with_scope::<Dynamic>().
  4. Преобразует результат Dynamic → Value через dynamic_to_value().
  5. При ошибке возвращает Value::None.

eval_condition() — вычисление условия

pub fn eval_condition(&self, expr: &str, variables: &HashMap<String, Value>) -> bool

Аналогичен eval_expr(), но типизирован как bool. При ошибке возвращает false.


execute_action() — выполнение скрипта

pub fn execute_action(&self, script: &str, variables: &mut HashMap<String, Value>)
  1. Синхронизирует переменные.
  2. Получает AST скрипта через get_or_compile_action().
  3. Выполняет через run_ast_with_scope().
  4. После выполнения обходит скоп и записывает обратно в HashMap изменившиеся переменные.

Кеширование AST

get_or_compile_action(script)

fn get_or_compile_action(&self, script: &str) -> Option<AST>

Проверяет action_cache. При промахе компилирует через engine.compile(), сохраняет в кеш.

get_or_compile_expr(expr)

fn get_or_compile_expr(&self, expr: &str) -> Option<AST>

Проверяет expr_cache. При промахе компилирует через engine.compile_expression(), сохраняет в кеш.

Оба метода при ошибке компиляции логируют её и возвращают None.


Пакетная прекомпиляция

pub fn precompile_scripts(&self, scripts: &[String])
pub fn precompile_actions(&self, actions: &[String])
pub fn precompile_exprs(&self, exprs: &[String])
pub fn precompile_all_from_doc(&self, doc: &super::Document)
Метод Действие
precompile_scripts Компилирует каждый скрипт как действие
precompile_actions То же, что precompile_scripts (алиас)
precompile_exprs Компилирует каждое выражение
precompile_all_from_doc Компилирует все doc.rhei_scripts и рекурсивно обходит дерево элементов

collect_and_precompile()

fn collect_and_precompile(el: &super::Element, ctx: &RheiContext)

Рекурсивно обходит дерево Element:

  • Для свойств, начинающихся с __on:* и непустых — компилирует как действие.
  • Для свойств, начинающихся с RHEI_PREFIX (!rhei:) — компилирует оставшуюся часть как выражение.

Конвертация типов

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)    str_to_dynamic(s)
Value::None      Dynamic::UNIT
Value::Array(a)  Dynamic::from_iter(value_to_dynamic каждого элемента)

dynamic_to_value(d: &Dynamic) -> Value

Проверяет тип через is_string(), is_int(), is_float(), is_bool(), is_array() в порядке приоритета. Если тип не распознан — возвращает Value::None.

str_to_dynamic(s: &str) -> Dynamic

Эвристический парсер строки, пробует последовательно:

  1. s.parse::<i64>() — целое число
  2. s.parse::<f64>() — дробное число
  3. s.parse::<bool>() — булево значение
  4. Иначе — Dynamic::from(s) как строка