refactor(lang): rename the ptr/ptrs types to pointer/pointers

Expand the abbreviated pointer types to full words on the language surface:
  ptr   ->  pointer     (a raw address / FFI handle)
  ptrs  ->  pointers    (a buffer of pointers)

The Ludic type name is distinct from LLVM's own `ptr` spelling: llty() maps
`pointer`/`pointers` to LLVM `ptr`, and the emitted IR keeps `ptr`, so only
the Ludic-level surface changes. Rewrites type annotations across all
sources, the 8 hardcoded pointer type-tags, the `pointers`-buffer indexing
in emit_addr, the grammars/LSP/JetBrains tokens, and the docs
(type-ptr -> type-pointer, type-ptrs -> type-pointers). int/bool keep their
conventional short spelling (like Math).

Reseeded; C-free fixpoint holds; all suites green (45/24/29); site + check.py OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-30 01:58:54 +03:00
parent b7745a4600
commit effb3f637f
76 changed files with 516 additions and 516 deletions

View file

@ -66,9 +66,9 @@ const E_SLICE: int = 44 # s[a..b] — substring (a=base, b=start, c=end
property Node {
kind: int = 0
s: ptr = null # name / operator / string / type-of-new
s: pointer = null # name / operator / string / type-of-new
ival: int = 0 # int literal, bool, flags
ty: ptr = null # declared type (let/param/field/fn/var/const)
ty: pointer = 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 = null, len: int = 0, cap: int = 0 }
property Buf { data: pointer = null, len: int = 0, cap: int = 0 }
function buf_new() -> Buf {
let b = new Buf
@ -21,9 +21,9 @@ function buf_putc(b: Buf, c: int) -> void {
b.data[b.len] = c
b.len = b.len + 1
}
function buf_puts(b: Buf, s: ptr) -> void {
function buf_puts(b: Buf, s: pointer) -> void {
var i = 0
while s[i] != 0 { buf_putc(b, s[i]); i = i + 1 }
}
function buf_puti(b: Buf, n: int) -> void { buf_puts(b, itoa(n)) }
function buf_str(b: Buf) -> ptr { b.data[b.len] = 0; return b.data }
function buf_str(b: Buf) -> pointer { b.data[b.len] = 0; return b.data }

View file

@ -2,10 +2,10 @@
# shared by expression loads and assignment stores. Each returns the address
# register; the element/field type is written into g_addr_ty.
var g_addr_ty: ptr # out-param: the type at the computed address
var g_addr_ty: pointer # out-param: the type at the computed address
# address of `base.field`
function emit_member_addr(e: Node) -> ptr {
function emit_member_addr(e: Node) -> pointer {
let base = emit_expr(e.a)
let s = layout_node(base.ty)
if (s == null) { perr(`member access on non-aggregate {base.ty}`) }
@ -19,7 +19,7 @@ function emit_member_addr(e: Node) -> ptr {
}
# address of `base[index]` (slices only in this subset)
function emit_index_addr(e: Node) -> ptr {
function emit_index_addr(e: Node) -> pointer {
let base = emit_expr(e.a)
if not is_slice_ty(base.ty) { # a raw pointer: address of element i
let bi = emit_expr(e.b) # (evaluate index first — it may set g_addr_ty)
@ -33,7 +33,7 @@ function emit_index_addr(e: Node) -> ptr {
g_addr_ty = "fixed"
return rf
}
if (base.ty == "ptrs") { # a `ptrs` buffer: pointer elements
if (base.ty == "pointers") { # a `pointers` buffer: pointer elements
let rp = emit_bind(`getelementptr inbounds ptr, ptr {base.code}, i32 {bi.code}`)
g_addr_ty = "ptr"
return rp

View file

@ -3,14 +3,14 @@
# origin at the top-left; circles are (x, y, r). Squared distances use i64 so a
# large coordinate can't overflow. Each returns a bool.
function is_collide_ns(meth: ptr) -> bool {
function is_collide_ns(meth: pointer) -> bool {
if (meth == "rects") or (meth == "point_rect") { return true }
if (meth == "circles") or (meth == "rect_circle") { return true }
return false
}
# dx*dx + dy*dy widened to i64 (no overflow for 32-bit deltas)
function coll_sq_sum(dx: ptr, dy: ptr) -> ptr {
function coll_sq_sum(dx: pointer, dy: pointer) -> pointer {
let dx64 = emit_bind(`sext i32 {dx} to i64`)
let dy64 = emit_bind(`sext i32 {dy} to i64`)
let xx = emit_bind(`mul i64 {dx64}, {dx64}`)
@ -19,14 +19,14 @@ function coll_sq_sum(dx: ptr, dy: ptr) -> ptr {
}
# max(lo, min(v, hi)) — clamp v into [lo, hi]
function coll_clamp(v: ptr, lo: ptr, hi: ptr) -> ptr {
function coll_clamp(v: pointer, lo: pointer, hi: pointer) -> pointer {
let c1 = emit_bind(`icmp slt i32 {v}, {hi}`)
let t = emit_bind(`select i1 {c1}, i32 {v}, i32 {hi}`)
let c2 = emit_bind(`icmp sgt i32 {lo}, {t}`)
return emit_bind(`select i1 {c2}, i32 {lo}, i32 {t}`)
}
function emit_collide_ns(meth: ptr, e: Node) -> Val {
function emit_collide_ns(meth: pointer, e: Node) -> Val {
if (meth == "rects") { # AABB overlap of two rects
let ax = emit_expr(e.kids[0]); let ay = emit_expr(e.kids[1]); let aw = emit_expr(e.kids[2]); let ah = emit_expr(e.kids[3])
let bx = emit_expr(e.kids[4]); let by = emit_expr(e.kids[5]); let bw = emit_expr(e.kids[6]); let bh = emit_expr(e.kids[7])

View file

@ -9,7 +9,7 @@
# Edit the palette there and regenerate; do not hand-edit this file.
# ============================================================================
function color_lookup(name: ptr) -> int {
function color_lookup(name: pointer) -> int {
if (name == "White") { return 0xFFFFFF }
if (name == "Snow") { return 0xFFFAFA }
if (name == "Ivory") { return 0xFFFFF0 }

View file

@ -3,20 +3,20 @@
# and blend them with plain integer/fixed math. rgb/rgba pack channels; lerp/
# darken/lighten/with_alpha transform an existing color.
function is_colorfn_ns(meth: ptr) -> bool {
function is_colorfn_ns(meth: pointer) -> bool {
if (meth == "rgb") or (meth == "rgba") or (meth == "lerp") { return true }
if (meth == "darken") or (meth == "lighten") or (meth == "with_alpha") { return true }
return false
}
# (c >> shift) & 255 -> code of a channel value
function color_ch(c: ptr, shift: ptr) -> ptr {
function color_ch(c: pointer, shift: pointer) -> pointer {
let sh = emit_bind(`lshr i32 {c}, {shift}`)
return emit_bind(`and i32 {sh}, 255`)
}
# (r << 16) | (g << 8) | b -> code of a packed color
function color_pack(r: ptr, g: ptr, b: ptr) -> ptr {
function color_pack(r: pointer, g: pointer, b: pointer) -> pointer {
let r16 = emit_bind(`shl i32 {r}, 16`)
let g8 = emit_bind(`shl i32 {g}, 8`)
let rg = emit_bind(`or i32 {r16}, {g8}`)
@ -24,14 +24,14 @@ function color_pack(r: ptr, g: ptr, b: ptr) -> ptr {
}
# ch0 + ((ch1 - ch0) * t >> 16), t a fixed 0..1 -> code of a blended channel
function color_lerp_ch(ch0: ptr, ch1: ptr, t: ptr) -> ptr {
function color_lerp_ch(ch0: pointer, ch1: pointer, t: pointer) -> pointer {
let d = emit_bind(`sub i32 {ch1}, {ch0}`)
let dt = emit_bind(`mul i32 {d}, {t}`)
let dsh = emit_bind(`ashr i32 {dt}, 16`)
return emit_bind(`add i32 {ch0}, {dsh}`)
}
function emit_colorfn_ns(meth: ptr, e: Node) -> Val {
function emit_colorfn_ns(meth: pointer, e: Node) -> Val {
if (meth == "rgb") { # rgb(r, g, b) -> 0xRRGGBB
let r = emit_expr(e.kids[0]); let g = emit_expr(e.kids[1]); let b = emit_expr(e.kids[2])
return val(color_pack(r.code, g.code, b.code), "int")

View file

@ -2,8 +2,8 @@
# 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 = null, ty: ptr = null }
function val(code: ptr, ty: ptr) -> Val { let v = new Val; v.code = code; v.ty = ty; return v }
property Val { code: pointer = null, ty: pointer = null }
function val(code: pointer, ty: pointer) -> 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
@ -13,9 +13,9 @@ var ll_lbl: int = 0 # label counter
var ll_str: int = 0 # string-constant counter
# local environment (parallel slices), reset per function
var loc_name: []ptr
var loc_reg: []ptr
var loc_ty: []ptr
var loc_name: []pointer
var loc_reg: []pointer
var loc_ty: []pointer
var loc_mut: []int # 1 = mutable (var / param / loop-var), 0 = immutable (let)
var nloc: int = 0
var g_uses_str: bool = false # a `str + str` / `str == str` was emitted -> emit the prelude
@ -28,13 +28,13 @@ var g_uses_hashrt: bool = false # Hash.of/fnv1a/crc32 was emitted -> emit the b
var g_uses_longstr: bool = false # string(long) / interpolating a long was emitted -> emit fn_long_str
# loop targets for break/continue (innermost last)
var brk_lbl: []ptr
var cnt_lbl: []ptr
var brk_lbl: []pointer
var cnt_lbl: []pointer
var nloop: int = 0
var ret_ty: ptr # current function's return type
var ret_ty: pointer # current function's return type
var g_term: bool = false # did the current block end in a terminator?
var self_stk: []ptr # entity-index slot (ip) per enclosing query, for self()
var self_stk: []pointer # entity-index slot (ip) per enclosing query, for self()
var nself: int = 0
var mach_stk: []Node # enclosing `machine` nodes, so `become` finds its register
var nmach: int = 0
@ -47,69 +47,69 @@ var g_scene_count: int = 0 # parse-time id counter
var g_start_scene: int = 0 # id of the scene marked `start` (else the first)
var g_cur_scene: Node = null # scene owning the handler being emitted, for `become`
function find_scene(name: ptr) -> Node {
function find_scene(name: pointer) -> Node {
var i = 0
while i < len(g_scenes) { if (g_scenes[i].s == name) { return g_scenes[i] }; i = i + 1 }
return null
}
function emit(s: ptr) -> void { buf_puts(code, s) }
function emith(s: ptr) -> void { buf_puts(head, s) }
function emit(s: pointer) -> void { buf_puts(code, s) }
function emith(s: pointer) -> 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.
function emit_alloca(llt: ptr) -> ptr {
function emit_alloca(llt: pointer) -> pointer {
let r = `%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
function nreg() -> ptr { let r = `%t{itoa(ll_t)}`; ll_t = ll_t + 1; return r }
function lbl(pfx: ptr) -> ptr { let r = (pfx + itoa(ll_lbl)); ll_lbl = ll_lbl + 1; return r }
function nreg() -> pointer { let r = `%t{itoa(ll_t)}`; ll_t = ll_t + 1; return r }
function lbl(pfx: pointer) -> pointer { let r = (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.
function llty(t: ptr) -> ptr {
function llty(t: pointer) -> pointer {
if (t == "int") or (t == "bool") or (t == "fixed") or (t == "entity") { return "i32" } # entity = an i32 handle (self())
if (t == "long") { return "i64" } # a 64-bit signed integer
if (t == "byte") { return "i8" } # a single byte (p[i] on a raw ptr)
if (t == "words") or (t == "fixeds") or (t == "ptrs") { return "ptr" } # typed buffers
if (t == "words") or (t == "fixeds") or (t == "pointers") { return "ptr" } # typed buffers
if (t == "void") { return "void" }
return "ptr"
}
function is_slice_ty(t: ptr) -> bool { return t[0] == 91 and t[1] == 93 } # "[]"
function slice_elem(t: ptr) -> ptr { return t[2..len(t)] }
function is_slice_ty(t: pointer) -> bool { return t[0] == 91 and t[1] == 93 } # "[]"
function slice_elem(t: pointer) -> pointer { return t[2..len(t)] }
function find_arch(name: ptr) -> Node {
function find_arch(name: pointer) -> Node {
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_ARCH and (d.s == name) { return d }; i = i + 1 }
return null
}
function find_comp(name: ptr) -> Node {
function find_comp(name: pointer) -> Node {
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_COMP and (d.s == name) { return d }; i = i + 1 }
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.
function layout_node(name: ptr) -> Node { return find_comp(name) }
function layout_ty(name: ptr) -> ptr { return (("%Cmp_") + name) }
function layout_node(name: pointer) -> Node { return find_comp(name) }
function layout_ty(name: pointer) -> pointer { return (("%Cmp_") + name) }
function field_index(s: Node, fname: ptr) -> int {
function field_index(s: Node, fname: pointer) -> int {
var i = 0
while i < len(s.kids) { if (s.kids[i].s == fname) { return i }; i = i + 1 }
return 0 - 1
}
function field_type(s: Node, fname: ptr) -> ptr {
function field_type(s: Node, fname: pointer) -> pointer {
var i = 0
while i < len(s.kids) { if (s.kids[i].s == fname) { return s.kids[i].ty }; i = i + 1 }
return "int"
}
# find a global var/const by name
function find_global(name: ptr) -> Node {
function find_global(name: pointer) -> Node {
var i = 0
while i < len(prog) {
let d = prog[i]
@ -122,7 +122,7 @@ function find_global(name: ptr) -> Node {
# `Enum.Variant` -> the variant's ordinal (its index), or -1 if `ename` names no
# enum with that variant. Enum names live in `prog` like any other declaration.
function enum_ordinal(ename: ptr, vname: ptr) -> int {
function enum_ordinal(ename: pointer, vname: pointer) -> int {
var i = 0
while i < len(prog) {
let d = prog[i]
@ -141,7 +141,7 @@ function enum_ordinal(ename: ptr, vname: ptr) -> int {
}
return 0 - 1
}
function find_fn(name: ptr) -> Node {
function find_fn(name: pointer) -> Node {
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_FN and (d.s == name) { return d }; i = i + 1 }
return null
@ -152,7 +152,7 @@ function find_fn(name: ptr) -> Node {
# the exact linked symbol), and emit_extern_decls emits a matching `declare`. This
# is the transport seam (net_send/net_poll), the windowing/socket FFI, and any
# C/Rust/Zig library binding — the same seam NETWORKING-DESIGN §5 names.
function find_extern(name: ptr) -> Node {
function find_extern(name: pointer) -> Node {
var i = 0
while i < len(prog) { let d = prog[i]; if d.kind == N_EXTERN and (d.s == name) { return d }; i = i + 1 }
return null
@ -163,11 +163,11 @@ function find_extern(name: ptr) -> Node {
# expression with its bare names read as fields of `x`. Populated at parse time.
var g_computed: []Node # each: s = "Prop.field", ty = result type, a = expr
function register_computed(prop: ptr, field: ptr, ty: ptr, e: Node) -> void {
function register_computed(prop: pointer, field: pointer, ty: pointer, e: Node) -> void {
let cf = node(N_FIELD); cf.s = `{prop}.{field}`; cf.ty = ty; cf.a = e
push(g_computed, cf)
}
function computed_expr(prop: ptr, field: ptr) -> Node {
function computed_expr(prop: pointer, field: pointer) -> Node {
if (prop == null) { return null }
let key = `{prop}.{field}`
var i = 0
@ -175,7 +175,7 @@ function computed_expr(prop: ptr, field: ptr) -> Node {
return null
}
# best-effort static type of an expression (for computed-field lookup; emits nothing)
function static_type(e: Node) -> ptr {
function static_type(e: Node) -> pointer {
if e.kind == E_ID { let li = loc_find(e.s); if li >= 0 { return loc_ty[li] } }
return null
}
@ -185,10 +185,10 @@ function static_type(e: Node) -> ptr {
# constructor). Spawn statically knows the model, so no runtime dispatch is needed.
var g_onspawn: []Node # each: s = Model name, a = hook body block
function register_onspawn(model: ptr, body: Node) -> void {
function register_onspawn(model: pointer, body: Node) -> void {
let n = node(N_BLOCK); n.s = model; n.a = body; push(g_onspawn, n)
}
function onspawn_body(model: ptr) -> Node {
function onspawn_body(model: pointer) -> Node {
var i = 0
while i < len(g_onspawn) { if (g_onspawn[i].s == model) { return g_onspawn[i].a }; i = i + 1 }
return null
@ -203,18 +203,18 @@ var g_onattach: []Node # each: s = Property name, a = hook body block
# LC1: `.ty` carries the optional `reason:` binding name (null if the hook took
# no reason). The despawn hook function gains an `i32 %reason` parameter and each
# teardown site passes a constant EndReason (see emit_despawn_hooks / emit_despawn).
function register_ondespawn(model: ptr, body: Node, reason: ptr) -> void {
function register_ondespawn(model: pointer, body: Node, reason: pointer) -> void {
let n = node(N_BLOCK); n.s = model; n.a = body; n.ty = reason; push(g_ondespawn, n)
}
function ondespawn_body(model: ptr) -> Node {
function ondespawn_body(model: pointer) -> Node {
var i = 0
while i < len(g_ondespawn) { if (g_ondespawn[i].s == model) { return g_ondespawn[i].a }; i = i + 1 }
return null
}
function register_onattach(prop: ptr, body: Node) -> void {
function register_onattach(prop: pointer, body: Node) -> void {
let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_onattach, n)
}
function onattach_body(prop: ptr) -> Node {
function onattach_body(prop: pointer) -> Node {
var i = 0
while i < len(g_onattach) { if (g_onattach[i].s == prop) { return g_onattach[i].a }; i = i + 1 }
return null
@ -224,10 +224,10 @@ function onattach_body(prop: ptr) -> Node {
# is removed from a live entity (`detach P on e`), with the property bound by name
# so the body can read its outgoing value before it is cleared.
var g_ondetach: []Node # each: s = Property name, a = hook body block
function register_ondetach(prop: ptr, body: Node) -> void {
function register_ondetach(prop: pointer, body: Node) -> void {
let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_ondetach, n)
}
function ondetach_body(prop: ptr) -> Node {
function ondetach_body(prop: pointer) -> Node {
var i = 0
while i < len(g_ondetach) { if (g_ondetach[i].s == prop) { return g_ondetach[i].a }; i = i + 1 }
return null
@ -237,14 +237,14 @@ function ondetach_body(prop: ptr) -> Node {
# entity, with the property bound by name.
var g_onenable: []Node
var g_ondisable: []Node
function register_onenable(prop: ptr, body: Node) -> void { let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_onenable, n) }
function register_ondisable(prop: ptr, body: Node) -> void { let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_ondisable, n) }
function onenable_body(prop: ptr) -> Node {
function register_onenable(prop: pointer, body: Node) -> void { let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_onenable, n) }
function register_ondisable(prop: pointer, body: Node) -> void { let n = node(N_BLOCK); n.s = prop; n.a = body; push(g_ondisable, n) }
function onenable_body(prop: pointer) -> Node {
var i = 0
while i < len(g_onenable) { if (g_onenable[i].s == prop) { return g_onenable[i].a }; i = i + 1 }
return null
}
function ondisable_body(prop: ptr) -> Node {
function ondisable_body(prop: pointer) -> Node {
var i = 0
while i < len(g_ondisable) { if (g_ondisable[i].s == prop) { return g_ondisable[i].a }; i = i + 1 }
return null
@ -257,15 +257,15 @@ function ondisable_body(prop: ptr) -> Node {
# Gated on `len(g_events) > 0`, so a program with no events is byte-identical.
var g_events: []Node # each: an N_EVENT node (s = name, kids = payload fields, ival=1 if cancellable)
var g_onlisten: []Node # each: N_BLOCK, s = event name, a = listener body block
var g_cancel_addr: ptr = null # EV3: address of the current cancellable dispatch's flag (null outside one)
var g_cancel_addr: pointer = null # EV3: address of the current cancellable dispatch's flag (null outside one)
function register_event(n: Node) -> void { push(g_events, n) }
function find_event(name: ptr) -> Node {
function find_event(name: pointer) -> Node {
var i = 0
while i < len(g_events) { if (g_events[i].s == name) { return g_events[i] }; i = i + 1 }
return null
}
function register_onlisten(evt: ptr, body: Node) -> void {
function register_onlisten(evt: pointer, body: Node) -> void {
let n = node(N_BLOCK); n.s = evt; n.a = body; push(g_onlisten, n)
}
@ -274,7 +274,7 @@ function register_onlisten(evt: ptr, body: Node) -> void {
# presence in g_events is what makes each lifecycle fire site also `emit` it, so
# `find_event(name) != null` doubles as the "is this hook public?" test. Names are
# the stable ABI contract: `model_<M>_spawn`, `model_<M>_despawn`, etc.
function ensure_event(name: ptr, with_reason: bool) -> void {
function ensure_event(name: pointer, with_reason: bool) -> void {
if (find_event(name) != null) { return }
let n = node(N_EVENT); n.s = name
let ent = node(N_FIELD); ent.s = "entity"; ent.ty = "int"; push(n.kids, ent)
@ -282,7 +282,7 @@ function ensure_event(name: ptr, with_reason: bool) -> void {
register_event(n)
}
# a promoted scene/program event has no per-entity payload
function ensure_event_empty(name: ptr) -> void {
function ensure_event_empty(name: pointer) -> void {
if (find_event(name) != null) { return }
let n = node(N_EVENT); n.s = name; register_event(n)
}
@ -291,33 +291,33 @@ function ensure_event_empty(name: ptr) -> void {
# layer L` statement becomes "managed": it gets an @LE_<L> enabled flag and its
# handlers gate on it. Only managed layers pay for this, so a scene program that
# never toggles a layer is byte-identical.
var g_toggled_layers: []ptr
function note_toggled_layer(name: ptr) -> void {
var g_toggled_layers: []pointer
function note_toggled_layer(name: pointer) -> void {
var i = 0
while i < len(g_toggled_layers) { if (g_toggled_layers[i] == name) { return }; i = i + 1 }
push(g_toggled_layers, name)
}
function is_toggled_layer(name: ptr) -> bool {
function is_toggled_layer(name: pointer) -> bool {
var i = 0
while i < len(g_toggled_layers) { if (g_toggled_layers[i] == name) { return true }; i = i + 1 }
return false
}
# is `name` a model (archetype)? — chooses model-vs-handler for a bare enable/disable
function is_model(name: ptr) -> bool { return find_arch_id(name) > 0 }
function is_model(name: pointer) -> bool { return find_arch_id(name) > 0 }
# local variable environment
function loc_reset() -> void { nloc = 0 }
# push a local. Defaults to mutable (params, loop and query bindings are all
# reassignable/rebindable); a `let` binding marks its slot immutable afterward
# via loc_set_mut, so a later `name = …` can be rejected.
function loc_push(name: ptr, r: ptr, ty: ptr) -> void {
function loc_push(name: pointer, r: pointer, ty: pointer) -> void {
if nloc < len(loc_name) { loc_name[nloc] = name; loc_reg[nloc] = r; loc_ty[nloc] = ty; loc_mut[nloc] = 1 }
else { push(loc_name, name); push(loc_reg, r); push(loc_ty, ty); push(loc_mut, 1) }
nloc = nloc + 1
}
function loc_set_mut(m: int) -> void { if nloc > 0 { loc_mut[nloc - 1] = m } }
function loc_find(name: ptr) -> int {
function loc_find(name: pointer) -> int {
var i = nloc - 1
while i >= 0 { if (loc_name[i] == name) { return i }; i = i - 1 }
return 0 - 1

View file

@ -71,9 +71,9 @@ function emit_program() -> void {
g_uses_intstr = false
g_uses_strslice = false
g_uses_loopback = false
loc_name = new []ptr; loc_reg = new []ptr; loc_ty = new []ptr; loc_mut = new []int
brk_lbl = new []ptr; cnt_lbl = new []ptr
self_stk = new []ptr
loc_name = new []pointer; loc_reg = new []pointer; loc_ty = new []pointer; loc_mut = new []int
brk_lbl = new []pointer; cnt_lbl = new []pointer
self_stk = new []pointer
mach_stk = new []Node
emit_header()
emit_extern_decls()
@ -109,7 +109,7 @@ function emit_program() -> void {
# Flush the emitted IR. With a null path it goes to stdout (the pipe the shell
# drivers read); with a path it is written to that file so ludicc can hand it to
# clang itself.
function ir_flush(path: ptr) -> bool {
function ir_flush(path: pointer) -> bool {
let h = buf_str(head)
let c = buf_str(code)
if (path == null) { # raw IR to stdout (no trailing newline)

View file

@ -2,19 +2,19 @@
# t in 0.0..1.0, returning an eased fixed. All pure Q16.16, deterministic. The
# "juice" layer that makes motion feel good (Robert Penner's easings).
function is_ease_ns(meth: ptr) -> bool {
function is_ease_ns(meth: pointer) -> bool {
if (meth == "in") or (meth == "out") or (meth == "in_out") { return true }
if (meth == "back") or (meth == "bounce") or (meth == "elastic") { return true }
return false
}
# n1 * u * u (u a fixed code) -> code of a fixed i32
function ease_bounce_seg(u: ptr) -> ptr {
function ease_bounce_seg(u: pointer) -> pointer {
let uu = fx_mul_code(u, u)
return fx_mul_code(uu, "495616") # 7.5625 * u*u
}
function emit_ease_ns(meth: ptr, e: Node) -> Val {
function emit_ease_ns(meth: pointer, e: Node) -> Val {
let t = emit_expr(e.kids[0])
if (meth == "in") { # ease-in quad: t*t
return val(fx_mul_code(t.code, t.code), "fixed")

View file

@ -1,6 +1,6 @@
# emit_expr.ludic — lower an expression to IR, returning its register and type.
function emit_load_at(addr: ptr, ty: ptr) -> Val {
function emit_load_at(addr: pointer, ty: pointer) -> Val {
let r = emit_bind(`load {llty(ty)}, ptr {addr}`)
return val(r, ty)
}
@ -26,7 +26,7 @@ function emit_logic(e: Node) -> Val {
return val(emit_bind(`load i32, ptr {slot}`), "bool")
}
function cmp_code(op: ptr) -> ptr {
function cmp_code(op: pointer) -> pointer {
if (op == ("<")) { return "slt" }
if (op == ("<=")) { return "sle" }
if (op == (">")) { return "sgt" }
@ -34,10 +34,10 @@ function cmp_code(op: ptr) -> ptr {
if (op == ("==")) { return "eq" }
return "ne"
}
function is_cmp(op: ptr) -> bool {
function is_cmp(op: pointer) -> bool {
return (op == ("<")) or (op == ("<=")) or (op == (">")) or (op == (">=")) or (op == ("==")) or (op == ("!="))
}
function arith_code(op: ptr) -> ptr {
function arith_code(op: pointer) -> pointer {
if (op == ("+")) { return "add" }
if (op == ("-")) { return "sub" }
if (op == ("*")) { return "mul" }
@ -51,7 +51,7 @@ function arith_code(op: ptr) -> ptr {
}
# widen an int value to Q16.16 by shifting left 16 (a fixed value passes through)
function to_fixed(v: Val) -> ptr {
function to_fixed(v: Val) -> pointer {
if (v.ty == "fixed") { return v.code }
return emit_bind(`shl i32 {v.code}, 16`)
}
@ -59,7 +59,7 @@ function to_fixed(v: Val) -> ptr {
# coerce a value's code to the LLVM type of `target`, for the only cross-width
# pair the language has: int (i32) <-> long (i64). int widens with sext, long
# narrows with trunc; everything else (same width, or ptr) passes through.
function coerce_code(v: Val, target: ptr) -> ptr {
function coerce_code(v: Val, target: pointer) -> pointer {
let lt = llty(target)
let vt = llty(v.ty)
if (lt == vt) { return v.code }
@ -69,14 +69,14 @@ function coerce_code(v: Val, target: ptr) -> ptr {
}
# widen an int value to i64 (a long passes through) — the long analogue of to_fixed
function to_long(v: Val) -> ptr {
function to_long(v: Val) -> pointer {
if (llty(v.ty) == "i64") { return v.code }
return emit_bind(`sext i32 {v.code} to i64`)
}
# string operators: `a + b` concatenates, `a == b` / `a != b` compare by content.
# Both call the @fn_str_* prelude (emitted once per program that uses them).
function emit_str_op(op: ptr, a: Val, b: Val) -> Val {
function emit_str_op(op: pointer, a: Val, b: Val) -> Val {
g_uses_str = true
if (op == ("+")) {
return val(emit_bind(`call ptr @fn_str_concat(ptr {a.code}, ptr {b.code})`), "string")
@ -152,7 +152,7 @@ function args_are_named(e: Node) -> bool {
while i < len(e.kids) { if e.kids[i].kind == E_FINIT { return true }; i = i + 1 }
return false
}
function reorder_named(e: Node, labels: []ptr) -> void {
function reorder_named(e: Node, labels: []pointer) -> void {
if not args_are_named(e) { return }
var i = 0
while i < len(e.kids) {
@ -173,15 +173,15 @@ function reorder_named(e: Node, labels: []ptr) -> void {
e.kids = out
}
# The parameter labels of a resolved fn/extern, in declaration order.
function param_labels(fn: Node) -> []ptr {
let out = new []ptr
function param_labels(fn: Node) -> []pointer {
let out = new []pointer
var i = 0
while i < len(fn.kids) { if fn.kids[i].kind == N_PARAM { push(out, fn.kids[i].s) }; i = i + 1 }
return out
}
function param_types(fn: Node) -> []ptr {
let out = new []ptr
function param_types(fn: Node) -> []pointer {
let out = new []pointer
var i = 0
while i < len(fn.kids) { if fn.kids[i].kind == N_PARAM { push(out, fn.kids[i].ty) }; i = i + 1 }
return out
@ -192,7 +192,7 @@ function param_types(fn: Node) -> []ptr {
# runtime builtin plus the parameter labels callers may use as named arguments;
# after reordering we rewrite the callee to that bare name and fall back into the
# ordinary builtin path (which resolves it to its rt_ function).
function emit_ns_call(ns: ptr, meth: ptr, e: Node) -> Val {
function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
# Math.* is computed inline (deterministic fixed-point), not routed through a
# bare rt_ name — so `floor`/`round`/`lerp` never leak into the bare namespace.
if (ns == "Math") {
@ -231,8 +231,8 @@ function emit_ns_call(ns: ptr, meth: ptr, e: Node) -> Val {
if is_hash_ns(meth) { return emit_hash_ns(meth, e) }
perr(`unknown builtin Hash.{meth}`)
}
var bare: ptr = null
let labels = new []ptr
var bare: pointer = null
let labels = new []pointer
if (ns == "Screen") {
if (meth == "clear") { bare = "clear"; push(labels, "color") }
if (meth == "fill_rectangle") { bare = "fill_rect"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height"); push(labels, "color") }
@ -428,7 +428,7 @@ function emit_call(e: Node) -> Val {
if (name == "bytes") { # bytes(n): allocate n bytes -> a byte buffer
let n = emit_expr(e.kids[0])
let w = emit_bind(`zext i32 {n.code} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "ptr")
return val(emit_bind(`call ptr @malloc(i64 {w})`), "pointer")
}
if (name == "words") { # words(n): allocate n 32-bit words
let n = emit_expr(e.kids[0])
@ -470,8 +470,8 @@ function emit_call(e: Node) -> Val {
let ext = find_extern(name)
if (ext != null) {
reorder_named(e, param_labels(ext))
let eargs = new []ptr
let eatys = new []ptr
let eargs = new []pointer
let eatys = new []pointer
var ei = 0
while ei < len(e.kids) { let v = emit_expr(e.kids[ei]); push(eargs, v.code); push(eatys, v.ty); ei = ei + 1 }
let erl = llty(ext.ty)
@ -501,8 +501,8 @@ function emit_call(e: Node) -> Val {
# evaluate args first (their IR is emitted before the call instruction), coercing
# each to the parameter's declared type so an int passed for a `long` widens.
let ptys = param_types(fn2)
let args = new []ptr
let atys = new []ptr
let args = new []pointer
let atys = new []pointer
var i = 0
while i < len(e.kids) {
let v = emit_expr(e.kids[i])
@ -530,7 +530,7 @@ function emit_expr(e: Node) -> Val {
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_NULL { return val("null", "pointer") }
if e.kind == E_SLICE { # s[a..b] -> a fresh substring
let base = emit_expr(e.a)
let lo = emit_expr(e.b)

View file

@ -50,7 +50,7 @@ function emit_call_one(d: Node) -> void {
# declaration (draw) order. The active scene is snapshotted once per phase, so a
# `become` mid-phase takes effect at the next phase boundary — exactly one scene
# is active within any single phase.
function emit_calls_for_phase(phase: ptr) -> void {
function emit_calls_for_phase(phase: pointer) -> void {
var i = 0
while i < len(prog) {
let d = prog[i]
@ -81,7 +81,7 @@ function emit_calls_for_phase(phase: ptr) -> void {
# on enter / on exit compile to void functions @scene_enter_<Name> /
# @scene_exit_<Name>, called at the transition point (and enter at boot for the
# start scene). Emitted for every scene, empty body when the hook is absent.
function emit_scene_fn(name: ptr, kind: ptr, body: Node) -> void {
function emit_scene_fn(name: pointer, kind: pointer, body: Node) -> void {
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
ret_ty = "void"
let fbody = buf_new()

View file

@ -11,7 +11,7 @@
# Hash.mix(x) fmix32 avalanche of a single int (turn a counter into a seed)
# Hash.combine(...) fold several ints into one (e.g. world_seed, cx, cy)
function is_hash_ns(meth: ptr) -> bool {
function is_hash_ns(meth: pointer) -> bool {
if (meth == "of") or (meth == "fnv1a") or (meth == "crc32") { return true }
if (meth == "mix") or (meth == "combine") { return true }
if (meth == "of64") or (meth == "fnv1a_64") or (meth == "mix64") { return true }
@ -21,7 +21,7 @@ function is_hash_ns(meth: ptr) -> bool {
# fmix32 (MurmurHash3 finalizer) of a single i32 -> code of an i32. A strong
# avalanche: flips ~half the output bits for any one input bit. Used on its own
# (Hash.mix) and nowhere else — combine has its own mixing step.
function hash_mix_code(x: ptr) -> ptr {
function hash_mix_code(x: pointer) -> pointer {
let a = emit_bind(`lshr i32 {x}, 16`)
let b = emit_bind(`xor i32 {x}, {a}`)
let c = emit_bind(`mul i32 {b}, -2048144789`) # * 0x85ebca6b
@ -35,7 +35,7 @@ function hash_mix_code(x: ptr) -> ptr {
# fmix64 (MurmurHash3 64-bit finalizer) of a single i64 -> code of an i64. The
# 64-bit twin of hash_mix_code: shift by 33 and multiply by the two 64-bit
# constants. Backs Hash.mix64.
function hash_mix64_code(x: ptr) -> ptr {
function hash_mix64_code(x: pointer) -> pointer {
let a = emit_bind(`lshr i64 {x}, 33`)
let b = emit_bind(`xor i64 {x}, {a}`)
let c = emit_bind(`mul i64 {b}, -49064778989728563`) # * 0xff51afd7ed558ccd
@ -46,7 +46,7 @@ function hash_mix64_code(x: ptr) -> ptr {
return emit_bind(`xor i64 {f}, {g}`)
}
function emit_hash_ns(meth: ptr, e: Node) -> Val {
function emit_hash_ns(meth: pointer, e: Node) -> Val {
if (meth == "of") or (meth == "fnv1a") { # FNV-1a 32-bit over the bytes
g_uses_hashrt = true
let s = emit_expr(e.kids[0])
@ -74,7 +74,7 @@ function emit_hash_ns(meth: ptr, e: Node) -> Val {
# seed = seed ^ (v + 0x9e3779b9 + (seed << 6) + (seed >> 2))
# order-sensitive and deterministic; seed starts at 0 so a single argument is
# still well-mixed with the golden-ratio constant.
var seed: ptr = "0"
var seed: pointer = "0"
var i = 0
while i < len(e.kids) {
let v = emit_expr(e.kids[i])

View file

@ -4,7 +4,7 @@
function 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
function emit_str_const(s: ptr) -> ptr {
function emit_str_const(s: pointer) -> pointer {
let name = `@.str{itoa(ll_str)}`
ll_str = ll_str + 1
let n = len(s)
@ -25,7 +25,7 @@ function emit_str_const(s: ptr) -> ptr {
}
# the constant initializer for a global var: a literal, or 0/null
function global_init(d: Node) -> ptr {
function global_init(d: Node) -> pointer {
if (d.a == null) { if (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) }

View file

@ -6,7 +6,7 @@ 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).
function is_intrinsic(name: ptr) -> bool {
function is_intrinsic(name: pointer) -> bool {
if (name == "resize") { return true }
if (name == "file_open") or (name == "file_read") or (name == "file_write") { return true }
if (name == "file_seek") or (name == "file_tell") or (name == "file_close") { return true }
@ -16,22 +16,22 @@ function is_intrinsic(name: ptr) -> bool {
return false
}
# emit " <r> = <rest>\n" and return r
function emit_bind(rest: ptr) -> ptr { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
function emit_bind(rest: pointer) -> pointer { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
function arg_code(e: Node, i: int) -> ptr { let v = emit_expr(e.kids[i]); return v.code }
function arg_code(e: Node, i: int) -> pointer { let v = emit_expr(e.kids[i]); return v.code }
function emit_intrinsic(name: ptr, e: Node) -> Val {
function emit_intrinsic(name: pointer, e: Node) -> Val {
g_intrin_ok = true
# ptr_null / ptr_is_null are the `null` literal and `x == null` now.
# mem_alloc is bytes(n) / words(n) now (see emit_call).
if (name == "resize") {
let p = arg_code(e, 0); let n = arg_code(e, 1)
let w = emit_bind(`zext i32 {n} to i64`)
return val(emit_bind(`call ptr @realloc(ptr {p}, i64 {w})`), "ptr")
return val(emit_bind(`call ptr @realloc(ptr {p}, i64 {w})`), "pointer")
}
if (name == "file_open") {
let p = arg_code(e, 0); let m = arg_code(e, 1)
return val(emit_bind(`call ptr @fopen(ptr {p}, ptr {m})`), "ptr")
return val(emit_bind(`call ptr @fopen(ptr {p}, ptr {m})`), "pointer")
}
if (name == "file_read") or (name == "file_write") {
let f = arg_code(e, 0); let b = arg_code(e, 1); let n = arg_code(e, 2)
@ -71,8 +71,8 @@ function emit_intrinsic(name: ptr, e: Node) -> Val {
g_term = true
return val("0", "void")
}
if (name == "file_stderr") { return val(emit_bind("load ptr, ptr @__stderrp"), "ptr") }
if (name == "file_stdout") { return val(emit_bind("load ptr, ptr @__stdoutp"), "ptr") }
if (name == "file_stderr") { return val(emit_bind("load ptr, ptr @__stderrp"), "pointer") }
if (name == "file_stdout") { return val(emit_bind("load ptr, ptr @__stdoutp"), "pointer") }
if (name == "run") {
let c = arg_code(e, 0)
return val(emit_bind(`call i32 @system(ptr {c})`), "int")

View file

@ -4,7 +4,7 @@
var g_windowed: bool = false # headless by default (games read stdin / dump PPM)
function is_intrinsic2(name: ptr) -> bool {
function is_intrinsic2(name: pointer) -> bool {
if (name == "free") or (name == "fill") { return true }
if (name == "offset") or (name == "read_char") { return true }
if (name == "as_fixed") or (name == "as_int") { return true }
@ -14,7 +14,7 @@ function is_intrinsic2(name: ptr) -> bool {
return false
}
function emit_intrinsic2(name: ptr, e: Node) -> Val {
function emit_intrinsic2(name: pointer, e: Node) -> Val {
if (name == "free") {
let p = arg_code(e, 0); emit(" call void @free(ptr "); emit(p); emit(")\n"); return val("0", "void")
}
@ -28,7 +28,7 @@ function emit_intrinsic2(name: ptr, e: Node) -> Val {
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")
return val(g, "ptr")
return val(g, "pointer")
}
if (name == "read_char") { return val(emit_bind("call i32 @getchar()"), "int") }
if (name == "as_fixed") { let a = emit_expr(e.kids[0]); return val(a.code, "fixed") }

View file

@ -5,13 +5,13 @@
# for reference elements (structs/strings), matching how `==` behaves elsewhere.
# address of element `idx` (an i32 code) in slice header `h`, element LLVM type `elt`
function list_elem_addr(h: ptr, elt: ptr, idx: ptr) -> ptr {
function list_elem_addr(h: pointer, elt: pointer, idx: pointer) -> pointer {
let dp = slice_field(h, 0)
let data = emit_bind(`load ptr, ptr {dp}`)
return emit_bind(`getelementptr inbounds {elt}, ptr {data}, i32 {idx}`)
}
function is_list_ns(meth: ptr) -> bool {
function is_list_ns(meth: pointer) -> bool {
if (meth == "len") or (meth == "push") or (meth == "clear") { return true }
if (meth == "first") or (meth == "last") or (meth == "pop") or (meth == "swap") { return true }
if (meth == "contains") or (meth == "index_of") or (meth == "reverse") { return true }
@ -21,7 +21,7 @@ function is_list_ns(meth: ptr) -> bool {
# grow the slice's backing buffer if it is full, exactly as push does (double,
# or 8 from empty). Leaves length untouched; only capacity/data may change.
function list_grow_if_full(h: ptr, elt: ptr) -> void {
function list_grow_if_full(h: pointer, elt: pointer) -> void {
let lp = slice_field(h, 1); let cp = slice_field(h, 2); let dp = slice_field(h, 0)
let l = emit_bind(`load i32, ptr {lp}`)
let c = emit_bind(`load i32, ptr {cp}`)
@ -43,7 +43,7 @@ function list_grow_if_full(h: ptr, elt: ptr) -> void {
emit(done); emit(":\n")
}
function emit_list_ns(meth: ptr, e: Node) -> Val {
function emit_list_ns(meth: pointer, e: Node) -> Val {
if (meth == "len") { return emit_len(e) } # same header length as len(s)
if (meth == "push") { return emit_push(e) } # same as push(s, v)
let s = emit_expr(e.kids[0])

View file

@ -5,11 +5,11 @@
# integer root, @fn_fx_sin a 256-entry interpolated sine table). Everything is
# plain integer IR, so it is bit-identical on every platform.
function is_math_builtin(name: ptr) -> bool {
function is_math_builtin(name: pointer) -> bool {
return (name == "min") or (name == "max") or (name == "abs") or (name == "clamp")
}
function emit_math_builtin(name: ptr, e: Node) -> Val {
function emit_math_builtin(name: pointer, e: Node) -> Val {
if (name == "abs") {
let a = emit_expr(e.kids[0])
let c = emit_bind(`icmp slt i32 {a.code}, 0`)
@ -32,7 +32,7 @@ function emit_math_builtin(name: ptr, e: Node) -> Val {
}
# a * b in Q16.16 (64-bit intermediate, arithmetic shift back) -> code of an i32
function fx_mul_code(a: ptr, b: ptr) -> ptr {
function fx_mul_code(a: pointer, b: pointer) -> pointer {
let a64 = emit_bind(`sext i32 {a} to i64`)
let b64 = emit_bind(`sext i32 {b} to i64`)
let m = emit_bind(`mul i64 {a64}, {b64}`)
@ -41,7 +41,7 @@ function fx_mul_code(a: ptr, b: ptr) -> ptr {
}
# a / b in Q16.16 (shift the numerator up before the divide) -> code of an i32
function fx_div_code(a: ptr, b: ptr) -> ptr {
function fx_div_code(a: pointer, b: pointer) -> pointer {
let a64 = emit_bind(`sext i32 {a} to i64`)
let ash = emit_bind(`shl i64 {a64}, 16`)
let b64 = emit_bind(`sext i32 {b} to i64`)
@ -50,14 +50,14 @@ function fx_div_code(a: ptr, b: ptr) -> ptr {
}
# lerp(a, b, t) = a + (b - a) * t, all Q16.16 -> code of a fixed i32
function fx_lerp_code(a: ptr, b: ptr, t: ptr) -> ptr {
function fx_lerp_code(a: pointer, b: pointer, t: pointer) -> pointer {
let d = emit_bind(`sub i32 {b}, {a}`)
let dt = fx_mul_code(d, t)
return emit_bind(`add i32 {a}, {dt}`)
}
# inverse_lerp(a, b, v) = (v - a) / (b - a), all Q16.16 -> code of a fixed i32
function fx_inv_lerp_code(a: ptr, b: ptr, v: ptr) -> ptr {
function fx_inv_lerp_code(a: pointer, b: pointer, v: pointer) -> pointer {
let num = emit_bind(`sub i32 {v}, {a}`)
let den = emit_bind(`sub i32 {b}, {a}`)
return fx_div_code(num, den)
@ -66,7 +66,7 @@ function fx_inv_lerp_code(a: ptr, b: ptr, v: ptr) -> ptr {
# Math.* — the namespaced surface. min/max/abs/clamp reuse the bare lowering;
# the rest are new deterministic fixed-point helpers. Returns g_intrin-style via
# a direct Val; callers guard with is_math_ns first.
function is_math_ns(meth: ptr) -> bool {
function is_math_ns(meth: pointer) -> bool {
if (meth == "min") or (meth == "max") or (meth == "abs") or (meth == "clamp") { return true }
if (meth == "sign") or (meth == "floor") or (meth == "ceil") or (meth == "round") { return true }
if (meth == "lerp") or (meth == "inverse_lerp") or (meth == "remap") { return true }
@ -79,7 +79,7 @@ function is_math_ns(meth: ptr) -> bool {
return false
}
function emit_math_ns(meth: ptr, e: Node) -> Val {
function emit_math_ns(meth: pointer, e: Node) -> Val {
if (meth == "min") or (meth == "max") or (meth == "abs") or (meth == "clamp") {
return emit_math_builtin(meth, e)
}

View file

@ -3,17 +3,17 @@
# read and write one byte. The low-level escape hatch, PICO-8's memcpy/memset/
# peek/poke by another name.
function is_mem_ns(meth: ptr) -> bool {
function is_mem_ns(meth: pointer) -> bool {
if (meth == "bytes") or (meth == "words") { return true }
if (meth == "copy") or (meth == "fill") or (meth == "peek") or (meth == "poke") { return true }
return false
}
function emit_mem_ns(meth: ptr, e: Node) -> Val {
function emit_mem_ns(meth: pointer, e: Node) -> Val {
if (meth == "bytes") { # allocate n bytes -> a byte buffer
let n = emit_expr(e.kids[0])
let w = emit_bind(`zext i32 {n.code} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "ptr")
return val(emit_bind(`call ptr @malloc(i64 {w})`), "pointer")
}
if (meth == "words") { # allocate n 32-bit words
let n = emit_expr(e.kids[0])

View file

@ -19,8 +19,8 @@ var g_uses_loopback: bool = false
# component participates in the entity's model (the model member is @Sync,
# member.ival==1). Participation is decided per model use-site.
function net_field_ibytes(ty: ptr) -> int { if (llty(ty) == "i8") { return 1 }; return 4 }
function net_field_bytes(ty: ptr) -> ptr { if (llty(ty) == "i8") { return "1" }; return "4" }
function net_field_ibytes(ty: pointer) -> int { if (llty(ty) == "i8") { return 1 }; return 4 }
function net_field_bytes(ty: pointer) -> pointer { if (llty(ty) == "i8") { return "1" }; return "4" }
# total replicated bytes for model m (compile-time constant)
function net_model_bytes(m: Node) -> int {
@ -67,7 +67,7 @@ function net_has_role() -> bool {
function net_any() -> bool { return net_has_sync() or net_has_owned() or net_has_role() }
# ---- diagnostics -------------------------------------------------------------
function net_warn(msg: ptr) -> void {
function net_warn(msg: pointer) -> void {
let e = file_stderr()
file_write(e, "ludicc(self): warning: ", 23)
file_write(e, msg, len(msg))
@ -300,14 +300,14 @@ function net_has_remote() -> bool {
return false
}
# stable wire id for an event = its index in g_events (same program both peers)
function net_event_id(name: ptr) -> int {
function net_event_id(name: pointer) -> int {
var i = 0
while i < len(g_events) { if (g_events[i].s == name) { return i }; i = i + 1 }
return 0 - 1
}
# the transport symbols: an `extern fn` override, else the built-in loopback.
function net_send_sym() -> ptr { let x = find_extern("net_send"); if (x != null) { return x.a.s }; return "L_net_send" }
function net_poll_sym() -> ptr { let x = find_extern("net_poll"); if (x != null) { return x.a.s }; return "L_net_poll" }
function net_send_sym() -> pointer { let x = find_extern("net_send"); if (x != null) { return x.a.s }; return "L_net_send" }
function net_poll_sym() -> pointer { let x = find_extern("net_poll"); if (x != null) { return x.a.s }; return "L_net_poll" }
# net_pump(): poll every pending frame and re-emit it locally. The receive path
# of a remote event — the runtime/game calls this each tick.

View file

@ -2,12 +2,12 @@
# push(slice, v) and len(slice). Slices are a { data, len, cap } header the
# holder points at, so growth is visible to every holder.
function emit_sizeof(llt: ptr) -> ptr {
function emit_sizeof(llt: pointer) -> pointer {
let p = emit_bind(`getelementptr {llt}, ptr null, i32 1`)
return emit_bind(`ptrtoint ptr {p} to i64`)
}
function emit_new_struct(name: ptr) -> Val {
function emit_new_struct(name: pointer) -> Val {
let s = layout_node(name) # a struct or a property — same shape
if (s == null) { perr("unknown record type in new") }
let lty = layout_ty(name)
@ -29,7 +29,7 @@ function emit_new_struct(name: ptr) -> Val {
return val(obj, name)
}
function emit_new_slice(ty: ptr) -> Val {
function emit_new_slice(ty: pointer) -> Val {
let sz = emit_sizeof("%LSlice")
let h = emit_bind(`call ptr @malloc(i64 {sz})`)
let d0 = nreg(); emit(" "); emit(d0); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h); emit(", i32 0, i32 0\n")
@ -41,7 +41,7 @@ function emit_new_slice(ty: ptr) -> Val {
return val(h, ty)
}
function slice_field(h: ptr, i: int) -> ptr {
function slice_field(h: pointer, i: int) -> pointer {
let r = nreg()
emit(" "); emit(r); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h)
emit(", i32 0, i32 "); emit(itoa(i)); emit("\n")

View file

@ -2,7 +2,7 @@
# components/archetypes each carries, binds the requested components, and runs
# the body once per match. Mirrors ll_query in compiler/back/ir_ecs.c.
function find_arch_id(name: ptr) -> int {
function find_arch_id(name: pointer) -> int {
var i = 0; var n = 1
while i < len(prog) { if prog[i].kind == N_ARCH { if (prog[i].s == name) { return n }; n = n + 1 }; i = i + 1 }
return 0

View file

@ -13,10 +13,10 @@ var g_iok: int = 0
# NOTE: a string initializer on a module `ptr` var lowers to null (global_init),
# so these are seeded at runtime in emit_snapshot before first use — never read
# them uninitialized (a null string `==` would deref and crash the compiler).
var g_snap_mode: ptr = null # "file" | "save" | "load" | "size"
var g_off: ptr = null # current byte-offset register, buffer modes
var g_snap_mode: pointer = null # "file" | "save" | "load" | "size"
var g_off: pointer = null # current byte-offset register, buffer modes
function emit_io(fn2: ptr, p: ptr, bytes: ptr) -> void {
function emit_io(fn2: pointer, p: pointer, bytes: pointer) -> void {
if (g_snap_mode == "file") {
let r = `%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")
@ -43,7 +43,7 @@ function emit_io(fn2: ptr, p: ptr, bytes: ptr) -> void {
g_iok = g_iok + 1
}
function emit_snapshot_blocks(fn2: ptr) -> void {
function emit_snapshot_blocks(fn2: pointer) -> void {
g_iok = 0
g_off = "0"
let me = itoa(MAX_ENT)

View file

@ -1,7 +1,7 @@
# emit_spawn.ludic — spawn / despawn / self(), and seeding a component's fields
# from its declared defaults plus any per-spawn overrides.
function emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
function emit_init_component(e: pointer, comp: pointer, rec: Node) -> void {
let me = itoa(MAX_ENT)
let c = find_comp(comp)
if (c == null) { perr(`spawn: unknown property {comp}`) }
@ -54,7 +54,7 @@ function emit_init_component(e: ptr, comp: ptr, rec: Node) -> void {
# bind each of a model's properties to entity `e`'s component storage, so an
# @OnSpawn hook body can address them by name (like a query binding for one entity).
function emit_bind_props(model: Node, e: ptr) -> void {
function emit_bind_props(model: Node, e: pointer) -> void {
let me = itoa(MAX_ENT)
var c = 0
while c < len(model.kids) {
@ -68,7 +68,7 @@ function emit_bind_props(model: Node, e: ptr) -> void {
}
}
function emit_spawn(st: Node) -> ptr {
function emit_spawn(st: Node) -> pointer {
let e = emit_bind("call i32 @L_alloc()")
let ak = find_arch_id(st.s)
if ak > 0 {

View file

@ -11,7 +11,7 @@ function emit_block(b: Node) -> void {
}
# store `val` (llvm type `lt`) into address `addr`
function store_at(lt: ptr, v: ptr, addr: ptr) -> void {
function store_at(lt: pointer, v: pointer, addr: pointer) -> void {
emit(" store "); emit(lt); emit(" "); emit(v); emit(", ptr "); emit(addr); emit("\n")
}
@ -167,8 +167,8 @@ function emit_emit(st: Node) -> Val {
let ev = find_event(st.s)
if (ev == null) { perr(`emit: unknown event {st.s}`) }
# evaluate each payload field in declared order (default for a missing arg)
let fcodes = new []ptr
let ftys = new []ptr
let fcodes = new []pointer
let ftys = new []pointer
var f = 0
while f < len(ev.kids) {
let fd = ev.kids[f]
@ -261,7 +261,7 @@ function emit_stmt(st: Node) -> void {
perr("cannot emit statement")
}
function loop_push(cont: ptr, brk: ptr) -> void {
function loop_push(cont: pointer, brk: pointer) -> void {
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

View file

@ -3,7 +3,7 @@
# contains/index_of/to_int) allocate nothing; slice/from_int/equals/concat reuse
# the string preludes that the `+`, `s[a..b]` and string(int) operators emit.
function is_text_ns(meth: ptr) -> bool {
function is_text_ns(meth: pointer) -> bool {
if (meth == "length") or (meth == "char_at") or (meth == "slice") { return true }
if (meth == "equals") or (meth == "concat") or (meth == "to_int") or (meth == "from_int") { return true }
if (meth == "starts_with") or (meth == "ends_with") { return true }
@ -14,7 +14,7 @@ function is_text_ns(meth: ptr) -> bool {
return false
}
function emit_text_ns(meth: ptr, e: Node) -> Val {
function emit_text_ns(meth: pointer, e: Node) -> Val {
if (meth == "from_int") { # int -> string, same as string(n)
let n = emit_expr(e.kids[0])
g_uses_intstr = true

View file

@ -3,12 +3,12 @@
# the wall clock and is explicitly non-deterministic. The frame clock ticks at a
# fixed 60 per second, so delta is the constant 1/60 s = 1092 in Q16.16.
function is_time_ns(meth: ptr) -> bool {
function is_time_ns(meth: pointer) -> bool {
if (meth == "frame") or (meth == "delta") or (meth == "elapsed") or (meth == "now") { return true }
return false
}
function emit_time_ns(meth: ptr, e: Node) -> Val {
function emit_time_ns(meth: pointer, e: Node) -> Val {
if (meth == "frame") { # completed frames since start
return val(emit_bind("load i32, ptr @L_frame"), "int")
}

View file

@ -14,7 +14,7 @@ function ui_wtype(w: Node) -> int {
if (s == "image") { return 5 }; if (s == "spacer") { return 6 }
return 0
}
function ui_prop(w: Node, key: ptr) -> Node {
function ui_prop(w: Node, key: pointer) -> Node {
var i = 0
while i < len(w.b.kids) { if (w.b.kids[i].s == key) { return w.b.kids[i].a }; i = i + 1 }
return null
@ -40,7 +40,7 @@ function has_ui() -> bool {
}
# index of a UI_<name>: a ui block's root, or a widget's id=
function ui_index_of(nm: ptr) -> int {
function ui_index_of(nm: pointer) -> int {
let s = nm[3..len(nm)] # strip "UI_"
let u = 0; var bi = 0
var i = 0
@ -56,14 +56,14 @@ function ui_index_of(nm: ptr) -> int {
}
return 0
}
function is_ui_ident(nm: ptr) -> bool {
function is_ui_ident(nm: pointer) -> bool {
return len(nm) > 3 and nm[0] == 85 and nm[1] == 73 and nm[2] == 95 # "UI_"
}
function ll_ui_set(idx: int, key: int, val: ptr) -> void {
function ll_ui_set(idx: int, key: int, val: pointer) -> void {
emit(" call void @fn_rt_ui_set(i32 "); emit(itoa(idx)); emit(", i32 "); emit(itoa(key)); emit(", i32 "); emit(val); emit(")\n")
}
function ui_prop_key(k: ptr) -> int {
function ui_prop_key(k: pointer) -> int {
if (k == "w") { return 2 }; if (k == "h") { return 3 }; if (k == "x") { return 4 }; if (k == "y") { return 5 }
if (k == "pad") { return 7 }; if (k == "gap") { return 8 }; if (k == "bg") { return 9 }; if (k == "fg") { return 10 }
if (k == "border") { return 11 }; if (k == "grow") { return 13 }; if (k == "font") { return 14 }; if (k == "size") { return 15 }

View file

@ -1,7 +1,7 @@
# io.ludic — reading the input file and writing the output, plus tiny stdio.
# The compiler reads one .ludic file whole and emits LLVM IR text to stdout.
function read_file(path: string) -> ptr {
function read_file(path: string) -> pointer {
let f = file_open(path, "rb")
if (f == null) { return null }
file_seek(f, 0, 2)
@ -15,7 +15,7 @@ function read_file(path: string) -> ptr {
}
# length of a NUL-terminated buffer
function cstr_len(s: ptr) -> int {
function cstr_len(s: pointer) -> int {
var n = 0
while s[n] != 0 { n = n + 1 }
return n

View file

@ -11,18 +11,18 @@ const TK_EOF: int = 5
const TK_FLOAT: int = 6
const TK_INTERP: int = 7 # `text {expr} text` — raw content, split by the parser
property Tok { kind: int = 0, text: ptr = null, ival: int = 0, line: int = 0 }
property Tok { kind: int = 0, text: pointer = null, ival: int = 0, line: int = 0 }
var toks: []Tok
function tok_push(kind: int, text: ptr, ival: int, line: int) -> void {
function tok_push(kind: int, text: pointer, ival: int, line: int) -> void {
let t = new Tok
t.kind = kind; t.text = text; t.ival = ival; t.line = line
push(toks, t)
}
# does src match the 2-char operator op at position i?
function two_at(src: ptr, i: int, a: int, b: int) -> bool {
function two_at(src: pointer, i: int, a: int, b: int) -> bool {
return src[i] == a and src[i + 1] == b
}
@ -37,7 +37,7 @@ function is_op1(c: int) -> bool {
return false
}
function lex(src: ptr) -> void {
function lex(src: pointer) -> void {
toks = new []Tok
var i = 0
var line = 1

View file

@ -438,7 +438,7 @@ declare void @win_close()
@.str337 = private unnamed_addr constant [3 x i8] c"i8\00"
@.str338 = private unnamed_addr constant [6 x i8] c"words\00"
@.str339 = private unnamed_addr constant [7 x i8] c"fixeds\00"
@.str340 = private unnamed_addr constant [5 x i8] c"ptrs\00"
@.str340 = private unnamed_addr constant [9 x i8] c"pointers\00"
@.str341 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str342 = private unnamed_addr constant [5 x i8] c"void\00"
@.str343 = private unnamed_addr constant [5 x i8] c"void\00"
@ -611,7 +611,7 @@ declare void @win_close()
@.str510 = private unnamed_addr constant [33 x i8] c"getelementptr inbounds i32, ptr \00"
@.str511 = private unnamed_addr constant [7 x i8] c", i32 \00"
@.str512 = private unnamed_addr constant [6 x i8] c"fixed\00"
@.str513 = private unnamed_addr constant [5 x i8] c"ptrs\00"
@.str513 = private unnamed_addr constant [9 x i8] c"pointers\00"
@.str514 = private unnamed_addr constant [33 x i8] c"getelementptr inbounds ptr, ptr \00"
@.str515 = private unnamed_addr constant [7 x i8] c", i32 \00"
@.str516 = private unnamed_addr constant [4 x i8] c"ptr\00"
@ -652,12 +652,12 @@ declare void @win_close()
@.str551 = private unnamed_addr constant [23 x i8] c"call ptr @realloc(ptr \00"
@.str552 = private unnamed_addr constant [7 x i8] c", i64 \00"
@.str553 = private unnamed_addr constant [2 x i8] c")\00"
@.str554 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str554 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str555 = private unnamed_addr constant [10 x i8] c"file_open\00"
@.str556 = private unnamed_addr constant [21 x i8] c"call ptr @fopen(ptr \00"
@.str557 = private unnamed_addr constant [7 x i8] c", ptr \00"
@.str558 = private unnamed_addr constant [2 x i8] c")\00"
@.str559 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str559 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str560 = private unnamed_addr constant [10 x i8] c"file_read\00"
@.str561 = private unnamed_addr constant [11 x i8] c"file_write\00"
@.str562 = private unnamed_addr constant [10 x i8] c"zext i32 \00"
@ -709,10 +709,10 @@ declare void @win_close()
@.str608 = private unnamed_addr constant [5 x i8] c"void\00"
@.str609 = private unnamed_addr constant [12 x i8] c"file_stderr\00"
@.str610 = private unnamed_addr constant [25 x i8] c"load ptr, ptr @__stderrp\00"
@.str611 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str611 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str612 = private unnamed_addr constant [12 x i8] c"file_stdout\00"
@.str613 = private unnamed_addr constant [25 x i8] c"load ptr, ptr @__stdoutp\00"
@.str614 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str614 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str615 = private unnamed_addr constant [4 x i8] c"run\00"
@.str616 = private unnamed_addr constant [22 x i8] c"call i32 @system(ptr \00"
@.str617 = private unnamed_addr constant [2 x i8] c")\00"
@ -755,7 +755,7 @@ declare void @win_close()
@.str654 = private unnamed_addr constant [35 x i8] c" = getelementptr inbounds i8, ptr \00"
@.str655 = private unnamed_addr constant [7 x i8] c", i32 \00"
@.str656 = private unnamed_addr constant [2 x i8] c"\0A\00"
@.str657 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str657 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str658 = private unnamed_addr constant [10 x i8] c"read_char\00"
@.str659 = private unnamed_addr constant [20 x i8] c"call i32 @getchar()\00"
@.str660 = private unnamed_addr constant [4 x i8] c"int\00"
@ -2585,7 +2585,7 @@ declare void @win_close()
@.str2484 = private unnamed_addr constant [8 x i8] c" to i64\00"
@.str2485 = private unnamed_addr constant [22 x i8] c"call ptr @malloc(i64 \00"
@.str2486 = private unnamed_addr constant [2 x i8] c")\00"
@.str2487 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str2487 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str2488 = private unnamed_addr constant [6 x i8] c"words\00"
@.str2489 = private unnamed_addr constant [9 x i8] c"mul i32 \00"
@.str2490 = private unnamed_addr constant [4 x i8] c", 4\00"
@ -3593,7 +3593,7 @@ declare void @win_close()
@.str3492 = private unnamed_addr constant [8 x i8] c" to i64\00"
@.str3493 = private unnamed_addr constant [22 x i8] c"call ptr @malloc(i64 \00"
@.str3494 = private unnamed_addr constant [2 x i8] c")\00"
@.str3495 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str3495 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str3496 = private unnamed_addr constant [6 x i8] c"words\00"
@.str3497 = private unnamed_addr constant [9 x i8] c"mul i32 \00"
@.str3498 = private unnamed_addr constant [4 x i8] c", 4\00"
@ -3707,7 +3707,7 @@ declare void @win_close()
@.str3606 = private unnamed_addr constant [6 x i8] c"fixed\00"
@.str3607 = private unnamed_addr constant [5 x i8] c"bool\00"
@.str3608 = private unnamed_addr constant [5 x i8] c"null\00"
@.str3609 = private unnamed_addr constant [4 x i8] c"ptr\00"
@.str3609 = private unnamed_addr constant [8 x i8] c"pointer\00"
@.str3610 = private unnamed_addr constant [28 x i8] c"call ptr @fn_str_slice(ptr \00"
@.str3611 = private unnamed_addr constant [7 x i8] c", i32 \00"
@.str3612 = private unnamed_addr constant [7 x i8] c", i32 \00"

View file

@ -15,7 +15,7 @@
# reseed.sh rely on, so the bootstrap is untouched.
# basename: the part of a path after the last '/'.
function base_name(path: ptr) -> ptr {
function base_name(path: pointer) -> pointer {
var last = 0 - 1
var i = 0
while path[i] != 0 { if path[i] == 47 { last = i }; i = i + 1 }
@ -23,7 +23,7 @@ function base_name(path: ptr) -> ptr {
}
# drop a trailing ".ludic" if present
function strip_ludic(name: ptr) -> ptr {
function strip_ludic(name: pointer) -> pointer {
let n = len(name)
if n > 6 {
if (name[n - 6..n] == ".ludic") { return name[0..n - 6] }
@ -32,21 +32,21 @@ function strip_ludic(name: ptr) -> ptr {
}
# env var with a fallback when unset
function getenv_or(name: ptr, dflt: ptr) -> ptr {
function getenv_or(name: pointer, dflt: pointer) -> pointer {
let v = getenv(name)
if (v == null) { return dflt }
return v
}
# guarantee a directory string ends in '/' so path_join concatenates cleanly
function ensure_slash(d: ptr) -> ptr {
function ensure_slash(d: pointer) -> pointer {
let n = len(d)
if n == 0 { return d }
if d[n - 1] == 47 { return d }
return (d + ("/"))
}
function die(msg: ptr) -> void {
function die(msg: pointer) -> void {
file_write(file_stderr(), msg, len(msg))
exit(1)
}

View file

@ -4,15 +4,15 @@
var pi: int = 0
var prog: []Node # the top-level declarations
var g_game_name: ptr # the `game`/`module` name
var g_game_name: pointer # the `game`/`module` name
function cur() -> Tok { return toks[pi] }
function pk(o: int) -> Tok { return toks[pi + o] }
function is_op(v: ptr) -> bool { let t = toks[pi]; return t.kind == TK_OP and (t.text == v) }
function is_id(v: ptr) -> bool { let t = toks[pi]; return t.kind == TK_ID and (t.text == v) }
function is_kw(v: ptr) -> bool { return is_id(v) }
function is_op(v: pointer) -> bool { let t = toks[pi]; return t.kind == TK_OP and (t.text == v) }
function is_id(v: pointer) -> bool { let t = toks[pi]; return t.kind == TK_ID and (t.text == v) }
function is_kw(v: pointer) -> bool { return is_id(v) }
function perr(msg: ptr) -> void {
function perr(msg: pointer) -> void {
let e = file_stderr()
file_write(e, "ludicc(self): parse error: ", 27)
file_write(e, msg, len(msg))
@ -20,8 +20,8 @@ function perr(msg: ptr) -> void {
exit(1)
}
function eat_op(v: ptr) -> void { if not is_op(v) { perr(v) }; pi = pi + 1 }
function eat_id() -> ptr {
function eat_op(v: pointer) -> void { if not is_op(v) { perr(v) }; pi = pi + 1 }
function eat_id() -> pointer {
let t = toks[pi]
if t.kind != TK_ID { perr("expected identifier") }
pi = pi + 1
@ -30,7 +30,7 @@ function eat_id() -> ptr {
function skipnl() -> void { while toks[pi].kind == TK_NL { pi = pi + 1 } }
# a type: `[]T` slice, or a plain name (int/ptr/str/bool/struct)
function ptype() -> ptr {
function ptype() -> pointer {
if is_op("[") {
pi = pi + 1
eat_op("]")
@ -70,7 +70,7 @@ function args_call(call: Node) -> void {
# ---- string interpolation --------------------------------------------------
# `text {expr} text` desugars to a `+` chain of string literals and `string(expr)`
# holes, so it reuses the string-concat operator and needs no new runtime.
function interp_lit(buf: ptr, len: int) -> Node { let n = node(E_STR); n.s = buf[0..0 + len]; return n }
function interp_lit(buf: pointer, len: int) -> Node { let n = node(E_STR); n.s = buf[0..0 + len]; return n }
function interp_add(acc: Node, part: Node) -> Node {
if acc == null { return part }
return mkbin("+", acc, part)
@ -78,14 +78,14 @@ function interp_add(acc: Node, part: Node) -> Node {
function interp_str(e: Node) -> Node { # wrap a hole in string(...)
let c = node(E_CALL); let id = node(E_ID); id.s = "string"; c.a = id; push(c.kids, e); return c
}
function parse_hole(inner: ptr) -> Node { # re-lex+parse an embedded expression
function parse_hole(inner: pointer) -> Node { # re-lex+parse an embedded expression
let saved_toks = toks; let saved_pi = pi
lex(inner); pi = 0; skipnl()
let e = expr()
toks = saved_toks; pi = saved_pi
return e
}
function parse_interp(raw: ptr) -> Node {
function parse_interp(raw: pointer) -> Node {
let n = len(raw)
var acc: Node = null
let lit = bytes(n + 1)
@ -176,7 +176,7 @@ function p_unary() -> Node {
return p_postfix()
}
function mkbin(op: ptr, l: Node, r: Node) -> Node { let b = node(E_BIN); b.s = op; b.a = l; b.b = r; return b }
function mkbin(op: pointer, l: Node, r: Node) -> Node { let b = node(E_BIN); b.s = op; b.a = l; b.b = r; return b }
# precedence (Go-style, so `flags & MASK == 0` needs no parens): shifts and `&`
# bind like `*`; `|` and `^` bind like `+`; both tighter than comparison.
@ -357,22 +357,22 @@ function parse_fn() -> Node {
function 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
function dir_of(path: ptr) -> ptr {
function dir_of(path: pointer) -> pointer {
var last = 0 - 1
var i = 0
while path[i] != 0 { if path[i] == 47 { last = i }; i = i + 1 }
if last < 0 { return "" }
return path[0..0 + (last + 1)]
}
function path_join(dir: ptr, rel: ptr) -> ptr {
function path_join(dir: pointer, rel: pointer) -> pointer {
if rel[0] == 47 { return rel } # absolute
return (dir + rel)
}
var loaded_paths: []ptr
var cur_dir: ptr
var loaded_paths: []pointer
var cur_dir: pointer
function already_loaded(full: ptr) -> bool {
function already_loaded(full: pointer) -> bool {
var i = 0
while i < len(loaded_paths) { if (loaded_paths[i] == full) { return true }; i = i + 1 }
return false
@ -384,20 +384,20 @@ function already_loaded(full: ptr) -> bool {
function parse_one_decl() -> void {
var is_export = false
var qspec: Node = null
var onspawn_model: ptr = null
var ondespawn_model: ptr = null
var ondespawn_reason: ptr = null # @OnDespawn(M, reason: r) — LC1 teardown reason binding
var onattach_prop: ptr = null
var ondetach_prop: ptr = null
var onenable_prop: ptr = null
var ondisable_prop: ptr = null
var on_event: ptr = null # @On(Event) — a compile-time event listener
var onspawn_model: pointer = null
var ondespawn_model: pointer = null
var ondespawn_reason: pointer = null # @OnDespawn(M, reason: r) — LC1 teardown reason binding
var onattach_prop: pointer = null
var ondetach_prop: pointer = null
var onenable_prop: pointer = null
var ondisable_prop: pointer = null
var on_event: pointer = null # @On(Event) — a compile-time event listener
var is_public = false # @Public — promote a lifecycle hook to an event
var hook_phase: ptr = null # @OnStart / @OnQuit override the phase
var hook_phase: pointer = null # @OnStart / @OnQuit override the phase
var is_sync_prop = false # @Sync property P — every field replicates (NETWORKING N2)
var is_owned = false # @Owned model M — entities carry a network owner (N3)
var role: ptr = null # @Server / @Predicted — a handler's network role (N5)
var remote_dir: ptr = null # @ToServer / @ToClients — a remote event's direction (N4)
var role: pointer = null # @Server / @Predicted — a handler's network role (N5)
var remote_dir: pointer = null # @ToServer / @ToClients — a remote event's direction (N4)
while is_op("@") {
pi = pi + 1; let a = eat_id() # collect a leading @annotation
if (a == "export") { is_export = true }
@ -489,7 +489,7 @@ function parse_one_decl() -> void {
}
# lex and parse an imported fragment into `prog`, saving/restoring lexer state
function do_import(rel: ptr) -> void {
function do_import(rel: pointer) -> void {
let full = path_join(cur_dir, rel)
if already_loaded(full) { return }
push(loaded_paths, full)
@ -528,8 +528,8 @@ function parse_program() -> void {
g_start_scene = 0
g_events = new []Node
g_onlisten = new []Node
g_toggled_layers = new []ptr
loaded_paths = new []ptr
g_toggled_layers = new []pointer
loaded_paths = new []pointer
skipnl()
g_game_name = "Ludic"
# imports may precede the program block

View file

@ -13,7 +13,7 @@ function char_is_alpha(c: int) -> bool {
function char_is_alnum(c: int) -> bool { return char_is_alpha(c) or char_is_digit(c) }
# integer -> fresh decimal string
function itoa(v: int) -> ptr {
function itoa(v: int) -> pointer {
if v == 0 { let z = bytes(2); z[0] = 48; z[1] = 0; return z }
var neg = false
var x = v