Phase 6f: let = immutable, var = mutable (binding-only)
Bindings now signal mutability the way Rust/Swift do, instead of `let` meaning
"local" and `var` meaning "module-level":
- `let x = e` -> immutable binding; a later `x = …` is a compile error
(`cannot assign to immutable 'x' … use var`).
- `var x = e` -> mutable binding, at local OR module scope (position decides
scope; the keyword decides mutability).
- `const` -> unchanged (compile-time).
Immutability is of the *binding*, not the object: `let n = new Node; n.kind = 1`
is fine (mutation through the reference); only rebinding `n` is rejected. The
check lives in emit_assign — a direct `name =` whose target is a `let` local
(loc_mut == 0) errors; field/index targets and `var`/param/loop bindings are
unaffected.
Delivered as three reseeds so the self-hosting compiler never had to compile
source its own rules would reject:
A) add `var` as a local statement + per-local mutability tracking (loc_mut),
no enforcement;
B) migrate every reassigned `let` -> `var` across the compiler, runtime and
examples (337 declarations), driven by a per-function, string/comment-aware
scan (binding targets only, never `x.f =` / `x[i] =`);
C) turn on the check. bootstrap-cfree (compiler vs its own source) and every
golden build (which splices the runtime) then proved zero reassigned `let`
was missed anywhere.
Also folded in: removed leftover debug instrumentation in block() (a `cur=` /
print_int(777…) trace on the separator-error path) and fixed parse.ludic's stale
header comment (no more `struct`). Reseeded (21711 lines); C-free fixpoint holds;
goldens identical; 17/17; vocab + doc-fences clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
573813609c
commit
e1d7797e29
38 changed files with 2909 additions and 2778 deletions
|
|
@ -116,11 +116,11 @@ handler Battle phase Update {
|
|||
|
||||
state EnemyTurn {
|
||||
if k != 0 {
|
||||
let done = 0
|
||||
var done = 0
|
||||
for (t3) in query [Stats, {Party}] {
|
||||
if done == 0 {
|
||||
if t3.hp > 0 {
|
||||
let ed = max(1, e.atk - t3.def + rng_range(0, 4))
|
||||
var ed = max(1, e.atk - t3.def + rng_range(0, 4))
|
||||
if t3.guard == 1 { ed = ed / 2; t3.guard = 0 }
|
||||
t3.hp = t3.hp - ed
|
||||
set_reg(R_EDMG, ed)
|
||||
|
|
@ -128,7 +128,7 @@ handler Battle phase Update {
|
|||
}
|
||||
}
|
||||
}
|
||||
let alive = 0
|
||||
var alive = 0
|
||||
for (t4) in query [Stats, {Party}] {
|
||||
if t4.hp > 0 { alive = alive + 1 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ handler Draw phase Render {
|
|||
clear(0x101018)
|
||||
|
||||
if m == 0 {
|
||||
let cx = 0
|
||||
let cy = 0
|
||||
var cx = 0
|
||||
var cy = 0
|
||||
for (p) in query [Pos, {Player}] {
|
||||
cx = clamp(p.x * 16 - 152, 0, MW * 16 - 320)
|
||||
cy = clamp(p.y * 16 - 112, 0, MH * 16 - 240)
|
||||
|
|
@ -18,7 +18,7 @@ handler Draw phase Render {
|
|||
let c = tile(tx, ty)
|
||||
let sx = tx * 16 - cx
|
||||
let sy = ty * 16 - cy
|
||||
let base = SPR_GRASS
|
||||
var base = SPR_GRASS
|
||||
match c {
|
||||
'r' => base = SPR_PATH
|
||||
',', 'S', 'B' => base = SPR_FLOOR
|
||||
|
|
@ -57,7 +57,7 @@ handler Draw phase Render {
|
|||
if m == 1 {
|
||||
fill_rect(0, 0, 320, 176, 0x241a2e)
|
||||
for (e, a) in query [Stats, Actor, {Enemy}] {
|
||||
let bs = 5
|
||||
var bs = 5
|
||||
if reg(R_ETYPE) == 2 { bs = 7 }
|
||||
draw_sprite_scaled(a.kind, 160 - bs * 8, 44, bs)
|
||||
text(100, 20, enemy_name(reg(R_ETYPE)), 0xffffff, 1)
|
||||
|
|
@ -65,14 +65,14 @@ handler Draw phase Render {
|
|||
fill_rect(100, 30, e.hp * 120 / e.maxhp, 8, 0xe04040)
|
||||
}
|
||||
let ph = reg(R_PHASE)
|
||||
let act = 0
|
||||
var act = 0
|
||||
if ph >= 2 { act = 1 }
|
||||
if ph >= 4 { act = 2 }
|
||||
fill_rect(0, 176, 320, 64, 0x0c0c16)
|
||||
frame_rect(0, 176, 320, 64, 0x3a3a55)
|
||||
for (st, pt) in query [Stats, Party] {
|
||||
let hx = 6 + pt.slot * 92
|
||||
let spr = 5
|
||||
var spr = 5
|
||||
if pt.slot == 1 { spr = 9 }
|
||||
if pt.slot == act { frame_rect(hx - 2, 180, 88, 44, 0xffff00) }
|
||||
draw_sprite_scaled(spr, hx, 184, 2)
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@ handler Control phase Input {
|
|||
if reg(R_MODE) == 0 {
|
||||
for (p) in query [Pos, {Player}] {
|
||||
let k = key()
|
||||
let nx = p.x
|
||||
let ny = p.y
|
||||
var nx = p.x
|
||||
var ny = p.y
|
||||
if k == 'w' { ny = p.y - 1 }
|
||||
if k == 's' { ny = p.y + 1 }
|
||||
if k == 'a' { nx = p.x - 1 }
|
||||
if k == 'd' { nx = p.x + 1 }
|
||||
let moved = 0
|
||||
var moved = 0
|
||||
if walkable(nx, ny) {
|
||||
if nx != p.x or ny != p.y {
|
||||
p.x = nx
|
||||
|
|
@ -88,7 +88,7 @@ handler Control phase Input {
|
|||
set_reg(R_ENC, reg(R_ENC) - 1)
|
||||
if reg(R_ENC) <= 0 {
|
||||
set_reg(R_ENC, rng_range(4, 9))
|
||||
let et = 0
|
||||
var et = 0
|
||||
if reg(R_MAP) == 1 { et = 1 }
|
||||
if reg(R_MAP) == 0 {
|
||||
if rng_chance(40) { et = 1 }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ program Combat {
|
|||
|
||||
# damage after armour, with a crit multiplier applied on an exact roll
|
||||
@export fn damage(attack: int, armour: int, roll: int) -> int {
|
||||
let raw = attack - armour
|
||||
var raw = attack - armour
|
||||
if raw < 1 { raw = 1 }
|
||||
if roll == 20 { raw = raw * CRIT_MULT }
|
||||
return raw
|
||||
|
|
@ -26,8 +26,8 @@ program Combat {
|
|||
# how many hits to drop a target — the loop is here so the caller cannot
|
||||
# accidentally disagree with `damage` about rounding
|
||||
@export fn hits_to_kill(hp: int, attack: int, armour: int) -> int {
|
||||
let left = hp
|
||||
let n = 0
|
||||
var left = hp
|
||||
var n = 0
|
||||
while left > 0 {
|
||||
left = left - damage(attack, armour, 0)
|
||||
n = n + 1
|
||||
|
|
|
|||
|
|
@ -114,14 +114,14 @@ program Snake {
|
|||
clear(0x0d1020)
|
||||
for gy in 0 .. GH {
|
||||
for gx in 0 .. GW {
|
||||
let col = 0x141a2e
|
||||
var col = 0x141a2e
|
||||
if (gx + gy) % 2 == 0 { col = 0x10162a }
|
||||
fill_rect(gx * TS, gy * TS, TS, TS, col)
|
||||
}
|
||||
}
|
||||
fill_rect(reg(R_FX) * TS + 3, reg(R_FY) * TS + 3, TS - 6, TS - 6, 0xe0405a)
|
||||
for (p, s) in query [Pos, Seg] {
|
||||
let col = 0x40c060
|
||||
var col = 0x40c060
|
||||
if s.order == 0 { col = 0x9dffb0 }
|
||||
fill_rect(p.x * TS + 1, p.y * TS + 1, TS - 2, TS - 2, col)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ program Toggles {
|
|||
|
||||
handler Run phase Render {
|
||||
for (e) in query [Player] { disable Shield on self() } # @OnDisable -> 6
|
||||
let n = 0
|
||||
var n = 0
|
||||
for (s) in query [Shield] { n += 1 }
|
||||
print_int(n) # 0 — no live Shield now
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue