docs: reorganize into en/ru and add English translation

This commit is contained in:
Glint Dev
2026-07-24 22:04:29 +03:00
parent 938f05f59d
commit 35f92435a3
20 changed files with 4651 additions and 0 deletions

305
docs/ru/modules/06-mod.md Normal file
View File

@@ -0,0 +1,305 @@
# Модуль `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<Document<'a>, 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<String, Value>,
components: &mut HashMap<String, ComponentDef<'a>>,
rhei_scripts: &mut Vec<String>,
stylesheet: &mut SS,
tracker: &mut ReactiveTracker,
is_root: bool,
) -> Result<Vec<Element<'a>>, InterpError>
```
Читает опкоды в цикле, используя стек для построения вложенности (`stack: Vec<Element>`). На `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<Element<'a>>, roots: &mut Vec<Element<'a>>, el: Element<'a>)
```
Если стек не пуст — добавляет в `parent.children`, иначе в `roots`.
---
## `evaluate_vdom()` — сборка VDOM (точка входа)
```rust
pub fn evaluate_vdom<'a>(
templates: &[Element<'a>],
variables: &mut HashMap<String, Value>,
components: &HashMap<String, ComponentDef<'a>>,
rhei: &RheiContext,
stylesheet: &SS,
ancestors: &[AncestorInfo],
) -> Vec<Element<'a>>
```
Делегирует `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<String, Value>,
components: &HashMap<String, ComponentDef<'a>>,
rhei: &RheiContext,
stylesheet: &SS,
ancestors: &[AncestorInfo],
dirty_set: &HashSet<ElementId>,
) -> Vec<Element<'a>>
```
### Предварительная обработка
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<AncestorInfo>
```
Копирует текущий `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<String, String>>
```
Извлекает `id`, `classes`, все атрибуты элемента, делегирует `stylesheet.matching_rules()` с полным контекстом для CSS-селекторов.
---
## `resolve_string()` — подстановка `$var`
```rust
pub fn resolve_string<'a>(val: &'a str, scope: &HashMap<String, Value>) -> Cow<'a, str>
```
- Если в строке нет `$` — возвращает `Cow::Borrowed(val)` (без аллокаций).
- Иначе обходит строку посимвольно. После `$` собирает имя переменной (буквы, цифры, `_`), ищет в `scope`, подставляет значение. Если переменная не найдена — оставляет `$var` как есть.
## `resolve_prop()` — разрешение значения свойства
```rust
fn resolve_prop(v: &str, variables: &HashMap<String, Value>, rhei: &RheiContext) -> String
```
- Если начинается с `!rhei:` — вызывает `rhei.eval_expr()`.
- Иначе — `resolve_string()`.
## `evaluate_condition()` — вычисление условия `@if`
```rust
fn evaluate_condition(cond: &str, variables: &HashMap<String, Value>, 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<Element<'a>>,
pub components: HashMap<String, ComponentDef<'a>>,
pub variables: HashMap<String, Value>,
pub rhei_scripts: Vec<String>,
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<Element<'a>>,
pub element_id: ElementId,
pub computed_style: Option<ComputedStyle>,
pub content_hash: u64,
}
```
## `ComponentDef`
```rust
pub struct ComponentDef<'a> {
pub name: String,
pub params: Vec<(String, String)>,
pub children: Vec<Element<'a>>,
}
```