docs: reorganize into en/ru and add English translation
This commit is contained in:
305
docs/en/modules/06-mod.md
Normal file
305
docs/en/modules/06-mod.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# Module `interpreter` — bytecode interpreter core
|
||||
|
||||
## Module structure
|
||||
|
||||
```
|
||||
interpreter/
|
||||
├── mod.rs — Interpreter: bytecode parsing, VDOM, styles
|
||||
├── opcodes.rs — Opcode definitions (OP_ELEM_PUSH, OP_IF, OP_EACH, …)
|
||||
├── reactive.rs — ReactiveTracker, ElementId — dirty node tracking
|
||||
├── reader.rs — Reader — bytecode reading (str_ref, i64, f64, value, …)
|
||||
├── rhei.rs — RheiContext — Rhai script and expression execution
|
||||
├── style.rs — StyleSheet, AncestorInfo, ComputedStyle, StructuralContext
|
||||
└── types.rs — Element, Document, ComponentDef, Value, FlatVDom, InterpError
|
||||
```
|
||||
|
||||
## `Interpreter` (empty struct)
|
||||
|
||||
```rust
|
||||
pub struct Interpreter;
|
||||
```
|
||||
|
||||
The struct has no fields — all methods are static. It serves as a namespace for interpretation functions.
|
||||
|
||||
## `Interpreter::run()` — primary bytecode parsing
|
||||
|
||||
```rust
|
||||
pub fn run<'a>(bytecode: &'a [u8]) -> Result<Document<'a>, InterpError>
|
||||
```
|
||||
|
||||
1. Checks the magic number (`bytecode[..4] == MAGIC`).
|
||||
2. Creates a `Reader`, `HashMap` for `variables` and `components`, `Vec` for `rhei_scripts`, an empty `StyleSheet`, and a `ReactiveTracker`.
|
||||
3. Calls `parse_block_elements()` for the root level, which reads the opcode stream and builds the `Element` tree.
|
||||
4. After parsing, calls `stylesheet.build_index()`.
|
||||
5. Returns `Document { roots, components, variables, rhei_scripts, stylesheet, interner, tracker }`.
|
||||
|
||||
## `parse_block_elements()` — recursive block parsing
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
Reads opcodes in a loop, using a stack for nesting (`stack: Vec<Element>`). On `OP_END_BLOCK` it finishes the current block (if `!is_root`).
|
||||
|
||||
### Handled opcodes
|
||||
|
||||
| Opcode | Action |
|
||||
|---|---|
|
||||
| `OP_ELEM_PUSH` | Creates an `Element` with `tracker.alloc_id()`, pushes onto the stack |
|
||||
| `OP_ELEM_POP` | Pops an element from the stack, calls `attach()` |
|
||||
| `OP_GLOBAL` / `OP_LET` | Reads name and value, inserts into `variables` |
|
||||
| `OP_SINGLETON` | Skips singleton data |
|
||||
| `OP_CONTENT` | Text content — `text` property. If `OP_PROP_RHEI` — prefix `!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` | Reads key and value, calls `el.push_prop()` |
|
||||
| `OP_RHEI_BLK` | At root level without a parent — script; otherwise — `#text` element with `!rhei:` |
|
||||
| `OP_COMPONENT` | Reads name, parameters, recursively parses child block; stores `ComponentDef` |
|
||||
| `OP_IF` | Reads condition, parses true-block, checks `has_else`, parses false-block. Creates `@if` with `@else` as the last child |
|
||||
| `OP_EACH` | Reads variable name, source (array or `$var` or Rhai), parses template block. Creates `@each` |
|
||||
| `OP_ON` | Reads event name, arguments, looks for `OP_RHEI_BLK` — handler script; sets `__on:{event}` |
|
||||
| `OP_STYLE_RULE` | Reads selector and properties, calls `stylesheet.add_rule()` |
|
||||
|
||||
### `attach()`
|
||||
|
||||
```rust
|
||||
fn attach<'a>(stack: &mut Vec<Element<'a>>, roots: &mut Vec<Element<'a>>, el: Element<'a>)
|
||||
```
|
||||
|
||||
If the stack is not empty — adds to `parent.children`, otherwise to `roots`.
|
||||
|
||||
---
|
||||
|
||||
## `evaluate_vdom()` — VDOM assembly (entry point)
|
||||
|
||||
```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>>
|
||||
```
|
||||
|
||||
Delegates to `evaluate_vdom_incr()` with an empty `dirty_set` — full recomputation.
|
||||
|
||||
### `evaluate_vdom_flat()`
|
||||
|
||||
```rust
|
||||
pub fn evaluate_vdom_flat<'a>(...) -> FlatVDom<'a>
|
||||
```
|
||||
|
||||
Wraps `evaluate_vdom()` and converts the result via `FlatVDom::from_elements()`.
|
||||
|
||||
---
|
||||
|
||||
## `evaluate_vdom_incr()` — incremental VDOM assembly
|
||||
|
||||
```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>>
|
||||
```
|
||||
|
||||
### Preprocessing
|
||||
|
||||
1. **`sibling_infos`** — for each element from `templates` creates an `AncestorInfo` (type_name, id, classes). Needed for structural pseudo-classes (CSS `:nth-child`, `:first-of-type`, etc.).
|
||||
|
||||
2. **`type_counts`** / **`type_seen`** — count of total elements of each type and a counter for `StructuralContext`.
|
||||
|
||||
### Main loop over `templates`
|
||||
|
||||
For each element, a `StructuralContext` is computed:
|
||||
|
||||
```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`
|
||||
|
||||
- Reads `condition`, calls `evaluate_condition()`.
|
||||
- Iterates over `child` elements: if `@else` — active when condition is false; otherwise active when true.
|
||||
- Recursively calls `evaluate_vdom_incr()` for the active branch.
|
||||
|
||||
#### `@each`
|
||||
|
||||
- Gets `var_name` and `source`.
|
||||
- Resolves the source: if prefixed with `!rhei:` — calls `normalize_rhai_array()`, otherwise — `resolve_string()`.
|
||||
- Splits the result by `,`, for each element:
|
||||
- Inserts `var_name` into `variables`, recursively processes the template, restores the previous variable value.
|
||||
|
||||
#### Regular element / Component
|
||||
|
||||
- If `el.type_name` is found in `components`:
|
||||
1. Collects arguments from component parameters via `resolve_prop()`.
|
||||
2. Saves old variable values, inserts new ones.
|
||||
3. Creates `vcomp`, copies properties, resolving them via `resolve_prop()` and `resolve_string()`.
|
||||
4. Computes styles: `collect_matching_styles()` → `stylesheet.compute_cached()`.
|
||||
5. Builds ancestor chain: `build_ancestor_chain()`.
|
||||
6. Recursively processes `comp.children`.
|
||||
7. Computes `content_hash`.
|
||||
8. Restores variables.
|
||||
- Otherwise (regular element):
|
||||
1. Creates `vnode`.
|
||||
2. Resolves properties: `__on:` → `resolve_string()`, `$var` → `__bind:{key}`, others → `resolve_prop()`.
|
||||
3. Computes styles via `collect_matching_styles()` + `compute_cached()`.
|
||||
4. Builds ancestor chain, recursively processes `el.children`.
|
||||
5. Computes `content_hash`.
|
||||
|
||||
---
|
||||
|
||||
## `compute_content_hash()`
|
||||
|
||||
```rust
|
||||
fn compute_content_hash(el: &Element) -> u64
|
||||
```
|
||||
|
||||
Element hash for caching. Considers:
|
||||
- `type_name`
|
||||
- All key-value pairs from `properties`
|
||||
- Recursively `content_hash` of child elements (`children`)
|
||||
|
||||
Uses `DefaultHasher`.
|
||||
|
||||
---
|
||||
|
||||
## `build_ancestor_chain()`
|
||||
|
||||
```rust
|
||||
fn build_ancestor_chain<'a>(ancestors: &[AncestorInfo], el: &Element) -> Vec<AncestorInfo>
|
||||
```
|
||||
|
||||
Copies the current `ancestors`, adds `AncestorInfo` for the current element (type_name, id, classes). Returns the extended chain for passing during recursive child traversal.
|
||||
|
||||
---
|
||||
|
||||
## `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>>
|
||||
```
|
||||
|
||||
Extracts `id`, `classes`, and all attributes of the element, delegates to `stylesheet.matching_rules()` with the full context for CSS selectors.
|
||||
|
||||
---
|
||||
|
||||
## `resolve_string()` — `$var` substitution
|
||||
|
||||
```rust
|
||||
pub fn resolve_string<'a>(val: &'a str, scope: &HashMap<String, Value>) -> Cow<'a, str>
|
||||
```
|
||||
|
||||
- If the string has no `$` — returns `Cow::Borrowed(val)` (no allocations).
|
||||
- Otherwise, traverses the string character by character. After `$`, collects the variable name (letters, digits, `_`), looks it up in `scope`, substitutes the value. If the variable is not found — leaves `$var` as is.
|
||||
|
||||
## `resolve_prop()` — property value resolution
|
||||
|
||||
```rust
|
||||
fn resolve_prop(v: &str, variables: &HashMap<String, Value>, rhei: &RheiContext) -> String
|
||||
```
|
||||
|
||||
- If it starts with `!rhei:` — calls `rhei.eval_expr()`.
|
||||
- Otherwise — `resolve_string()`.
|
||||
|
||||
## `evaluate_condition()` — `@if` condition evaluation
|
||||
|
||||
```rust
|
||||
fn evaluate_condition(cond: &str, variables: &HashMap<String, Value>, rhei: &RheiContext) -> bool
|
||||
```
|
||||
|
||||
- If `!rhei:` — `rhei.eval_condition()`.
|
||||
- Otherwise:
|
||||
1. Strips curly braces `{...}`.
|
||||
2. Performs `resolve_string()`.
|
||||
3. Checks `is_truthy_str()`, then `false`/`0`/empty.
|
||||
4. Attempts to parse numeric operators (`>=`, `<=`, `>`, `<`, `==`, `!=`).
|
||||
|
||||
## `is_truthy()` / `is_truthy_str()` — conversion to bool
|
||||
|
||||
```rust
|
||||
fn is_truthy(v: &Value) -> bool
|
||||
fn is_truthy_str(s: &str) -> bool
|
||||
```
|
||||
|
||||
- `Value::Bool` — by value.
|
||||
- `Value::Int` — non-zero.
|
||||
- `Value::Float` — non-zero.
|
||||
- `Value::Str` — delegates to `is_truthy_str()`.
|
||||
- `Value::None` — `false`.
|
||||
- `Value::Array` — not empty.
|
||||
- String: `""`, `"false"`, `"0"`, `"null"` → `false`; `"true"`, `"1"` → `true`; otherwise parses as `f64`.
|
||||
|
||||
## `normalize_rhai_array()` — Rhai array normalization
|
||||
|
||||
```rust
|
||||
fn normalize_rhai_array(s: &str) -> String
|
||||
```
|
||||
|
||||
Trims `[...]`, splits by `,`, trims whitespace, joins with `,`. Used in `@each` to convert a Rhai array to the format expected by the loop.
|
||||
|
||||
---
|
||||
|
||||
## `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