docs: reorganize into en/ru and add English translation
This commit is contained in:
305
docs/ru/modules/06-mod.md
Normal file
305
docs/ru/modules/06-mod.md
Normal 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>>,
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user