Phase 2: enforce statement separators (Rule B)
The parser now requires a newline or ';' between statements (block() in selfhost/parse.ludic); two statements may no longer sit adjacent with only spaces. Also fixed if-without-else swallowing its trailing separator. Migration: tools/ludic-tools/migrate_separators.c inserts ';' at statement boundaries corpus-wide (examples, runtime, 25 self-host fragments, ~1100 boundaries). Verified semantically identical — the migrated compiler compiles itself to IR byte-identical to the pre-migration seed, and every golden game renders identically. Reseeded to the strict compiler; C-free fixpoint holds; test.sh 14/14. Docs: Rule B documented in LANGUAGE.md; BOOTSTRAP.md R1 + stale fences updated; check-docs green across all docs. Fixed a multi-line string literal in emit_expr.ludic (byte-identical \n escape) that the C toolchain lexer mis-lexes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
985f9ad8f2
commit
eac8f8f335
35 changed files with 3861 additions and 3588 deletions
23
BOOTSTRAP.md
23
BOOTSTRAP.md
|
|
@ -853,8 +853,8 @@ current tree (`./test.sh` = 64/64).
|
|||
**Recursion** ✅ → `55`
|
||||
```ludic
|
||||
game P {
|
||||
fn fib(n: int) -> int { if n < 2 { return n } return fib(n-1) + fib(n-2) }
|
||||
system B phase Start { print_int(fib(10)) quit() }
|
||||
fn fib(n: int) -> int { if n < 2 { return n }; return fib(n-1) + fib(n-2) }
|
||||
system B phase Start { print_int(fib(10)); quit() }
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -878,11 +878,11 @@ fn streq(a: ptr, b: ptr) -> bool {
|
|||
fn itoa(v: int, buf: ptr) -> int {
|
||||
let n = 0
|
||||
let x = v
|
||||
if x == 0 { poke8(buf,0,48) return 1 }
|
||||
if x == 0 { poke8(buf,0,48); return 1 }
|
||||
let tmp = mem_alloc(16)
|
||||
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 i = 0
|
||||
while i < n { poke8(buf, i, peek8(tmp, n-1-i)) i = i + 1 }
|
||||
while i < n { poke8(buf, i, peek8(tmp, n-1-i)); i = i + 1 }
|
||||
mem_free(tmp)
|
||||
return n
|
||||
}
|
||||
|
|
@ -905,10 +905,17 @@ file_close(f)
|
|||
Each is a spelling the language accepts; the point is that the *alternative*
|
||||
spelling is equally legal (§5.3).
|
||||
|
||||
**R1 — three statements on one line, no separators** → `2`
|
||||
**R1 — statements now require a separator (Rule B, syntax-redesign Phase 2)** → parse error
|
||||
```ludic
|
||||
# doc-check: skip — intentionally rejected under Rule B: needs a newline or ';'
|
||||
game P { system B phase Start { let x = 1 x = x + 1 print_int(x) quit() } }
|
||||
```
|
||||
Statements no longer sit adjacent with only spaces between them; the compiler
|
||||
reports `expected newline or ';' between statements`. Put each on its own line,
|
||||
or separate them with `;` (both lex to the same separator token):
|
||||
```ludic
|
||||
game P { system B phase Start { let x = 1; x = x + 1; print_int(x); quit() } }
|
||||
```
|
||||
|
||||
**R2 — commas omitted throughout** → `7`
|
||||
```ludic
|
||||
|
|
@ -954,7 +961,7 @@ parenthesisation) while `ludic-fmt` returns the input **unchanged**.
|
|||
**Verified-missing** — each a compile error today:
|
||||
```ludic
|
||||
# doc-check: expect-error — every line here is a compile error by design
|
||||
while i < 10 { i = i + 1 if i == 3 { break } } # unknown identifier 'break'
|
||||
while i < 10 { i = i + 1; if i == 3 { break } } # unknown identifier 'break'
|
||||
struct Node { k: int, a: ptr } # expected declaration (got 'struct')
|
||||
var t: [int; 8] # expected identifier (got '[')
|
||||
var h: fn = a # unknown type 'fn' for var h
|
||||
|
|
@ -963,5 +970,5 @@ print_int(os_argc()) # unknown function 'os_argc'
|
|||
print_err("x") # unknown function 'print_err'
|
||||
let p = mem_realloc(ptr_null(), 10) # unknown function 'mem_realloc'
|
||||
print_str("ab" + "cd") # passes front-end, invalid IR
|
||||
let a = 100000 print_int(a*100000) # 1410065408 — i32 wrap
|
||||
let a = 100000; print_int(a*100000) # 1410065408 — i32 wrap
|
||||
```
|
||||
|
|
|
|||
14
LANGUAGE.md
14
LANGUAGE.md
|
|
@ -130,7 +130,7 @@ system Nav phase Update {
|
|||
if ui_clicked(UI_Quit) { quit() }
|
||||
ui_set_int(UI_HpLabel, hp) # poke dynamic values by id
|
||||
}
|
||||
system Draw phase Render { clear(0x0e0e16) ui_render() present() }
|
||||
system Draw phase Render { clear(0x0e0e16); ui_render(); present() }
|
||||
```
|
||||
|
||||
See `examples/menu.ludic` for a complete title screen.
|
||||
|
|
@ -335,6 +335,18 @@ another `.ludic` file (see `examples/lib/`).
|
|||
`for (…) in query […] { }` · `break` · `continue` · `return` · `spawn` ·
|
||||
`despawn` · `match` · `machine`.
|
||||
|
||||
**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
|
||||
compiler reports `expected newline or ';' between statements`. Write one
|
||||
statement per line, or, to pack several onto a line, separate them with `;`:
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a bare statement block, not a whole declaration
|
||||
let x = 1
|
||||
x = x + 1 # one per line, the usual form
|
||||
let y = 1; y = y + 1 # or `;`-separated on one line
|
||||
```
|
||||
|
||||
`break` and `continue` apply to the innermost enclosing loop, and work in all
|
||||
three loop forms — `while`, the numeric `for`, and the ECS query loop, where
|
||||
`continue` advances to the next matching entity. Using either outside a loop is
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ A plan to make Ludic's syntax internally consistent. It fixes the drift between
|
|||
the spec and the compiler, then unifies the grammar around two rules. Scope:
|
||||
**full redesign (Phases 0–5)**. Named-field direction: **colon everywhere**.
|
||||
|
||||
> Status: **Phase 1 landed** (see below). Phases 2→5 are proposals. The phases
|
||||
> are ordered so the documentation never describes syntax the compiler rejects,
|
||||
> and every phase ends with the compiler still self-hosting to a fixpoint
|
||||
> (`./test.sh`).
|
||||
> Status: **Phases 1 & 2 landed** (see below). Phases 3→5 are proposals. The
|
||||
> phases are ordered so the documentation never describes syntax the compiler
|
||||
> rejects, and every phase ends with the compiler still self-hosting to a
|
||||
> fixpoint (`./test.sh`).
|
||||
>
|
||||
> Coordinated with the toolchain agent (CLI front-end / `ludicc`+`ludic`
|
||||
> binaries) via serialized reseeds of `selfhost/ludicc.seed.ll`; Phase 1 rode in
|
||||
|
|
@ -208,13 +208,36 @@ Made spec ⇄ compiler agree **before** any grammar change. What landed:
|
|||
compile yet — a positive test would fail; the header note + LANGUAGE.md warning
|
||||
cover the drift instead).
|
||||
|
||||
### Phase 2 — Statement separation (Rule B)
|
||||
- Enforce a separator in `block()`/`stmt()` ([parse.ludic:130-199](selfhost/parse.ludic)):
|
||||
after a statement, require `TK_NL` or `}`.
|
||||
- Teach `ludic-fmt` to normalize one-statement-per-line and insert `;` where two
|
||||
share a line. Reformat the whole corpus with it.
|
||||
- Risk: the self-host sources themselves use space-juxtaposed statements heavily
|
||||
— reformat them in the same commit, re-seed, re-verify fixpoint.
|
||||
### Phase 2 — Statement separation (Rule B) ✅ DONE
|
||||
Landed on branch `syntax-redesign-phase2` (baseline committed on `main` first).
|
||||
- ✅ **Parser enforces a separator** — `block()` requires a newline or `;` after
|
||||
each statement, else `expected newline or ';' between statements`
|
||||
([parse.ludic](selfhost/parse.ludic)). Also fixed `if`-without-`else` swallowing
|
||||
its trailing separator (it now peeks for `else` and restores if absent).
|
||||
- ✅ **Interpretation chosen:** *require a separator*, not *reflow to one-per-line*.
|
||||
The migration **inserts `;` at statement boundaries** and leaves lines intact —
|
||||
comment-safe, minimal-diff, and it makes boundaries visible without an
|
||||
opinionated reflow. One-per-line stays the recommended hand-written form.
|
||||
- ✅ **Migration tool** ([tools/ludic-tools/migrate_separators.c](tools/ludic-tools/migrate_separators.c),
|
||||
reuses the toolchain lexer) with a
|
||||
**verification oracle**: a `;` inserted at a real boundary is a semantic no-op,
|
||||
proven by the migrated compiler compiling itself to **IR byte-identical to the
|
||||
seed** and every golden game rendering identically. ~1100 boundaries across the
|
||||
corpus (examples, runtime, and the 25 self-host fragments).
|
||||
- ✅ **Reseeded** to the strict compiler (19557 lines); C-free bootstrap fixpoint
|
||||
holds; `./test.sh` 14/14; all goldens byte-identical; qdecl runs correctly.
|
||||
- ✅ **Docs updated** — Rule B documented in LANGUAGE.md §Statements; BOOTSTRAP.md
|
||||
R1 (which advertised no-separator juxtaposition as legal) and its stale code
|
||||
fences updated; `check-docs` (now a live strict parse gate) green across all docs.
|
||||
|
||||
**Bug found & fixed en route:** a multi-line string literal in
|
||||
[emit_expr.ludic](selfhost/emit_expr.ludic) (`emit(")<newline>")`) lexed fine in
|
||||
the self-host lexer but the **C toolchain lexer** (`ludic_syntax.h`, shared by
|
||||
sepfix, `ludic-fmt`, and the LSP) stops strings at newline — so it mis-lexed and
|
||||
`ludic-fmt` would corrupt such a file. Converted it to the byte-identical `\n`
|
||||
escape. **Open follow-up:** align the C lexer to allow newlines in strings, or
|
||||
forbid literal newlines in string literals language-wide (the two lexers should
|
||||
agree). Flagged to the toolchain owners.
|
||||
|
||||
### Phase 3 — Named-field unification (Rule A)
|
||||
- Migrate spawn/record init (`= {…=…}` → `{…:…}`) and ui props (`key=value` →
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ system Battle phase Update {
|
|||
e.hp = e.hp - d
|
||||
setreg(R_PDMG, d)
|
||||
}
|
||||
1 => { st.guard = 1 setreg(R_PDMG, 0) }
|
||||
1 => { st.guard = 1; setreg(R_PDMG, 0) }
|
||||
2 => {
|
||||
if reg(R_POTION) > 0 {
|
||||
setreg(R_POTION, reg(R_POTION) - 1)
|
||||
|
|
@ -34,7 +34,7 @@ system Battle phase Update {
|
|||
} else { setreg(R_PDMG, 0) }
|
||||
}
|
||||
3 => {
|
||||
if rng_chance(50) { e.hp = 0 setreg(R_MODE, 0) }
|
||||
if rng_chance(50) { e.hp = 0; setreg(R_MODE, 0) }
|
||||
else { setreg(R_PDMG, 0) }
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ system Battle phase Update {
|
|||
if e.hp <= 0 {
|
||||
grant_xp(e.xp)
|
||||
setreg(R_GOLD, reg(R_GOLD) + e.xp)
|
||||
if reg(R_ETYPE) == 2 { setreg(R_BOSS, 1) setreg(R_MODE, 3) setreg(R_ACK, 0) }
|
||||
if reg(R_ETYPE) == 2 { setreg(R_BOSS, 1); setreg(R_MODE, 3); setreg(R_ACK, 0) }
|
||||
else { setreg(R_MODE, 0) }
|
||||
become KnightMenu
|
||||
} else {
|
||||
|
|
@ -85,7 +85,7 @@ system Battle phase Update {
|
|||
setreg(R_PDMG, 16)
|
||||
} else { setreg(R_PDMG, 0) }
|
||||
}
|
||||
2 => { st.guard = 1 setreg(R_PDMG, 0) }
|
||||
2 => { st.guard = 1; setreg(R_PDMG, 0) }
|
||||
}
|
||||
become MageResolve
|
||||
setreg(R_CUR, 0)
|
||||
|
|
@ -100,7 +100,7 @@ system Battle phase Update {
|
|||
if e.hp <= 0 {
|
||||
grant_xp(e.xp)
|
||||
setreg(R_GOLD, reg(R_GOLD) + e.xp)
|
||||
if reg(R_ETYPE) == 2 { setreg(R_BOSS, 1) setreg(R_MODE, 3) setreg(R_ACK, 0) }
|
||||
if reg(R_ETYPE) == 2 { setreg(R_BOSS, 1); setreg(R_MODE, 3); setreg(R_ACK, 0) }
|
||||
else { setreg(R_MODE, 0) }
|
||||
become KnightMenu
|
||||
} else {
|
||||
|
|
@ -116,7 +116,7 @@ system Battle phase Update {
|
|||
if done == 0 {
|
||||
if t3.hp > 0 {
|
||||
let 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
|
||||
setreg(R_EDMG, ed)
|
||||
done = 1
|
||||
|
|
@ -127,7 +127,7 @@ system Battle phase Update {
|
|||
for (t4) in query [Stats, {Party}] {
|
||||
if t4.hp > 0 { alive = alive + 1 }
|
||||
}
|
||||
if alive == 0 { setreg(R_MODE, 2) setreg(R_ACK, 0) }
|
||||
if alive == 0 { setreg(R_MODE, 2); setreg(R_ACK, 0) }
|
||||
else {
|
||||
become KnightMenu
|
||||
setreg(R_CUR, 0)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
game SceneDemo {
|
||||
const R_N: int = 0
|
||||
|
||||
system Boot phase Start { setreg(R_N, 0) print_int(1000) }
|
||||
system Boot phase Start { setreg(R_N, 0); print_int(1000) }
|
||||
|
||||
scene Title start {
|
||||
on enter { print_int(1) }
|
||||
|
|
@ -22,7 +22,7 @@ game SceneDemo {
|
|||
}
|
||||
|
||||
scene Play {
|
||||
on enter { print_int(3) setreg(R_N, 0) }
|
||||
on enter { print_int(3); setreg(R_N, 0) }
|
||||
on exit { print_int(4) }
|
||||
layer World {
|
||||
system Step phase Update {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ game Snake {
|
|||
|
||||
fn reset(x: int) -> int {
|
||||
for (p, s) in query [Pos, Seg] { despawn self() }
|
||||
spawn S0 { Seg = { order = 0 } Pos = { x = 10, y = 7 } }
|
||||
spawn S1 { Seg = { order = 1 } Pos = { x = 9, y = 7 } }
|
||||
spawn S2 { Seg = { order = 2 } Pos = { x = 8, y = 7 } }
|
||||
spawn S0 { Seg = { order = 0 }; Pos = { x = 10, y = 7 } }
|
||||
spawn S1 { Seg = { order = 1 }; Pos = { x = 9, y = 7 } }
|
||||
spawn S2 { Seg = { order = 2 }; Pos = { x = 8, y = 7 } }
|
||||
setreg(R_DIR, 3)
|
||||
setreg(R_LEN, 3)
|
||||
setreg(R_TICK, 0)
|
||||
|
|
@ -62,7 +62,7 @@ game Snake {
|
|||
setreg(R_TICK, 0)
|
||||
let len = reg(R_LEN)
|
||||
for (pt, stg) in query [Pos, Seg] {
|
||||
if stg.order == len - 1 { setreg(R_TX, pt.x) setreg(R_TY, pt.y) }
|
||||
if stg.order == len - 1 { setreg(R_TX, pt.x); setreg(R_TY, pt.y) }
|
||||
}
|
||||
for step in 0 .. len {
|
||||
let i = len - 1 - step
|
||||
|
|
@ -70,7 +70,7 @@ game Snake {
|
|||
for (pa, sa) in query [Pos, Seg] {
|
||||
if sa.order == i {
|
||||
for (pb, sb) in query [Pos, Seg] {
|
||||
if sb.order == i - 1 { pa.x = pb.x pa.y = pb.y }
|
||||
if sb.order == i - 1 { pa.x = pb.x; pa.y = pb.y }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ game Snake {
|
|||
}
|
||||
if reg(R_ATE) == 1 {
|
||||
setreg(R_SCORE, reg(R_SCORE) + 1)
|
||||
spawn Body { Seg = { order = len } Pos = { x = reg(R_TX), y = reg(R_TY) } }
|
||||
spawn Body { Seg = { order = len }; Pos = { x = reg(R_TX), y = reg(R_TY) } }
|
||||
setreg(R_LEN, len + 1)
|
||||
setreg(R_FX, rng_range(0, GW - 1))
|
||||
setreg(R_FY, rng_range(0, GH - 1))
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ fn rt_read_file(path: str) -> ptr {
|
|||
file_seek(f, 0, 2)
|
||||
let n = file_tell(f)
|
||||
file_seek(f, 0, 0)
|
||||
if n <= 0 { file_close(f) return ptr_null() }
|
||||
if n <= 0 { file_close(f); return ptr_null() }
|
||||
let buf = mem_alloc(n + 8)
|
||||
file_read(f, buf, n)
|
||||
file_close(f)
|
||||
|
|
@ -70,9 +70,9 @@ fn rt_decode_png(path: str) -> bool {
|
|||
let d = rt_read_file(path)
|
||||
if ptr_is_null(d) { return false }
|
||||
let size = rt_file_len
|
||||
if size < 8 { 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 size < 8 { 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 }
|
||||
|
||||
let w = 0
|
||||
let h = 0
|
||||
|
|
@ -119,8 +119,8 @@ fn rt_decode_png(path: str) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
if w <= 0 { mem_free(d) return false }
|
||||
if h <= 0 { mem_free(d) return false }
|
||||
if w <= 0 { mem_free(d); return false }
|
||||
if h <= 0 { mem_free(d); return false }
|
||||
|
||||
let channels = 1
|
||||
if ct == 2 { channels = 3 }
|
||||
|
|
@ -133,7 +133,7 @@ fn rt_decode_png(path: str) -> bool {
|
|||
let rawlen = h * (stride + 1)
|
||||
let raw = mem_alloc(rawlen + 8)
|
||||
if z_uncompress(idat, idlen, raw, rawlen) < 0 {
|
||||
mem_free(d) mem_free(raw) mem_free(idat)
|
||||
mem_free(d); mem_free(raw); mem_free(idat)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -378,7 +378,7 @@ fn rt_hexval(c: int) -> int {
|
|||
|
||||
fn rt_load_sprites(path: str) -> void {
|
||||
let d = rt_read_file(path)
|
||||
if ptr_is_null(d) { spr_n = 0 return }
|
||||
if ptr_is_null(d) { spr_n = 0; return }
|
||||
let size = rt_file_len
|
||||
let pal = mem_alloc(128 * 4)
|
||||
mem_set(pal, 0, 128 * 4)
|
||||
|
|
@ -391,7 +391,7 @@ fn rt_load_sprites(path: str) -> void {
|
|||
let j = start
|
||||
while j < size {
|
||||
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 }
|
||||
}
|
||||
let c0 = peek8(d, start)
|
||||
if c0 == 112 { # 'p' — "pal <char> <rrggbb>"
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
|
|||
let r = peek8(d, p)
|
||||
p = p + 1
|
||||
while r > 0 {
|
||||
if i < npts { poke8(flags, i, fl) i = i + 1 }
|
||||
if i < npts { poke8(flags, i, fl); i = i + 1 }
|
||||
r = r - 1
|
||||
}
|
||||
}
|
||||
|
|
@ -338,7 +338,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
|
|||
p = p + 1
|
||||
if band(fl, 16) != 0 { xv = xv + dxv } else { xv = xv - dxv }
|
||||
} else {
|
||||
if band(fl, 16) == 0 { xv = xv + tt_i16(d, p) p = p + 2 }
|
||||
if band(fl, 16) == 0 { xv = xv + tt_i16(d, p); p = p + 2 }
|
||||
}
|
||||
poke32(xs, k, xv)
|
||||
}
|
||||
|
|
@ -350,7 +350,7 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
|
|||
p = p + 1
|
||||
if band(fl, 32) != 0 { yv = yv + dyv } else { yv = yv - dyv }
|
||||
} else {
|
||||
if band(fl, 32) == 0 { yv = yv + tt_i16(d, p) p = p + 2 }
|
||||
if band(fl, 32) == 0 { yv = yv + tt_i16(d, p); p = p + 2 }
|
||||
}
|
||||
poke32(ys, k, yv)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ fn buf_putc(b: Buf, c: int) -> void {
|
|||
}
|
||||
fn buf_puts(b: Buf, s: ptr) -> void {
|
||||
let 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_str(b: Buf) -> ptr { poke8(b.data, b.len, 0) return b.data }
|
||||
fn buf_str(b: Buf) -> ptr { poke8(b.data, b.len, 0); return b.data }
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ fn emit_member_addr(e: Node) -> ptr {
|
|||
if fidx < 0 { perr(sconcat("no such field ", e.s)) }
|
||||
g_addr_ty = field_type(s, e.s)
|
||||
let r = nreg()
|
||||
emit(" ") emit(r) emit(" = getelementptr inbounds ") emit(layout_ty(base.ty))
|
||||
emit(", ptr ") emit(base.code) emit(", i32 0, i32 ") emit(itoa(fidx)) emit("\n")
|
||||
emit(" "); emit(r); emit(" = getelementptr inbounds "); emit(layout_ty(base.ty))
|
||||
emit(", ptr "); emit(base.code); emit(", i32 0, i32 "); emit(itoa(fidx)); emit("\n")
|
||||
return r
|
||||
}
|
||||
|
||||
|
|
@ -26,13 +26,13 @@ fn emit_index_addr(e: Node) -> ptr {
|
|||
g_addr_ty = el
|
||||
# load the data pointer from the slice header (field 0)
|
||||
let dp = nreg()
|
||||
emit(" ") emit(dp) emit(" = getelementptr inbounds %LSlice, ptr ")
|
||||
emit(base.code) emit(", i32 0, i32 0\n")
|
||||
emit(" "); emit(dp); emit(" = getelementptr inbounds %LSlice, ptr ")
|
||||
emit(base.code); emit(", i32 0, i32 0\n")
|
||||
let data = nreg()
|
||||
emit(" ") emit(data) emit(" = load ptr, ptr ") emit(dp) emit("\n")
|
||||
emit(" "); emit(data); emit(" = load ptr, ptr "); emit(dp); emit("\n")
|
||||
let ix = emit_expr(e.b)
|
||||
let r = nreg()
|
||||
emit(" ") emit(r) emit(" = getelementptr inbounds ") emit(llty(el))
|
||||
emit(", ptr ") emit(data) emit(", i32 ") emit(ix.code) emit("\n")
|
||||
emit(" "); emit(r); emit(" = getelementptr inbounds "); emit(llty(el))
|
||||
emit(", ptr "); emit(data); emit(", i32 "); emit(ix.code); emit("\n")
|
||||
return r
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# structs and slices are references, so every non-scalar type lowers to `ptr`.
|
||||
|
||||
struct Val { code: ptr = ptr_null(), ty: ptr = ptr_null() }
|
||||
fn val(code: ptr, ty: ptr) -> Val { let v = new Val v.code = code v.ty = ty return v }
|
||||
fn val(code: ptr, ty: ptr) -> Val { let v = new Val; v.code = code; v.ty = ty; return v }
|
||||
|
||||
var head: Buf # module-level: types, globals, string constants
|
||||
var code: Buf # function bodies
|
||||
|
|
@ -36,24 +36,24 @@ fn emith(s: ptr) -> void { buf_puts(head, s) }
|
|||
# stack slots MUST live in the entry block (an alloca in a loop walks the stack
|
||||
# off its end), so they go into a per-function buffer spliced in at entry.
|
||||
fn emit_alloca(llt: ptr) -> ptr {
|
||||
let r = sconcat("%t", itoa(ll_t)) ll_t = ll_t + 1
|
||||
buf_puts(falloc, " ") buf_puts(falloc, r) buf_puts(falloc, " = alloca ") buf_puts(falloc, llt) buf_puts(falloc, "\n")
|
||||
let r = sconcat("%t", itoa(ll_t)); ll_t = ll_t + 1
|
||||
buf_puts(falloc, " "); buf_puts(falloc, r); buf_puts(falloc, " = alloca "); buf_puts(falloc, llt); buf_puts(falloc, "\n")
|
||||
return r
|
||||
}
|
||||
|
||||
# "%t<n>" fresh register
|
||||
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 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
|
||||
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)
|
||||
return out
|
||||
}
|
||||
fn nreg() -> ptr { let r = sconcat("%t", itoa(ll_t)) ll_t = ll_t + 1 return r }
|
||||
fn lbl(pfx: ptr) -> ptr { let r = sconcat(pfx, itoa(ll_lbl)) ll_lbl = ll_lbl + 1 return r }
|
||||
fn nreg() -> ptr { let r = sconcat("%t", itoa(ll_t)); ll_t = ll_t + 1; return r }
|
||||
fn lbl(pfx: ptr) -> ptr { let r = sconcat(pfx, itoa(ll_lbl)); ll_lbl = ll_lbl + 1; return r }
|
||||
|
||||
# Ludic type -> LLVM type. int/bool are i32; everything else (ptr/str/struct/
|
||||
# slice) is a pointer; void is void.
|
||||
|
|
@ -79,17 +79,17 @@ fn is_struct_ty(t: ptr) -> bool { return not ptr_is_null(find_struct(t)) }
|
|||
|
||||
fn find_arch(name: ptr) -> Node {
|
||||
let 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()
|
||||
}
|
||||
fn find_comp(name: ptr) -> Node {
|
||||
let 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()
|
||||
}
|
||||
# a struct or a component — both have %Str_/%Cmp_ layouts with named fields
|
||||
fn layout_node(name: ptr) -> Node {
|
||||
let s = find_struct(name) if not ptr_is_null(s) { return s }
|
||||
let s = find_struct(name); if not ptr_is_null(s) { return s }
|
||||
return find_comp(name)
|
||||
}
|
||||
fn layout_ty(name: ptr) -> ptr {
|
||||
|
|
@ -99,12 +99,12 @@ fn layout_ty(name: ptr) -> ptr {
|
|||
|
||||
fn field_index(s: Node, fname: ptr) -> int {
|
||||
let 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
|
||||
}
|
||||
fn field_type(s: Node, fname: ptr) -> ptr {
|
||||
let 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"
|
||||
}
|
||||
|
||||
|
|
@ -121,19 +121,19 @@ fn find_global(name: ptr) -> Node {
|
|||
}
|
||||
fn find_fn(name: ptr) -> Node {
|
||||
let 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()
|
||||
}
|
||||
|
||||
# local variable environment
|
||||
fn loc_reset() -> void { nloc = 0 }
|
||||
fn loc_push(name: ptr, r: ptr, ty: ptr) -> void {
|
||||
if nloc < len(loc_name) { loc_name[nloc] = name loc_reg[nloc] = r loc_ty[nloc] = ty }
|
||||
else { push(loc_name, name) push(loc_reg, r) push(loc_ty, ty) }
|
||||
if nloc < len(loc_name) { loc_name[nloc] = name; loc_reg[nloc] = r; loc_ty[nloc] = ty }
|
||||
else { push(loc_name, name); push(loc_reg, r); push(loc_ty, ty) }
|
||||
nloc = nloc + 1
|
||||
}
|
||||
fn loc_find(name: ptr) -> int {
|
||||
let 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,26 +6,26 @@ fn emit_params_sig(d: Node) -> void {
|
|||
let i = 0
|
||||
while i < len(d.kids) {
|
||||
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)
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_fn(d: Node) -> void {
|
||||
ll_t = 0 ll_lbl = 0 g_term = false loc_reset() nloop = 0
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
|
||||
ret_ty = d.ty
|
||||
let fbody = buf_new()
|
||||
falloc = buf_new()
|
||||
let saved = code
|
||||
code = fbody
|
||||
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
|
||||
let i = 0
|
||||
while i < len(d.kids) {
|
||||
let p = d.kids[i]
|
||||
let slot = emit_alloca(llty(p.ty))
|
||||
emit(" store ") emit(llty(p.ty)) emit(" %arg_") emit(p.s) emit(", ptr ") emit(slot) emit("\n")
|
||||
emit(" store "); emit(llty(p.ty)); emit(" %arg_"); emit(p.s); emit(", ptr "); emit(slot); emit("\n")
|
||||
loc_push(p.s, slot, p.ty)
|
||||
i = i + 1
|
||||
}
|
||||
|
|
@ -33,16 +33,16 @@ fn emit_fn(d: Node) -> void {
|
|||
if not g_term { emit(" br label %ret\n") }
|
||||
emit("ret:\n")
|
||||
if streq(rl, "void") { emit(" ret void\n") }
|
||||
else { let r = emit_bind(sconcat("load ", sconcat(rl, ", ptr %retval"))) emit(" ret ") emit(rl) emit(" ") emit(r) emit("\n") }
|
||||
else { let r = emit_bind(sconcat("load ", sconcat(rl, ", ptr %retval"))); emit(" ret "); emit(rl); emit(" "); emit(r); emit("\n") }
|
||||
code = saved
|
||||
emit("define ") emit(rl) emit(" @fn_") emit(d.s) emit("(") emit_params_sig(d) emit(") {\nentry:\n")
|
||||
emit("define "); emit(rl); emit(" @fn_"); emit(d.s); emit("("); emit_params_sig(d); emit(") {\nentry:\n")
|
||||
emit(buf_str(falloc))
|
||||
emit(buf_str(fbody))
|
||||
emit("}\n\n")
|
||||
}
|
||||
|
||||
fn emit_main(d: Node) -> void {
|
||||
ll_t = 0 ll_lbl = 0 g_term = false loc_reset() nloop = 0
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
|
||||
ret_ty = "int"
|
||||
let fbody = buf_new()
|
||||
falloc = buf_new()
|
||||
|
|
@ -56,7 +56,7 @@ fn emit_main(d: Node) -> void {
|
|||
if not g_term { emit(" br label %ret\n") }
|
||||
emit("ret:\n")
|
||||
let r = emit_bind("load i32, ptr %retval")
|
||||
emit(" ret i32 ") emit(r) emit("\n")
|
||||
emit(" ret i32 "); emit(r); emit("\n")
|
||||
code = saved
|
||||
emit("define i32 @main(i32 %argc, ptr %argv) {\nentry:\n")
|
||||
emit(buf_str(falloc))
|
||||
|
|
@ -67,20 +67,20 @@ fn emit_main(d: Node) -> void {
|
|||
fn emit_program() -> void {
|
||||
head = buf_new()
|
||||
code = buf_new()
|
||||
loc_name = new []ptr loc_reg = new []ptr loc_ty = new []ptr
|
||||
brk_lbl = new []ptr cnt_lbl = new []ptr
|
||||
loc_name = new []ptr; loc_reg = new []ptr; loc_ty = new []ptr
|
||||
brk_lbl = new []ptr; cnt_lbl = new []ptr
|
||||
self_stk = new []ptr
|
||||
mach_stk = new []Node
|
||||
emit_header()
|
||||
if has_ecs() { emit_ecs_storage() }
|
||||
let i = 0
|
||||
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() }
|
||||
while i < len(prog) { if prog[i].kind == N_FN { emit_fn(prog[i]) }; i = i + 1 }
|
||||
if has_ecs() { emit_ecs_allocator(); emit_snapshot() }
|
||||
if has_ui() { emit_ui_build() }
|
||||
if has_systems() { emit_game_main() }
|
||||
else {
|
||||
i = 0
|
||||
while i < len(prog) { if prog[i].kind == N_MAIN { emit_main(prog[i]) } i = i + 1 }
|
||||
while i < len(prog) { if prog[i].kind == N_MAIN { emit_main(prog[i]) }; i = i + 1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ const MAX_ENT: int = 1024
|
|||
|
||||
fn has_ecs() -> bool {
|
||||
let i = 0
|
||||
while i < len(prog) { let k = prog[i].kind if k == N_COMP or k == N_SYS { return true } i = i + 1 }
|
||||
while i < len(prog) { let k = prog[i].kind; if k == N_COMP or k == N_SYS { return true }; i = i + 1 }
|
||||
return false
|
||||
}
|
||||
fn has_systems() -> bool {
|
||||
let 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
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ fn emit_ecs_storage() -> void {
|
|||
emith(sconcat("%Cmp_", sconcat(c.s, " = type { ")))
|
||||
if len(c.kids) == 0 { emith("i32") }
|
||||
let f = 0
|
||||
while f < len(c.kids) { if f > 0 { emith(", ") } emith(llty(c.kids[f].ty)) f = f + 1 }
|
||||
while f < len(c.kids) { if f > 0 { emith(", ") }; emith(llty(c.kids[f].ty)); f = f + 1 }
|
||||
emith(" }\n")
|
||||
emith(sconcat("@S_", sconcat(c.s, sconcat(" = internal global [", sconcat(me, sconcat(" x %Cmp_", sconcat(c.s, "] zeroinitializer\n")))))))
|
||||
emith(sconcat("@H_", sconcat(c.s, sconcat(" = internal global [", sconcat(me, " x i8] zeroinitializer\n")))))
|
||||
|
|
@ -49,12 +49,12 @@ fn emit_ecs_allocator() -> void {
|
|||
while i < len(prog) {
|
||||
if prog[i].kind == N_COMP {
|
||||
let hn = sconcat("%h", itoa(i))
|
||||
emit(" ") emit(hn) emit(" = getelementptr inbounds [") emit(me) emit(" x i8], ptr @H_") emit(prog[i].s) emit(", i32 0, i32 %e\n")
|
||||
emit(" store i8 0, ptr ") emit(hn) emit("\n")
|
||||
emit(" "); emit(hn); emit(" = getelementptr inbounds ["); emit(me); emit(" x i8], ptr @H_"); emit(prog[i].s); emit(", i32 0, i32 %e\n")
|
||||
emit(" store i8 0, ptr "); emit(hn); emit("\n")
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
emit(" %k = getelementptr inbounds [") emit(me) emit(" x i32], ptr @L_kind, i32 0, i32 %e\n")
|
||||
emit(" %k = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_kind, i32 0, i32 %e\n")
|
||||
emit(" store i32 0, ptr %k\n ret void\n}\n\n")
|
||||
|
||||
emit("define i32 @L_alloc() {\nentry:\n")
|
||||
|
|
@ -64,7 +64,7 @@ fn emit_ecs_allocator() -> void {
|
|||
emit("reuse:\n")
|
||||
emit(" %fn1 = sub i32 %fn, 1\n")
|
||||
emit(" store i32 %fn1, ptr @L_freen\n")
|
||||
emit(" %fp = getelementptr inbounds [") emit(me) emit(" x i32], ptr @L_freelist, i32 0, i32 %fn1\n")
|
||||
emit(" %fp = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_freelist, i32 0, i32 %fn1\n")
|
||||
emit(" %re = load i32, ptr %fp\n")
|
||||
emit(" br label %done\n")
|
||||
emit("fresh:\n")
|
||||
|
|
@ -75,16 +75,16 @@ fn emit_ecs_allocator() -> void {
|
|||
emit("done:\n")
|
||||
emit(" %e = phi i32 [ %re, %reuse ], [ %ec, %fresh ]\n")
|
||||
emit(" call void @L_reset(i32 %e)\n")
|
||||
emit(" %ap = getelementptr inbounds [") emit(me) emit(" x i32], ptr @L_alive, i32 0, i32 %e\n")
|
||||
emit(" %ap = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_alive, i32 0, i32 %e\n")
|
||||
emit(" store i32 1, ptr %ap\n")
|
||||
emit(" ret i32 %e\n}\n\n")
|
||||
|
||||
emit("define void @L_free_entity(i32 %e) {\nentry:\n")
|
||||
emit(" %ap = getelementptr inbounds [") emit(me) emit(" x i32], ptr @L_alive, i32 0, i32 %e\n")
|
||||
emit(" %ap = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_alive, i32 0, i32 %e\n")
|
||||
emit(" store i32 0, ptr %ap\n")
|
||||
emit(" call void @L_reset(i32 %e)\n")
|
||||
emit(" %fn = load i32, ptr @L_freen\n")
|
||||
emit(" %fp = getelementptr inbounds [") emit(me) emit(" x i32], ptr @L_freelist, i32 0, i32 %fn\n")
|
||||
emit(" %fp = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_freelist, i32 0, i32 %fn\n")
|
||||
emit(" store i32 %e, ptr %fp\n")
|
||||
emit(" %fn1 = add i32 %fn, 1\n")
|
||||
emit(" store i32 %fn1, ptr @L_freen\n")
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@ fn emit_logic(e: Node) -> Val {
|
|||
let la = emit_expr(e.a)
|
||||
let lc = emit_bind(sconcat("icmp ne i32 ", sconcat(la.code, ", 0")))
|
||||
let lz = emit_bind(sconcat("zext i1 ", sconcat(lc, " to i32")))
|
||||
emit(" store i32 ") emit(lz) emit(", ptr ") emit(slot) emit("\n")
|
||||
let ev = lbl("sc") let done = lbl("scend")
|
||||
if streq(e.s, "and") { emit(" br i1 ") emit(lc) emit(", label %") emit(ev) emit(", label %") emit(done) emit("\n") }
|
||||
else { emit(" br i1 ") emit(lc) emit(", label %") emit(done) emit(", label %") emit(ev) emit("\n") }
|
||||
emit(ev) emit(":\n")
|
||||
emit(" store i32 "); emit(lz); emit(", ptr "); emit(slot); emit("\n")
|
||||
let ev = lbl("sc"); let done = lbl("scend")
|
||||
if streq(e.s, "and") { emit(" br i1 "); emit(lc); emit(", label %"); emit(ev); emit(", label %"); emit(done); emit("\n") }
|
||||
else { emit(" br i1 "); emit(lc); emit(", label %"); emit(done); emit(", label %"); emit(ev); emit("\n") }
|
||||
emit(ev); emit(":\n")
|
||||
let rb = emit_expr(e.b)
|
||||
let rc = emit_bind(sconcat("icmp ne i32 ", sconcat(rb.code, ", 0")))
|
||||
let rz = emit_bind(sconcat("zext i1 ", sconcat(rc, " to i32")))
|
||||
emit(" store i32 ") emit(rz) emit(", ptr ") emit(slot) emit("\n")
|
||||
emit(" br label %") emit(done) emit("\n")
|
||||
emit(done) emit(":\n")
|
||||
emit(" store i32 "); emit(rz); emit(", ptr "); emit(slot); emit("\n")
|
||||
emit(" br label %"); emit(done); emit("\n")
|
||||
emit(done); emit(":\n")
|
||||
return val(emit_bind(sconcat("load i32, ptr ", slot)), "bool")
|
||||
}
|
||||
|
||||
|
|
@ -57,13 +57,13 @@ fn emit_bin(e: Node) -> Val {
|
|||
let b = emit_expr(e.b)
|
||||
let fx = streq(a.ty, "fixed") or streq(b.ty, "fixed")
|
||||
if is_cmp(e.s) {
|
||||
let ac = a.code let bc = b.code
|
||||
if fx { ac = to_fixed(a) bc = to_fixed(b) }
|
||||
let ac = a.code; let bc = b.code
|
||||
if fx { ac = to_fixed(a); bc = to_fixed(b) }
|
||||
let c = emit_bind(sconcat("icmp ", sconcat(cmp_code(e.s), sconcat(" i32 ", sconcat(ac, sconcat(", ", bc))))))
|
||||
return val(emit_bind(sconcat("zext i1 ", sconcat(c, " to i32"))), "bool")
|
||||
}
|
||||
if fx {
|
||||
let af = to_fixed(a) let bf = to_fixed(b)
|
||||
let af = to_fixed(a); let bf = to_fixed(b)
|
||||
if streq(e.s, "*") {
|
||||
let a64 = emit_bind(sconcat("sext i32 ", sconcat(af, " to i64")))
|
||||
let b64 = emit_bind(sconcat("sext i32 ", sconcat(bf, " to i64")))
|
||||
|
|
@ -87,16 +87,16 @@ fn emit_bin(e: Node) -> Val {
|
|||
|
||||
fn emit_call(e: Node) -> Val {
|
||||
let name = e.a.s
|
||||
if streq(name, "self") { if nself == 0 { return val("0", "entity") } return val(emit_bind(sconcat("load i32, ptr ", self_stk[nself - 1])), "entity") }
|
||||
if streq(name, "self") { if nself == 0 { return val("0", "entity") }; return val(emit_bind(sconcat("load i32, ptr ", self_stk[nself - 1])), "entity") }
|
||||
if streq(name, "key") { return val(emit_bind("load i32, ptr @L_key"), "int") }
|
||||
if streq(name, "save") { emit(" call void @L_save()\n") return val("0", "void") }
|
||||
if streq(name, "ui_build") { emit(" call void @ui_build()\n") return val("0", "void") }
|
||||
if streq(name, "save") { emit(" call void @L_save()\n"); return val("0", "void") }
|
||||
if streq(name, "ui_build") { emit(" call void @ui_build()\n"); return val("0", "void") }
|
||||
if streq(name, "load") { return val(emit_bind("call i32 @L_load()"), "bool") }
|
||||
if streq(name, "quit") { emit(" store i32 0, ptr @L_running\n") return val("0", "void") }
|
||||
if streq(name, "quit") { emit(" store i32 0, ptr @L_running\n"); return val("0", "void") }
|
||||
if streq(name, "len") { return emit_len(e) }
|
||||
if streq(name, "push") { return emit_push(e) }
|
||||
if streq(name, "fx") { let a = emit_expr(e.kids[0]) return val(emit_bind(sconcat("shl i32 ", sconcat(a.code, ", 16"))), "fixed") }
|
||||
if streq(name, "flr") { let a = emit_expr(e.kids[0]) return val(emit_bind(sconcat("ashr i32 ", sconcat(a.code, ", 16"))), "int") }
|
||||
if streq(name, "fx") { let a = emit_expr(e.kids[0]); return val(emit_bind(sconcat("shl i32 ", sconcat(a.code, ", 16"))), "fixed") }
|
||||
if streq(name, "flr") { let a = emit_expr(e.kids[0]); return val(emit_bind(sconcat("ashr i32 ", sconcat(a.code, ", 16"))), "int") }
|
||||
if is_intrinsic(name) { return emit_intrinsic(name, e) }
|
||||
if is_intrinsic2(name) { return emit_intrinsic2(name, e) }
|
||||
if is_math_builtin(name) { return emit_math_builtin(name, e) }
|
||||
|
|
@ -113,20 +113,19 @@ fn emit_call(e: Node) -> Val {
|
|||
let args = new []ptr
|
||||
let atys = new []ptr
|
||||
let 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)
|
||||
emit(" ")
|
||||
let rreg = "0"
|
||||
if not streq(rl, "void") { rreg = nreg() emit(rreg) emit(" = ") }
|
||||
emit("call ") emit(rl) emit(" @fn_") emit(cname) emit("(")
|
||||
if not streq(rl, "void") { rreg = nreg(); emit(rreg); emit(" = ") }
|
||||
emit("call "); emit(rl); emit(" @fn_"); emit(cname); emit("(")
|
||||
i = 0
|
||||
while i < len(args) {
|
||||
if i > 0 { emit(", ") }
|
||||
emit(llty(atys[i])) emit(" ") emit(args[i])
|
||||
emit(llty(atys[i])); emit(" "); emit(args[i])
|
||||
i = i + 1
|
||||
}
|
||||
emit(")
|
||||
")
|
||||
emit(")\n")
|
||||
return val(rreg, fn2.ty)
|
||||
}
|
||||
|
||||
|
|
@ -153,8 +152,8 @@ fn emit_expr(e: Node) -> Val {
|
|||
if is_ui_ident(e.s) { return val(itoa(ui_index_of(e.s)), "int") }
|
||||
perr(sconcat("unknown identifier ", e.s))
|
||||
}
|
||||
if e.kind == E_MEMBER { let a = emit_member_addr(e) return emit_load_at(a, g_addr_ty) }
|
||||
if e.kind == E_INDEX { let a = emit_index_addr(e) return emit_load_at(a, g_addr_ty) }
|
||||
if e.kind == E_MEMBER { let a = emit_member_addr(e); return emit_load_at(a, g_addr_ty) }
|
||||
if e.kind == E_INDEX { let a = emit_index_addr(e); return emit_load_at(a, g_addr_ty) }
|
||||
if e.kind == E_CALL { return emit_call(e) }
|
||||
if e.kind == E_BIN { return emit_bin(e) }
|
||||
if e.kind == E_UN {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# called only when the runtime defines them.
|
||||
|
||||
fn emit_system_fn(sys: Node) -> void {
|
||||
ll_t = 0 ll_lbl = 0 g_term = false loc_reset() nloop = 0 nself = 0
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
|
||||
ret_ty = "void"
|
||||
let fbody = buf_new()
|
||||
falloc = buf_new()
|
||||
|
|
@ -14,7 +14,7 @@ fn emit_system_fn(sys: Node) -> void {
|
|||
if not g_term { emit(" br label %ret\n") }
|
||||
emit("ret:\n ret void\n")
|
||||
code = saved
|
||||
emit("define void @sys_") emit(sys.s) emit("() {\nentry:\n")
|
||||
emit("define void @sys_"); emit(sys.s); emit("() {\nentry:\n")
|
||||
emit(buf_str(falloc))
|
||||
emit(buf_str(fbody))
|
||||
emit("}\n\n")
|
||||
|
|
@ -24,7 +24,7 @@ fn emit_calls_for_phase(phase: ptr) -> void {
|
|||
let i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if d.kind == N_SYS and streq(d.ty, phase) { emit(" call void @sys_") emit(d.s) emit("()\n") }
|
||||
if d.kind == N_SYS and streq(d.ty, phase) { emit(" call void @sys_"); emit(d.s); emit("()\n") }
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ fn emit_calls_for_phase(phase: ptr) -> void {
|
|||
fn emit_game_main() -> void {
|
||||
# every system becomes a function first
|
||||
let 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("define i32 @main(i32 %argc, ptr %argv) {\nentry:\n")
|
||||
emit(" store i32 %argc, ptr @L_argc\n")
|
||||
|
|
@ -47,14 +47,14 @@ fn emit_game_main() -> void {
|
|||
let pr = emit_bind("call i32 @fn_rt_running()")
|
||||
let pc = emit_bind(sconcat("icmp ne i32 ", sconcat(pr, ", 0")))
|
||||
let go = emit_bind(sconcat("and i1 ", sconcat(rc, sconcat(", ", pc))))
|
||||
emit(" br i1 ") emit(go) emit(", label %body, label %done\n")
|
||||
emit(" br i1 "); emit(go); emit(", label %body, label %done\n")
|
||||
} else {
|
||||
emit(" br i1 ") emit(rc) emit(", label %body, label %done\n")
|
||||
emit(" br i1 "); emit(rc); emit(", label %body, label %done\n")
|
||||
}
|
||||
emit("body:\n")
|
||||
if not ptr_is_null(find_fn("rt_poll")) {
|
||||
let k = emit_bind("call i32 @fn_rt_poll()")
|
||||
emit(" store i32 ") emit(k) emit(", ptr @L_key\n")
|
||||
emit(" store i32 "); emit(k); emit(", ptr @L_key\n")
|
||||
}
|
||||
emit_calls_for_phase("Input")
|
||||
emit_calls_for_phase("FixedUpdate")
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# emit_head.ludic — string constants and the module header (libc declarations,
|
||||
# the slice header type, struct layouts, globals, argv, format strings).
|
||||
|
||||
fn hexdig(n: int) -> int { if n < 10 { return 48 + n } return 55 + n } # 0-9 A-F
|
||||
fn hexdig(n: int) -> int { if n < 10 { return 48 + n }; return 55 + n } # 0-9 A-F
|
||||
|
||||
# emit `@.strN = ... c"escaped\00"` and return its name; % and non-print -> \XX
|
||||
fn emit_str_const(s: ptr) -> ptr {
|
||||
let name = sconcat("@.str", itoa(ll_str))
|
||||
ll_str = ll_str + 1
|
||||
let n = slen(s)
|
||||
emith(name) emith(" = private unnamed_addr constant [")
|
||||
emith(itoa(n + 1)) emith(" x i8] c\"")
|
||||
emith(name); emith(" = private unnamed_addr constant [")
|
||||
emith(itoa(n + 1)); emith(" x i8] c\"")
|
||||
let i = 0
|
||||
while i < n {
|
||||
let c = peek8(s, i)
|
||||
|
|
@ -27,14 +27,14 @@ fn emit_str_const(s: ptr) -> ptr {
|
|||
# a fresh SSA register bound to a `getelementptr`, returned as its name
|
||||
fn emit_gep_i8(base: ptr, idx: ptr) -> ptr {
|
||||
let r = nreg()
|
||||
emit(" ") emit(r) emit(" = getelementptr inbounds i8, ptr ")
|
||||
emit(base) emit(", i32 ") emit(idx) emit("\n")
|
||||
emit(" "); emit(r); emit(" = getelementptr inbounds i8, ptr ")
|
||||
emit(base); emit(", i32 "); emit(idx); emit("\n")
|
||||
return r
|
||||
}
|
||||
|
||||
# the constant initializer for a global var: a literal, or 0/null
|
||||
fn global_init(d: Node) -> ptr {
|
||||
if ptr_is_null(d.a) { if streq(llty(d.ty), "ptr") { return "null" } return "0" }
|
||||
if ptr_is_null(d.a) { if streq(llty(d.ty), "ptr") { return "null" }; return "0" }
|
||||
let e = d.a
|
||||
if e.kind == E_INT or e.kind == E_FLOAT or e.kind == E_BOOL { return itoa(e.ival) }
|
||||
if e.kind == E_UN and streq(e.s, "-") and e.a.kind == E_INT { return sconcat("-", itoa(e.a.ival)) }
|
||||
|
|
@ -74,14 +74,14 @@ fn emit_header() -> void {
|
|||
emith("@L_argc = internal global i32 0\n")
|
||||
emith("@L_argv = internal global ptr null\n")
|
||||
emith("@.gametitle = private unnamed_addr constant [")
|
||||
emith(itoa(slen(g_game_name) + 1)) emith(" x i8] c\"") emith(g_game_name) emith("\\00\"\n")
|
||||
emith(itoa(slen(g_game_name) + 1)); emith(" x i8] c\""); emith(g_game_name); emith("\\00\"\n")
|
||||
emith("%LSlice = type { ptr, i32, i32 }\n")
|
||||
# struct layouts
|
||||
let i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if d.kind == N_STRUCT {
|
||||
emith("%Str_") emith(d.s) emith(" = type { ")
|
||||
emith("%Str_"); emith(d.s); emith(" = type { ")
|
||||
if len(d.kids) == 0 { emith("i32") }
|
||||
let f = 0
|
||||
while f < len(d.kids) {
|
||||
|
|
@ -98,8 +98,8 @@ fn emit_header() -> void {
|
|||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if d.kind == N_VAR {
|
||||
emith("@g_") emith(d.s) emith(" = internal global ")
|
||||
emith(llty(d.ty)) emith(" ")
|
||||
emith("@g_"); emith(d.s); emith(" = internal global ")
|
||||
emith(llty(d.ty)); emith(" ")
|
||||
emith(global_init(d))
|
||||
emith("\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ fn is_intrinsic(name: ptr) -> bool {
|
|||
return false
|
||||
}
|
||||
# emit " <r> = <rest>\n" and return r
|
||||
fn emit_bind(rest: ptr) -> ptr { let r = nreg() emit(" ") emit(r) emit(" = ") emit(rest) emit("\n") return r }
|
||||
fn emit_bind(rest: ptr) -> ptr { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
|
||||
|
||||
fn arg_code(e: Node, i: int) -> ptr { let v = emit_expr(e.kids[i]) return v.code }
|
||||
fn arg_code(e: Node, i: int) -> ptr { let v = emit_expr(e.kids[i]); return v.code }
|
||||
|
||||
fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
||||
g_intrin_ok = true
|
||||
|
|
@ -32,21 +32,21 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
return val(emit_bind(sconcat("call ptr @malloc(i64 ", sconcat(w, ")"))), "ptr")
|
||||
}
|
||||
if streq(name, "mem_realloc") {
|
||||
let p = arg_code(e, 0) let n = arg_code(e, 1)
|
||||
let p = arg_code(e, 0); let n = arg_code(e, 1)
|
||||
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
|
||||
return val(emit_bind(sconcat("call ptr @realloc(ptr ", sconcat(p, sconcat(", i64 ", sconcat(w, ")"))))), "ptr")
|
||||
}
|
||||
if streq(name, "peek8") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1)
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1)
|
||||
let a = emit_gep_i8(p, i)
|
||||
let b = emit_bind(sconcat("load i8, ptr ", a))
|
||||
return val(emit_bind(sconcat("zext i8 ", sconcat(b, " to i32"))), "int")
|
||||
}
|
||||
if streq(name, "poke8") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1) let v = arg_code(e, 2)
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1); let v = arg_code(e, 2)
|
||||
let a = emit_gep_i8(p, i)
|
||||
let t = emit_bind(sconcat("trunc i32 ", sconcat(v, " to i8")))
|
||||
emit(" store i8 ") emit(t) emit(", ptr ") emit(a) emit("\n")
|
||||
emit(" store i8 "); emit(t); emit(", ptr "); emit(a); emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "ptr_is_null") {
|
||||
|
|
@ -55,11 +55,11 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
return val(emit_bind(sconcat("zext i1 ", sconcat(c, " to i32"))), "bool")
|
||||
}
|
||||
if streq(name, "file_open") {
|
||||
let p = arg_code(e, 0) let m = arg_code(e, 1)
|
||||
let p = arg_code(e, 0); let m = arg_code(e, 1)
|
||||
return val(emit_bind(sconcat("call ptr @fopen(ptr ", sconcat(p, sconcat(", ptr ", sconcat(m, ")"))))), "ptr")
|
||||
}
|
||||
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 fn2 = "@fread"
|
||||
if streq(name, "file_write") { fn2 = "@fwrite" }
|
||||
|
|
@ -67,7 +67,7 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
return val(emit_bind(sconcat("trunc i64 ", sconcat(r, " to i32"))), "int")
|
||||
}
|
||||
if streq(name, "file_seek") {
|
||||
let f = arg_code(e, 0) let off = arg_code(e, 1) let wh = arg_code(e, 2)
|
||||
let f = arg_code(e, 0); let off = arg_code(e, 1); let wh = arg_code(e, 2)
|
||||
let o = emit_bind(sconcat("sext i32 ", sconcat(off, " to i64")))
|
||||
return val(emit_bind(sconcat("call i32 @fseek(ptr ", sconcat(f, sconcat(", i64 ", sconcat(o, sconcat(", i32 ", sconcat(wh, ")"))))))), "int")
|
||||
}
|
||||
|
|
@ -78,17 +78,17 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
}
|
||||
if streq(name, "file_close") {
|
||||
let f = arg_code(e, 0)
|
||||
emit(" call i32 @fclose(ptr ") emit(f) emit(")\n")
|
||||
emit(" call i32 @fclose(ptr "); emit(f); emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "print_str") {
|
||||
let s = arg_code(e, 0)
|
||||
emit(" call i32 (ptr, ...) @printf(ptr @.fmt_str, ptr ") emit(s) emit(")\n")
|
||||
emit(" call i32 (ptr, ...) @printf(ptr @.fmt_str, ptr "); emit(s); emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "print_int") {
|
||||
let n = arg_code(e, 0)
|
||||
emit(" call i32 (ptr, ...) @printf(ptr @.fmt_int, i32 ") emit(n) emit(")\n")
|
||||
emit(" call i32 (ptr, ...) @printf(ptr @.fmt_int, i32 "); emit(n); emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "os_argc") { return val(emit_bind("load i32, ptr @L_argc"), "int") }
|
||||
|
|
@ -100,7 +100,7 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
}
|
||||
if streq(name, "os_exit") {
|
||||
let n = arg_code(e, 0)
|
||||
emit(" call void @exit(i32 ") emit(n) emit(")\n")
|
||||
emit(" call void @exit(i32 "); emit(n); emit(")\n")
|
||||
emit(" unreachable\n")
|
||||
g_term = true
|
||||
return val("0", "void")
|
||||
|
|
@ -114,9 +114,9 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
let n = arg_code(e, 0)
|
||||
return val(emit_bind(sconcat("call ptr @getenv(ptr ", sconcat(n, ")"))), "str")
|
||||
}
|
||||
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") {
|
||||
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"
|
||||
if streq(name,"shr") { opc = "lshr" }
|
||||
if streq(name,"band") { opc = "and" }
|
||||
|
|
@ -125,16 +125,16 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
return val(emit_bind(sconcat(opc, sconcat(" i32 ", sconcat(a, sconcat(", ", b))))), "int")
|
||||
}
|
||||
if streq(name, "peek32") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1)
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1)
|
||||
let g = nreg()
|
||||
emit(" ") emit(g) emit(" = getelementptr inbounds i32, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
emit(" "); emit(g); emit(" = getelementptr inbounds i32, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
||||
return val(emit_bind(sconcat("load i32, ptr ", g)), "int")
|
||||
}
|
||||
if streq(name, "poke32") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1) let v = arg_code(e, 2)
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1); let v = arg_code(e, 2)
|
||||
let g = nreg()
|
||||
emit(" ") emit(g) emit(" = getelementptr inbounds i32, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
emit(" store i32 ") emit(v) emit(", ptr ") emit(g) emit("\n")
|
||||
emit(" "); emit(g); emit(" = getelementptr inbounds i32, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
||||
emit(" store i32 "); emit(v); emit(", ptr "); emit(g); emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
g_intrin_ok = false
|
||||
|
|
|
|||
|
|
@ -18,29 +18,29 @@ fn is_intrinsic2(name: ptr) -> bool {
|
|||
|
||||
fn emit_intrinsic2(name: ptr, e: Node) -> Val {
|
||||
if streq(name, "mem_free") {
|
||||
let p = arg_code(e, 0) emit(" call void @free(ptr ") emit(p) emit(")\n") return val("0", "void")
|
||||
let p = arg_code(e, 0); emit(" call void @free(ptr "); emit(p); emit(")\n"); return val("0", "void")
|
||||
}
|
||||
if streq(name, "mem_copy") {
|
||||
let d = arg_code(e, 0) let s = arg_code(e, 1) let n = arg_code(e, 2)
|
||||
let d = arg_code(e, 0); let s = arg_code(e, 1); let n = arg_code(e, 2)
|
||||
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
|
||||
emit(" call ptr @memcpy(ptr ") emit(d) emit(", ptr ") emit(s) emit(", i64 ") emit(w) emit(")\n")
|
||||
emit(" call ptr @memcpy(ptr "); emit(d); emit(", ptr "); emit(s); emit(", i64 "); emit(w); emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "mem_set") {
|
||||
let p = arg_code(e, 0) let v = arg_code(e, 1) let n = arg_code(e, 2)
|
||||
let p = arg_code(e, 0); let v = arg_code(e, 1); let n = arg_code(e, 2)
|
||||
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
|
||||
emit(" call ptr @memset(ptr ") emit(p) emit(", i32 ") emit(v) emit(", i64 ") emit(w) emit(")\n")
|
||||
emit(" call ptr @memset(ptr "); emit(p); emit(", i32 "); emit(v); emit(", i64 "); emit(w); emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "ptr_add") {
|
||||
let p = arg_code(e, 0) let n = arg_code(e, 1)
|
||||
let p = arg_code(e, 0); let n = arg_code(e, 1)
|
||||
let g = nreg()
|
||||
emit(" ") emit(g) emit(" = getelementptr inbounds i8, ptr ") emit(p) emit(", i32 ") emit(n) emit("\n")
|
||||
emit(" "); emit(g); emit(" = getelementptr inbounds i8, ptr "); emit(p); emit(", i32 "); emit(n); emit("\n")
|
||||
return val(g, "ptr")
|
||||
}
|
||||
if streq(name, "read_byte") { return val(emit_bind("call i32 @getchar()"), "int") }
|
||||
if streq(name, "write_byte") {
|
||||
let v = arg_code(e, 0) emit(" call i32 @putchar(i32 ") emit(v) emit(")\n") return val("0", "void")
|
||||
let v = arg_code(e, 0); emit(" call i32 @putchar(i32 "); emit(v); emit(")\n"); return val("0", "void")
|
||||
}
|
||||
if streq(name, "str_len") {
|
||||
let s = arg_code(e, 0)
|
||||
|
|
@ -52,44 +52,44 @@ fn emit_intrinsic2(name: ptr, e: Node) -> Val {
|
|||
return val(emit_bind(sconcat("trunc i64 ", sconcat(r, " to i32"))), "int")
|
||||
}
|
||||
if streq(name, "peekf") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1)
|
||||
let g = nreg() emit(" ") emit(g) emit(" = getelementptr inbounds i32, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1)
|
||||
let g = nreg(); emit(" "); emit(g); emit(" = getelementptr inbounds i32, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
||||
return val(emit_bind(sconcat("load i32, ptr ", g)), "fixed")
|
||||
}
|
||||
if streq(name, "pokef") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1) let v = arg_code(e, 2)
|
||||
let g = nreg() emit(" ") emit(g) emit(" = getelementptr inbounds i32, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
emit(" store i32 ") emit(v) emit(", ptr ") emit(g) emit("\n")
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1); let v = arg_code(e, 2)
|
||||
let g = nreg(); emit(" "); emit(g); emit(" = getelementptr inbounds i32, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
||||
emit(" store i32 "); emit(v); emit(", ptr "); emit(g); emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "peekp") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1)
|
||||
let g = nreg() emit(" ") emit(g) emit(" = getelementptr inbounds ptr, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1)
|
||||
let g = nreg(); emit(" "); emit(g); emit(" = getelementptr inbounds ptr, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
||||
return val(emit_bind(sconcat("load ptr, ptr ", g)), "ptr")
|
||||
}
|
||||
if streq(name, "pokep") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1) let v = arg_code(e, 2)
|
||||
let g = nreg() emit(" ") emit(g) emit(" = getelementptr inbounds ptr, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
emit(" store ptr ") emit(v) emit(", ptr ") emit(g) emit("\n")
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1); let v = arg_code(e, 2)
|
||||
let g = nreg(); emit(" "); emit(g); emit(" = getelementptr inbounds ptr, ptr "); emit(p); emit(", i32 "); emit(i); emit("\n")
|
||||
emit(" store ptr "); emit(v); emit(", ptr "); emit(g); emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "as_fixed") { let a = emit_expr(e.kids[0]) return val(a.code, "fixed") }
|
||||
if streq(name, "as_int") { let a = emit_expr(e.kids[0]) return val(a.code, "int") }
|
||||
if streq(name, "is_windowed") { if g_windowed { return val("1", "bool") } return val("0", "bool") }
|
||||
if streq(name, "as_fixed") { let a = emit_expr(e.kids[0]); return val(a.code, "fixed") }
|
||||
if streq(name, "as_int") { let a = emit_expr(e.kids[0]); return val(a.code, "int") }
|
||||
if streq(name, "is_windowed") { if g_windowed { return val("1", "bool") }; return val("0", "bool") }
|
||||
if streq(name, "game_title") { return val("@.gametitle", "str") }
|
||||
# windowing hooks — declared external; only reached on the is_windowed() branch
|
||||
if streq(name, "win_poll") { return val(emit_bind("call i32 @win_poll()"), "int") }
|
||||
if streq(name, "win_running") { return val(emit_bind("call i32 @win_running()"), "bool") }
|
||||
if streq(name, "win_open") {
|
||||
let a = arg_code(e, 0) let b = arg_code(e, 1) let c = arg_code(e, 2) let d = arg_code(e, 3)
|
||||
emit(" call void @win_open(i32 ") emit(a) emit(", i32 ") emit(b) emit(", i32 ") emit(c) emit(", ptr ") emit(d) emit(")\n")
|
||||
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); let d = arg_code(e, 3)
|
||||
emit(" call void @win_open(i32 "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(", ptr "); emit(d); emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "win_present") {
|
||||
let a = arg_code(e, 0) let b = arg_code(e, 1) let c = arg_code(e, 2)
|
||||
emit(" call void @win_present(ptr ") emit(a) emit(", i32 ") emit(b) emit(", i32 ") emit(c) emit(")\n")
|
||||
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
|
||||
emit(" call void @win_present(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "win_close") { emit(" call void @win_close()\n") return val("0", "void") }
|
||||
if streq(name, "win_close") { emit(" call void @win_close()\n"); return val("0", "void") }
|
||||
return val("0", "void")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ fn emit_machine(st: Node) -> void {
|
|||
let state = st.kids[i]
|
||||
let v = emit_expr(state.b)
|
||||
let c = emit_bind(sconcat("icmp eq i32 ", sconcat(s, sconcat(", ", v.code))))
|
||||
let body = lbl("sbody") let nxt = lbl("sarm")
|
||||
emit(" br i1 ") emit(c) emit(", label %") emit(body) emit(", label %") emit(nxt) emit("\n")
|
||||
emit(body) emit(":\n") g_term = false
|
||||
let body = lbl("sbody"); let nxt = lbl("sarm")
|
||||
emit(" br i1 "); emit(c); emit(", label %"); emit(body); emit(", label %"); emit(nxt); emit("\n")
|
||||
emit(body); emit(":\n"); g_term = false
|
||||
emit_block(state.a)
|
||||
if not g_term { emit(" br label %") emit(endl) emit("\n") }
|
||||
emit(nxt) emit(":\n") g_term = false
|
||||
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
|
||||
emit(nxt); emit(":\n"); g_term = false
|
||||
i = i + 1
|
||||
}
|
||||
if not g_term { emit(" br label %") emit(endl) emit("\n") }
|
||||
emit(endl) emit(":\n") g_term = false
|
||||
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
|
||||
emit(endl); emit(":\n"); g_term = false
|
||||
nmach = nmach - 1
|
||||
}
|
||||
|
||||
|
|
@ -31,9 +31,9 @@ fn emit_become(st: Node) -> void {
|
|||
let m = mach_stk[nmach - 1]
|
||||
let target: Node = ptr_null()
|
||||
let 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)) }
|
||||
let regv = emit_expr(m.a)
|
||||
let sv = emit_expr(target.b)
|
||||
emit(" call void @fn_rt_setreg(i32 ") emit(regv.code) emit(", i32 ") emit(sv.code) emit(")\n")
|
||||
emit(" call void @fn_rt_setreg(i32 "); emit(regv.code); emit(", i32 "); emit(sv.code); emit(")\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ fn emit_math_builtin(name: ptr, e: Node) -> Val {
|
|||
return val(emit_bind(sconcat("select i1 ", sconcat(c, sconcat(", i32 ", sconcat(n, sconcat(", i32 ", a.code)))))), "int")
|
||||
}
|
||||
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"
|
||||
if streq(name, "max") { op = "sgt" }
|
||||
let c = emit_bind(sconcat("icmp ", sconcat(op, sconcat(" i32 ", sconcat(a.code, sconcat(", ", b.code))))))
|
||||
return val(emit_bind(sconcat("select i1 ", sconcat(c, sconcat(", i32 ", sconcat(a.code, sconcat(", i32 ", b.code)))))), "int")
|
||||
}
|
||||
# clamp(v, lo, hi) = max(lo, min(v, hi))
|
||||
let v = emit_expr(e.kids[0]) let lo = emit_expr(e.kids[1]) let hi = emit_expr(e.kids[2])
|
||||
let v = emit_expr(e.kids[0]); let lo = emit_expr(e.kids[1]); let hi = emit_expr(e.kids[2])
|
||||
let c1 = emit_bind(sconcat("icmp slt i32 ", sconcat(v.code, sconcat(", ", hi.code))))
|
||||
let t = emit_bind(sconcat("select i1 ", sconcat(c1, sconcat(", i32 ", sconcat(v.code, sconcat(", i32 ", hi.code))))))
|
||||
let c2 = emit_bind(sconcat("icmp sgt i32 ", sconcat(lo.code, sconcat(", ", t))))
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ fn emit_new_struct(name: ptr) -> Val {
|
|||
while f < len(s.kids) {
|
||||
let fd = s.kids[f]
|
||||
let addr = nreg()
|
||||
emit(" ") emit(addr) emit(" = getelementptr inbounds %Str_") emit(name)
|
||||
emit(", ptr ") emit(obj) emit(", i32 0, i32 ") emit(itoa(f)) emit("\n")
|
||||
emit(" "); emit(addr); emit(" = getelementptr inbounds %Str_"); emit(name)
|
||||
emit(", ptr "); emit(obj); emit(", i32 0, i32 "); emit(itoa(f)); emit("\n")
|
||||
let lt = llty(fd.ty)
|
||||
let v = "0"
|
||||
if streq(lt, "ptr") { v = "null" }
|
||||
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a) v = dv.code }
|
||||
emit(" store ") emit(lt) emit(" ") emit(v) emit(", ptr ") emit(addr) emit("\n")
|
||||
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")
|
||||
f = f + 1
|
||||
}
|
||||
return val(obj, name)
|
||||
|
|
@ -31,19 +31,19 @@ fn emit_new_struct(name: ptr) -> Val {
|
|||
fn emit_new_slice(ty: ptr) -> Val {
|
||||
let sz = emit_sizeof("%LSlice")
|
||||
let h = emit_bind(sconcat("call ptr @malloc(i64 ", sconcat(sz, ")")))
|
||||
let d0 = nreg() emit(" ") emit(d0) emit(" = getelementptr inbounds %LSlice, ptr ") emit(h) emit(", i32 0, i32 0\n")
|
||||
emit(" store ptr null, ptr ") emit(d0) emit("\n")
|
||||
let d1 = nreg() emit(" ") emit(d1) emit(" = getelementptr inbounds %LSlice, ptr ") emit(h) emit(", i32 0, i32 1\n")
|
||||
emit(" store i32 0, ptr ") emit(d1) emit("\n")
|
||||
let d2 = nreg() emit(" ") emit(d2) emit(" = getelementptr inbounds %LSlice, ptr ") emit(h) emit(", i32 0, i32 2\n")
|
||||
emit(" store i32 0, ptr ") emit(d2) emit("\n")
|
||||
let d0 = nreg(); emit(" "); emit(d0); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h); emit(", i32 0, i32 0\n")
|
||||
emit(" store ptr null, ptr "); emit(d0); emit("\n")
|
||||
let d1 = nreg(); emit(" "); emit(d1); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h); emit(", i32 0, i32 1\n")
|
||||
emit(" store i32 0, ptr "); emit(d1); emit("\n")
|
||||
let d2 = nreg(); emit(" "); emit(d2); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h); emit(", i32 0, i32 2\n")
|
||||
emit(" store i32 0, ptr "); emit(d2); emit("\n")
|
||||
return val(h, ty)
|
||||
}
|
||||
|
||||
fn slice_field(h: ptr, i: int) -> ptr {
|
||||
let r = nreg()
|
||||
emit(" ") emit(r) emit(" = getelementptr inbounds %LSlice, ptr ") emit(h)
|
||||
emit(", i32 0, i32 ") emit(itoa(i)) emit("\n")
|
||||
emit(" "); emit(r); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h)
|
||||
emit(", i32 0, i32 "); emit(itoa(i)); emit("\n")
|
||||
return r
|
||||
}
|
||||
|
||||
|
|
@ -58,13 +58,13 @@ fn emit_push(e: Node) -> Val {
|
|||
let el = slice_elem(s.ty)
|
||||
let elt = llty(el)
|
||||
let h = s.code
|
||||
let lp = slice_field(h, 1) let cp = slice_field(h, 2) let dp = slice_field(h, 0)
|
||||
let lp = slice_field(h, 1); let cp = slice_field(h, 2); let dp = slice_field(h, 0)
|
||||
let l = emit_bind(sconcat("load i32, ptr ", lp))
|
||||
let c = emit_bind(sconcat("load i32, ptr ", cp))
|
||||
let full = emit_bind(sconcat("icmp sge i32 ", sconcat(l, sconcat(", ", c))))
|
||||
let grow = lbl("grow") let put = lbl("put")
|
||||
emit(" br i1 ") emit(full) emit(", label %") emit(grow) emit(", label %") emit(put) emit("\n")
|
||||
emit(grow) emit(":\n")
|
||||
let grow = lbl("grow"); let put = lbl("put")
|
||||
emit(" br i1 "); emit(full); emit(", label %"); emit(grow); emit(", label %"); emit(put); emit("\n")
|
||||
emit(grow); emit(":\n")
|
||||
let dbl = emit_bind(sconcat("mul i32 ", sconcat(c, ", 2")))
|
||||
let isz = emit_bind(sconcat("icmp eq i32 ", sconcat(c, ", 0")))
|
||||
let nc = emit_bind(sconcat("select i1 ", sconcat(isz, sconcat(", i32 8, i32 ", dbl))))
|
||||
|
|
@ -73,16 +73,16 @@ fn emit_push(e: Node) -> Val {
|
|||
let bytes = emit_bind(sconcat("mul i64 ", sconcat(ncw, sconcat(", ", esz))))
|
||||
let old = emit_bind(sconcat("load ptr, ptr ", dp))
|
||||
let nd = emit_bind(sconcat("call ptr @realloc(ptr ", sconcat(old, sconcat(", i64 ", sconcat(bytes, ")")))))
|
||||
emit(" store ptr ") emit(nd) emit(", ptr ") emit(dp) emit("\n")
|
||||
emit(" store i32 ") emit(nc) emit(", ptr ") emit(cp) emit("\n")
|
||||
emit(" br label %") emit(put) emit("\n")
|
||||
emit(put) emit(":\n")
|
||||
emit(" store ptr "); emit(nd); emit(", ptr "); emit(dp); emit("\n")
|
||||
emit(" store i32 "); emit(nc); emit(", ptr "); emit(cp); emit("\n")
|
||||
emit(" br label %"); emit(put); emit("\n")
|
||||
emit(put); emit(":\n")
|
||||
let v = emit_expr(e.kids[1])
|
||||
let data = emit_bind(sconcat("load ptr, ptr ", dp))
|
||||
let slot = nreg()
|
||||
emit(" ") emit(slot) emit(" = getelementptr inbounds ") emit(elt) emit(", ptr ") emit(data) emit(", i32 ") emit(l) emit("\n")
|
||||
emit(" store ") emit(elt) emit(" ") emit(v.code) emit(", ptr ") emit(slot) emit("\n")
|
||||
emit(" "); emit(slot); emit(" = getelementptr inbounds "); emit(elt); emit(", ptr "); emit(data); emit(", i32 "); emit(l); emit("\n")
|
||||
emit(" store "); emit(elt); emit(" "); emit(v.code); emit(", ptr "); emit(slot); emit("\n")
|
||||
let l1 = emit_bind(sconcat("add i32 ", sconcat(l, ", 1")))
|
||||
emit(" store i32 ") emit(l1) emit(", ptr ") emit(lp) emit("\n")
|
||||
emit(" store i32 "); emit(l1); emit(", ptr "); emit(lp); emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
# the body once per match. Mirrors ll_query in compiler/back/ir_ecs.c.
|
||||
|
||||
fn find_arch_id(name: ptr) -> int {
|
||||
let i = 0 let n = 1
|
||||
while i < len(prog) { if prog[i].kind == N_ARCH { if streq(prog[i].s, name) { return n } n = n + 1 } i = i + 1 }
|
||||
let i = 0; let n = 1
|
||||
while i < len(prog) { if prog[i].kind == N_ARCH { if streq(prog[i].s, name) { return n }; n = n + 1 }; i = i + 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -15,22 +15,22 @@ fn emit_query(st: Node) -> void {
|
|||
if nself < len(self_stk) { self_stk[nself] = ip } else { push(self_stk, ip) }
|
||||
nself = nself + 1
|
||||
|
||||
let cond = lbl("qcond") let body = lbl("qbody") let nxt = lbl("qnext") let endl = lbl("qend")
|
||||
emit(" br label %") emit(cond) emit("\n")
|
||||
emit(cond) emit(":\n")
|
||||
let cond = lbl("qcond"); let body = lbl("qbody"); let nxt = lbl("qnext"); let endl = lbl("qend")
|
||||
emit(" br label %"); emit(cond); emit("\n")
|
||||
emit(cond); emit(":\n")
|
||||
let i0 = emit_bind(sconcat("load i32, ptr ", ip))
|
||||
let ec = emit_bind("load i32, ptr @L_entc")
|
||||
let lt = emit_bind(sconcat("icmp slt i32 ", sconcat(i0, sconcat(", ", ec))))
|
||||
emit(" br i1 ") emit(lt) emit(", label %") emit(body) emit(", label %") emit(endl) emit("\n")
|
||||
emit(body) emit(":\n") g_term = false
|
||||
emit(" br i1 "); emit(lt); emit(", label %"); emit(body); emit(", label %"); emit(endl); emit("\n")
|
||||
emit(body); emit(":\n"); g_term = false
|
||||
let i1 = emit_bind(sconcat("load i32, ptr ", ip))
|
||||
# alive?
|
||||
let ap = nreg() emit(" ") emit(ap) emit(" = getelementptr inbounds [") emit(me) emit(" x i32], ptr @L_alive, i32 0, i32 ") emit(i1) emit("\n")
|
||||
let ap = nreg(); emit(" "); emit(ap); emit(" = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_alive, i32 0, i32 "); emit(i1); emit("\n")
|
||||
let al = emit_bind(sconcat("load i32, ptr ", ap))
|
||||
let alc = emit_bind(sconcat("icmp ne i32 ", sconcat(al, ", 0")))
|
||||
let ka = lbl("qa")
|
||||
emit(" br i1 ") emit(alc) emit(", label %") emit(ka) emit(", label %") emit(nxt) emit("\n")
|
||||
emit(ka) emit(":\n")
|
||||
emit(" br i1 "); emit(alc); emit(", label %"); emit(ka); emit(", label %"); emit(nxt); emit("\n")
|
||||
emit(ka); emit(":\n")
|
||||
|
||||
let terms = st.c
|
||||
# component / archetype filters
|
||||
|
|
@ -40,17 +40,17 @@ fn emit_query(st: Node) -> void {
|
|||
let ak = find_arch_id(tm.s)
|
||||
let ok = "0"
|
||||
if ak > 0 {
|
||||
let kp = nreg() emit(" ") emit(kp) emit(" = getelementptr inbounds [") emit(me) emit(" x i32], ptr @L_kind, i32 0, i32 ") emit(i1) emit("\n")
|
||||
let 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))
|
||||
ok = emit_bind(sconcat("icmp eq i32 ", sconcat(kv, sconcat(", ", itoa(ak)))))
|
||||
} else {
|
||||
let hp = nreg() emit(" ") emit(hp) emit(" = getelementptr inbounds [") emit(me) emit(" x i8], ptr @H_") emit(tm.s) emit(", i32 0, i32 ") emit(i1) emit("\n")
|
||||
let hp = nreg(); emit(" "); emit(hp); emit(" = getelementptr inbounds ["); emit(me); emit(" x i8], ptr @H_"); emit(tm.s); emit(", i32 0, i32 "); emit(i1); emit("\n")
|
||||
let hv = emit_bind(sconcat("load i8, ptr ", hp))
|
||||
ok = emit_bind(sconcat("icmp ne i8 ", sconcat(hv, ", 0")))
|
||||
}
|
||||
let keep = lbl("qt")
|
||||
emit(" br i1 ") emit(ok) emit(", label %") emit(keep) emit(", label %") emit(nxt) emit("\n")
|
||||
emit(keep) emit(":\n")
|
||||
emit(" br i1 "); emit(ok); emit(", label %"); emit(keep); emit(", label %"); emit(nxt); emit("\n")
|
||||
emit(keep); emit(":\n")
|
||||
t = t + 1
|
||||
}
|
||||
|
||||
|
|
@ -63,9 +63,9 @@ fn emit_query(st: Node) -> void {
|
|||
if tm.ival == 0 and find_arch_id(tm.s) == 0 {
|
||||
if vi < len(st.kids) {
|
||||
let slot = nreg()
|
||||
emit(" ") emit(slot) emit(" = getelementptr inbounds [") emit(me) emit(" x %Cmp_") emit(tm.s) emit("], ptr @S_") emit(tm.s) emit(", i32 0, i32 ") emit(i1) emit("\n")
|
||||
emit(" "); emit(slot); emit(" = getelementptr inbounds ["); emit(me); emit(" x %Cmp_"); emit(tm.s); emit("], ptr @S_"); emit(tm.s); emit(", i32 0, i32 "); emit(i1); emit("\n")
|
||||
let vslot = emit_alloca("ptr")
|
||||
emit(" store ptr ") emit(slot) emit(", ptr ") emit(vslot) emit("\n")
|
||||
emit(" store ptr "); emit(slot); emit(", ptr "); emit(vslot); emit("\n")
|
||||
loc_push(st.kids[vi].s, vslot, tm.s)
|
||||
vi = vi + 1
|
||||
}
|
||||
|
|
@ -78,21 +78,21 @@ fn emit_query(st: Node) -> void {
|
|||
let w = emit_expr(st.b)
|
||||
let wc = emit_bind(sconcat("icmp ne i32 ", sconcat(w.code, ", 0")))
|
||||
let kw = lbl("qw")
|
||||
emit(" br i1 ") emit(wc) emit(", label %") emit(kw) emit(", label %") emit(nxt) emit("\n")
|
||||
emit(kw) emit(":\n")
|
||||
emit(" br i1 "); emit(wc); emit(", label %"); emit(kw); emit(", label %"); emit(nxt); emit("\n")
|
||||
emit(kw); emit(":\n")
|
||||
}
|
||||
|
||||
loop_push(nxt, endl)
|
||||
emit_block(st.a)
|
||||
loop_pop()
|
||||
nloc = save
|
||||
if not g_term { emit(" br label %") emit(nxt) emit("\n") }
|
||||
if not g_term { emit(" br label %"); emit(nxt); emit("\n") }
|
||||
|
||||
emit(nxt) emit(":\n")
|
||||
emit(nxt); emit(":\n")
|
||||
let i2 = emit_bind(sconcat("load i32, ptr ", ip))
|
||||
let i3 = emit_bind(sconcat("add i32 ", sconcat(i2, ", 1")))
|
||||
store_at("i32", i3, ip)
|
||||
emit(" br label %") emit(cond) emit("\n")
|
||||
emit(endl) emit(":\n") g_term = false
|
||||
emit(" br label %"); emit(cond); emit("\n")
|
||||
emit(endl); emit(":\n"); g_term = false
|
||||
nself = nself - 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
var g_iok: int = 0
|
||||
|
||||
fn emit_io(fn2: ptr, p: ptr, bytes: ptr) -> void {
|
||||
let r = sconcat("%io", itoa(g_iok)) g_iok = g_iok + 1
|
||||
emit(" ") emit(r) emit(" = call i64 @") emit(fn2) emit("(ptr ") emit(p) emit(", i64 1, i64 ") emit(bytes) emit(", ptr %f)\n")
|
||||
let r = sconcat("%io", itoa(g_iok)); g_iok = g_iok + 1
|
||||
emit(" "); emit(r); emit(" = call i64 @"); emit(fn2); emit("(ptr "); emit(p); emit(", i64 1, i64 "); emit(bytes); emit(", ptr %f)\n")
|
||||
}
|
||||
|
||||
fn emit_snapshot_blocks(fn2: ptr) -> void {
|
||||
|
|
@ -15,19 +15,19 @@ fn emit_snapshot_blocks(fn2: ptr) -> void {
|
|||
let me = itoa(MAX_ENT)
|
||||
emit_io(fn2, "@L_entc", "4")
|
||||
emit_io(fn2, "@L_freen", "4")
|
||||
emit(" %nalive = mul i64 ") emit(me) emit(", 4\n")
|
||||
emit(" %nalive = mul i64 "); emit(me); emit(", 4\n")
|
||||
emit_io(fn2, "@L_alive", "%nalive")
|
||||
emit_io(fn2, "@L_freelist", "%nalive")
|
||||
emit_io(fn2, "@L_kind", "%nalive")
|
||||
let 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
|
||||
i = 0
|
||||
while i < len(prog) {
|
||||
if prog[i].kind == N_COMP {
|
||||
let c = prog[i].s
|
||||
let csz = sconcat("%csz", itoa(ci))
|
||||
emit(" ") emit(csz) emit(" = ptrtoint ptr getelementptr (%Cmp_") emit(c) emit(", ptr null, i32 ") emit(me) emit(") to i64\n")
|
||||
emit(" "); emit(csz); emit(" = ptrtoint ptr getelementptr (%Cmp_"); emit(c); emit(", ptr null, i32 "); emit(me); emit(") to i64\n")
|
||||
emit_io(fn2, sconcat("@S_", c), csz)
|
||||
emit_io(fn2, sconcat("@H_", c), me)
|
||||
ci = ci + 1
|
||||
|
|
|
|||
|
|
@ -5,21 +5,21 @@ fn emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
|
|||
let me = itoa(MAX_ENT)
|
||||
let c = find_comp(comp)
|
||||
if ptr_is_null(c) { perr(sconcat("spawn: unknown component ", comp)) }
|
||||
let hp = nreg() emit(" ") emit(hp) emit(" = getelementptr inbounds [") emit(me) emit(" x i8], ptr @H_") emit(comp) emit(", i32 0, i32 ") emit(e) emit("\n")
|
||||
emit(" store i8 1, ptr ") emit(hp) emit("\n")
|
||||
let hp = nreg(); emit(" "); emit(hp); emit(" = getelementptr inbounds ["); emit(me); emit(" x i8], ptr @H_"); emit(comp); emit(", i32 0, i32 "); emit(e); emit("\n")
|
||||
emit(" store i8 1, ptr "); emit(hp); emit("\n")
|
||||
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
|
||||
let f = 0
|
||||
while f < len(c.kids) {
|
||||
let fd = c.kids[f]
|
||||
let addr = nreg()
|
||||
emit(" ") emit(addr) emit(" = getelementptr inbounds %Cmp_") emit(comp) emit(", ptr ") emit(slot) emit(", i32 0, i32 ") emit(itoa(f)) emit("\n")
|
||||
emit(" "); emit(addr); emit(" = getelementptr inbounds %Cmp_"); emit(comp); emit(", ptr "); emit(slot); emit(", i32 0, i32 "); emit(itoa(f)); emit("\n")
|
||||
let lt = llty(fd.ty)
|
||||
let v = "0"
|
||||
if streq(lt, "ptr") { v = "null" }
|
||||
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a) v = dv.code }
|
||||
emit(" store ") emit(lt) emit(" ") emit(v) emit(", ptr ") emit(addr) emit("\n")
|
||||
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")
|
||||
f = f + 1
|
||||
}
|
||||
# per-spawn overrides
|
||||
|
|
@ -30,9 +30,9 @@ fn emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
|
|||
let fidx = field_index(c, fi.s)
|
||||
if fidx >= 0 {
|
||||
let addr = nreg()
|
||||
emit(" ") emit(addr) emit(" = getelementptr inbounds %Cmp_") emit(comp) emit(", ptr ") emit(slot) emit(", i32 0, i32 ") emit(itoa(fidx)) emit("\n")
|
||||
emit(" "); emit(addr); emit(" = getelementptr inbounds %Cmp_"); emit(comp); emit(", ptr "); emit(slot); emit(", i32 0, i32 "); emit(itoa(fidx)); emit("\n")
|
||||
let dv = emit_expr(fi.a)
|
||||
emit(" store ") emit(llty(field_type(c, fi.s))) emit(" ") emit(dv.code) emit(", ptr ") emit(addr) emit("\n")
|
||||
emit(" store "); emit(llty(field_type(c, fi.s))); emit(" "); emit(dv.code); emit(", ptr "); emit(addr); emit("\n")
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
|
|
@ -44,25 +44,25 @@ fn emit_spawn(st: Node) -> void {
|
|||
let ak = find_arch_id(st.s)
|
||||
if ak > 0 {
|
||||
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(e) emit("\n")
|
||||
emit(" store i32 ") emit(itoa(ak)) emit(", ptr ") emit(kp) 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")
|
||||
let arch = find_arch(st.s)
|
||||
let c = 0
|
||||
while c < len(arch.kids) {
|
||||
let cn = arch.kids[c].s
|
||||
let rec = ptr_null()
|
||||
let 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)
|
||||
c = c + 1
|
||||
}
|
||||
} else {
|
||||
let 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 }
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_despawn(st: Node) -> void {
|
||||
let v = emit_expr(st.a)
|
||||
emit(" call void @L_free_entity(i32 ") emit(v.code) emit(")\n")
|
||||
emit(" call void @L_free_entity(i32 "); emit(v.code); emit(")\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ fn emit_block(b: Node) -> void {
|
|||
|
||||
# store `val` (llvm type `lt`) into address `addr`
|
||||
fn store_at(lt: ptr, v: ptr, addr: ptr) -> void {
|
||||
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")
|
||||
}
|
||||
|
||||
fn emit_assign(st: Node) -> void {
|
||||
|
|
@ -22,15 +22,15 @@ fn emit_assign(st: Node) -> void {
|
|||
let ty = "int"
|
||||
if t.kind == E_ID {
|
||||
let li = loc_find(t.s)
|
||||
if li >= 0 { addr = loc_reg[li] ty = loc_ty[li] }
|
||||
if li >= 0 { addr = loc_reg[li]; ty = loc_ty[li] }
|
||||
else {
|
||||
let g = find_global(t.s)
|
||||
if ptr_is_null(g) { perr(sconcat("assign to unknown ", t.s)) }
|
||||
addr = sconcat("@g_", t.s) ty = g.ty
|
||||
addr = sconcat("@g_", t.s); ty = g.ty
|
||||
}
|
||||
} else {
|
||||
if t.kind == E_MEMBER { addr = emit_member_addr(t) ty = g_addr_ty }
|
||||
else { if t.kind == E_INDEX { addr = emit_index_addr(t) ty = g_addr_ty }
|
||||
if t.kind == E_MEMBER { addr = emit_member_addr(t); ty = g_addr_ty }
|
||||
else { if t.kind == E_INDEX { addr = emit_index_addr(t); ty = g_addr_ty }
|
||||
else { perr("bad assignment target") } }
|
||||
}
|
||||
let lt = llty(ty)
|
||||
|
|
@ -51,34 +51,34 @@ fn emit_if(st: Node) -> void {
|
|||
let c = emit_expr(st.a)
|
||||
let cc = emit_bind(sconcat("icmp ne i32 ", sconcat(c.code, ", 0")))
|
||||
let has_else = not ptr_is_null(st.c)
|
||||
let tl = lbl("then") let el = lbl("else") let en = lbl("ifend")
|
||||
if has_else { emit(" br i1 ") emit(cc) emit(", label %") emit(tl) emit(", label %") emit(el) emit("\n") }
|
||||
else { emit(" br i1 ") emit(cc) emit(", label %") emit(tl) emit(", label %") emit(en) emit("\n") }
|
||||
emit(tl) emit(":\n") g_term = false
|
||||
let tl = lbl("then"); let el = lbl("else"); let en = lbl("ifend")
|
||||
if has_else { emit(" br i1 "); emit(cc); emit(", label %"); emit(tl); emit(", label %"); emit(el); emit("\n") }
|
||||
else { emit(" br i1 "); emit(cc); emit(", label %"); emit(tl); emit(", label %"); emit(en); emit("\n") }
|
||||
emit(tl); emit(":\n"); g_term = false
|
||||
emit_block(st.b)
|
||||
if not g_term { emit(" br label %") emit(en) emit("\n") }
|
||||
if not g_term { emit(" br label %"); emit(en); emit("\n") }
|
||||
if has_else {
|
||||
emit(el) emit(":\n") g_term = false
|
||||
emit(el); emit(":\n"); g_term = false
|
||||
# else is either a block or a nested if-statement
|
||||
if st.c.kind == S_IF { emit_stmt(st.c) } else { emit_block(st.c) }
|
||||
if not g_term { emit(" br label %") emit(en) emit("\n") }
|
||||
if not g_term { emit(" br label %"); emit(en); emit("\n") }
|
||||
}
|
||||
emit(en) emit(":\n") g_term = false
|
||||
emit(en); emit(":\n"); g_term = false
|
||||
}
|
||||
|
||||
fn emit_while(st: Node) -> void {
|
||||
let cl = lbl("wcond") let bl = lbl("wbody") let en = lbl("wend")
|
||||
emit(" br label %") emit(cl) emit("\n")
|
||||
emit(cl) emit(":\n")
|
||||
let cl = lbl("wcond"); let bl = lbl("wbody"); let en = lbl("wend")
|
||||
emit(" br label %"); emit(cl); emit("\n")
|
||||
emit(cl); emit(":\n")
|
||||
let c = emit_expr(st.a)
|
||||
let cc = emit_bind(sconcat("icmp ne i32 ", sconcat(c.code, ", 0")))
|
||||
emit(" br i1 ") emit(cc) emit(", label %") emit(bl) emit(", label %") emit(en) emit("\n")
|
||||
emit(bl) emit(":\n") g_term = false
|
||||
emit(" br i1 "); emit(cc); emit(", label %"); emit(bl); emit(", label %"); emit(en); emit("\n")
|
||||
emit(bl); emit(":\n"); g_term = false
|
||||
loop_push(cl, en)
|
||||
emit_block(st.b)
|
||||
loop_pop()
|
||||
if not g_term { emit(" br label %") emit(cl) emit("\n") }
|
||||
emit(en) emit(":\n") g_term = false
|
||||
if not g_term { emit(" br label %"); emit(cl); emit("\n") }
|
||||
emit(en); emit(":\n"); g_term = false
|
||||
}
|
||||
|
||||
fn emit_for(st: Node) -> void {
|
||||
|
|
@ -86,24 +86,24 @@ fn emit_for(st: Node) -> void {
|
|||
let lo = emit_expr(st.a)
|
||||
store_at("i32", lo.code, slot)
|
||||
loc_push(st.s, slot, "int")
|
||||
let cl = lbl("fcond") let bl = lbl("fbody") let ct = lbl("fcont") let en = lbl("fend")
|
||||
emit(" br label %") emit(cl) emit("\n")
|
||||
emit(cl) emit(":\n")
|
||||
let cl = lbl("fcond"); let bl = lbl("fbody"); let ct = lbl("fcont"); let en = lbl("fend")
|
||||
emit(" br label %"); emit(cl); emit("\n")
|
||||
emit(cl); emit(":\n")
|
||||
let iv = emit_bind(sconcat("load i32, ptr ", slot))
|
||||
let hi = emit_expr(st.b)
|
||||
let cc = emit_bind(sconcat("icmp slt i32 ", sconcat(iv, sconcat(", ", hi.code))))
|
||||
emit(" br i1 ") emit(cc) emit(", label %") emit(bl) emit(", label %") emit(en) emit("\n")
|
||||
emit(bl) emit(":\n") g_term = false
|
||||
emit(" br i1 "); emit(cc); emit(", label %"); emit(bl); emit(", label %"); emit(en); emit("\n")
|
||||
emit(bl); emit(":\n"); g_term = false
|
||||
loop_push(ct, en)
|
||||
emit_block(st.c)
|
||||
loop_pop()
|
||||
if not g_term { emit(" br label %") emit(ct) emit("\n") }
|
||||
emit(ct) emit(":\n")
|
||||
if not g_term { emit(" br label %"); emit(ct); emit("\n") }
|
||||
emit(ct); emit(":\n")
|
||||
let i2 = emit_bind(sconcat("load i32, ptr ", slot))
|
||||
let i3 = emit_bind(sconcat("add i32 ", sconcat(i2, ", 1")))
|
||||
store_at("i32", i3, slot)
|
||||
emit(" br label %") emit(cl) emit("\n")
|
||||
emit(en) emit(":\n") g_term = false
|
||||
emit(" br label %"); emit(cl); emit("\n")
|
||||
emit(en); emit(":\n"); g_term = false
|
||||
}
|
||||
|
||||
fn emit_return(st: Node) -> void {
|
||||
|
|
@ -117,7 +117,7 @@ fn emit_return(st: Node) -> void {
|
|||
|
||||
fn arm_is_default(arm: Node) -> bool {
|
||||
let 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
|
||||
}
|
||||
|
||||
|
|
@ -136,58 +136,58 @@ fn emit_match(st: Node) -> void {
|
|||
while p < len(arm.kids) {
|
||||
let pv = emit_expr(arm.kids[p])
|
||||
let c = emit_bind(sconcat("icmp eq i32 ", sconcat(sv.code, sconcat(", ", pv.code))))
|
||||
if first { acc = c first = false }
|
||||
if first { acc = c; first = false }
|
||||
else { acc = emit_bind(sconcat("or i1 ", sconcat(acc, sconcat(", ", c)))) }
|
||||
p = p + 1
|
||||
}
|
||||
let bodyl = lbl("mbody") let nextl = lbl("marm")
|
||||
emit(" br i1 ") emit(acc) emit(", label %") emit(bodyl) emit(", label %") emit(nextl) emit("\n")
|
||||
emit(bodyl) emit(":\n") g_term = false
|
||||
let bodyl = lbl("mbody"); let nextl = lbl("marm")
|
||||
emit(" br i1 "); emit(acc); emit(", label %"); emit(bodyl); emit(", label %"); emit(nextl); emit("\n")
|
||||
emit(bodyl); emit(":\n"); g_term = false
|
||||
emit_block(arm.a)
|
||||
if not g_term { emit(" br label %") emit(endl) emit("\n") }
|
||||
emit(nextl) emit(":\n") g_term = false
|
||||
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
|
||||
emit(nextl); emit(":\n"); g_term = false
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
if not ptr_is_null(deflt) { emit_block(deflt.a) }
|
||||
if not g_term { emit(" br label %") emit(endl) emit("\n") }
|
||||
emit(endl) emit(":\n") g_term = false
|
||||
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
|
||||
emit(endl); emit(":\n"); g_term = false
|
||||
}
|
||||
|
||||
fn emit_stmt(st: Node) -> void {
|
||||
if st.kind == S_LET {
|
||||
let ty = st.ty
|
||||
if ptr_is_null(ty) { let v0 = emit_expr(st.a) ty = v0.ty
|
||||
let slot = emit_alloca(llty(ty)) store_at(llty(ty), v0.code, slot) loc_push(st.s, slot, ty) return }
|
||||
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))
|
||||
if ptr_is_null(st.a) {
|
||||
let z = "0"
|
||||
if streq(llty(ty), "ptr") { z = "null" }
|
||||
store_at(llty(ty), z, slot)
|
||||
} else { let v = emit_expr(st.a) store_at(llty(ty), v.code, slot) }
|
||||
} else { let v = emit_expr(st.a); store_at(llty(ty), v.code, slot) }
|
||||
loc_push(st.s, slot, ty)
|
||||
return
|
||||
}
|
||||
if st.kind == S_ASSIGN { emit_assign(st) return }
|
||||
if st.kind == S_IF { emit_if(st) return }
|
||||
if st.kind == S_WHILE { emit_while(st) return }
|
||||
if st.kind == S_FOR { emit_for(st) return }
|
||||
if st.kind == S_RETURN { emit_return(st) return }
|
||||
if st.kind == S_BREAK { emit(" br label %") emit(brk_lbl[nloop - 1]) emit("\n") g_term = true return }
|
||||
if st.kind == S_CONTINUE { emit(" br label %") emit(cnt_lbl[nloop - 1]) emit("\n") g_term = true return }
|
||||
if st.kind == S_MATCH { emit_match(st) return }
|
||||
if st.kind == S_QUERY { emit_query(st) return }
|
||||
if st.kind == S_SPAWN { emit_spawn(st) return }
|
||||
if st.kind == S_DESPAWN { emit_despawn(st) return }
|
||||
if st.kind == S_MACHINE { emit_machine(st) return }
|
||||
if st.kind == S_BECOME { emit_become(st) return }
|
||||
if st.kind == S_EXPR { let v = emit_expr(st.a) return }
|
||||
if st.kind == S_ASSIGN { emit_assign(st); return }
|
||||
if st.kind == S_IF { emit_if(st); return }
|
||||
if st.kind == S_WHILE { emit_while(st); return }
|
||||
if st.kind == S_FOR { emit_for(st); return }
|
||||
if st.kind == S_RETURN { emit_return(st); return }
|
||||
if st.kind == S_BREAK { emit(" br label %"); emit(brk_lbl[nloop - 1]); emit("\n"); g_term = true; return }
|
||||
if st.kind == S_CONTINUE { emit(" br label %"); emit(cnt_lbl[nloop - 1]); emit("\n"); g_term = true; return }
|
||||
if st.kind == S_MATCH { emit_match(st); return }
|
||||
if st.kind == S_QUERY { emit_query(st); return }
|
||||
if st.kind == S_SPAWN { emit_spawn(st); return }
|
||||
if st.kind == S_DESPAWN { emit_despawn(st); return }
|
||||
if st.kind == S_MACHINE { emit_machine(st); return }
|
||||
if st.kind == S_BECOME { emit_become(st); return }
|
||||
if st.kind == S_EXPR { let v = emit_expr(st.a); return }
|
||||
perr("cannot emit statement")
|
||||
}
|
||||
|
||||
fn loop_push(cont: ptr, brk: ptr) -> void {
|
||||
if nloop < len(cnt_lbl) { cnt_lbl[nloop] = cont brk_lbl[nloop] = brk }
|
||||
else { push(cnt_lbl, cont) push(brk_lbl, brk) }
|
||||
if nloop < len(cnt_lbl) { cnt_lbl[nloop] = cont; brk_lbl[nloop] = brk }
|
||||
else { push(cnt_lbl, cont); push(brk_lbl, brk) }
|
||||
nloop = nloop + 1
|
||||
}
|
||||
fn loop_pop() -> void { nloop = nloop - 1 }
|
||||
|
|
|
|||
|
|
@ -9,43 +9,43 @@ var ui_roots: []int # first-widget index of each ui block
|
|||
|
||||
fn ui_wtype(w: Node) -> int {
|
||||
let s = w.s
|
||||
if streq(s, "panel") { return 0 } if streq(s, "col") { return 1 } if streq(s, "row") { return 2 }
|
||||
if streq(s, "label") { return 3 } if streq(s, "button") { return 4 }
|
||||
if streq(s, "image") { return 5 } if streq(s, "spacer") { return 6 }
|
||||
if streq(s, "panel") { return 0 }; if streq(s, "col") { return 1 }; if streq(s, "row") { return 2 }
|
||||
if streq(s, "label") { return 3 }; if streq(s, "button") { return 4 }
|
||||
if streq(s, "image") { return 5 }; if streq(s, "spacer") { return 6 }
|
||||
return 0
|
||||
}
|
||||
fn ui_prop(w: Node, key: ptr) -> Node {
|
||||
let 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()
|
||||
}
|
||||
fn ui_flatten(w: Node, parent: int) -> void {
|
||||
let idx = len(uiw)
|
||||
push(uiw, w) push(uiw_parent, parent)
|
||||
push(uiw, w); push(uiw_parent, parent)
|
||||
let 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 {
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
fn has_ui() -> bool {
|
||||
let 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
|
||||
}
|
||||
|
||||
# index of a UI_<name>: a ui block's root, or a widget's id=
|
||||
fn ui_index_of(nm: ptr) -> int {
|
||||
let s = substr(nm, 3, slen(nm) - 3) # strip "UI_"
|
||||
let u = 0 let bi = 0
|
||||
let u = 0; let bi = 0
|
||||
let i = 0
|
||||
while i < len(prog) {
|
||||
if prog[i].kind == N_UI and prog[i].ival == 1 { if streq(prog[i].s, s) { return ui_roots[bi] } bi = bi + 1 }
|
||||
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 = 0
|
||||
|
|
@ -61,24 +61,24 @@ fn is_ui_ident(nm: ptr) -> bool {
|
|||
}
|
||||
|
||||
fn ll_ui_set(idx: int, key: int, val: ptr) -> void {
|
||||
emit(" call void @fn_rt_ui_set(i32 ") emit(itoa(idx)) emit(", i32 ") emit(itoa(key)) emit(", i32 ") emit(val) emit(")\n")
|
||||
emit(" call void @fn_rt_ui_set(i32 "); emit(itoa(idx)); emit(", i32 "); emit(itoa(key)); emit(", i32 "); emit(val); emit(")\n")
|
||||
}
|
||||
fn ui_prop_key(k: ptr) -> int {
|
||||
if streq(k,"w") { return 2 } if streq(k,"h") { return 3 } if streq(k,"x") { return 4 } if streq(k,"y") { return 5 }
|
||||
if streq(k,"pad") { return 7 } if streq(k,"gap") { return 8 } if streq(k,"bg") { return 9 } if streq(k,"fg") { return 10 }
|
||||
if streq(k,"border") { return 11 } if streq(k,"grow") { return 13 } if streq(k,"font") { return 14 } if streq(k,"size") { return 15 }
|
||||
if streq(k,"inset") { return 16 } if streq(k,"focus") { return 17 }
|
||||
if streq(k,"w") { return 2 }; if streq(k,"h") { return 3 }; if streq(k,"x") { return 4 }; if streq(k,"y") { return 5 }
|
||||
if streq(k,"pad") { return 7 }; if streq(k,"gap") { return 8 }; if streq(k,"bg") { return 9 }; if streq(k,"fg") { return 10 }
|
||||
if streq(k,"border") { return 11 }; if streq(k,"grow") { return 13 }; if streq(k,"font") { return 14 }; if streq(k,"size") { return 15 }
|
||||
if streq(k,"inset") { return 16 }; if streq(k,"focus") { return 17 }
|
||||
return 0 - 1
|
||||
}
|
||||
|
||||
fn emit_ui_build() -> void {
|
||||
ui_flatten_all()
|
||||
ll_t = 0 ll_lbl = 0 g_term = false loc_reset() nloop = 0
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
|
||||
let fbody = buf_new()
|
||||
falloc = buf_new()
|
||||
let saved = code
|
||||
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
|
||||
while i < len(uiw) {
|
||||
let w = uiw[i]
|
||||
|
|
@ -91,10 +91,10 @@ fn emit_ui_build() -> void {
|
|||
let pr = w.b.kids[p]
|
||||
let k = pr.s
|
||||
let v = pr.a
|
||||
if streq(k, "id") { p = p + 1 continue }
|
||||
if streq(k, "id") { p = p + 1; continue }
|
||||
if streq(k, "text") {
|
||||
let sv = emit_expr(v)
|
||||
emit(" call void @fn_rt_ui_static_text(i32 ") emit(itoa(i)) emit(", ptr ") emit(sv.code) emit(")\n")
|
||||
emit(" call void @fn_rt_ui_static_text(i32 "); emit(itoa(i)); emit(", ptr "); emit(sv.code); emit(")\n")
|
||||
} else { if streq(k, "skin") or streq(k, "image") {
|
||||
let sv = emit_expr(v)
|
||||
let r = emit_bind(sconcat("call i32 @fn_rt_image_load(ptr ", sconcat(sv.code, ")")))
|
||||
|
|
@ -102,9 +102,9 @@ fn emit_ui_build() -> void {
|
|||
} else { if streq(k, "align") {
|
||||
if v.kind == E_ID {
|
||||
let 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))
|
||||
} 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) }
|
||||
} else {
|
||||
let key = ui_prop_key(k)
|
||||
if key >= 0 {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ var toks: []Tok
|
|||
|
||||
fn tok_push(kind: int, text: ptr, ival: int, line: int) -> void {
|
||||
let t = new Tok
|
||||
t.kind = kind t.text = text t.ival = ival t.line = line
|
||||
t.kind = kind; t.text = text; t.ival = ival; t.line = line
|
||||
push(toks, t)
|
||||
}
|
||||
|
||||
|
|
@ -42,8 +42,8 @@ fn lex(src: ptr) -> void {
|
|||
let n = slen(src)
|
||||
while i < n {
|
||||
let c = peek8(src, i)
|
||||
if c == 10 { tok_push(TK_NL, ptr_null(), 0, line) line = line + 1 i = i + 1 continue }
|
||||
if c == 32 or c == 9 or c == 13 { i = i + 1 continue }
|
||||
if c == 10 { tok_push(TK_NL, ptr_null(), 0, line); line = line + 1; i = i + 1; continue }
|
||||
if c == 32 or c == 9 or c == 13 { i = i + 1; continue }
|
||||
if c == 35 { # '#' comment to end of line
|
||||
while i < n and peek8(src, i) != 10 { i = i + 1 }
|
||||
continue
|
||||
|
|
@ -60,8 +60,8 @@ fn lex(src: ptr) -> void {
|
|||
if e == 110 { r = 10 }
|
||||
if e == 116 { r = 9 }
|
||||
if e == 48 { r = 0 }
|
||||
poke8(out, j, r) j = j + 1 i = i + 2
|
||||
} else { poke8(out, j, peek8(src, i)) j = j + 1 i = i + 1 }
|
||||
poke8(out, j, r); j = j + 1; i = i + 2
|
||||
} else { poke8(out, j, peek8(src, i)); j = j + 1; i = i + 1 }
|
||||
}
|
||||
i = i + 1
|
||||
poke8(out, j, 0)
|
||||
|
|
@ -77,7 +77,7 @@ fn lex(src: ptr) -> void {
|
|||
if e == 116 { v = 9 }
|
||||
if e == 48 { v = 0 }
|
||||
i = i + 2
|
||||
} else { v = peek8(src, i) i = i + 1 }
|
||||
} else { v = peek8(src, i); i = i + 1 }
|
||||
if peek8(src, i) == 39 { i = i + 1 }
|
||||
tok_push(TK_INT, ptr_null(), v, line)
|
||||
continue
|
||||
|
|
@ -99,12 +99,12 @@ fn lex(src: ptr) -> void {
|
|||
continue
|
||||
}
|
||||
let 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
|
||||
if i < n and peek8(src, i) == 46 and char_is_digit(peek8(src, i + 1)) {
|
||||
i = i + 1
|
||||
let fnum = 0 let fden = 1
|
||||
while i < n and char_is_digit(peek8(src, i)) { fnum = fnum * 10 + (peek8(src, i) - 48) fden = fden * 10 i = i + 1 }
|
||||
let fnum = 0; let fden = 1
|
||||
while i < n and char_is_digit(peek8(src, i)) { fnum = fnum * 10 + (peek8(src, i) - 48); fden = fden * 10; i = i + 1 }
|
||||
let bits = shl(v, 16) + shl(fnum, 16) / fden
|
||||
tok_push(TK_FLOAT, ptr_null(), bits, line)
|
||||
continue
|
||||
|
|
@ -118,20 +118,20 @@ fn lex(src: ptr) -> void {
|
|||
tok_push(TK_ID, substr(src, start, i - start), 0, line)
|
||||
continue
|
||||
}
|
||||
if c == 59 { tok_push(TK_NL, ptr_null(), 0, line) i = i + 1 continue } # ';'
|
||||
if c == 59 { tok_push(TK_NL, ptr_null(), 0, line); i = i + 1; continue } # ';'
|
||||
# two-character operators
|
||||
if two_at(src, i, 45, 62) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # ->
|
||||
if two_at(src, i, 61, 62) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # =>
|
||||
if two_at(src, i, 61, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # ==
|
||||
if two_at(src, i, 33, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # !=
|
||||
if two_at(src, i, 60, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # <=
|
||||
if two_at(src, i, 62, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # >=
|
||||
if two_at(src, i, 43, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # +=
|
||||
if two_at(src, i, 45, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # -=
|
||||
if two_at(src, i, 42, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # *=
|
||||
if two_at(src, i, 47, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # /=
|
||||
if two_at(src, i, 46, 46) { tok_push(TK_OP, substr(src, i, 2), 0, line) i = i + 2 continue } # ..
|
||||
if is_op1(c) { tok_push(TK_OP, substr(src, i, 1), 0, line) i = i + 1 continue }
|
||||
if two_at(src, i, 45, 62) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # ->
|
||||
if two_at(src, i, 61, 62) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # =>
|
||||
if two_at(src, i, 61, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # ==
|
||||
if two_at(src, i, 33, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # !=
|
||||
if two_at(src, i, 60, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # <=
|
||||
if two_at(src, i, 62, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # >=
|
||||
if two_at(src, i, 43, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # +=
|
||||
if two_at(src, i, 45, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # -=
|
||||
if two_at(src, i, 42, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # *=
|
||||
if two_at(src, i, 47, 61) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # /=
|
||||
if two_at(src, i, 46, 46) { tok_push(TK_OP, substr(src, i, 2), 0, line); i = i + 2; continue } # ..
|
||||
if is_op1(c) { tok_push(TK_OP, substr(src, i, 1), 0, line); i = i + 1; continue }
|
||||
i = i + 1 # skip anything unrecognised
|
||||
}
|
||||
tok_push(TK_EOF, ptr_null(), 0, line)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,7 +18,7 @@
|
|||
fn base_name(path: ptr) -> ptr {
|
||||
let last = 0 - 1
|
||||
let 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))
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ main {
|
|||
else { if streq(a, "--fmt") { fmt = true }
|
||||
else { if streq(a, "--save-temps") { save = true }
|
||||
else { if streq(a, "--run") { run = true }
|
||||
else { if streq(a, "-o") { ai = ai + 1 if ai < os_argc() { out = os_arg(ai) } }
|
||||
else { if streq(a, "-o") { ai = ai + 1; if ai < os_argc() { out = os_arg(ai) } }
|
||||
else {
|
||||
# an unknown flag is ignored (with a note) rather than mistaken for the
|
||||
# input file — '-' is ASCII 45
|
||||
|
|
@ -112,8 +112,8 @@ main {
|
|||
emit_program()
|
||||
|
||||
# --emit-llvm, or no binary target: emit IR and stop (stdout when out is null).
|
||||
if emit_ir { ir_flush(out) return }
|
||||
if not has_target { ir_flush(ptr_null()) return }
|
||||
if emit_ir { ir_flush(out); return }
|
||||
if not has_target { ir_flush(ptr_null()); return }
|
||||
|
||||
# a run with no explicit -o lands in a temp file
|
||||
if run and ptr_is_null(out) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ var g_game_name: ptr # the `game`/`module` name
|
|||
|
||||
fn cur() -> Tok { return toks[pi] }
|
||||
fn pk(o: int) -> Tok { return toks[pi + o] }
|
||||
fn is_op(v: ptr) -> bool { let t = toks[pi] return t.kind == TK_OP and streq(t.text, v) }
|
||||
fn is_id(v: ptr) -> bool { let t = toks[pi] return t.kind == TK_ID and streq(t.text, v) }
|
||||
fn is_op(v: ptr) -> bool { let t = toks[pi]; return t.kind == TK_OP and streq(t.text, v) }
|
||||
fn is_id(v: ptr) -> bool { let t = toks[pi]; return t.kind == TK_ID and streq(t.text, v) }
|
||||
fn is_kw(v: ptr) -> bool { return is_id(v) }
|
||||
|
||||
fn perr(msg: ptr) -> void {
|
||||
|
|
@ -20,7 +20,7 @@ fn perr(msg: ptr) -> void {
|
|||
os_exit(1)
|
||||
}
|
||||
|
||||
fn eat_op(v: ptr) -> void { if not is_op(v) { perr(v) } pi = pi + 1 }
|
||||
fn eat_op(v: ptr) -> void { if not is_op(v) { perr(v) }; pi = pi + 1 }
|
||||
fn eat_id() -> ptr {
|
||||
let t = toks[pi]
|
||||
if t.kind != TK_ID { perr("expected identifier") }
|
||||
|
|
@ -36,9 +36,9 @@ fn ptype() -> ptr {
|
|||
eat_op("]")
|
||||
let el = ptype()
|
||||
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
|
||||
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)
|
||||
return out
|
||||
}
|
||||
|
|
@ -49,23 +49,23 @@ fn ptype() -> ptr {
|
|||
fn expr() -> Node { return p_or() }
|
||||
|
||||
fn args_call(call: Node) -> void {
|
||||
eat_op("(") skipnl()
|
||||
while not is_op(")") { push(call.kids, expr()) skipnl() if is_op(",") { pi = pi + 1 skipnl() } }
|
||||
eat_op("("); skipnl()
|
||||
while not is_op(")") { push(call.kids, expr()); skipnl(); if is_op(",") { pi = pi + 1; skipnl() } }
|
||||
eat_op(")")
|
||||
}
|
||||
|
||||
fn p_primary() -> Node {
|
||||
let t = toks[pi]
|
||||
if t.kind == TK_INT { let n = node(E_INT) n.ival = t.ival pi = pi + 1 return n }
|
||||
if t.kind == TK_FLOAT { let n = node(E_FLOAT) n.ival = t.ival pi = pi + 1 return n }
|
||||
if t.kind == TK_STR { let n = node(E_STR) n.s = t.text pi = pi + 1 return n }
|
||||
if t.kind == TK_INT { let n = node(E_INT); n.ival = t.ival; pi = pi + 1; return n }
|
||||
if t.kind == TK_FLOAT { let n = node(E_FLOAT); n.ival = t.ival; pi = pi + 1; return n }
|
||||
if t.kind == TK_STR { let n = node(E_STR); n.s = t.text; pi = pi + 1; return n }
|
||||
if t.kind == TK_ID {
|
||||
if streq(t.text, "true") { let n = node(E_BOOL) n.ival = 1 pi = pi + 1 return n }
|
||||
if streq(t.text, "false") { let n = node(E_BOOL) n.ival = 0 pi = pi + 1 return n }
|
||||
if streq(t.text, "new") { pi = pi + 1 let n = node(E_NEW) n.s = ptype() return n }
|
||||
let n = node(E_ID) n.s = t.text pi = pi + 1 return n
|
||||
if streq(t.text, "true") { let n = node(E_BOOL); n.ival = 1; pi = pi + 1; return n }
|
||||
if streq(t.text, "false") { let n = node(E_BOOL); n.ival = 0; pi = pi + 1; return n }
|
||||
if streq(t.text, "new") { pi = pi + 1; let n = node(E_NEW); n.s = ptype(); return n }
|
||||
let n = node(E_ID); n.s = t.text; pi = pi + 1; return n
|
||||
}
|
||||
if is_op("(") { pi = pi + 1 skipnl() let e = expr() skipnl() eat_op(")") return e }
|
||||
if is_op("(") { pi = pi + 1; skipnl(); let e = expr(); skipnl(); eat_op(")"); return e }
|
||||
perr("expected expression")
|
||||
return node(E_INT)
|
||||
}
|
||||
|
|
@ -73,46 +73,46 @@ fn p_primary() -> Node {
|
|||
fn p_postfix() -> Node {
|
||||
let e = p_primary()
|
||||
while true {
|
||||
if is_op(".") { pi = pi + 1 let m = node(E_MEMBER) m.a = e m.s = eat_id() e = m }
|
||||
else { if is_op("[") { pi = pi + 1 let ix = node(E_INDEX) ix.a = e ix.b = expr() eat_op("]") e = ix }
|
||||
else { if is_op("(") { let c = node(E_CALL) c.a = e args_call(c) e = c } else { break } } }
|
||||
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("(") { let c = node(E_CALL); c.a = e; args_call(c); e = c } else { break } } }
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn p_unary() -> Node {
|
||||
if is_op("-") { pi = pi + 1 let n = node(E_UN) n.s = "-" n.a = p_unary() return n }
|
||||
if is_id("not") { pi = pi + 1 let n = node(E_UN) n.s = "not" n.a = p_unary() return n }
|
||||
if is_op("-") { pi = pi + 1; let n = node(E_UN); n.s = "-"; n.a = p_unary(); return n }
|
||||
if is_id("not") { pi = pi + 1; let n = node(E_UN); n.s = "not"; n.a = p_unary(); return n }
|
||||
return p_postfix()
|
||||
}
|
||||
|
||||
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 {
|
||||
let 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
|
||||
}
|
||||
fn p_add() -> Node {
|
||||
let 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
|
||||
}
|
||||
fn p_cmp() -> Node {
|
||||
let l = p_add()
|
||||
while is_op("<") or is_op("<=") or is_op(">") or is_op(">=") or is_op("==") or is_op("!=") {
|
||||
let op = toks[pi].text pi = pi + 1 l = mkbin(op, l, p_add())
|
||||
let op = toks[pi].text; pi = pi + 1; l = mkbin(op, l, p_add())
|
||||
}
|
||||
return l
|
||||
}
|
||||
fn p_and() -> Node {
|
||||
let 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
|
||||
}
|
||||
fn p_or() -> Node {
|
||||
let 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
|
||||
}
|
||||
|
||||
|
|
@ -120,17 +120,30 @@ fn p_or() -> Node {
|
|||
fn record() -> Node {
|
||||
eat_op("{")
|
||||
let r = node(E_REC)
|
||||
while true { skipnl() if is_op("}") { break }
|
||||
let fi = node(E_FINIT) fi.s = eat_id() eat_op("=") fi.a = expr() push(r.kids, fi)
|
||||
while true { skipnl(); if is_op("}") { break }
|
||||
let fi = node(E_FINIT); fi.s = eat_id(); eat_op("="); fi.a = expr(); push(r.kids, fi)
|
||||
if is_op(",") { pi = pi + 1 } }
|
||||
eat_op("}") return r
|
||||
eat_op("}"); return r
|
||||
}
|
||||
|
||||
# ---- statements ------------------------------------------------------------
|
||||
fn block() -> Node {
|
||||
skipnl() eat_op("{")
|
||||
skipnl(); eat_op("{")
|
||||
let b = node(N_BLOCK)
|
||||
while true { skipnl() if is_op("}") { break } push(b.kids, stmt()) }
|
||||
while true { skipnl(); if is_op("}") { break }
|
||||
push(b.kids, stmt())
|
||||
# Rule B: statements are separated by a newline or ';' (both lex to TK_NL).
|
||||
# After a statement the next token must be that separator or the block's end —
|
||||
# two statements may not sit adjacent with only spaces between them.
|
||||
let sep = toks[pi].kind == TK_NL
|
||||
if is_op("}") { sep = true }
|
||||
if not sep {
|
||||
let ez = file_stderr(); file_write(ez, "cur=", 4)
|
||||
if not ptr_is_null(toks[pi].text) { file_write(ez, toks[pi].text, slen(toks[pi].text)) }
|
||||
file_write(ez, "\n", 1); print_int(7770000 + toks[pi].line)
|
||||
perr("expected newline or ';' between statements")
|
||||
}
|
||||
}
|
||||
eat_op("}")
|
||||
return b
|
||||
}
|
||||
|
|
@ -139,106 +152,108 @@ fn stmt() -> Node {
|
|||
let t = toks[pi]
|
||||
if t.kind == TK_ID {
|
||||
if streq(t.text, "let") {
|
||||
pi = pi + 1 let n = node(S_LET) n.s = eat_id()
|
||||
if is_op(":") { pi = pi + 1 n.ty = ptype() }
|
||||
if is_op("=") { pi = pi + 1 n.a = expr() }
|
||||
pi = pi + 1; let n = node(S_LET); n.s = eat_id()
|
||||
if is_op(":") { pi = pi + 1; n.ty = ptype() }
|
||||
if is_op("=") { pi = pi + 1; n.a = expr() }
|
||||
return n
|
||||
}
|
||||
if streq(t.text, "return") {
|
||||
pi = pi + 1 let n = node(S_RETURN)
|
||||
pi = pi + 1; let n = node(S_RETURN)
|
||||
if toks[pi].kind != TK_NL and not is_op("}") { n.a = expr() }
|
||||
return n
|
||||
}
|
||||
if streq(t.text, "if") {
|
||||
pi = pi + 1 let n = node(S_IF) n.a = expr() n.b = block() skipnl()
|
||||
if is_id("else") { pi = pi + 1 skipnl()
|
||||
pi = pi + 1; let n = node(S_IF); n.a = expr(); n.b = block()
|
||||
let save = pi; skipnl() # peek past newlines for a trailing `else`
|
||||
if is_id("else") { pi = pi + 1; skipnl()
|
||||
if is_id("if") { n.c = stmt() } else { n.c = block() } }
|
||||
else { pi = save } # no else: keep the separator for block()'s Rule-B check
|
||||
return n
|
||||
}
|
||||
if streq(t.text, "when") { # `when c { }` — an if with no else
|
||||
pi = pi + 1 let n = node(S_IF) n.a = expr() n.b = block()
|
||||
pi = pi + 1; let n = node(S_IF); n.a = expr(); n.b = block()
|
||||
return n
|
||||
}
|
||||
if streq(t.text, "while") { pi = pi + 1 let n = node(S_WHILE) n.a = expr() n.b = block() return n }
|
||||
if streq(t.text, "while") { pi = pi + 1; let n = node(S_WHILE); n.a = expr(); n.b = block(); return n }
|
||||
if streq(t.text, "for") {
|
||||
pi = pi + 1
|
||||
if is_op("(") { return parse_query_for() }
|
||||
let n = node(S_FOR) n.s = eat_id()
|
||||
let n = node(S_FOR); n.s = eat_id()
|
||||
let inkw = eat_id() # 'in'
|
||||
n.a = expr() eat_op("..") n.b = expr() n.c = block()
|
||||
n.a = expr(); eat_op(".."); n.b = expr(); n.c = block()
|
||||
return n
|
||||
}
|
||||
if streq(t.text, "spawn") { return parse_spawn() }
|
||||
if streq(t.text, "machine") {
|
||||
pi = pi + 1 let n = node(S_MACHINE) n.a = expr() skipnl() eat_op("{")
|
||||
while true { skipnl() if is_op("}") { break }
|
||||
pi = pi + 1; let n = node(S_MACHINE); n.a = expr(); skipnl(); eat_op("{")
|
||||
while true { skipnl(); if is_op("}") { break }
|
||||
let stkw = eat_id() # 'state'
|
||||
let s = node(S_STATE) s.s = eat_id() eat_op("=") s.b = expr() skipnl() s.a = block()
|
||||
let s = node(S_STATE); s.s = eat_id(); eat_op("="); s.b = expr(); skipnl(); s.a = block()
|
||||
push(n.kids, s) }
|
||||
eat_op("}") return n
|
||||
eat_op("}"); 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, "enter") { pi = pi + 1 let n = node(S_BECOME) n.s = eat_id() n.ival = 1 return n }
|
||||
if streq(t.text, "despawn") { pi = pi + 1 let n = node(S_DESPAWN) n.a = expr() return n }
|
||||
if streq(t.text, "break") { pi = pi + 1 return node(S_BREAK) }
|
||||
if streq(t.text, "continue") { pi = pi + 1 return node(S_CONTINUE) }
|
||||
if streq(t.text, "become") { pi = pi + 1; let n = node(S_BECOME); n.s = eat_id(); return n }
|
||||
if streq(t.text, "enter") { pi = pi + 1; let n = node(S_BECOME); n.s = eat_id(); n.ival = 1; return n }
|
||||
if streq(t.text, "despawn") { pi = pi + 1; let n = node(S_DESPAWN); n.a = expr(); return n }
|
||||
if streq(t.text, "break") { pi = pi + 1; return node(S_BREAK) }
|
||||
if streq(t.text, "continue") { pi = pi + 1; return node(S_CONTINUE) }
|
||||
if streq(t.text, "match") {
|
||||
pi = pi + 1 let n = node(S_MATCH) n.a = expr() skipnl() eat_op("{")
|
||||
pi = pi + 1; let n = node(S_MATCH); n.a = expr(); skipnl(); eat_op("{")
|
||||
while true {
|
||||
skipnl() if is_op("}") { break }
|
||||
skipnl(); if is_op("}") { break }
|
||||
let arm = node(S_MARM)
|
||||
while true { push(arm.kids, expr()) if is_op(",") { pi = pi + 1 skipnl() continue } break }
|
||||
eat_op("=>") skipnl()
|
||||
if is_op("{") { arm.a = block() } else { let b = node(N_BLOCK) push(b.kids, stmt()) arm.a = b }
|
||||
while true { push(arm.kids, expr()); if is_op(",") { pi = pi + 1; skipnl(); continue }; break }
|
||||
eat_op("=>"); skipnl()
|
||||
if is_op("{") { arm.a = block() } else { let b = node(N_BLOCK); push(b.kids, stmt()); arm.a = b }
|
||||
push(n.kids, arm)
|
||||
}
|
||||
eat_op("}") return n
|
||||
eat_op("}"); return n
|
||||
}
|
||||
}
|
||||
let e = expr()
|
||||
if is_op("=") or is_op("+=") or is_op("-=") or is_op("*=") or is_op("/=") {
|
||||
let n = node(S_ASSIGN) n.s = toks[pi].text pi = pi + 1 n.a = e n.b = expr() return n
|
||||
let n = node(S_ASSIGN); n.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
|
||||
}
|
||||
|
||||
# ---- declarations ----------------------------------------------------------
|
||||
fn parse_struct() -> Node {
|
||||
pi = pi + 1 let n = node(N_STRUCT) n.s = eat_id() skipnl() eat_op("{")
|
||||
while true { skipnl() if is_op("}") { break }
|
||||
let f = node(N_FIELD) f.s = eat_id() eat_op(":") f.ty = ptype()
|
||||
if is_op("=") { pi = pi + 1 f.a = expr() }
|
||||
push(n.kids, f) if is_op(",") { pi = pi + 1 } }
|
||||
eat_op("}") return n
|
||||
pi = pi + 1; let n = node(N_STRUCT); n.s = eat_id(); skipnl(); eat_op("{")
|
||||
while true { skipnl(); if is_op("}") { break }
|
||||
let f = node(N_FIELD); f.s = eat_id(); eat_op(":"); f.ty = ptype()
|
||||
if is_op("=") { pi = pi + 1; f.a = expr() }
|
||||
push(n.kids, f); if is_op(",") { pi = pi + 1 } }
|
||||
eat_op("}"); return n
|
||||
}
|
||||
fn parse_var() -> Node {
|
||||
pi = pi + 1 let n = node(N_VAR) n.s = eat_id() eat_op(":") n.ty = ptype()
|
||||
if is_op("=") { pi = pi + 1 n.a = expr() }
|
||||
pi = pi + 1; let n = node(N_VAR); n.s = eat_id(); eat_op(":"); n.ty = ptype()
|
||||
if is_op("=") { pi = pi + 1; n.a = expr() }
|
||||
return n
|
||||
}
|
||||
fn parse_const() -> Node {
|
||||
pi = pi + 1 let n = node(N_CONST) n.s = eat_id() eat_op(":") n.ty = ptype() eat_op("=") n.a = expr()
|
||||
pi = pi + 1; let n = node(N_CONST); n.s = eat_id(); eat_op(":"); n.ty = ptype(); eat_op("="); n.a = expr()
|
||||
return n
|
||||
}
|
||||
fn parse_fn() -> Node {
|
||||
pi = pi + 1 let n = node(N_FN) n.s = eat_id() eat_op("(")
|
||||
pi = pi + 1; let n = node(N_FN); n.s = eat_id(); eat_op("(")
|
||||
while not is_op(")") {
|
||||
let p = node(N_PARAM) p.s = eat_id() eat_op(":") p.ty = ptype() push(n.kids, p)
|
||||
let p = node(N_PARAM); p.s = eat_id(); eat_op(":"); p.ty = ptype(); push(n.kids, p)
|
||||
if is_op(",") { pi = pi + 1 }
|
||||
}
|
||||
eat_op(")")
|
||||
n.ty = "void"
|
||||
if is_op("->") { pi = pi + 1 n.ty = ptype() }
|
||||
if is_op("->") { pi = pi + 1; n.ty = ptype() }
|
||||
n.a = block()
|
||||
return n
|
||||
}
|
||||
fn parse_main() -> Node { pi = pi + 1 let n = node(N_MAIN) n.a = block() return n }
|
||||
fn parse_main() -> Node { pi = pi + 1; let n = node(N_MAIN); n.a = block(); return n }
|
||||
|
||||
# directory part of a path, including the trailing '/', or "" if none
|
||||
fn dir_of(path: ptr) -> ptr {
|
||||
let last = 0 - 1
|
||||
let 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 "" }
|
||||
return substr(path, 0, last + 1)
|
||||
}
|
||||
|
|
@ -252,7 +267,7 @@ var cur_dir: ptr
|
|||
|
||||
fn already_loaded(full: ptr) -> bool {
|
||||
let 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
|
||||
}
|
||||
|
||||
|
|
@ -261,21 +276,21 @@ fn parse_one_decl() -> void {
|
|||
if is_id("import") { pi = pi + 1
|
||||
let t = toks[pi]
|
||||
if t.kind != TK_STR { perr("expected \"path\" after import") }
|
||||
let rel = t.text pi = pi + 1
|
||||
let rel = t.text; pi = pi + 1
|
||||
do_import(rel)
|
||||
return
|
||||
}
|
||||
if is_id("struct") { push(prog, parse_struct()) return }
|
||||
if is_id("component") { push(prog, parse_component()) return }
|
||||
if is_id("archetype") { push(prog, parse_archetype()) return }
|
||||
if is_id("system") or is_id("edge") { push(prog, parse_system()) return }
|
||||
if is_id("ui") { push(prog, parse_ui()) return }
|
||||
if is_id("var") { push(prog, parse_var()) return }
|
||||
if is_id("const") { push(prog, parse_const()) return }
|
||||
if is_id("export") { pi = pi + 1 let f = parse_fn() f.ival = 1 push(prog, f) return }
|
||||
if is_id("fn") or is_id("pure") { if is_id("pure") { pi = pi + 1 } push(prog, parse_fn()) return }
|
||||
if is_id("extern") { push(prog, parse_extern()) return }
|
||||
if is_id("main") { push(prog, parse_main()) return }
|
||||
if is_id("struct") { push(prog, parse_struct()); return }
|
||||
if is_id("component") { push(prog, parse_component()); return }
|
||||
if is_id("archetype") { push(prog, parse_archetype()); return }
|
||||
if is_id("system") or is_id("edge") { push(prog, parse_system()); return }
|
||||
if is_id("ui") { push(prog, parse_ui()); return }
|
||||
if is_id("var") { push(prog, parse_var()); return }
|
||||
if is_id("const") { push(prog, parse_const()); return }
|
||||
if is_id("export") { pi = pi + 1; let f = parse_fn(); f.ival = 1; push(prog, f); return }
|
||||
if is_id("fn") or is_id("pure") { if is_id("pure") { pi = pi + 1 }; push(prog, parse_fn()); return }
|
||||
if is_id("extern") { push(prog, parse_extern()); return }
|
||||
if is_id("main") { push(prog, parse_main()); return }
|
||||
perr("expected declaration")
|
||||
}
|
||||
|
||||
|
|
@ -286,13 +301,13 @@ fn do_import(rel: ptr) -> void {
|
|||
push(loaded_paths, full)
|
||||
let src = read_file(full)
|
||||
if ptr_is_null(src) { perr(sconcat("cannot open import ", full)) }
|
||||
let saved_toks = toks let saved_pi = pi let saved_dir = cur_dir
|
||||
let saved_toks = toks; let saved_pi = pi; let saved_dir = cur_dir
|
||||
cur_dir = dir_of(full)
|
||||
lex(src) # resets the global token stream
|
||||
pi = 0
|
||||
skipnl()
|
||||
while toks[pi].kind != TK_EOF { parse_one_decl() skipnl() }
|
||||
toks = saved_toks pi = saved_pi cur_dir = saved_dir
|
||||
while toks[pi].kind != TK_EOF { parse_one_decl(); skipnl() }
|
||||
toks = saved_toks; pi = saved_pi; cur_dir = saved_dir
|
||||
}
|
||||
|
||||
# a game (has systems/components) links the Ludic runtime; auto-splice it the
|
||||
|
|
@ -311,8 +326,8 @@ fn parse_program() -> void {
|
|||
skipnl()
|
||||
g_game_name = "Ludic"
|
||||
# imports may precede the game block
|
||||
while is_id("import") { pi = pi + 1 let t = toks[pi] let rel = t.text pi = pi + 1 do_import(rel) skipnl() }
|
||||
if is_id("game") or is_id("module") { pi = pi + 1 g_game_name = eat_id() skipnl() eat_op("{") }
|
||||
while is_id("import") { pi = pi + 1; let t = toks[pi]; let rel = t.text; pi = pi + 1; do_import(rel); skipnl() }
|
||||
if is_id("game") or is_id("module") { pi = pi + 1; g_game_name = eat_id(); skipnl(); eat_op("{") }
|
||||
while true {
|
||||
skipnl()
|
||||
if toks[pi].kind == TK_EOF { break }
|
||||
|
|
|
|||
|
|
@ -3,25 +3,25 @@
|
|||
# game-construct parsing in compiler/front/parse.c.
|
||||
|
||||
fn parse_component() -> Node {
|
||||
pi = pi + 1 let n = node(N_COMP) n.s = eat_id() skipnl() eat_op("{")
|
||||
while true { skipnl() if is_op("}") { break }
|
||||
let f = node(N_FIELD) f.s = eat_id() eat_op(":") f.ty = ptype()
|
||||
if is_op("=") { pi = pi + 1 f.a = expr() }
|
||||
push(n.kids, f) if is_op(",") { pi = pi + 1 } }
|
||||
eat_op("}") return n
|
||||
pi = pi + 1; let n = node(N_COMP); n.s = eat_id(); skipnl(); eat_op("{")
|
||||
while true { skipnl(); if is_op("}") { break }
|
||||
let f = node(N_FIELD); f.s = eat_id(); eat_op(":"); f.ty = ptype()
|
||||
if is_op("=") { pi = pi + 1; f.a = expr() }
|
||||
push(n.kids, f); if is_op(",") { pi = pi + 1 } }
|
||||
eat_op("}"); return n
|
||||
}
|
||||
|
||||
# skip an @annotation or a reads/writes/query/... clause we don't model yet
|
||||
fn skip_clause() -> void {
|
||||
if is_op("[") { let depth = 0
|
||||
while true { if is_op("[") { depth = depth + 1 } if is_op("]") { depth = depth - 1 }
|
||||
pi = pi + 1 if depth == 0 { break } }
|
||||
while true { if is_op("[") { depth = depth + 1 }; if is_op("]") { depth = depth - 1 }
|
||||
pi = pi + 1; if depth == 0 { break } }
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_system() -> Node {
|
||||
if is_id("edge") { pi = pi + 1 } # optional `edge` modifier before `system`
|
||||
pi = pi + 1 let n = node(N_SYS) n.s = eat_id() n.ty = "Update"
|
||||
pi = pi + 1; let n = node(N_SYS); n.s = eat_id(); n.ty = "Update"
|
||||
# clauses: @anno, phase X, reads/writes/needs/uses [..], query (..) [..]
|
||||
# A `query (vars) [terms] where c` clause desugars to a body wrapped in one
|
||||
# `for (vars) in query [terms] where c { ... }` — the same S_QUERY node the
|
||||
|
|
@ -30,15 +30,15 @@ fn parse_system() -> Node {
|
|||
let qn = node(S_QUERY)
|
||||
while true {
|
||||
skipnl() # clauses may span several lines
|
||||
if is_op("@") { pi = pi + 1 let a = eat_id() if is_op("(") { let d = 0 # @anno, one per turn so a
|
||||
while true { if is_op("(") { d = d + 1 } if is_op(")") { d = d - 1 } pi = pi + 1 if d == 0 { break } } }
|
||||
if is_op("@") { pi = pi + 1; let a = eat_id(); if is_op("(") { let d = 0 # @anno, one per turn so a
|
||||
while true { if is_op("(") { d = d + 1 }; if is_op(")") { d = d - 1 }; pi = pi + 1; if d == 0 { break } } }
|
||||
continue } # newline-separated @anno re-skips at the loop top
|
||||
if is_id("phase") { pi = pi + 1 n.ty = eat_id() continue }
|
||||
if is_id("reads") or is_id("writes") or is_id("needs") or is_id("uses") { pi = pi + 1 skip_clause() continue }
|
||||
if is_id("phase") { pi = pi + 1; n.ty = eat_id(); continue }
|
||||
if is_id("reads") or is_id("writes") or is_id("needs") or is_id("uses") { pi = pi + 1; skip_clause(); continue }
|
||||
if is_id("query") {
|
||||
pi = pi + 1 has_q = true
|
||||
pi = pi + 1; has_q = true
|
||||
if is_op("(") { eat_op("(") # optional (vars); omitted when nothing binds
|
||||
while not is_op(")") { let v = node(E_ID) v.s = eat_id() push(qn.kids, v) if is_op(",") { pi = pi + 1 } }
|
||||
while not is_op(")") { let v = node(E_ID); v.s = eat_id(); push(qn.kids, v); if is_op(",") { pi = pi + 1 } }
|
||||
eat_op(")") }
|
||||
qn.c = parse_query_tail()
|
||||
qn.b = qn.c.a # where-expr (or null)
|
||||
|
|
@ -50,7 +50,7 @@ fn parse_system() -> Node {
|
|||
let body = block()
|
||||
if has_q {
|
||||
qn.a = body
|
||||
let wrap = node(N_BLOCK) push(wrap.kids, qn)
|
||||
let wrap = node(N_BLOCK); push(wrap.kids, qn)
|
||||
n.a = wrap
|
||||
} else { n.a = body }
|
||||
return n
|
||||
|
|
@ -62,12 +62,12 @@ fn parse_query_tail() -> Node {
|
|||
eat_op("[")
|
||||
let q = node(N_BLOCK)
|
||||
while not is_op("]") {
|
||||
if is_op("{") { pi = pi + 1 let t = node(E_ID) t.s = eat_id() t.ival = 1 push(q.kids, t) eat_op("}") }
|
||||
else { let t = node(E_ID) t.s = eat_id() t.ival = 0 push(q.kids, t) }
|
||||
if is_op("{") { pi = pi + 1; let t = node(E_ID); t.s = eat_id(); t.ival = 1; push(q.kids, t); eat_op("}") }
|
||||
else { let t = node(E_ID); t.s = eat_id(); t.ival = 0; push(q.kids, t) }
|
||||
if is_op(",") { pi = pi + 1 }
|
||||
}
|
||||
eat_op("]")
|
||||
if is_id("where") { pi = pi + 1 q.a = expr() }
|
||||
if is_id("where") { pi = pi + 1; q.a = expr() }
|
||||
return q
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ fn parse_query_tail() -> Node {
|
|||
fn parse_query_for() -> Node {
|
||||
let n = node(S_QUERY)
|
||||
eat_op("(")
|
||||
while not is_op(")") { let v = node(E_ID) v.s = eat_id() push(n.kids, v) if is_op(",") { pi = pi + 1 } }
|
||||
while not is_op(")") { let v = node(E_ID); v.s = eat_id(); push(n.kids, v); if is_op(",") { pi = pi + 1 } }
|
||||
eat_op(")")
|
||||
let inkw = eat_id() # 'in'
|
||||
let qkw = eat_id() # 'query'
|
||||
|
|
@ -86,59 +86,59 @@ fn parse_query_for() -> Node {
|
|||
}
|
||||
|
||||
fn parse_spawn() -> Node {
|
||||
pi = pi + 1 let n = node(S_SPAWN) n.s = eat_id() skipnl() eat_op("{")
|
||||
pi = pi + 1; let n = node(S_SPAWN); n.s = eat_id(); skipnl(); eat_op("{")
|
||||
while true {
|
||||
skipnl() if is_op("}") { break }
|
||||
let ci = node(E_FINIT) ci.s = eat_id() eat_op("=")
|
||||
skipnl(); if is_op("}") { break }
|
||||
let ci = node(E_FINIT); ci.s = eat_id(); eat_op("=")
|
||||
ci.a = record() # { field = val, ... }
|
||||
push(n.kids, ci)
|
||||
if is_op(",") { pi = pi + 1 }
|
||||
}
|
||||
eat_op("}") return n
|
||||
eat_op("}"); return n
|
||||
}
|
||||
|
||||
# archetype Name { CompA, CompB } — a named entity kind (bundle of components)
|
||||
fn parse_archetype() -> Node {
|
||||
pi = pi + 1 let n = node(N_ARCH) n.s = eat_id() skipnl() eat_op("{")
|
||||
while true { skipnl() if is_op("}") { break }
|
||||
let c = node(E_ID) c.s = eat_id() push(n.kids, c)
|
||||
if is_op(",") { pi = pi + 1 } skipnl() }
|
||||
eat_op("}") return n
|
||||
pi = pi + 1; let n = node(N_ARCH); n.s = eat_id(); skipnl(); eat_op("{")
|
||||
while true { skipnl(); if is_op("}") { break }
|
||||
let c = node(E_ID); c.s = eat_id(); push(n.kids, c)
|
||||
if is_op(",") { pi = pi + 1 }; skipnl() }
|
||||
eat_op("}"); return n
|
||||
}
|
||||
|
||||
# extern fn name(params) -> T = "symbol"
|
||||
fn parse_extern() -> Node {
|
||||
pi = pi + 1 # 'extern'
|
||||
let fnkw = eat_id() # 'fn'
|
||||
let n = node(N_EXTERN) n.s = eat_id() eat_op("(")
|
||||
while not is_op(")") { let p = node(N_PARAM) p.s = eat_id() eat_op(":") p.ty = ptype() push(n.kids, p)
|
||||
let n = node(N_EXTERN); n.s = eat_id(); eat_op("(")
|
||||
while not is_op(")") { let p = node(N_PARAM); p.s = eat_id(); eat_op(":"); p.ty = ptype(); push(n.kids, p)
|
||||
if is_op(",") { pi = pi + 1 } }
|
||||
eat_op(")")
|
||||
n.ty = "void"
|
||||
if is_op("->") { pi = pi + 1 n.ty = ptype() }
|
||||
if is_op("->") { pi = pi + 1; n.ty = ptype() }
|
||||
eat_op("=")
|
||||
let t = toks[pi] # "symbol"
|
||||
n.a = node(E_STR) n.a.s = t.text pi = pi + 1
|
||||
n.a = node(E_STR); n.a.s = t.text; pi = pi + 1
|
||||
return n
|
||||
}
|
||||
|
||||
# ui Name { widget-tree } — parsed into a widget node tree (emitted later)
|
||||
fn parse_widget() -> Node {
|
||||
let w = node(N_UI) w.s = eat_id() # widget type name
|
||||
let w = node(N_UI); w.s = eat_id() # widget type name
|
||||
w.b = node(N_BLOCK) # b.kids = props (E_FINIT)
|
||||
while toks[pi].kind == TK_ID and toks[pi + 1].kind == TK_OP and streq(toks[pi + 1].text, "=") {
|
||||
let pr = node(E_FINIT) pr.s = eat_id() eat_op("=") pr.a = expr() push(w.b.kids, pr)
|
||||
let pr = node(E_FINIT); pr.s = eat_id(); eat_op("="); pr.a = expr(); push(w.b.kids, pr)
|
||||
}
|
||||
skipnl()
|
||||
if is_op("{") { pi = pi + 1
|
||||
while true { skipnl() if is_op("}") { break } push(w.kids, parse_widget()) }
|
||||
while true { skipnl(); if is_op("}") { break }; push(w.kids, parse_widget()) }
|
||||
eat_op("}") }
|
||||
return w
|
||||
}
|
||||
fn parse_ui() -> Node {
|
||||
pi = pi + 1 let n = node(N_UI) n.s = eat_id() n.ival = 1 # ival=1 marks the top ui block
|
||||
skipnl() eat_op("{") skipnl()
|
||||
pi = pi + 1; let n = node(N_UI); n.s = eat_id(); n.ival = 1 # ival=1 marks the top ui block
|
||||
skipnl(); eat_op("{"); skipnl()
|
||||
n.a = parse_widget()
|
||||
skipnl() eat_op("}")
|
||||
skipnl(); eat_op("}")
|
||||
return n
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ fn slen(s: ptr) -> int {
|
|||
fn substr(src: ptr, start: int, n: int) -> ptr {
|
||||
let b = mem_alloc(n + 1)
|
||||
let 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)
|
||||
return b
|
||||
}
|
||||
|
|
@ -39,20 +39,20 @@ fn char_is_alnum(c: int) -> bool { return char_is_alpha(c) or char_is_digit(c) }
|
|||
|
||||
# integer -> fresh decimal string
|
||||
fn itoa(v: int) -> ptr {
|
||||
if v == 0 { let z = mem_alloc(2) poke8(z, 0, 48) poke8(z, 1, 0) return z }
|
||||
if v == 0 { let z = mem_alloc(2); poke8(z, 0, 48); poke8(z, 1, 0); return z }
|
||||
let neg = false
|
||||
let x = v
|
||||
if x < 0 { neg = true x = 0 - x }
|
||||
if x < 0 { neg = true; x = 0 - x }
|
||||
let tmp = mem_alloc(16)
|
||||
let 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
|
||||
if neg { total = total + 1 }
|
||||
let out = mem_alloc(total + 1)
|
||||
let k = 0
|
||||
if neg { poke8(out, 0, 45) k = 1 }
|
||||
if neg { poke8(out, 0, 45); k = 1 }
|
||||
let 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)
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
game 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) }
|
||||
main {
|
||||
print_int(fib(10)) # 55
|
||||
let 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
|
||||
let i = 0 let t = 0
|
||||
while true { i = i + 1 if i > 5 { break } t = t + i }
|
||||
let i = 0; let t = 0
|
||||
while true { i = i + 1; if i > 5 { break }; t = t + i }
|
||||
print_int(t) # 15
|
||||
if false and (1/0 == 0) { print_int(999) } else { print_int(1) } # short-circuit: no div by zero
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
game T {
|
||||
fn classify(c: int) -> int {
|
||||
match c { 65, 66 => { return 1 } 67 => { return 2 } _ => { return 9 } }
|
||||
match c { 65, 66 => { return 1 }; 67 => { return 2 }; _ => { return 9 } }
|
||||
return 0
|
||||
}
|
||||
main {
|
||||
|
|
|
|||
119
tools/ludic-tools/migrate_separators.c
Normal file
119
tools/ludic-tools/migrate_separators.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* sepfix.c — one-time migration for Rule B: insert `;` at statement boundaries
|
||||
* that are currently spelled with only whitespace. Reuses the toolchain lexer.
|
||||
*
|
||||
* A boundary is inserted between two significant, same-line tokens prev,cur when
|
||||
* - we are at paren/bracket depth 0 (not inside a call's args or an index), AND
|
||||
* - we are NOT inside a `ui` block (widget props are `k=v`, space-separated,
|
||||
* and the parser does not skip newlines between them), AND
|
||||
* - prev can END an operand/statement, AND cur can START a statement.
|
||||
* Inserting `;` (which the lexer maps to a newline token) at a REAL boundary is
|
||||
* a semantic no-op under today's permissive parser — the IR-identity check in
|
||||
* the driver is the proof. Wrong insertions change the IR and are rejected.
|
||||
*
|
||||
* usage: sepfix <in.ludic> -> writes migrated source to stdout
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ludic_syntax.h"
|
||||
|
||||
static const char* STMT_START[] = {
|
||||
"let","return","if","when","while","for","spawn","despawn",
|
||||
"match","machine","become","enter","break","continue", 0
|
||||
};
|
||||
|
||||
static int ender(const LLex* L, int i){
|
||||
const LTok* t = &L->v[i];
|
||||
switch (t->kind){
|
||||
case LT_ID: case LT_INT: case LT_FLOAT: case LT_STR: case LT_CHAR:
|
||||
case LT_BOOL: case LT_TYPE: case LT_PHASE: return 1;
|
||||
case LT_OP: {
|
||||
if (ltok_len(t) != 1) return 0;
|
||||
char c = L->src[t->start];
|
||||
if (c == ']'){
|
||||
/* `]` closing an empty `[]` is a slice-TYPE marker (`[]Node`),
|
||||
* not an operand end — the type name that follows continues it. */
|
||||
int p = ltok_prev_sig(L, i);
|
||||
if (p >= 0 && L->v[p].kind == LT_OP && ltok_len(&L->v[p]) == 1 && L->src[L->v[p].start] == '[') return 0;
|
||||
return 1;
|
||||
}
|
||||
return (c == ')' || c == '}'); }
|
||||
case LT_KW: { char b[32]; ltok_text(L, i, b, sizeof b);
|
||||
return !strcmp(b, "break") || !strcmp(b, "continue"); }
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
static int starter(const LLex* L, int i){
|
||||
const LTok* t = &L->v[i];
|
||||
if (t->kind == LT_ID || t->kind == LT_INT || t->kind == LT_FLOAT ||
|
||||
t->kind == LT_STR || t->kind == LT_CHAR || t->kind == LT_BOOL) return 1;
|
||||
if (t->kind == LT_KW){ char b[32]; ltok_text(L, i, b, sizeof b); return lud_in(STMT_START, b); }
|
||||
return 0; /* an operator never starts a statement here */
|
||||
}
|
||||
|
||||
int main(int argc, char** argv){
|
||||
if (argc < 2){ fprintf(stderr, "usage: sepfix <file>\n"); return 2; }
|
||||
FILE* f = fopen(argv[1], "rb");
|
||||
if (!f){ fprintf(stderr, "sepfix: cannot open %s\n", argv[1]); return 2; }
|
||||
fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET);
|
||||
char* src = malloc(n + 1); fread(src, 1, n, f); src[n] = 0; fclose(f);
|
||||
|
||||
LLex L; lud_lex(&L, src);
|
||||
|
||||
/* insert[k] = 1 means: emit a ';' immediately after token k's bytes */
|
||||
char* insert = calloc(L.n, 1);
|
||||
|
||||
int paren = 0; /* unclosed ( or [ */
|
||||
/* ui suppression: brace depth stack, marking which open braces are ui/widget */
|
||||
int ui_stack[512]; int bp = 0; int ui_active = 0;
|
||||
int pendingUiOpen = 0; /* saw `ui` or a widget word; the next { is a ui brace */
|
||||
|
||||
int prev = -1; /* previous significant token index */
|
||||
for (int i = 0; i < L.n; i++){
|
||||
int k = L.v[i].kind;
|
||||
if (k == LT_EOF) break;
|
||||
if (k == LT_NL || k == LT_COMMENT) continue;
|
||||
|
||||
char c0 = L.src[L.v[i].start];
|
||||
int op1 = (k == LT_OP && ltok_len(&L.v[i]) == 1);
|
||||
|
||||
/* track ui context by the `ui` KEYWORD only — it brackets the whole
|
||||
* widget tree via its root brace, so every nested widget brace is
|
||||
* already inside ui_active. (Do NOT key off widget words like col/row/
|
||||
* image/label: those are also ordinary variable names, and matching them
|
||||
* would wrongly suppress real statement boundaries.) */
|
||||
if (k == LT_KW && ltok_is(&L, i, "ui")) pendingUiOpen = 1;
|
||||
|
||||
if (op1 && c0 == '{'){
|
||||
if (bp < 512){ ui_stack[bp] = pendingUiOpen; bp++; if (pendingUiOpen) ui_active++; }
|
||||
pendingUiOpen = 0;
|
||||
} else if (op1 && c0 == '}'){
|
||||
if (bp > 0){ bp--; if (ui_stack[bp]) { if (ui_active) ui_active--; } }
|
||||
} else if (op1 && (c0 == '(' || c0 == '[')){ paren++; if (getenv("SEPFIX_TRACE")) fprintf(stderr, " L%d '%c' -> paren=%d\n", L.v[i].line + 1, c0, paren); }
|
||||
else if (op1 && (c0 == ')' || c0 == ']')){ if (paren > 0) paren--; if (getenv("SEPFIX_TRACE")) fprintf(stderr, " L%d '%c' -> paren=%d\n", L.v[i].line + 1, c0, paren); }
|
||||
|
||||
/* candidate boundary between prev and this token */
|
||||
if (prev >= 0 && paren == 0 && !ui_active &&
|
||||
L.v[prev].line == L.v[i].line &&
|
||||
ender(&L, prev) && starter(&L, i)){
|
||||
insert[prev] = 1;
|
||||
}
|
||||
prev = i;
|
||||
}
|
||||
|
||||
if (getenv("SEPFIX_DEBUG")) fprintf(stderr, "END STATE %s: paren=%d ui_active=%d bp=%d\n", argv[1], paren, ui_active, bp);
|
||||
|
||||
/* rebuild: copy bytes, dropping in ';' right after any token flagged */
|
||||
for (int i = 0, t = 0; i < n; ){
|
||||
/* find if a token ends exactly at i and is flagged */
|
||||
putchar(src[i]);
|
||||
i++;
|
||||
/* emit ';' after the last byte of a flagged token */
|
||||
for (; t < L.n; t++){
|
||||
if (L.v[t].end == i && insert[t]){ putchar(';'); break; }
|
||||
if (L.v[t].end > i) break;
|
||||
}
|
||||
}
|
||||
lud_lex_free(&L); free(insert); free(src);
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue