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

@ -109,15 +109,15 @@ All verified. A compiler needs each of these, and each one works today.
| Deep recursion (recursive-descent parsing) | ✅ | 5,000 frames, no crash |
| Heap allocation | ✅ | `mem_alloc`, `mem_free`, `mem_copy`, `mem_set`; 1 MiB alloc verified |
| Byte-level memory | ✅ | `peek8`/`poke8`, `peek32`/`poke32`, `peekp`/`pokep`, `ptr_add` |
| `ptr` locals, params, returns | ✅ | `function make(n: int) -> ptr` |
| `ptr` in a property field | ✅ | `property Nd { kind: int = 0, a: ptr = ptr_null() }` |
| `ptr` locals, params, returns | ✅ | `function make(n: int) -> pointer` |
| `ptr` in a property field | ✅ | `property Nd { kind: int = 0, a: pointer = ptr_null() }` |
| String literals as readable bytes | ✅ | `peek8("hello", 1)` → `101` |
| `str` accepted where `ptr` expected | ✅ | `f("A")` into `function f(p: ptr)` |
| `str` accepted where `ptr` expected | ✅ | `f("A")` into `function f(p: pointer)` |
| String comparison, **hand-written in Ludic** | ✅ | `streq` over `peek8` |
| Integer → decimal, **hand-written in Ludic** | ✅ | `itoa(48291)` → `"48291"` |
| File read: open/seek/tell/read/close | ✅ | full round-trip of a written file |
| File write | ✅ | `file_open`/`file_write`/`file_close` |
| Module-level mutable state | ✅ | `var count: int`, `var heap: ptr` |
| Module-level mutable state | ✅ | `var count: int`, `var heap: pointer` |
| `let` is mutable | ✅ | `i = i + 1` in a loop |
| `while`, numeric `for i in a .. b` with runtime bounds | ✅ | |
| `if` / `else if` / `else` chains | ✅ | |
@ -186,7 +186,7 @@ records. Today there are two workarounds, and both are bad at compiler scale:
what `truetype.ludic` does, and it works, but every field access becomes a
magic number. Across a 6,000-line compiler this is the difference between
maintainable and not.
- **ECS entities as nodes** — verified working (`property Nd { kind, a: ptr }`),
- **ECS entities as nodes** — verified working (`property Nd { kind, a: pointer }`),
and initially seductive because queries give you free traversal. **Do not do
this.** `LUDIC_MAX_ENT` is 1024 in `native.c:18`; the entity world is a fixed
array of per-property storage. A compiler needs hundreds of thousands of
@ -198,7 +198,7 @@ unblocks everything:
```ludic
# doc-check: skip — proposed syntax: struct does not exist yet
struct Tok { kind: int = 0, text: ptr = ptr_null(), line: int = 0 }
struct Tok { kind: int = 0, text: pointer = ptr_null(), line: int = 0 }
let t = new Tok # heap-allocated, fields seeded from defaults
t.kind = T_ID
@ -278,7 +278,7 @@ Every table in a compiler grows: tokens, nodes, the output buffer. Hand-rolling
alloc-copy-free works but is written once per table and gotten wrong once per
table.
**Design.** `mem_realloc(p: ptr, n: int) -> ptr`.
**Design.** `mem_realloc(p: pointer, n: int) -> pointer`.
**Lowering.** `declare ptr @realloc(ptr, <size_t>)` plus one `INTRINSICS[]`
entry. **Use `ll_size_t()` / `ll_widen()` for the size argument — do not
@ -303,7 +303,7 @@ plumbing is reused rather than duplicated:
```ludic
# doc-check: skip — proposed signature notation, not code
file_stderr() -> ptr # then file_write(f, buf, n) as usual
file_stderr() -> pointer # then file_write(f, buf, n) as usual
```
**Lowering — note the portability wrinkle.** There is no portable `@stderr`
@ -870,7 +870,7 @@ program P {
**String comparison, hand-written** ✅ → `1`
```ludic
function streq(a: ptr, b: ptr) -> bool {
function streq(a: pointer, b: pointer) -> bool {
let i = 0
while true {
let ca = peek8(a,i)
@ -885,7 +885,7 @@ function streq(a: ptr, b: ptr) -> bool {
**Integer → string, hand-written** ✅ → `48291`
```ludic
function itoa(v: int, buf: ptr) -> int {
function itoa(v: int, buf: pointer) -> int {
let n = 0
let x = v
if x == 0 { poke8(buf,0,48); return 1 }
@ -972,7 +972,7 @@ parenthesisation) while `ludic-fmt` returns the input **unchanged**.
```ludic
# doc-check: expect-error — every line here is a compile error by design
while i < 10 { i = i + 1; if i == 3 { break } } # unknown identifier 'break'
struct Node { k: int, a: ptr } # expected declaration (got 'struct')
struct Node { k: int, a: pointer } # expected declaration (got 'struct')
var t: [int; 8] # expected identifier (got '[')
var h: fn = a # unknown type 'fn' for var h
let p = &cb # fails to compile

View file

@ -160,10 +160,10 @@ compiler intrinsics that lower to direct calls into the platform ABI.
| intrinsic | lowers to |
| --- | --- |
| `mem_alloc(n) -> ptr`, `mem_free`, `mem_copy`, `mem_set` | `malloc`, `free`, `memcpy`, `memset` |
| `mem_alloc(n) -> pointer`, `mem_free`, `mem_copy`, `mem_set` | `malloc`, `free`, `memcpy`, `memset` |
| `peek8/peek32(p, i) -> int`, `poke8/poke32(p, i, v)` | `load` / `store` |
| `ptr_add(p, n) -> ptr`, `ptr_null()`, `ptr_is_null(p)` | `getelementptr`, `null` |
| `file_open(path, mode) -> ptr`, `file_read`, `file_write`, `file_close` | `fopen`, `fread`, `fwrite`, `fclose` |
| `ptr_add(p, n) -> pointer`, `ptr_null()`, `ptr_is_null(p)` | `getelementptr`, `null` |
| `file_open(path, mode) -> pointer`, `file_read`, `file_write`, `file_close` | `fopen`, `fread`, `fwrite`, `fclose` |
| `read_byte() -> int`, `write_byte(c)`, `print_str(s)`, `print_int(n)` | `getchar`, `putchar`, `printf` |
| `str_len(s) -> int`, `os_exit(code)`, `os_time() -> int` | `strlen`, `exit`, `time` |

View file

@ -1211,7 +1211,7 @@ hand-rolled pointer arithmetic these features exist to delete.
```ludic
# before (runtime/native/image.ludic today)
function px(img: ptr, x: int, y: int, w: int) -> int { return peek32(img, (y*w+x)*4) }
function px(img: pointer, x: int, y: int, w: int) -> int { return peek32(img, (y*w+x)*4) }
# after
struct Rgba { r: u8, g: u8, b: u8, a: u8 }

View file

@ -185,7 +185,7 @@ explicitly built for.
- **Touch input changes the input seam.** `win_poll()` returns a single `int`
keycode today ([emit_intrin2.ludic:41](selfhost/emit_intrin2.ludic:41),
[core.ludic:217](runtime/native/core.ludic:217)) — insufficient for touch, which
needs `(x, y, phase, id)`. Options: (a) a parallel `win_poll_touch() -> ptr`
needs `(x, y, phase, id)`. Options: (a) a parallel `win_poll_touch() -> pointer`
draining an event queue, or (b) widen the input model to a small event struct for
all platforms. This is the one place mobile forces a decision above the window
seam. Proposed: add touch as a **separate** seam so keyboard platforms stay
@ -211,9 +211,9 @@ no new language feature, no new intrinsic:
```ludic
# doc-check: skip — runtime/native/gfx3d.ludic, illustrative
extern function gl_gen_textures(n: int, out: ptr) -> void = "glGenTextures"
extern function gl_tex_image_2d(t: int, w: int, h: int, px: ptr) -> void = "gl_tex_image_2d"
extern function gl_draw_elements(mode: int, count: int, ty: int, idx: ptr) -> void = "glDrawElements"
extern function gl_gen_textures(n: int, out: pointer) -> void = "glGenTextures"
extern function gl_tex_image_2d(t: int, w: int, h: int, px: pointer) -> void = "gl_tex_image_2d"
extern function gl_draw_elements(mode: int, count: int, ty: int, idx: pointer) -> void = "glDrawElements"
```
Two phases, additive:

View file

@ -151,9 +151,9 @@ directly from these; the high-level layer (§6) is sugar over them.
| Primitive | Signature (sketch) | Enables |
|---|---|---|
| **Transport seam** | `extern function net_send(peer: int, buf: ptr, len: int)` · `extern function net_poll(buf: ptr, cap: int) -> int` | any model; host binds UDP (native) or WebRTC/WebSocket (wasm), or a loopback for tests |
| **World snapshot ↔ buffer** | `world_save(buf: ptr) -> int` · `world_load(buf: ptr, len: int)` | rollback, replication, join/resync — generalizes `save()`/`load()` off the filesystem |
| **Generated serializers** | `serialize_<Model>(e: entity, buf: ptr) -> int` · `apply_<Model>(e: entity, buf: ptr, len: int)` | per-model, touch only the `@Sync` fields; emitted from the schema |
| **Transport seam** | `extern function net_send(peer: int, buf: pointer, len: int)` · `extern function net_poll(buf: pointer, cap: int) -> int` | any model; host binds UDP (native) or WebRTC/WebSocket (wasm), or a loopback for tests |
| **World snapshot ↔ buffer** | `world_save(buf: pointer) -> int` · `world_load(buf: pointer, len: int)` | rollback, replication, join/resync — generalizes `save()`/`load()` off the filesystem |
| **Generated serializers** | `serialize_<Model>(e: entity, buf: pointer) -> int` · `apply_<Model>(e: entity, buf: pointer, len: int)` | per-model, touch only the `@Sync` fields; emitted from the schema |
| **Ownership** | `owner(e: entity) -> int` · `set_owner(e: entity, id: int)` | authority checks, per-entity owner metadata (an `@L_owner` array, like `@L_kind`) |
| **Role registers** | `is_server() -> bool` · `is_owner(e: entity) -> bool` · `local_id() -> int` | the runtime sets these; role-guarded dispatch reads them |
| **Drivable sim** | `tick_fixed()` · `tick_render()` · seed get/set | a developer-owned loop for prediction/rollback (also: replay, headless tests, AI) |

View file

@ -9,7 +9,7 @@ tip: Mark fields as replicated and models as owned so the compiler generates net
order: 4
---
These are the two declarative switches at the top of Ludic's networking: <code>@Sync</code> says <b>what</b> replicates and <code>@Owned</code> says <b>who</b> owns an instance. Replication is opt-in at the field level and decided per model use-site — a field crosses the wire only when it is both replicable (marked <code>@Sync</code>, either on the field or via <code>@Sync property P</code> which marks every field of <code>P</code>) <b>and</b> participating (the model marks the component <code>@Sync</code>). So the same property can replicate in one model and not another, and there is no <code>@NoSync</code> because the surface is purely additive. From these marks the compiler generates the per-model <code>serialize</code>/<code>apply</code> codecs; <code>@Sync</code> on a non-POD-scalar field (like a <code>ptr</code>) is a compile error, since a machine-local pointer cannot cross the wire. <code>@Owned</code> adds the owner slot the ownership builtins and <code>@Predicted</code> read. See the dedicated <code>@Owned</code> page for ownership details.
These are the two declarative switches at the top of Ludic's networking: <code>@Sync</code> says <b>what</b> replicates and <code>@Owned</code> says <b>who</b> owns an instance. Replication is opt-in at the field level and decided per model use-site — a field crosses the wire only when it is both replicable (marked <code>@Sync</code>, either on the field or via <code>@Sync property P</code> which marks every field of <code>P</code>) <b>and</b> participating (the model marks the component <code>@Sync</code>). So the same property can replicate in one model and not another, and there is no <code>@NoSync</code> because the surface is purely additive. From these marks the compiler generates the per-model <code>serialize</code>/<code>apply</code> codecs; <code>@Sync</code> on a non-POD-scalar field (like a <code>pointer</code>) is a compile error, since a machine-local pointer cannot cross the wire. <code>@Owned</code> adds the owner slot the ownership builtins and <code>@Predicted</code> read. See the dedicated <code>@Owned</code> page for ownership details.
```ludic
program SyncedWorld {

View file

@ -4,7 +4,7 @@ name: bytes
category: builtins
kind: builtin
tokens: bytes
sig: bytes(n) -> ptr
sig: bytes(n) -> pointer
tip: Allocate a raw buffer of n bytes and return a pointer to it.
order: 50
---

View file

@ -4,7 +4,7 @@ name: file_stderr
category: builtins
kind: builtin
tokens: file_stderr
sig: file_stderr() -> ptr
sig: file_stderr() -> pointer
tip: The standard-error stream handle for use with file_write.
order: 50
---

View file

@ -4,7 +4,7 @@ name: file_stdout
category: builtins
kind: builtin
tokens: file_stdout
sig: file_stdout() -> ptr
sig: file_stdout() -> pointer
tip: The standard-output stream handle for use with file_write.
order: 50
---

View file

@ -4,7 +4,7 @@ name: Memory.bytes
category: memory
kind: namespace-method
tokens: Memory.bytes
sig: Memory.bytes(n) -> ptr
sig: Memory.bytes(n) -> pointer
tip: Allocate n bytes.
order: 0
ns: Memory

View file

@ -8,7 +8,7 @@ tip: Integer, hex, character, string, boolean, and null-pointer literals.
order: 8
---
Literals are the fixed values you write directly in source. `42` is a decimal `int` and `0x1E90FF` is a hex `int` — hex is how colors are written, so a raw color is just an integer. A number with a decimal point (`1.5`) is a `fixed`. `'w'` is a character literal, an `int` code point handy for comparing against `Input.key()`. `"text"` is a `string`, `true` / `false` are `bool`s, and `null` is the null-pointer literal used to test an unset record, slice, or `ptr` field.
Literals are the fixed values you write directly in source. `42` is a decimal `int` and `0x1E90FF` is a hex `int` — hex is how colors are written, so a raw color is just an integer. A number with a decimal point (`1.5`) is a `fixed`. `'w'` is a character literal, an `int` code point handy for comparing against `Input.key()`. `"text"` is a `string`, `true` / `false` are `bool`s, and `null` is the null-pointer literal used to test an unset record, slice, or `pointer` field.
```ludic
let sky_color: int = 0x1E90FF

View file

@ -9,7 +9,7 @@ tip: Bind a name to an external native symbol — the seam for platform and libr
order: 12
---
An <code>extern function</code> declares a function whose body lives outside Ludic and binds it to a native symbol resolved at link time. It is the seam through which Ludic reaches anything with a native interface — a system library, a math routine, or even another `.ludic` file compiled as a `module`. You write the Ludic signature you want to call and give the real symbol name after `=`; the linker connects them (pass `-L`/`-l` to `ludicc` to point at the library). Types must match the foreign ABI, so map each parameter and the return to the right Ludic type (`int`, `fixed`, `ptr`, …).
An <code>extern function</code> declares a function whose body lives outside Ludic and binds it to a native symbol resolved at link time. It is the seam through which Ludic reaches anything with a native interface — a system library, a math routine, or even another `.ludic` file compiled as a `module`. You write the Ludic signature you want to call and give the real symbol name after `=`; the linker connects them (pass `-L`/`-l` to `ludicc` to point at the library). Types must match the foreign ABI, so map each parameter and the return to the right Ludic type (`int`, `fixed`, `pointer`, …).
Parameters:
- `a` — a typed argument passed straight through to the foreign symbol

View file

@ -4,7 +4,7 @@ name: System.file_open
category: system
kind: namespace-method
tokens: System.file_open
sig: System.file_open(path, mode) -> ptr
sig: System.file_open(path, mode) -> pointer
tip: Open a file.
order: 6
ns: System

View file

@ -4,7 +4,7 @@ name: System.stderr
category: system
kind: namespace-method
tokens: System.stderr
sig: System.stderr() -> ptr
sig: System.stderr() -> pointer
tip: The standard error handle.
order: 13
ns: System

View file

@ -4,7 +4,7 @@ name: System.stdout
category: system
kind: namespace-method
tokens: System.stdout
sig: System.stdout() -> ptr
sig: System.stdout() -> pointer
tip: The standard output handle.
order: 12
ns: System

View file

@ -9,7 +9,7 @@ tip: A raw buffer indexed one byte at a time — each buffer[i] reads or writes
order: 7
---
`byte` is a raw buffer viewed one byte at a time: indexing `buffer[index]` reads or writes a single 8-bit value. It is the byte-sized view of the memory a `ptr` addresses — allocate it with `bytes(count)` and bind it as `byte` when you want per-byte access, for example to build up text, decode a file, or pack a compact grid. Each element is an `int` in the range of a byte. Like the other raw buffers it is unchecked, so you are responsible for staying in bounds.
`byte` is a raw buffer viewed one byte at a time: indexing `buffer[index]` reads or writes a single 8-bit value. It is the byte-sized view of the memory a `pointer` addresses — allocate it with `bytes(count)` and bind it as `byte` when you want per-byte access, for example to build up text, decode a file, or pack a compact grid. Each element is an `int` in the range of a byte. Like the other raw buffers it is unchecked, so you are responsible for staying in bounds.
```ludic
let name_buffer: byte = bytes(16)

View file

@ -0,0 +1,18 @@
---
id: type-pointer
name: pointer
category: types
kind: type
tokens: pointer
sig: pointer
tip: A raw address into memory — a byte buffer from bytes(n), or an FFI handle.
order: 5
---
`pointer` is a raw address into memory — the low-level type for runtime and foreign-function work, not something an everyday game reaches for. Allocate a raw byte buffer with `bytes(count)`, which returns a `pointer` you index as `buffer[index]` to read or write one byte; retype the binding as `words` / `fixeds` / `pointers` to index in larger element sizes. A `pointer` is also how an `extern function` passes an opaque C handle across the ABI. Test one for emptiness against the `null` literal.
```ludic
let scratch: pointer = bytes(256)
scratch[0] = 65
if scratch != null { scratch[1] = 66 }
```

View file

@ -0,0 +1,25 @@
---
id: type-pointers
name: pointers
category: types
kind: type
tokens: pointers
sig: pointers
tip: A buffer of pointers — p[i] reads and writes a pointer.
order: 9
---
A <code>pointers</code> is a raw buffer whose elements are pointers: index it with <code>buffer[i]</code> to read or write a <code>pointer</code>, with no bounds checking. Allocate the storage with <code>words(n)</code> (a pointer fits in a slot) and type the binding as <code>pointers</code> so indexing uses the pointer element size. It is the low-level building block for hand-rolled data structures — a table of records made with <code>new</code>, a free list, a bucket array — for when you want manual control instead of a growable <code>[]T</code> slice.
```ludic
program Records {
property Position { column: int = 0, row: int = 0 }
handler Setup phase Start {
let slots: pointers = words(2)
slots[0] = new Position
slots[1] = new Position
print(2)
}
}
```

View file

@ -1,18 +0,0 @@
---
id: type-ptr
name: ptr
category: types
kind: type
tokens: ptr
sig: ptr
tip: A raw address into memory — a byte buffer from bytes(n), or an FFI handle.
order: 5
---
`ptr` is a raw address into memory — the low-level type for runtime and foreign-function work, not something an everyday game reaches for. Allocate a raw byte buffer with `bytes(count)`, which returns a `ptr` you index as `buffer[index]` to read or write one byte; retype the binding as `words` / `fixeds` / `ptrs` to index in larger element sizes. A `ptr` is also how an `extern function` passes an opaque C handle across the ABI. Test one for emptiness against the `null` literal.
```ludic
let scratch: ptr = bytes(256)
scratch[0] = 65
if scratch != null { scratch[1] = 66 }
```

View file

@ -1,25 +0,0 @@
---
id: type-ptrs
name: ptrs
category: types
kind: type
tokens: ptrs
sig: ptrs
tip: A buffer of pointers — p[i] reads and writes a ptr.
order: 9
---
A <code>ptrs</code> is a raw buffer whose elements are pointers: index it with <code>buffer[i]</code> to read or write a <code>ptr</code>, with no bounds checking. Allocate the storage with <code>words(n)</code> (a pointer fits in a slot) and type the binding as <code>ptrs</code> so indexing uses the pointer element size. It is the low-level building block for hand-rolled data structures — a table of records made with <code>new</code>, a free list, a bucket array — for when you want manual control instead of a growable <code>[]T</code> slice.
```ludic
program Records {
property Position { column: int = 0, row: int = 0 }
handler Setup phase Start {
let slots: ptrs = words(2)
slots[0] = new Position
slots[1] = new Position
print(2)
}
}
```

View file

@ -9,7 +9,7 @@ tip: A raw buffer indexed as 32-bit words — each buffer[i] reads or writes an
order: 6
---
`words` is a raw buffer viewed as a sequence of 32-bit words: indexing `buffer[index]` reads or writes one `int`. It is the same underlying memory a `ptr` addresses, retyped so the element size is a word instead of a byte — allocate the storage with `words(count)` (count 32-bit words) and bind it as `words` to index it that way. Reach for it when you need a flat integer array outside the ECS — a lookup table, a scratch grid — and want plain integer indexing without the growable-slice header. Like all raw buffers it is unbounded and unchecked, so keep your own length.
`words` is a raw buffer viewed as a sequence of 32-bit words: indexing `buffer[index]` reads or writes one `int`. It is the same underlying memory a `pointer` addresses, retyped so the element size is a word instead of a byte — allocate the storage with `words(count)` (count 32-bit words) and bind it as `words` to index it that way. Reach for it when you need a flat integer array outside the ECS — a lookup table, a scratch grid — and want plain integer indexing without the growable-slice header. Like all raw buffers it is unbounded and unchecked, so keep your own length.
```ludic
let height_map: words = words(64)

View file

@ -3,7 +3,7 @@
# a `ptr` field, so its byte offset is 8, not 4; writing qty=99 by name and reading
# it back proves the world table addresses fields by their true layout. Prints 99.
program WorldMixed {
property Slot { ref: ptr = null, qty: int = 0 }
property Slot { ref: pointer = null, qty: int = 0 }
model Item { Slot }
@Public @OnSpawn(Item) handler Born { } # makes it a modding program

View file

@ -329,7 +329,7 @@ function rt_running() -> bool {
}
# ---- writing the frame out ------------------------------------------------
function rt_put_str(buf: ptr, at: int, s: string) -> int {
function rt_put_str(buf: pointer, at: int, s: string) -> int {
var i = 0
var n = at
var ch = s[0]
@ -342,7 +342,7 @@ function rt_put_str(buf: ptr, at: int, s: string) -> int {
return n
}
function rt_put_int(buf: ptr, at: int, v: int) -> int {
function rt_put_int(buf: pointer, at: int, v: int) -> int {
if v == 0 {
buf[at] = 48
return at + 1
@ -402,7 +402,7 @@ import "ui.ludic"
# A character grid the game paints with map_row() and reads with tile(). Stored
# as one byte per cell in a flat buffer; out-of-bounds reads answer '#' so a
# caller can treat the edge of the world as wall without special-casing it.
var rt_map: ptr = null
var rt_map: pointer = null
var rt_mapw: int = 0
var rt_maph: int = 0
@ -436,7 +436,7 @@ function rt_tile(x: int, y: int) -> int {
# ---- status line ----------------------------------------------------------
# One persistent string of feedback/dialogue, copied into runtime-owned memory
# so it survives whatever the caller does with the original.
var rt_statusbuf: ptr = null
var rt_statusbuf: pointer = null
function rt_status(s: string) -> void {
var i = 0
@ -452,7 +452,7 @@ function rt_status(s: string) -> void {
rt_statusbuf[i] = 0
}
function rt_status_text() -> ptr {
function rt_status_text() -> pointer {
return rt_statusbuf
}
@ -460,7 +460,7 @@ function rt_status_text() -> ptr {
# The compiler writes the ECS (entities, components, archetype kinds) because
# only it knows their shape. Everything below belongs to the runtime, so the
# runtime writes it — same order both ways.
function rt_save_state(f: ptr) -> void {
function rt_save_state(f: pointer) -> void {
let w: words = bytes(16)
w[0] = rt_rng
w[1] = rt_mapw
@ -473,7 +473,7 @@ function rt_save_state(f: ptr) -> void {
free(w)
}
function rt_load_state(f: ptr) -> void {
function rt_load_state(f: pointer) -> void {
let w: words = bytes(16)
file_read(f, w, 16)
rt_rng = w[0]

View file

@ -13,7 +13,7 @@ const IMG_MAX: int = 32
const SPR_MAX: int = 160
const SPR_SZ: int = 16
var img_px: ptrs = null # IMG_MAX pointers to RGBA buffers
var img_px: pointers = null # IMG_MAX pointers to RGBA buffers
var img_w: words = null
var img_h: words = null
var img_n: int = 0
@ -30,11 +30,11 @@ function rt_image_init() -> void {
}
# ---- decoding -------------------------------------------------------------
function png_be32(b: ptr, at: int) -> int {
function png_be32(b: pointer, at: int) -> int {
return (b[at] << 24) | (b[at + 1] << 16) | (b[at + 2] << 8) | b[at + 3]
}
function png_tag(b: ptr, at: int, a: int, c: int, d: int, e: int) -> bool {
function png_tag(b: pointer, at: int, a: int, c: int, d: int, e: int) -> bool {
if b[at] != a { return false }
if b[at + 1] != c { return false }
if b[at + 2] != d { return false }
@ -42,7 +42,7 @@ function png_tag(b: ptr, at: int, a: int, c: int, d: int, e: int) -> bool {
return true
}
function rt_read_file(path: string) -> ptr {
function rt_read_file(path: string) -> pointer {
let f = file_open(path, "rb")
if (f == null) { return null }
file_seek(f, 0, 2)

View file

@ -13,14 +13,14 @@
# ============================================================================
# ---- bit reader (DEFLATE packs bits least-significant-first) ---------------
var z_src: ptr = null
var z_src: pointer = null
var z_len: int = 0
var z_pos: int = 0
var z_bitbuf: int = 0
var z_bitcnt: int = 0
var z_err: int = 0
function z_start(src: ptr, len: int) -> void {
function z_start(src: pointer, len: int) -> void {
z_src = src
z_len = len
z_pos = 0
@ -48,7 +48,7 @@ function z_bits(need: int) -> int {
# ---- Huffman tables -------------------------------------------------------
# A table is a single buffer: 16 length-counts followed by the symbols in
# canonical order. One allocation, no structs.
function z_table_new(nsym: int) -> ptr {
function z_table_new(nsym: int) -> pointer {
return words((16 + nsym))
}
@ -125,7 +125,7 @@ function z_dist_extra(sym: int) -> int {
# ---- block decoders -------------------------------------------------------
# `out` is the destination window; returns the new write position, or -1.
function z_stored(out: ptr, at: int, cap: int) -> int {
function z_stored(out: pointer, at: int, cap: int) -> int {
z_bitbuf = 0
z_bitcnt = 0 # stored blocks are byte-aligned
if z_pos + 4 > z_len { return -1 }
@ -142,7 +142,7 @@ function z_stored(out: ptr, at: int, cap: int) -> int {
return w
}
function z_codes(out: ptr, at: int, cap: int, lit: ptr, dist: ptr) -> int {
function z_codes(out: pointer, at: int, cap: int, lit: pointer, dist: pointer) -> int {
var w = at
var sym = z_decode(lit)
while sym != 256 {
@ -172,7 +172,7 @@ function z_codes(out: ptr, at: int, cap: int, lit: ptr, dist: ptr) -> int {
return w
}
function z_fixed_tables(lit: ptr, dist: ptr) -> void {
function z_fixed_tables(lit: pointer, dist: pointer) -> void {
let lengths: words = words(288)
for i in 0 .. 144 { lengths[i] = 8 }
for i in 144 .. 256 { lengths[i] = 9 }
@ -184,7 +184,7 @@ function z_fixed_tables(lit: ptr, dist: ptr) -> void {
free(lengths)
}
function z_dynamic_tables(lit: ptr, dist: ptr) -> int {
function z_dynamic_tables(lit: pointer, dist: pointer) -> int {
let nlen = z_bits(5) + 257
let ndist = z_bits(5) + 1
let ncode = z_bits(4) + 4
@ -240,7 +240,7 @@ function z_dynamic_tables(lit: ptr, dist: ptr) -> int {
}
# Inflate a raw DEFLATE stream. Returns bytes written, or -1.
function z_inflate(src: ptr, len: int, out: ptr, cap: int) -> int {
function z_inflate(src: pointer, len: int, out: pointer, cap: int) -> int {
z_start(src, len)
let lit = z_table_new(288)
let dist = z_table_new(30)
@ -268,7 +268,7 @@ function z_inflate(src: ptr, len: int, out: ptr, cap: int) -> int {
}
# zlib wrapper (RFC 1950): two header bytes, then DEFLATE, then Adler-32.
function z_uncompress(src: ptr, len: int, out: ptr, cap: int) -> int {
function z_uncompress(src: pointer, len: int, out: pointer, cap: int) -> int {
if len < 2 { return -1 }
let cmf = src[0]
if (cmf & 15) != 8 { return -1 }

View file

@ -18,7 +18,7 @@ const TT_EDGES: int = 16384 # line segments in one glyph
const TT_GC: int = 512 # glyph cache entries
# ---- per-font tables (parallel arrays; index = font id) -------------------
var tt_data: ptrs = null
var tt_data: pointers = null
var tt_size: words = null
var tt_upem: words = null
var tt_nglyf: words = null
@ -61,7 +61,7 @@ var gc_h: words = null
var gc_ox: words = null
var gc_oy: words = null
var gc_adv: words = null
var gc_bmp: ptrs = null
var gc_bmp: pointers = null
function rt_tt_init() -> void {
tt_data = bytes(TT_MAX * 8)
@ -105,24 +105,24 @@ function rt_tt_init() -> void {
}
# ---- big-endian readers ---------------------------------------------------
function tt_u16(d: ptr, at: int) -> int {
function tt_u16(d: pointer, at: int) -> int {
return ((d[at] << 8) | d[at + 1])
}
function tt_i16(d: ptr, at: int) -> int {
function tt_i16(d: pointer, at: int) -> int {
let v = tt_u16(d, at)
if v >= 32768 { return v - 65536 }
return v
}
function tt_u32(d: ptr, at: int) -> int {
function tt_u32(d: pointer, at: int) -> int {
return (d[at] << 24) | (d[at + 1] << 16) | (d[at + 2] << 8) | d[at + 3]
}
function tt_i8(d: ptr, at: int) -> int {
function tt_i8(d: pointer, at: int) -> int {
let v = d[at]
if v >= 128 { return v - 256 }
return v
}
function tt_find_table(d: ptr, base: int, a: int, b: int, c: int, e: int) -> int {
function tt_find_table(d: pointer, base: int, a: int, b: int, c: int, e: int) -> int {
let n = tt_u16(d, base + 4)
for i in 0 .. n {
let rec = base + 12 + i * 16
@ -138,7 +138,7 @@ function tt_find_table(d: ptr, base: int, a: int, b: int, c: int, e: int) -> int
}
# pick the most capable Unicode subtable, as the C loader did
function tt_pick_cmap(id: int, d: ptr, co: int) -> void {
function tt_pick_cmap(id: int, d: pointer, co: int) -> void {
tt_cmap[id] = 0 - 1
tt_cfmt[id] = 0
if co < 0 { return }

View file

@ -51,7 +51,7 @@ var gr_ox: int = 0
var gr_oy: int = 0
var gr_adv: int = 0
function tt_raster(id: int, gid: int, px: int) -> ptr {
function tt_raster(id: int, gid: int, px: int) -> pointer {
let upem = tt_upem[id]
let scale = fx(px) / upem
gr_adv = flr(fx(tt_advance(id, gid)) * scale + 0.5)
@ -298,7 +298,7 @@ function tt_utf8(s: string, at: int) -> int {
}
# ---- drawing --------------------------------------------------------------
function tt_blit(bmp: ptr, w: int, h: int, dx: int, dy: int, colour: int) -> void {
function tt_blit(bmp: pointer, w: int, h: int, dx: int, dy: int, colour: int) -> void {
if (bmp == null) { return }
let cr = ((colour >> 16) & 255)
let cg = ((colour >> 8) & 255)

View file

@ -72,8 +72,8 @@ var ui_rh: words = null
var ui_visible: words = null
var ui_fired: words = null
var ui_hasdyn: words = null
var ui_dyn: ptr = null # UI_MAX * 96 bytes
var ui_text: ptrs = null # UI_MAX static string pointers
var ui_dyn: pointer = null # UI_MAX * 96 bytes
var ui_text: pointers = null # UI_MAX static string pointers
var ui_n: int = 0
var ui_active: int = -1
@ -175,14 +175,14 @@ function rt_ui_static_text(i: int, s: string) -> void {
}
# ---- text helpers: TrueType when a font is loaded, bitmap otherwise -------
function ui_str(i: int) -> ptr {
function ui_str(i: int) -> pointer {
if ui_hasdyn[i] == 1 { return offset(ui_dyn, i * 96) }
let t = ui_text[i]
if (t == null) { return offset(ui_dyn, i * 96) }
return t
}
function ui_draw_text(font: int, x: int, y: int, s: ptr, colour: int, size: int) -> void {
function ui_draw_text(font: int, x: int, y: int, s: pointer, colour: int, size: int) -> void {
if font >= 0 {
if font < tt_n {
rt_text_ttf(font, x, y, s, colour, size)
@ -192,7 +192,7 @@ function ui_draw_text(font: int, x: int, y: int, s: ptr, colour: int, size: int)
rt_text(x, y, s, colour, max(size / 8, 1))
}
function ui_tw(font: int, s: ptr, size: int) -> int {
function ui_tw(font: int, s: pointer, size: int) -> int {
if font >= 0 {
if font < tt_n { return rt_text_w(font, s, size) }
}

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

View file

@ -47,9 +47,9 @@ game Atlas {
return buf
}
var atlas_scratch: ptr = ptr_null()
var atlas_scratch: pointer = ptr_null()
function atlas_buf() -> ptr {
function atlas_buf() -> pointer {
if ptr_is_null(atlas_scratch) { atlas_scratch = mem_alloc(512) }
return atlas_scratch
}

View file

@ -324,8 +324,8 @@
"type-fixed",
"type-fixeds",
"type-int",
"type-ptr",
"type-ptrs",
"type-pointer",
"type-pointers",
"type-slices",
"type-string",
"type-void",

View file

@ -52,7 +52,7 @@ object LudicVocabulary {
"enable", "disable", "match", "machine", "state", "become", "where",
"and", "or", "not", "break", "continue", "new", "emit", "cancel"
)
val PRIMITIVES = setOf("int", "long", "fixed", "bool", "entity", "string", "ptr", "byte", "words", "fixeds", "ptrs", "void")
val PRIMITIVES = setOf("int", "long", "fixed", "bool", "entity", "string", "pointer", "byte", "words", "fixeds", "pointers", "void")
val PHASES = setOf("Start", "Input", "FixedUpdate", "Update", "LateUpdate", "Render")
val WIDGETS = setOf("panel", "col", "row", "label", "button", "image", "spacer")

View file

@ -177,7 +177,7 @@
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\b" },
{ "name": "storage.type.ludic", "match": "\\b(program|property|model|enum|ui|const|var|let|function|handler|entry|state)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|long|fixed|bool|entity|string|ptr|byte|words|fixeds|ptrs|void)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|long|fixed|bool|entity|string|pointer|byte|words|fixeds|pointers|void)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\b" },
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
]

View file

@ -177,7 +177,7 @@
{ "name": "keyword.other.clause.ludic", "match": "\\b(phase|query|on)\\b" },
{ "name": "keyword.other.ludic", "match": "\\b(import|extern)\\b" },
{ "name": "storage.type.ludic", "match": "\\b(program|property|model|enum|ui|const|var|let|function|handler|entry|state)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|long|fixed|bool|entity|string|ptr|byte|words|fixeds|ptrs|void)\\b" },
{ "name": "support.type.primitive.ludic", "match": "\\b(int|long|fixed|bool|entity|string|pointer|byte|words|fixeds|pointers|void)\\b" },
{ "name": "constant.language.boolean.ludic", "match": "\\b(true|false|null)\\b" },
{ "name": "constant.language.phase.ludic", "match": "\\b(Start|Input|FixedUpdate|Update|LateUpdate|Render)\\b" }
]

View file

@ -28,7 +28,7 @@ program LudicFmt {
const LT_ERR: int = 14
# ---- tiny stdio + string helpers (self-contained) ----
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)
@ -40,7 +40,7 @@ program LudicFmt {
file_close(f)
return buf
}
function cstr_len(s: ptr) -> int { var n = 0; while s[n] != 0 { n = n + 1 }; return n }
function cstr_len(s: pointer) -> int { var n = 0; while s[n] != 0 { n = n + 1 }; return n }
function char_is_digit(c: int) -> bool { return c >= 48 and c <= 57 }
function char_is_alpha(c: int) -> bool {
if c >= 65 and c <= 90 { return true }
@ -49,7 +49,7 @@ program LudicFmt {
}
function char_is_alnum(c: int) -> bool { return char_is_alpha(c) or char_is_digit(c) }
function char_is_hex(c: int) -> bool { return char_is_digit(c) or (c >= 97 and c <= 102) or (c >= 65 and c <= 70) }
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
if x < 0 { neg = true; x = 0 - x }
@ -66,7 +66,7 @@ program LudicFmt {
}
# ---- a growable byte buffer ----
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; b.cap = 256; b.data = bytes(b.cap); b.len = 0; return b }
function buf_ensure(b: Buf, extra: int) -> void {
if b.len + extra + 1 <= b.cap { return }
@ -74,14 +74,14 @@ program LudicFmt {
b.data = resize(b.data, b.cap)
}
function buf_putc(b: Buf, c: int) -> void { buf_ensure(b, 1); b.data[b.len] = c; b.len = b.len + 1 }
function buf_puts(b: Buf, s: ptr) -> void { var i = 0; while s[i] != 0 { buf_putc(b, s[i]); i = i + 1 } }
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_indent(b: Buf, n: int) -> void { var i = 0; while i < n { buf_putc(b, 32); i = i + 1 } }
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 }
# append src[a..b) raw
function buf_addrange(b: Buf, s: ptr, a: int, e: int) -> void { var k = a; while k < e { buf_putc(b, s[k]); k = k + 1 } }
function buf_addrange(b: Buf, s: pointer, a: int, e: int) -> void { var k = a; while k < e { buf_putc(b, s[k]); k = k + 1 } }
# ---- the token stream (parallel slices) ----
var src: ptr = null
var src: pointer = null
var tk_kind: []int
var tk_start: []int
var tk_end: []int
@ -90,19 +90,19 @@ program LudicFmt {
function ntok() -> int { return len(tk_kind) }
function tok_len(i: int) -> int { return tk_end[i] - tk_start[i] }
function tok_text(i: int) -> ptr { return src[tk_start[i]..tk_end[i]] }
function tok_text(i: int) -> pointer { return src[tk_start[i]..tk_end[i]] }
function push_tok(k: int, st: int, en: int, ln: int) -> void {
push(tk_kind, k); push(tk_start, st); push(tk_end, en); push(tk_line, ln)
}
# ---- vocabulary classifiers ----
function is_type_word(w: ptr) -> bool {
function is_type_word(w: pointer) -> bool {
return (w == "int") or (w == "long") or (w == "fixed") or (w == "bool") or (w == "entity") or (w == "str") or (w == "ptr") or (w == "byte") or (w == "words") or (w == "fixeds") or (w == "ptrs") or (w == "void")
}
function is_phase_word(w: ptr) -> bool {
function is_phase_word(w: pointer) -> bool {
return (w == "Start") or (w == "Input") or (w == "FixedUpdate") or (w == "Update") or (w == "LateUpdate") or (w == "Render")
}
function is_keyword_word(w: ptr) -> bool {
function is_keyword_word(w: pointer) -> bool {
if (w == "program") or (w == "import") or (w == "property") or (w == "model") or (w == "enum") or (w == "ui") { return true }
if (w == "const") or (w == "var") or (w == "function") or (w == "extern") or (w == "handler") or (w == "entry") or (w == "event") or (w == "scene") { return true }
if (w == "phase") or (w == "query") or (w == "on") or (w == "cancellable") or (w == "public") or (w == "layer") or (w == "start") { return true }
@ -111,7 +111,7 @@ program LudicFmt {
if (w == "and") or (w == "or") or (w == "not") or (w == "break") or (w == "continue") or (w == "new") or (w == "emit") or (w == "cancel") { return true }
return false
}
function is_clause_word(w: ptr) -> bool {
function is_clause_word(w: pointer) -> bool {
return (w == "phase") or (w == "query") or (w == "reads") or (w == "writes") or (w == "needs") or (w == "uses") or (w == "requires") or (w == "ensures") or (w == "invariant") or (w == "effects")
}
@ -129,7 +129,7 @@ program LudicFmt {
return c == 43 or c == 45 or c == 42 or c == 47 or c == 37 or c == 60 or c == 62 or c == 61 or c == 40 or c == 41 or c == 123 or c == 125 or c == 91 or c == 93 or c == 44 or c == 58 or c == 46 or c == 33 or c == 64 or c == 59
}
function lex(s: ptr) -> void {
function lex(s: pointer) -> void {
src = s
tk_kind = new []int; tk_start = new []int; tk_end = new []int; tk_line = new []int
linestart = new []int
@ -394,7 +394,7 @@ program LudicFmt {
var g_info: int = 0
var g_marker: int = 0
# detect a fence at line offset i; sets g_flen/g_info/g_marker; returns bool
function md_fence_at(s: ptr, i: int) -> bool {
function md_fence_at(s: pointer, i: int) -> bool {
var j = i; while s[j] == 32 { j = j + 1 }
let m = s[j]
if m != 96 and m != 126 { return false } # ` or ~
@ -403,16 +403,16 @@ program LudicFmt {
g_flen = n; g_info = j; g_marker = m
return true
}
function md_info_is_ludic(s: ptr, at: int) -> bool {
function md_info_is_ludic(s: pointer, at: int) -> bool {
var a = at; while s[a] == 32 or s[a] == 9 { a = a + 1 }
# case-insensitive "ludic"
if not (((s[a] == 108 or s[a] == 76)) and ((s[a + 1] == 117 or s[a + 1] == 85)) and ((s[a + 2] == 100 or s[a + 2] == 68)) and ((s[a + 3] == 105 or s[a + 3] == 73)) and ((s[a + 4] == 99 or s[a + 4] == 67))) { return false }
let af = s[a + 5]
return af == 0 or af == 10 or af == 32 or af == 9 or af == 13
}
function line_end(s: ptr, i: int) -> int { var e = i; while s[e] != 0 and s[e] != 10 { e = e + 1 }; return e }
function line_end(s: pointer, i: int) -> int { var e = i; while s[e] != 0 and s[e] != 10 { e = e + 1 }; return e }
function format_markdown(s: ptr, indent_width: int) -> Buf {
function format_markdown(s: pointer, indent_width: int) -> Buf {
let o = buf_new()
var i = 0
while s[i] != 0 {
@ -478,14 +478,14 @@ program LudicFmt {
}
# ---- ends-with helpers for extension detection ----
function ends_with(s: ptr, suf: ptr) -> bool {
function ends_with(s: pointer, suf: pointer) -> bool {
let n = cstr_len(s); let m = cstr_len(suf)
if m > n { return false }
return (s[n - m..n] == suf)
}
# ---- stdin slurp (for `-`) ----
function slurp_stdin() -> ptr {
function slurp_stdin() -> pointer {
let b = buf_new()
var c = read_char()
while c >= 0 { buf_putc(b, c); c = read_char() }
@ -493,14 +493,14 @@ program LudicFmt {
}
# format one source string according to its kind (markdown vs ludic)
function format_source(text: ptr, is_md: bool, indent_width: int) -> ptr {
function format_source(text: pointer, is_md: bool, indent_width: int) -> pointer {
if is_md { let m = format_markdown(text, indent_width); return buf_str(m) }
lex(text)
let f = format(indent_width)
return buf_str(f)
}
function streq(a: ptr, b: ptr) -> bool { return (a == b) }
function streq(a: pointer, b: pointer) -> bool { return (a == b) }
entry {
var write = false
@ -509,7 +509,7 @@ program LudicFmt {
var quiet = false
var changed = false
var failed = false
let files = new []ptr
let files = new []pointer
var ai = 1
while ai < arg_count() {
let a = arg(ai)

View file

@ -72,7 +72,7 @@ program LudicLsp {
const SC_UNKNOWN: int = 24
# ---- tiny helpers ----
function read_file(path: ptr) -> ptr {
function read_file(path: pointer) -> pointer {
let f = file_open(path, "rb")
if (f == null) { return null }
file_seek(f, 0, 2)
@ -84,7 +84,7 @@ program LudicLsp {
file_close(f)
return buf
}
function cstr_len(s: ptr) -> int { var n = 0; while s[n] != 0 { n = n + 1 }; return n }
function cstr_len(s: pointer) -> int { var n = 0; while s[n] != 0 { n = n + 1 }; return n }
function char_is_digit(c: int) -> bool { return c >= 48 and c <= 57 }
function char_is_alpha(c: int) -> bool {
if c >= 65 and c <= 90 { return true }
@ -93,7 +93,7 @@ program LudicLsp {
}
function char_is_alnum(c: int) -> bool { return char_is_alpha(c) or char_is_digit(c) }
function char_is_hex(c: int) -> bool { return char_is_digit(c) or (c >= 97 and c <= 102) or (c >= 65 and c <= 70) }
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
if x < 0 { neg = true; x = 0 - x }
@ -108,7 +108,7 @@ program LudicLsp {
out[total] = 0
return out
}
function atoi(s: ptr) -> int {
function atoi(s: pointer) -> int {
var n = 0; var i = 0; var neg = false
while s[i] == 32 or s[i] == 9 { i = i + 1 }
if s[i] == 45 { neg = true; i = i + 1 }
@ -118,7 +118,7 @@ program LudicLsp {
}
# ---- growable byte buffer ----
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; b.cap = 256; b.data = bytes(b.cap); b.len = 0; return b }
function buf_ensure(b: Buf, extra: int) -> void {
if b.len + extra + 1 <= b.cap { return }
@ -126,20 +126,20 @@ program LudicLsp {
b.data = resize(b.data, b.cap)
}
function buf_putc(b: Buf, c: int) -> void { buf_ensure(b, 1); b.data[b.len] = c; b.len = b.len + 1 }
function buf_puts(b: Buf, s: ptr) -> void { var i = 0; while s[i] != 0 { buf_putc(b, s[i]); i = i + 1 } }
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_addrange(b: Buf, s: ptr, a: int, e: int) -> void { var k = a; while k < e { buf_putc(b, s[k]); k = k + 1 } }
function buf_str(b: Buf) -> ptr { b.data[b.len] = 0; return b.data }
function buf_addrange(b: Buf, s: pointer, a: int, e: int) -> void { var k = a; while k < e { buf_putc(b, s[k]); k = k + 1 } }
function buf_str(b: Buf) -> pointer { b.data[b.len] = 0; return b.data }
# ---- string helpers ----
function streq(a: ptr, b: ptr) -> bool { return (a == b) }
function concat3(a: ptr, b: ptr, c: ptr) -> ptr { return ((a + b) + c) }
function ends_with(s: ptr, suf: ptr) -> bool {
function streq(a: pointer, b: pointer) -> bool { return (a == b) }
function concat3(a: pointer, b: pointer, c: pointer) -> pointer { return ((a + b) + c) }
function ends_with(s: pointer, suf: pointer) -> bool {
let n = cstr_len(s); let m = cstr_len(suf)
if m > n { return false }
return (s[n - m..n] == suf)
}
function starts_with(s: ptr, pre: ptr) -> bool {
function starts_with(s: pointer, pre: pointer) -> bool {
let m = cstr_len(pre)
if m > cstr_len(s) { return false }
return (s[0..m] == pre)
@ -150,10 +150,10 @@ program LudicLsp {
# ============================================================================
property Sym {
kind: int = 0
name: ptr = null
ty: ptr = null
detail: ptr = null
doc: ptr = null
name: pointer = null
ty: pointer = null
detail: pointer = null
doc: pointer = null
tok: int = 0
start: int = 0
end: int = 0
@ -163,15 +163,15 @@ program LudicLsp {
exported: int = 0
}
property Doc {
path: ptr = null
uri: ptr = null
raw: ptr = null
text: ptr = null
path: pointer = null
uri: pointer = null
raw: pointer = null
text: pointer = null
is_md: int = 0
is_unit: int = 0
is_module: int = 0
unit: ptr = null
src: ptr = null
unit: pointer = null
src: pointer = null
tk_kind: []int
tk_start: []int
tk_end: []int
@ -180,14 +180,14 @@ program LudicLsp {
tmatch: []int
cls: []int
syms: []Sym
imports: []ptr
imports: []pointer
had_diags: int = 0
isopen: int = 0
version: int = 0
}
var g_docs: []Doc
var g_root: ptr = null
var g_root: pointer = null
var g_shutdown: int = 0
# ---- the lexer, over a Doc ----
@ -203,13 +203,13 @@ program LudicLsp {
function is_op1(c: int) -> bool {
return c == 43 or c == 45 or c == 42 or c == 47 or c == 37 or c == 60 or c == 62 or c == 61 or c == 40 or c == 41 or c == 123 or c == 125 or c == 91 or c == 93 or c == 44 or c == 58 or c == 46 or c == 33 or c == 64 or c == 59
}
function is_type_word(w: ptr) -> bool {
return (w == "int") or (w == "long") or (w == "fixed") or (w == "bool") or (w == "entity") or (w == "string") or (w == "ptr") or (w == "byte") or (w == "words") or (w == "fixeds") or (w == "ptrs") or (w == "void")
function is_type_word(w: pointer) -> bool {
return (w == "int") or (w == "long") or (w == "fixed") or (w == "bool") or (w == "entity") or (w == "string") or (w == "pointer") or (w == "byte") or (w == "words") or (w == "fixeds") or (w == "pointers") or (w == "void")
}
function is_phase_word(w: ptr) -> bool {
function is_phase_word(w: pointer) -> bool {
return (w == "Start") or (w == "Input") or (w == "FixedUpdate") or (w == "Update") or (w == "LateUpdate") or (w == "Render")
}
function is_keyword_word(w: ptr) -> bool {
function is_keyword_word(w: pointer) -> bool {
if (w == "program") or (w == "import") or (w == "property") or (w == "model") or (w == "enum") or (w == "ui") { return true }
if (w == "const") or (w == "var") or (w == "function") or (w == "extern") or (w == "handler") or (w == "entry") or (w == "event") or (w == "scene") { return true }
if (w == "phase") or (w == "query") or (w == "on") or (w == "cancellable") or (w == "public") or (w == "layer") or (w == "start") { return true }
@ -218,7 +218,7 @@ program LudicLsp {
if (w == "and") or (w == "or") or (w == "not") or (w == "break") or (w == "continue") or (w == "new") or (w == "emit") or (w == "cancel") { return true }
return false
}
function is_widget_word(w: ptr) -> bool {
function is_widget_word(w: pointer) -> bool {
return (w == "panel") or (w == "col") or (w == "row") or (w == "label") or (w == "button") or (w == "image") or (w == "spacer")
}
@ -267,13 +267,13 @@ program LudicLsp {
function ntok(D: Doc) -> int { return len(D.tk_kind) }
function tlen(D: Doc, i: int) -> int { return D.tk_end[i] - D.tk_start[i] }
function ttext(D: Doc, i: int) -> ptr { return D.src[D.tk_start[i]..D.tk_end[i]] }
function tis(D: Doc, i: int, s: ptr) -> bool { if i < 0 or i >= ntok(D) { return false }; return (ttext(D, i) == s) }
function ttext(D: Doc, i: int) -> pointer { return D.src[D.tk_start[i]..D.tk_end[i]] }
function tis(D: Doc, i: int, s: pointer) -> bool { if i < 0 or i >= ntok(D) { return false }; return (ttext(D, i) == s) }
function next_sig(D: Doc, i: int) -> int { var j = i + 1; while j < ntok(D) { let k = D.tk_kind[j]; if k != LT_NL and k != LT_COMMENT { return j }; j = j + 1 }; return 0 - 1 }
function prev_sig(D: Doc, i: int) -> int { var j = i - 1; while j >= 0 { let k = D.tk_kind[j]; if k != LT_NL and k != LT_COMMENT { return j }; j = j - 1 }; return 0 - 1 }
# ---- positions (UTF-16 for LSP) ----
function utf16_len(s: ptr, a: int, nbytes: int) -> int {
function utf16_len(s: pointer, a: int, nbytes: int) -> int {
var u = 0; var i = 0
while i < nbytes {
let c = s[a + i]
@ -342,7 +342,7 @@ program LudicLsp {
}
# ---- doc comment: the comment block directly above a declaration ----
function collect_doc(D: Doc, decl_tok: int) -> ptr {
function collect_doc(D: Doc, decl_tok: int) -> pointer {
var line = D.tk_line[decl_tok]
var first = 0 - 1
var i = decl_tok - 1
@ -380,10 +380,10 @@ program LudicLsp {
var pi: int = 0
function pk() -> int { if pi < ntok(pD) { return pD.tk_kind[pi] }; return LT_EOF }
function pis(s: ptr) -> bool { return tis(pD, pi, s) }
function pis(s: pointer) -> bool { return tis(pD, pi, s) }
function pskipnl() -> void { while pi < ntok(pD) and (pk() == LT_NL or pk() == LT_COMMENT) { pi = pi + 1 } }
function padv() -> void { if pi < ntok(pD) { pi = pi + 1 }; pskipnl() }
function pword() -> ptr { if pi < ntok(pD) { return ttext(pD, pi) }; return "" }
function pword() -> pointer { if pi < ntok(pD) { return ttext(pD, pi) }; return "" }
function pname() -> int {
let k = pk()
if k == LT_ID or k == LT_TYPE or k == LT_PHASE or k == LT_KW or k == LT_BOOL { let t = pi; padv(); return t }
@ -797,7 +797,7 @@ program LudicLsp {
function parse_doc(D: Doc) -> void {
D.syms = new []Sym
D.imports = new []ptr
D.imports = new []pointer
D.is_unit = 0; D.is_module = 0; D.unit = ""
pD = D; pi = 0
pskipnl()
@ -848,7 +848,7 @@ program LudicLsp {
if l >= 97 and l <= 102 { return l - 97 + 10 }
return 0 - 1
}
function uri_to_path(uri: ptr) -> ptr {
function uri_to_path(uri: pointer) -> pointer {
var s = uri
if starts_with(uri, "file://") { s = uri[7..cstr_len(uri)] }
let n = cstr_len(s)
@ -866,7 +866,7 @@ program LudicLsp {
return c == 45 or c == 46 or c == 126 or c == 47 # - . ~ /
}
function hexch(n: int) -> int { if n < 10 { return 48 + n }; return 55 + n }
function path_to_uri(path: ptr) -> ptr {
function path_to_uri(path: pointer) -> pointer {
let out = buf_new()
buf_puts(out, "file://")
var i = 0
@ -878,7 +878,7 @@ program LudicLsp {
}
return buf_str(out)
}
function dirname(p: ptr) -> ptr {
function dirname(p: pointer) -> pointer {
var last = 0 - 1
var i = 0
while p[i] != 0 { if p[i] == 47 { last = i }; i = i + 1 }
@ -886,10 +886,10 @@ program LudicLsp {
if last == 0 { return "/" }
return p[0..last]
}
function normpath(p: ptr) -> ptr {
function normpath(p: pointer) -> pointer {
let n = cstr_len(p)
let abs = (n > 0 and p[0] == 47)
let list = new []ptr
let list = new []pointer
var cnt = 0
var i = 0
while i < n {
@ -914,16 +914,16 @@ program LudicLsp {
while k < cnt { if k > 0 { buf_putc(out, 47) }; buf_puts(out, list[k]); k = k + 1 }
return buf_str(out)
}
function join_path(dir: ptr, rel: ptr) -> ptr {
function join_path(dir: pointer, rel: pointer) -> pointer {
if rel[0] == 47 { return normpath(rel) }
return normpath(concat3(dir, "/", rel))
}
# ---- markdown scrub: blank everything outside a ```ludic fence ----
function word_ludic_at(s: ptr, at: int) -> bool {
function word_ludic_at(s: pointer, at: int) -> bool {
return ((s[at] == 108 or s[at] == 76)) and ((s[at + 1] == 117 or s[at + 1] == 85)) and ((s[at + 2] == 100 or s[at + 2] == 68)) and ((s[at + 3] == 105 or s[at + 3] == 73)) and ((s[at + 4] == 99 or s[at + 4] == 67))
}
function scrub_markdown(src: ptr) -> ptr {
function scrub_markdown(src: pointer) -> pointer {
let n = cstr_len(src)
let out = bytes(n + 1)
var c0 = 0
@ -974,7 +974,7 @@ program LudicLsp {
}
# ---- documents ----
function set_text(D: Doc, raw: ptr) -> void {
function set_text(D: Doc, raw: pointer) -> void {
D.raw = raw
if D.is_md == 1 { D.text = scrub_markdown(raw) } else { D.text = raw }
D.src = D.text
@ -988,24 +988,24 @@ program LudicLsp {
let N = ntok(D)
while i < N { push(D.cls, SC_NONE); i = i + 1 }
}
function new_doc(path: ptr, text: ptr) -> Doc {
function new_doc(path: pointer, text: pointer) -> Doc {
let D = new Doc
D.path = normpath(path)
D.uri = path_to_uri(D.path)
D.is_md = 0
if ends_with(D.path, ".md") or ends_with(D.path, ".markdown") { D.is_md = 1 }
D.syms = new []Sym; D.imports = new []ptr; D.unit = ""
D.syms = new []Sym; D.imports = new []pointer; D.unit = ""
set_text(D, text)
reindex(D)
return D
}
function by_path(path: ptr) -> Doc {
function by_path(path: pointer) -> Doc {
let p = normpath(path)
var i = 0
while i < len(g_docs) { if (g_docs[i].path == p) { return g_docs[i] }; i = i + 1 }
return null
}
function by_uri(uri: ptr) -> Doc {
function by_uri(uri: pointer) -> Doc {
let p = uri_to_path(uri)
let D = by_path(p)
if (D != null) { return D }
@ -1013,7 +1013,7 @@ program LudicLsp {
while i < len(g_docs) { if (g_docs[i].uri == uri) { return g_docs[i] }; i = i + 1 }
return null
}
function ensure_doc(path: ptr) -> Doc {
function ensure_doc(path: pointer) -> Doc {
let D = by_path(path)
if (D != null) { return D }
let text = read_file(path)
@ -1023,7 +1023,7 @@ program LudicLsp {
return nd
}
function scan_root(root: ptr) -> void {
function scan_root(root: pointer) -> void {
if (root == null) { return }
let listfile = "/tmp/ludic_lsp_scan.txt"
run(concat3(concat3("find '", root, "' -name '*.ludic' -not -path '*/build/*' -not -path '*/node_modules/*' -not -path '*/.*' > "), listfile, " 2>/dev/null"))
@ -1096,7 +1096,7 @@ program LudicLsp {
while i < len(rel) { if rel[i].is_unit == 1 and rel[i].is_md == 0 { return rel[i] }; i = i + 1 }
return null
}
function find_local(D: Doc, name: ptr, off: int) -> int {
function find_local(D: Doc, name: pointer, off: int) -> int {
var best = 0 - 1
var i = 0
while i < len(D.syms) {
@ -1109,7 +1109,7 @@ program LudicLsp {
return best
}
# returns sym index in g_owner (set), or -1
function find_top(D: Doc, name: ptr) -> int {
function find_top(D: Doc, name: pointer) -> int {
let rel = related(D)
var i = 0
while i < len(rel) {
@ -1183,18 +1183,18 @@ program LudicLsp {
}
# ---- builtins / intrinsics (name -> signature) ----
var g_builtins: []ptr
var g_bsigs: []ptr
function badd(name: ptr, sig: ptr) -> void { push(g_builtins, name); push(g_bsigs, sig) }
function is_builtin_name(name: ptr) -> bool { var i = 0; while i < len(g_builtins) { if (g_builtins[i] == name) { return true }; i = i + 1 }; return false }
function builtin_sig(name: ptr) -> ptr { var i = 0; while i < len(g_builtins) { if (g_builtins[i] == name) { return g_bsigs[i] }; i = i + 1 }; return null }
var g_builtins: []pointer
var g_bsigs: []pointer
function badd(name: pointer, sig: pointer) -> void { push(g_builtins, name); push(g_bsigs, sig) }
function is_builtin_name(name: pointer) -> bool { var i = 0; while i < len(g_builtins) { if (g_builtins[i] == name) { return true }; i = i + 1 }; return false }
function builtin_sig(name: pointer) -> pointer { var i = 0; while i < len(g_builtins) { if (g_builtins[i] == name) { return g_bsigs[i] }; i = i + 1 }; return null }
# The namespaced standard-library surface: `Namespace.method`, mirroring the
# compiler's emit_ns_call (selfhost/emit_expr.ludic). Screen/Random/Input/Map
# methods resolve to the underlying bare builtin's signature; Math/Text/List
# are pure fixed-point / string / slice ops with their own signatures. Returns
# the signature label for signature help, or null if it is not a known method.
function ns_method_sig(ns: ptr, meth: ptr) -> ptr {
function ns_method_sig(ns: pointer, meth: pointer) -> pointer {
if (ns == "Screen") {
if (meth == "clear") { return builtin_sig("clear") }
if (meth == "fill_rectangle") { return builtin_sig("fill_rect") }
@ -1366,14 +1366,14 @@ program LudicLsp {
if (meth == "run") { return "System.run(command) -> int" }
if (meth == "env") { return "System.env(name) -> string" }
if (meth == "read_char") { return "System.read_char() -> int" }
if (meth == "file_open") { return "System.file_open(path, mode) -> ptr" }
if (meth == "file_open") { return "System.file_open(path, mode) -> pointer" }
if (meth == "file_read") { return "System.file_read(file, buf, n) -> int" }
if (meth == "file_write") { return "System.file_write(file, buf, n) -> int" }
if (meth == "file_seek") { return "System.file_seek(file, offset, whence) -> int" }
if (meth == "file_tell") { return "System.file_tell(file) -> int" }
if (meth == "file_close") { return "System.file_close(file)" }
if (meth == "stdout") { return "System.stdout() -> ptr" }
if (meth == "stderr") { return "System.stderr() -> ptr" }
if (meth == "stdout") { return "System.stdout() -> pointer" }
if (meth == "stderr") { return "System.stderr() -> pointer" }
}
if (ns == "Save") {
if (meth == "write") { return "Save.write()" }
@ -1386,7 +1386,7 @@ program LudicLsp {
if (meth == "now") { return "Time.now() -> int" }
}
if (ns == "Memory") {
if (meth == "bytes") { return "Memory.bytes(n) -> ptr" }
if (meth == "bytes") { return "Memory.bytes(n) -> pointer" }
if (meth == "words") { return "Memory.words(n) -> words" }
if (meth == "copy") { return "Memory.copy(dst, src, n)" }
if (meth == "fill") { return "Memory.fill(buf, value, n)" }
@ -1396,7 +1396,7 @@ program LudicLsp {
return null
}
function init_builtins() -> void {
g_builtins = new []ptr; g_bsigs = new []ptr
g_builtins = new []pointer; g_bsigs = new []pointer
badd("min", "min(a: int, b: int) -> int")
badd("max", "max(a: int, b: int) -> int")
badd("abs", "abs(a: int) -> int")
@ -1448,31 +1448,31 @@ program LudicLsp {
badd("print", "print(x)")
badd("string", "string(x) -> string")
badd("quit", "quit()")
badd("bytes", "bytes(n: int) -> ptr")
badd("bytes", "bytes(n: int) -> pointer")
badd("words", "words(n: int) -> words")
badd("resize", "resize(p: ptr, n: int) -> ptr")
badd("resize", "resize(p: pointer, n: int) -> pointer")
badd("arg_count", "arg_count() -> int")
badd("arg", "arg(i: int) -> string")
badd("file_stderr", "file_stderr() -> ptr")
badd("file_stdout", "file_stdout() -> ptr")
badd("free", "free(p: ptr)")
badd("fill", "fill(p: ptr, byte: int, n: int)")
badd("offset", "offset(p: ptr, off: int) -> ptr")
badd("file_stderr", "file_stderr() -> pointer")
badd("file_stdout", "file_stdout() -> pointer")
badd("free", "free(p: pointer)")
badd("fill", "fill(p: pointer, byte: int, n: int)")
badd("offset", "offset(p: pointer, off: int) -> pointer")
badd("as_fixed", "as_fixed(i: int) -> fixed")
badd("as_int", "as_int(f: fixed) -> int")
badd("file_open", "file_open(path: string, mode: string) -> ptr")
badd("file_read", "file_read(f: ptr, buf: ptr, n: int) -> int")
badd("file_write", "file_write(f: ptr, buf: ptr, n: int) -> int")
badd("file_seek", "file_seek(f: ptr, off: int, whence: int) -> int")
badd("file_tell", "file_tell(f: ptr) -> int")
badd("file_close", "file_close(f: ptr)")
badd("file_open", "file_open(path: string, mode: string) -> pointer")
badd("file_read", "file_read(f: pointer, buf: pointer, n: int) -> int")
badd("file_write", "file_write(f: pointer, buf: pointer, n: int) -> int")
badd("file_seek", "file_seek(f: pointer, off: int, whence: int) -> int")
badd("file_tell", "file_tell(f: pointer) -> int")
badd("file_close", "file_close(f: pointer)")
badd("read_char", "read_char() -> int")
badd("exit", "exit(code: int)")
badd("is_windowed", "is_windowed() -> bool")
badd("game_title", "game_title() -> string")
badd("win_open", "win_open(title: string, w: int, h: int, scale: int)")
badd("win_poll", "win_poll() -> int")
badd("win_present", "win_present(px: ptr, w: int, h: int)")
badd("win_present", "win_present(px: pointer, w: int, h: int)")
badd("win_running", "win_running() -> bool")
badd("win_close", "win_close()")
}
@ -1579,13 +1579,13 @@ program LudicLsp {
const JSTR: int = 3
const JARR: int = 4
const JOBJ: int = 5
property JVal { t: int = 0, num: int = 0, b: int = 0, s: ptr = null, kids: []JVal, keys: []ptr }
function jval(t: int) -> JVal { let v = new JVal; v.t = t; v.kids = new []JVal; v.keys = new []ptr; return v }
property JVal { t: int = 0, num: int = 0, b: int = 0, s: pointer = null, kids: []JVal, keys: []pointer }
function jval(t: int) -> JVal { let v = new JVal; v.t = t; v.kids = new []JVal; v.keys = new []pointer; return v }
var jsrc: ptr = null
var jsrc: pointer = null
var jp: int = 0
function jskip() -> void { while jsrc[jp] == 32 or jsrc[jp] == 9 or jsrc[jp] == 10 or jsrc[jp] == 13 { jp = jp + 1 } }
function jhex4(s: ptr, at: int) -> int {
function jhex4(s: pointer, at: int) -> int {
var v = 0; var i = 0
while i < 4 { let h = hexval(s[at + i]); if h < 0 { return v }; v = v * 16 + h; i = i + 1 }
return v
@ -1596,7 +1596,7 @@ program LudicLsp {
else { if cp < 65536 { buf_putc(o, 224 | (cp >> 12)); buf_putc(o, 128 | ((cp >> 6) & 63)); buf_putc(o, 128 | (cp & 63)) }
else { buf_putc(o, 240 | (cp >> 18)); buf_putc(o, 128 | ((cp >> 12) & 63)); buf_putc(o, 128 | ((cp >> 6) & 63)); buf_putc(o, 128 | (cp & 63)) } } }
}
function jparse_string() -> ptr {
function jparse_string() -> pointer {
jp = jp + 1 # skip opening quote
let o = buf_new()
while jsrc[jp] != 0 and jsrc[jp] != 34 {
@ -1671,7 +1671,7 @@ program LudicLsp {
jp = jp + 1
return jval(JNULL)
}
function jget(v: JVal, key: ptr) -> JVal {
function jget(v: JVal, key: pointer) -> JVal {
if (v == null) { return null }
if v.t != JOBJ { return null }
var i = 0
@ -1680,9 +1680,9 @@ program LudicLsp {
}
function jat(v: JVal, i: int) -> JVal { if (v == null) { return null }; if i < 0 or i >= len(v.kids) { return null }; return v.kids[i] }
function jlen(v: JVal) -> int { if (v == null) { return 0 }; return len(v.kids) }
function jstr(v: JVal, def: ptr) -> ptr { if (v != null) and v.t == JSTR and (v.s != null) { return v.s }; return def }
function jstr(v: JVal, def: pointer) -> pointer { if (v != null) and v.t == JSTR and (v.s != null) { return v.s }; return def }
function jint(v: JVal, def: int) -> int { if (v != null) and v.t == JNUM { return v.num }; return def }
function jpath(v: JVal, path: ptr) -> JVal {
function jpath(v: JVal, path: pointer) -> JVal {
var cur = v
let n = cstr_len(path)
var start = 0; var i = 0
@ -1741,16 +1741,16 @@ program LudicLsp {
}
return 1
}
function f_is_clause(w: ptr) -> bool {
function f_is_clause(w: pointer) -> bool {
return (w == "phase") or (w == "query") or (w == "reads") or (w == "writes") or (w == "needs") or (w == "uses") or (w == "requires") or (w == "ensures") or (w == "invariant") or (w == "effects")
}
function make_text_doc(text: ptr) -> Doc {
function make_text_doc(text: pointer) -> Doc {
let F = new Doc
F.raw = text; F.text = text; F.src = text; F.is_md = 0
lex_doc(F)
return F
}
function format_doc(F: Doc, iw: int) -> ptr {
function format_doc(F: Doc, iw: int) -> pointer {
let o = buf_new()
let stack = words(600)
let hang = words(600)
@ -1855,7 +1855,7 @@ program LudicLsp {
var mf_flen: int = 0
var mf_info: int = 0
var mf_marker: int = 0
function mf_fence_at(s: ptr, i: int) -> bool {
function mf_fence_at(s: pointer, i: int) -> bool {
var j = i; while s[j] == 32 { j = j + 1 }
let m = s[j]
if m != 96 and m != 126 { return false }
@ -1864,8 +1864,8 @@ program LudicLsp {
mf_flen = n; mf_info = j; mf_marker = m
return true
}
function f_line_end(s: ptr, i: int) -> int { var e = i; while s[e] != 0 and s[e] != 10 { e = e + 1 }; return e }
function format_md(s: ptr, iw: int) -> ptr {
function f_line_end(s: pointer, i: int) -> int { var e = i; while s[e] != 0 and s[e] != 10 { e = e + 1 }; return e }
function format_md(s: pointer, iw: int) -> pointer {
let o = buf_new()
var i = 0
while s[i] != 0 {
@ -1925,14 +1925,14 @@ program LudicLsp {
}
return buf_str(o)
}
function skip_ws2(s: ptr, at: int) -> int { var a = at; while s[a] == 32 or s[a] == 9 { a = a + 1 }; return a }
function skip_ws2(s: pointer, at: int) -> int { var a = at; while s[a] == 32 or s[a] == 9 { a = a + 1 }; return a }
# ============================================================================
# framing + output
# ============================================================================
extern function fflush(f: ptr) -> int = "fflush"
extern function fflush(f: pointer) -> int = "fflush"
function jstr_out(o: Buf, z: ptr) -> void {
function jstr_out(o: Buf, z: pointer) -> void {
buf_putc(o, 34)
if (z == null) { buf_putc(o, 34); return }
var i = 0
@ -1950,7 +1950,7 @@ program LudicLsp {
buf_putc(o, 34)
}
function send_raw(body: ptr) -> void {
function send_raw(body: pointer) -> void {
let hdr = buf_new()
buf_puts(hdr, "Content-Length: "); buf_puti(hdr, cstr_len(body)); buf_putc(hdr, 13); buf_putc(hdr, 10); buf_putc(hdr, 13); buf_putc(hdr, 10)
let hs = buf_str(hdr)
@ -1959,7 +1959,7 @@ program LudicLsp {
file_write(out, body, cstr_len(body))
let z = fflush(out)
}
function send_result(idv: JVal, result: ptr) -> void {
function send_result(idv: JVal, result: pointer) -> void {
let o = buf_new()
buf_puts(o, "{\"jsonrpc\":\"2.0\",\"id\":")
if (idv == null) { buf_puts(o, "null") }
@ -1969,19 +1969,19 @@ program LudicLsp {
buf_putc(o, 125)
send_raw(buf_str(o))
}
function send_notify(method: ptr, params: ptr) -> void {
function send_notify(method: pointer, params: pointer) -> void {
let o = buf_new()
buf_puts(o, "{\"jsonrpc\":\"2.0\",\"method\":"); jstr_out(o, method); buf_puts(o, ",\"params\":"); buf_puts(o, params); buf_putc(o, 125)
send_raw(buf_str(o))
}
function lower(c: int) -> int { if c >= 65 and c <= 90 { return c + 32 }; return c }
function starts_with_ci(s: ptr, pre: ptr) -> bool {
function starts_with_ci(s: pointer, pre: pointer) -> bool {
var i = 0
while pre[i] != 0 { if s[i] == 0 or lower(s[i]) != lower(pre[i]) { return false }; i = i + 1 }
return true
}
function read_message() -> ptr {
function read_message() -> pointer {
var length = 0 - 1
let line = buf_new()
while true {
@ -2015,12 +2015,12 @@ program LudicLsp {
if (D != null) { return D }
return ensure_doc(uri_to_path(uri))
}
function off_of(msg: JVal, D: Doc, base: ptr) -> int {
function off_of(msg: JVal, D: Doc, base: pointer) -> int {
let line = jint(jpath(msg, concat3(base, ".line", "")), 0)
let ch = jint(jpath(msg, concat3(base, ".character", "")), 0)
return offset_of(D, line, ch)
}
function basename(p: ptr) -> ptr { var last = 0 - 1; var i = 0; while p[i] != 0 { if p[i] == 47 { last = i }; i = i + 1 }; return p[last + 1..i] }
function basename(p: pointer) -> pointer { var last = 0 - 1; var i = 0; while p[i] != 0 { if p[i] == 47 { last = i }; i = i + 1 }; return p[last + 1..i] }
# ---- LSP symbol kinds & completion kinds ----
function lsp_symbol_kind(k: int) -> int {
@ -2055,7 +2055,7 @@ program LudicLsp {
# ---- diagnostics (structural) ----
function diag_structural(D: Doc, o: Buf) -> void {
let ds_s = new []int; let ds_e = new []int; let ds_m = new []ptr
let ds_s = new []int; let ds_e = new []int; let ds_m = new []pointer
let stack = words(1200); var top = 0
var i = 0; let N = ntok(D)
while i < N {
@ -2116,7 +2116,7 @@ program LudicLsp {
}
# ---- hover ----
function hover_md(o: Buf, code: ptr, docstr: ptr) -> void {
function hover_md(o: Buf, code: pointer, docstr: pointer) -> void {
let m = buf_new()
buf_puts(m, "```ludic\n"); buf_puts(m, code); buf_puts(m, "\n```")
if (docstr != null) and (docstr[0] != 0) { buf_puts(m, "\n\n"); buf_puts(m, docstr) }
@ -2250,7 +2250,7 @@ program LudicLsp {
# ---- completion ----
var g_first: int = 1
function comp_item(o: Buf, label: ptr, kind: int, detail: ptr) -> void {
function comp_item(o: Buf, label: pointer, kind: int, detail: pointer) -> void {
if g_first == 0 { buf_putc(o, 44) }
g_first = 0
buf_puts(o, "{\"label\":"); jstr_out(o, label); buf_puts(o, ",\"kind\":"); buf_puti(o, kind)
@ -2287,7 +2287,7 @@ program LudicLsp {
}
function comp_types(o: Buf) -> void {
comp_item(o, "int", 14, "built-in type"); comp_item(o, "long", 14, "built-in type"); comp_item(o, "fixed", 14, "built-in type"); comp_item(o, "bool", 14, "built-in type")
comp_item(o, "entity", 14, "built-in type"); comp_item(o, "string", 14, "built-in type"); comp_item(o, "ptr", 14, "built-in type"); comp_item(o, "void", 14, "built-in type")
comp_item(o, "entity", 14, "built-in type"); comp_item(o, "string", 14, "built-in type"); comp_item(o, "pointer", 14, "built-in type"); comp_item(o, "void", 14, "built-in type")
}
function comp_builtins(o: Buf) -> void { var i = 0; while i < len(g_builtins) { comp_item(o, g_builtins[i], 3, g_bsigs[i]); i = i + 1 } }
function on_completion(msg: JVal, id: JVal) -> void {
@ -2392,7 +2392,7 @@ program LudicLsp {
}
if callee < 0 or D.tk_kind[callee] != LT_ID { send_result(id, "null"); return }
let name = ttext(D, callee)
var sig: ptr = null
var sig: pointer = null
# a namespaced call `Namespace.method(` resolves through the ns surface first
let dot = prev_sig(D, callee)
if dot >= 0 and tis(D, dot, ".") {
@ -2487,7 +2487,7 @@ program LudicLsp {
}
buf_putc(o, 93); send_result(id, buf_str(o))
}
function contains_ci(hay: ptr, needle: ptr) -> bool {
function contains_ci(hay: pointer, needle: pointer) -> bool {
let hn = cstr_len(hay); let nn = cstr_len(needle)
if nn == 0 { return true }
var i = 0
@ -2548,7 +2548,7 @@ program LudicLsp {
if (disk != null) { set_text(D, disk); reindex(D) }
}
function caps() -> ptr {
function caps() -> pointer {
let o = buf_new()
buf_puts(o, "{\"capabilities\":{")
buf_puts(o, "\"positionEncoding\":\"utf-16\"")

View file

@ -71,7 +71,7 @@ static const char* LUDIC_KW_STMT[] = {
"and","or","not","break","continue","new","emit","cancel", 0
};
static const char* LUDIC_TYPES[] = {
"int","long","fixed","bool","entity","string","ptr","byte","words","fixeds","ptrs","void", 0
"int","long","fixed","bool","entity","string","pointer","byte","words","fixeds","pointers","void", 0
};
static const char* LUDIC_PHASES[] = {
"Start","Input","FixedUpdate","Update","LateUpdate","Render", 0

View file

@ -5,7 +5,7 @@
# compile a Ludic source to a native binary in bin/ via ludicc + clang (-O2).
# Returns true on success. Used for x itself and the editor tools.
function build_tool(name: ptr, src: ptr) -> bool {
function build_tool(name: pointer, src: pointer) -> bool {
let ll = `build/{name}.ll`
if not shq(`bin/ludicc {src} --emit-llvm -o {ll} 2>/dev/null`) { print(`build failed: {name} (compile)`); return false }
# write to a temp then move, so a running bin/x can rebuild itself in place

View file

@ -43,7 +43,7 @@ program X {
}
# a positional argument, or a default when absent
function argn(i: int, dflt: ptr) -> ptr {
function argn(i: int, dflt: pointer) -> pointer {
if (i < arg_count()) { return arg(i) }
return dflt
}

View file

@ -13,7 +13,7 @@
# ---- file IO ----------------------------------------------------------------
# read a whole file into a fresh NUL-terminated buffer (null if it cannot open)
function read_file(path: ptr) -> ptr {
function read_file(path: pointer) -> pointer {
let f = file_open(path, "rb")
if (f == null) { return null }
file_seek(f, 0, 2)
@ -27,7 +27,7 @@ function read_file(path: ptr) -> ptr {
}
# overwrite `path` with `s`; returns false if it could not be opened
function write_file(path: ptr, s: ptr) -> bool {
function write_file(path: pointer, s: pointer) -> bool {
let f = file_open(path, "wb")
if (f == null) { return false }
file_write(f, s, len(s))
@ -35,22 +35,22 @@ function write_file(path: ptr, s: ptr) -> bool {
return true
}
function file_exists(path: ptr) -> bool { return shq(`test -e {path}`) }
function is_exec(path: ptr) -> bool { return shq(`test -x {path}`) }
function file_exists(path: pointer) -> bool { return shq(`test -e {path}`) }
function is_exec(path: pointer) -> bool { return shq(`test -x {path}`) }
# is `a` newer than `b` (like the shell's `-nt`)?
function newer(a: ptr, b: ptr) -> bool { return shq(`test {a} -nt {b}`) }
function newer(a: pointer, b: pointer) -> bool { return shq(`test {a} -nt {b}`) }
# ---- process control --------------------------------------------------------
# `run` returns the raw wait status; the program's exit code is the high byte.
function exit_code(st: int) -> int { return (st >> 8) & 255 }
# run a command, returning its exit code (0 = success)
function sh(cmd: ptr) -> int { return exit_code(run(cmd)) }
function sh(cmd: pointer) -> int { return exit_code(run(cmd)) }
# run a command, true when it succeeded
function shq(cmd: ptr) -> bool { return exit_code(run(cmd)) == 0 }
function shq(cmd: pointer) -> bool { return exit_code(run(cmd)) == 0 }
# run `cmd` and return its stdout (stderr discarded). Never null.
function capture(cmd: ptr) -> ptr {
function capture(cmd: pointer) -> pointer {
let tmp = "/tmp/x_capture.out"
run(`{cmd} > {tmp} 2>/dev/null`)
let s = read_file(tmp)
@ -60,16 +60,16 @@ function capture(cmd: ptr) -> ptr {
# run `cmd`, join its output lines with single spaces and trim — the Ludic twin
# of the shell idiom `$(cmd | tr '\n' ' ' | sed 's/ *$//')`.
function capture_line(cmd: ptr) -> ptr {
function capture_line(cmd: pointer) -> pointer {
return capture(`{cmd} | tr '\n' ' ' | sed 's/ *$//'`)
}
# the last line of a command's output (for one-line error messages)
function capture_tail(cmd: ptr) -> ptr {
function capture_tail(cmd: pointer) -> pointer {
return capture(`{cmd} 2>&1 | tail -1`)
}
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
@ -78,34 +78,34 @@ function getenv_or(name: ptr, dflt: ptr) -> ptr {
# ---- stdout helpers ---------------------------------------------------------
# write `s` with no trailing newline (print() always adds one)
function out(s: ptr) -> void { file_write(file_stdout(), s, len(s)) }
function err(s: ptr) -> void { file_write(file_stderr(), s, len(s)) }
function out(s: pointer) -> void { file_write(file_stdout(), s, len(s)) }
function err(s: pointer) -> void { file_write(file_stderr(), s, len(s)) }
# an ESC byte — the lexer has no \033, so build it by hand
function esc() -> ptr { let b = bytes(2); b[0] = 27; b[1] = 0; return b }
function c_green() -> ptr { return esc() + "[32m" }
function c_red() -> ptr { return esc() + "[31m" }
function c_reset() -> ptr { return esc() + "[0m" }
function esc() -> pointer { let b = bytes(2); b[0] = 27; b[1] = 0; return b }
function c_green() -> pointer { return esc() + "[32m" }
function c_red() -> pointer { return esc() + "[31m" }
function c_reset() -> pointer { return esc() + "[0m" }
# ---- the PASS/FAIL test harness ---------------------------------------------
var PASS: int = 0
var FAIL: int = 0
function ok(msg: ptr) -> void {
function ok(msg: pointer) -> void {
PASS = PASS + 1
print(` {c_green()}PASS{c_reset()} {msg}`)
}
function bad(msg: ptr) -> void {
function bad(msg: pointer) -> void {
FAIL = FAIL + 1
print(` {c_red()}FAIL{c_reset()} {msg}`)
}
function bad2(msg: ptr, detail: ptr) -> void {
function bad2(msg: pointer, detail: pointer) -> void {
bad(msg)
print(` {detail}`)
}
# assert two strings equal, reporting the mismatch
function check(label: ptr, got: ptr, want: ptr) -> void {
function check(label: pointer, got: pointer, want: pointer) -> void {
if (got == want) { ok(label) }
else { bad2(label, `expected [{want}] got [{got}]`) }
}

View file

@ -6,12 +6,12 @@
# now lives here once, in selfhost_frags().
# the C toolchain driver (clang) that assembles and links the emitted IR
function cc() -> ptr { return getenv_or("LUDIC_CC", "clang") }
function cc() -> pointer { return getenv_or("LUDIC_CC", "clang") }
# the self-host compiler's source fragments, in link order. This is THE list;
# the old scripts each carried their own copy.
function selfhost_frags() -> []ptr {
let f = new []ptr
function selfhost_frags() -> []pointer {
let f = new []pointer
push(f, "selfhost/str.ludic")
push(f, "selfhost/buf.ludic")
push(f, "selfhost/io.ludic")
@ -52,7 +52,7 @@ function selfhost_frags() -> []ptr {
# concatenate the fragments into `program SelfHost { ... }` at `outpath`.
# This is the pure-Ludic equivalent of the scripts' `{ echo; for; cat; }` block.
function write_selfhost_src(outpath: ptr) -> bool {
function write_selfhost_src(outpath: pointer) -> bool {
let f = file_open(outpath, "wb")
if (f == null) { return false }
let hdr = "program SelfHost {\n"
@ -72,7 +72,7 @@ function write_selfhost_src(outpath: ptr) -> bool {
}
# count the lines in a file (for the progress notes the scripts printed)
function line_count(path: ptr) -> ptr {
function line_count(path: pointer) -> pointer {
return capture_line(`wc -l < {path}`)
}
@ -90,7 +90,7 @@ function ensure_ludicc() -> void {
# ---- selfhost-build: assemble + compile the self-host compiler ---------------
# usage: x selfhost-build [ludicc] [outbin] (defaults: bin/ludicc, build/selfhost)
function cmd_selfhost_build(lc: ptr, outbin: ptr) -> int {
function cmd_selfhost_build(lc: pointer, outbin: pointer) -> int {
run("mkdir -p build")
let src = "build/selfhost.ludic"
if not write_selfhost_src(src) { err("x: cannot write build/selfhost.ludic\n"); return 1 }
@ -104,7 +104,7 @@ function cmd_selfhost_build(lc: ptr, outbin: ptr) -> int {
# ---- sh-compile: compile one .ludic with a given selfhost binary and link ----
# usage: x sh-compile <selfhost-binary> <input.ludic> <output-binary>
function cmd_sh_compile(shbin: ptr, in: ptr, outbin: ptr) -> int {
function cmd_sh_compile(shbin: pointer, in: pointer, outbin: pointer) -> int {
if not shq(`{shbin} {in} > {outbin}.ll`) { return 1 }
if not shq(`{cc()} {outbin}.ll -o {outbin}`) { return 1 }
return 0
@ -116,7 +116,7 @@ function cmd_sh_compile(shbin: ptr, in: ptr, outbin: ptr) -> int {
# the quiet core, reused by the test suites; returns true on success. On failure
# the self-host/link diagnostics are left in /tmp/x_gb.err.
function game_build_ok(shbin: ptr, game: ptr, outbin: ptr) -> bool {
function game_build_ok(shbin: pointer, game: pointer, outbin: pointer) -> bool {
let ll = `{outbin}.ll`
if not shq(`{shbin} {game} > {ll} 2>/tmp/x_gb.err`) { return false }
if not shq(`{cc()} -O2 {ll} -o {outbin} 2>/tmp/x_gb.err`) { return false }
@ -125,7 +125,7 @@ function game_build_ok(shbin: ptr, game: ptr, outbin: ptr) -> bool {
}
# usage: x game-build <selfhost-binary> <game.ludic> <out-binary>
function cmd_game_build(shbin: ptr, game: ptr, outbin: ptr) -> int {
function cmd_game_build(shbin: pointer, game: pointer, outbin: pointer) -> int {
if game_build_ok(shbin, game, outbin) { print(`built {outbin}`); return 0 }
print("build failed:")
out(capture("grep -i error /tmp/x_gb.err | head -5"))

View file

@ -3,7 +3,7 @@
# fixpoint). Replaces selfhost/test.sh.
# compile selfhost/tests/<name>.ludic with bin/ludicc, assemble, run, compare
function sh_case(name: ptr, exp: ptr) -> void {
function sh_case(name: pointer, exp: pointer) -> void {
let ll = `/tmp/x_sh_{name}.ll`
let er = `/tmp/x_sh_{name}.err`
let bn = `/tmp/x_sh_{name}`
@ -16,7 +16,7 @@ function sh_case(name: ptr, exp: ptr) -> void {
}
# compile examples/<name>.ludic as a game and diff its render against the golden
function game_case(name: ptr, keys: ptr) -> void {
function game_case(name: pointer, keys: pointer) -> void {
if not game_build_ok("bin/ludicc", `examples/{name}.ludic`, `/tmp/x_g_{name}`) { bad(`{name}: build`); return }
run(`printf '%s' '{keys}' | /tmp/x_g_{name} >/dev/null 2>&1`)
if shq(`cmp -s selfhost/golden/{name}.ppm out.ppm`) { ok(`{name} matches the golden render`) }

View file

@ -9,7 +9,7 @@
# compile a feature example as a game, run it (optional keys on stdin) and match
# its stdout against the expected space-joined output.
function feat_case(name: ptr, keys: ptr, exp: ptr, label: ptr) -> void {
function feat_case(name: pointer, keys: pointer, exp: pointer, label: pointer) -> void {
if not game_build_ok("bin/ludicc", `examples/{name}.ludic`, `/tmp/x_e_{name}`) {
bad2(label, capture_line("grep -i error /tmp/x_gb.err | head -1")); return
}
@ -20,7 +20,7 @@ function feat_case(name: ptr, keys: ptr, exp: ptr, label: ptr) -> void {
}
# a "does it still compile" smoke test (parse -> lower -> link), no run
function qsmoke(name: ptr) -> void {
function qsmoke(name: pointer) -> void {
if game_build_ok("bin/ludicc", `examples/{name}.ludic`, `/tmp/x_q_{name}`) {
ok(`{name} compiles (@Queries desugars to S_QUERY)`)
} else { bad2(name, capture_line("grep -i error /tmp/x_gb.err | head -1")) }
@ -28,7 +28,7 @@ function qsmoke(name: ptr) -> void {
# a pure-Ludic example that drives and asserts itself: compile headless to IR,
# let clang assemble it, run, and compare stdout. No C host and no C compiled.
function net_case(name: ptr, exp: ptr) -> void {
function net_case(name: pointer, exp: pointer) -> void {
let ll = `/tmp/x_n_{name}.ll`
let log = `/tmp/x_n_{name}.out`
if not shq(`bin/ludicc --headless examples/{name}.ludic --emit-llvm -o {ll} > {log} 2>&1`) {

View file

@ -7,7 +7,7 @@
# driven through python3/node (they are not project shell scripts), invoked here.
# refresh one editor-copy of a shared grammar file if it has drifted
function sync_one(shared: ptr, dst: ptr) -> void {
function sync_one(shared: pointer, dst: pointer) -> void {
if not shq(`cmp -s {shared} {dst}`) {
run(`cp {shared} {dst}`)
print(`sync: {shared} -> {dst}`)
@ -25,7 +25,7 @@ function sync_vscode_grammar() -> void {
}
# is `flag` present anywhere in argv?
function has_flag(flag: ptr) -> bool {
function has_flag(flag: pointer) -> bool {
var i = 2
while i < arg_count() { if (arg(i) == flag) { return true }; i = i + 1 }
return false
@ -172,13 +172,13 @@ function cmd_test_tools() -> int {
return report()
}
function test_json(path: ptr) -> void {
function test_json(path: pointer) -> void {
let bn = capture_line(`basename {path}`)
if shq(`python3 -c 'import json,sys;json.load(open(sys.argv[1]))' {path} 2>/dev/null`) {
ok(`valid JSON: {bn}`)
} else { bad(`invalid JSON: {path}`) }
}
function test_xml(path: ptr) -> void {
function test_xml(path: pointer) -> void {
let bn = capture_line(`basename {path}`)
if shq(`python3 -c 'import xml.dom.minidom,sys;xml.dom.minidom.parse(sys.argv[1])' {path} 2>/dev/null`) {
ok(`valid XML: {bn}`)