You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase H.5 host integration: safe becomes a first-class OMC keyword
Until now, `safe a / b` and `safe arr_get(a, idx)` only worked inside
the OMC-written self-healing compiler demos (self_healing_h4.omc,
h5.omc), which carry their own OMC-side parser/AST/encoder/executor.
The host Rust parser/interpreter didn't know `safe` as a keyword —
it would tokenize as an unknown identifier.
This integration brings `safe` into the language proper:
parser.rs: Token::Safe variant + "safe" keyword recognition
ast.rs: Expression::Safe(Box<Expression>) variant
parser.rs: parse_expression peeks for Safe and wraps the rest.
Bare `safe arr_set(buf, i, v);` statements work via
the existing expression-statement fallback.
interpreter.rs: eval_expr dispatches Safe by inner shape:
Div(l, r) → safe_divide(l, r)
arr_get(a, idx) → safe_arr_get(a, idx)
arr_set(a, idx,v) → safe_arr_set(a, idx, v)
Unknown shapes evaluate inner directly.
compiler.rs: Same dispatch for the Rust VM bytecode path,
plus type inference delegates Safe to its inner.
examples/safe_keyword_host.omc — new minimal smoke test that runs
without any self-healing-compiler infrastructure. Eight outputs,
all correct:
safe 89 / 0 → 89
compute(144, 0) [dynamic /0] → 144
compute(144, 3) → 48
safe arr_get(xs, 999) → 20 [fold(999)=610, 610%3=1]
safe arr_get(xs, 1) → 20
safe arr_set(xs, 999, 99) → xs becomes [10, 99, 30]
The mutation case works cleanly through tree-walk because the
interpreter pattern-matches Safe(Call("arr_set", ...)) before
any synthetic-arg shim runs — safe_arr_set receives the actual
Expression::Variable it needs and writes back to the caller's
scope.
Known gap: Safe(Call("arr_set", ...)) compiled to bytecode and
run through the Rust VM still routes via vm_call_builtin's
synthetic-arg shim and loses the mutation. This is the same
shape gap V.7c closed for arr_set with Op::ArrSetNamed; a future
Op::SafeArrSetNamed would close it here too. Tree-walk works
cleanly today; the named-mutation gap is documented and bounded.
Regression: V.9b ✓✓✓ still passes. H.5 OMC demo file (six
sub-demos) all converge. No breakage of the existing surface.
The Phase H story is no longer "fork the self-healing-compiler
demo file." It's "write `safe` where you'd write a runtime guard."
This commit also includes the (slightly delayed) Phase H.5.1
CHANGELOG entry covering the bytecode-VM gap closure shipped in
commit 1deae52.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+41Lines changed: 41 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,47 @@ All notable changes to OMNIcode will be documented in this file.
4
4
5
5
## [Unreleased]
6
6
7
+
### Added (Phase H.5 host-language integration: `safe` as a first-class keyword, 2026-05-14)
8
+
9
+
🎯 **`safe` is now a host-level OMC keyword — no self-healing-demo infrastructure required.**
10
+
11
+
Until now, `safe a / b` and `safe arr_get(a, idx)` only worked inside the OMC-written self-healing compiler demos (`examples/self_healing_h4.omc`, `h5.omc`), which carry their own OMC-side parser, AST, encoder, and executor. The host Rust parser/interpreter didn't know `safe` as a keyword — it would tokenize as an unknown identifier.
12
+
13
+
This integration brings `safe` into the language proper:
14
+
15
+
| Layer | Change |
16
+
|---|---|
17
+
| Lexer (`parser.rs`) | New `Token::Safe`; `"safe"` keyword recognized |
18
+
| AST (`ast.rs`) | New `Expression::Safe(Box<Expression>)` variant |
19
+
| Parser (`parser.rs`) |`parse_expression` peeks for `Token::Safe`, wraps the rest of the expression. Bare statements (`safe arr_set(buf, i, v);`) work via the existing expression-statement fallback |
| Compiler (`compiler.rs`) |`Expression::Safe(inner)` lowers to the matching `Op::Call("safe_*", n)` for known shapes; type inference delegates to the inner expression |
22
+
23
+
#### Smoke test (`examples/safe_keyword_host.omc`)
24
+
25
+
Eight assertions, all pass on the host interpreter without any OMC-written self-healing wrapper:
-`safe arr_set(xs, 999, 99)` writes xs[1]=99; xs[0] and xs[2] unchanged
33
+
34
+
The mutation case (the H.5 named-store fix in OMC bytecode) is naturally clean through tree-walk because the interpreter pattern-matches `Safe(Call("arr_set", [Variable(name), ...]))` before any synthetic-arg shim runs — `safe_arr_set` receives the actual `Expression::Variable(name)` it needs and writes back to the caller's scope.
35
+
36
+
#### What still doesn't work
37
+
38
+
`Safe(Call("arr_set", ...))` compiled to bytecode and run through the Rust VM lowers to `Op::Call("safe_arr_set", 3)`, which routes via `vm_call_builtin`'s synthetic-arg shim → mutation lost. This is the same gap V.7c closed for `arr_set` with `Op::ArrSetNamed`. A future `Op::SafeArrSetNamed(String)` would close it here too. Tonight's scope kept the Rust-VM bytecode path on the existing call shim — tree-walk works cleanly, the named-mutation gap is documented and bounded.
39
+
40
+
#### Why this matters
41
+
42
+
The H.4/H.5 OMC-written demos remain the architecturally pure proof — the bytecode VM rewrites and executes `safe` semantics end-to-end on the φ-math substrate. But for a developer who just wants the feature in their OMC code, it's now a one-keyword opt-in at the language level. The Phase H story is no longer "fork the self-healing-compiler demo file." It's "write `safe` where you'd write a runtime guard."
43
+
44
+
### Added (Phase H.5.1: close the safe arr_set bytecode-VM gap, 2026-05-14)
45
+
46
+
`examples/self_healing_h5.omc` — `safe arr_set(VAR, idx, val)` works through the OMC bytecode VM, not just under tree-walk. New `SAFE_ARR_SET_NAMED varname` opcode in the OMC-written executor mirrors V.7c's `ARR_SET_NAMED` pattern: the variable name rides on the opcode itself rather than going through `CALL_BUILTIN`'s synthetic-arg shim that copies array arguments. Encoder detects bare-VAR first-arg shape and emits the named form; executor pops idx/val, looks up array in scope, computes fold-and-mod healed index, mutates, writes back. Demo 4b verifies: `[55, 13, 0, 0, 34]` buffer state after four `safe arr_set` writes with `idx ∈ {0, 100, -1, 6}`. Six demos, six convergences.
47
+
7
48
### Added (Phase H.5: array-bounds healing via fold_escape on the index, 2026-05-14)
8
49
9
50
🎯 **`examples/self_healing_h5.omc` — `safe arr_get(a, idx)` and `safe arr_set(a, idx, v)` make out-of-bounds accesses total.**
Copy file name to clipboardExpand all lines: README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -42,6 +42,7 @@ What this is **not**: a fast runtime, a production toolchain, a stable API, a de
42
42
| Self-healing across two stages (token + AST), 5 bugs healed in one source |`examples/self_healing_h3.omc`| All four demos converge; `safe(8) → 8` on the integrated case |
43
43
| User-declared runtime self-healing via `safe` keyword |`examples/self_healing_h4.omc`|`compute(144, 0) → 144` — runtime crash converted to finite answer on attractor |
44
44
| Array-bounds healing — out-of-bounds reads become attractor-landing |`examples/self_healing_h5.omc`| Loop walking 8 indices off a 5-element array; every output has `φ=1.000`|
45
+
| Host-level `safe` keyword — works in any OMC program, not just the self-healing demos |`examples/safe_keyword_host.omc`|`safe 89/0 → 89`, `safe arr_get(xs, 999) → 20`, `safe arr_set(xs, 999, 99)` mutates xs[1]|
45
46
46
47
Run any of these with the binary built from this repo:
0 commit comments