Merge Phase 6f: let/var mutability model

This commit is contained in:
Orkun ÇAKILKAYA 2026-08-27 23:52:59 +03:00
commit 0e0c58519d
38 changed files with 2909 additions and 2778 deletions

View file

@ -466,11 +466,37 @@ another `.ludic` file (see `examples/lib/`).
## Statements
`let x = expr` · `x = expr` (`+= -= *= /=`) · `if cond { }` / `if/else`
(the `else` is optional) · `while cond { }` · `for i in a .. b { }` (numeric range) ·
`for (…) in query […] { }` · `break` · `continue` · `return` · `spawn` ·
`despawn` · `enable` / `disable` (a property `on e`, a model, or a handler) ·
`match` · `machine`.
`let x = expr` / `var x = expr` · `x = expr` (`+= -= *= /=`) · `if cond { }` /
`if/else` (the `else` is optional) · `while cond { }` · `for i in a .. b { }`
(numeric range) · `for (…) in query […] { }` · `break` · `continue` · `return` ·
`spawn` · `despawn` · `enable` / `disable` (a property `on e`, a model, or a
handler) · `match` · `machine`.
### Bindings: `let`, `var`, `const`
A binding's keyword states whether it can be reassigned, the way Rust and Swift
use them — not its scope (position decides that: inside a body it is a local,
at the top level it is module state).
- **`let x = e`** — an *immutable* binding. `x = …` afterward is a compile error
(`cannot assign to immutable 'x'`). Reach for `let` by default.
- **`var x = e`** — a *mutable* binding: `x`, `x += 1`, … reassign it. Use it for
loop accumulators and anything that genuinely changes.
- **`const NAME = e`** — a compile-time constant (folded, no storage).
Immutability is of the **binding**, not the object. A `let` that holds a record
or slice still lets you mutate *through* it — the reference itself just cannot be
repointed:
```ludic
# doc-check: skip — illustrative bindings
let n = new Node # immutable binding…
n.kind = 1 # …but mutation through it is fine
n = new Node # ERROR: cannot assign to immutable 'n'
var total = 0
for i in 0 .. 10 { total += i } # a var is the right tool for an accumulator
```
**Statements are separated by a newline or `;`** (both lex to the same separator
token). Two statements may not sit adjacent with only spaces between them — the

View file

@ -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 }
}

View file

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

View file

@ -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 }

View file

@ -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

View file

@ -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)
}

View file

@ -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

View file

