refactor(lang): rename the fn keyword to function

Expand the function-declaration keyword to the full word across the whole
language and toolchain:
  fn name(...) -> T { ... }   ->   function name(...) -> T { ... }

Done as a self-hosting migration: teach the parser both spellings, reseed,
rewrite every .ludic definition to `function`, then drop `fn`. The compiler
now rejects `fn`. Touches the parser, all selfhost/tools/runtime/example/test
sources, the grammars (TextMate shared+vscode, ludic_syntax.h, JetBrains
LudicTokens.kt), the LSP and formatter, the Python doc/vocab tools
(check-impl, check-docs, validate, palette, test-lsp), and the docs
(fences, prose, kw-fn -> kw-function).

Reseeded; C-free bootstrap fixpoint holds. All suites green (45 regression,
24 self-host, 29 tool); the docs site generates and check.py passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-30 01:43:22 +03:00
parent 2f19c8d8e2
commit 4c48077d68
86 changed files with 793 additions and 793 deletions

View file

@ -19,11 +19,11 @@ 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.
fn net_field_ibytes(ty: ptr) -> int { if (llty(ty) == "i8") { return 1 }; return 4 }
fn net_field_bytes(ty: ptr) -> ptr { if (llty(ty) == "i8") { return "1" }; return "4" }
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" }
# total replicated bytes for model m (compile-time constant)
fn net_model_bytes(m: Node) -> int {
function net_model_bytes(m: Node) -> int {
var total = 0
var ci = 0
while ci < len(m.kids) {
@ -39,16 +39,16 @@ fn net_model_bytes(m: Node) -> int {
return total
}
fn net_model_syncs(m: Node) -> bool { return net_model_bytes(m) > 0 }
function net_model_syncs(m: Node) -> bool { return net_model_bytes(m) > 0 }
fn net_has_sync() -> bool {
function net_has_sync() -> bool {
var i = 0
while i < len(prog) { if prog[i].kind == N_ARCH { if net_model_syncs(prog[i]) { return true } }; i = i + 1 }
return false
}
# ---- N3: ownership -----------------------------------------------------------
fn net_has_owned() -> bool {
function net_has_owned() -> bool {
var i = 0
while i < len(prog) { if prog[i].kind == N_ARCH and (prog[i].ival == 1) { return true }; i = i + 1 }
return false
@ -56,7 +56,7 @@ fn net_has_owned() -> bool {
# ---- N5: role-tagged handlers ------------------------------------------------
# A handler tagged @Server (ival==1) or @Predicted (ival==2) has a network role.
fn net_has_role() -> bool {
function net_has_role() -> bool {
var i = 0
while i < len(prog) { if prog[i].kind == N_SYS and (prog[i].ival != 0) { return true }; i = i + 1 }
return false
@ -64,10 +64,10 @@ fn net_has_role() -> bool {
# Any networking feature in use → emit the shared role registers (@L_role /
# @L_localid). A runtime sets them; offline they keep their single-player default.
fn net_any() -> bool { return net_has_sync() or net_has_owned() or net_has_role() }
function net_any() -> bool { return net_has_sync() or net_has_owned() or net_has_role() }
# ---- diagnostics -------------------------------------------------------------
fn net_warn(msg: ptr) -> void {
function net_warn(msg: ptr) -> void {
let e = file_stderr()
file_write(e, "ludicc(self): warning: ", 23)
file_write(e, msg, len(msg))
@ -77,7 +77,7 @@ fn net_warn(msg: ptr) -> void {
# Validate @Sync usage: a participating member whose component replicates nothing
# is a warning (participation that replicates nothing); a @Sync ptr field is a
# hard error (footgun 3 — networked fields must be POD scalars).
fn net_check() -> void {
function net_check() -> void {
var i = 0
while i < len(prog) {
if prog[i].kind == N_ARCH {
@ -110,7 +110,7 @@ fn net_check() -> void {
# ---- N2: per-model serializer / applier --------------------------------------
# serialize_<M>(e, buf) -> bytes written. Copies each replicated field, tightly
# packed in member-then-field order, so apply reads the identical layout.
fn emit_net_serialize(m: Node) -> void {
function emit_net_serialize(m: Node) -> void {
ll_t = 0
let me = itoa(MAX_ENT)
emit("define i32 @L_serialize_"); emit(m.s); emit("(i32 %e, ptr %buf) {\nentry:\n")
@ -143,7 +143,7 @@ fn emit_net_serialize(m: Node) -> void {
# apply_<M>(e, buf, len): the inverse — copy each replicated field back from the
# buffer into component storage. `len` is accepted for symmetry (the runtime's
# framing) but the layout is fixed, so it is not consulted.
fn emit_net_apply(m: Node) -> void {
function emit_net_apply(m: Node) -> void {
ll_t = 0
let me = itoa(MAX_ENT)
emit("define void @L_apply_"); emit(m.s); emit("(i32 %e, ptr %buf, i32 %len) {\nentry:\n")
@ -177,7 +177,7 @@ fn emit_net_apply(m: Node) -> void {
# ludic_serialize(e, buf) -> bytes / ludic_apply(e, buf, len) / ludic_sync_size(e)
# route on the entity's model kind to the per-model function above, so a
# replication runtime replicates any entity without knowing its type.
fn emit_net_dispatch() -> void {
function emit_net_dispatch() -> void {
let me = itoa(MAX_ENT)
emit("define i32 @ludic_serialize(i32 %e, ptr %buf) {\nentry:\n")
@ -236,7 +236,7 @@ fn emit_net_dispatch() -> void {
# @L_owner: one i32 owner id per entity, -1 = unowned. Only emitted when a model
# is @Owned, and L_reset clears it to -1 on alloc/free (see emit_ecs). owner()/
# set_owner()/is_owner() read and write it; the authority assigns.
fn emit_net_owner() -> void {
function emit_net_owner() -> void {
let me = itoa(MAX_ENT)
emit("define i32 @L_owner(i32 %e) {\nentry:\n")
emit(" %p = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_owner_arr, i32 0, i32 %e\n %v = load i32, ptr %p\n ret i32 %v\n}\n\n")
@ -256,7 +256,7 @@ fn emit_net_owner() -> void {
# with NO foreign host at all (the Ludic-native default). Datagram-preserving:
# one message per poll, matching how replication/RPC frame. Emitted only when a
# program actually calls net_send/net_poll without an extern override.
fn emit_loopback() -> void {
function emit_loopback() -> void {
emith("@L_netq = internal global [64 x [2048 x i8]] zeroinitializer\n")
emith("@L_netlen = internal global [64 x i32] zeroinitializer\n")
emith("@L_nethead = internal global i32 0\n")
@ -294,24 +294,24 @@ fn emit_loopback() -> void {
# re-emits each into the ordinary @ev_<E> dispatch on the far side. Reuses the
# EV0 payload (already flat) and the transport seam — no new concept.
fn net_has_remote() -> bool {
function net_has_remote() -> bool {
var i = 0
while i < len(g_events) { if (g_events[i].ty != null) { return true }; i = i + 1 }
return false
}
# stable wire id for an event = its index in g_events (same program both peers)
fn net_event_id(name: ptr) -> int {
function net_event_id(name: ptr) -> 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.
fn net_send_sym() -> ptr { let x = find_extern("net_send"); if (x != null) { return x.a.s }; return "L_net_send" }
fn 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() -> 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" }
# 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.
fn emit_net_pump() -> void {
function emit_net_pump() -> void {
emith("@L_recvbuf = internal global [2048 x i8] zeroinitializer\n")
if (find_extern("net_poll") == null) { g_uses_loopback = true }
let psym = net_poll_sym()
@ -352,7 +352,7 @@ fn emit_net_pump() -> void {
}
# ---- driver ------------------------------------------------------------------
fn emit_net() -> void {
function emit_net() -> void {
if net_has_sync() {
net_check()
var i = 0