Merge Phase 8b: modern primitive names

This commit is contained in:
Orkun ÇAKILKAYA 2026-08-28 02:55:25 +03:00
commit 96d01e45ab
19 changed files with 2468 additions and 2614 deletions

View file

@ -640,7 +640,8 @@ literal; test any pointer/record/slice with `x == null` / `x != null` (an unset
# control quit() print(x) (a value + newline)
# convert str(x) -> str (int/bool/fixed -> text)
# length len(x) -> int (elements of a slice, or bytes of a string)
# process os_argc()->int os_arg(i)->str (the command line; argv[0] included)
# process arg_count()->int arg(i)->str (the command line; argv[0] included)
# exit(code) run(cmd) getenv(name) read_char()->int
# file_stderr()->ptr file_stdout()->ptr (handles for file_write)
```

View file

@ -36,9 +36,9 @@ fn rt_font() -> str {
fn rt_init() -> void {
rt_fb = words(320 * 240)
rt_regs = words(64)
mem_set(rt_regs, 0, 64 * 4)
fill(rt_regs, 0, 64 * 4)
rt_map = bytes(96 * 64)
mem_set(rt_map, 32, 96 * 64)
fill(rt_map, 32, 96 * 64)
rt_statusbuf = bytes(96)
rt_statusbuf[0] = 0
rt_image_init()
@ -218,7 +218,7 @@ fn rt_poll() -> int {
if is_windowed() {
return win_poll()
}
let c = read_byte()
let c = read_char()
if c < 0 {
rt_alive = 0
return 0
@ -297,8 +297,8 @@ fn rt_dump_ppm(path: str) -> void {
}
file_write(f, buf, px * 3)
file_close(f)
mem_free(buf)
mem_free(hdr)
free(buf)
free(hdr)
}
import "inflate.ludic"
@ -317,7 +317,7 @@ var rt_maph: int = 0
fn rt_map_size(w: int, h: int) -> void {
rt_mapw = clamp(w, 0, 96)
rt_maph = clamp(h, 0, 64)
mem_set(rt_map, 32, 96 * 64)
fill(rt_map, 32, 96 * 64)
}
fn rt_map_row(y: int, s: str) -> void {
@ -378,7 +378,7 @@ fn rt_save_state(f: ptr) -> void {
file_write(f, rt_regs, 64 * 4)
file_write(f, rt_map, 96 * 64)
file_write(f, rt_statusbuf, 96)
mem_free(w)
free(w)
}
fn rt_load_state(f: ptr) -> void {
@ -390,5 +390,5 @@ fn rt_load_state(f: ptr) -> void {
file_read(f, rt_regs, 64 * 4)
file_read(f, rt_map, 96 * 64)
file_read(f, rt_statusbuf, 96)
mem_free(w)
free(w)
}

View file

@ -26,7 +26,7 @@ fn rt_image_init() -> void {
img_w = words(IMG_MAX)
img_h = words(IMG_MAX)
spr_px = words(SPR_MAX * SPR_SZ * SPR_SZ)
mem_set(spr_px, 0, SPR_MAX * SPR_SZ * SPR_SZ * 4)
fill(spr_px, 0, SPR_MAX * SPR_SZ * SPR_SZ * 4)
}
# ---- decoding -------------------------------------------------------------
@ -69,9 +69,9 @@ fn rt_decode_png(path: str) -> bool {
let d = rt_read_file(path)
if (d == null) { return false }
let size = rt_file_len
if size < 8 { mem_free(d); return false }
if d[0] != 137 { mem_free(d); return false }
if d[1] != 80 { mem_free(d); return false }
if size < 8 { free(d); return false }
if d[0] != 137 { free(d); return false }
if d[1] != 80 { free(d); return false }
var w = 0
var h = 0
@ -118,8 +118,8 @@ fn rt_decode_png(path: str) -> bool {
}
}
if w <= 0 { mem_free(d); return false }
if h <= 0 { mem_free(d); return false }
if w <= 0 { free(d); return false }
if h <= 0 { free(d); return false }
var channels = 1
if ct == 2 { channels = 3 }
@ -132,7 +132,7 @@ fn rt_decode_png(path: str) -> bool {
let rawlen = h * (stride + 1)
let raw = bytes(rawlen + 8)
if z_uncompress(idat, idlen, raw, rawlen) < 0 {
mem_free(d); mem_free(raw); mem_free(idat)
free(d); free(raw); free(idat)
return false
}
@ -215,11 +215,11 @@ fn rt_decode_png(path: str) -> bool {
}
}
mem_free(d)
mem_free(idat)
mem_free(raw)
mem_free(plte)
mem_free(trns)
free(d)
free(idat)
free(raw)
free(plte)
free(trns)
png_w = w
png_h = h
png_px = out
@ -333,7 +333,7 @@ fn rt_png_load(path: str) -> int {
spr_px[base + y * SPR_SZ + x] = px
}
}
mem_free(png_px)
free(png_px)
png_px = null
return id
}
@ -380,7 +380,7 @@ fn rt_sprites_load(path: str) -> void {
if (d == null) { spr_n = 0; return }
let size = rt_file_len
let pal: words = words(128)
mem_set(pal, 0, 128 * 4)
fill(pal, 0, 128 * 4)
var cur = 0 - 1
var row = 0
var i = 0
@ -407,7 +407,7 @@ fn rt_sprites_load(path: str) -> void {
if spr_n < SPR_MAX {
cur = spr_n
spr_n = spr_n + 1
mem_set(ptr_add(spr_px, cur * SPR_SZ * SPR_SZ * 4), 0, SPR_SZ * SPR_SZ * 4)
fill(offset(spr_px, cur * SPR_SZ * SPR_SZ * 4), 0, SPR_SZ * SPR_SZ * 4)
}
row = 0
}
@ -429,6 +429,6 @@ fn rt_sprites_load(path: str) -> void {
}
i = start + len + 1
}
mem_free(pal)
mem_free(d)
free(pal)
free(d)
}

View file

@ -75,7 +75,7 @@ fn z_table_build(table: words, lengths: words, n: int) -> void {
offs[l] = offs[l] + 1
}
}
mem_free(offs)
free(offs)
}
fn z_decode(table: words) -> int {
@ -181,7 +181,7 @@ fn z_fixed_tables(lit: ptr, dist: ptr) -> void {
z_table_build(lit, lengths, 288)
for i in 0 .. 30 { lengths[i] = 5 }
z_table_build(dist, lengths, 30)
mem_free(lengths)
free(lengths)
}
fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
@ -233,9 +233,9 @@ fn z_dynamic_tables(lit: ptr, dist: ptr) -> int {
let dl: words = words(32)
for i in 0 .. ndist { dl[i] = lengths[nlen + i] }
z_table_build(dist, dl, ndist)
mem_free(dl)
mem_free(lengths)
mem_free(clen)
free(dl)
free(lengths)
free(clen)
return 1
}
@ -262,8 +262,8 @@ fn z_inflate(src: ptr, len: int, out: ptr, cap: int) -> int {
if btype == 3 { return -1 }
if w < 0 { return -1 }
}
mem_free(lit)
mem_free(dist)
free(lit)
free(dist)
return w
}
@ -272,5 +272,5 @@ fn z_uncompress(src: ptr, len: int, out: ptr, cap: int) -> int {
if len < 2 { return -1 }
let cmf = src[0]
if (cmf & 15) != 8 { return -1 }
return z_inflate(ptr_add(src, 2), len - 2, out, cap)
return z_inflate(offset(src, 2), len - 2, out, cap)
}

View file

@ -101,7 +101,7 @@ fn rt_tt_init() -> void {
gc_oy = words(TT_GC)
gc_adv = words(TT_GC)
gc_bmp = bytes(TT_GC * 8)
mem_set(gc_used, 0, TT_GC * 4)
fill(gc_used, 0, TT_GC * 4)
}
# ---- big-endian readers ---------------------------------------------------
@ -368,9 +368,9 @@ fn tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fixed, dx
}
start = last + 1
}
mem_free(flags)
mem_free(xs)
mem_free(ys)
free(flags)
free(xs)
free(ys)
} else {
# composite glyph: components, each with its own placement and transform
var p = g + 10

View file

@ -152,7 +152,7 @@ fn tt_raster(id: int, gid: int, px: int) -> ptr {
let SW = W * TT_SS
let SH = H * TT_SS
let cover = bytes(W * H)
mem_set(cover, 0, W * H)
fill(cover, 0, W * H)
let unit = 255 / (TT_SS * TT_SS)
for sy in 0 .. SH {
@ -250,7 +250,7 @@ fn tt_glyph_get(font: int, cp: int, px: int) -> int {
let s = h
if gc_used[s] == 1 {
let old = gc_bmp[s]
if (old == null) == false { mem_free(old) }
if (old == null) == false { free(old) }
}
let bmp = tt_raster(font, tt_glyph_index(font, cp), px)
gc_bmp[s] = bmp

View file

@ -176,9 +176,9 @@ fn rt_ui_static_text(i: int, s: str) -> void {
# ---- text helpers: TrueType when a font is loaded, bitmap otherwise -------
fn ui_str(i: int) -> ptr {
if ui_hasdyn[i] == 1 { return ptr_add(ui_dyn, i * 96) }
if ui_hasdyn[i] == 1 { return offset(ui_dyn, i * 96) }
let t = ui_text[i]
if (t == null) { return ptr_add(ui_dyn, i * 96) }
if (t == null) { return offset(ui_dyn, i * 96) }
return t
}

View file

@ -14,7 +14,7 @@ fn buf_new() -> Buf {
fn buf_ensure(b: Buf, extra: int) -> void {
if b.len + extra + 1 <= b.cap { return }
while b.len + extra + 1 > b.cap { b.cap = b.cap * 2 }
b.data = mem_realloc(b.data, b.cap)
b.data = resize(b.data, b.cap)
}
fn buf_putc(b: Buf, c: int) -> void {
buf_ensure(b, 1)

View file

@ -7,12 +7,12 @@ 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 (name == "mem_realloc") { return true }
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 }
if (name == "os_argc") or (name == "os_arg") or (name == "os_exit") or (name == "file_stderr") { return true }
if (name == "file_stdout") { return true }
if (name == "os_system") or (name == "os_getenv") { return true }
if (name == "arg_count") or (name == "arg") or (name == "exit") { return true }
if (name == "file_stderr") or (name == "file_stdout") { return true }
if (name == "run") or (name == "getenv") { return true }
return false
}
# emit " <r> = <rest>\n" and return r
@ -24,7 +24,7 @@ fn emit_intrinsic(name: ptr, 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 == "mem_realloc") {
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")
@ -57,14 +57,14 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
return val("0", "void")
}
# print_str / print_int are the polymorphic `print(x)` builtin now (emit_call).
if (name == "os_argc") { return val(emit_bind("load i32, ptr @L_argc"), "int") }
if (name == "os_arg") {
if (name == "arg_count") { return val(emit_bind("load i32, ptr @L_argc"), "int") }
if (name == "arg") {
let i = arg_code(e, 0)
let v = emit_bind("load ptr, ptr @L_argv")
let q = emit_bind(`getelementptr ptr, ptr {v}, i32 {i}`)
return val(emit_bind(`load ptr, ptr {q}`), "str")
}
if (name == "os_exit") {
if (name == "exit") {
let n = arg_code(e, 0)
emit(" call void @exit(i32 "); emit(n); emit(")\n")
emit(" unreachable\n")
@ -73,11 +73,11 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
}
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 == "os_system") {
if (name == "run") {
let c = arg_code(e, 0)
return val(emit_bind(`call i32 @system(ptr {c})`), "int")
}
if (name == "os_getenv") {
if (name == "getenv") {
let n = arg_code(e, 0)
return val(emit_bind(`call ptr @getenv(ptr {n})`), "str")
}

View file

@ -5,9 +5,8 @@
var g_windowed: bool = false # headless by default (games read stdin / dump PPM)
fn is_intrinsic2(name: ptr) -> bool {
if (name == "mem_free") or (name == "mem_copy") or (name == "mem_set") { return true }
if (name == "ptr_add") or (name == "read_byte") or (name == "write_byte") { return true }
if (name == "os_time") { return true }
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 }
if (name == "is_windowed") or (name == "game_title") { return true }
if (name == "win_open") or (name == "win_poll") or (name == "win_present") { return true }
@ -16,35 +15,22 @@ fn is_intrinsic2(name: ptr) -> bool {
}
fn emit_intrinsic2(name: ptr, e: Node) -> Val {
if (name == "mem_free") {
if (name == "free") {
let p = arg_code(e, 0); emit(" call void @free(ptr "); emit(p); emit(")\n"); return val("0", "void")
}
if (name == "mem_copy") {
let d = arg_code(e, 0); let s = arg_code(e, 1); let n = arg_code(e, 2)
let w = emit_bind(`zext i32 {n} to i64`)
emit(" call ptr @memcpy(ptr "); emit(d); emit(", ptr "); emit(s); emit(", i64 "); emit(w); emit(")\n")
return val("0", "void")
}
if (name == "mem_set") {
if (name == "fill") {
let p = arg_code(e, 0); let v = arg_code(e, 1); let n = arg_code(e, 2)
let w = emit_bind(`zext i32 {n} to i64`)
emit(" call ptr @memset(ptr "); emit(p); emit(", i32 "); emit(v); emit(", i64 "); emit(w); emit(")\n")
return val("0", "void")
}
if (name == "ptr_add") {
if (name == "offset") {
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")
}
if (name == "read_byte") { return val(emit_bind("call i32 @getchar()"), "int") }
if (name == "write_byte") {
let v = arg_code(e, 0); emit(" call i32 @putchar(i32 "); emit(v); emit(")\n"); return val("0", "void")
}
if (name == "os_time") {
let r = emit_bind("call i64 @time(ptr null)")
return val(emit_bind(`trunc i64 {r} to i32`), "int")
}
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") }
if (name == "as_int") { let a = emit_expr(e.kids[0]); return val(a.code, "int") }
if (name == "is_windowed") { if g_windowed { return val("1", "bool") }; return val("0", "bool") }

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,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)
let v = getenv(name)
if (v == null) { return dflt }
return v
}
@ -48,7 +48,7 @@ fn ensure_slash(d: ptr) -> ptr {
fn die(msg: ptr) -> void {
file_write(file_stderr(), msg, len(msg))
os_exit(1)
exit(1)
}
entry {
@ -61,18 +61,18 @@ entry {
var run = false # compile then execute the result
# multi-call: invoked as `ludic` -> run mode by default
if (base_name(os_arg(0)) == "ludic") { run = true }
if (base_name(arg(0)) == "ludic") { run = true }
var ai = 1
while ai < os_argc() {
let a = os_arg(ai)
while ai < arg_count() {
let a = arg(ai)
if (a == ("--windowed")) { want = 1 }
else { if (a == ("--headless")) { want = 2 }
else { if (a == ("--emit-llvm")) { emit_ir = true }
else { if (a == ("--fmt")) { fmt = true }
else { if (a == ("--save-temps")) { save = true }
else { if (a == ("--run")) { run = true }
else { if (a == ("-o")) { ai = ai + 1; if ai < os_argc() { out = os_arg(ai) } }
else { if (a == ("-o")) { ai = ai + 1; if ai < arg_count() { out = arg(ai) } }
else {
# an unknown flag is ignored (with a note) rather than mistaken for the
# input file — '-' is ASCII 45
@ -97,7 +97,7 @@ entry {
# --fmt is the doc-check gate: reaching here means it lexed and parsed. A parse
# error would already have exited nonzero, so a clean parse exits 0. (Canonical
# formatting output is not yet reimplemented on the self-hosted toolchain.)
if fmt { os_exit(0) }
if fmt { exit(0) }
maybe_splice_runtime()
@ -122,13 +122,13 @@ entry {
# make the output directory if the user asked for e.g. bin/app
let odir = dir_of(out)
if len(odir) > 0 { os_system(`mkdir -p {odir}`) }
if len(odir) > 0 { run(`mkdir -p {odir}`) }
let ll = `{out}.ll`
if not ir_flush(ll) { die("ludicc: cannot write IR\n") }
let cc = getenv_or("LUDIC_CC", "clang")
let home = ensure_slash(getenv_or("LUDIC_HOME", dir_of(os_arg(0))))
let home = ensure_slash(getenv_or("LUDIC_HOME", dir_of(arg(0))))
# clang twice: assemble the IR, then link. A windowed build adds the macOS
# platform layer and the Cocoa framework.
@ -141,13 +141,13 @@ entry {
}
cmd = `{cmd} -o {out}`
let rc = os_system(cmd)
if not save { os_system(`rm -f {ll}`) }
let rc = run(cmd)
if not save { run(`rm -f {ll}`) }
if rc != 0 { die("ludicc: link failed\n") }
# run mode: execute the binary we just built and forward its exit code
if run {
let st = os_system(out)
os_exit(((st >> 8) & 255))
let st = run(out)
exit(((st >> 8) & 255))
}
}

View file

@ -17,7 +17,7 @@ fn perr(msg: ptr) -> void {
file_write(e, "ludicc(self): parse error: ", 27)
file_write(e, msg, len(msg))
file_write(e, "\n", 1)
os_exit(1)
exit(1)
}
fn eat_op(v: ptr) -> void { if not is_op(v) { perr(v) }; pi = pi + 1 }

View file

@ -93,7 +93,7 @@ if ./ludicc examples/snake.ludic --emit-llvm -o /tmp/cli_snake.ll >/tmp/cli.out
else bad "ludicc --emit-llvm: $(tail -1 /tmp/cli.out)"; fi
# ludic compiles, runs, and forwards the program's exit code.
printf 'entry { os_exit(42) }\n' > /tmp/cli_exit.ludic
printf 'entry { exit(42) }\n' > /tmp/cli_exit.ludic
./ludic /tmp/cli_exit.ludic >/tmp/cli.out 2>&1
if [ "$?" -eq 42 ]; then ok "ludic app.ludic -> compiles, runs, forwards exit code"
else bad "ludic run: expected exit 42, got $? ($(tail -1 /tmp/cli.out))"; fi

View file

@ -137,7 +137,7 @@ compare("builtins", h_builtins | h_intrinsics, kotlin_set(kotlin, "BUILTINS"), "
# --- against the TextMate grammar ------------------------------------------
gpath = ["repository", "builtin", "patterns"]
compare("builtins", h_builtins, grammar_alternation(grammar, gpath, "rng_chance"), "ludic.tmLanguage.json")
compare("intrinsics", h_intrinsics, grammar_alternation(grammar, gpath, "mem_free"), "ludic.tmLanguage.json")
compare("intrinsics", h_intrinsics, grammar_alternation(grammar, gpath, "as_fixed"), "ludic.tmLanguage.json")
kpath = ["repository", "keyword", "patterns"]
compare("phases", h_phases, grammar_alternation(grammar, kpath, "FixedUpdate"), "ludic.tmLanguage.json")
compare("primitive types", h_types, grammar_alternation(grammar, kpath, "fixed"), "ludic.tmLanguage.json")

View file

@ -66,12 +66,10 @@ object LudicVocabulary {
"ui_focused", "ui_visible", "key", "reg", "set_reg", "self", "save", "load",
"status", "print", "str", "quit",
// compiler intrinsics: the floor the Ludic-written runtime stands on
"bytes", "words", "mem_realloc", "mem_free", "mem_copy", "mem_set", "os_argc", "os_arg", "file_stderr", "file_stdout",
"ptr_add",
"bytes", "words", "resize", "free", "fill", "arg_count", "arg", "file_stderr", "file_stdout",
"offset",
"as_fixed", "as_int", "file_open", "file_read", "file_write",
"file_seek", "file_tell", "file_close", "read_byte", "write_byte",
"os_exit", "os_time",
"is_windowed", "game_title", "win_open", "win_poll", "win_present",
"file_seek", "file_tell", "file_close", "read_char", "exit", "is_windowed", "game_title", "win_open", "win_poll", "win_present",
"win_running", "win_close"
)

View file

@ -209,7 +209,7 @@
{
"comment": "compiler intrinsics — these lower straight to libc or the OS",
"name": "support.function.intrinsic.ludic",
"match": "\\b(bytes|words|mem_realloc|mem_free|mem_copy|mem_set|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|os_exit|os_time|os_argc|os_arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
"match": "\\b(bytes|words|resize|free|fill|offset|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_char|exit|arg_count|arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
}
]
},
@ -233,7 +233,7 @@
"patterns": [
{ "name": "keyword.operator.arrow.ludic", "match": "->|=>" },
{ "name": "keyword.operator.range.ludic", "match": "\\.\\." },
{ "name": "keyword.operator.bitwise.ludic", "match": "<<|>>|&|\\||\\^|~" },
{ "name": "keyword.operator.bitwise.ludic", "match": "<<|>>|&|\\|\\^|~" },
{ "name": "keyword.operator.comparison.ludic", "match": "==|!=|<=|>=|<|>" },
{ "name": "keyword.operator.assignment.ludic", "match": "\\+=|-=|\\*=|/=|=" },
{ "name": "keyword.operator.arithmetic.ludic", "match": "[+\\-*/%]" }

View file

@ -209,7 +209,7 @@
{
"comment": "compiler intrinsics — these lower straight to libc or the OS",
"name": "support.function.intrinsic.ludic",
"match": "\\b(bytes|words|mem_realloc|mem_free|mem_copy|mem_set|ptr_add|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_byte|write_byte|os_exit|os_time|os_argc|os_arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
"match": "\\b(bytes|words|resize|free|fill|offset|as_fixed|as_int|file_open|file_read|file_write|file_seek|file_tell|file_close|read_char|exit|arg_count|arg|file_stderr|file_stdout|is_windowed|game_title|win_open|win_poll|win_present|win_running|win_close)\\b(?=\\s*\\()"
}
]
},
@ -233,7 +233,7 @@
"patterns": [
{ "name": "keyword.operator.arrow.ludic", "match": "->|=>" },
{ "name": "keyword.operator.range.ludic", "match": "\\.\\." },
{ "name": "keyword.operator.bitwise.ludic", "match": "<<|>>|&|\\||\\^|~" },
{ "name": "keyword.operator.bitwise.ludic", "match": "<<|>>|&|\\|\\^|~" },
{ "name": "keyword.operator.comparison.ludic", "match": "==|!=|<=|>=|<|>" },
{ "name": "keyword.operator.assignment.ludic", "match": "\\+=|-=|\\*=|/=|=" },
{ "name": "keyword.operator.arithmetic.ludic", "match": "[+\\-*/%]" }

View file

@ -147,15 +147,14 @@ static const LBuiltin LUDIC_BUILTINS[] = {
static const LBuiltin LUDIC_INTRINSICS[] = {
{"bytes","bytes(n: int) -> ptr","Allocate a buffer of n bytes."},
{"words","words(n: int) -> words","Allocate a buffer of n 32-bit words."},
{"mem_realloc","mem_realloc(p: ptr, n: int) -> ptr","Resize a block to n bytes, preserving its contents (realloc)."},
{"os_argc","os_argc() -> int","Number of command-line arguments, argv[0] included."},
{"os_arg","os_arg(i: int) -> str","The i-th command-line argument."},
{"resize","resize(p: ptr, n: int) -> ptr","Resize a block to n bytes, preserving its contents (realloc)."},
{"arg_count","arg_count() -> int","Number of command-line arguments, argv[0] included."},
{"arg","arg(i: int) -> str","The i-th command-line argument."},
{"file_stderr","file_stderr() -> ptr","The standard error stream, for file_write."},
{"file_stdout","file_stdout() -> ptr","The standard output stream, for file_write."},
{"mem_free","mem_free(p: ptr)","Release an allocation."},
{"mem_copy","mem_copy(dst: ptr, src: ptr, n: int)","memcpy."},
{"mem_set","mem_set(p: ptr, byte: int, n: int)","memset."},
{"ptr_add","ptr_add(p: ptr, off: int) -> ptr","Offset a pointer by bytes."},
{"free","free(p: ptr)","Release an allocation."},
{"fill","fill(p: ptr, byte: int, n: int)","memset."},
{"offset","offset(p: ptr, off: int) -> ptr","Offset a pointer by bytes."},
{"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."},
@ -164,10 +163,8 @@ static const LBuiltin LUDIC_INTRINSICS[] = {
{"file_seek","file_seek(f: ptr, off: int, whence: int) -> int","fseek."},
{"file_tell","file_tell(f: ptr) -> int","ftell."},
{"file_close","file_close(f: ptr)","fclose."},
{"read_byte","read_byte() -> int","Read one byte from stdin, -1 at EOF."},
{"write_byte","write_byte(b: int)","Write one byte to stdout."},
{"os_exit","os_exit(code: int)","Terminate the process."},
{"os_time","os_time() -> int","Seconds since the epoch."},
{"read_char","read_char() -> int","Read one byte from stdin, -1 at EOF."},
{"exit","exit(code: int)","Terminate the process."},
{"is_windowed","is_windowed() -> bool","True when the build has a window."},
{"game_title","game_title() -> str","The name from the `game` declaration."},
{"win_open","win_open(title: str, w: int, h: int, scale: int)","Open the platform window."},