Files
Glint-Runtime/docs/ru/modules/06-mod.md

13 KiB
Raw Blame History

Модуль 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 (пустая структура)

pub struct Interpreter;

Структура не содержит полей — все методы статические. Служит пространством имён для функций интерпретации.

Interpreter::run() — первичный парсинг байткода

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() — рекурсивный парсинг блока

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()

fn attach<'a>(stack: &mut Vec<Element<'a>>, roots: &mut Vec<Element<'a>>, el: Element<'a>)

Если стек не пуст — добавляет в parent.children, иначе в roots.


evaluate_vdom() — сборка VDOM (точка входа)

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()

pub fn evaluate_vdom_flat<'a>(...) -> FlatVDom<'a>

Оборачивает evaluate_vdom() и конвертирует результат через FlatVDom::from_elements().


evaluate_vdom_incr() — инкрементальная сборка VDOM

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:

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()

fn compute_content_hash(el: &Element) -> u64

Хеш элемента для кэширования. Учитывает:

  • type_name
  • Все пары ключ-значение из properties
  • Рекурсивно content_hash дочерних элементов (children)

Использует DefaultHasher.


build_ancestor_chain()

fn build_ancestor_chain<'a>(ancestors: &[AncestorInfo], el: &Element) -> Vec<AncestorInfo>

Копирует текущий ancestors, добавляет AncestorInfo для текущего элемента (type_name, id, classes). Возвращает расширенную цепочку для передачи при рекурсивном обходе детей.


collect_matching_styles()

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

pub fn resolve_string<'a>(val: &'a str, scope: &HashMap<String, Value>) -> Cow<'a, str>
  • Если в строке нет $ — возвращает Cow::Borrowed(val) (без аллокаций).
  • Иначе обходит строку посимвольно. После $ собирает имя переменной (буквы, цифры, _), ищет в scope, подставляет значение. Если переменная не найдена — оставляет $var как есть.

resolve_prop() — разрешение значения свойства

fn resolve_prop(v: &str, variables: &HashMap<String, Value>, rhei: &RheiContext) -> String
  • Если начинается с !rhei: — вызывает rhei.eval_expr().
  • Иначе — resolve_string().

evaluate_condition() — вычисление условия @if

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

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::Nonefalse.
  • Value::Array — не пустой.
  • Строка: "", "false", "0", "null"false; "true", "1"true; иначе парсит как f64.

normalize_rhai_array() — нормализация Rhai-массива

fn normalize_rhai_array(s: &str) -> String

Обрезает [...], разбивает по ,, обрезает пробелы, соединяет через ,. Используется в @each для приведения Rhai-массива к формату, ожидаемому циклом.


Document

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

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

pub struct ComponentDef<'a> {
    pub name:     String,
    pub params:   Vec<(String, String)>,
    pub children: Vec<Element<'a>>,
}