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 ## Statements
`let x = expr` · `x = expr` (`+= -= *= /=`) · `if cond { }` / `if/else` `let x = expr` / `var x = expr` · `x = expr` (`+= -= *= /=`) · `if cond { }` /
(the `else` is optional) · `while cond { }` · `for i in a .. b { }` (numeric range) · `if/else` (the `else` is optional) · `while cond { }` · `for i in a .. b { }`
`for (…) in query […] { }` · `break` · `continue` · `return` · `spawn` · (numeric range) · `for (…) in query […] { }` · `break` · `continue` · `return` ·
`despawn` · `enable` / `disable` (a property `on e`, a model, or a handler) · `spawn` · `despawn` · `enable` / `disable` (a property `on e`, a model, or a
`match` · `machine`. 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 **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 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 { state EnemyTurn {
if k != 0 { if k != 0 {
let done = 0 var done = 0
for (t3) in query [Stats, {Party}] { for (t3) in query [Stats, {Party}] {
if done == 0 { if done == 0 {
if t3.hp > 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 } if t3.guard == 1 { ed = ed / 2; t3.guard = 0 }
t3.hp = t3.hp - ed t3.hp = t3.hp - ed
set_reg(R_EDMG, 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}] { for (t4) in query [Stats, {Party}] {
if t4.hp > 0 { alive = alive + 1 } if t4.hp > 0 { alive = alive + 1 }
} }

View file

@ -5,8 +5,8 @@ handler Draw phase Render {
clear(0x101018) clear(0x101018)
if m == 0 { if m == 0 {
let cx = 0 var cx = 0
let cy = 0 var cy = 0
for (p) in query [Pos, {Player}] { for (p) in query [Pos, {Player}] {
cx = clamp(p.x * 16 - 152, 0, MW * 16 - 320) cx = clamp(p.x * 16 - 152, 0, MW * 16 - 320)
cy = clamp(p.y * 16 - 112, 0, MH * 16 - 240) cy = clamp(p.y * 16 - 112, 0, MH * 16 - 240)
@ -18,7 +18,7 @@ handler Draw phase Render {
let c = tile(tx, ty) let c = tile(tx, ty)
let sx = tx * 16 - cx let sx = tx * 16 - cx
let sy = ty * 16 - cy let sy = ty * 16 - cy
let base = SPR_GRASS var base = SPR_GRASS
match c { match c {
'r' => base = SPR_PATH 'r' => base = SPR_PATH
',', 'S', 'B' => base = SPR_FLOOR ',', 'S', 'B' => base = SPR_FLOOR
@ -57,7 +57,7 @@ handler Draw phase Render {
if m == 1 { if m == 1 {
fill_rect(0, 0, 320, 176, 0x241a2e) fill_rect(0, 0, 320, 176, 0x241a2e)
for (e, a) in query [Stats, Actor, {Enemy}] { for (e, a) in query [Stats, Actor, {Enemy}] {
let bs = 5 var bs = 5
if reg(R_ETYPE) == 2 { bs = 7 } if reg(R_ETYPE) == 2 { bs = 7 }
draw_sprite_scaled(a.kind, 160 - bs * 8, 44, bs) draw_sprite_scaled(a.kind, 160 - bs * 8, 44, bs)
text(100, 20, enemy_name(reg(R_ETYPE)), 0xffffff, 1) 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) fill_rect(100, 30, e.hp * 120 / e.maxhp, 8, 0xe04040)
} }
let ph = reg(R_PHASE) let ph = reg(R_PHASE)
let act = 0 var act = 0
if ph >= 2 { act = 1 } if ph >= 2 { act = 1 }
if ph >= 4 { act = 2 } if ph >= 4 { act = 2 }
fill_rect(0, 176, 320, 64, 0x0c0c16) fill_rect(0, 176, 320, 64, 0x0c0c16)
frame_rect(0, 176, 320, 64, 0x3a3a55) frame_rect(0, 176, 320, 64, 0x3a3a55)
for (st, pt) in query [Stats, Party] { for (st, pt) in query [Stats, Party] {
let hx = 6 + pt.slot * 92 let hx = 6 + pt.slot * 92
let spr = 5 var spr = 5
if pt.slot == 1 { spr = 9 } if pt.slot == 1 { spr = 9 }
if pt.slot == act { frame_rect(hx - 2, 180, 88, 44, 0xffff00) } if pt.slot == act { frame_rect(hx - 2, 180, 88, 44, 0xffff00) }
draw_sprite_scaled(spr, hx, 184, 2) draw_sprite_scaled(spr, hx, 184, 2)

View file

@ -37,13 +37,13 @@ handler Control phase Input {
if reg(R_MODE) == 0 { if reg(R_MODE) == 0 {
for (p) in query [Pos, {Player}] { for (p) in query [Pos, {Player}] {
let k = key() let k = key()
let nx = p.x var nx = p.x
let ny = p.y var ny = p.y
if k == 'w' { ny = p.y - 1 } if k == 'w' { ny = p.y - 1 }
if k == 's' { ny = p.y + 1 } if k == 's' { ny = p.y + 1 }
if k == 'a' { nx = p.x - 1 } if k == 'a' { nx = p.x - 1 }
if k == 'd' { nx = p.x + 1 } if k == 'd' { nx = p.x + 1 }
let moved = 0 var moved = 0
if walkable(nx, ny) { if walkable(nx, ny) {
if nx != p.x or ny != p.y { if nx != p.x or ny != p.y {
p.x = nx p.x = nx
@ -88,7 +88,7 @@ handler Control phase Input {
set_reg(R_ENC, reg(R_ENC) - 1) set_reg(R_ENC, reg(R_ENC) - 1)
if reg(R_ENC) <= 0 { if reg(R_ENC) <= 0 {
set_reg(R_ENC, rng_range(4, 9)) 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) == 1 { et = 1 }
if reg(R_MAP) == 0 { if reg(R_MAP) == 0 {
if rng_chance(40) { et = 1 } 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 # damage after armour, with a crit multiplier applied on an exact roll
@export fn damage(attack: int, armour: int, roll: int) -> int { @export fn damage(attack: int, armour: int, roll: int) -> int {
let raw = attack - armour var raw = attack - armour
if raw < 1 { raw = 1 } if raw < 1 { raw = 1 }
if roll == 20 { raw = raw * CRIT_MULT } if roll == 20 { raw = raw * CRIT_MULT }
return raw return raw
@ -26,8 +26,8 @@ program Combat {
# how many hits to drop a target — the loop is here so the caller cannot # how many hits to drop a target — the loop is here so the caller cannot
# accidentally disagree with `damage` about rounding # accidentally disagree with `damage` about rounding
@export fn hits_to_kill(hp: int, attack: int, armour: int) -> int { @export fn hits_to_kill(hp: int, attack: int, armour: int) -> int {
let left = hp var left = hp
let n = 0 var n = 0
while left > 0 { while left > 0 {
left = left - damage(attack, armour, 0) left = left - damage(attack, armour, 0)
n = n + 1 n = n + 1

View file

@ -114,14 +114,14 @@ program Snake {
clear(0x0d1020) clear(0x0d1020)
for gy in 0 .. GH { for gy in 0 .. GH {
for gx in 0 .. GW { for gx in 0 .. GW {
let col = 0x141a2e var col = 0x141a2e
if (gx + gy) % 2 == 0 { col = 0x10162a } if (gx + gy) % 2 == 0 { col = 0x10162a }
fill_rect(gx * TS, gy * TS, TS, TS, col) 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) fill_rect(reg(R_FX) * TS + 3, reg(R_FY) * TS + 3, TS - 6, TS - 6, 0xe0405a)
for (p, s) in query [Pos, Seg] { for (p, s) in query [Pos, Seg] {
let col = 0x40c060 var col = 0x40c060
if s.order == 0 { col = 0x9dffb0 } if s.order == 0 { col = 0x9dffb0 }
fill_rect(p.x * TS + 1, p.y * TS + 1, TS - 2, TS - 2, col) 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 { handler Run phase Render {
for (e) in query [Player] { disable Shield on self() } # @OnDisable -> 6 for (e) in query [Player] { disable Shield on self() } # @OnDisable -> 6
let n = 0 var n = 0
for (s) in query [Shield] { n += 1 } for (s) in query [Shield] { n += 1 }
print_int(n) # 0 — no live Shield now 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 y0 = max(0, y)
let x1 = min(rt_fbw, x + w) let x1 = min(rt_fbw, x + w)
let y1 = min(rt_fbh, y + h) let y1 = min(rt_fbh, y + h)
let j = y0 var j = y0
while j < y1 { while j < y1 {
let row = j * rt_fbw let row = j * rt_fbw
let i = x0 var i = x0
while i < x1 { while i < x1 {
poke32(rt_fb, row + i, c) poke32(rt_fb, row + i, c)
i = i + 1 i = i + 1
@ -108,7 +108,7 @@ fn rt_present() -> void {
# ---- text ----------------------------------------------------------------- # ---- text -----------------------------------------------------------------
fn rt_glyph(x: int, y: int, ch: int, colour: int, sc: int) -> void { 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 >= 97 {
if c <= 122 { c = c - 32 } 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() let font = rt_font()
for row in 0 .. 7 { for row in 0 .. 7 {
let bits = peek8(font, base + row) - 48 let bits = peek8(font, base + row) - 48
let b = bits var b = bits
for cc in 0 .. 5 { for cc in 0 .. 5 {
let on = b / 16 let on = b / 16
if on == 1 { 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 { fn rt_text(x: int, y: int, s: str, colour: int, sc: int) -> void {
let i = 0 var i = 0
let cx = x var cx = x
let ch = peek8(s, 0) var ch = peek8(s, 0)
while ch != 0 { while ch != 0 {
rt_glyph(cx, y, ch, colour, sc) rt_glyph(cx, y, ch, colour, sc)
cx = cx + 6 * 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) rt_glyph(x, y, 48, colour, sc)
return return
} }
let v = n var v = n
let cx = x var cx = x
if v < 0 { if v < 0 {
rt_glyph(cx, y, 45, colour, sc) rt_glyph(cx, y, 45, colour, sc)
cx = cx + 6 * sc cx = cx + 6 * sc
v = 0 - v v = 0 - v
} }
let digits = 0 var digits = 0
let t = v var t = v
while t > 0 { while t > 0 {
digits = digits + 1 digits = digits + 1
t = t / 10 t = t / 10
} }
let p = digits var p = digits
while p > 0 { while p > 0 {
let div = 1 var div = 1
for k in 1 .. p { for k in 1 .. p {
div = div * 10 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 # xorshift32 (Marsaglia). Runs on the raw 32-bit pattern, so the sign bit is
# masked off only when a caller asks for a number. # masked off only when a caller asks for a number.
fn rt_next_rand() -> int { fn rt_next_rand() -> int {
let x = rt_rng var x = rt_rng
x = bxor(x, shl(x, 13)) x = bxor(x, shl(x, 13))
x = bxor(x, shr(x, 17)) x = bxor(x, shr(x, 17))
x = bxor(x, shl(x, 5)) x = bxor(x, shl(x, 5))
@ -238,9 +238,9 @@ fn rt_running() -> bool {
# ---- writing the frame out ------------------------------------------------ # ---- writing the frame out ------------------------------------------------
fn rt_put_str(buf: ptr, at: int, s: str) -> int { fn rt_put_str(buf: ptr, at: int, s: str) -> int {
let i = 0 var i = 0
let n = at var n = at
let ch = peek8(s, 0) var ch = peek8(s, 0)
while ch != 0 { while ch != 0 {
poke8(buf, n, ch) poke8(buf, n, ch)
n = n + 1 n = n + 1
@ -255,16 +255,16 @@ fn rt_put_int(buf: ptr, at: int, v: int) -> int {
poke8(buf, at, 48) poke8(buf, at, 48)
return at + 1 return at + 1
} }
let digits = 0 var digits = 0
let t = v var t = v
while t > 0 { while t > 0 {
digits = digits + 1 digits = digits + 1
t = t / 10 t = t / 10
} }
let n = at var n = at
let p = digits var p = digits
while p > 0 { while p > 0 {
let div = 1 var div = 1
for k in 1 .. p { for k in 1 .. p {
div = div * 10 div = div * 10
} }
@ -280,7 +280,7 @@ fn rt_dump_ppm(path: str) -> void {
if ptr_is_null(f) { return } if ptr_is_null(f) { return }
let hdr = mem_alloc(64) 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_int(hdr, n, rt_fbw)
n = rt_put_str(hdr, n, " ") n = rt_put_str(hdr, n, " ")
n = rt_put_int(hdr, n, rt_fbh) 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 { fn rt_map_row(y: int, s: str) -> void {
if y < 0 { return } if y < 0 { return }
if y >= 64 { return } if y >= 64 { return }
let x = 0 var x = 0
let ch = peek8(s, 0) var ch = peek8(s, 0)
while ch != 0 { while ch != 0 {
if x >= 96 { return } if x >= 96 { return }
poke8(rt_map, y * 96 + x, ch) 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() var rt_statusbuf: ptr = ptr_null()
fn rt_status(s: str) -> void { fn rt_status(s: str) -> void {
let i = 0 var i = 0
let ch = peek8(s, 0) var ch = peek8(s, 0)
while ch != 0 { while ch != 0 {
if i >= 95 { ch = 0 } if i >= 95 { ch = 0 }
if 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, 0) != 137 { mem_free(d); return false }
if peek8(d, 1) != 80 { mem_free(d); return false } if peek8(d, 1) != 80 { mem_free(d); return false }
let w = 0 var w = 0
let h = 0 var h = 0
let bd = 0 var bd = 0
let ct = 0 var ct = 0
let plte = mem_alloc(768) let plte = mem_alloc(768)
let trns = mem_alloc(256) let trns = mem_alloc(256)
let ntrns = 0 var ntrns = 0
let idat = mem_alloc(size) let idat = mem_alloc(size)
let idlen = 0 var idlen = 0
let i = 8 var i = 8
let done = 0 var done = 0
while done == 0 { while done == 0 {
if i + 8 > size { done = 1 } 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 w <= 0 { mem_free(d); return false }
if h <= 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 == 2 { channels = 3 }
if ct == 6 { channels = 4 } if ct == 6 { channels = 4 }
if ct == 4 { channels = 2 } if ct == 4 { channels = 2 }
let bppbits = bd * channels let bppbits = bd * channels
let fbpp = (bppbits + 7) / 8 var fbpp = (bppbits + 7) / 8
if fbpp < 1 { fbpp = 1 } if fbpp < 1 { fbpp = 1 }
let stride = (w * bppbits + 7) / 8 let stride = (w * bppbits + 7) / 8
let rawlen = h * (stride + 1) let rawlen = h * (stride + 1)
@ -144,13 +144,13 @@ fn rt_decode_png(path: str) -> bool {
let cur = line + 1 let cur = line + 1
let prev = cur - (stride + 1) let prev = cur - (stride + 1)
for x in 0 .. stride { for x in 0 .. stride {
let a = 0 var a = 0
let b = 0 var b = 0
let c = 0 var c = 0
if x >= fbpp { a = peek8(raw, cur + x - fbpp) } if x >= fbpp { a = peek8(raw, cur + x - fbpp) }
if y > 0 { b = peek8(raw, prev + x) } if y > 0 { b = peek8(raw, prev + x) }
if x >= fbpp { if y > 0 { c = peek8(raw, prev + x - fbpp) } } 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 == 1 { v = v + a }
if ft == 2 { v = v + b } if ft == 2 { v = v + b }
if ft == 3 { v = v + (a + b) / 2 } 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 pa = abs(p - a)
let pb = abs(p - b) let pb = abs(p - b)
let pc = abs(p - c) let pc = abs(p - c)
let pick = c var pick = c
if pb <= pc { pick = b } if pb <= pc { pick = b }
if pa <= pb { if pa <= pc { pick = a } } if pa <= pb { if pa <= pc { pick = a } }
v = v + pick v = v + pick
@ -174,10 +174,10 @@ fn rt_decode_png(path: str) -> bool {
for y in 0 .. h { for y in 0 .. h {
let row = y * (stride + 1) + 1 let row = y * (stride + 1) + 1
for x in 0 .. w { for x in 0 .. w {
let R = 0 var R = 0
let G = 0 var G = 0
let B = 0 var B = 0
let A = 255 var A = 255
if ct == 6 { if ct == 6 {
R = peek8(raw, row + x * 4) R = peek8(raw, row + x * 4)
G = peek8(raw, row + x * 4 + 1) G = peek8(raw, row + x * 4 + 1)
@ -206,7 +206,7 @@ fn rt_decode_png(path: str) -> bool {
if ct == 0 { if ct == 0 {
let bp = x * bd let bp = x * bd
let s = band(shr(peek8(raw, row + bp / 8), 8 - bd - bp % 8), maxv) 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 } if bd != 8 { g = s * 255 / maxv }
R = g R = g
G = g G = g
@ -246,9 +246,9 @@ fn rt_blend_px(x: int, y: int, argb: int) -> void {
if y < 0 { return } if y < 0 { return }
if x >= rt_fbw { return } if x >= rt_fbw { return }
if y >= rt_fbh { return } if y >= rt_fbh { return }
let r = band(shr(argb, 16), 255) var r = band(shr(argb, 16), 255)
let g = band(shr(argb, 8), 255) var g = band(shr(argb, 8), 255)
let b = band(argb, 255) var b = band(argb, 255)
if a != 255 { if a != 255 {
let dst = peek32(rt_fb, y * rt_fbw + x) let dst = peek32(rt_fb, y * rt_fbw + x)
let dr = band(shr(dst, 16), 255) 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 let base = id * SPR_SZ * SPR_SZ
for y in 0 .. SPR_SZ { for y in 0 .. SPR_SZ {
for x in 0 .. SPR_SZ { for x in 0 .. SPR_SZ {
let px = 0 var px = 0
if x < png_w { if x < png_w {
if y < png_h { if y < png_h {
let c = peek32(png_px, y * png_w + x) 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 size = rt_file_len
let pal = mem_alloc(128 * 4) let pal = mem_alloc(128 * 4)
mem_set(pal, 0, 128 * 4) mem_set(pal, 0, 128 * 4)
let cur = 0 - 1 var cur = 0 - 1
let row = 0 var row = 0
let i = 0 var i = 0
while i < size { while i < size {
let start = i let start = i
let len = 0 var len = 0
let j = start var j = start
while j < size { while j < size {
let ch = peek8(d, j) let ch = peek8(d, j)
if ch == 10 { j = size } else { len = len + 1; j = j + 1 } 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) let c0 = peek8(d, start)
if c0 == 112 { # 'p' — "pal <char> <rrggbb>" if c0 == 112 { # 'p' — "pal <char> <rrggbb>"
let ch = peek8(d, start + 4) let ch = peek8(d, start + 4)
let v = 0 var v = 0
for k in 0 .. 6 { for k in 0 .. 6 {
let hv = rt_hexval(peek8(d, start + 6 + k)) let hv = rt_hexval(peek8(d, start + 6 + k))
if hv >= 0 { v = v * 16 + hv } 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 { fn z_bits(need: int) -> int {
let val = z_bitbuf var val = z_bitbuf
while z_bitcnt < need { while z_bitcnt < need {
if z_pos >= z_len { if z_pos >= z_len {
z_err = 1 z_err = 1
@ -79,9 +79,9 @@ fn z_table_build(table: ptr, lengths: ptr, n: int) -> void {
} }
fn z_decode(table: ptr) -> int { fn z_decode(table: ptr) -> int {
let code = 0 var code = 0
let first = 0 var first = 0
let index = 0 var index = 0
for len in 1 .. 16 { for len in 1 .. 16 {
code = bor(code, z_bits(1)) code = bor(code, z_bits(1))
let count = peek32(table, len) 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 } if z_pos + 4 > z_len { return -1 }
let n = peek8(z_src, z_pos) + shl(peek8(z_src, z_pos + 1), 8) 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 z_pos = z_pos + 4 # LEN then its one's complement
let w = at var w = at
for i in 0 .. n { for i in 0 .. n {
if z_pos >= z_len { return -1 } if z_pos >= z_len { return -1 }
if w >= cap { 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 { fn z_codes(out: ptr, at: int, cap: int, lit: ptr, dist: ptr) -> int {
let w = at var w = at
let sym = z_decode(lit) var sym = z_decode(lit)
while sym != 256 { while sym != 256 {
if z_err != 0 { return -1 } if z_err != 0 { return -1 }
if sym < 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) let clen = z_table_new(19)
z_table_build(clen, lengths, 19) z_table_build(clen, lengths, 19)
let n = 0 var n = 0
while n < nlen + ndist { while n < nlen + ndist {
let sym = z_decode(clen) let sym = z_decode(clen)
if sym < 0 { return 0 } if sym < 0 { return 0 }
@ -211,8 +211,8 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
n = n + 1 n = n + 1
} }
if sym >= 16 { if sym >= 16 {
let prev = 0 var prev = 0
let rep = 0 var rep = 0
if sym == 16 { if sym == 16 {
if n == 0 { return 0 } if n == 0 { return 0 }
prev = peek32(lengths, n - 1) 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) z_start(src, len)
let lit = z_table_new(288) let lit = z_table_new(288)
let dist = z_table_new(30) let dist = z_table_new(30)
let w = 0 var w = 0
let final = 0 var final = 0
while final == 0 { while final == 0 {
final = z_bits(1) final = z_bits(1)
let btype = z_bits(2) 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) poke32(tt_cfmt, id, 0)
if co < 0 { return } if co < 0 { return }
let n = tt_u16(d, co + 2) let n = tt_u16(d, co + 2)
let best = 0 - 1 var best = 0 - 1
let bestscore = 0 - 1 var bestscore = 0 - 1
for i in 0 .. n { for i in 0 .. n {
let rec = co + 4 + i * 8 let rec = co + 4 + i * 8
let plat = tt_u16(d, rec) let plat = tt_u16(d, rec)
let enc = tt_u16(d, rec + 2) let enc = tt_u16(d, rec + 2)
let off = tt_u32(d, rec + 4) let off = tt_u32(d, rec + 4)
let score = 0 - 1 var score = 0 - 1
if plat == 0 { score = 2 } if plat == 0 { score = 2 }
if plat == 0 { if enc == 4 { score = 4 } } if plat == 0 { if enc == 4 { score = 4 } }
if plat == 0 { if enc == 6 { 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) let d = rt_read_file(path)
if ptr_is_null(d) { return 0 - 1 } if ptr_is_null(d) { return 0 - 1 }
let size = rt_file_len let size = rt_file_len
let base = 0 var base = 0
# a .ttc collection points at its first font # a .ttc collection points at its first font
if size >= 16 { if size >= 16 {
if peek8(d, 0) == 116 { if peek8(d, 1) == 116 { if peek8(d, 2) == 99 { if peek8(d, 3) == 102 { 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 tt_n = tt_n + 1
pokep(tt_data, id, d) pokep(tt_data, id, d)
poke32(tt_size, id, size) poke32(tt_size, id, size)
let upem = tt_u16(d, head + 18) var upem = tt_u16(d, head + 18)
if upem <= 0 { upem = 1000 } if upem <= 0 { upem = 1000 }
poke32(tt_upem, id, upem) poke32(tt_upem, id, upem)
poke32(tt_locfm, id, tt_i16(d, head + 50)) 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 { if nc >= 0 {
let endpts = g + 10 let endpts = g + 10
let npts = 0 var npts = 0
if nc > 0 { npts = tt_u16(d, endpts + (nc - 1) * 2) + 1 } 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 p = p + 2 + tt_u16(d, p) # skip instructions
let flags = mem_alloc(npts + 8) let flags = mem_alloc(npts + 8)
let i = 0 var i = 0
while i < npts { while i < npts {
let fl = peek8(d, p) let fl = peek8(d, p)
p = p + 1 p = p + 1
poke8(flags, i, fl) poke8(flags, i, fl)
i = i + 1 i = i + 1
if band(fl, 8) != 0 { # REPEAT if band(fl, 8) != 0 { # REPEAT
let r = peek8(d, p) var r = peek8(d, p)
p = p + 1 p = p + 1
while r > 0 { while r > 0 {
if i < npts { poke8(flags, i, fl); i = i + 1 } 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 xs = mem_alloc((npts + 1) * 4)
let ys = mem_alloc((npts + 1) * 4) let ys = mem_alloc((npts + 1) * 4)
let xv = 0 var xv = 0
for k in 0 .. npts { for k in 0 .. npts {
let fl = peek8(flags, k) let fl = peek8(flags, k)
if band(fl, 2) != 0 { 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) poke32(xs, k, xv)
} }
let yv = 0 var yv = 0
for k in 0 .. npts { for k in 0 .. npts {
let fl = peek8(flags, k) let fl = peek8(flags, k)
if band(fl, 4) != 0 { 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) poke32(ys, k, yv)
} }
let start = 0 var start = 0
for ci in 0 .. nc { for ci in 0 .. nc {
let last = tt_u16(d, endpts + ci * 2) let last = tt_u16(d, endpts + ci * 2)
for k in start .. last + 1 { 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) mem_free(ys)
} else { } else {
# composite glyph: components, each with its own placement and transform # composite glyph: components, each with its own placement and transform
let p = g + 10 var p = g + 10
let more = 1 var more = 1
while more == 1 { while more == 1 {
let flags = tt_u16(d, p) let flags = tt_u16(d, p)
let cgid = tt_u16(d, p + 2) let cgid = tt_u16(d, p + 2)
p = p + 4 p = p + 4
let arg1 = 0 var arg1 = 0
let arg2 = 0 var arg2 = 0
if band(flags, 1) != 0 { if band(flags, 1) != 0 {
arg1 = tt_i16(d, p) arg1 = tt_i16(d, p)
arg2 = tt_i16(d, p + 2) 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) arg2 = tt_i8(d, p + 1)
p = p + 2 p = p + 2
} }
let ca = 1.0 var ca = 1.0
let cb = 0.0 var cb = 0.0
let cc = 0.0 var cc = 0.0
let ce = 1.0 var ce = 1.0
if band(flags, 8) != 0 { if band(flags, 8) != 0 {
ca = as_fixed(shl(tt_i16(d, p), 2)) # F2Dot14 -> Q16.16 ca = as_fixed(shl(tt_i16(d, p), 2)) # F2Dot14 -> Q16.16
ce = ca 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)) ce = as_fixed(shl(tt_i16(d, p + 6), 2))
p = p + 8 p = p + 8
} }
let odx = dx var odx = dx
let ody = dy var ody = dy
if band(flags, 2) != 0 { if band(flags, 2) != 0 {
odx = a * fx(arg1) + c * fx(arg2) + dx odx = a * fx(arg1) + c * fx(arg2) + dx
ody = b * fx(arg1) + e * fx(arg2) + dy 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 { fn tt_isqrt(v: int) -> int {
if v <= 0 { return 0 } if v <= 0 { return 0 }
let r = 0 var r = 0
let b = 32768 var b = 32768
while b > 0 { while b > 0 {
let t = r + b let t = r + b
if t * t <= v { r = t } 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 { fn ed_quad(x0: fixed, y0: fixed, cx: fixed, cy: fixed, x1: fixed, y1: fixed) -> void {
let dx = flr(x1) - flr(x0) let dx = flr(x1) - flr(x0)
let dy = flr(y1) - flr(y0) 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 < 2 { n = 2 }
if n > 24 { n = 24 } if n > 24 { n = 24 }
let px = x0 var px = x0
let py = y0 var py = y0
for i in 1 .. n + 1 { for i in 1 .. n + 1 {
let t = fx(i) / n let t = fx(i) / n
let u = 1.0 - t 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) 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() } if ol_n == 0 { return ptr_null() }
let minx = 999999.0 var minx = 999999.0
let miny = 999999.0 var miny = 999999.0
let maxx = 0.0 - 999999.0 var maxx = 0.0 - 999999.0
let maxy = 0.0 - 999999.0 var maxy = 0.0 - 999999.0
for i in 0 .. ol_n { for i in 0 .. ol_n {
let X = peekf(ol_x, i) * scale let X = peekf(ol_x, i) * scale
let Y = peekf(ol_y, 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 x0 = flr(minx)
let y0 = flr(miny) let y0 = flr(miny)
let x1 = flr(maxx) + 1 var x1 = flr(maxx) + 1
let y1 = flr(maxy) + 1 var y1 = flr(maxy) + 1
if maxx == fx(flr(maxx)) { x1 = flr(maxx) } if maxx == fx(flr(maxx)) { x1 = flr(maxx) }
if maxy == fx(flr(maxy)) { y1 = flr(maxy) } if maxy == fx(flr(maxy)) { y1 = flr(maxy) }
let W = x1 - x0 let W = x1 - x0
@ -93,17 +93,17 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
gr_oy = y1 gr_oy = y1
# build edges in supersample space (y grows downward) # build edges in supersample space (y grows downward)
let start = 0 var start = 0
for ci in 0 .. ol_ne { for ci in 0 .. ol_ne {
let end = peek32(ol_ends, ci) let end = peek32(ol_ends, ci)
let cnt = end - start let cnt = end - start
if cnt >= 2 { if cnt >= 2 {
let first_on = 0 - 1 var first_on = 0 - 1
for i in 0 .. cnt { for i in 0 .. cnt {
if first_on < 0 { if peek32(ol_on, start + i) == 1 { first_on = i } } if first_on < 0 { if peek32(ol_on, start + i) == 1 { first_on = i } }
} }
let sx = 0.0 var sx = 0.0
let sy = 0.0 var sy = 0.0
if first_on < 0 { if first_on < 0 {
# all off-curve: start at the midpoint of the first and last point # 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 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 sx = (peekf(ol_x, start + first_on) * scale - fx(x0)) * TT_SS
sy = (fx(y1) - peekf(ol_y, start + first_on) * scale) * TT_SS sy = (fx(y1) - peekf(ol_y, start + first_on) * scale) * TT_SS
} }
let curx = sx var curx = sx
let cury = sy var cury = sy
let step = 0 var step = 0
while step < cnt { while step < cnt {
let i = (first_on + 1 + step) % cnt let i = (first_on + 1 + step) % cnt
let ix = (peekf(ol_x, start + i) * scale - fx(x0)) * TT_SS 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 curx = ix
cury = iy cury = iy
} else { } else {
let j = (i + 1) % cnt var j = (i + 1) % cnt
let jx = (peekf(ol_x, start + j) * scale - fx(x0)) * TT_SS 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 jy = (fx(y1) - peekf(ol_y, start + j) * scale) * TT_SS
let ex = jx var ex = jx
let ey = jy var ey = jy
if peek32(ol_on, start + j) == 1 { if peek32(ol_on, start + j) == 1 {
step = step + 1 step = step + 1
} else { } else {
@ -157,11 +157,11 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
for sy in 0 .. SH { for sy in 0 .. SH {
let yc = fx(sy) + 0.5 let yc = fx(sy) + 0.5
let m = 0 var m = 0
for i in 0 .. ed_n { for i in 0 .. ed_n {
let ya = peekf(ed_y0, i) let ya = peekf(ed_y0, i)
let yb = peekf(ed_y1, i) let yb = peekf(ed_y1, i)
let hit = 0 var hit = 0
if ya <= yc { if yb > yc { hit = 1 } } if ya <= yc { if yb > yc { hit = 1 } }
if yb <= yc { if ya > yc { hit = 1 } } if yb <= yc { if ya > yc { hit = 1 } }
if hit == 1 { if hit == 1 {
@ -175,8 +175,8 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
for i in 1 .. m { for i in 1 .. m {
let kx = peekf(sc_x, i) let kx = peekf(sc_x, i)
let kd = peek32(sc_d, i) let kd = peek32(sc_d, i)
let j = i - 1 var j = i - 1
let placed = 0 var placed = 0
while j >= 0 { while j >= 0 {
if peekf(sc_x, j) > kx { if peekf(sc_x, j) > kx {
pokef(sc_x, j + 1, peekf(sc_x, j)) 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) poke32(sc_d, 0, kd)
} }
} }
let wind = 0 var wind = 0
let oy = sy / TT_SS let oy = sy / TT_SS
for i in 0 .. m - 1 { for i in 0 .. m - 1 {
wind = wind + peek32(sc_d, i) wind = wind + peek32(sc_d, i)
if wind != 0 { if wind != 0 {
let xa = peekf(sc_x, i) var xa = peekf(sc_x, i)
let xb = peekf(sc_x, i + 1) var xb = peekf(sc_x, i + 1)
if xa < 0.0 { xa = 0.0 } if xa < 0.0 { xa = 0.0 }
if xb > fx(SW) { xb = fx(SW) } if xb > fx(SW) { xb = fx(SW) }
if xb > xa { if xb > xa {
let ixa = flr(xa) let ixa = flr(xa)
let ixb = flr(xb) + 1 let ixb = flr(xb) + 1
for sx in ixa .. ixb { for sx in ixa .. ixb {
let cxl = xa var cxl = xa
let cxr = xb var cxr = xb
if fx(sx) > cxl { cxl = fx(sx) } if fx(sx) > cxl { cxl = fx(sx) }
if fx(sx + 1) < cxr { cxr = fx(sx + 1) } if fx(sx + 1) < cxr { cxr = fx(sx + 1) }
let cvr = cxr - cxl let cvr = cxr - cxl
if cvr > 0.0 { if cvr > 0.0 {
let oxp = sx / TT_SS let oxp = sx / TT_SS
if oxp >= 0 { if oxp < W { if oy >= 0 { if oy < H { 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 } if v > 255 { v = 255 }
poke8(cover, oy * W + oxp, v) 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 { fn tt_utf8(s: str, at: int) -> int {
let c = peek8(s, at) let c = peek8(s, at)
let extra = 0 - 1 var extra = 0 - 1
if c < 128 { extra = 0 } if c < 128 { extra = 0 }
if shr(c, 5) == 6 { extra = 1 } if shr(c, 5) == 6 { extra = 1 }
if shr(c, 4) == 14 { extra = 2 } if shr(c, 4) == 14 { extra = 2 }
@ -284,7 +284,7 @@ fn tt_utf8(s: str, at: int) -> int {
u8_next = at + 1 u8_next = at + 1
return c return c
} }
let cp = band(c, shr(63, extra)) var cp = band(c, shr(63, extra))
for i in 0 .. extra { for i in 0 .. extra {
let b = peek8(s, at + 1 + i) let b = peek8(s, at + 1 + i)
if band(b, 192) != 128 { 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 } if px <= 0 { return }
let upem = peek32(tt_upem, font) let upem = peek32(tt_upem, font)
let scale = fx(px) / upem 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 lineh = flr(fx(peek32(tt_asc, font) - peek32(tt_desc, font) + peek32(tt_gap, font)) * scale + 0.5)
let penx = x var penx = x
let i = 0 var i = 0
while peek8(s, i) != 0 { while peek8(s, i) != 0 {
let cp = tt_utf8(s, i) let cp = tt_utf8(s, i)
i = u8_next 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 < 0 { return 0 }
if font >= tt_n { return 0 } if font >= tt_n { return 0 }
if px <= 0 { return 0 } if px <= 0 { return 0 }
let w = 0 var w = 0
let best = 0 var best = 0
let i = 0 var i = 0
while peek8(s, i) != 0 { while peek8(s, i) != 0 {
let cp = tt_utf8(s, i) let cp = tt_utf8(s, i)
i = u8_next i = u8_next

View file

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

View file

@ -22,7 +22,7 @@ fn buf_putc(b: Buf, c: int) -> void {
b.len = b.len + 1 b.len = b.len + 1
} }
fn buf_puts(b: Buf, s: ptr) -> void { 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 } 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)) } 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_name: []ptr
var loc_reg: []ptr var loc_reg: []ptr
var loc_ty: []ptr var loc_ty: []ptr
var loc_mut: []int # 1 = mutable (var / param / loop-var), 0 = immutable (let)
var nloc: int = 0 var nloc: int = 0
# loop targets for break/continue (innermost last) # loop targets for break/continue (innermost last)
@ -45,9 +46,9 @@ fn emit_alloca(llt: ptr) -> ptr {
fn sconcat(a: ptr, b: ptr) -> ptr { fn sconcat(a: ptr, b: ptr) -> ptr {
let la = slen(a); let lb = slen(b) let la = slen(a); let lb = slen(b)
let out = mem_alloc(la + lb + 1) 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 } 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 } while j < lb { poke8(out, la + j, peek8(b, j)); j = j + 1 }
poke8(out, la + lb, 0) poke8(out, la + lb, 0)
return out 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 slice_elem(t: ptr) -> ptr { return substr(t, 2, slen(t) - 2) }
fn find_arch(name: ptr) -> Node { 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 } 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() return ptr_null()
} }
fn find_comp(name: ptr) -> Node { 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 } 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() 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 layout_ty(name: ptr) -> ptr { return sconcat("%Cmp_", name) }
fn field_index(s: Node, fname: ptr) -> int { 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 } while i < len(s.kids) { if streq(s.kids[i].s, fname) { return i }; i = i + 1 }
return 0 - 1 return 0 - 1
} }
fn field_type(s: Node, fname: ptr) -> ptr { 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 } while i < len(s.kids) { if streq(s.kids[i].s, fname) { return s.kids[i].ty }; i = i + 1 }
return "int" return "int"
} }
# find a global var/const by name # find a global var/const by name
fn find_global(name: ptr) -> Node { fn find_global(name: ptr) -> Node {
let i = 0 var i = 0
while i < len(prog) { while i < len(prog) {
let d = prog[i] let d = prog[i]
if d.kind == N_VAR and streq(d.s, name) { return d } 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.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. # enum with that variant. Enum names live in `prog` like any other declaration.
fn enum_ordinal(ename: ptr, vname: ptr) -> int { fn enum_ordinal(ename: ptr, vname: ptr) -> int {
let i = 0 var i = 0
while i < len(prog) { while i < len(prog) {
let d = prog[i] let d = prog[i]
if d.kind == N_ENUM and streq(d.s, ename) { 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 } while j < len(d.kids) { if streq(d.kids[j].s, vname) { return j }; j = j + 1 }
} }
i = i + 1 i = i + 1
@ -119,7 +120,7 @@ fn enum_ordinal(ename: ptr, vname: ptr) -> int {
return 0 - 1 return 0 - 1
} }
fn find_fn(name: ptr) -> Node { 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 } 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() 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 { fn computed_expr(prop: ptr, field: ptr) -> Node {
if ptr_is_null(prop) { return ptr_null() } if ptr_is_null(prop) { return ptr_null() }
let key = sconcat(prop, sconcat(".", field)) 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 } while i < len(g_computed) { if streq(g_computed[i].s, key) { return g_computed[i].a }; i = i + 1 }
return ptr_null() 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) let n = node(N_BLOCK); n.s = model; n.a = body; push(g_onspawn, n)
} }
fn onspawn_body(model: ptr) -> Node { 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 } while i < len(g_onspawn) { if streq(g_onspawn[i].s, model) { return g_onspawn[i].a }; i = i + 1 }
return ptr_null() 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) let n = node(N_BLOCK); n.s = model; n.a = body; push(g_ondespawn, n)
} }
fn ondespawn_body(model: ptr) -> Node { 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 } while i < len(g_ondespawn) { if streq(g_ondespawn[i].s, model) { return g_ondespawn[i].a }; i = i + 1 }
return ptr_null() 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) let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_onattach, n)
} }
fn onattach_body(prop: ptr) -> Node { 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 } while i < len(g_onattach) { if streq(g_onattach[i].s, prop) { return g_onattach[i].a }; i = i + 1 }
return ptr_null() 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_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 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 { 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 } while i < len(g_onenable) { if streq(g_onenable[i].s, prop) { return g_onenable[i].a }; i = i + 1 }
return ptr_null() return ptr_null()
} }
fn ondisable_body(prop: ptr) -> Node { 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 } while i < len(g_ondisable) { if streq(g_ondisable[i].s, prop) { return g_ondisable[i].a }; i = i + 1 }
return ptr_null() return ptr_null()
} }
@ -205,13 +206,17 @@ fn is_model(name: ptr) -> bool { return find_arch_id(name) > 0 }
# local variable environment # local variable environment
fn loc_reset() -> void { nloc = 0 } 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 { 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 } 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) } else { push(loc_name, name); push(loc_reg, r); push(loc_ty, ty); push(loc_mut, 1) }
nloc = nloc + 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 { 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 } while i >= 0 { if streq(loc_name[i], name) { return i }; i = i - 1 }
return 0 - 1 return 0 - 1
} }

View file

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

View file

@ -6,12 +6,12 @@
const MAX_ENT: int = 1024 const MAX_ENT: int = 1024
fn has_systems() -> bool { 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 } while i < len(prog) { if prog[i].kind == N_SYS { return true }; i = i + 1 }
return false return false
} }
fn has_models() -> bool { 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 } while i < len(prog) { if prog[i].kind == N_ARCH { return true }; i = i + 1 }
return false 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_kind = internal global [", sconcat(me, " x i32] zeroinitializer\n")))
emith(sconcat("@L_freelist = 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") emith("@L_freen = internal global i32 0\n")
let i = 0 var i = 0
while i < len(prog) { while i < len(prog) {
let c = prog[i] let c = prog[i]
# per-entity storage for a property (its %Cmp_ layout is emitted in the # 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 { fn emit_ecs_allocator() -> void {
let me = itoa(MAX_ENT) let me = itoa(MAX_ENT)
emit("define void @L_reset(i32 %e) {\nentry:\n") emit("define void @L_reset(i32 %e) {\nentry:\n")
let i = 0 var i = 0
while i < len(prog) { while i < len(prog) {
if prog[i].kind == N_COMP { if prog[i].kind == N_COMP {
let hn = sconcat("%h", itoa(i)) 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 b = emit_expr(e.b)
let fx = streq(a.ty, "fixed") or streq(b.ty, "fixed") let fx = streq(a.ty, "fixed") or streq(b.ty, "fixed")
if is_cmp(e.s) { 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) } 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)))))) 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") 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_intrinsic(name) { return emit_intrinsic(name, e) }
if is_intrinsic2(name) { return emit_intrinsic2(name, e) } if is_intrinsic2(name) { return emit_intrinsic2(name, e) }
if is_math_builtin(name) { return emit_math_builtin(name, e) } if is_math_builtin(name) { return emit_math_builtin(name, e) }
let fn2 = find_fn(name) var fn2 = find_fn(name)
let cname = name var cname = name
if ptr_is_null(fn2) { if ptr_is_null(fn2) {
# a builtin like clear()/reg() is satisfied by its rt_ function # a builtin like clear()/reg() is satisfied by its rt_ function
let rtname = sconcat("rt_", name) 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) # evaluate args first (their IR is emitted before the call instruction)
let args = new []ptr let args = new []ptr
let atys = 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 } 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) let rl = llty(fn2.ty)
emit(" ") emit(" ")
let rreg = "0" var rreg = "0"
if not streq(rl, "void") { rreg = nreg(); emit(rreg); emit(" = ") } if not streq(rl, "void") { rreg = nreg(); emit(rreg); emit(" = ") }
emit("call "); emit(rl); emit(" @fn_"); emit(cname); emit("(") emit("call "); emit(rl); emit(" @fn_"); emit(cname); emit("(")
i = 0 i = 0

View file

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

View file

@ -10,7 +10,7 @@ fn emit_str_const(s: ptr) -> ptr {
let n = slen(s) let n = slen(s)
emith(name); emith(" = private unnamed_addr constant [") emith(name); emith(" = private unnamed_addr constant [")
emith(itoa(n + 1)); emith(" x i8] c\"") emith(itoa(n + 1)); emith(" x i8] c\"")
let i = 0 var i = 0
while i < n { while i < n {
let c = peek8(s, i) let c = peek8(s, i)
if c == 34 or c == 92 or c < 32 or c > 126 { 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` # 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 # 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. # are separate (emit_ecs_storage), emitted only for a program that runs the ECS.
let i = 0 var i = 0
while i < len(prog) { while i < len(prog) {
let d = prog[i] let d = prog[i]
if d.kind == N_COMP { if d.kind == N_COMP {
emith(layout_ty(d.s)); emith(" = type { ") emith(layout_ty(d.s)); emith(" = type { ")
if len(d.kids) == 0 { emith("i32") } if len(d.kids) == 0 { emith("i32") }
let f = 0 var f = 0
while f < len(d.kids) { while f < len(d.kids) {
if f > 0 { emith(", ") } if f > 0 { emith(", ") }
emith(llty(d.kids[f].ty)) 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") { 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 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 w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
let fn2 = "@fread" var fn2 = "@fread"
if streq(name, "file_write") { fn2 = "@fwrite" } 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, ")"))))))))) 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") 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, "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") { 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 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,"shr") { opc = "lshr" }
if streq(name,"band") { opc = "and" } if streq(name,"band") { opc = "and" }
if streq(name,"bor") { opc = "or" } 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) } if nmach < len(mach_stk) { mach_stk[nmach] = st } else { push(mach_stk, st) }
nmach = nmach + 1 nmach = nmach + 1
let endl = lbl("smend") let endl = lbl("smend")
let i = 0 var i = 0
while i < len(st.kids) { while i < len(st.kids) {
let state = st.kids[i] let state = st.kids[i]
let v = emit_expr(state.b) let v = emit_expr(state.b)
@ -29,8 +29,8 @@ fn emit_machine(st: Node) -> void {
fn emit_become(st: Node) -> void { fn emit_become(st: Node) -> void {
if nmach == 0 { perr("'become' outside a machine") } if nmach == 0 { perr("'become' outside a machine") }
let m = mach_stk[nmach - 1] let m = mach_stk[nmach - 1]
let target: Node = ptr_null() var target: Node = ptr_null()
let i = 0 var i = 0
while i < len(m.kids) { if streq(m.kids[i].s, st.s) { target = m.kids[i] }; i = i + 1 } 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)) } if ptr_is_null(target) { perr(sconcat("become: no state ", st.s)) }
let regv = emit_expr(m.a) 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") { if streq(name, "min") or streq(name, "max") {
let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1]) 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" } if streq(name, "max") { op = "sgt" }
let c = emit_bind(sconcat("icmp ", sconcat(op, sconcat(" i32 ", sconcat(a.code, sconcat(", ", b.code)))))) 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") 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 lty = layout_ty(name)
let sz = emit_sizeof(lty) let sz = emit_sizeof(lty)
let obj = emit_bind(sconcat("call ptr @malloc(i64 ", sconcat(sz, ")"))) let obj = emit_bind(sconcat("call ptr @malloc(i64 ", sconcat(sz, ")")))
let f = 0 var f = 0
while f < len(s.kids) { while f < len(s.kids) {
let fd = s.kids[f] let fd = s.kids[f]
let addr = nreg() let addr = nreg()
emit(" "); emit(addr); emit(" = getelementptr inbounds "); emit(lty) emit(" "); emit(addr); emit(" = getelementptr inbounds "); emit(lty)
emit(", ptr "); emit(obj); emit(", i32 0, i32 "); emit(itoa(f)); emit("\n") emit(", ptr "); emit(obj); emit(", i32 0, i32 "); emit(itoa(f)); emit("\n")
let lt = llty(fd.ty) let lt = llty(fd.ty)
let v = "0" var v = "0"
if streq(lt, "ptr") { v = "null" } if streq(lt, "ptr") { v = "null" }
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a); v = dv.code } 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") 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. # the body once per match. Mirrors ll_query in compiler/back/ir_ecs.c.
fn find_arch_id(name: ptr) -> int { 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 } 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 return 0
} }
@ -34,11 +34,11 @@ fn emit_query(st: Node) -> void {
let terms = st.c let terms = st.c
# component / archetype filters # component / archetype filters
let t = 0 var t = 0
while t < len(terms.kids) { while t < len(terms.kids) {
let tm = terms.kids[t] let tm = terms.kids[t]
let ak = find_arch_id(tm.s) let ak = find_arch_id(tm.s)
let ok = "0" var ok = "0"
if ak > 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 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)) 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 # bind the requested components to the loop variables, in order
let save = nloc let save = nloc
let vi = 0 var vi = 0
t = 0 t = 0
while t < len(terms.kids) { while t < len(terms.kids) {
let tm = terms.kids[t] 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_alive", "%nalive")
emit_io(fn2, "@L_freelist", "%nalive") emit_io(fn2, "@L_freelist", "%nalive")
emit_io(fn2, "@L_kind", "%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 } 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 i = 0
while i < len(prog) { while i < len(prog) {
if prog[i].kind == N_COMP { 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() 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") 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 # defaults
let f = 0 var f = 0
while f < len(c.kids) { while f < len(c.kids) {
let fd = c.kids[f] let fd = c.kids[f]
let addr = nreg() 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") 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 lt = llty(fd.ty)
let v = "0" var v = "0"
if streq(lt, "ptr") { v = "null" } if streq(lt, "ptr") { v = "null" }
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a); v = dv.code } 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") 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 # per-spawn overrides
if not ptr_is_null(rec) { if not ptr_is_null(rec) {
let j = 0 var j = 0
while j < len(rec.kids) { while j < len(rec.kids) {
let fi = rec.kids[j] let fi = rec.kids[j]
let fidx = field_index(c, fi.s) 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). # @OnSpawn hook body can address them by name (like a query binding for one entity).
fn emit_bind_props(model: Node, e: ptr) -> void { fn emit_bind_props(model: Node, e: ptr) -> void {
let me = itoa(MAX_ENT) let me = itoa(MAX_ENT)
let c = 0 var c = 0
while c < len(model.kids) { while c < len(model.kids) {
let pname = model.kids[c].s let pname = model.kids[c].s
let slot = nreg() 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") 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") emit(" store i32 "); emit(itoa(ak)); emit(", ptr "); emit(kp); emit("\n")
let arch = find_arch(st.s) let arch = find_arch(st.s)
let c = 0 var c = 0
while c < len(arch.kids) { while c < len(arch.kids) {
let cn = arch.kids[c].s let cn = arch.kids[c].s
let rec = ptr_null() var rec = ptr_null()
let i = 0 var i = 0
while i < len(st.kids) { if streq(st.kids[i].s, cn) { rec = st.kids[i].a }; i = i + 1 } 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) emit_init_component(e, cn, rec)
c = c + 1 c = c + 1
@ -90,7 +90,7 @@ fn emit_spawn(st: Node) -> void {
nloc = save nloc = save
} }
} else { } 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 } 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 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 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 kind = emit_bind(sconcat("load i32, ptr ", kp))
let i = 0 var i = 0
while i < len(g_ondespawn) { while i < len(g_ondespawn) {
let mname = g_ondespawn[i].s let mname = g_ondespawn[i].s
let c = emit_bind(sconcat("icmp eq i32 ", sconcat(kind, sconcat(", ", itoa(find_arch_id(mname)))))) 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 # data persists, so re-enabling restores it, and queries already skip a cleared
# flag). A bare `<Model>` / `<Handler>` flips a global enabled flag. # flag). A bare `<Model>` / `<Handler>` flips a global enabled flag.
fn emit_toggle(st: Node) -> void { 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) { if not ptr_is_null(st.a) {
let ev = emit_expr(st.a) let ev = emit_expr(st.a)
let me = itoa(MAX_ENT) 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") 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") 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 st.ival == 1 { hb = onenable_body(st.s) } else { hb = ondisable_body(st.s) }
if not ptr_is_null(hb) { if not ptr_is_null(hb) {
let save = nloc let save = nloc
@ -138,7 +138,7 @@ fn emit_toggle(st: Node) -> void {
nloc = save nloc = save
} }
} else { } 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") 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. # block is skipped until a new basic block opens.
fn emit_block(b: Node) -> void { fn emit_block(b: Node) -> void {
let i = 0 var i = 0
while i < len(b.kids) { while i < len(b.kids) {
if g_term { return } if g_term { return }
emit_stmt(b.kids[i]) 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 { fn emit_assign(st: Node) -> void {
# resolve the target's address and type # resolve the target's address and type
let t = st.a let t = st.a
let addr = "0" var addr = "0"
let ty = "int" var ty = "int"
if t.kind == E_ID { if t.kind == E_ID {
let li = loc_find(t.s) 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 { else {
let g = find_global(t.s) let g = find_global(t.s)
if ptr_is_null(g) { perr(sconcat("assign to unknown ", 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 lt = llty(ty)
let rv = emit_expr(st.b) let rv = emit_expr(st.b)
let v = rv.code var v = rv.code
if not streq(st.s, "=") { if not streq(st.s, "=") {
let cur = emit_bind(sconcat("load ", sconcat(lt, sconcat(", ptr ", addr)))) 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 = "sub" }
if streq(st.s, "*=") { opc = "mul" } if streq(st.s, "*=") { opc = "mul" }
if streq(st.s, "/=") { opc = "sdiv" } if streq(st.s, "/=") { opc = "sdiv" }
@ -116,7 +121,7 @@ fn emit_return(st: Node) -> void {
} }
fn arm_is_default(arm: Node) -> bool { 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 } 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 return false
} }
@ -124,15 +129,15 @@ fn arm_is_default(arm: Node) -> bool {
fn emit_match(st: Node) -> void { fn emit_match(st: Node) -> void {
let sv = emit_expr(st.a) let sv = emit_expr(st.a)
let endl = lbl("mend") let endl = lbl("mend")
let deflt: Node = ptr_null() var deflt: Node = ptr_null()
let i = 0 var i = 0
while i < len(st.kids) { while i < len(st.kids) {
let arm = st.kids[i] let arm = st.kids[i]
if arm_is_default(arm) { deflt = arm } if arm_is_default(arm) { deflt = arm }
else { else {
let acc = "0" var acc = "0"
let first = true var first = true
let p = 0 var p = 0
while p < len(arm.kids) { while p < len(arm.kids) {
let pv = emit_expr(arm.kids[p]) let pv = emit_expr(arm.kids[p])
let c = emit_bind(sconcat("icmp eq i32 ", sconcat(sv.code, sconcat(", ", pv.code)))) 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 { fn emit_stmt(st: Node) -> void {
if st.kind == S_LET { 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 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)) let slot = emit_alloca(llty(ty))
if ptr_is_null(st.a) { if ptr_is_null(st.a) {
let z = "0" var z = "0"
if streq(llty(ty), "ptr") { z = "null" } if streq(llty(ty), "ptr") { z = "null" }
store_at(llty(ty), z, slot) store_at(llty(ty), z, slot)
} else { let v = emit_expr(st.a); store_at(llty(ty), v.code, slot) } } else { let v = emit_expr(st.a); store_at(llty(ty), v.code, slot) }
loc_push(st.s, slot, ty) loc_push(st.s, slot, ty)
loc_set_mut(st.ival)
return return
} }
if st.kind == S_ASSIGN { emit_assign(st); return } if st.kind == S_ASSIGN { emit_assign(st); return }

View file

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

View file

@ -37,8 +37,8 @@ fn is_op1(c: int) -> bool {
fn lex(src: ptr) -> void { fn lex(src: ptr) -> void {
toks = new []Tok toks = new []Tok
let i = 0 var i = 0
let line = 1 var line = 1
let n = slen(src) let n = slen(src)
while i < n { while i < n {
let c = peek8(src, i) let c = peek8(src, i)
@ -52,11 +52,11 @@ fn lex(src: ptr) -> void {
i = i + 1 i = i + 1
let start = i let start = i
let out = mem_alloc(n) let out = mem_alloc(n)
let j = 0 var j = 0
while i < n and peek8(src, i) != 34 { while i < n and peek8(src, i) != 34 {
if peek8(src, i) == 92 { # backslash escape if peek8(src, i) == 92 { # backslash escape
let e = peek8(src, i + 1) let e = peek8(src, i + 1)
let r = e var r = e
if e == 110 { r = 10 } if e == 110 { r = 10 }
if e == 116 { r = 9 } if e == 116 { r = 9 }
if e == 48 { r = 0 } if e == 48 { r = 0 }
@ -70,7 +70,7 @@ fn lex(src: ptr) -> void {
} }
if c == 39 { # 'c' char literal -> int if c == 39 { # 'c' char literal -> int
i = i + 1 i = i + 1
let v = 0 var v = 0
if peek8(src, i) == 92 { if peek8(src, i) == 92 {
let e = peek8(src, i + 1) let e = peek8(src, i + 1)
if e == 110 { v = 10 } if e == 110 { v = 10 }
@ -84,11 +84,11 @@ fn lex(src: ptr) -> void {
} }
if char_is_digit(c) { if char_is_digit(c) {
if c == 48 and peek8(src, i + 1) == 120 { # 0x hex if c == 48 and peek8(src, i + 1) == 120 { # 0x hex
let v = 0 var v = 0
i = i + 2 i = i + 2
while i < n { while i < n {
let h = peek8(src, i) let h = peek8(src, i)
let d = 0 var d = 0
if char_is_digit(h) { d = h - 48 } if char_is_digit(h) { d = h - 48 }
else { if h >= 97 and h <= 102 { d = h - 87 } else { if h >= 97 and h <= 102 { d = h - 87 }
else { if h >= 65 and h <= 70 { d = h - 55 } else { break } } } 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) tok_push(TK_INT, ptr_null(), v, line)
continue 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 } 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 # 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)) { if i < n and peek8(src, i) == 46 and char_is_digit(peek8(src, i + 1)) {
i = 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 } 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 let bits = shl(v, 16) + shl(fnum, 16) / fden
tok_push(TK_FLOAT, ptr_null(), bits, line) 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 '/'. # basename: the part of a path after the last '/'.
fn base_name(path: ptr) -> ptr { fn base_name(path: ptr) -> ptr {
let last = 0 - 1 var last = 0 - 1
let i = 0 var i = 0
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 } while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 }
return substr(path, last + 1, i - (last + 1)) return substr(path, last + 1, i - (last + 1))
} }
@ -52,18 +52,18 @@ fn die(msg: ptr) -> void {
} }
entry { entry {
let path = ptr_null() var path = ptr_null()
let out = ptr_null() var out = ptr_null()
let want = 0 # 0 = auto, 1 = windowed, 2 = headless var want = 0 # 0 = auto, 1 = windowed, 2 = headless
let emit_ir = false # --emit-llvm: stop after writing IR var emit_ir = false # --emit-llvm: stop after writing IR
let fmt = false # --fmt: lex + parse only, then exit (the doc-check gate) var fmt = false # --fmt: lex + parse only, then exit (the doc-check gate)
let save = false # --save-temps: keep the intermediate .ll var save = false # --save-temps: keep the intermediate .ll
let run = false # compile then execute the result var run = false # compile then execute the result
# multi-call: invoked as `ludic` -> run mode by default # multi-call: invoked as `ludic` -> run mode by default
if streq(base_name(os_arg(0)), "ludic") { run = true } if streq(base_name(os_arg(0)), "ludic") { run = true }
let ai = 1 var ai = 1
while ai < os_argc() { while ai < os_argc() {
let a = os_arg(ai) let a = os_arg(ai)
if streq(a, "--windowed") { want = 1 } if streq(a, "--windowed") { want = 1 }
@ -134,7 +134,7 @@ entry {
# platform layer and the Cocoa framework. # platform layer and the Cocoa framework.
# -Wno-override-module: our IR carries an explicit target triple, which clang # -Wno-override-module: our IR carries an explicit target triple, which clang
# would otherwise warn about on every build. # 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 { if g_windowed {
let cocoa = path_join(home, "runtime/native/cocoa.ll") let cocoa = path_join(home, "runtime/native/cocoa.ll")
cmd = sconcat(cmd, sconcat(" ", sconcat(cocoa, " -framework Cocoa -Wl,-rpath,@loader_path"))) 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. # parse.ludic — recursive-descent parser: the token slice -> an AST.
# Reduced grammar: structs, vars, consts, fns, main; no ECS/scene/ui/match. # Core grammar: properties, vars/lets, consts, fns, entry; the ECS/ui/match
# Mirrors compiler/front/parse.c. Uses the global `toks` and a cursor `pi`. # constructs live in parse_game.ludic. Uses the global `toks` and a cursor `pi`.
var pi: int = 0 var pi: int = 0
var prog: []Node # the top-level declarations var prog: []Node # the top-level declarations
@ -37,7 +37,7 @@ fn ptype() -> ptr {
let el = ptype() let el = ptype()
let out = mem_alloc(slen(el) + 3) let out = mem_alloc(slen(el) + 3)
poke8(out, 0, 91); poke8(out, 1, 93) # "[]" 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 } while peek8(el, i) != 0 { poke8(out, 2 + i, peek8(el, i)); i = i + 1 }
poke8(out, 2 + i, 0) poke8(out, 2 + i, 0)
return out return out
@ -71,7 +71,7 @@ fn p_primary() -> Node {
} }
fn p_postfix() -> Node { fn p_postfix() -> Node {
let e = p_primary() var e = p_primary()
while true { while true {
if is_op(".") { pi = pi + 1; let m = node(E_MEMBER); m.a = e; m.s = eat_id(); e = m } 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 } 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 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 { 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()) } 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 return l
} }
fn p_add() -> Node { 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()) } while is_op("+") or is_op("-") { let op = toks[pi].text; pi = pi + 1; l = mkbin(op, l, p_mul()) }
return l return l
} }
fn p_cmp() -> Node { 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("!=") { 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()) let op = toks[pi].text; pi = pi + 1; l = mkbin(op, l, p_add())
} }
return l return l
} }
fn p_and() -> Node { 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()) } while is_id("and") { pi = pi + 1; l = mkbin("and", l, p_cmp()) }
return l return l
} }
fn p_or() -> Node { 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()) } while is_id("or") { pi = pi + 1; l = mkbin("or", l, p_and()) }
return l return l
} }
@ -136,14 +136,9 @@ fn block() -> Node {
# Rule B: statements are separated by a newline or ';' (both lex to TK_NL). # 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 — # 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. # 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 is_op("}") { sep = true }
if not sep { if not sep { perr("expected newline or ';' between statements") }
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")
}
} }
eat_op("}") eat_op("}")
return b return b
@ -152,8 +147,9 @@ fn block() -> Node {
fn stmt() -> Node { fn stmt() -> Node {
let t = toks[pi] let t = toks[pi]
if t.kind == TK_ID { if t.kind == TK_ID {
if streq(t.text, "let") { if streq(t.text, "let") or streq(t.text, "var") {
pi = pi + 1; let n = node(S_LET); n.s = eat_id() 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.ty = ptype() }
if is_op("=") { pi = pi + 1; n.a = expr() } if is_op("=") { pi = pi + 1; n.a = expr() }
return n return n
@ -183,7 +179,7 @@ fn stmt() -> Node {
if streq(t.text, "spawn") { return parse_spawn() } if streq(t.text, "spawn") { return parse_spawn() }
if streq(t.text, "machine") { if streq(t.text, "machine") {
pi = pi + 1; let n = node(S_MACHINE); n.a = expr(); skipnl(); eat_op("{") 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 } while true { skipnl(); if is_op("}") { break }
let stkw = eat_id() # 'state' let stkw = eat_id() # 'state'
let s = node(S_STATE); s.s = eat_id() 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, "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, "despawn") { pi = pi + 1; let n = node(S_DESPAWN); n.a = expr(); return n }
if streq(t.text, "enable") or streq(t.text, "disable") { 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` 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 if is_id("on") { pi = pi + 1; n.a = expr() } # property on an entity
return n return n
@ -218,7 +214,7 @@ fn stmt() -> Node {
} }
let e = expr() let e = expr()
if is_op("=") or is_op("+=") or is_op("-=") or is_op("*=") or is_op("/=") { 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 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 # directory part of a path, including the trailing '/', or "" if none
fn dir_of(path: ptr) -> ptr { fn dir_of(path: ptr) -> ptr {
let last = 0 - 1 var last = 0 - 1
let i = 0 var i = 0
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 } while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 }
if last < 0 { return "" } if last < 0 { return "" }
return substr(path, 0, last + 1) return substr(path, 0, last + 1)
@ -264,7 +260,7 @@ var loaded_paths: []ptr
var cur_dir: ptr var cur_dir: ptr
fn already_loaded(full: ptr) -> bool { 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 } while i < len(loaded_paths) { if streq(loaded_paths[i], full) { return true }; i = i + 1 }
return false return false
} }
@ -273,14 +269,14 @@ fn already_loaded(full: ptr) -> bool {
# Modifiers are `@annotations` in front of the declaration: `@export`, `@edge`, # Modifiers are `@annotations` in front of the declaration: `@export`, `@edge`,
# `@pure`, `@deterministic`, … — one channel, not a zoo of prefix keywords. # `@pure`, `@deterministic`, … — one channel, not a zoo of prefix keywords.
fn parse_one_decl() -> void { fn parse_one_decl() -> void {
let is_export = false var is_export = false
let qspec: Node = ptr_null() var qspec: Node = ptr_null()
let onspawn_model: ptr = ptr_null() var onspawn_model: ptr = ptr_null()
let ondespawn_model: ptr = ptr_null() var ondespawn_model: ptr = ptr_null()
let onattach_prop: ptr = ptr_null() var onattach_prop: ptr = ptr_null()
let onenable_prop: ptr = ptr_null() var onenable_prop: ptr = ptr_null()
let ondisable_prop: ptr = ptr_null() var ondisable_prop: ptr = ptr_null()
let hook_phase: ptr = ptr_null() # @OnStart / @OnQuit override the phase var hook_phase: ptr = ptr_null() # @OnStart / @OnQuit override the phase
while is_op("@") { while is_op("@") {
pi = pi + 1; let a = eat_id() # collect a leading @annotation pi = pi + 1; let a = eat_id() # collect a leading @annotation
if streq(a, "export") { is_export = true } 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, "OnDisable") { eat_op("("); ondisable_prop = eat_id(); eat_op(")") }
else { if streq(a, "OnStart") { hook_phase = "Start" } # boot else { if streq(a, "OnStart") { hook_phase = "Start" } # boot
else { if streq(a, "OnQuit") { hook_phase = "OnQuit" } # shutdown 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 } } } } } } } } } } } } while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } } } } } } } } } } }
skipnl() skipnl()
} }
@ -371,7 +367,7 @@ fn parse_program() -> void {
# @annotations on the program itself (e.g. @Handles(Movement)) — parsed, skipped # @annotations on the program itself (e.g. @Handles(Movement)) — parsed, skipped
while is_op("@") { while is_op("@") {
pi = pi + 1; let a = eat_id() 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 } } } while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } }
skipnl() skipnl()
} }

View file

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

View file

@ -3,7 +3,7 @@
# byte buffers reached with peek8/poke8. # byte buffers reached with peek8/poke8.
fn streq(a: ptr, b: ptr) -> bool { fn streq(a: ptr, b: ptr) -> bool {
let i = 0 var i = 0
while true { while true {
let ca = peek8(a, i) let ca = peek8(a, i)
let cb = peek8(b, i) let cb = peek8(b, i)
@ -15,7 +15,7 @@ fn streq(a: ptr, b: ptr) -> bool {
} }
fn slen(s: ptr) -> int { fn slen(s: ptr) -> int {
let n = 0 var n = 0
while peek8(s, n) != 0 { n = n + 1 } while peek8(s, n) != 0 { n = n + 1 }
return n return n
} }
@ -23,7 +23,7 @@ fn slen(s: ptr) -> int {
# a fresh NUL-terminated copy of src[start .. start+n] # a fresh NUL-terminated copy of src[start .. start+n]
fn substr(src: ptr, start: int, n: int) -> ptr { fn substr(src: ptr, start: int, n: int) -> ptr {
let b = mem_alloc(n + 1) 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 } while i < n { poke8(b, i, peek8(src, start + i)); i = i + 1 }
poke8(b, n, 0) poke8(b, n, 0)
return b 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 # integer -> fresh decimal string
fn itoa(v: int) -> ptr { fn itoa(v: int) -> ptr {
if v == 0 { let z = mem_alloc(2); poke8(z, 0, 48); poke8(z, 1, 0); return z } if v == 0 { let z = mem_alloc(2); poke8(z, 0, 48); poke8(z, 1, 0); return z }
let neg = false var neg = false
let x = v var x = v
if x < 0 { neg = true; x = 0 - x } if x < 0 { neg = true; x = 0 - x }
let tmp = mem_alloc(16) 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 } 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 } if neg { total = total + 1 }
let out = mem_alloc(total + 1) let out = mem_alloc(total + 1)
let k = 0 var k = 0
if neg { poke8(out, 0, 45); k = 1 } 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 } while i < n { poke8(out, k + i, peek8(tmp, n - 1 - i)); i = i + 1 }
poke8(out, total, 0) poke8(out, total, 0)
return out 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) } fn fib(n: int) -> int { if n < 2 { return n }; return fib(n-1) + fib(n-2) }
entry { entry {
print_int(fib(10)) # 55 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 } for i in 0 .. 10 { if i == 5 { break }; if i % 2 == 0 { continue }; s = s + i }
print_int(s) # 1+3 = 4 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 } while true { i = i + 1; if i > 5 { break }; t = t + i }
print_int(t) # 15 print_int(t) # 15
if false and (1/0 == 0) { print_int(999) } else { print_int(1) } # short-circuit: no div by zero if false and (1/0 == 0) { print_int(999) } else { print_int(1) } # short-circuit: no div by zero