Merge Phase 7b: null literal

This commit is contained in:
Orkun ÇAKILKAYA 2026-08-28 00:50:21 +03:00
commit 56024f2cba
34 changed files with 5064 additions and 5124 deletions

View file

@ -581,7 +581,9 @@ Operators are built-in only (no overloading). The boolean operators are spelled
rejected with a diagnostic naming the fix (`!=` is unaffected). Bitwise operators
are **`& | ^ << >> ~`** (`>>` is a logical/unsigned shift). `expr with { field: … }`
is not implemented; records appear only in `spawn`. Char literals (`'w'`) are
`int` code points; colors are hex ints (`0xff8800`).
`int` code points; colors are hex ints (`0xff8800`). `null` is the null-pointer
literal; test any pointer/record/slice with `x == null` / `x != null` (an unset
`Node`/`ptr` field reads back as `null`).
## Builtins (the standard library / runtime surface)

View file

@ -20,10 +20,10 @@
# ============================================================================
# ---- state ----------------------------------------------------------------
var rt_fb: ptr = ptr_null() # framebuffer, one i32 (0x00RRGGBB) per pixel
var rt_fb: ptr = null # framebuffer, one i32 (0x00RRGGBB) per pixel
var rt_fbw: int = 320
var rt_fbh: int = 240
var rt_regs: ptr = ptr_null() # the 64 general-purpose game registers
var rt_regs: ptr = null # the 64 general-purpose game registers
var rt_rng: int = 305419896 # xorshift32 state
var rt_alive: int = 1 # platform still running?
@ -277,7 +277,7 @@ fn rt_put_int(buf: ptr, at: int, v: int) -> int {
fn rt_dump_ppm(path: str) -> void {
let f = file_open(path, "wb")
if ptr_is_null(f) { return }
if (f == null) { return }
let hdr = mem_alloc(64)
var n = rt_put_str(hdr, 0, "P6\n")
@ -310,7 +310,7 @@ import "ui.ludic"
# A character grid the game paints with map_row() and reads with tile(). Stored
# as one byte per cell in a flat buffer; out-of-bounds reads answer '#' so a
# caller can treat the edge of the world as wall without special-casing it.
var rt_map: ptr = ptr_null()
var rt_map: ptr = null
var rt_mapw: int = 0
var rt_maph: int = 0
@ -344,7 +344,7 @@ fn rt_tile(x: int, y: int) -> int {
# ---- status line ----------------------------------------------------------
# One persistent string of feedback/dialogue, copied into runtime-owned memory
# so it survives whatever the caller does with the original.
var rt_statusbuf: ptr = ptr_null()
var rt_statusbuf: ptr = null
fn rt_status(s: str) -> void {
var i = 0

View file

@ -13,12 +13,12 @@ const IMG_MAX: int = 32
const SPR_MAX: int = 160
const SPR_SZ: int = 16
var img_px: ptr = ptr_null() # IMG_MAX pointers to RGBA buffers
var img_w: ptr = ptr_null()
var img_h: ptr = ptr_null()
var img_px: ptr = null # IMG_MAX pointers to RGBA buffers
var img_w: ptr = null
var img_h: ptr = null
var img_n: int = 0
var spr_px: ptr = ptr_null() # SPR_MAX * 16 * 16 RGBA pixels, one block
var spr_px: ptr = null # SPR_MAX * 16 * 16 RGBA pixels, one block
var spr_n: int = 0
fn rt_image_init() -> void {
@ -44,11 +44,11 @@ fn png_tag(b: ptr, at: int, a: int, c: int, d: int, e: int) -> bool {
fn rt_read_file(path: str) -> ptr {
let f = file_open(path, "rb")
if ptr_is_null(f) { return ptr_null() }
if (f == null) { return null }
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 null }
let buf = mem_alloc(n + 8)
file_read(f, buf, n)
file_close(f)
@ -60,14 +60,14 @@ var rt_file_len: int = 0
# decoded image is left in these; -1 width means failure
var png_w: int = 0
var png_h: int = 0
var png_px: ptr = ptr_null()
var png_px: ptr = null
fn rt_decode_png(path: str) -> bool {
png_w = 0
png_h = 0
png_px = ptr_null()
png_px = null
let d = rt_read_file(path)
if ptr_is_null(d) { return false }
if (d == null) { 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 }
@ -334,7 +334,7 @@ fn rt_png_load(path: str) -> int {
}
}
mem_free(png_px)
png_px = ptr_null()
png_px = null
return id
}
@ -377,7 +377,7 @@ fn rt_hexval(c: int) -> int {
fn rt_sprites_load(path: str) -> void {
let d = rt_read_file(path)
if ptr_is_null(d) { spr_n = 0; return }
if (d == null) { spr_n = 0; return }
let size = rt_file_len
let pal = mem_alloc(128 * 4)
mem_set(pal, 0, 128 * 4)

View file

@ -13,7 +13,7 @@
# ============================================================================
# ---- bit reader (DEFLATE packs bits least-significant-first) ---------------
var z_src: ptr = ptr_null()
var z_src: ptr = null
var z_len: int = 0
var z_pos: int = 0
var z_bitbuf: int = 0

View file

@ -18,50 +18,50 @@ const TT_EDGES: int = 16384 # line segments in one glyph
const TT_GC: int = 512 # glyph cache entries
# ---- per-font tables (parallel arrays; index = font id) -------------------
var tt_data: ptr = ptr_null()
var tt_size: ptr = ptr_null()
var tt_upem: ptr = ptr_null()
var tt_nglyf: ptr = ptr_null()
var tt_locfm: ptr = ptr_null()
var tt_nhm: ptr = ptr_null()
var tt_asc: ptr = ptr_null()
var tt_desc: ptr = ptr_null()
var tt_gap: ptr = ptr_null()
var tt_glyf: ptr = ptr_null()
var tt_loca: ptr = ptr_null()
var tt_hmtx: ptr = ptr_null()
var tt_cmap: ptr = ptr_null()
var tt_cfmt: ptr = ptr_null()
var tt_data: ptr = null
var tt_size: ptr = null
var tt_upem: ptr = null
var tt_nglyf: ptr = null
var tt_locfm: ptr = null
var tt_nhm: ptr = null
var tt_asc: ptr = null
var tt_desc: ptr = null
var tt_gap: ptr = null
var tt_glyf: ptr = null
var tt_loca: ptr = null
var tt_hmtx: ptr = null
var tt_cmap: ptr = null
var tt_cfmt: ptr = null
var tt_n: int = 0
# ---- outline + edge scratch (reused for every glyph) ----------------------
var ol_x: ptr = ptr_null()
var ol_y: ptr = ptr_null()
var ol_on: ptr = ptr_null()
var ol_ends: ptr = ptr_null()
var ol_x: ptr = null
var ol_y: ptr = null
var ol_on: ptr = null
var ol_ends: ptr = null
var ol_n: int = 0
var ol_ne: int = 0
var ed_x0: ptr = ptr_null()
var ed_y0: ptr = ptr_null()
var ed_x1: ptr = ptr_null()
var ed_y1: ptr = ptr_null()
var ed_x0: ptr = null
var ed_y0: ptr = null
var ed_x1: ptr = null
var ed_y1: ptr = null
var ed_n: int = 0
var sc_x: ptr = ptr_null() # scanline crossings
var sc_d: ptr = ptr_null()
var sc_x: ptr = null # scanline crossings
var sc_d: ptr = null
# ---- glyph cache ----------------------------------------------------------
var gc_used: ptr = ptr_null()
var gc_font: ptr = ptr_null()
var gc_cp: ptr = ptr_null()
var gc_px: ptr = ptr_null()
var gc_w: ptr = ptr_null()
var gc_h: ptr = ptr_null()
var gc_ox: ptr = ptr_null()
var gc_oy: ptr = ptr_null()
var gc_adv: ptr = ptr_null()
var gc_bmp: ptr = ptr_null()
var gc_used: ptr = null
var gc_font: ptr = null
var gc_cp: ptr = null
var gc_px: ptr = null
var gc_w: ptr = null
var gc_h: ptr = null
var gc_ox: ptr = null
var gc_oy: ptr = null
var gc_adv: ptr = null
var gc_bmp: ptr = null
fn rt_tt_init() -> void {
tt_data = mem_alloc(TT_MAX * 8)
@ -169,7 +169,7 @@ fn tt_pick_cmap(id: int, d: ptr, co: int) -> void {
fn rt_font_load(path: str) -> int {
if tt_n >= TT_MAX { return 0 - 1 }
let d = rt_read_file(path)
if ptr_is_null(d) { return 0 - 1 }
if (d == null) { return 0 - 1 }
let size = rt_file_len
var base = 0
# a .ttc collection points at its first font

View file

@ -61,7 +61,7 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
ol_ne = 0
ed_n = 0
tt_load_outline(id, gid, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0)
if ol_n == 0 { return ptr_null() }
if ol_n == 0 { return null }
var minx = 999999.0
var miny = 999999.0
@ -83,10 +83,10 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
if maxy == fx(flr(maxy)) { y1 = flr(maxy) }
let W = x1 - x0
let H = y1 - y0
if W <= 0 { return ptr_null() }
if H <= 0 { return ptr_null() }
if W > 1024 { return ptr_null() }
if H > 1024 { return ptr_null() }
if W <= 0 { return null }
if H <= 0 { return null }
if W > 1024 { return null }
if H > 1024 { return null }
gr_w = W
gr_h = H
gr_ox = x0
@ -250,7 +250,7 @@ fn tt_glyph_get(font: int, cp: int, px: int) -> int {
let s = h
if peek32(gc_used, s) == 1 {
let old = peekp(gc_bmp, s)
if ptr_is_null(old) == false { mem_free(old) }
if (old == null) == false { mem_free(old) }
}
let bmp = tt_raster(font, tt_glyph_index(font, cp), px)
pokep(gc_bmp, s, bmp)
@ -299,7 +299,7 @@ fn tt_utf8(s: str, at: int) -> int {
# ---- drawing --------------------------------------------------------------
fn tt_blit(bmp: ptr, w: int, h: int, dx: int, dy: int, colour: int) -> void {
if ptr_is_null(bmp) { return }
if (bmp == null) { return }
let cr = ((colour >> 16) & 255)
let cg = ((colour >> 8) & 255)
let cb = (colour & 255)

View file

@ -45,35 +45,35 @@ const K_VISIBLE: int = 18
const K_IMG: int = 19
const K_SKIN: int = 20
var ui_type: ptr = ptr_null()
var ui_parent: ptr = ptr_null()
var ui_w: ptr = ptr_null()
var ui_h: ptr = ptr_null()
var ui_x: ptr = ptr_null()
var ui_y: ptr = ptr_null()
var ui_haspos: ptr = ptr_null()
var ui_pad: ptr = ptr_null()
var ui_gap: ptr = ptr_null()
var ui_bg: ptr = ptr_null()
var ui_fg: ptr = ptr_null()
var ui_border: ptr = ptr_null()
var ui_align: ptr = ptr_null()
var ui_grow: ptr = ptr_null()
var ui_font: ptr = ptr_null()
var ui_size: ptr = ptr_null()
var ui_skin: ptr = ptr_null()
var ui_inset: ptr = ptr_null()
var ui_img: ptr = ptr_null()
var ui_focusable: ptr = ptr_null()
var ui_rx: ptr = ptr_null()
var ui_ry: ptr = ptr_null()
var ui_rw: ptr = ptr_null()
var ui_rh: ptr = ptr_null()
var ui_visible: ptr = ptr_null()
var ui_fired: ptr = ptr_null()
var ui_hasdyn: ptr = ptr_null()
var ui_dyn: ptr = ptr_null() # UI_MAX * 96 bytes
var ui_text: ptr = ptr_null() # UI_MAX static string pointers
var ui_type: ptr = null
var ui_parent: ptr = null
var ui_w: ptr = null
var ui_h: ptr = null
var ui_x: ptr = null
var ui_y: ptr = null
var ui_haspos: ptr = null
var ui_pad: ptr = null
var ui_gap: ptr = null
var ui_bg: ptr = null
var ui_fg: ptr = null
var ui_border: ptr = null
var ui_align: ptr = null
var ui_grow: ptr = null
var ui_font: ptr = null
var ui_size: ptr = null
var ui_skin: ptr = null
var ui_inset: ptr = null
var ui_img: ptr = null
var ui_focusable: ptr = null
var ui_rx: ptr = null
var ui_ry: ptr = null
var ui_rw: ptr = null
var ui_rh: ptr = null
var ui_visible: ptr = null
var ui_fired: ptr = null
var ui_hasdyn: ptr = null
var ui_dyn: ptr = null # UI_MAX * 96 bytes
var ui_text: ptr = null # UI_MAX static string pointers
var ui_n: int = 0
var ui_active: int = -1
@ -138,7 +138,7 @@ fn rt_ui_reset(n: int) -> void {
poke32(ui_visible, i, 1)
poke32(ui_fired, i, 0)
poke32(ui_hasdyn, i, 0)
pokep(ui_text, i, ptr_null())
pokep(ui_text, i, null)
}
}
@ -178,7 +178,7 @@ fn rt_ui_static_text(i: int, s: str) -> void {
fn ui_str(i: int) -> ptr {
if peek32(ui_hasdyn, i) == 1 { return ptr_add(ui_dyn, i * 96) }
let t = peekp(ui_text, i)
if ptr_is_null(t) { return ptr_add(ui_dyn, i * 96) }
if (t == null) { return ptr_add(ui_dyn, i * 96) }
return t
}

View file

@ -146,10 +146,10 @@ fn ui_draw_node(i: int) -> void {
}
# ---- focus and activation -------------------------------------------------
var ui_fl: ptr = ptr_null()
var ui_fl: ptr = null
fn ui_focusables() -> int {
if ptr_is_null(ui_fl) { ui_fl = mem_alloc(UI_MAX * 4) }
if (ui_fl == null) { ui_fl = mem_alloc(UI_MAX * 4) }
var n = 0
for i in 0 .. ui_n {
if peek32(ui_focusable, i) == 1 {

View file

@ -49,12 +49,13 @@ const E_NEW: int = 39
const E_FLOAT: int = 40
const E_REC: int = 41
const E_FINIT: int = 42
const E_NULL: int = 43 # the `null` pointer literal
property Node {
kind: int = 0
s: ptr = ptr_null() # name / operator / string / type-of-new
s: ptr = null # name / operator / string / type-of-new
ival: int = 0 # int literal, bool, flags
ty: ptr = ptr_null() # declared type (let/param/field/fn/var/const)
ty: ptr = null # declared type (let/param/field/fn/var/const)
a: Node # fixed children (meaning per kind)
b: Node
c: Node

View file

@ -2,7 +2,7 @@
# module-level material (types, globals, string constants) into one buffer and
# function bodies into another, then prints them in order.
property Buf { data: ptr = ptr_null(), len: int = 0, cap: int = 0 }
property Buf { data: ptr = null, len: int = 0, cap: int = 0 }
fn buf_new() -> Buf {
let b = new Buf

View file

@ -8,7 +8,7 @@ var g_addr_ty: ptr # out-param: the type at the computed address
fn emit_member_addr(e: Node) -> ptr {
let base = emit_expr(e.a)
let s = layout_node(base.ty)
if ptr_is_null(s) { perr(sconcat("member access on non-aggregate ", base.ty)) }
if (s == null) { perr(sconcat("member access on non-aggregate ", base.ty)) }
let fidx = field_index(s, e.s)
if fidx < 0 { perr(sconcat("no such field ", e.s)) }
g_addr_ty = field_type(s, e.s)

View file

@ -2,7 +2,7 @@
# module header. Mirrors the pieces of compiler/back/ that this subset needs.
# structs and slices are references, so every non-scalar type lowers to `ptr`.
property Val { code: ptr = ptr_null(), ty: ptr = ptr_null() }
property Val { code: ptr = null, ty: ptr = null }
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
@ -70,12 +70,12 @@ fn slice_elem(t: ptr) -> ptr { return substr(t, 2, slen(t) - 2) }
fn find_arch(name: ptr) -> Node {
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_ARCH and streq(d.s, name) { return d }; i = i + 1 }
return ptr_null()
return null
}
fn find_comp(name: ptr) -> Node {
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_COMP and streq(d.s, name) { return d }; i = i + 1 }
return ptr_null()
return null
}
# every record is a `property` with a %Cmp_ layout of named fields — whether it
# is stored per-entity by the ECS or heap-allocated by `new` is a matter of use.
@ -102,7 +102,7 @@ fn find_global(name: ptr) -> Node {
if d.kind == N_CONST and streq(d.s, name) { return d }
i = i + 1
}
return ptr_null()
return null
}
# `Enum.Variant` -> the variant's ordinal (its index), or -1 if `ename` names no
@ -122,7 +122,7 @@ fn enum_ordinal(ename: ptr, vname: ptr) -> int {
fn find_fn(name: ptr) -> Node {
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_FN and streq(d.s, name) { return d }; i = i + 1 }
return ptr_null()
return null
}
# @Computed derived fields: a per-property (Prop.field -> expression) registry.
@ -135,16 +135,16 @@ fn register_computed(prop: ptr, field: ptr, ty: ptr, e: Node) -> void {
push(g_computed, cf)
}
fn computed_expr(prop: ptr, field: ptr) -> Node {
if ptr_is_null(prop) { return ptr_null() }
if (prop == null) { return null }
let key = sconcat(prop, sconcat(".", field))
var i = 0
while i < len(g_computed) { if streq(g_computed[i].s, key) { return g_computed[i].a }; i = i + 1 }
return ptr_null()
return null
}
# best-effort static type of an expression (for computed-field lookup; emits nothing)
fn static_type(e: Node) -> ptr {
if e.kind == E_ID { let li = loc_find(e.s); if li >= 0 { return loc_ty[li] } }
return ptr_null()
return null
}
# @OnSpawn(Model) hooks: a Model -> hook-body registry. Populated at parse time;
@ -158,7 +158,7 @@ fn register_onspawn(model: ptr, body: Node) -> void {
fn onspawn_body(model: ptr) -> Node {
var i = 0
while i < len(g_onspawn) { if streq(g_onspawn[i].s, model) { return g_onspawn[i].a }; i = i + 1 }
return ptr_null()
return null
}
# @OnDespawn(Model): a Model -> hook-body registry. Despawn does not statically
@ -173,7 +173,7 @@ fn register_ondespawn(model: ptr, body: Node) -> void {
fn ondespawn_body(model: ptr) -> Node {
var i = 0
while i < len(g_ondespawn) { if streq(g_ondespawn[i].s, model) { return g_ondespawn[i].a }; i = i + 1 }
return ptr_null()
return null
}
fn register_onattach(prop: ptr, body: Node) -> void {
let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_onattach, n)
@ -181,7 +181,7 @@ fn register_onattach(prop: ptr, body: Node) -> void {
fn onattach_body(prop: ptr) -> Node {
var i = 0
while i < len(g_onattach) { if streq(g_onattach[i].s, prop) { return g_onattach[i].a }; i = i + 1 }
return ptr_null()
return null
}
# @OnEnable(Property) / @OnDisable(Property): run when a property is toggled on an
@ -193,12 +193,12 @@ fn register_ondisable(prop: ptr, body: Node) -> void { let n = node(N_BLOCK); n.
fn onenable_body(prop: ptr) -> Node {
var i = 0
while i < len(g_onenable) { if streq(g_onenable[i].s, prop) { return g_onenable[i].a }; i = i + 1 }
return ptr_null()
return null
}
fn ondisable_body(prop: ptr) -> Node {
var i = 0
while i < len(g_ondisable) { if streq(g_ondisable[i].s, prop) { return g_ondisable[i].a }; i = i + 1 }
return ptr_null()
return null
}
# is `name` a model (archetype)? — chooses model-vs-handler for a bare enable/disable

View file

@ -90,13 +90,13 @@ fn emit_program() -> void {
fn ir_flush(path: ptr) -> bool {
let h = buf_str(head)
let c = buf_str(code)
if ptr_is_null(path) {
if (path == null) {
print_str(h)
print_str(c)
return true
}
let f = file_open(path, "wb")
if ptr_is_null(f) { return false }
if (f == null) { return false }
file_write(f, h, slen(h))
file_write(f, c, slen(c))
file_close(f)

View file

@ -63,8 +63,10 @@ fn emit_bin(e: Node) -> Val {
let fx = streq(a.ty, "fixed") or streq(b.ty, "fixed")
if is_cmp(e.s) {
var ac = a.code; var bc = b.code
var ct = "i32"
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))))))
else { if streq(llty(a.ty), "ptr") or streq(llty(b.ty), "ptr") { ct = "ptr" } } # `p == null`, str/record identity
let c = emit_bind(sconcat("icmp ", sconcat(cmp_code(e.s), sconcat(sconcat(" ", sconcat(ct, " ")), sconcat(ac, sconcat(", ", bc))))))
return val(emit_bind(sconcat("zext i1 ", sconcat(c, " to i32"))), "bool")
}
if fx {
@ -107,11 +109,11 @@ fn emit_call(e: Node) -> Val {
if is_math_builtin(name) { return emit_math_builtin(name, e) }
var fn2 = find_fn(name)
var cname = name
if ptr_is_null(fn2) {
if (fn2 == null) {
# a builtin like clear()/reg() is satisfied by its rt_ function
let rtname = sconcat("rt_", name)
fn2 = find_fn(rtname)
if ptr_is_null(fn2) { perr(sconcat("unknown function ", name)) }
if (fn2 == null) { perr(sconcat("unknown function ", name)) }
cname = rtname
}
# evaluate args first (their IR is emitted before the call instruction)
@ -135,10 +137,11 @@ fn emit_call(e: Node) -> Val {
}
fn emit_expr(e: Node) -> Val {
if ptr_is_null(e) { return val("0", "int") }
if (e == null) { return val("0", "int") }
if e.kind == E_INT { return val(itoa(e.ival), "int") }
if e.kind == E_FLOAT { return val(itoa(e.ival), "fixed") }
if e.kind == E_BOOL { return val(itoa(e.ival), "bool") }
if e.kind == E_NULL { return val("null", "ptr") }
if e.kind == E_STR { return val(emit_str_const(e.s), "str") }
if e.kind == E_NEW {
if is_slice_ty(e.s) { return emit_new_slice(e.s) }
@ -148,7 +151,7 @@ fn emit_expr(e: Node) -> Val {
let li = loc_find(e.s)
if li >= 0 { return emit_load_at(loc_reg[li], loc_ty[li]) }
let g = find_global(e.s)
if not ptr_is_null(g) {
if (g != null) {
if g.kind == N_CONST { return val(itoa(g.a.ival), "int") }
let r = emit_bind(sconcat("load ", sconcat(llty(g.ty), sconcat(", ptr @g_", e.s))))
return val(r, g.ty)
@ -163,9 +166,9 @@ fn emit_expr(e: Node) -> Val {
if ord >= 0 { return val(itoa(ord), "int") }
}
let bt = static_type(e.a) # `x.field` where field is @Computed -> inline it
if not ptr_is_null(bt) {
if (bt != null) {
let cx = computed_expr(bt, e.s)
if not ptr_is_null(cx) { return emit_expr(qualify_fields(cx, e.a)) }
if (cx != null) { return emit_expr(qualify_fields(cx, e.a)) }
}
let a = emit_member_addr(e); return emit_load_at(a, g_addr_ty)
}

View file

@ -71,13 +71,13 @@ fn emit_game_main() -> void {
emit("define i32 @main(i32 %argc, ptr %argv) {\nentry:\n")
emit(" store i32 %argc, ptr @L_argc\n")
emit(" store ptr %argv, ptr @L_argv\n")
if not ptr_is_null(find_fn("rt_init")) { emit(" call void @fn_rt_init()\n") }
if (find_fn("rt_init") != null) { emit(" call void @fn_rt_init()\n") }
emit_calls_for_phase("Start")
emit(" br label %loop\n")
emit("loop:\n")
let r = emit_bind("load i32, ptr @L_running")
let rc = emit_bind(sconcat("icmp ne i32 ", sconcat(r, ", 0")))
if not ptr_is_null(find_fn("rt_running")) {
if (find_fn("rt_running") != null) {
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))))
@ -86,7 +86,7 @@ fn emit_game_main() -> void {
emit(" br i1 "); emit(rc); emit(", label %body, label %done\n")
}
emit("body:\n")
if not ptr_is_null(find_fn("rt_poll")) {
if (find_fn("rt_poll") != null) {
let k = emit_bind("call i32 @fn_rt_poll()")
emit(" store i32 "); emit(k); emit(", ptr @L_key\n")
}
@ -98,6 +98,6 @@ fn emit_game_main() -> void {
emit(" br label %loop\n")
emit("done:\n")
emit_calls_for_phase("OnQuit") # @OnQuit shutdown hooks run once, before teardown
if not ptr_is_null(find_fn("rt_shutdown")) { emit(" call void @fn_rt_shutdown()\n") }
if (find_fn("rt_shutdown") != null) { emit(" call void @fn_rt_shutdown()\n") }
emit(" ret i32 0\n}\n")
}

View file

@ -34,7 +34,7 @@ fn emit_gep_i8(base: ptr, idx: ptr) -> ptr {
# 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 (d.a == null) { 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)) }

View file

@ -7,8 +7,8 @@ var g_intrin_ok: bool = false
# is `name` a low-level intrinsic? A pure name check, so it can gate dispatch
# without evaluating arguments (which could clobber shared state).
fn is_intrinsic(name: ptr) -> bool {
if streq(name,"ptr_null") or streq(name,"mem_alloc") or streq(name,"mem_realloc") { return true }
if streq(name,"peek8") or streq(name,"poke8") or streq(name,"ptr_is_null") { return true }
if streq(name,"mem_alloc") or streq(name,"mem_realloc") { return true }
if streq(name,"peek8") or streq(name,"poke8") { return true }
if streq(name,"file_open") or streq(name,"file_read") or streq(name,"file_write") { return true }
if streq(name,"file_seek") or streq(name,"file_tell") or streq(name,"file_close") { return true }
if streq(name,"print_str") or streq(name,"print_int") { return true }
@ -24,7 +24,7 @@ fn arg_code(e: Node, i: int) -> ptr { let v = emit_expr(e.kids[i]); return v.co
fn emit_intrinsic(name: ptr, e: Node) -> Val {
g_intrin_ok = true
if streq(name, "ptr_null") { return val("null", "ptr") }
# ptr_null / ptr_is_null are the `null` literal and `x == null` now.
if streq(name, "mem_alloc") {
let n = arg_code(e, 0)
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
@ -48,11 +48,6 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
emit(" store i8 "); emit(t); emit(", ptr "); emit(a); emit("\n")
return val("0", "void")
}
if streq(name, "ptr_is_null") {
let p = arg_code(e, 0)
let c = emit_bind(sconcat("icmp eq ptr ", sconcat(p, ", null")))
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)
return val(emit_bind(sconcat("call ptr @fopen(ptr ", sconcat(p, sconcat(", ptr ", sconcat(m, ")"))))), "ptr")

View file

@ -29,10 +29,10 @@ fn emit_machine(st: Node) -> void {
fn emit_become(st: Node) -> void {
if nmach == 0 { perr("'become' outside a machine") }
let m = mach_stk[nmach - 1]
var target: Node = ptr_null()
var target: Node = null
var i = 0
while i < len(m.kids) { if streq(m.kids[i].s, st.s) { target = m.kids[i] }; i = i + 1 }
if ptr_is_null(target) { perr(sconcat("become: no state ", st.s)) }
if (target == null) { perr(sconcat("become: no state ", st.s)) }
let regv = emit_expr(m.a)
let sv = emit_expr(target.b)
emit(" call void @fn_rt_set_reg(i32 "); emit(regv.code); emit(", i32 "); emit(sv.code); emit(")\n")

View file

@ -9,7 +9,7 @@ fn emit_sizeof(llt: ptr) -> ptr {
fn emit_new_struct(name: ptr) -> Val {
let s = layout_node(name) # a struct or a property — same shape
if ptr_is_null(s) { perr("unknown record type in new") }
if (s == null) { perr("unknown record type in new") }
let lty = layout_ty(name)
let sz = emit_sizeof(lty)
let obj = emit_bind(sconcat("call ptr @malloc(i64 ", sconcat(sz, ")")))
@ -22,7 +22,7 @@ fn emit_new_struct(name: ptr) -> Val {
let lt = llty(fd.ty)
var v = "0"
if streq(lt, "ptr") { v = "null" }
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a); v = dv.code }
if (fd.a != null) { 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
}

View file

@ -77,7 +77,7 @@ fn emit_query(st: Node) -> void {
}
# optional where-clause
if not ptr_is_null(st.b) {
if (st.b != null) {
let w = emit_expr(st.b)
let wc = emit_bind(sconcat("icmp ne i32 ", sconcat(w.code, ", 0")))
let kw = lbl("qw")

View file

@ -40,8 +40,8 @@ fn emit_snapshot() -> void {
emith("@.sav_path = private unnamed_addr constant [10 x i8] c\"ludic.sav\\00\"\n")
emith("@.sav_wb = private unnamed_addr constant [3 x i8] c\"wb\\00\"\n")
emith("@.sav_rb = private unnamed_addr constant [3 x i8] c\"rb\\00\"\n")
let has_save = not ptr_is_null(find_fn("rt_save_state"))
let has_load = not ptr_is_null(find_fn("rt_load_state"))
let has_save = (find_fn("rt_save_state") != null)
let has_load = (find_fn("rt_load_state") != null)
emit("define void @L_save() {\nentry:\n")
emit(" %f = call ptr @fopen(ptr @.sav_path, ptr @.sav_wb)\n")

View file

@ -4,7 +4,7 @@
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 property ", comp)) }
if (c == null) { perr(sconcat("spawn: unknown property ", 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 slot = nreg()
@ -18,12 +18,12 @@ fn emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
let lt = llty(fd.ty)
var v = "0"
if streq(lt, "ptr") { v = "null" }
if not ptr_is_null(fd.a) { let dv = emit_expr(fd.a); v = dv.code }
if (fd.a != null) { 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
if not ptr_is_null(rec) {
if (rec != null) {
var j = 0
while j < len(rec.kids) {
let fi = rec.kids[j]
@ -39,7 +39,7 @@ fn emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
}
# @OnAttach(Property) runs once the property is attached and seeded
let ab = onattach_body(comp)
if not ptr_is_null(ab) {
if (ab != null) {
let save = nloc
let vslot = emit_alloca("ptr")
emit(" store ptr "); emit(slot); emit(", ptr "); emit(vslot); emit("\n")
@ -76,14 +76,14 @@ fn emit_spawn(st: Node) -> void {
var c = 0
while c < len(arch.kids) {
let cn = arch.kids[c].s
var rec = ptr_null()
var rec = null
var i = 0
while i < len(st.kids) { if streq(st.kids[i].s, cn) { rec = st.kids[i].a }; i = i + 1 }
emit_init_component(e, cn, rec)
c = c + 1
}
let ob = onspawn_body(st.s) # @OnSpawn(Model) hook runs after init
if not ptr_is_null(ob) {
if (ob != null) {
let save = nloc
emit_bind_props(arch, e)
emit_block(ob)
@ -121,14 +121,14 @@ fn emit_despawn(st: Node) -> void {
# flag). A bare `<Model>` / `<Handler>` flips a global enabled flag.
fn emit_toggle(st: Node) -> void {
var val = "0"; if st.ival == 1 { val = "1" }
if not ptr_is_null(st.a) {
if (st.a != null) {
let ev = emit_expr(st.a)
let me = itoa(MAX_ENT)
let hp = nreg(); emit(" "); emit(hp); emit(" = getelementptr inbounds ["); emit(me); emit(" x i8], ptr @H_"); emit(st.s); emit(", i32 0, i32 "); emit(ev.code); emit("\n")
emit(" store i8 "); emit(val); emit(", ptr "); emit(hp); emit("\n")
var hb: Node = ptr_null()
var hb: Node = null
if st.ival == 1 { hb = onenable_body(st.s) } else { hb = ondisable_body(st.s) }
if not ptr_is_null(hb) {
if (hb != null) {
let save = nloc
let slot = nreg(); emit(" "); emit(slot); emit(" = getelementptr inbounds ["); emit(me); emit(" x %Cmp_"); emit(st.s); emit("], ptr @S_"); emit(st.s); emit(", i32 0, i32 "); emit(ev.code); emit("\n")
let vslot = emit_alloca("ptr")

View file

@ -30,7 +30,7 @@ fn emit_assign(st: Node) -> void {
}
else {
let g = find_global(t.s)
if ptr_is_null(g) { perr(sconcat("assign to unknown ", t.s)) }
if (g == null) { perr(sconcat("assign to unknown ", t.s)) }
addr = sconcat("@g_", t.s); ty = g.ty
}
} else {
@ -55,7 +55,7 @@ fn emit_assign(st: Node) -> void {
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 has_else = (st.c != null)
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") }
@ -112,7 +112,7 @@ fn emit_for(st: Node) -> void {
}
fn emit_return(st: Node) -> void {
if not ptr_is_null(st.a) {
if (st.a != null) {
let v = emit_expr(st.a)
store_at(llty(ret_ty), v.code, "%retval")
}
@ -129,7 +129,7 @@ fn arm_is_default(arm: Node) -> bool {
fn emit_match(st: Node) -> void {
let sv = emit_expr(st.a)
let endl = lbl("mend")
var deflt: Node = ptr_null()
var deflt: Node = null
var i = 0
while i < len(st.kids) {
let arm = st.kids[i]
@ -154,7 +154,7 @@ fn emit_match(st: Node) -> void {
}
i = i + 1
}
if not ptr_is_null(deflt) { emit_block(deflt.a) }
if (deflt != null) { emit_block(deflt.a) }
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
emit(endl); emit(":\n"); g_term = false
}
@ -162,10 +162,10 @@ fn emit_match(st: Node) -> void {
fn emit_stmt(st: Node) -> void {
if st.kind == S_LET {
var ty = st.ty
if ptr_is_null(ty) { let v0 = emit_expr(st.a); ty = v0.ty
if (ty == null) { 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); loc_set_mut(st.ival); return }
let slot = emit_alloca(llty(ty))
if ptr_is_null(st.a) {
if (st.a == null) {
var z = "0"
if streq(llty(ty), "ptr") { z = "null" }
store_at(llty(ty), z, slot)

View file

@ -17,7 +17,7 @@ fn ui_wtype(w: Node) -> int {
fn ui_prop(w: Node, key: ptr) -> Node {
var i = 0
while i < len(w.b.kids) { if streq(w.b.kids[i].s, key) { return w.b.kids[i].a }; i = i + 1 }
return ptr_null()
return null
}
fn ui_flatten(w: Node, parent: int) -> void {
let idx = len(uiw)
@ -51,7 +51,7 @@ fn ui_index_of(nm: ptr) -> int {
i = 0
while i < len(uiw) {
let idp = ui_prop(uiw[i], "id")
if not ptr_is_null(idp) { if idp.kind == E_ID and streq(idp.s, s) { return i } }
if (idp != null) { if idp.kind == E_ID and streq(idp.s, s) { return i } }
i = i + 1
}
return 0

View file

@ -3,7 +3,7 @@
fn read_file(path: str) -> ptr {
let f = file_open(path, "rb")
if ptr_is_null(f) { return ptr_null() }
if (f == null) { return null }
file_seek(f, 0, 2)
let n = file_tell(f)
file_seek(f, 0, 0)

View file

@ -10,7 +10,7 @@ const TK_NL: int = 4
const TK_EOF: int = 5
const TK_FLOAT: int = 6
property Tok { kind: int = 0, text: ptr = ptr_null(), ival: int = 0, line: int = 0 }
property Tok { kind: int = 0, text: ptr = null, ival: int = 0, line: int = 0 }
var toks: []Tok
@ -43,7 +43,7 @@ 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 == 10 { tok_push(TK_NL, 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 }
@ -80,7 +80,7 @@ fn lex(src: ptr) -> void {
i = i + 2
} else { v = peek8(src, i); i = i + 1 }
if peek8(src, i) == 39 { i = i + 1 }
tok_push(TK_INT, ptr_null(), v, line)
tok_push(TK_INT, null, v, line)
continue
}
if char_is_digit(c) {
@ -96,7 +96,7 @@ fn lex(src: ptr) -> void {
v = v * 16 + d
i = i + 1
}
tok_push(TK_INT, ptr_null(), v, line)
tok_push(TK_INT, null, v, line)
continue
}
var v = 0
@ -107,10 +107,10 @@ fn lex(src: ptr) -> void {
var fnum = 0; var fden = 1
while i < n and char_is_digit(peek8(src, i)) { fnum = fnum * 10 + (peek8(src, i) - 48); fden = fden * 10; i = i + 1 }
let bits = (v << 16) + (fnum << 16) / fden
tok_push(TK_FLOAT, ptr_null(), bits, line)
tok_push(TK_FLOAT, null, bits, line)
continue
}
tok_push(TK_INT, ptr_null(), v, line)
tok_push(TK_INT, null, v, line)
continue
}
if char_is_alpha(c) {
@ -119,7 +119,7 @@ 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, 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 } # =>
@ -137,5 +137,5 @@ fn lex(src: ptr) -> void {
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)
tok_push(TK_EOF, null, 0, line)
}

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,7 @@ fn strip_ludic(name: ptr) -> ptr {
# env var with a fallback when unset
fn getenv_or(name: ptr, dflt: ptr) -> ptr {
let v = os_getenv(name)
if ptr_is_null(v) { return dflt }
if (v == null) { return dflt }
return v
}
@ -52,8 +52,8 @@ fn die(msg: ptr) -> void {
}
entry {
var path = ptr_null()
var out = ptr_null()
var path = null
var out = null
var want = 0 # 0 = auto, 1 = windowed, 2 = headless
var emit_ir = false # --emit-llvm: stop after writing IR
var fmt = false # --fmt: lex + parse only, then exit (the doc-check gate)
@ -84,11 +84,11 @@ entry {
ai = ai + 1
}
if ptr_is_null(path) {
if (path == null) {
die("usage: ludicc <file.ludic> [-o out] [--windowed|--headless] [--emit-llvm] [--save-temps]\n")
}
let src = read_file(path)
if ptr_is_null(src) { die("ludicc: cannot open input\n") }
if (src == null) { die("ludicc: cannot open input\n") }
cur_dir = dir_of(path)
lex(src)
@ -104,7 +104,7 @@ entry {
# a game gets a window by default; a plain program stays headless. An explicit
# flag always wins. The stdout-IR path (no target) also stays headless, which
# is what reseed.sh compiles the compiler itself with.
let has_target = run or (not ptr_is_null(out))
let has_target = run or ((out != null))
if want == 1 { g_windowed = true }
else { if want == 2 { g_windowed = false }
else { g_windowed = has_target and has_systems() } }
@ -113,10 +113,10 @@ entry {
# --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 not has_target { ir_flush(null); return }
# a run with no explicit -o lands in a temp file
if run and ptr_is_null(out) {
if run and (out == null) {
out = sconcat("/tmp/ludic-run-", strip_ludic(base_name(path)))
}

View file

@ -62,6 +62,7 @@ fn p_primary() -> Node {
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, "null") { pi = pi + 1; return node(E_NULL) }
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
}
@ -275,13 +276,13 @@ fn already_loaded(full: ptr) -> bool {
# `@pure`, `@deterministic`, … — one channel, not a zoo of prefix keywords.
fn parse_one_decl() -> void {
var is_export = false
var qspec: Node = ptr_null()
var onspawn_model: ptr = ptr_null()
var ondespawn_model: ptr = ptr_null()
var onattach_prop: ptr = ptr_null()
var onenable_prop: ptr = ptr_null()
var ondisable_prop: ptr = ptr_null()
var hook_phase: ptr = ptr_null() # @OnStart / @OnQuit override the phase
var qspec: Node = null
var onspawn_model: ptr = null
var ondespawn_model: ptr = null
var onattach_prop: ptr = null
var onenable_prop: ptr = null
var ondisable_prop: ptr = null
var hook_phase: ptr = null # @OnStart / @OnQuit override the phase
while is_op("@") {
pi = pi + 1; let a = eat_id() # collect a leading @annotation
if streq(a, "export") { is_export = true }
@ -309,13 +310,13 @@ fn parse_one_decl() -> void {
if is_id("model") { push(prog, parse_archetype()); return }
if is_id("handler") {
let h = parse_system()
if not ptr_is_null(onspawn_model) { register_onspawn(onspawn_model, h.a); return } # spawn hook
if not ptr_is_null(ondespawn_model) { register_ondespawn(ondespawn_model, h.a); return } # despawn hook
if not ptr_is_null(onattach_prop) { register_onattach(onattach_prop, h.a); return } # attach hook
if not ptr_is_null(onenable_prop) { register_onenable(onenable_prop, h.a); return } # enable hook
if not ptr_is_null(ondisable_prop) { register_ondisable(ondisable_prop, h.a); return } # disable hook
if not ptr_is_null(hook_phase) { h.ty = hook_phase } # @OnStart/@OnQuit
if not ptr_is_null(qspec) { # @Queries wraps the body in its S_QUERY
if (onspawn_model != null) { register_onspawn(onspawn_model, h.a); return } # spawn hook
if (ondespawn_model != null) { register_ondespawn(ondespawn_model, h.a); return } # despawn hook
if (onattach_prop != null) { register_onattach(onattach_prop, h.a); return } # attach hook
if (onenable_prop != null) { register_onenable(onenable_prop, h.a); return } # enable hook
if (ondisable_prop != null) { register_ondisable(ondisable_prop, h.a); return } # disable hook
if (hook_phase != null) { h.ty = hook_phase } # @OnStart/@OnQuit
if (qspec != null) { # @Queries wraps the body in its S_QUERY
qspec.a = h.a
let wrap = node(N_BLOCK); push(wrap.kids, qspec); h.a = wrap
}
@ -336,7 +337,7 @@ fn do_import(rel: ptr) -> void {
if already_loaded(full) { return }
push(loaded_paths, full)
let src = read_file(full)
if ptr_is_null(src) { perr(sconcat("cannot open import ", full)) }
if (src == null) { perr(sconcat("cannot open import ", full)) }
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

View file

@ -71,7 +71,7 @@ fn parse_query_for() -> Node {
# the whole query backend (iteration, filters, binding, break/continue) is reused.
fn mk_and(a: Node, b: Node) -> Node {
if ptr_is_null(a) { return b }
if (a == null) { return b }
let n = node(E_BIN); n.s = "and"; n.a = a; n.b = b; return n
}
@ -80,7 +80,7 @@ fn mk_and(a: Node, b: Node) -> Node {
# expansion (base is the accessed value). Non-destructive: builds a fresh tree,
# so a stored computed expression can be expanded at many access sites.
fn qualify_fields(e: Node, base: Node) -> Node {
if ptr_is_null(e) { return e }
if (e == null) { return e }
if e.kind == E_ID {
let m = node(E_MEMBER); m.a = base; m.s = e.s; return m
}
@ -99,7 +99,7 @@ fn parse_queries_anno() -> Node {
eat_op("(")
let qn = node(S_QUERY)
let terms = node(N_BLOCK)
var wh: Node = ptr_null()
var wh: Node = null
while not is_op(")") {
skipnl()
if is_op(")") { break }

View file

@ -68,8 +68,8 @@ object LudicVocabulary {
// compiler intrinsics: the floor the Ludic-written runtime stands on
"mem_alloc", "mem_realloc", "mem_free", "mem_copy", "mem_set", "peek8", "poke8", "peek32",
"os_argc", "os_arg", "file_stderr",
"poke32", "peekp", "pokep", "peekf", "pokef", "ptr_add", "ptr_null",
"ptr_is_null", "as_fixed", "as_int", "file_open", "file_read", "file_write",
"poke32", "peekp", "pokep", "peekf", "pokef", "ptr_add",
"as_fixed", "as_int", "file_open", "file_read", "file_write",
"file_seek", "file_tell", "file_close", "read_byte", "write_byte", "print_str",
"str_len", "os_exit", "os_time",
"is_windowed", "game_title", "win_open", "win_poll", "win_present",

View file

@ -167,7 +167,7 @@
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\b" },
{ "name": "storage.type.ludic", "match": "\\b(program|property|model|enum|ui|const|var|let|fn|handler|entry|state)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\b" },
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
]
},
@ -198,7 +198,7 @@
{
"comment": "compiler intrinsics — these lower straight to libc or the OS",
"name": "support.function.intrinsic.ludic",
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peek8|poke8|peek32|poke32|peekp|pokep|peekf|pokef|ptr_add|ptr_null|ptr_is_null|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|print_str|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peek8|poke8|peek32|poke32|peekp|pokep|peekf|pokef|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|print_str|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
}
]
},

View file

@ -167,7 +167,7 @@
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\b" },
{ "name": "storage.type.ludic", "match": "\\b(program|property|model|enum|ui|const|var|let|fn|handler|entry|state)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|fixed|bool|entity|str|ptr|void)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\b" },
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
]
},
@ -198,7 +198,7 @@
{
"comment": "compiler intrinsics — these lower straight to libc or the OS",
"name": "support.function.intrinsic.ludic",
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peek8|poke8|peek32|poke32|peekp|pokep|peekf|pokef|ptr_add|ptr_null|ptr_is_null|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|print_str|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
"match": "\\b(mem_alloc|mem_realloc|mem_free|mem_copy|mem_set|peek8|poke8|peek32|poke32|peekp|pokep|peekf|pokef|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|print_str|str_len|os_exit|os_time|os_argc|os_arg|file_stderr|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
}
]
},

View file

@ -161,8 +161,6 @@ static const LBuiltin LUDIC_INTRINSICS[] = {
{"peekf","peekf(p: ptr, off: int) -> fixed","Read a fixed-point word."},
{"pokef","pokef(p: ptr, off: int, v: fixed)","Write a fixed-point word."},
{"ptr_add","ptr_add(p: ptr, off: int) -> ptr","Offset a pointer by bytes."},
{"ptr_null","ptr_null() -> ptr","The null pointer."},
{"ptr_is_null","ptr_is_null(p: ptr) -> bool","Null test."},
{"as_fixed","as_fixed(i: int) -> fixed","Reinterpret an int as fixed (no conversion)."},
{"as_int","as_int(f: fixed) -> int","Reinterpret a fixed as int (no conversion)."},
{"file_open","file_open(path: str, mode: str) -> ptr","fopen."},
@ -259,7 +257,7 @@ static void lud_lex(LLex* L, const char* src){
int len = i - s; char w[128];
if (len < (int)sizeof(w)){ memcpy(w, src + s, len); w[len] = 0; } else { w[0] = 0; }
int k = LT_ID;
if (!strcmp(w, "true") || !strcmp(w, "false")) k = LT_BOOL;
if (!strcmp(w, "true") || !strcmp(w, "false") || !strcmp(w, "null")) k = LT_BOOL;
else if (lud_in(LUDIC_TYPES, w)) k = LT_TYPE;
else if (lud_in(LUDIC_PHASES, w)) k = LT_PHASE;
else if (lud_is_keyword(w)) k = LT_KW;