# 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, 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, components: &mut HashMap>, rhei_scripts: &mut Vec, stylesheet: &mut SS, tracker: &mut ReactiveTracker, is_root: bool, ) -> Result>, InterpError> ``` Reads opcodes in a loop, using a stack for nesting (`stack: Vec`). 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>, roots: &mut Vec>, 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, components: &HashMap>, rhei: &RheiContext, stylesheet: &SS, ancestors: &[AncestorInfo], ) -> Vec> ``` 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, components: &HashMap>, rhei: &RheiContext, stylesheet: &SS, ancestors: &[AncestorInfo], dirty_set: &HashSet, ) -> Vec> ``` ### 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 ``` 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> ``` 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) -> 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, 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, 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>, 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>, } ```