# Модуль `interpreter` — ядро интерпретатора байткода ## Структура модуля ``` interpreter/ ├── mod.rs — Интерпретатор: парсинг байткода, VDOM, стили ├── opcodes.rs — Определения opcode (OP_ELEM_PUSH, OP_IF, OP_EACH, …) ├── reactive.rs — ReactiveTracker, ElementId — отслеживание грязных узлов ├── reader.rs — Reader — чтение байткода (str_ref, i64, f64, value, …) ├── rhei.rs — RheiContext — выполнение Rhai-скриптов и выражений ├── style.rs — StyleSheet, AncestorInfo, ComputedStyle, StructuralContext └── types.rs — Element, Document, ComponentDef, Value, FlatVDom, InterpError ``` ## `Interpreter` (пустая структура) ```rust pub struct Interpreter; ``` Структура не содержит полей — все методы статические. Служит пространством имён для функций интерпретации. ## `Interpreter::run()` — первичный парсинг байткода ```rust pub fn run<'a>(bytecode: &'a [u8]) -> Result, InterpError> ``` 1. Проверяет магическое число (`bytecode[..4] == MAGIC`). 2. Создаёт `Reader`, `HashMap` для `variables` и `components`, `Vec` для `rhei_scripts`, пустой `StyleSheet` и `ReactiveTracker`. 3. Вызывает `parse_block_elements()` для корневого уровня, который читает поток опкодов и строит дерево `Element`. 4. После парсинга вызывает `stylesheet.build_index()`. 5. Возвращает `Document { roots, components, variables, rhei_scripts, stylesheet, interner, tracker }`. ## `parse_block_elements()` — рекурсивный парсинг блока ```rust fn parse_block_elements<'a>( r: &mut Reader<'a>, variables: &mut HashMap, components: &mut HashMap>, rhei_scripts: &mut Vec, stylesheet: &mut SS, tracker: &mut ReactiveTracker, is_root: bool, ) -> Result>, InterpError> ``` Читает опкоды в цикле, используя стек для построения вложенности (`stack: Vec`). На `OP_END_BLOCK` завершает текущий блок (если `!is_root`). ### Обрабатываемые опкоды | Опкод | Действие | |---|---| | `OP_ELEM_PUSH` | Создаёт `Element` с `tracker.alloc_id()`, кладёт на стек | | `OP_ELEM_POP` | Снимает элемент со стека, вызывает `attach()` | | `OP_GLOBAL` / `OP_LET` | Читает имя и значение, вставляет в `variables` | | `OP_SINGLETON` | Пропускает данные синглтона | | `OP_CONTENT` | Текстовое содержимое — свойство `text`. Если `OP_PROP_RHEI` — префикс `!rhei:` | | `OP_PROP_STR`, `OP_PROP_VAR`, `OP_PROP_INT`, `OP_PROP_FLOAT`, `OP_PROP_BOOL`, `OP_PROP_RHEI`, `OP_PROP_UNIT`, `OP_PROP_CALL`, `OP_PROP_IDENT`, `OP_PROP_FSPATH`, `OP_PROP_COLOR` | Читает ключ и значение, вызывает `el.push_prop()` | | `OP_RHEI_BLK` | На корневом уровне без родителя — скрипт; иначе — элемент `#text` с `!rhei:` | | `OP_COMPONENT` | Читает имя, параметры, рекурсивно парсит дочерний блок; сохраняет `ComponentDef` | | `OP_IF` | Читает условие, парсит true-блок, проверяет `has_else`, парсит false-блок. Создаёт `@if` с `@else` как последним child | | `OP_EACH` | Читает имя переменной, источник (массив или `$var` или Rhai), парсит блок-шаблон. Создаёт `@each` | | `OP_ON` | Читает имя события, аргументы, ищет `OP_RHEI_BLK` — скрипт обработчика; устанавливает `__on:{event}` | | `OP_STYLE_RULE` | Читает селектор и свойства, вызывает `stylesheet.add_rule()` | ### `attach()` ```rust fn attach<'a>(stack: &mut Vec>, roots: &mut Vec>, el: Element<'a>) ``` Если стек не пуст — добавляет в `parent.children`, иначе в `roots`. --- ## `evaluate_vdom()` — сборка VDOM (точка входа) ```rust pub fn evaluate_vdom<'a>( templates: &[Element<'a>], variables: &mut HashMap, components: &HashMap>, rhei: &RheiContext, stylesheet: &SS, ancestors: &[AncestorInfo], ) -> Vec> ``` Делегирует `evaluate_vdom_incr()` с пустым `dirty_set` — полный пересчёт. ### `evaluate_vdom_flat()` ```rust pub fn evaluate_vdom_flat<'a>(...) -> FlatVDom<'a> ``` Оборачивает `evaluate_vdom()` и конвертирует результат через `FlatVDom::from_elements()`. --- ## `evaluate_vdom_incr()` — инкрементальная сборка VDOM ```rust pub fn evaluate_vdom_incr<'a>( templates: &[&Element<'a>], variables: &mut HashMap, components: &HashMap>, rhei: &RheiContext, stylesheet: &SS, ancestors: &[AncestorInfo], dirty_set: &HashSet, ) -> Vec> ``` ### Предварительная обработка 1. **`sibling_infos`** — для каждого элемента из `templates` создаётся `AncestorInfo` (type_name, id, classes). Нужен для структурных псевдоклассов (CSS `:nth-child`, `:first-of-type` и т.д.). 2. **`type_counts`** / **`type_seen`** — подсчёт общего числа элементов каждого типа и счётчик для `StructuralContext`. ### Основной цикл по `templates` Для каждого элемента вычисляется `StructuralContext`: ```rust let structural = StructuralContext { sibling_index: i, sibling_total: templates.len(), type_index: *type_idx, type_total, has_children: ..., is_root: ancestors.is_empty(), }; ``` #### `@if` - Читает `condition`, вызывает `evaluate_condition()`. - Проходит по `child` элементам: если `@else` — активна когда условие ложно; иначе активна когда истинно. - Рекурсивно вызывает `evaluate_vdom_incr()` для активной ветки. #### `@each` - Получает `var_name` и `source`. - Разрешает источник: если с префиксом `!rhei:` — вызывает `normalize_rhai_array()`, иначе — `resolve_string()`. - Разбивает результат по `,`, для каждого элемента: - Вставляет `var_name` в `variables`, рекурсивно обходит шаблон, восстанавливает предыдущее значение переменной. #### Обычный элемент / Компонент - Если `el.type_name` найден в `components`: 1. Собирает аргументы из параметров компонента через `resolve_prop()`. 2. Сохраняет старые значения переменных, вставляет новые. 3. Создаёт `vcomp`, копирует свойства, разрешая их через `resolve_prop()` и `resolve_string()`. 4. Вычисляет стили: `collect_matching_styles()` → `stylesheet.compute_cached()`. 5. Строит chain предков: `build_ancestor_chain()`. 6. Рекурсивно обходит `comp.children`. 7. Вычисляет `content_hash`. 8. Восстанавливает переменные. - Иначе (обычный элемент): 1. Создаёт `vnode`. 2. Разрешает свойства: `__on:` → `resolve_string()`, `$var` → `__bind:{key}`, остальные → `resolve_prop()`. 3. Вычисляет стили через `collect_matching_styles()` + `compute_cached()`. 4. Строит chain предков, рекурсивно обходит `el.children`. 5. Вычисляет `content_hash`. --- ## `compute_content_hash()` ```rust fn compute_content_hash(el: &Element) -> u64 ``` Хеш элемента для кэширования. Учитывает: - `type_name` - Все пары ключ-значение из `properties` - Рекурсивно `content_hash` дочерних элементов (`children`) Использует `DefaultHasher`. --- ## `build_ancestor_chain()` ```rust fn build_ancestor_chain<'a>(ancestors: &[AncestorInfo], el: &Element) -> Vec ``` Копирует текущий `ancestors`, добавляет `AncestorInfo` для текущего элемента (type_name, id, classes). Возвращает расширенную цепочку для передачи при рекурсивном обходе детей. --- ## `collect_matching_styles()` ```rust fn collect_matching_styles<'a>( el: &Element, stylesheet: &'a SS, active_pseudo: &[&str], structural: &StructuralContext, ancestors: &[AncestorInfo], preceding_siblings: &[AncestorInfo], ) -> Vec<&'a HashMap> ``` Извлекает `id`, `classes`, все атрибуты элемента, делегирует `stylesheet.matching_rules()` с полным контекстом для CSS-селекторов. --- ## `resolve_string()` — подстановка `$var` ```rust pub fn resolve_string<'a>(val: &'a str, scope: &HashMap) -> Cow<'a, str> ``` - Если в строке нет `$` — возвращает `Cow::Borrowed(val)` (без аллокаций). - Иначе обходит строку посимвольно. После `$` собирает имя переменной (буквы, цифры, `_`), ищет в `scope`, подставляет значение. Если переменная не найдена — оставляет `$var` как есть. ## `resolve_prop()` — разрешение значения свойства ```rust fn resolve_prop(v: &str, variables: &HashMap, rhei: &RheiContext) -> String ``` - Если начинается с `!rhei:` — вызывает `rhei.eval_expr()`. - Иначе — `resolve_string()`. ## `evaluate_condition()` — вычисление условия `@if` ```rust fn evaluate_condition(cond: &str, variables: &HashMap, rhei: &RheiContext) -> bool ``` - Если `!rhei:` — `rhei.eval_condition()`. - Иначе: 1. Удаляет фигурные скобки `{...}`. 2. Выполняет `resolve_string()`. 3. Проверяет `is_truthy_str()`, затем `false`/`0`/пусто. 4. Пытается разобрать числовые операторы (`>=`, `<=`, `>`, `<`, `==`, `!=`). ## `is_truthy()` / `is_truthy_str()` — приведение к bool ```rust fn is_truthy(v: &Value) -> bool fn is_truthy_str(s: &str) -> bool ``` - `Value::Bool` — по значению. - `Value::Int` — ненулевой. - `Value::Float` — ненулевой. - `Value::Str` — делегирует `is_truthy_str()`. - `Value::None` — `false`. - `Value::Array` — не пустой. - Строка: `""`, `"false"`, `"0"`, `"null"` → `false`; `"true"`, `"1"` → `true`; иначе парсит как `f64`. ## `normalize_rhai_array()` — нормализация Rhai-массива ```rust fn normalize_rhai_array(s: &str) -> String ``` Обрезает `[...]`, разбивает по `,`, обрезает пробелы, соединяет через `,`. Используется в `@each` для приведения Rhai-массива к формату, ожидаемому циклом. --- ## `Document` ```rust pub struct Document<'a> { pub roots: Vec>, pub components: HashMap>, pub variables: HashMap, pub rhei_scripts: Vec, pub stylesheet: StyleSheet, pub interner: Interner, pub tracker: ReactiveTracker, } ``` ## `Element` ```rust pub struct Element<'a> { pub type_name: &'a str, pub properties: Vec<(String, String)>, pub children: Vec>, pub element_id: ElementId, pub computed_style: Option, pub content_hash: u64, } ``` ## `ComponentDef` ```rust pub struct ComponentDef<'a> { pub name: String, pub params: Vec<(String, String)>, pub children: Vec>, } ```