@ -79,10 +79,10 @@ fn rt_fill_rect(x: int, y: int, w: int, h: int, c: int) -> void {
let y0 = max(0, y)
let x1 = min(rt_fbw, x + w)
let y1 = min(rt_fbh, y + h)
let j = y0
var j = y0
while j < y1 {
let row = j * rt_fbw
let i = x0
var i = x0
while i < x1 {
poke32(rt_fb, row + i, c)
i = i + 1
@ -108,7 +108,7 @@ fn rt_present() -> void {
# ---- text -----------------------------------------------------------------
fn rt_glyph(x: int, y: int, ch: int, colour: int, sc: int) -> void {
let c = ch
var c = ch
if c >= 97 {
if c <= 122 { c = c - 32 }
}
@ -118,7 +118,7 @@ fn rt_glyph(x: int, y: int, ch: int, colour: int, sc: int) -> void {
let font = rt_font()
for row in 0 .. 7 {
let bits = peek8(font, base + row) - 48
let b = bits
var b = bits
for cc in 0 .. 5 {
let on = b / 16
if on == 1 {
@ -130,9 +130,9 @@ fn rt_glyph(x: int, y: int, ch: int, colour: int, sc: int) -> void {
}
fn rt_text(x: int, y: int, s: str, colour: int, sc: int) -> void {
let i = 0
let cx = x
let ch = peek8(s, 0)
var i = 0
var cx = x
var ch = peek8(s, 0)
while ch != 0 {
rt_glyph(cx, y, ch, colour, sc)
cx = cx + 6 * sc
@ -146,22 +146,22 @@ fn rt_text_int(x: int, y: int, n: int, colour: int, sc: int) -> void {
rt_glyph(x, y, 48, colour, sc)
return
}
let v = n
let cx = x
var v = n
var cx = x
if v < 0 {
rt_glyph(cx, y, 45, colour, sc)
cx = cx + 6 * sc
v = 0 - v
}
let digits = 0
let t = v
var digits = 0
var t = v
while t > 0 {
digits = digits + 1
t = t / 10
}
let p = digits
var p = digits
while p > 0 {
let div = 1
var div = 1
for k in 1 .. p {
div = div * 10
}
@ -196,7 +196,7 @@ fn rt_seed(s: int) -> void {
# xorshift32 (Marsaglia). Runs on the raw 32-bit pattern, so the sign bit is
# masked off only when a caller asks for a number.
fn rt_next_rand() -> int {
let x = rt_rng
var x = rt_rng
x = bxor(x, shl(x, 13))
x = bxor(x, shr(x, 17))
x = bxor(x, shl(x, 5))
@ -238,9 +238,9 @@ fn rt_running() -> bool {
# ---- writing the frame out ------------------------------------------------
fn rt_put_str(buf: ptr, at: int, s: str) -> int {
let i = 0
let n = at
let ch = peek8(s, 0)
var i = 0
var n = at
var ch = peek8(s, 0)
while ch != 0 {
poke8(buf, n, ch)
n = n + 1
@ -255,16 +255,16 @@ fn rt_put_int(buf: ptr, at: int, v: int) -> int {
poke8(buf, at, 48)
return at + 1
}
let digits = 0
let t = v
var digits = 0
var t = v
while t > 0 {
digits = digits + 1
t = t / 10
}
let n = at
let p = digits
var n = at
var p = digits
while p > 0 {
let div = 1
var div = 1
for k in 1 .. p {
div = div * 10
}
@ -280,7 +280,7 @@ fn rt_dump_ppm(path: str) -> void {
if ptr_is_null(f) { return }
let hdr = mem_alloc(64)
let n = rt_put_str(hdr, 0, "P6\n")
var n = rt_put_str(hdr, 0, "P6\n")
n = rt_put_int(hdr, n, rt_fbw)
n = rt_put_str(hdr, n, " ")
n = rt_put_int(hdr, n, rt_fbh)
@ -323,8 +323,8 @@ fn rt_map_size(w: int, h: int) -> void {
fn rt_map_row(y: int, s: str) -> void {
if y < 0 { return }
if y >= 64 { return }
let x = 0
let ch = peek8(s, 0)
var x = 0
var ch = peek8(s, 0)
while ch != 0 {
if x >= 96 { return }
poke8(rt_map, y * 96 + x, ch)
@ -347,8 +347,8 @@ fn rt_tile(x: int, y: int) -> int {
var rt_statusbuf: ptr = ptr_null()
fn rt_status(s: str) -> void {
let i = 0
let ch = peek8(s, 0)
var i = 0
var ch = peek8(s, 0)
while ch != 0 {
if i >= 95 { ch = 0 }
if ch != 0 {

View file

@ -74,17 +74,17 @@ fn rt_decode_png(path: str) -> bool {
if peek8(d, 0) != 137 { mem_free(d); return false }
if peek8(d, 1) != 80 { mem_free(d); return false }
let w = 0
let h = 0
let bd = 0
let ct = 0
var w = 0
var h = 0
var bd = 0
var ct = 0
let plte = mem_alloc(768)
let trns = mem_alloc(256)
let ntrns = 0
var ntrns = 0
let idat = mem_alloc(size)
let idlen = 0
let i = 8
let done = 0
var idlen = 0
var i = 8
var done = 0
while done == 0 {
if i + 8 > size { done = 1 }
@ -122,12 +122,12 @@ fn rt_decode_png(path: str) -> bool {
if w <= 0 { mem_free(d); return false }
if h <= 0 { mem_free(d); return false }
let channels = 1
var channels = 1
if ct == 2 { channels = 3 }
if ct == 6 { channels = 4 }
if ct == 4 { channels = 2 }
let bppbits = bd * channels
let fbpp = (bppbits + 7) / 8
var fbpp = (bppbits + 7) / 8
if fbpp < 1 { fbpp = 1 }
let stride = (w * bppbits + 7) / 8
let rawlen = h * (stride + 1)
@ -144,13 +144,13 @@ fn rt_decode_png(path: str) -> bool {
let cur = line + 1
let prev = cur - (stride + 1)
for x in 0 .. stride {
let a = 0
let b = 0
let c = 0
var a = 0
var b = 0
var c = 0
if x >= fbpp { a = peek8(raw, cur + x - fbpp) }
if y > 0 { b = peek8(raw, prev + x) }
if x >= fbpp { if y > 0 { c = peek8(raw, prev + x - fbpp) } }
let v = peek8(raw, cur + x)
var v = peek8(raw, cur + x)
if ft == 1 { v = v + a }
if ft == 2 { v = v + b }
if ft == 3 { v = v + (a + b) / 2 }
@ -159,7 +159,7 @@ fn rt_decode_png(path: str) -> bool {
let pa = abs(p - a)
let pb = abs(p - b)
let pc = abs(p - c)
let pick = c
var pick = c
if pb <= pc { pick = b }
if pa <= pb { if pa <= pc { pick = a } }
v = v + pick
@ -174,10 +174,10 @@ fn rt_decode_png(path: str) -> bool {
for y in 0 .. h {
let row = y * (stride + 1) + 1
for x in 0 .. w {
let R = 0
let G = 0
let B = 0
let A = 255
var R = 0
var G = 0
var B = 0
var A = 255
if ct == 6 {
R = peek8(raw, row + x * 4)
G = peek8(raw, row + x * 4 + 1)
@ -206,7 +206,7 @@ fn rt_decode_png(path: str) -> bool {
if ct == 0 {
let bp = x * bd
let s = band(shr(peek8(raw, row + bp / 8), 8 - bd - bp % 8), maxv)
let g = s
var g = s
if bd != 8 { g = s * 255 / maxv }
R = g
G = g
@ -246,9 +246,9 @@ fn rt_blend_px(x: int, y: int, argb: int) -> void {
if y < 0 { return }
if x >= rt_fbw { return }
if y >= rt_fbh { return }
let r = band(shr(argb, 16), 255)
let g = band(shr(argb, 8), 255)
let b = band(argb, 255)
var r = band(shr(argb, 16), 255)
var g = band(shr(argb, 8), 255)
var b = band(argb, 255)
if a != 255 {
let dst = peek32(rt_fb, y * rt_fbw + x)
let dr = band(shr(dst, 16), 255)
@ -324,7 +324,7 @@ fn rt_png_load(path: str) -> int {
let base = id * SPR_SZ * SPR_SZ
for y in 0 .. SPR_SZ {
for x in 0 .. SPR_SZ {
let px = 0
var px = 0
if x < png_w {
if y < png_h {
let c = peek32(png_px, y * png_w + x)
@ -382,13 +382,13 @@ fn rt_sprites_load(path: str) -> void {
let size = rt_file_len
let pal = mem_alloc(128 * 4)
mem_set(pal, 0, 128 * 4)
let cur = 0 - 1
let row = 0
let i = 0
var cur = 0 - 1
var row = 0
var i = 0
while i < size {
let start = i
let len = 0
let j = start
var len = 0
var j = start
while j < size {
let ch = peek8(d, j)
if ch == 10 { j = size } else { len = len + 1; j = j + 1 }
@ -396,7 +396,7 @@ fn rt_sprites_load(path: str) -> void {
let c0 = peek8(d, start)
if c0 == 112 { # 'p' — "pal <char> <rrggbb>"
let ch = peek8(d, start + 4)
let v = 0
var v = 0
for k in 0 .. 6 {
let hv = rt_hexval(peek8(d, start + 6 + k))
if hv >= 0 { v = v * 16 + hv }

View file

@ -30,7 +30,7 @@ fn z_start(src: ptr, len: int) -> void {
}
fn z_bits(need: int) -> int {
let val = z_bitbuf
var val = z_bitbuf
while z_bitcnt < need {
if z_pos >= z_len {
z_err = 1
@ -79,9 +79,9 @@ fn z_table_build(table: ptr, lengths: ptr, n: int) -> void {
}
fn z_decode(table: ptr) -> int {
let code = 0
let first = 0
let index = 0
var code = 0
var first = 0
var index = 0
for len in 1 .. 16 {
code = bor(code, z_bits(1))
let count = peek32(table, len)
@ -131,7 +131,7 @@ fn z_stored(out: ptr, at: int, cap: int) -> int {
if z_pos + 4 > z_len { return -1 }
let n = peek8(z_src, z_pos) + shl(peek8(z_src, z_pos + 1), 8)
z_pos = z_pos + 4 # LEN then its one's complement
let w = at
var w = at
for i in 0 .. n {
if z_pos >= z_len { return -1 }
if w >= cap { return -1 }
@ -143,8 +143,8 @@ fn z_stored(out: ptr, at: int, cap: int) -> int {
}
fn z_codes(out: ptr, at: int, cap: int, lit: ptr, dist: ptr) -> int {
let w = at
let sym = z_decode(lit)
var w = at
var sym = z_decode(lit)
while sym != 256 {
if z_err != 0 { return -1 }
if sym < 0 { return -1 }
@ -202,7 +202,7 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
let clen = z_table_new(19)
z_table_build(clen, lengths, 19)
let n = 0
var n = 0
while n < nlen + ndist {
let sym = z_decode(clen)
if sym < 0 { return 0 }
@ -211,8 +211,8 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
n = n + 1
}
if sym >= 16 {
let prev = 0
let rep = 0
var prev = 0
var rep = 0
if sym == 16 {
if n == 0 { return 0 }
prev = peek32(lengths, n - 1)
@ -244,8 +244,8 @@ fn z_inflate(src: ptr, len: int, out: ptr, cap: int) -> int {
z_start(src, len)
let lit = z_table_new(288)
let dist = z_table_new(30)
let w = 0
let final = 0
var w = 0
var final = 0
while final == 0 {
final = z_bits(1)
let btype = z_bits(2)

View file

@ -144,14 +144,14 @@ fn tt_pick_cmap(id: int, d: ptr, co: int) -> void {
poke32(tt_cfmt, id, 0)
if co < 0 { return }
let n = tt_u16(d, co + 2)
let best = 0 - 1
let bestscore = 0 - 1
var best = 0 - 1
var bestscore = 0 - 1
for i in 0 .. n {
let rec = co + 4 + i * 8
let plat = tt_u16(d, rec)
let enc = tt_u16(d, rec + 2)
let off = tt_u32(d, rec + 4)
let score = 0 - 1
var score = 0 - 1
if plat == 0 { score = 2 }
if plat == 0 { if enc == 4 { score = 4 } }
if plat == 0 { if enc == 6 { score = 4 } }
@ -172,7 +172,7 @@ fn rt_font_load(path: str) -> int {
let d = rt_read_file(path)
if ptr_is_null(d) { return 0 - 1 }
let size = rt_file_len
let base = 0
var base = 0
# a .ttc collection points at its first font
if size >= 16 {
if peek8(d, 0) == 116 { if peek8(d, 1) == 116 { if peek8(d, 2) == 99 { if peek8(d, 3) == 102 {
@ -196,7 +196,7 @@ fn rt_font_load(path: str) -> int {
tt_n = tt_n + 1
pokep(tt_data, id, d)
poke32(tt_size, id, size)
let upem = tt_u16(d, head + 18)
var upem = tt_u16(d, head + 18)
if upem <= 0 { upem = 1000 }
poke32(tt_upem, id, upem)
poke32(tt_locfm, id, tt_i16(d, head + 50))
@ -306,20 +306,20 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
if nc >= 0 {
let endpts = g + 10
let npts = 0
var npts = 0
if nc > 0 { npts = tt_u16(d, endpts + (nc - 1) * 2) + 1 }
let p = endpts + nc * 2
var p = endpts + nc * 2
p = p + 2 + tt_u16(d, p) # skip instructions
let flags = mem_alloc(npts + 8)
let i = 0
var i = 0
while i < npts {
let fl = peek8(d, p)
p = p + 1
poke8(flags, i, fl)
i = i + 1
if band(fl, 8) != 0 { # REPEAT
let r = peek8(d, p)
var r = peek8(d, p)
p = p + 1
while r > 0 {
if i < npts { poke8(flags, i, fl); i = i + 1 }
@ -330,7 +330,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
let xs = mem_alloc((npts + 1) * 4)
let ys = mem_alloc((npts + 1) * 4)
let xv = 0
var xv = 0
for k in 0 .. npts {
let fl = peek8(flags, k)
if band(fl, 2) != 0 {
@ -342,7 +342,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
}
poke32(xs, k, xv)
}
let yv = 0
var yv = 0
for k in 0 .. npts {
let fl = peek8(flags, k)
if band(fl, 4) != 0 {
@ -355,7 +355,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
poke32(ys, k, yv)
}
let start = 0
var start = 0
for ci in 0 .. nc {
let last = tt_u16(d, endpts + ci * 2)
for k in start .. last + 1 {
@ -374,14 +374,14 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
mem_free(ys)
} else {
# composite glyph: components, each with its own placement and transform
let p = g + 10
let more = 1
var p = g + 10
var more = 1
while more == 1 {
let flags = tt_u16(d, p)
let cgid = tt_u16(d, p + 2)
p = p + 4
let arg1 = 0
let arg2 = 0
var arg1 = 0
var arg2 = 0
if band(flags, 1) != 0 {
arg1 = tt_i16(d, p)
arg2 = tt_i16(d, p + 2)
@ -391,10 +391,10 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
arg2 = tt_i8(d, p + 1)
p = p + 2
}
let ca = 1.0
let cb = 0.0
let cc = 0.0
let ce = 1.0
var ca = 1.0
var cb = 0.0
var cc = 0.0
var ce = 1.0
if band(flags, 8) != 0 {
ca = as_fixed(shl(tt_i16(d, p), 2)) # F2Dot14 -> Q16.16
ce = ca
@ -412,8 +412,8 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
ce = as_fixed(shl(tt_i16(d, p + 6), 2))
p = p + 8
}
let odx = dx
let ody = dy
var odx = dx
var ody = dy
if band(flags, 2) != 0 {
odx = a * fx(arg1) + c * fx(arg2) + dx
ody = b * fx(arg1) + e * fx(arg2) + dy

View file

@ -13,8 +13,8 @@ fn ed_add(x0: fixed, y0: fixed, x1: fixed, y1: fixed) -> void {
fn tt_isqrt(v: int) -> int {
if v <= 0 { return 0 }
let r = 0
let b = 32768
var r = 0
var b = 32768
while b > 0 {
let t = r + b
if t * t <= v { r = t }
@ -27,11 +27,11 @@ fn tt_isqrt(v: int) -> int {
fn ed_quad(x0: fixed, y0: fixed, cx: fixed, cy: fixed, x1: fixed, y1: fixed) -> void {
let dx = flr(x1) - flr(x0)
let dy = flr(y1) - flr(y0)
let n = tt_isqrt(dx * dx + dy * dy) / 3
var n = tt_isqrt(dx * dx + dy * dy) / 3
if n < 2 { n = 2 }
if n > 24 { n = 24 }
let px = x0
let py = y0
var px = x0
var py = y0
for i in 1 .. n + 1 {
let t = fx(i) / n
let u = 1.0 - t
@ -63,10 +63,10 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
tt_load_outline(id, gid, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0)
if ol_n == 0 { return ptr_null() }
let minx = 999999.0
let miny = 999999.0
let maxx = 0.0 - 999999.0
let maxy = 0.0 - 999999.0
var minx = 999999.0
var miny = 999999.0
var maxx = 0.0 - 999999.0
var maxy = 0.0 - 999999.0
for i in 0 .. ol_n {
let X = peekf(ol_x, i) * scale
let Y = peekf(ol_y, i) * scale
@ -77,8 +77,8 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
}
let x0 = flr(minx)
let y0 = flr(miny)
let x1 = flr(maxx) + 1
let y1 = flr(maxy) + 1
var x1 = flr(maxx) + 1
var y1 = flr(maxy) + 1
if maxx == fx(flr(maxx)) { x1 = flr(maxx) }
if maxy == fx(flr(maxy)) { y1 = flr(maxy) }
let W = x1 - x0
@ -93,17 +93,17 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
gr_oy = y1
# build edges in supersample space (y grows downward)
let start = 0
var start = 0
for ci in 0 .. ol_ne {
let end = peek32(ol_ends, ci)
let cnt = end - start
if cnt >= 2 {
let first_on = 0 - 1
var first_on = 0 - 1
for i in 0 .. cnt {
if first_on < 0 { if peek32(ol_on, start + i) == 1 { first_on = i } }
}
let sx = 0.0
let sy = 0.0
var sx = 0.0
var sy = 0.0
if first_on < 0 {
# all off-curve: start at the midpoint of the first and last point
let mx = (peekf(ol_x, start) + peekf(ol_x, start + cnt - 1)) / 2
@ -115,9 +115,9 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
sx = (peekf(ol_x, start + first_on) * scale - fx(x0)) * TT_SS
sy = (fx(y1) - peekf(ol_y, start + first_on) * scale) * TT_SS
}
let curx = sx
let cury = sy
let step = 0
var curx = sx
var cury = sy
var step = 0
while step < cnt {
let i = (first_on + 1 + step) % cnt
let ix = (peekf(ol_x, start + i) * scale - fx(x0)) * TT_SS
@ -127,11 +127,11 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
curx = ix
cury = iy
} else {
let j = (i + 1) % cnt
var j = (i + 1) % cnt
let jx = (peekf(ol_x, start + j) * scale - fx(x0)) * TT_SS
let jy = (fx(y1) - peekf(ol_y, start + j) * scale) * TT_SS
let ex = jx
let ey = jy
var ex = jx
var ey = jy
if peek32(ol_on, start + j) == 1 {
step = step + 1
} else {
@ -157,11 +157,11 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
for sy in 0 .. SH {
let yc = fx(sy) + 0.5
let m = 0
var m = 0
for i in 0 .. ed_n {
let ya = peekf(ed_y0, i)
let yb = peekf(ed_y1, i)
let hit = 0
var hit = 0
if ya <= yc { if yb > yc { hit = 1 } }
if yb <= yc { if ya > yc { hit = 1 } }
if hit == 1 {
@ -175,8 +175,8 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
for i in 1 .. m {
let kx = peekf(sc_x, i)
let kd = peek32(sc_d, i)
let j = i - 1
let placed = 0
var j = i - 1
var placed = 0
while j >= 0 {
if peekf(sc_x, j) > kx {
pokef(sc_x, j + 1, peekf(sc_x, j))
@ -194,28 +194,28 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
poke32(sc_d, 0, kd)
}
}
let wind = 0
var wind = 0
let oy = sy / TT_SS
for i in 0 .. m - 1 {
wind = wind + peek32(sc_d, i)
if wind != 0 {
let xa = peekf(sc_x, i)
let xb = peekf(sc_x, i + 1)
var xa = peekf(sc_x, i)
var xb = peekf(sc_x, i + 1)
if xa < 0.0 { xa = 0.0 }
if xb > fx(SW) { xb = fx(SW) }
if xb > xa {
let ixa = flr(xa)
let ixb = flr(xb) + 1
for sx in ixa .. ixb {
let cxl = xa
let cxr = xb
var cxl = xa
var cxr = xb
if fx(sx) > cxl { cxl = fx(sx) }
if fx(sx + 1) < cxr { cxr = fx(sx + 1) }
let cvr = cxr - cxl
if cvr > 0.0 {
let oxp = sx / TT_SS
if oxp >= 0 { if oxp < W { if oy >= 0 { if oy < H {
let v = peek8(cover, oy * W + oxp) + flr(cvr * unit)
var v = peek8(cover, oy * W + oxp) + flr(cvr * unit)
if v > 255 { v = 255 }
poke8(cover, oy * W + oxp, v)
} } } }
@ -271,7 +271,7 @@ var u8_next: int = 0 # byte index just past the codepoint last decoded
fn tt_utf8(s: str, at: int) -> int {
let c = peek8(s, at)
let extra = 0 - 1
var extra = 0 - 1
if c < 128 { extra = 0 }
if shr(c, 5) == 6 { extra = 1 }
if shr(c, 4) == 14 { extra = 2 }
@ -284,7 +284,7 @@ fn tt_utf8(s: str, at: int) -> int {
u8_next = at + 1
return c
}
let cp = band(c, shr(63, extra))
var cp = band(c, shr(63, extra))
for i in 0 .. extra {
let b = peek8(s, at + 1 + i)
if band(b, 192) != 128 {
@ -333,10 +333,10 @@ fn rt_text_ttf(font: int, x: int, y: int, s: str, colour: int, px: int) -> void
if px <= 0 { return }
let upem = peek32(tt_upem, font)
let scale = fx(px) / upem
let baseline = y + flr(fx(peek32(tt_asc, font)) * scale + 0.5)
var baseline = y + flr(fx(peek32(tt_asc, font)) * scale + 0.5)
let lineh = flr(fx(peek32(tt_asc, font) - peek32(tt_desc, font) + peek32(tt_gap, font)) * scale + 0.5)
let penx = x
let i = 0
var penx = x
var i = 0
while peek8(s, i) != 0 {
let cp = tt_utf8(s, i)
i = u8_next
@ -355,9 +355,9 @@ fn rt_text_w(font: int, s: str, px: int) -> int {
if font < 0 { return 0 }
if font >= tt_n { return 0 }
if px <= 0 { return 0 }
let w = 0
let best = 0
let i = 0
var w = 0
var best = 0
var i = 0
while peek8(s, i) != 0 {
let cp = tt_utf8(s, i)
i = u8_next

View file

@ -196,7 +196,7 @@ fn ui_tw(font: int, s: ptr, size: int) -> int {
if font >= 0 {
if font < tt_n { return rt_text_w(font, s, size) }
}
let n = 0
var n = 0
while peek8(s, n) != 0 { n = n + 1 }
return n * 6 * max(size / 8, 1)
}
@ -219,7 +219,7 @@ fn ui_dir(t: int) -> int {
return 1
}
fn ui_under(i: int, root: int) -> bool {
let k = i
var k = i
while k >= 0 {
if k == root { return true }
k = peek32(ui_parent, k)
@ -229,7 +229,7 @@ fn ui_under(i: int, root: int) -> bool {
# ---- measure: content sizes, children before parents ----------------------
fn ui_measure() -> void {
let i = ui_n - 1
var i = ui_n - 1
while i >= 0 {
if peek32(ui_visible, i) == 0 {
poke32(ui_rw, i, 0)
@ -237,8 +237,8 @@ fn ui_measure() -> void {
} else {
let t = peek32(ui_type, i)
let pad = peek32(ui_pad, i)
let cw = 0
let ch = 0
var cw = 0
var ch = 0
if t == WT_LABEL { cw = 0 - 1 }
if t == WT_BUTTON { cw = 0 - 1 }
if cw == 0 - 1 {
@ -262,9 +262,9 @@ fn ui_measure() -> void {
ch = peek32(ui_h, i)
} else {
let dir = ui_dir(t)
let mainsz = 0
let cross = 0
let nc = 0
var mainsz = 0
var cross = 0
var nc = 0
for c in 0 .. ui_n {
if peek32(ui_parent, c) == i {
if peek32(ui_visible, c) == 1 {
@ -292,8 +292,8 @@ fn ui_measure() -> void {
}
}
}
let rw = peek32(ui_w, i)
let rh = peek32(ui_h, i)
var rw = peek32(ui_w, i)
var rh = peek32(ui_h, i)
if rw == 0 { rw = cw }
if rh == 0 { rh = ch }
poke32(ui_rw, i, rw)

View file

@ -12,9 +12,9 @@ fn ui_arrange(i: int, x: int, y: int) -> void {
let gap = peek32(ui_gap, i)
let innerw = peek32(ui_rw, i) - 2 * pad
let innerh = peek32(ui_rh, i) - 2 * pad
let total = 0
let ng = 0
let grows = 0
var total = 0
var ng = 0
var grows = 0
for c in 0 .. ui_n {
if peek32(ui_parent, c) == i {
if peek32(ui_visible, c) == 1 {
@ -25,16 +25,16 @@ fn ui_arrange(i: int, x: int, y: int) -> void {
}
}
if ng > 1 { total = total + gap * (ng - 1) }
let avail = innerw
var avail = innerw
if dir == 1 { avail = innerh }
let extra = max(avail - total, 0)
let cx = x + pad
let cy = y + pad
var cx = x + pad
var cy = y + pad
for c in 0 .. ui_n {
if peek32(ui_parent, c) == i {
if peek32(ui_visible, c) == 1 {
let kw = peek32(ui_rw, c)
let kh = peek32(ui_rh, c)
var kw = peek32(ui_rw, c)
var kh = peek32(ui_rh, c)
if peek32(ui_grow, c) != 0 {
if grows > 0 {
if dir == 1 { kh = kh + extra / grows } else { kw = kw + extra / grows }
@ -44,13 +44,13 @@ fn ui_arrange(i: int, x: int, y: int) -> void {
poke32(ui_rw, c, kw)
poke32(ui_rh, c, kh)
if dir == 1 {
let ax = x + pad
var ax = x + pad
if al == 1 { ax = x + pad + (innerw - kw) / 2 }
if al == 2 { ax = x + pad + (innerw - kw) }
ui_arrange(c, ax, cy)
cy = cy + kh + gap
} else {
let ay = y + pad
var ay = y + pad
if al == 1 { ay = y + pad + (innerh - kh) / 2 }
if al == 2 { ay = y + pad + (innerh - kh) }
ui_arrange(c, cx, ay)
@ -64,8 +64,8 @@ fn ui_arrange(i: int, x: int, y: int) -> void {
fn ui_layout() -> void {
if ui_active < 0 { return }
ui_measure()
let px = (rt_fbw - peek32(ui_rw, ui_active)) / 2
let py = (rt_fbh - peek32(ui_rh, ui_active)) / 2
var px = (rt_fbw - peek32(ui_rw, ui_active)) / 2
var py = (rt_fbh - peek32(ui_rh, ui_active)) / 2
if peek32(ui_haspos, ui_active) == 1 {
px = peek32(ui_x, ui_active)
py = peek32(ui_y, ui_active)
@ -75,9 +75,9 @@ fn ui_layout() -> void {
# ---- drawing --------------------------------------------------------------
fn ui_lighten(c: int) -> int {
let r = band(shr(c, 16), 255)
let g = band(shr(c, 8), 255)
let b = band(c, 255)
var r = band(shr(c, 16), 255)
var g = band(shr(c, 8), 255)
var b = band(c, 255)
r = r + (255 - r) * 4 / 10
g = g + (255 - g) * 4 / 10
b = b + (255 - b) * 4 / 10
@ -92,7 +92,7 @@ fn ui_draw_node(i: int) -> void {
let rw = peek32(ui_rw, i)
let rh = peek32(ui_rh, i)
let skin = peek32(ui_skin, i)
let focused = 0
var focused = 0
if i == ui_focus { focused = 1 }
if t == WT_PANEL {
@ -104,7 +104,7 @@ fn ui_draw_node(i: int) -> void {
}
}
if t == WT_BUTTON {
let bg = peek32(ui_bg, i)
var bg = peek32(ui_bg, i)
if bg < 0 { bg = 0x2a2a3a }
if focused == 1 { bg = ui_lighten(bg) }
if skin >= 0 {
@ -112,12 +112,12 @@ fn ui_draw_node(i: int) -> void {
} else {
rt_fill_rect(rx, ry, rw, rh, bg)
}
let bc = peek32(ui_border, i)
var bc = peek32(ui_border, i)
if bc < 0 { bc = 0x555577 }
if focused == 1 { bc = 0xffff00 }
rt_frame_rect(rx, ry, rw, rh, bc)
let s = ui_str(i)
let fg = peek32(ui_fg, i)
var fg = peek32(ui_fg, i)
if fg < 0 { fg = 0xffffff }
let font = peek32(ui_font, i)
let size = peek32(ui_size, i)
@ -125,13 +125,13 @@ fn ui_draw_node(i: int) -> void {
}
if t == WT_LABEL {
let s = ui_str(i)
let fg = peek32(ui_fg, i)
var fg = peek32(ui_fg, i)
if fg < 0 { fg = 0xffffff }
let font = peek32(ui_font, i)
let size = peek32(ui_size, i)
let pad = peek32(ui_pad, i)
let tw = ui_tw(font, s, size)
let tx = rx + pad
var tx = rx + pad
let al = peek32(ui_align, i)
if al == 1 { tx = rx + (rw - tw) / 2 }
if al == 2 { tx = rx + rw - pad - tw }
@ -150,7 +150,7 @@ var ui_fl: ptr = ptr_null()
fn ui_focusables() -> int {
if ptr_is_null(ui_fl) { ui_fl = mem_alloc(UI_MAX * 4) }
let n = 0
var n = 0
for i in 0 .. ui_n {
if peek32(ui_focusable, i) == 1 {
if peek32(ui_visible, i) == 1 {
@ -180,7 +180,7 @@ fn rt_ui_tick(k: int) -> void {
ui_focus = 0 - 1
return
}
let cur = 0 - 1
var cur = 0 - 1
for i in 0 .. n {
if peek32(ui_fl, i) == ui_focus { cur = i }
}
@ -218,8 +218,8 @@ fn rt_ui_set_text(id: int, s: str) -> void {
if id < 0 { return }
if id >= ui_n { return }
let base = id * 96
let i = 0
let ch = peek8(s, 0)
var i = 0
var ch = peek8(s, 0)
while ch != 0 {
if i >= 95 { ch = 0 }
if ch != 0 {
@ -236,8 +236,8 @@ fn rt_ui_set_int(id: int, n: int) -> void {
if id < 0 { return }
if id >= ui_n { return }
let base = id * 96
let at = 0
let v = n
var at = 0
var v = n
if v < 0 {
poke8(ui_dyn, base, 45)
at = 1
@ -247,15 +247,15 @@ fn rt_ui_set_int(id: int, n: int) -> void {
poke8(ui_dyn, base + at, 48)
at = at + 1
} else {
let digits = 0
let t = v
var digits = 0
var t = v
while t > 0 {
digits = digits + 1
t = t / 10
}
let p = digits
var p = digits
while p > 0 {
let div = 1
var div = 1
for k in 1 .. p { div = div * 10 }
poke8(ui_dyn, base + at, 48 + (v / div) % 10)
at = at + 1

View file

@ -22,7 +22,7 @@ fn buf_putc(b: Buf, c: int) -> void {
b.len = b.len + 1
}
fn buf_puts(b: Buf, s: ptr) -> void {
let i = 0
var i = 0
while peek8(s, i) != 0 { buf_putc(b, peek8(s, i)); i = i + 1 }
}
fn buf_puti(b: Buf, n: int) -> void { buf_puts(b, itoa(n)) }

View file

@ -16,6 +16,7 @@ var ll_str: int = 0 # string-constant counter
var loc_name: []ptr
var loc_reg: []ptr
var loc_ty: []ptr
var loc_mut: []int # 1 = mutable (var / param / loop-var), 0 = immutable (let)
var nloc: int = 0
# loop targets for break/continue (innermost last)
@ -45,9 +46,9 @@ fn emit_alloca(llt: ptr) -> ptr {
fn sconcat(a: ptr, b: ptr) -> ptr {
let la = slen(a); let lb = slen(b)
let out = mem_alloc(la + lb + 1)
let i = 0
var i = 0
while i < la { poke8(out, i, peek8(a, i)); i = i + 1 }
let j = 0
var j = 0
while j < lb { poke8(out, la + j, peek8(b, j)); j = j + 1 }
poke8(out, la + lb, 0)
return out
@ -67,12 +68,12 @@ fn is_slice_ty(t: ptr) -> bool { return peek8(t, 0) == 91 and peek8(t, 1) == 93
fn slice_elem(t: ptr) -> ptr { return substr(t, 2, slen(t) - 2) }
fn find_arch(name: ptr) -> Node {
let i = 0
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_ARCH and streq(d.s, name) { return d }; i = i + 1 }
return ptr_null()
}
fn find_comp(name: ptr) -> Node {
let i = 0
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_COMP and streq(d.s, name) { return d }; i = i + 1 }
return ptr_null()
}
@ -82,19 +83,19 @@ fn layout_node(name: ptr) -> Node { return find_comp(name) }
fn layout_ty(name: ptr) -> ptr { return sconcat("%Cmp_", name) }
fn field_index(s: Node, fname: ptr) -> int {
let i = 0
var i = 0
while i < len(s.kids) { if streq(s.kids[i].s, fname) { return i }; i = i + 1 }
return 0 - 1
}
fn field_type(s: Node, fname: ptr) -> ptr {
let i = 0
var i = 0
while i < len(s.kids) { if streq(s.kids[i].s, fname) { return s.kids[i].ty }; i = i + 1 }
return "int"
}
# find a global var/const by name
fn find_global(name: ptr) -> Node {
let i = 0
var i = 0
while i < len(prog) {
let d = prog[i]
if d.kind == N_VAR and streq(d.s, name) { return d }
@ -107,11 +108,11 @@ fn find_global(name: ptr) -> Node {
# `Enum.Variant` -> the variant's ordinal (its index), or -1 if `ename` names no
# enum with that variant. Enum names live in `prog` like any other declaration.
fn enum_ordinal(ename: ptr, vname: ptr) -> int {
let i = 0
var i = 0
while i < len(prog) {
let d = prog[i]
if d.kind == N_ENUM and streq(d.s, ename) {
let j = 0
var j = 0
while j < len(d.kids) { if streq(d.kids[j].s, vname) { return j }; j = j + 1 }
}
i = i + 1
@ -119,7 +120,7 @@ fn enum_ordinal(ename: ptr, vname: ptr) -> int {
return 0 - 1
}
fn find_fn(name: ptr) -> Node {
let i = 0
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_FN and streq(d.s, name) { return d }; i = i + 1 }
return ptr_null()
}
@ -136,7 +137,7 @@ fn register_computed(prop: ptr, field: ptr, ty: ptr, e: Node) -> void {
fn computed_expr(prop: ptr, field: ptr) -> Node {
if ptr_is_null(prop) { return ptr_null() }
let key = sconcat(prop, sconcat(".", field))
let i = 0
var i = 0
while i < len(g_computed) { if streq(g_computed[i].s, key) { return g_computed[i].a }; i = i + 1 }
return ptr_null()
}
@ -155,7 +156,7 @@ fn register_onspawn(model: ptr, body: Node) -> void {
let n = node(N_BLOCK); n.s = model; n.a = body; push(g_onspawn, n)
}
fn onspawn_body(model: ptr) -> Node {
let i = 0
var i = 0
while i < len(g_onspawn) { if streq(g_onspawn[i].s, model) { return g_onspawn[i].a }; i = i + 1 }
return ptr_null()
}
@ -170,7 +171,7 @@ fn register_ondespawn(model: ptr, body: Node) -> void {
let n = node(N_BLOCK); n.s = model; n.a = body; push(g_ondespawn, n)
}
fn ondespawn_body(model: ptr) -> Node {
let i = 0
var i = 0
while i < len(g_ondespawn) { if streq(g_ondespawn[i].s, model) { return g_ondespawn[i].a }; i = i + 1 }
return ptr_null()
}
@ -178,7 +179,7 @@ fn register_onattach(prop: ptr, body: Node) -> void {
let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_onattach, n)
}
fn onattach_body(prop: ptr) -> Node {
let i = 0
var i = 0
while i < len(g_onattach) { if streq(g_onattach[i].s, prop) { return g_onattach[i].a }; i = i + 1 }
return ptr_null()
}
@ -190,12 +191,12 @@ var g_ondisable: []Node
fn register_onenable(prop: ptr, body: Node) -> void { let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_onenable, n) }
fn register_ondisable(prop: ptr, body: Node) -> void { let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_ondisable, n) }
fn onenable_body(prop: ptr) -> Node {
let i = 0
var i = 0
while i < len(g_onenable) { if streq(g_onenable[i].s, prop) { return g_onenable[i].a }; i = i + 1 }
return ptr_null()
}
fn ondisable_body(prop: ptr) -> Node {
let i = 0
var i = 0
while i < len(g_ondisable) { if streq(g_ondisable[i].s, prop) { return g_ondisable[i].a }; i = i + 1 }
return ptr_null()
}
@ -205,13 +206,17 @@ fn is_model(name: ptr) -> bool { return find_arch_id(name) > 0 }
# local variable environment
fn loc_reset() -> void { nloc = 0 }
# push a local. Defaults to mutable (params, loop and query bindings are all
# reassignable/rebindable); a `let` binding marks its slot immutable afterward
# via loc_set_mut, so a later `name = …` can be rejected.
fn loc_push(name: ptr, r: ptr, ty: ptr) -> void {
if nloc < len(loc_name) { loc_name[nloc] = name; loc_reg[nloc] = r; loc_ty[nloc] = ty }
else { push(loc_name, name); push(loc_reg, r); push(loc_ty, ty) }
if nloc < len(loc_name) { loc_name[nloc] = name; loc_reg[nloc] = r; loc_ty[nloc] = ty; loc_mut[nloc] = 1 }
else { push(loc_name, name); push(loc_reg, r); push(loc_ty, ty); push(loc_mut, 1) }
nloc = nloc + 1
}
fn loc_set_mut(m: int) -> void { if nloc > 0 { loc_mut[nloc - 1] = m } }
fn loc_find(name: ptr) -> int {
let i = nloc - 1
var i = nloc - 1
while i >= 0 { if streq(loc_name[i], name) { return i }; i = i - 1 }
return 0 - 1
}

View file

@ -3,7 +3,7 @@
# ahead of it (see emit_alloca). Returns go through a %retval slot + `ret` label.
fn emit_params_sig(d: Node) -> void {
let i = 0
var i = 0
while i < len(d.kids) {
if i > 0 { emit(", ") }
emit(llty(d.kids[i].ty)); emit(" %arg_"); emit(d.kids[i].s)
@ -21,7 +21,7 @@ fn emit_fn(d: Node) -> void {
let rl = llty(ret_ty)
if not streq(rl, "void") { buf_puts(falloc, " %retval = alloca "); buf_puts(falloc, rl); buf_puts(falloc, "\n") }
# params: store each incoming argument into a stack slot
let i = 0
var i = 0
while i < len(d.kids) {
let p = d.kids[i]
let slot = emit_alloca(llty(p.ty))
@ -67,13 +67,13 @@ fn emit_main(d: Node) -> void {
fn emit_program() -> void {
head = buf_new()
code = buf_new()
loc_name = new []ptr; loc_reg = new []ptr; loc_ty = new []ptr
loc_name = new []ptr; loc_reg = new []ptr; loc_ty = new []ptr; loc_mut = new []int
brk_lbl = new []ptr; cnt_lbl = new []ptr
self_stk = new []ptr
mach_stk = new []Node
emit_header()
if has_ecs() { emit_ecs_storage() }
let i = 0
var i = 0
while i < len(prog) { if prog[i].kind == N_FN { emit_fn(prog[i]) }; i = i + 1 }
if has_ecs() { emit_ecs_allocator(); emit_snapshot() }
if has_ui() { emit_ui_build() }

View file

@ -6,12 +6,12 @@
const MAX_ENT: int = 1024
fn has_systems() -> bool {
let i = 0
var i = 0
while i < len(prog) { if prog[i].kind == N_SYS { return true }; i = i + 1 }
return false
}
fn has_models() -> bool {
let i = 0
var i = 0
while i < len(prog) { if prog[i].kind == N_ARCH { return true }; i = i + 1 }
return false
}
@ -31,7 +31,7 @@ fn emit_ecs_storage() -> void {
emith(sconcat("@L_kind = internal global [", sconcat(me, " x i32] zeroinitializer\n")))
emith(sconcat("@L_freelist = internal global [", sconcat(me, " x i32] zeroinitializer\n")))
emith("@L_freen = internal global i32 0\n")
let i = 0
var i = 0
while i < len(prog) {
let c = prog[i]
# per-entity storage for a property (its %Cmp_ layout is emitted in the
@ -52,7 +52,7 @@ fn emit_ecs_storage() -> void {
fn emit_ecs_allocator() -> void {
let me = itoa(MAX_ENT)
emit("define void @L_reset(i32 %e) {\nentry:\n")
let i = 0
var i = 0
while i < len(prog) {
if prog[i].kind == N_COMP {
let hn = sconcat("%h", itoa(i))

View file

@ -57,7 +57,7 @@ fn emit_bin(e: Node) -> Val {
let b = emit_expr(e.b)
let fx = streq(a.ty, "fixed") or streq(b.ty, "fixed")
if is_cmp(e.s) {
let ac = a.code; let bc = b.code
var ac = a.code; var bc = b.code
if fx { ac = to_fixed(a); bc = to_fixed(b) }
let c = emit_bind(sconcat("icmp ", sconcat(cmp_code(e.s), sconcat(" i32 ", sconcat(ac, sconcat(", ", bc))))))
return val(emit_bind(sconcat("zext i1 ", sconcat(c, " to i32"))), "bool")
@ -100,8 +100,8 @@ fn emit_call(e: Node) -> Val {
if is_intrinsic(name) { return emit_intrinsic(name, e) }
if is_intrinsic2(name) { return emit_intrinsic2(name, e) }
if is_math_builtin(name) { return emit_math_builtin(name, e) }
let fn2 = find_fn(name)
let cname = name
var fn2 = find_fn(name)
var cname = name
if ptr_is_null(fn2) {
# a builtin like clear()/reg() is satisfied by its rt_ function
let rtname = sconcat("rt_", name)
@ -112,11 +112,11 @@ fn emit_call(e: Node) -> Val {
# evaluate args first (their IR is emitted before the call instruction)
let args = new []ptr
let atys = new []ptr
let i = 0
var i = 0
while i < len(e.kids) { let v = emit_expr(e.kids[i]); push(args, v.code); push(atys, v.ty); i = i + 1 }
let rl = llty(fn2.ty)
emit(" ")
let rreg = "0"
var rreg = "0"
if not streq(rl, "void") { rreg = nreg(); emit(rreg); emit(" = ") }
emit("call "); emit(rl); emit(" @fn_"); emit(cname); emit("(")
i = 0

View file

@ -21,7 +21,7 @@ fn emit_system_fn(sys: Node) -> void {
}
fn emit_calls_for_phase(phase: ptr) -> void {
let i = 0
var i = 0
while i < len(prog) {
let d = prog[i]
if d.kind == N_SYS and streq(d.ty, phase) { # skip a disabled handler
@ -39,7 +39,7 @@ fn emit_calls_for_phase(phase: ptr) -> void {
# @OnDespawn(Model) hooks compile to `@on_despawn_<Model>(entity)` functions that
# bind the model's properties and run the body — dispatched by kind at `despawn`.
fn emit_despawn_hooks() -> void {
let i = 0
var i = 0
while i < len(g_ondespawn) {
let hk = g_ondespawn[i]
let model = find_arch(hk.s)
@ -64,7 +64,7 @@ fn emit_despawn_hooks() -> void {
fn emit_game_main() -> void {
# every system becomes a function first
let i = 0
var i = 0
while i < len(prog) { if prog[i].kind == N_SYS { emit_system_fn(prog[i]) }; i = i + 1 }
emit_despawn_hooks()

View file

@ -10,7 +10,7 @@ fn emit_str_const(s: ptr) -> ptr {
let n = slen(s)
emith(name); emith(" = private unnamed_addr constant [")
emith(itoa(n + 1)); emith(" x i8] c\"")
let i = 0
var i = 0
while i < n {
let c = peek8(s, i)
if c == 34 or c == 92 or c < 32 or c > 126 {
@ -79,13 +79,13 @@ fn emit_header() -> void {
# property layouts — a %Cmp_ record of named fields, emitted here so `new`
# works whether or not the program runs the ECS. The per-entity @S_/@H_ arrays
# are separate (emit_ecs_storage), emitted only for a program that runs the ECS.
let i = 0
var i = 0
while i < len(prog) {
let d = prog[i]
if d.kind == N_COMP {
emith(layout_ty(d.s)); emith(" = type { ")
if len(d.kids) == 0 { emith("i32") }
let f = 0
var f = 0
while f < len(d.kids) {
if f > 0 { emith(", ") }
emith(llty(d.kids[f].ty))

View file

@ -61,7 +61,7 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
if streq(name, "file_read") or streq(name, "file_write") {
let f = arg_code(e, 0); let b = arg_code(e, 1); let n = arg_code(e, 2)
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
let fn2 = "@fread"
var fn2 = "@fread"
if streq(name, "file_write") { fn2 = "@fwrite" }
let r = emit_bind(sconcat("call i64 ", sconcat(fn2, sconcat("(ptr ", sconcat(b, sconcat(", i64 1, i64 ", sconcat(w, sconcat(", ptr ", sconcat(f, ")")))))))))
return val(emit_bind(sconcat("trunc i64 ", sconcat(r, " to i32"))), "int")
@ -117,7 +117,7 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
if streq(name, "bnot") { let a = arg_code(e, 0); return val(emit_bind(sconcat("xor i32 ", sconcat(a, ", -1"))), "int") }
if streq(name,"shl") or streq(name,"shr") or streq(name,"band") or streq(name,"bor") or streq(name,"bxor") {
let a = arg_code(e, 0); let b = arg_code(e, 1)
let opc = "shl"
var opc = "shl"
if streq(name,"shr") { opc = "lshr" }
if streq(name,"band") { opc = "and" }
if streq(name,"bor") { opc = "or" }

View file

@ -8,7 +8,7 @@ fn emit_machine(st: Node) -> void {
if nmach < len(mach_stk) { mach_stk[nmach] = st } else { push(mach_stk, st) }
nmach = nmach + 1
let endl = lbl("smend")
let i = 0
var i = 0
while i < len(st.kids) {
let state = st.kids[i]
let v = emit_expr(state.b)
@ -29,8 +29,8 @@ fn emit_machine(st: Node) -> void {
fn emit_become(st: Node) -> void {
if nmach == 0 { perr("'become' outside a machine") }
let m = mach_stk[nmach - 1]
let target: Node = ptr_null()
let i = 0
var target: Node = ptr_null()
var i = 0
while i < len(m.kids) { if streq(m.kids[i].s, st.s) { target = m.kids[i] }; i = i + 1 }
if ptr_is_null(target) { perr(sconcat("become: no state ", st.s)) }
let regv = emit_expr(m.a)

View file

@ -15,7 +15,7 @@ fn emit_math_builtin(name: ptr, e: Node) -> Val {
}
if streq(name, "min") or streq(name, "max") {
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1])
let op = "slt"
var op = "slt"
if streq(name, "max") { op = "sgt" }
let c = emit_bind(sconcat("icmp ", sconcat(op, sconcat(" i32 ", sconcat(a.code, sconcat(", ", b.code))))))
return val(emit_bind(sconcat("select i1 ", sconcat(c, sconcat(", i32 ", sconcat(a.code, sconcat(", i32 ", b.code)))))), "int")

View file

@ -13,14 +13,14 @@ fn emit_new_struct(name: ptr) -> Val {
let lty = layout_ty(name)
let sz = emit_sizeof(lty)
let obj = emit_bind(sconcat("call ptr @malloc(i64 ", sconcat(sz, ")")))
let f = 0
var f = 0
while f < len(s.kids) {
let fd = s.kids[f]
let addr = nreg()
emit(" "); emit(addr); emit(" = getelementptr inbounds "); emit(lty)
emit(", ptr "); emit(obj); emit(", i32 0, i32 "); emit(itoa(f)); emit("\n")
let lt = llty(fd.ty)
let v = "0"
var v = "0"
if streq(lt, "ptr") { v = "null" }
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a); v = dv.code }
emit(" store "); emit(lt); emit(" "); emit(v); emit(", ptr "); emit(addr); emit("\n")

View file

@ -3,7 +3,7 @@
# the body once per match. Mirrors ll_query in compiler/back/ir_ecs.c.
fn find_arch_id(name: ptr) -> int {
let i = 0; let n = 1
var i = 0; var n = 1
while i < len(prog) { if prog[i].kind == N_ARCH { if streq(prog[i].s, name) { return n }; n = n + 1 }; i = i + 1 }
return 0
}
@ -34,11 +34,11 @@ fn emit_query(st: Node) -> void {
let terms = st.c
# component / archetype filters
let t = 0
var t = 0
while t < len(terms.kids) {
let tm = terms.kids[t]
let ak = find_arch_id(tm.s)
let ok = "0"
var ok = "0"
if ak > 0 {
let kp = nreg(); emit(" "); emit(kp); emit(" = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_kind, i32 0, i32 "); emit(i1); emit("\n")
let kv = emit_bind(sconcat("load i32, ptr ", kp))
@ -59,7 +59,7 @@ fn emit_query(st: Node) -> void {
# bind the requested components to the loop variables, in order
let save = nloc
let vi = 0
var vi = 0
t = 0
while t < len(terms.kids) {
let tm = terms.kids[t]

View file

@ -19,9 +19,9 @@ fn emit_snapshot_blocks(fn2: ptr) -> void {
emit_io(fn2, "@L_alive", "%nalive")
emit_io(fn2, "@L_freelist", "%nalive")
emit_io(fn2, "@L_kind", "%nalive")
let i = 0
var i = 0
while i < len(prog) { if prog[i].kind == N_VAR { emit_io(fn2, sconcat("@g_", prog[i].s), "4") }; i = i + 1 }
let ci = 0
var ci = 0
i = 0
while i < len(prog) {
if prog[i].kind == N_COMP {

View file

@ -10,13 +10,13 @@ fn emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
let slot = nreg()
emit(" "); emit(slot); emit(" = getelementptr inbounds ["); emit(me); emit(" x %Cmp_"); emit(comp); emit("], ptr @S_"); emit(comp); emit(", i32 0, i32 "); emit(e); emit("\n")
# defaults
let f = 0
var f = 0
while f < len(c.kids) {
let fd = c.kids[f]
let addr = nreg()
emit(" "); emit(addr); emit(" = getelementptr inbounds %Cmp_"); emit(comp); emit(", ptr "); emit(slot); emit(", i32 0, i32 "); emit(itoa(f)); emit("\n")
let lt = llty(fd.ty)
let v = "0"
var v = "0"
if streq(lt, "ptr") { v = "null" }
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a); v = dv.code }
emit(" store "); emit(lt); emit(" "); emit(v); emit(", ptr "); emit(addr); emit("\n")
@ -24,7 +24,7 @@ fn emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
}
# per-spawn overrides
if not ptr_is_null(rec) {
let j = 0
var j = 0
while j < len(rec.kids) {
let fi = rec.kids[j]
let fidx = field_index(c, fi.s)
@ -53,7 +53,7 @@ fn emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
# @OnSpawn hook body can address them by name (like a query binding for one entity).
fn emit_bind_props(model: Node, e: ptr) -> void {
let me = itoa(MAX_ENT)
let c = 0
var c = 0
while c < len(model.kids) {
let pname = model.kids[c].s
let slot = nreg()
@ -73,11 +73,11 @@ fn emit_spawn(st: Node) -> void {
let kp = nreg(); emit(" "); emit(kp); emit(" = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_kind, i32 0, i32 "); emit(e); emit("\n")
emit(" store i32 "); emit(itoa(ak)); emit(", ptr "); emit(kp); emit("\n")
let arch = find_arch(st.s)
let c = 0
var c = 0
while c < len(arch.kids) {
let cn = arch.kids[c].s
let rec = ptr_null()
let i = 0
var rec = ptr_null()
var i = 0
while i < len(st.kids) { if streq(st.kids[i].s, cn) { rec = st.kids[i].a }; i = i + 1 }
emit_init_component(e, cn, rec)
c = c + 1
@ -90,7 +90,7 @@ fn emit_spawn(st: Node) -> void {
nloc = save
}
} else {
let i = 0
var i = 0
while i < len(st.kids) { emit_init_component(e, st.kids[i].s, st.kids[i].a); i = i + 1 }
}
}
@ -102,7 +102,7 @@ fn emit_despawn(st: Node) -> void {
let me = itoa(MAX_ENT)
let kp = nreg(); emit(" "); emit(kp); emit(" = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_kind, i32 0, i32 "); emit(v.code); emit("\n")
let kind = emit_bind(sconcat("load i32, ptr ", kp))
let i = 0
var i = 0
while i < len(g_ondespawn) {
let mname = g_ondespawn[i].s
let c = emit_bind(sconcat("icmp eq i32 ", sconcat(kind, sconcat(", ", itoa(find_arch_id(mname))))))
@ -120,13 +120,13 @@ fn emit_despawn(st: Node) -> void {
# data persists, so re-enabling restores it, and queries already skip a cleared
# flag). A bare `<Model>` / `<Handler>` flips a global enabled flag.
fn emit_toggle(st: Node) -> void {
let val = "0"; if st.ival == 1 { val = "1" }
var val = "0"; if st.ival == 1 { val = "1" }
if not ptr_is_null(st.a) {
let ev = emit_expr(st.a)
let me = itoa(MAX_ENT)
let hp = nreg(); emit(" "); emit(hp); emit(" = getelementptr inbounds ["); emit(me); emit(" x i8], ptr @H_"); emit(st.s); emit(", i32 0, i32 "); emit(ev.code); emit("\n")
emit(" store i8 "); emit(val); emit(", ptr "); emit(hp); emit("\n")
let hb: Node = ptr_null()
var hb: Node = ptr_null()
if st.ival == 1 { hb = onenable_body(st.s) } else { hb = ondisable_body(st.s) }
if not ptr_is_null(hb) {
let save = nloc
@ -138,7 +138,7 @@ fn emit_toggle(st: Node) -> void {
nloc = save
}
} else {
let g = "@HE_"; if is_model(st.s) { g = "@ME_" } # model vs handler
var g = "@HE_"; if is_model(st.s) { g = "@ME_" } # model vs handler
emit(" store i32 "); emit(val); emit(", ptr "); emit(g); emit(st.s); emit("\n")
}
}

View file

@ -2,7 +2,7 @@
# block is skipped until a new basic block opens.
fn emit_block(b: Node) -> void {
let i = 0
var i = 0
while i < len(b.kids) {
if g_term { return }
emit_stmt(b.kids[i])
@ -18,11 +18,16 @@ fn store_at(lt: ptr, v: ptr, addr: ptr) -> void {
fn emit_assign(st: Node) -> void {
# resolve the target's address and type
let t = st.a
let addr = "0"
let ty = "int"
var addr = "0"
var ty = "int"
if t.kind == E_ID {
let li = loc_find(t.s)
if li >= 0 { addr = loc_reg[li]; ty = loc_ty[li] }
if li >= 0 {
# a `let` binding is immutable: rebinding it is an error (mutating THROUGH
# it — `x.field = …`, `x[i] = …` — is fine, and lands in the branches below).
if loc_mut[li] == 0 { perr(sconcat("cannot assign to immutable '", sconcat(t.s, sconcat("' (declared with let; use var) — line ", itoa(st.line))))) }
addr = loc_reg[li]; ty = loc_ty[li]
}
else {
let g = find_global(t.s)
if ptr_is_null(g) { perr(sconcat("assign to unknown ", t.s)) }
@ -35,10 +40,10 @@ fn emit_assign(st: Node) -> void {
}
let lt = llty(ty)
let rv = emit_expr(st.b)
let v = rv.code
var v = rv.code
if not streq(st.s, "=") {
let cur = emit_bind(sconcat("load ", sconcat(lt, sconcat(", ptr ", addr))))
let opc = "add"
var opc = "add"
if streq(st.s, "-=") { opc = "sub" }
if streq(st.s, "*=") { opc = "mul" }
if streq(st.s, "/=") { opc = "sdiv" }
@ -116,7 +121,7 @@ fn emit_return(st: Node) -> void {
}
fn arm_is_default(arm: Node) -> bool {
let p = 0
var p = 0
while p < len(arm.kids) { if arm.kids[p].kind == E_ID and streq(arm.kids[p].s, "_") { return true }; p = p + 1 }
return false
}
@ -124,15 +129,15 @@ fn arm_is_default(arm: Node) -> bool {
fn emit_match(st: Node) -> void {
let sv = emit_expr(st.a)
let endl = lbl("mend")
let deflt: Node = ptr_null()
let i = 0
var deflt: Node = ptr_null()
var i = 0
while i < len(st.kids) {
let arm = st.kids[i]
if arm_is_default(arm) { deflt = arm }
else {
let acc = "0"
let first = true
let p = 0
var acc = "0"
var first = true
var p = 0
while p < len(arm.kids) {
let pv = emit_expr(arm.kids[p])
let c = emit_bind(sconcat("icmp eq i32 ", sconcat(sv.code, sconcat(", ", pv.code))))
@ -156,16 +161,17 @@ fn emit_match(st: Node) -> void {
fn emit_stmt(st: Node) -> void {
if st.kind == S_LET {
let ty = st.ty
var ty = st.ty
if ptr_is_null(ty) { let v0 = emit_expr(st.a); ty = v0.ty
let slot = emit_alloca(llty(ty)); store_at(llty(ty), v0.code, slot); loc_push(st.s, slot, ty); return }
let slot = emit_alloca(llty(ty)); store_at(llty(ty), v0.code, slot); loc_push(st.s, slot, ty); loc_set_mut(st.ival); return }
let slot = emit_alloca(llty(ty))
if ptr_is_null(st.a) {
let z = "0"
var z = "0"
if streq(llty(ty), "ptr") { z = "null" }
store_at(llty(ty), z, slot)
} else { let v = emit_expr(st.a); store_at(llty(ty), v.code, slot) }
loc_push(st.s, slot, ty)
loc_set_mut(st.ival)
return
}
if st.kind == S_ASSIGN { emit_assign(st); return }

View file

@ -15,26 +15,26 @@ fn ui_wtype(w: Node) -> int {
return 0
}
fn ui_prop(w: Node, key: ptr) -> Node {
let i = 0
var i = 0
while i < len(w.b.kids) { if streq(w.b.kids[i].s, key) { return w.b.kids[i].a }; i = i + 1 }
return ptr_null()
}
fn ui_flatten(w: Node, parent: int) -> void {
let idx = len(uiw)
push(uiw, w); push(uiw_parent, parent)
let i = 0
var i = 0
while i < len(w.kids) { ui_flatten(w.kids[i], idx); i = i + 1 }
}
fn ui_flatten_all() -> void {
uiw = new []Node; uiw_parent = new []int; ui_roots = new []int
let i = 0
var i = 0
while i < len(prog) {
if prog[i].kind == N_UI and prog[i].ival == 1 { push(ui_roots, len(uiw)); ui_flatten(prog[i].a, 0 - 1) }
i = i + 1
}
}
fn has_ui() -> bool {
let i = 0
var i = 0
while i < len(prog) { if prog[i].kind == N_UI and prog[i].ival == 1 { return true }; i = i + 1 }
return false
}
@ -42,8 +42,8 @@ fn has_ui() -> bool {
# index of a UI_<name>: a ui block's root, or a widget's id=
fn ui_index_of(nm: ptr) -> int {
let s = substr(nm, 3, slen(nm) - 3) # strip "UI_"
let u = 0; let bi = 0
let i = 0
let u = 0; var bi = 0
var i = 0
while i < len(prog) {
if prog[i].kind == N_UI and prog[i].ival == 1 { if streq(prog[i].s, s) { return ui_roots[bi] }; bi = bi + 1 }
i = i + 1
@ -79,14 +79,14 @@ fn emit_ui_build() -> void {
let saved = code
code = fbody
emit(" call void @fn_rt_ui_reset(i32 "); emit(itoa(len(uiw))); emit(")\n")
let i = 0
var i = 0
while i < len(uiw) {
let w = uiw[i]
let t = ui_wtype(w)
ll_ui_set(i, 0, itoa(t))
ll_ui_set(i, 1, itoa(uiw_parent[i]))
if t == 4 { ll_ui_set(i, 17, "1") }
let p = 0
var p = 0
while p < len(w.b.kids) {
let pr = w.b.kids[p]
let k = pr.s
@ -101,7 +101,7 @@ fn emit_ui_build() -> void {
if streq(k, "skin") { ll_ui_set(i, 20, r) } else { ll_ui_set(i, 19, r) }
} else { if streq(k, "align") {
if v.kind == E_ID {
let a = 0
var a = 0
if streq(v.s, "center") { a = 1 }; if streq(v.s, "end") { a = 2 }
ll_ui_set(i, 12, itoa(a))
} else { let vv = emit_expr(v); ll_ui_set(i, 12, vv.code) }

View file

@ -16,7 +16,7 @@ fn read_file(path: str) -> ptr {
# length of a NUL-terminated buffer
fn cstr_len(s: ptr) -> int {
let n = 0
var n = 0
while peek8(s, n) != 0 { n = n + 1 }
return n
}

View file

@ -37,8 +37,8 @@ fn is_op1(c: int) -> bool {
fn lex(src: ptr) -> void {
toks = new []Tok
let i = 0
let line = 1
var i = 0
var line = 1
let n = slen(src)
while i < n {
let c = peek8(src, i)
@ -52,11 +52,11 @@ fn lex(src: ptr) -> void {
i = i + 1
let start = i
let out = mem_alloc(n)
let j = 0
var j = 0
while i < n and peek8(src, i) != 34 {
if peek8(src, i) == 92 { # backslash escape
let e = peek8(src, i + 1)
let r = e
var r = e
if e == 110 { r = 10 }
if e == 116 { r = 9 }
if e == 48 { r = 0 }
@ -70,7 +70,7 @@ fn lex(src: ptr) -> void {
}
if c == 39 { # 'c' char literal -> int
i = i + 1
let v = 0
var v = 0
if peek8(src, i) == 92 {
let e = peek8(src, i + 1)
if e == 110 { v = 10 }
@ -84,11 +84,11 @@ fn lex(src: ptr) -> void {
}
if char_is_digit(c) {
if c == 48 and peek8(src, i + 1) == 120 { # 0x hex
let v = 0
var v = 0
i = i + 2
while i < n {
let h = peek8(src, i)
let d = 0
var d = 0
if char_is_digit(h) { d = h - 48 }
else { if h >= 97 and h <= 102 { d = h - 87 }
else { if h >= 65 and h <= 70 { d = h - 55 } else { break } } }
@ -98,12 +98,12 @@ fn lex(src: ptr) -> void {
tok_push(TK_INT, ptr_null(), v, line)
continue
}
let v = 0
var v = 0
while i < n and char_is_digit(peek8(src, i)) { v = v * 10 + (peek8(src, i) - 48); i = i + 1 }
# a fractional part makes it a Q16.16 fixed literal
if i < n and peek8(src, i) == 46 and char_is_digit(peek8(src, i + 1)) {
i = i + 1
let fnum = 0; let fden = 1
var fnum = 0; var fden = 1
while i < n and char_is_digit(peek8(src, i)) { fnum = fnum * 10 + (peek8(src, i) - 48); fden = fden * 10; i = i + 1 }
let bits = shl(v, 16) + shl(fnum, 16) / fden
tok_push(TK_FLOAT, ptr_null(), bits, line)

File diff suppressed because it is too large Load diff

View file

@ -16,8 +16,8 @@
# basename: the part of a path after the last '/'.
fn base_name(path: ptr) -> ptr {
let last = 0 - 1
let i = 0
var last = 0 - 1
var i = 0
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 }
return substr(path, last + 1, i - (last + 1))
}
@ -52,18 +52,18 @@ fn die(msg: ptr) -> void {
}
entry {
let path = ptr_null()
let out = ptr_null()
let want = 0 # 0 = auto, 1 = windowed, 2 = headless
let emit_ir = false # --emit-llvm: stop after writing IR
let fmt = false # --fmt: lex + parse only, then exit (the doc-check gate)
let save = false # --save-temps: keep the intermediate .ll
let run = false # compile then execute the result
var path = ptr_null()
var out = ptr_null()
var want = 0 # 0 = auto, 1 = windowed, 2 = headless
var emit_ir = false # --emit-llvm: stop after writing IR
var fmt = false # --fmt: lex + parse only, then exit (the doc-check gate)
var save = false # --save-temps: keep the intermediate .ll
var run = false # compile then execute the result
# multi-call: invoked as `ludic` -> run mode by default
if streq(base_name(os_arg(0)), "ludic") { run = true }
let ai = 1
var ai = 1
while ai < os_argc() {
let a = os_arg(ai)
if streq(a, "--windowed") { want = 1 }
@ -134,7 +134,7 @@ entry {
# platform layer and the Cocoa framework.
# -Wno-override-module: our IR carries an explicit target triple, which clang
# would otherwise warn about on every build.
let cmd = sconcat(cc, sconcat(" -O2 -Wno-override-module ", ll))
var cmd = sconcat(cc, sconcat(" -O2 -Wno-override-module ", ll))
if g_windowed {
let cocoa = path_join(home, "runtime/native/cocoa.ll")
cmd = sconcat(cmd, sconcat(" ", sconcat(cocoa, " -framework Cocoa -Wl,-rpath,@loader_path")))

View file

@ -1,6 +1,6 @@
# parse.ludic — recursive-descent parser: the token slice -> an AST.
# Reduced grammar: structs, vars, consts, fns, main; no ECS/scene/ui/match.
# Mirrors compiler/front/parse.c. Uses the global `toks` and a cursor `pi`.
# Core grammar: properties, vars/lets, consts, fns, entry; the ECS/ui/match
# constructs live in parse_game.ludic. Uses the global `toks` and a cursor `pi`.
var pi: int = 0
var prog: []Node # the top-level declarations
@ -37,7 +37,7 @@ fn ptype() -> ptr {
let el = ptype()
let out = mem_alloc(slen(el) + 3)
poke8(out, 0, 91); poke8(out, 1, 93) # "[]"
let i = 0
var i = 0
while peek8(el, i) != 0 { poke8(out, 2 + i, peek8(el, i)); i = i + 1 }
poke8(out, 2 + i, 0)
return out
@ -71,7 +71,7 @@ fn p_primary() -> Node {
}
fn p_postfix() -> Node {
let e = p_primary()
var e = p_primary()
while true {
if is_op(".") { pi = pi + 1; let m = node(E_MEMBER); m.a = e; m.s = eat_id(); e = m }
else { if is_op("[") { pi = pi + 1; let ix = node(E_INDEX); ix.a = e; ix.b = expr(); eat_op("]"); e = ix }
@ -89,29 +89,29 @@ fn p_unary() -> Node {
fn mkbin(op: ptr, l: Node, r: Node) -> Node { let b = node(E_BIN); b.s = op; b.a = l; b.b = r; return b }
fn p_mul() -> Node {
let l = p_unary()
var l = p_unary()
while is_op("*") or is_op("/") or is_op("%") { let op = toks[pi].text; pi = pi + 1; l = mkbin(op, l, p_unary()) }
return l
}
fn p_add() -> Node {
let l = p_mul()
var l = p_mul()
while is_op("+") or is_op("-") { let op = toks[pi].text; pi = pi + 1; l = mkbin(op, l, p_mul()) }
return l
}
fn p_cmp() -> Node {
let l = p_add()
var l = p_add()
while is_op("<") or is_op("<=") or is_op(">") or is_op(">=") or is_op("==") or is_op("!=") {
let op = toks[pi].text; pi = pi + 1; l = mkbin(op, l, p_add())
}
return l
}
fn p_and() -> Node {
let l = p_cmp()
var l = p_cmp()
while is_id("and") { pi = pi + 1; l = mkbin("and", l, p_cmp()) }
return l
}
fn p_or() -> Node {
let l = p_and()
var l = p_and()
while is_id("or") { pi = pi + 1; l = mkbin("or", l, p_and()) }
return l
}
@ -136,14 +136,9 @@ fn block() -> Node {
# Rule B: statements are separated by a newline or ';' (both lex to TK_NL).
# After a statement the next token must be that separator or the block's end —
# two statements may not sit adjacent with only spaces between them.
let sep = toks[pi].kind == TK_NL
var sep = toks[pi].kind == TK_NL
if is_op("}") { sep = true }
if not sep {
let ez = file_stderr(); file_write(ez, "cur=", 4)
if not ptr_is_null(toks[pi].text) { file_write(ez, toks[pi].text, slen(toks[pi].text)) }
file_write(ez, "\n", 1); print_int(7770000 + toks[pi].line)
perr("expected newline or ';' between statements")
}
if not sep { perr("expected newline or ';' between statements") }
}
eat_op("}")
return b
@ -152,8 +147,9 @@ fn block() -> Node {
fn stmt() -> Node {
let t = toks[pi]
if t.kind == TK_ID {
if streq(t.text, "let") {
pi = pi + 1; let n = node(S_LET); n.s = eat_id()
if streq(t.text, "let") or streq(t.text, "var") {
var mut = 0; if streq(t.text, "var") { mut = 1 } # let = immutable, var = mutable
pi = pi + 1; let n = node(S_LET); n.ival = mut; n.line = toks[pi].line; n.s = eat_id()
if is_op(":") { pi = pi + 1; n.ty = ptype() }
if is_op("=") { pi = pi + 1; n.a = expr() }
return n
@ -183,7 +179,7 @@ fn stmt() -> Node {
if streq(t.text, "spawn") { return parse_spawn() }
if streq(t.text, "machine") {
pi = pi + 1; let n = node(S_MACHINE); n.a = expr(); skipnl(); eat_op("{")
let sidx = 0 # states auto-number by declaration order
var sidx = 0 # states auto-number by declaration order
while true { skipnl(); if is_op("}") { break }
let stkw = eat_id() # 'state'
let s = node(S_STATE); s.s = eat_id()
@ -196,7 +192,7 @@ fn stmt() -> Node {
if streq(t.text, "become") { pi = pi + 1; let n = node(S_BECOME); n.s = eat_id(); return n }
if streq(t.text, "despawn") { pi = pi + 1; let n = node(S_DESPAWN); n.a = expr(); return n }
if streq(t.text, "enable") or streq(t.text, "disable") {
let en = 0; if streq(t.text, "enable") { en = 1 }
var en = 0; if streq(t.text, "enable") { en = 1 }
pi = pi + 1; let n = node(S_TOGGLE); n.ival = en; n.s = eat_id() # `enable P on e` / `disable Model` / `disable Handler`
if is_id("on") { pi = pi + 1; n.a = expr() } # property on an entity
return n
@ -218,7 +214,7 @@ fn stmt() -> Node {
}
let e = expr()
if is_op("=") or is_op("+=") or is_op("-=") or is_op("*=") or is_op("/=") {
let n = node(S_ASSIGN); n.s = toks[pi].text; pi = pi + 1; n.a = e; n.b = expr(); return n
let n = node(S_ASSIGN); n.line = toks[pi].line; n.s = toks[pi].text; pi = pi + 1; n.a = e; n.b = expr(); return n
}
let n = node(S_EXPR); n.a = e; return n
}
@ -249,8 +245,8 @@ fn parse_main() -> Node { pi = pi + 1; let n = node(N_MAIN); n.a = block(); retu
# directory part of a path, including the trailing '/', or "" if none
fn dir_of(path: ptr) -> ptr {
let last = 0 - 1
let i = 0
var last = 0 - 1
var i = 0
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 }
if last < 0 { return "" }
return substr(path, 0, last + 1)
@ -264,7 +260,7 @@ var loaded_paths: []ptr
var cur_dir: ptr
fn already_loaded(full: ptr) -> bool {
let i = 0
var i = 0
while i < len(loaded_paths) { if streq(loaded_paths[i], full) { return true }; i = i + 1 }
return false
}
@ -273,14 +269,14 @@ fn already_loaded(full: ptr) -> bool {
# Modifiers are `@annotations` in front of the declaration: `@export`, `@edge`,
# `@pure`, `@deterministic`, … — one channel, not a zoo of prefix keywords.
fn parse_one_decl() -> void {
let is_export = false
let qspec: Node = ptr_null()
let onspawn_model: ptr = ptr_null()
let ondespawn_model: ptr = ptr_null()
let onattach_prop: ptr = ptr_null()
let onenable_prop: ptr = ptr_null()
let ondisable_prop: ptr = ptr_null()
let hook_phase: ptr = ptr_null() # @OnStart / @OnQuit override the phase
var is_export = false
var qspec: Node = ptr_null()
var onspawn_model: ptr = ptr_null()
var ondespawn_model: ptr = ptr_null()
var onattach_prop: ptr = ptr_null()
var onenable_prop: ptr = ptr_null()
var ondisable_prop: ptr = ptr_null()
var hook_phase: ptr = ptr_null() # @OnStart / @OnQuit override the phase
while is_op("@") {
pi = pi + 1; let a = eat_id() # collect a leading @annotation
if streq(a, "export") { is_export = true }
@ -292,7 +288,7 @@ fn parse_one_decl() -> void {
else { if streq(a, "OnDisable") { eat_op("("); ondisable_prop = eat_id(); eat_op(")") }
else { if streq(a, "OnStart") { hook_phase = "Start" } # boot
else { if streq(a, "OnQuit") { hook_phase = "OnQuit" } # shutdown
else { if is_op("(") { let d = 0 # any other @anno(args) — parsed and skipped
else { if is_op("(") { var d = 0 # any other @anno(args) — parsed and skipped
while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } } } } } } } } } } }
skipnl()
}
@ -371,7 +367,7 @@ fn parse_program() -> void {
# @annotations on the program itself (e.g. @Handles(Movement)) — parsed, skipped
while is_op("@") {
pi = pi + 1; let a = eat_id()
if is_op("(") { let d = 0
if is_op("(") { var d = 0
while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } }
skipnl()
}

View file

@ -5,7 +5,7 @@
fn parse_component() -> Node {
pi = pi + 1; let n = node(N_COMP); n.s = eat_id(); skipnl(); eat_op("{")
while true { skipnl(); if is_op("}") { break }
let is_computed = false
var is_computed = false
if is_op("@") { pi = pi + 1; let ann = eat_id(); if streq(ann, "Computed") { is_computed = true }; skipnl() }
let f = node(N_FIELD); f.s = eat_id(); eat_op(":"); f.ty = ptype()
if is_op("=") { pi = pi + 1; f.a = expr() }
@ -23,7 +23,7 @@ fn parse_system() -> Node {
# in a signature clause.
while true {
skipnl() # clauses may span several lines
if is_op("@") { pi = pi + 1; let a = eat_id(); if is_op("(") { let d = 0 # @anno, one per turn so a
if is_op("@") { pi = pi + 1; let a = eat_id(); if is_op("(") { var d = 0 # @anno, one per turn so a
while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } }
continue } # newline-separated @anno re-skips at the loop top
if is_id("phase") { pi = pi + 1; n.ty = eat_id(); continue }
@ -99,7 +99,7 @@ fn parse_queries_anno() -> Node {
eat_op("(")
let qn = node(S_QUERY)
let terms = node(N_BLOCK)
let wh: Node = ptr_null()
var wh: Node = ptr_null()
while not is_op(")") {
skipnl()
if is_op(")") { break }

View file

@ -3,7 +3,7 @@
# byte buffers reached with peek8/poke8.
fn streq(a: ptr, b: ptr) -> bool {
let i = 0
var i = 0
while true {
let ca = peek8(a, i)
let cb = peek8(b, i)
@ -15,7 +15,7 @@ fn streq(a: ptr, b: ptr) -> bool {
}
fn slen(s: ptr) -> int {
let n = 0
var n = 0
while peek8(s, n) != 0 { n = n + 1 }
return n
}
@ -23,7 +23,7 @@ fn slen(s: ptr) -> int {
# a fresh NUL-terminated copy of src[start .. start+n]
fn substr(src: ptr, start: int, n: int) -> ptr {
let b = mem_alloc(n + 1)
let i = 0
var i = 0
while i < n { poke8(b, i, peek8(src, start + i)); i = i + 1 }
poke8(b, n, 0)
return b
@ -40,18 +40,18 @@ fn char_is_alnum(c: int) -> bool { return char_is_alpha(c) or char_is_digit(c) }
# integer -> fresh decimal string
fn itoa(v: int) -> ptr {
if v == 0 { let z = mem_alloc(2); poke8(z, 0, 48); poke8(z, 1, 0); return z }
let neg = false
let x = v
var neg = false
var x = v
if x < 0 { neg = true; x = 0 - x }
let tmp = mem_alloc(16)
let n = 0
var n = 0
while x > 0 { poke8(tmp, n, 48 + x % 10); x = x / 10; n = n + 1 }
let total = n
var total = n
if neg { total = total + 1 }
let out = mem_alloc(total + 1)
let k = 0
var k = 0
if neg { poke8(out, 0, 45); k = 1 }
let i = 0
var i = 0
while i < n { poke8(out, k + i, peek8(tmp, n - 1 - i)); i = i + 1 }
poke8(out, total, 0)
return out

View file

@ -2,10 +2,10 @@ program T {
fn fib(n: int) -> int { if n < 2 { return n }; return fib(n-1) + fib(n-2) }
entry {
print_int(fib(10)) # 55
let s = 0
var s = 0
for i in 0 .. 10 { if i == 5 { break }; if i % 2 == 0 { continue }; s = s + i }
print_int(s) # 1+3 = 4
let i = 0; let t = 0
var i = 0; var t = 0
while true { i = i + 1; if i > 5 { break }; t = t + i }
print_int(t) # 15
if false and (1/0 == 0) { print_int(999) } else { print_int(1) } # short-circuit: no div by zero