feat(lang): L6 namespaces declared in Ludic - alias
`alias meth(labels) = target` in a namespace block makes Ns.meth a call to target with those labels (the target's own parameter names without a list). The engine's 41 table-driven namespaces - 438 methods: Http, Udp, Process, Json, Value, Screen, Input, Audio, World, Tiled, ... - leave emit_ns_call for runtime/native/namespaces.ludic, spliced into every program; 532 lines of compiler go and the seed shrinks by 23k lines of IR. The game's IR is byte identical. The checker checks an alias call's arguments against its target. Still built in: the inline namespaces (Math, Text, List, Vector, Color, Time, Date, ...) and the methods that pick a target by argument type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9662680bb0
commit
9259808f80
16 changed files with 49226 additions and 95736 deletions
28
LANGUAGE.md
28
LANGUAGE.md
|
|
@ -187,6 +187,34 @@ Generics are compiled by instantiation: each instance the program uses is an ord
|
|||
function, made and checked once, so it costs exactly what writing it out by hand would. A generic
|
||||
nothing instantiates is not compiled at all.
|
||||
|
||||
### Namespaces declared in Ludic (`alias`)
|
||||
|
||||
A namespace method can be a name for a function. Inside a `namespace` block,
|
||||
|
||||
```ludic
|
||||
program Trails {
|
||||
namespace Trail {
|
||||
export alias length(from, to) = trail_distance
|
||||
alias km = trail_km
|
||||
}
|
||||
function trail_distance(a: int, b: int) -> int { return b - a }
|
||||
function trail_km(metres: int) -> int { return metres / 1000 }
|
||||
entry { print(Trail.length(to: 12, from: 2) + Trail.km(metres: 5000)) }
|
||||
}
|
||||
```
|
||||
|
||||
makes `Trail.length(...)` a call to `trail_distance`: the list after the method is the labels a call
|
||||
may name its arguments by, in the target's parameter order, and without one the target's own
|
||||
parameter names are the labels (`alias show() = present` takes none). The call is the target's -
|
||||
same arguments, same checks, same code - so a namespace costs nothing over calling the function.
|
||||
|
||||
This is how the engine declares its own namespaces: `Http`, `Udp`, `Process`, `Json`, `Value`,
|
||||
`Screen`, `Input`, `Audio`, `World`, `Tiled` and the rest are `alias` blocks in
|
||||
`runtime/native/namespaces.ludic`, not branches in the compiler, and a package owns an API the same
|
||||
way in its own files. What is still built into the compiler is the namespaces that compute inline -
|
||||
`Math`, `Text`, `List`, `Vector`, `Color`, `Time`, `Date` - and the few methods that choose their
|
||||
target by an argument's type (`Audio.play` of a handle or a name).
|
||||
|
||||
## Models (entity kinds)
|
||||
|
||||
An `model` names a *kind* of entity and the fixed set of properties it
|
||||
|
|
|
|||
9
changes/declared-namespaces.md
Normal file
9
changes/declared-namespaces.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
bump: minor
|
||||
type: feature
|
||||
**Namespaces are declared in Ludic.** `alias meth(labels) = target` in a `namespace` block makes
|
||||
`Ns.meth(...)` a call to `target`, taking named arguments by those labels; with no label list the
|
||||
target's own parameter names are the labels. The engine's 41 table-driven namespaces - `Http`,
|
||||
`Udp`, `Process`, `Json`, `Value`, `Screen`, `Input`, `Audio`, `World`, `Tiled` and the rest, 438
|
||||
methods - moved out of the compiler into `runtime/native/namespaces.ludic`, and a package owns an
|
||||
API the same way. The code a program compiles to is unchanged byte for byte, and the checker now
|
||||
checks an alias call's arguments against its target.
|
||||
|
|
@ -144,6 +144,11 @@ error naming both modules.
|
|||
|
||||
## Package-declarable namespaces and engine systems (issue #62)
|
||||
|
||||
A package's namespace can also be an `alias` block (L6): `namespace Weapon { alias fire(slot) =
|
||||
wp_fire_slot }` names each method and the function it calls, with its own labels, so the public
|
||||
names need not be the implementation's. The engine's own `Http.*`, `Json.*`, `Screen.*` and the
|
||||
rest are declared exactly this way, in `runtime/native/namespaces.ludic`.
|
||||
|
||||
A package can register two things that used to be compiler-hardcoded — a `Foo.*`
|
||||
namespace and an engine-owned system — with **no compiler edit**, via two
|
||||
keyword-free annotations. This is what lets gameplay-controller libraries ship as
|
||||
|
|
|
|||
19
examples/lang/aliases.ludic
Normal file
19
examples/lang/aliases.ludic
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# aliases.ludic — L6: a namespace method that is a name for a function. `alias` gives the method,
|
||||
# the labels a call may use (in the target's parameter order) and the function it calls; without a
|
||||
# label list the target's own parameter names are the labels. This is how the engine declares
|
||||
# Http, Screen, Json and the rest (runtime/native/namespaces.ludic), and how a package owns an API.
|
||||
#
|
||||
# Running it prints: 10 5 3
|
||||
program Aliases {
|
||||
namespace Trail {
|
||||
export alias length(from, to) = trail_distance
|
||||
alias km = trail_km
|
||||
alias stops() = trail_stops
|
||||
}
|
||||
function trail_distance(a: int, b: int) -> int { return b - a }
|
||||
function trail_km(metres: int) -> int { return metres / 1000 }
|
||||
function trail_stops() -> int { return 3 }
|
||||
entry {
|
||||
print(`{Trail.length(to: 12, from: 2)} {Trail.km(metres: 5000)} {Trail.stops()}`)
|
||||
}
|
||||
}
|
||||
8
examples/rejected/alias_arity.ludic
Normal file
8
examples/rejected/alias_arity.ludic
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# L6: an alias is its target, so a call gives it the target's arguments
|
||||
program AliasArity {
|
||||
namespace Trail {
|
||||
alias length(from, to) = trail_distance
|
||||
}
|
||||
function trail_distance(a: int, b: int) -> int { return b - a }
|
||||
entry { print(Trail.length(4)) }
|
||||
}
|
||||
9
examples/rejected/alias_twice.ludic
Normal file
9
examples/rejected/alias_twice.ludic
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# L6: a namespace method is declared once
|
||||
program AliasTwice {
|
||||
namespace Trail {
|
||||
alias length(from, to) = trail_distance
|
||||
alias length(a, b) = trail_distance
|
||||
}
|
||||
function trail_distance(a: int, b: int) -> int { return b - a }
|
||||
entry { print(Trail.length(1, 2)) }
|
||||
}
|
||||
544
runtime/native/namespaces.ludic
Normal file
544
runtime/native/namespaces.ludic
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
# namespaces.ludic — the engine's namespaces that are a name for a runtime function: `Http.get`
|
||||
# is `http_get`, `Screen.fill_rectangle` is `fill_rect`. Each `alias` names the method, the labels
|
||||
# a call may name its arguments by (in the target's parameter order), and the function it calls.
|
||||
# Spliced into every program, and costs nothing until a method is called: the file that defines
|
||||
# the target is spliced by its own use. A package declares its namespace the same way (L6).
|
||||
namespace Screen {
|
||||
alias clear(color) = clear
|
||||
alias fill_rectangle(x, y, width, height, color) = fill_rect
|
||||
alias bar(x, y, width, height, value, max, color, back) = bar
|
||||
alias draw_rectangle(x, y, width, height, color) = frame_rect
|
||||
alias put_pixel(x, y, color) = put_px
|
||||
alias draw_text(x, y, text, color, scale) = text
|
||||
alias draw_number(x, y, value, color, scale) = text_int
|
||||
alias show() = present
|
||||
alias width() = screen_w
|
||||
alias height() = screen_h
|
||||
alias status(text) = status
|
||||
alias line(x1, y1, x2, y2, color) = line
|
||||
alias circle(x, y, radius, color) = circle
|
||||
alias fill_circle(x, y, radius, color) = fill_circle
|
||||
alias triangle(x1, y1, x2, y2, x3, y3, color) = triangle
|
||||
alias fill_triangle(x1, y1, x2, y2, x3, y3, color) = fill_triangle
|
||||
alias sprite(id, x, y) = draw_sprite
|
||||
alias sprite_scaled(id, x, y, scale) = draw_sprite_scaled
|
||||
alias oval(x, y, rx, ry, color) = oval
|
||||
alias camera(x, y) = camera
|
||||
alias clip(x, y, width, height) = clip
|
||||
alias clip_reset() = clip_reset
|
||||
alias blend_mode(mode) = blend_mode
|
||||
alias measure_text(text) = measure_text
|
||||
alias pixel(x, y) = get_px
|
||||
}
|
||||
namespace Sprite {
|
||||
alias sheet(path, cellw, cellh) = atlas_sheet
|
||||
alias cell(sheet, col, row) = atlas_cell
|
||||
alias cell_span(sheet, col, row, cols, rows) = atlas_cell_span
|
||||
alias strip(sheet, col, row, count, rows) = atlas_strip
|
||||
alias define(name, sheet, col, row) = atlas_define
|
||||
alias named(name) = atlas_named
|
||||
alias draw(id, x, y) = atlas_draw
|
||||
alias draw_scaled(id, x, y, scale) = atlas_draw_scaled
|
||||
alias draw_meter(x, y, value, max, per_icon, spacing, full, half, empty) = atlas_draw_meter
|
||||
alias width(id) = atlas_width
|
||||
alias height(id) = atlas_height
|
||||
}
|
||||
namespace Font {
|
||||
alias load(path) = font_load
|
||||
}
|
||||
namespace Ui {
|
||||
alias build() = ui_build
|
||||
alias open(id) = ui_open
|
||||
alias close() = ui_close
|
||||
alias tick(key) = ui_tick
|
||||
alias clicked(id) = ui_clicked
|
||||
alias set_text(id, text) = ui_set_text
|
||||
alias render() = ui_render
|
||||
}
|
||||
namespace File {
|
||||
alias open(path, mode) = file_open
|
||||
alias read(file, buffer, count) = file_read
|
||||
alias write(file, buffer, count) = file_write
|
||||
alias seek(file, offset, whence) = file_seek
|
||||
alias tell(file) = file_tell
|
||||
alias close(file) = file_close
|
||||
}
|
||||
namespace Fx {
|
||||
alias sparks(x, y, color, count) = fx_sparks
|
||||
alias number(x, y, value, color) = fx_number
|
||||
alias clear() = fx_clear
|
||||
}
|
||||
namespace Assets {
|
||||
alias font(name) = assets_font # a .ttf loaded through the queue
|
||||
alias enqueue_dir(path) = assets_enqueue_dir # every file of a directory, by base name
|
||||
alias image(path) = atlas_image
|
||||
alias load(path) = atlas_image # alias
|
||||
alias get(name) = atlas_named # alias of Sprite.named
|
||||
# #82 — incremental preload queue + progress for a loading scene.
|
||||
alias enqueue(name, path) = assets_enqueue
|
||||
alias pump(max) = assets_pump
|
||||
alias total() = assets_total
|
||||
alias loaded() = assets_loaded
|
||||
alias ready() = assets_ready
|
||||
alias progress() = assets_progress
|
||||
}
|
||||
namespace Camera {
|
||||
alias set(x, y) = camera
|
||||
alias follow(x, y, lerp) = camera_follow
|
||||
alias shake(amount) = camera_shake
|
||||
alias shake_for(amount, frames) = camera_shake_for # a timed shake the engine decays
|
||||
alias zoom(scale) = camera_zoom # #78 deterministic Q16.16 zoom
|
||||
}
|
||||
namespace Map {
|
||||
alias size(width, height) = map_size
|
||||
alias row(y, cells) = map_row
|
||||
alias tile(x, y) = tile
|
||||
alias get(x, y) = tile
|
||||
# the cell API: edit the grid in place, ask it what is where
|
||||
alias set(x, y, glyph) = map_set
|
||||
alias fill(glyph) = map_fill
|
||||
alias rect(x, y, width, height, glyph) = map_rect
|
||||
alias border(glyph) = map_border
|
||||
alias random_cell(glyph) = map_random_cell
|
||||
alias random_cell_far(glyph, from, min_tiles) = map_random_cell_far
|
||||
alias is_solid(x, y) = map_is_solid
|
||||
alias is_solid_at(x, y) = map_is_solid_at
|
||||
alias to_tile(pixel) = map_to_tile
|
||||
alias width() = map_width
|
||||
alias height() = map_height
|
||||
}
|
||||
namespace Random {
|
||||
alias range(low, high) = rng_range
|
||||
alias weighted(weights) = rng_weighted # an index in proportion to its weight
|
||||
alias chance(percent) = rng_chance
|
||||
alias seed(value) = seed
|
||||
alias value() = rng_value
|
||||
alias int(max) = rng_int
|
||||
alias sign() = rng_sign
|
||||
}
|
||||
namespace Input {
|
||||
alias key() = key
|
||||
# action maps + deterministic record/replay (#7) — spliced runtime in
|
||||
# runtime/native/input.ludic, reached as ordinary @fn_input_* calls.
|
||||
alias bind(action, key) = input_bind
|
||||
alias rebind(action, old, new) = input_rebind
|
||||
alias poll() = input_poll
|
||||
alias down() = input_down
|
||||
alias pressed() = input_pressed
|
||||
alias record() = input_record
|
||||
alias replay() = input_replay
|
||||
# Input Manager (#83): default bindings + device-agnostic actions that read the
|
||||
# multi-key device layer (the frame loop now commits it automatically).
|
||||
alias action(action, key) = input_default
|
||||
alias bind_pad(action, button) = input_bind_pad
|
||||
alias active(action) = input_active
|
||||
alias just_pressed(action) = input_just_pressed
|
||||
alias just_released(action) = input_just_released
|
||||
# device layer (#50): multi-key held state, analog axes/vectors, mouse,
|
||||
# gamepads, touch — reached as ordinary @fn_input_* calls into input.ludic.
|
||||
alias key_down(key) = input_key_down
|
||||
alias key_pressed(key) = input_key_pressed
|
||||
alias key_released(key) = input_key_released
|
||||
alias key_label(key) = input_key_label
|
||||
alias text() = input_text # what was TYPED, UTF-8
|
||||
alias press(key) = input_press
|
||||
alias release(key) = input_release
|
||||
alias axis(neg, pos) = input_axis
|
||||
alias axis_i(neg, pos) = input_axis_i # #79 directional int (-1/0/1)
|
||||
alias move_i() = input_move_i # WASD / arrows / left stick -> IVec2
|
||||
alias vector(left, right, up, down) = input_vector
|
||||
alias strength(action) = input_strength
|
||||
alias mouse_x() = input_mouse_x
|
||||
alias mouse_y() = input_mouse_y
|
||||
alias mouse_dx() = input_mouse_dx
|
||||
alias mouse_dy() = input_mouse_dy
|
||||
alias mouse_down(button) = input_mouse_down
|
||||
alias wheel() = input_wheel
|
||||
alias set_mouse(x, y, buttons, wheel) = input_set_mouse
|
||||
alias pad_connected(pad) = input_pad_connected
|
||||
alias pad_button(pad, button) = input_pad_button
|
||||
alias pad_axis(pad, axis) = input_pad_axis
|
||||
alias set_pad(pad, connected, buttons, lx, ly, rx, ry) = input_set_pad
|
||||
alias touch_count() = input_touch_count
|
||||
alias touch_x(index) = input_touch_x
|
||||
alias touch_y(index) = input_touch_y
|
||||
alias set_touch(index, x, y, active) = input_set_touch
|
||||
alias cursor_mode(mode) = input_cursor_mode # #89 cursor capture
|
||||
}
|
||||
namespace Audio {
|
||||
alias load(path) = audio_load
|
||||
alias define(name, path) = audio_define
|
||||
alias named(name) = audio_named
|
||||
alias play_sound(id) = audio_play
|
||||
# Audio.play_at(id, gain:, pitch:, pan:) — one shot with its own gain, pitch and
|
||||
# stereo position. Audio.volume / Audio.pitch stay global (they are the options
|
||||
# screen); this is the per-voice one, and it is what distance attenuation is built on.
|
||||
alias play_at(id, gain, pitch, pan) = audio_play_at
|
||||
alias stop(id) = audio_stop
|
||||
alias stop_music() = audio_stop_music
|
||||
alias stop_all() = audio_stop_all
|
||||
alias volume(v) = audio_volume
|
||||
alias pitch(v) = audio_pitch
|
||||
alias is_playing(id) = audio_is_playing
|
||||
}
|
||||
namespace Http {
|
||||
alias get(url) = http_get
|
||||
alias post(url, body) = http_post
|
||||
alias request(method, url, body) = http_request
|
||||
alias open(method, url) = http_open
|
||||
alias set(handle, name, value) = http_set_header
|
||||
alias body(handle, body) = http_body
|
||||
alias body_bytes(handle, bytes, len) = http_body_n
|
||||
alias send(handle) = http_send_req
|
||||
alias poll(handle) = http_poll
|
||||
alias status(handle) = http_status_of
|
||||
alias ok(handle) = http_ok
|
||||
alias text(handle) = http_text
|
||||
alias body_len(handle) = http_body_len
|
||||
alias header(handle, name) = http_header_of
|
||||
alias free(handle) = http_close
|
||||
alias parse(bytes, len) = http_parse
|
||||
alias save_to(handle, path) = http_save_to
|
||||
alias received(handle) = http_received
|
||||
alias expected(handle) = http_expected
|
||||
}
|
||||
namespace Udp {
|
||||
alias open(port) = udp_open
|
||||
alias port(socket) = udp_port
|
||||
alias send(socket, ip, port, bytes, len) = udp_send
|
||||
alias recv(socket, bytes, cap) = udp_recv
|
||||
alias from_ip(socket) = udp_from_ip
|
||||
alias from_port(socket) = udp_from_port
|
||||
alias close(socket) = udp_close
|
||||
alias resolve(name) = udp_resolve
|
||||
alias local_ip() = udp_local_ip
|
||||
alias ip(text) = udp_ip
|
||||
alias ip_text(ip) = udp_ip_text
|
||||
}
|
||||
namespace Process {
|
||||
alias spawn(path, args) = process_spawn
|
||||
alias poll(handle) = process_poll
|
||||
alias kill(handle) = process_kill
|
||||
alias free(handle) = process_free
|
||||
}
|
||||
namespace World {
|
||||
alias get() = world_get
|
||||
alias set() = world_set
|
||||
alias has() = world_has
|
||||
alias count() = world_count
|
||||
alias size() = world_size
|
||||
alias spawn() = world_spawn
|
||||
alias despawn() = world_despawn # #84 — despawn an entity by id (runs @OnDespawn + frees)
|
||||
alias save() = world_save
|
||||
alias load() = world_load
|
||||
alias prop_id() = world_prop_id
|
||||
alias field_id() = world_field_id
|
||||
alias model_id() = world_model_id
|
||||
alias kind() = world_kind
|
||||
alias register_prop() = world_register_prop
|
||||
alias attach() = world_attach_dyn
|
||||
alias detach() = world_detach_dyn
|
||||
alias query_next() = world_query_next
|
||||
}
|
||||
namespace Network {
|
||||
alias send() = net_send
|
||||
alias poll() = net_poll
|
||||
alias serialize() = serialize
|
||||
alias apply() = apply
|
||||
alias owner() = owner
|
||||
alias set_owner() = set_owner
|
||||
alias is_server() = is_server
|
||||
alias is_owner() = is_owner
|
||||
alias local_id() = local_id
|
||||
}
|
||||
namespace System {
|
||||
# System.* is the low-level file/process surface only. The environment slice
|
||||
# (arg/arg_count/env/exit) and the standard streams (stdout/stderr) were
|
||||
# duplicated by the canonical Os.* namespace and have been retired — see
|
||||
# Os.arg/arg_count/env/exit and Os.stdout_write/stderr_write (issue #40).
|
||||
alias run() = run
|
||||
alias read_char() = read_char
|
||||
alias file_open() = file_open
|
||||
alias file_read() = file_read
|
||||
alias file_write() = file_write
|
||||
alias file_seek() = file_seek
|
||||
alias file_tell() = file_tell
|
||||
alias file_close() = file_close
|
||||
}
|
||||
namespace Save {
|
||||
alias write() = save
|
||||
alias read() = load
|
||||
}
|
||||
namespace Regex {
|
||||
alias compile(pattern) = regex_compile
|
||||
alias valid(pattern) = regex_valid
|
||||
alias matches(text, pattern) = regex_matches
|
||||
alias test(text, re) = regex_test
|
||||
alias find(text, pattern) = regex_find
|
||||
alias exec(text, re) = regex_exec
|
||||
alias next(text, re, from) = regex_next
|
||||
alias replace(text, pattern, replacement) = regex_replace
|
||||
alias group(match, n) = regex_group
|
||||
alias group_count(match) = regex_group_count
|
||||
alias start(match, n) = regex_start
|
||||
alias end(match, n) = regex_end
|
||||
alias ok(match) = regex_ok
|
||||
}
|
||||
namespace BigInt {
|
||||
alias from(value) = bigint_from
|
||||
alias parse(text) = bigint_from_str
|
||||
alias add(a, b) = bigint_add
|
||||
alias sub(a, b) = bigint_sub
|
||||
alias mul(a, b) = bigint_mul
|
||||
alias neg(a) = bigint_neg
|
||||
alias pow(a, exp) = bigint_pow
|
||||
alias div(a, d) = bigint_div_int
|
||||
alias mod(a, d) = bigint_mod_int
|
||||
alias cmp(a, b) = bigint_cmp
|
||||
alias eq(a, b) = bigint_eq
|
||||
alias is_zero(a) = bigint_is_zero
|
||||
alias to_int(a) = bigint_to_int
|
||||
alias str(a) = bigint_str
|
||||
}
|
||||
namespace Decimal {
|
||||
alias from(value) = decimal_from
|
||||
alias parse(text) = decimal_from_str
|
||||
alias add(a, b) = decimal_add
|
||||
alias sub(a, b) = decimal_sub
|
||||
alias mul(a, b) = decimal_mul
|
||||
alias neg(a) = decimal_neg
|
||||
alias cmp(a, b) = decimal_cmp
|
||||
alias eq(a, b) = decimal_eq
|
||||
alias scale(d) = decimal_scale
|
||||
alias rescale(d, places) = decimal_rescale
|
||||
alias str(d) = decimal_str
|
||||
}
|
||||
namespace Dict {
|
||||
alias new() = dict_new
|
||||
alias set(d, key, value) = dict_set
|
||||
alias get(d, key) = dict_get
|
||||
alias get_or(d, key, fallback) = dict_get_or
|
||||
alias has(d, key) = dict_has
|
||||
alias remove(d, key) = dict_remove
|
||||
alias size(d) = dict_size
|
||||
alias clear(d) = dict_clear
|
||||
alias keys(d) = dict_keys
|
||||
}
|
||||
namespace Set {
|
||||
alias new() = set_new
|
||||
alias add(s, key) = set_add
|
||||
alias has(s, key) = set_has
|
||||
alias remove(s, key) = set_remove
|
||||
alias size(s) = set_size
|
||||
alias clear(s) = set_clear
|
||||
alias members(s) = set_members
|
||||
}
|
||||
namespace Job {
|
||||
alias defer() = job_defer
|
||||
alias run(kind, arg) = job_run
|
||||
alias fulfill(handle, value) = job_fulfill
|
||||
alias fail(handle, error) = job_fail
|
||||
alias cancel(handle) = job_cancel
|
||||
alias pump(budget) = job_pump
|
||||
alias done(handle) = job_done
|
||||
alias ok(handle) = job_ok
|
||||
alias failed(handle) = job_failed
|
||||
alias cancelled(handle) = job_cancelled
|
||||
alias result(handle) = job_result
|
||||
alias error(handle) = job_error
|
||||
alias pending() = job_pending
|
||||
alias free(handle) = job_free
|
||||
alias is_worker() = job_is_worker
|
||||
}
|
||||
namespace Promise {
|
||||
alias all(handles) = prom_all
|
||||
alias race(handles) = prom_race
|
||||
alias count_done(handles) = prom_count_done
|
||||
alias all_done(handles) = prom_all_done
|
||||
}
|
||||
namespace Sync {
|
||||
alias mutex() = sync_mutex
|
||||
alias lock(mutex) = sync_lock
|
||||
alias unlock(mutex) = sync_unlock
|
||||
alias try_lock(mutex) = sync_try_lock
|
||||
alias atomic() = sync_atomic
|
||||
alias get(atomic) = sync_get
|
||||
alias set(atomic, value) = sync_set
|
||||
alias add(atomic, delta) = sync_add
|
||||
alias cas(atomic, expect, next) = sync_cas
|
||||
alias channel() = sync_channel
|
||||
alias send(channel, value) = sync_send
|
||||
alias recv(channel) = sync_recv
|
||||
alias can_recv(channel) = sync_can_recv
|
||||
alias len(channel) = sync_len
|
||||
alias cpu_count() = sync_cpu_count
|
||||
}
|
||||
namespace Huge {
|
||||
alias from(value) = huge_from
|
||||
alias add(a, b) = huge_add
|
||||
alias sub(a, b) = huge_sub
|
||||
alias mul(a, b) = huge_mul
|
||||
alias neg(a) = huge_neg
|
||||
alias cmp(a, b) = huge_cmp
|
||||
alias sign(a) = huge_sign
|
||||
alias mantissa(a) = huge_mantissa
|
||||
alias exp(a) = huge_exp
|
||||
alias str(a) = huge_str
|
||||
}
|
||||
namespace Angle {
|
||||
alias from_degrees(d) = angle_from_degrees
|
||||
alias to_degrees(a) = angle_to_degrees
|
||||
alias wrap(a) = angle_wrap
|
||||
alias sin(a) = angle_sin
|
||||
alias cos(a) = angle_cos
|
||||
alias add(a, b) = angle_add
|
||||
alias diff(a, b) = angle_diff
|
||||
alias diff_degrees(a, b) = angle_diff_degrees # signed, -180..180, ints
|
||||
alias lerp(a, b, t) = angle_lerp
|
||||
}
|
||||
namespace Percent {
|
||||
alias clamp(v) = percent_clamp
|
||||
alias of(num, den) = percent_of
|
||||
alias lerp(a, b, t) = percent_lerp
|
||||
alias apply(value, p) = percent_apply
|
||||
}
|
||||
namespace Grid {
|
||||
alias line(x0, y0, x1, y1) = grid_line
|
||||
alias blocked(x, y, wall) = grid_blocked
|
||||
alias line_of_sight(x0, y0, x1, y1, wall) = grid_line_of_sight
|
||||
alias flood(x, y, wall) = grid_flood
|
||||
alias a_star(x0, y0, x1, y1, wall) = path_a_star
|
||||
}
|
||||
namespace Light {
|
||||
alias ambient(color) = light_ambient
|
||||
alias point(x, y, radius, color, energy) = light_point
|
||||
alias spot(x, y, radius, color, energy, direction, spread) = light_spot
|
||||
alias occlude(x, y, width, height) = light_occlude
|
||||
alias clear_occluders() = light_clear_occluders
|
||||
alias falloff(exponent) = light_set_falloff
|
||||
alias soft(radius) = light_set_soft
|
||||
alias gel(color) = light_set_gel
|
||||
alias clear_gel() = light_clear_gel
|
||||
alias height(height) = light_set_height
|
||||
alias normal(x, y, width, height, nx, ny) = light_normal_rect
|
||||
alias clear_normals() = light_clear_normals
|
||||
alias time_of_day(t) = light_time_of_day
|
||||
}
|
||||
namespace Motion {
|
||||
alias to(entity, from, to, dur, ease) = motion_to
|
||||
}
|
||||
namespace Tween {
|
||||
alias to(from, to, dur, ease) = tween_to
|
||||
alias chain(handle, to, dur, ease) = tween_chain
|
||||
alias delay(handle, ticks) = tween_delay
|
||||
alias value(handle) = tween_value
|
||||
alias stop(handle) = tween_stop
|
||||
alias parallel(a, b) = tween_parallel
|
||||
}
|
||||
namespace Query {
|
||||
alias count(prop) = query_count
|
||||
alias first(prop) = query_first
|
||||
alias nearest(prop, pos, x_field, y_field, x, y) = query_nearest
|
||||
alias within(prop, pos, x, y, radius, x_field, y_field) = query_within
|
||||
}
|
||||
namespace Reflect {
|
||||
alias prop(name) = world_prop_id
|
||||
alias field(prop, name) = world_field_id
|
||||
alias prop_count() = world_prop_count
|
||||
alias prop_name(index) = world_prop_name
|
||||
alias field_count(prop) = world_field_count
|
||||
alias field_name(prop, index) = world_field_name
|
||||
alias field_type(prop, index) = world_field_type
|
||||
alias get(entity, prop, field) = world_get
|
||||
alias set(entity, prop, field, value) = world_set
|
||||
alias has(entity, prop) = world_has
|
||||
alias kind(entity) = world_kind
|
||||
alias model(name) = world_model_id
|
||||
# #44 — serialize an entity to a value tree and apply one back (reflect_io.ludic)
|
||||
alias serialize(entity) = reflect_serialize
|
||||
alias apply(entity, value) = reflect_apply
|
||||
}
|
||||
namespace Value {
|
||||
alias null() = value_null
|
||||
alias int(n) = value_int
|
||||
alias fixed(f) = value_fixed
|
||||
alias bool(b) = value_bool
|
||||
alias str(s) = value_str
|
||||
alias list() = value_list
|
||||
alias object() = value_object
|
||||
alias add(list, item) = value_add
|
||||
alias put(obj, key, item) = value_put
|
||||
alias get(obj, key) = value_get
|
||||
alias has(obj, key) = value_has
|
||||
alias at(list, index) = value_at
|
||||
alias key_at(obj, index) = value_key_at
|
||||
alias count(value) = value_count
|
||||
alias kind(value) = value_kind
|
||||
alias as_int(value) = value_as_int
|
||||
alias as_str(value) = value_as_str
|
||||
}
|
||||
namespace Json {
|
||||
alias encode(value) = json_encode
|
||||
alias parse(text) = json_parse
|
||||
}
|
||||
namespace Xml {
|
||||
alias parse(text) = xml_parse
|
||||
alias tag(node) = xml_tag
|
||||
alias text(node) = xml_text
|
||||
alias attr(node, key) = xml_attr
|
||||
alias attr_int(node, key, dflt) = xml_attr_int
|
||||
alias has(node, key) = xml_has
|
||||
alias attr_count(node) = xml_attr_count
|
||||
alias child_count(node) = xml_child_count
|
||||
alias child(node, index) = xml_child
|
||||
alias find(node, tag) = xml_find
|
||||
alias count(node, tag) = xml_count
|
||||
}
|
||||
namespace Base64 {
|
||||
alias decode(src) = base64_decode
|
||||
alias encode(src) = base64_encode
|
||||
}
|
||||
namespace Tiled {
|
||||
alias read(path) = tiled_read
|
||||
alias read_tsx(path) = tiled_read_tsx
|
||||
alias load(path) = tiled_load
|
||||
alias gid(map, layer, x, y) = tmap_gid
|
||||
alias resolve(map, gid) = tmap_resolve
|
||||
alias width(map) = tmap_width
|
||||
alias height(map) = tmap_height
|
||||
alias layer_count(map) = tmap_layer_count
|
||||
alias layer_name(map, index) = tmap_layer_name
|
||||
alias draw(map, camx, camy) = tmap_draw
|
||||
alias draw_anim(map, camx, camy, frame) = tmap_draw_anim
|
||||
alias frame_gid(map, gid, frame) = tmap_frame_gid
|
||||
alias animated(map, gid) = tmap_is_animated
|
||||
alias project(map, layer) = tmap_project
|
||||
alias collide(map, layer) = tmap_collide
|
||||
alias collision_kind(map, gid) = tmap_collision_kind
|
||||
alias tree(map) = tmap_tree
|
||||
alias tile_prop(map, gid, name) = tmap_tile_prop
|
||||
alias tile_shapes(map, gid) = tmap_tile_has_shapes
|
||||
# P4 (#72): objects / properties / custom types / templates / opt-in spawn
|
||||
alias object_count(map, layer) = tmap_object_count
|
||||
alias object(map, layer, index) = tmap_object
|
||||
alias object_shape(object) = tiled_object_shape
|
||||
alias prop(container, name) = tiled_prop_str
|
||||
alias prop_int(container, name) = tiled_prop_int
|
||||
alias prop_type(container, name) = tiled_prop_type
|
||||
alias load_types(path) = tiled_load_types
|
||||
alias template(path) = tiled_read_template
|
||||
alias spawn(map, object, model) = tiled_spawn_object
|
||||
alias spawn_layer(map, layer, model) = tiled_spawn_layer
|
||||
# P5 (#73): orientation coords + image/group-layer accessors
|
||||
alias cell_x(map, x, y) = tmap_cell_sx
|
||||
alias cell_y(map, x, y) = tmap_cell_sy
|
||||
alias layer_kind(map, layer) = tmap_layer_kind
|
||||
alias layer_opacity(map, layer) = tmap_layer_opacity
|
||||
alias layer_tint(map, layer) = tmap_layer_tint
|
||||
alias layer_offsetx(map, layer) = tmap_layer_offsetx
|
||||
alias layer_offsety(map, layer) = tmap_layer_offsety
|
||||
# P6 (#74): .world stitching
|
||||
alias world(path) = tiled_read_world
|
||||
alias world_count(world) = tiled_world_count
|
||||
alias world_map(world, index) = tiled_world_map
|
||||
}
|
||||
|
|
@ -300,80 +300,19 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
}
|
||||
perr(`unknown builtin Pool.{meth}`)
|
||||
}
|
||||
# L6: a method declared by `alias` in a namespace block - the engine's own in
|
||||
# runtime/native/namespaces.ludic, a package's in its files - is a call to its target
|
||||
let al = ns_alias_find(ns, meth)
|
||||
if al >= 0 { return emit_alias_call(al, e) }
|
||||
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") }
|
||||
if (meth == "bar") { bare = "bar"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height"); push(labels, "value"); push(labels, "max"); push(labels, "color"); push(labels, "back") }
|
||||
if (meth == "draw_rectangle") { bare = "frame_rect"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height"); push(labels, "color") }
|
||||
if (meth == "put_pixel") { bare = "put_px"; push(labels, "x"); push(labels, "y"); push(labels, "color") }
|
||||
if (meth == "draw_text") { bare = "text"; push(labels, "x"); push(labels, "y"); push(labels, "text"); push(labels, "color"); push(labels, "scale") }
|
||||
if (meth == "draw_number") { bare = "text_int"; push(labels, "x"); push(labels, "y"); push(labels, "value"); push(labels, "color"); push(labels, "scale") }
|
||||
if (meth == "show") { bare = "present" }
|
||||
if (meth == "width") { bare = "screen_w" }
|
||||
if (meth == "height") { bare = "screen_h" }
|
||||
if (meth == "status") { bare = "status"; push(labels, "text") }
|
||||
if (meth == "line") { bare = "line"; push(labels, "x1"); push(labels, "y1"); push(labels, "x2"); push(labels, "y2"); push(labels, "color") }
|
||||
if (meth == "circle") { bare = "circle"; push(labels, "x"); push(labels, "y"); push(labels, "radius"); push(labels, "color") }
|
||||
if (meth == "fill_circle") { bare = "fill_circle"; push(labels, "x"); push(labels, "y"); push(labels, "radius"); push(labels, "color") }
|
||||
if (meth == "triangle") { bare = "triangle"; push(labels, "x1"); push(labels, "y1"); push(labels, "x2"); push(labels, "y2"); push(labels, "x3"); push(labels, "y3"); push(labels, "color") }
|
||||
if (meth == "fill_triangle") { bare = "fill_triangle"; push(labels, "x1"); push(labels, "y1"); push(labels, "x2"); push(labels, "y2"); push(labels, "x3"); push(labels, "y3"); push(labels, "color") }
|
||||
if (meth == "sprite") { bare = "draw_sprite"; push(labels, "id"); push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "sprite_scaled") { bare = "draw_sprite_scaled"; push(labels, "id"); push(labels, "x"); push(labels, "y"); push(labels, "scale") }
|
||||
if (meth == "oval") { bare = "oval"; push(labels, "x"); push(labels, "y"); push(labels, "rx"); push(labels, "ry"); push(labels, "color") }
|
||||
if (meth == "camera") { bare = "camera"; push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "clip") { bare = "clip"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height") }
|
||||
if (meth == "clip_reset") { bare = "clip_reset" }
|
||||
if (meth == "blend_mode") { bare = "blend_mode"; push(labels, "mode") }
|
||||
if (meth == "measure_text") { bare = "measure_text"; push(labels, "text") }
|
||||
if (meth == "pixel") { bare = "get_px"; push(labels, "x"); push(labels, "y") }
|
||||
}
|
||||
# Sprite.* / Assets.* — the namespaced spritesheet / atlas API (#81), a runtime
|
||||
# in atlas.ludic over the variable-size image loader. A cell (or multi-cell span)
|
||||
# is a sub-rect of a loaded sheet; draws go through rt_put_px so camera/zoom/clip
|
||||
# apply. Distinct from the `Sprite` engine component (#85) — same name, different
|
||||
# namespace (a namespaced builtin, never an entity).
|
||||
if (ns == "Sprite") {
|
||||
if (meth == "sheet") { bare = "atlas_sheet"; push(labels, "path"); push(labels, "cellw"); push(labels, "cellh") }
|
||||
if (meth == "cell") { bare = "atlas_cell"; push(labels, "sheet"); push(labels, "col"); push(labels, "row") }
|
||||
if (meth == "cell_span") { bare = "atlas_cell_span"; push(labels, "sheet"); push(labels, "col"); push(labels, "row"); push(labels, "cols"); push(labels, "rows") }
|
||||
if (meth == "strip") { bare = "atlas_strip"; push(labels, "sheet"); push(labels, "col"); push(labels, "row"); push(labels, "count"); push(labels, "rows") }
|
||||
if (meth == "define") { bare = "atlas_define"; push(labels, "name"); push(labels, "sheet"); push(labels, "col"); push(labels, "row") }
|
||||
if (meth == "named") { bare = "atlas_named"; push(labels, "name") }
|
||||
if (meth == "draw") { bare = "atlas_draw"; push(labels, "id"); push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "draw_scaled") { bare = "atlas_draw_scaled"; push(labels, "id"); push(labels, "x"); push(labels, "y"); push(labels, "scale") }
|
||||
if (meth == "draw_meter") { bare = "atlas_draw_meter"; push(labels, "x"); push(labels, "y"); push(labels, "value"); push(labels, "max"); push(labels, "per_icon"); push(labels, "spacing"); push(labels, "full"); push(labels, "half"); push(labels, "empty") }
|
||||
if (meth == "width") { bare = "atlas_width"; push(labels, "id") }
|
||||
if (meth == "height") { bare = "atlas_height"; push(labels, "id") }
|
||||
}
|
||||
if (ns == "Font") {
|
||||
if (meth == "load") { bare = "font_load"; push(labels, "path") }
|
||||
}
|
||||
# Ui.* — the retained UI (ui blocks): navigation is engine-ticked (esys_ui) and
|
||||
# activations arrive as UiClicked events; these are the remaining verbs.
|
||||
if (ns == "Ui") {
|
||||
if (meth == "build") { bare = "ui_build" }
|
||||
if (meth == "open") { bare = "ui_open"; push(labels, "id") }
|
||||
if (meth == "close") { bare = "ui_close" }
|
||||
if (meth == "tick") { bare = "ui_tick"; push(labels, "key") }
|
||||
if (meth == "clicked") { bare = "ui_clicked"; push(labels, "id") }
|
||||
if (meth == "set_text") { bare = "ui_set_text"; push(labels, "id"); push(labels, "text") }
|
||||
if (meth == "render") { bare = "ui_render" }
|
||||
}
|
||||
if (ns == "File") {
|
||||
if (meth == "open") { bare = "file_open"; push(labels, "path"); push(labels, "mode") }
|
||||
if (meth == "read") { bare = "file_read"; push(labels, "file"); push(labels, "buffer"); push(labels, "count") }
|
||||
if (meth == "write") { bare = "file_write"; push(labels, "file"); push(labels, "buffer"); push(labels, "count") }
|
||||
if (meth == "seek") { bare = "file_seek"; push(labels, "file"); push(labels, "offset"); push(labels, "whence") }
|
||||
if (meth == "tell") { bare = "file_tell"; push(labels, "file") }
|
||||
if (meth == "close") { bare = "file_close"; push(labels, "file") }
|
||||
}
|
||||
if (ns == "Fx") { # engine-owned sparks and floating numbers (fx.ludic)
|
||||
if (meth == "sparks") { bare = "fx_sparks"; push(labels, "x"); push(labels, "y"); push(labels, "color"); push(labels, "count") }
|
||||
if (meth == "number") { bare = "fx_number"; push(labels, "x"); push(labels, "y"); push(labels, "value"); push(labels, "color") }
|
||||
if (meth == "clear") { bare = "fx_clear" }
|
||||
}
|
||||
if (ns == "Prefab") { # spawn a prefab by name at runtime (see emit_prefab_fns)
|
||||
if (meth == "spawn_at") { # Prefab.spawn_at(name:, at: IVec2) — spawned, then placed
|
||||
if not has_prefabs() { perr("Prefab.spawn_at: the program declares no prefab") }
|
||||
|
|
@ -404,243 +343,29 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
}
|
||||
perr(`unknown builtin Prefab.{meth}`)
|
||||
}
|
||||
if (ns == "Assets") {
|
||||
if (meth == "font") { bare = "assets_font"; push(labels, "name") } # a .ttf loaded through the queue
|
||||
if (meth == "enqueue_dir") { bare = "assets_enqueue_dir"; push(labels, "path") } # every file of a directory, by base name
|
||||
if (meth == "image") { bare = "atlas_image"; push(labels, "path") }
|
||||
if (meth == "load") { bare = "atlas_image"; push(labels, "path") } # alias
|
||||
if (meth == "get") { bare = "atlas_named"; push(labels, "name") } # alias of Sprite.named
|
||||
# #82 — incremental preload queue + progress for a loading scene.
|
||||
if (meth == "enqueue") { bare = "assets_enqueue"; push(labels, "name"); push(labels, "path") }
|
||||
if (meth == "pump") { bare = "assets_pump"; push(labels, "max") }
|
||||
if (meth == "total") { bare = "assets_total" }
|
||||
if (meth == "loaded") { bare = "assets_loaded" }
|
||||
if (meth == "ready") { bare = "assets_ready" }
|
||||
if (meth == "progress") { bare = "assets_progress" }
|
||||
}
|
||||
# Camera.* — the world-space camera: a draw offset threaded through the render
|
||||
# path (runtime/native/core.ludic). set/follow move it; shake jitters it from
|
||||
# the seeded RNG, so a replay shakes identically.
|
||||
if (ns == "Camera") {
|
||||
if (meth == "set") { bare = "camera"; push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "follow") { bare = "camera_follow"; push(labels, "x"); push(labels, "y"); push(labels, "lerp") }
|
||||
if (meth == "shake") { bare = "camera_shake"; push(labels, "amount") }
|
||||
if (meth == "shake_for") { bare = "camera_shake_for"; push(labels, "amount"); push(labels, "frames") } # a timed shake the engine decays
|
||||
if (meth == "zoom") { bare = "camera_zoom"; push(labels, "scale") } # #78 deterministic Q16.16 zoom
|
||||
}
|
||||
if (ns == "Map") {
|
||||
if (meth == "size") { bare = "map_size"; push(labels, "width"); push(labels, "height") }
|
||||
if (meth == "row") { bare = "map_row"; push(labels, "y"); push(labels, "cells") }
|
||||
if (meth == "tile") { bare = "tile"; push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "get") { bare = "tile"; push(labels, "x"); push(labels, "y") }
|
||||
# the cell API: edit the grid in place, ask it what is where
|
||||
if (meth == "set") { bare = "map_set"; push(labels, "x"); push(labels, "y"); push(labels, "glyph") }
|
||||
if (meth == "fill") { bare = "map_fill"; push(labels, "glyph") }
|
||||
if (meth == "rect") { bare = "map_rect"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height"); push(labels, "glyph") }
|
||||
if (meth == "border") { bare = "map_border"; push(labels, "glyph") }
|
||||
if (meth == "random_cell") { bare = "map_random_cell"; push(labels, "glyph") }
|
||||
if (meth == "random_cell_far") { bare = "map_random_cell_far"; push(labels, "glyph"); push(labels, "from"); push(labels, "min_tiles") }
|
||||
if (meth == "is_solid") { bare = "map_is_solid"; push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "is_solid_at") { bare = "map_is_solid_at"; push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "to_tile") { bare = "map_to_tile"; push(labels, "pixel") }
|
||||
if (meth == "width") { bare = "map_width" }
|
||||
if (meth == "height") { bare = "map_height" }
|
||||
}
|
||||
if (ns == "Random") {
|
||||
if (meth == "range") { bare = "rng_range"; push(labels, "low"); push(labels, "high") }
|
||||
if (meth == "weighted") { bare = "rng_weighted"; push(labels, "weights") } # an index in proportion to its weight
|
||||
if (meth == "chance") { bare = "rng_chance"; push(labels, "percent") }
|
||||
if (meth == "seed") { bare = "seed"; push(labels, "value") }
|
||||
if (meth == "value") { bare = "rng_value" }
|
||||
if (meth == "int") { bare = "rng_int"; push(labels, "max") }
|
||||
if (meth == "sign") { bare = "rng_sign" }
|
||||
}
|
||||
if (ns == "Input") {
|
||||
if (meth == "key") { bare = "key" }
|
||||
# action maps + deterministic record/replay (#7) — spliced runtime in
|
||||
# runtime/native/input.ludic, reached as ordinary @fn_input_* calls.
|
||||
if (meth == "bind") { bare = "input_bind"; push(labels, "action"); push(labels, "key") }
|
||||
if (meth == "rebind") { bare = "input_rebind"; push(labels, "action"); push(labels, "old"); push(labels, "new") }
|
||||
if (meth == "poll") { bare = "input_poll" }
|
||||
if (meth == "down") { bare = "input_down" }
|
||||
if (meth == "pressed") { bare = "input_pressed" }
|
||||
if (meth == "record") { bare = "input_record" }
|
||||
if (meth == "replay") { bare = "input_replay" }
|
||||
# Input Manager (#83): default bindings + device-agnostic actions that read the
|
||||
# multi-key device layer (the frame loop now commits it automatically).
|
||||
if (meth == "action") { bare = "input_default"; push(labels, "action"); push(labels, "key") }
|
||||
if (meth == "bind_pad") { bare = "input_bind_pad"; push(labels, "action"); push(labels, "button") }
|
||||
if (meth == "active") { bare = "input_active"; push(labels, "action") }
|
||||
if (meth == "just_pressed") { bare = "input_just_pressed"; push(labels, "action") }
|
||||
if (meth == "just_released") { bare = "input_just_released"; push(labels, "action") }
|
||||
# device layer (#50): multi-key held state, analog axes/vectors, mouse,
|
||||
# gamepads, touch — reached as ordinary @fn_input_* calls into input.ludic.
|
||||
if (meth == "key_down") { bare = "input_key_down"; push(labels, "key") }
|
||||
if (meth == "key_pressed") { bare = "input_key_pressed"; push(labels, "key") }
|
||||
if (meth == "key_released") { bare = "input_key_released"; push(labels, "key") }
|
||||
if (meth == "key_label") { bare = "input_key_label"; push(labels, "key") }
|
||||
if (meth == "text") { bare = "input_text" } # what was TYPED, UTF-8
|
||||
if (meth == "press") { bare = "input_press"; push(labels, "key") }
|
||||
if (meth == "release") { bare = "input_release"; push(labels, "key") }
|
||||
if (meth == "axis") { bare = "input_axis"; push(labels, "neg"); push(labels, "pos") }
|
||||
if (meth == "axis_i") { bare = "input_axis_i"; push(labels, "neg"); push(labels, "pos") } # #79 directional int (-1/0/1)
|
||||
if (meth == "move_i") { bare = "input_move_i" } # WASD / arrows / left stick -> IVec2
|
||||
if (meth == "vector") { bare = "input_vector"; push(labels, "left"); push(labels, "right"); push(labels, "up"); push(labels, "down") }
|
||||
if (meth == "strength") { bare = "input_strength"; push(labels, "action") }
|
||||
if (meth == "mouse_x") { bare = "input_mouse_x" }
|
||||
if (meth == "mouse_y") { bare = "input_mouse_y" }
|
||||
if (meth == "mouse_dx") { bare = "input_mouse_dx" }
|
||||
if (meth == "mouse_dy") { bare = "input_mouse_dy" }
|
||||
if (meth == "mouse_down") { bare = "input_mouse_down"; push(labels, "button") }
|
||||
if (meth == "wheel") { bare = "input_wheel" }
|
||||
if (meth == "set_mouse") { bare = "input_set_mouse"; push(labels, "x"); push(labels, "y"); push(labels, "buttons"); push(labels, "wheel") }
|
||||
if (meth == "pad_connected") { bare = "input_pad_connected"; push(labels, "pad") }
|
||||
if (meth == "pad_button") { bare = "input_pad_button"; push(labels, "pad"); push(labels, "button") }
|
||||
if (meth == "pad_axis") { bare = "input_pad_axis"; push(labels, "pad"); push(labels, "axis") }
|
||||
if (meth == "set_pad") { bare = "input_set_pad"; push(labels, "pad"); push(labels, "connected"); push(labels, "buttons"); push(labels, "lx"); push(labels, "ly"); push(labels, "rx"); push(labels, "ry") }
|
||||
if (meth == "touch_count") { bare = "input_touch_count" }
|
||||
if (meth == "touch_x") { bare = "input_touch_x"; push(labels, "index") }
|
||||
if (meth == "touch_y") { bare = "input_touch_y"; push(labels, "index") }
|
||||
if (meth == "set_touch") { bare = "input_set_touch"; push(labels, "index"); push(labels, "x"); push(labels, "y"); push(labels, "active") }
|
||||
if (meth == "cursor_mode") { bare = "input_cursor_mode"; push(labels, "mode") } # #89 cursor capture
|
||||
}
|
||||
# Audio.* (#22) — sfx/music playback over the platform audio backend
|
||||
# (runtime/native/audio.ludic + audio.ll). Playback is out-of-band; the
|
||||
# triggers are ordinary frame-driven calls, so a replay fires the same sounds.
|
||||
if (ns == "Audio") {
|
||||
if (meth == "load") { bare = "audio_load"; push(labels, "path") }
|
||||
if (meth == "define") { bare = "audio_define"; push(labels, "name"); push(labels, "path") }
|
||||
if (meth == "named") { bare = "audio_named"; push(labels, "name") }
|
||||
# Audio.play / play_music take a handle, or a name from the sound bank (Audio.define)
|
||||
if (meth == "play") { if first_arg_is_text(e) { bare = "audio_play_named"; push(labels, "name") } else { bare = "audio_play"; push(labels, "id") } }
|
||||
if (meth == "play_sound") { bare = "audio_play"; push(labels, "id") }
|
||||
# Audio.play_at(id, gain:, pitch:, pan:) — one shot with its own gain, pitch and
|
||||
# stereo position. Audio.volume / Audio.pitch stay global (they are the options
|
||||
# screen); this is the per-voice one, and it is what distance attenuation is built on.
|
||||
if (meth == "play_at") { bare = "audio_play_at"; push(labels, "id"); push(labels, "gain"); push(labels, "pitch"); push(labels, "pan") }
|
||||
if (meth == "play_music") { if first_arg_is_text(e) { bare = "audio_play_music_named"; push(labels, "name") } else { bare = "audio_play_music"; push(labels, "id") } }
|
||||
if (meth == "stop") { bare = "audio_stop"; push(labels, "id") }
|
||||
if (meth == "stop_music") { bare = "audio_stop_music" }
|
||||
if (meth == "stop_all") { bare = "audio_stop_all" }
|
||||
if (meth == "volume") { bare = "audio_volume"; push(labels, "v") }
|
||||
if (meth == "pitch") { bare = "audio_pitch"; push(labels, "v") }
|
||||
if (meth == "is_playing") { bare = "audio_is_playing"; push(labels, "id") }
|
||||
}
|
||||
# Http.* (#6) — a poll-based HTTP/HTTPS client (runtime/native/http.ludic +
|
||||
# http.ll). Out-of-band, never part of the deterministic sim. Pairs with Json.*
|
||||
# (#44) for (de)serialization: Json.parse(Http.text(h)).
|
||||
if (ns == "Http") {
|
||||
if (meth == "get") { bare = "http_get"; push(labels, "url") }
|
||||
if (meth == "post") { bare = "http_post"; push(labels, "url"); push(labels, "body") }
|
||||
if (meth == "request") { bare = "http_request"; push(labels, "method"); push(labels, "url"); push(labels, "body") }
|
||||
if (meth == "open") { bare = "http_open"; push(labels, "method"); push(labels, "url") }
|
||||
if (meth == "set") { bare = "http_set_header"; push(labels, "handle"); push(labels, "name"); push(labels, "value") }
|
||||
if (meth == "body") { bare = "http_body"; push(labels, "handle"); push(labels, "body") }
|
||||
if (meth == "body_bytes") { bare = "http_body_n"; push(labels, "handle"); push(labels, "bytes"); push(labels, "len") }
|
||||
if (meth == "send") { bare = "http_send_req"; push(labels, "handle") }
|
||||
if (meth == "poll") { bare = "http_poll"; push(labels, "handle") }
|
||||
if (meth == "status") { bare = "http_status_of"; push(labels, "handle") }
|
||||
if (meth == "ok") { bare = "http_ok"; push(labels, "handle") }
|
||||
if (meth == "text") { bare = "http_text"; push(labels, "handle") }
|
||||
if (meth == "body_len") { bare = "http_body_len"; push(labels, "handle") }
|
||||
if (meth == "header") { bare = "http_header_of"; push(labels, "handle"); push(labels, "name") }
|
||||
if (meth == "free") { bare = "http_close"; push(labels, "handle") }
|
||||
if (meth == "parse") { bare = "http_parse"; push(labels, "bytes"); push(labels, "len") }
|
||||
if (meth == "save_to") { bare = "http_save_to"; push(labels, "handle"); push(labels, "path") }
|
||||
if (meth == "received") { bare = "http_received"; push(labels, "handle") }
|
||||
if (meth == "expected") { bare = "http_expected"; push(labels, "handle") }
|
||||
}
|
||||
# Udp.* — polled IPv4 datagrams (runtime/native/udp.ludic + udp.ll / udp_win.ll).
|
||||
# Out-of-band like Http.*: the transport under a game's own netcode.
|
||||
if (ns == "Udp") {
|
||||
if (meth == "open") { bare = "udp_open"; push(labels, "port") }
|
||||
if (meth == "port") { bare = "udp_port"; push(labels, "socket") }
|
||||
if (meth == "send") { bare = "udp_send"; push(labels, "socket"); push(labels, "ip"); push(labels, "port"); push(labels, "bytes"); push(labels, "len") }
|
||||
if (meth == "recv") { bare = "udp_recv"; push(labels, "socket"); push(labels, "bytes"); push(labels, "cap") }
|
||||
if (meth == "from_ip") { bare = "udp_from_ip"; push(labels, "socket") }
|
||||
if (meth == "from_port") { bare = "udp_from_port"; push(labels, "socket") }
|
||||
if (meth == "close") { bare = "udp_close"; push(labels, "socket") }
|
||||
if (meth == "resolve") { bare = "udp_resolve"; push(labels, "name") }
|
||||
if (meth == "local_ip") { bare = "udp_local_ip" }
|
||||
if (meth == "ip") { bare = "udp_ip"; push(labels, "text") }
|
||||
if (meth == "ip_text") { bare = "udp_ip_text"; push(labels, "ip") }
|
||||
}
|
||||
# Process.* — child processes, started and polled (runtime/native/process.ludic +
|
||||
# process.ll / process_win.ll). Out-of-band, like Http.*.
|
||||
if (ns == "Process") {
|
||||
if (meth == "spawn") { bare = "process_spawn"; push(labels, "path"); push(labels, "args") }
|
||||
if (meth == "poll") { bare = "process_poll"; push(labels, "handle") }
|
||||
if (meth == "kill") { bare = "process_kill"; push(labels, "handle") }
|
||||
if (meth == "free") { bare = "process_free"; push(labels, "handle") }
|
||||
}
|
||||
# Phase 3: the bare reflection / networking / process builtins, namespaced.
|
||||
# Each is a pure alias — the callee is rewritten to the bare name below.
|
||||
if (ns == "World") {
|
||||
if (meth == "get") { bare = "world_get" }
|
||||
if (meth == "set") { bare = "world_set" }
|
||||
if (meth == "has") { bare = "world_has" }
|
||||
if (meth == "count") { bare = "world_count" }
|
||||
if (meth == "size") { bare = "world_size" }
|
||||
if (meth == "spawn") { bare = "world_spawn" }
|
||||
if (meth == "despawn") { bare = "world_despawn" } # #84 — despawn an entity by id (runs @OnDespawn + frees)
|
||||
if (meth == "save") { bare = "world_save" }
|
||||
if (meth == "load") { bare = "world_load" }
|
||||
if (meth == "prop_id") { bare = "world_prop_id" }
|
||||
if (meth == "field_id") { bare = "world_field_id" }
|
||||
if (meth == "model_id") { bare = "world_model_id" }
|
||||
if (meth == "kind") { bare = "world_kind" }
|
||||
if (meth == "register_prop") { bare = "world_register_prop" }
|
||||
if (meth == "attach") { bare = "world_attach_dyn" }
|
||||
if (meth == "detach") { bare = "world_detach_dyn" }
|
||||
if (meth == "query_next") { bare = "world_query_next" }
|
||||
}
|
||||
if (ns == "Network") {
|
||||
if (meth == "send") { bare = "net_send" }
|
||||
if (meth == "poll") { bare = "net_poll" }
|
||||
if (meth == "serialize") { bare = "serialize" }
|
||||
if (meth == "apply") { bare = "apply" }
|
||||
if (meth == "owner") { bare = "owner" }
|
||||
if (meth == "set_owner") { bare = "set_owner" }
|
||||
if (meth == "is_server") { bare = "is_server" }
|
||||
if (meth == "is_owner") { bare = "is_owner" }
|
||||
if (meth == "local_id") { bare = "local_id" }
|
||||
}
|
||||
if (ns == "System") {
|
||||
# System.* is the low-level file/process surface only. The environment slice
|
||||
# (arg/arg_count/env/exit) and the standard streams (stdout/stderr) were
|
||||
# duplicated by the canonical Os.* namespace and have been retired — see
|
||||
# Os.arg/arg_count/env/exit and Os.stdout_write/stderr_write (issue #40).
|
||||
if (meth == "run") { bare = "run" }
|
||||
if (meth == "read_char") { bare = "read_char" }
|
||||
if (meth == "file_open") { bare = "file_open" }
|
||||
if (meth == "file_read") { bare = "file_read" }
|
||||
if (meth == "file_write") { bare = "file_write" }
|
||||
if (meth == "file_seek") { bare = "file_seek" }
|
||||
if (meth == "file_tell") { bare = "file_tell" }
|
||||
if (meth == "file_close") { bare = "file_close" }
|
||||
}
|
||||
if (ns == "Save") {
|
||||
if (meth == "write") { bare = "save" }
|
||||
if (meth == "read") { bare = "load" }
|
||||
}
|
||||
# Regex.* -> the regex_* engine functions (spliced from runtime/native/regex*.ludic
|
||||
# when a program mentions Regex.*). Each is a plain alias; the engine functions
|
||||
# are ordinary Ludic, so the generic call path resolves them to @fn_regex_*.
|
||||
if (ns == "Regex") {
|
||||
if (meth == "compile") { bare = "regex_compile"; push(labels, "pattern") }
|
||||
if (meth == "valid") { bare = "regex_valid"; push(labels, "pattern") }
|
||||
if (meth == "matches") { bare = "regex_matches"; push(labels, "text"); push(labels, "pattern") }
|
||||
if (meth == "test") { bare = "regex_test"; push(labels, "text"); push(labels, "re") }
|
||||
if (meth == "find") { bare = "regex_find"; push(labels, "text"); push(labels, "pattern") }
|
||||
if (meth == "exec") { bare = "regex_exec"; push(labels, "text"); push(labels, "re") }
|
||||
if (meth == "next") { bare = "regex_next"; push(labels, "text"); push(labels, "re"); push(labels, "from") }
|
||||
if (meth == "replace") { bare = "regex_replace"; push(labels, "text"); push(labels, "pattern"); push(labels, "replacement") }
|
||||
if (meth == "group") { bare = "regex_group"; push(labels, "match"); push(labels, "n") }
|
||||
if (meth == "group_count") { bare = "regex_group_count"; push(labels, "match") }
|
||||
if (meth == "start") { bare = "regex_start"; push(labels, "match"); push(labels, "n") }
|
||||
if (meth == "end") { bare = "regex_end"; push(labels, "match"); push(labels, "n") }
|
||||
if (meth == "ok") { bare = "regex_ok"; push(labels, "match") }
|
||||
}
|
||||
# Grid.* — tile geometry and pathfinding over the Map tilemap, from
|
||||
# runtime/native/grid.ludic (spliced with core.ludic). `wall` is the impassable
|
||||
# tile char, e.g. '#'. line/flood/a_star return []Cell slices. (Pathfinding
|
||||
|
|
@ -650,162 +375,24 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
# spliced on demand). These are ordinary Ludic functions, so the generic call
|
||||
# path resolves them to @fn_bigint_* / @fn_decimal_* and keeps their return
|
||||
# types (BigNum / Dec / int / bool / string).
|
||||
if (ns == "BigInt") {
|
||||
if (meth == "from") { bare = "bigint_from"; push(labels, "value") }
|
||||
if (meth == "parse") { bare = "bigint_from_str"; push(labels, "text") }
|
||||
if (meth == "add") { bare = "bigint_add"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "sub") { bare = "bigint_sub"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "mul") { bare = "bigint_mul"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "neg") { bare = "bigint_neg"; push(labels, "a") }
|
||||
if (meth == "pow") { bare = "bigint_pow"; push(labels, "a"); push(labels, "exp") }
|
||||
if (meth == "div") { bare = "bigint_div_int"; push(labels, "a"); push(labels, "d") }
|
||||
if (meth == "mod") { bare = "bigint_mod_int"; push(labels, "a"); push(labels, "d") }
|
||||
if (meth == "cmp") { bare = "bigint_cmp"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "eq") { bare = "bigint_eq"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "is_zero") { bare = "bigint_is_zero"; push(labels, "a") }
|
||||
if (meth == "to_int") { bare = "bigint_to_int"; push(labels, "a") }
|
||||
if (meth == "str") { bare = "bigint_str"; push(labels, "a") }
|
||||
}
|
||||
if (ns == "Decimal") {
|
||||
if (meth == "from") { bare = "decimal_from"; push(labels, "value") }
|
||||
if (meth == "parse") { bare = "decimal_from_str"; push(labels, "text") }
|
||||
if (meth == "add") { bare = "decimal_add"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "sub") { bare = "decimal_sub"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "mul") { bare = "decimal_mul"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "neg") { bare = "decimal_neg"; push(labels, "a") }
|
||||
if (meth == "cmp") { bare = "decimal_cmp"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "eq") { bare = "decimal_eq"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "scale") { bare = "decimal_scale"; push(labels, "d") }
|
||||
if (meth == "rescale") { bare = "decimal_rescale"; push(labels, "d"); push(labels, "places") }
|
||||
if (meth == "str") { bare = "decimal_str"; push(labels, "d") }
|
||||
}
|
||||
# Dict.* / Set.* -> the hash-table engine (runtime/native/dict.ludic, spliced
|
||||
# on demand). Ordinary Ludic functions, so the generic call path resolves them
|
||||
# to @fn_dict_* / @fn_set_* and keeps their return types (Dict / int / bool /
|
||||
# []pointer).
|
||||
if (ns == "Dict") {
|
||||
if (meth == "new") { bare = "dict_new" }
|
||||
if (meth == "set") { bare = "dict_set"; push(labels, "d"); push(labels, "key"); push(labels, "value") }
|
||||
if (meth == "get") { bare = "dict_get"; push(labels, "d"); push(labels, "key") }
|
||||
if (meth == "get_or") { bare = "dict_get_or"; push(labels, "d"); push(labels, "key"); push(labels, "fallback") }
|
||||
if (meth == "has") { bare = "dict_has"; push(labels, "d"); push(labels, "key") }
|
||||
if (meth == "remove") { bare = "dict_remove"; push(labels, "d"); push(labels, "key") }
|
||||
if (meth == "size") { bare = "dict_size"; push(labels, "d") }
|
||||
if (meth == "clear") { bare = "dict_clear"; push(labels, "d") }
|
||||
if (meth == "keys") { bare = "dict_keys"; push(labels, "d") }
|
||||
}
|
||||
if (ns == "Set") {
|
||||
if (meth == "new") { bare = "set_new" }
|
||||
if (meth == "add") { bare = "set_add"; push(labels, "s"); push(labels, "key") }
|
||||
if (meth == "has") { bare = "set_has"; push(labels, "s"); push(labels, "key") }
|
||||
if (meth == "remove") { bare = "set_remove"; push(labels, "s"); push(labels, "key") }
|
||||
if (meth == "size") { bare = "set_size"; push(labels, "s") }
|
||||
if (meth == "clear") { bare = "set_clear"; push(labels, "s") }
|
||||
if (meth == "members") { bare = "set_members"; push(labels, "s") }
|
||||
}
|
||||
# Job.* / Promise.* / Sync.* -> the concurrency runtime (runtime/native/jobs.ludic,
|
||||
# spliced on demand). Ordinary Ludic functions, so the generic call path keeps
|
||||
# their return types (int / bool). The safe tier (Job/Promise) is a deterministic
|
||||
# cooperative scheduler; Sync.* is the advanced, opt-in message-passing tier. #14.
|
||||
if (ns == "Job") {
|
||||
if (meth == "defer") { bare = "job_defer" }
|
||||
if (meth == "run") { bare = "job_run"; push(labels, "kind"); push(labels, "arg") }
|
||||
if (meth == "fulfill") { bare = "job_fulfill"; push(labels, "handle"); push(labels, "value") }
|
||||
if (meth == "fail") { bare = "job_fail"; push(labels, "handle"); push(labels, "error") }
|
||||
if (meth == "cancel") { bare = "job_cancel"; push(labels, "handle") }
|
||||
if (meth == "pump") { bare = "job_pump"; push(labels, "budget") }
|
||||
if (meth == "done") { bare = "job_done"; push(labels, "handle") }
|
||||
if (meth == "ok") { bare = "job_ok"; push(labels, "handle") }
|
||||
if (meth == "failed") { bare = "job_failed"; push(labels, "handle") }
|
||||
if (meth == "cancelled") { bare = "job_cancelled"; push(labels, "handle") }
|
||||
if (meth == "result") { bare = "job_result"; push(labels, "handle") }
|
||||
if (meth == "error") { bare = "job_error"; push(labels, "handle") }
|
||||
if (meth == "pending") { bare = "job_pending" }
|
||||
if (meth == "free") { bare = "job_free"; push(labels, "handle") }
|
||||
if (meth == "parallel_for") { bare = "job_parallel_for"; push(labels, "count"); push(labels, "work"); push(labels, "ctx"); if len(e.kids) > 1 { check_worker_ref(e.kids[1]) } }
|
||||
if (meth == "is_worker") { bare = "job_is_worker" }
|
||||
}
|
||||
if (ns == "Promise") {
|
||||
if (meth == "all") { bare = "prom_all"; push(labels, "handles") }
|
||||
if (meth == "race") { bare = "prom_race"; push(labels, "handles") }
|
||||
if (meth == "count_done") { bare = "prom_count_done"; push(labels, "handles") }
|
||||
if (meth == "all_done") { bare = "prom_all_done"; push(labels, "handles") }
|
||||
}
|
||||
if (ns == "Sync") {
|
||||
if (meth == "mutex") { bare = "sync_mutex" }
|
||||
if (meth == "lock") { bare = "sync_lock"; push(labels, "mutex") }
|
||||
if (meth == "unlock") { bare = "sync_unlock"; push(labels, "mutex") }
|
||||
if (meth == "try_lock") { bare = "sync_try_lock"; push(labels, "mutex") }
|
||||
if (meth == "atomic") { bare = "sync_atomic" }
|
||||
if (meth == "get") { bare = "sync_get"; push(labels, "atomic") }
|
||||
if (meth == "set") { bare = "sync_set"; push(labels, "atomic"); push(labels, "value") }
|
||||
if (meth == "add") { bare = "sync_add"; push(labels, "atomic"); push(labels, "delta") }
|
||||
if (meth == "cas") { bare = "sync_cas"; push(labels, "atomic"); push(labels, "expect"); push(labels, "next") }
|
||||
if (meth == "channel") { bare = "sync_channel" }
|
||||
if (meth == "send") { bare = "sync_send"; push(labels, "channel"); push(labels, "value") }
|
||||
if (meth == "recv") { bare = "sync_recv"; push(labels, "channel") }
|
||||
if (meth == "can_recv") { bare = "sync_can_recv"; push(labels, "channel") }
|
||||
if (meth == "len") { bare = "sync_len"; push(labels, "channel") }
|
||||
if (meth == "cpu_count") { bare = "sync_cpu_count" }
|
||||
}
|
||||
# Huge.* / Angle.* / Percent.* -> the numeric runtime (runtime/native/numeric.ludic,
|
||||
# spliced on demand). Ordinary Ludic functions, so the generic call path keeps
|
||||
# their return types (Huge / fixed / int / bool).
|
||||
if (ns == "Huge") {
|
||||
if (meth == "from") { bare = "huge_from"; push(labels, "value") }
|
||||
if (meth == "add") { bare = "huge_add"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "sub") { bare = "huge_sub"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "mul") { bare = "huge_mul"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "neg") { bare = "huge_neg"; push(labels, "a") }
|
||||
if (meth == "cmp") { bare = "huge_cmp"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "sign") { bare = "huge_sign"; push(labels, "a") }
|
||||
if (meth == "mantissa") { bare = "huge_mantissa"; push(labels, "a") }
|
||||
if (meth == "exp") { bare = "huge_exp"; push(labels, "a") }
|
||||
if (meth == "str") { bare = "huge_str"; push(labels, "a") }
|
||||
}
|
||||
if (ns == "Angle") {
|
||||
if (meth == "from_degrees") { bare = "angle_from_degrees"; push(labels, "d") }
|
||||
if (meth == "to_degrees") { bare = "angle_to_degrees"; push(labels, "a") }
|
||||
if (meth == "wrap") { bare = "angle_wrap"; push(labels, "a") }
|
||||
if (meth == "sin") { bare = "angle_sin"; push(labels, "a") }
|
||||
if (meth == "cos") { bare = "angle_cos"; push(labels, "a") }
|
||||
if (meth == "add") { bare = "angle_add"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "diff") { bare = "angle_diff"; push(labels, "a"); push(labels, "b") }
|
||||
if (meth == "diff_degrees") { bare = "angle_diff_degrees"; push(labels, "a"); push(labels, "b") } # signed, -180..180, ints
|
||||
if (meth == "lerp") { bare = "angle_lerp"; push(labels, "a"); push(labels, "b"); push(labels, "t") }
|
||||
}
|
||||
if (ns == "Percent") {
|
||||
if (meth == "clamp") { bare = "percent_clamp"; push(labels, "v") }
|
||||
if (meth == "of") { bare = "percent_of"; push(labels, "num"); push(labels, "den") }
|
||||
if (meth == "lerp") { bare = "percent_lerp"; push(labels, "a"); push(labels, "b"); push(labels, "t") }
|
||||
if (meth == "apply") { bare = "percent_apply"; push(labels, "value"); push(labels, "p") }
|
||||
}
|
||||
if (ns == "Grid") {
|
||||
if (meth == "line") { bare = "grid_line"; push(labels, "x0"); push(labels, "y0"); push(labels, "x1"); push(labels, "y1") }
|
||||
if (meth == "blocked") { bare = "grid_blocked"; push(labels, "x"); push(labels, "y"); push(labels, "wall") }
|
||||
if (meth == "line_of_sight") { bare = "grid_line_of_sight"; push(labels, "x0"); push(labels, "y0"); push(labels, "x1"); push(labels, "y1"); push(labels, "wall") }
|
||||
if (meth == "flood") { bare = "grid_flood"; push(labels, "x"); push(labels, "y"); push(labels, "wall") }
|
||||
if (meth == "a_star") { bare = "path_a_star"; push(labels, "x0"); push(labels, "y0"); push(labels, "x1"); push(labels, "y1"); push(labels, "wall") }
|
||||
}
|
||||
# Light.* — the 2D light-accumulation pass (runtime/native/light.ludic, spliced
|
||||
# on demand). A game runs it in its render phase: ambient multiplies the scene
|
||||
# down, point adds a radial glow (blocked by occluders -> hard shadows). Screen
|
||||
# space, deterministic (integer + Q16.16), diffable. `energy` is a fixed.
|
||||
if (ns == "Light") {
|
||||
if (meth == "ambient") { bare = "light_ambient"; push(labels, "color") }
|
||||
if (meth == "point") { bare = "light_point"; push(labels, "x"); push(labels, "y"); push(labels, "radius"); push(labels, "color"); push(labels, "energy") }
|
||||
if (meth == "spot") { bare = "light_spot"; push(labels, "x"); push(labels, "y"); push(labels, "radius"); push(labels, "color"); push(labels, "energy"); push(labels, "direction"); push(labels, "spread") }
|
||||
if (meth == "occlude") { bare = "light_occlude"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height") }
|
||||
if (meth == "clear_occluders") { bare = "light_clear_occluders" }
|
||||
if (meth == "falloff") { bare = "light_set_falloff"; push(labels, "exponent") }
|
||||
if (meth == "soft") { bare = "light_set_soft"; push(labels, "radius") }
|
||||
if (meth == "gel") { bare = "light_set_gel"; push(labels, "color") }
|
||||
if (meth == "clear_gel") { bare = "light_clear_gel" }
|
||||
if (meth == "height") { bare = "light_set_height"; push(labels, "height") }
|
||||
if (meth == "normal") { bare = "light_normal_rect"; push(labels, "x"); push(labels, "y"); push(labels, "width"); push(labels, "height"); push(labels, "nx"); push(labels, "ny") }
|
||||
if (meth == "clear_normals") { bare = "light_clear_normals" }
|
||||
if (meth == "time_of_day") { bare = "light_time_of_day"; push(labels, "t") }
|
||||
}
|
||||
# Anim.* / Motion.* ergonomic writes over the engine components (#48), spliced
|
||||
# from runtime/native/systems.ludic. Anim.play(entity, "run") plays a named clip
|
||||
# registered with Anim.clip; the four-arg Anim.play sets fps/frames/mode
|
||||
|
|
@ -820,147 +407,29 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
else { bare = "anim_play"; push(labels, "entity"); push(labels, "fps"); push(labels, "frames"); push(labels, "mode") }
|
||||
}
|
||||
}
|
||||
if (ns == "Motion") {
|
||||
if (meth == "to") { bare = "motion_to"; push(labels, "entity"); push(labels, "from"); push(labels, "to"); push(labels, "dur"); push(labels, "ease") }
|
||||
}
|
||||
# Tween.* fluent stateful handles (#48), spliced from runtime/native/tween.ludic
|
||||
# and advanced each Update tick by esys_tween. These stand alongside the pure
|
||||
# Tween.* interpolators (emit_anim.ludic): the stateful ones take/return a handle.
|
||||
if (ns == "Tween") {
|
||||
if (meth == "to") { bare = "tween_to"; push(labels, "from"); push(labels, "to"); push(labels, "dur"); push(labels, "ease") }
|
||||
if (meth == "chain") { bare = "tween_chain"; push(labels, "handle"); push(labels, "to"); push(labels, "dur"); push(labels, "ease") }
|
||||
if (meth == "delay") { bare = "tween_delay"; push(labels, "handle"); push(labels, "ticks") }
|
||||
if (meth == "value") { bare = "tween_value"; push(labels, "handle") }
|
||||
if (meth == "stop") { bare = "tween_stop"; push(labels, "handle") }
|
||||
if (meth == "parallel") { bare = "tween_parallel"; push(labels, "a"); push(labels, "b") }
|
||||
}
|
||||
# Query.* — ECS spatial queries over the reflection ABI (runtime/native/query.ludic,
|
||||
# spliced on demand). `prop` is a property id (World.prop_id); the spatial forms
|
||||
# read two int fields (field ids) as (x, y). nearest/first return an entity (-1 =
|
||||
# none); within returns a []int of entities. A linear scan — ample for the entity
|
||||
# counts Ludic targets, like the grid pathfinder's open set.
|
||||
if (ns == "Query") {
|
||||
if (meth == "count") { bare = "query_count"; push(labels, "prop") }
|
||||
if (meth == "first") { bare = "query_first"; push(labels, "prop") }
|
||||
if (meth == "nearest") { bare = "query_nearest"; push(labels, "prop"); push(labels, "pos"); push(labels, "x_field"); push(labels, "y_field"); push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "within") { bare = "query_within"; push(labels, "prop"); push(labels, "pos"); push(labels, "x"); push(labels, "y"); push(labels, "radius"); push(labels, "x_field"); push(labels, "y_field") }
|
||||
}
|
||||
# Reflect.* — runtime type reflection over the world schema (the EV2/EV8 ABI).
|
||||
# Enumerate properties and fields by index, resolve ids by name, and read/write
|
||||
# a field by (prop, field) id — the foundation for auto-serialization and debug
|
||||
# inspectors. Reads the same generated metadata a foreign mod binds.
|
||||
if (ns == "Reflect") {
|
||||
if (meth == "prop") { bare = "world_prop_id"; push(labels, "name") }
|
||||
if (meth == "field") { bare = "world_field_id"; push(labels, "prop"); push(labels, "name") }
|
||||
if (meth == "prop_count") { bare = "world_prop_count" }
|
||||
if (meth == "prop_name") { bare = "world_prop_name"; push(labels, "index") }
|
||||
if (meth == "field_count") { bare = "world_field_count"; push(labels, "prop") }
|
||||
if (meth == "field_name") { bare = "world_field_name"; push(labels, "prop"); push(labels, "index") }
|
||||
if (meth == "field_type") { bare = "world_field_type"; push(labels, "prop"); push(labels, "index") }
|
||||
if (meth == "get") { bare = "world_get"; push(labels, "entity"); push(labels, "prop"); push(labels, "field") }
|
||||
if (meth == "set") { bare = "world_set"; push(labels, "entity"); push(labels, "prop"); push(labels, "field"); push(labels, "value") }
|
||||
if (meth == "has") { bare = "world_has"; push(labels, "entity"); push(labels, "prop") }
|
||||
if (meth == "kind") { bare = "world_kind"; push(labels, "entity") }
|
||||
if (meth == "model") { bare = "world_model_id"; push(labels, "name") }
|
||||
# #44 — serialize an entity to a value tree and apply one back (reflect_io.ludic)
|
||||
if (meth == "serialize") { bare = "reflect_serialize"; push(labels, "entity") }
|
||||
if (meth == "apply") { bare = "reflect_apply"; push(labels, "entity"); push(labels, "value") }
|
||||
}
|
||||
# Value.* — the generic value tree (runtime/native/value.ludic, spliced on
|
||||
# demand). Nodes are int/fixed/bool/str/list/object; the methods map straight
|
||||
# to the spliced value_* functions, whose signatures carry the return types.
|
||||
if (ns == "Value") {
|
||||
if (meth == "null") { bare = "value_null" }
|
||||
if (meth == "int") { bare = "value_int"; push(labels, "n") }
|
||||
if (meth == "fixed") { bare = "value_fixed"; push(labels, "f") }
|
||||
if (meth == "bool") { bare = "value_bool"; push(labels, "b") }
|
||||
if (meth == "str") { bare = "value_str"; push(labels, "s") }
|
||||
if (meth == "list") { bare = "value_list" }
|
||||
if (meth == "object") { bare = "value_object" }
|
||||
if (meth == "add") { bare = "value_add"; push(labels, "list"); push(labels, "item") }
|
||||
if (meth == "put") { bare = "value_put"; push(labels, "obj"); push(labels, "key"); push(labels, "item") }
|
||||
if (meth == "get") { bare = "value_get"; push(labels, "obj"); push(labels, "key") }
|
||||
if (meth == "has") { bare = "value_has"; push(labels, "obj"); push(labels, "key") }
|
||||
if (meth == "at") { bare = "value_at"; push(labels, "list"); push(labels, "index") }
|
||||
if (meth == "key_at") { bare = "value_key_at"; push(labels, "obj"); push(labels, "index") }
|
||||
if (meth == "count") { bare = "value_count"; push(labels, "value") }
|
||||
if (meth == "kind") { bare = "value_kind"; push(labels, "value") }
|
||||
if (meth == "as_int") { bare = "value_as_int"; push(labels, "value") }
|
||||
if (meth == "as_str") { bare = "value_as_str"; push(labels, "value") }
|
||||
}
|
||||
# Json.* — the text bridge over the value tree (runtime/native/value.ludic).
|
||||
if (ns == "Json") {
|
||||
if (meth == "encode") { bare = "json_encode"; push(labels, "value") }
|
||||
if (meth == "parse") { bare = "json_parse"; push(labels, "text") }
|
||||
}
|
||||
# Xml.* — the minimal XML reader (runtime/native/xml.ludic, spliced on demand).
|
||||
# parse returns an Xml node; the accessors read tag/text/attributes/children.
|
||||
if (ns == "Xml") {
|
||||
if (meth == "parse") { bare = "xml_parse"; push(labels, "text") }
|
||||
if (meth == "tag") { bare = "xml_tag"; push(labels, "node") }
|
||||
if (meth == "text") { bare = "xml_text"; push(labels, "node") }
|
||||
if (meth == "attr") { bare = "xml_attr"; push(labels, "node"); push(labels, "key") }
|
||||
if (meth == "attr_int") { bare = "xml_attr_int"; push(labels, "node"); push(labels, "key"); push(labels, "dflt") }
|
||||
if (meth == "has") { bare = "xml_has"; push(labels, "node"); push(labels, "key") }
|
||||
if (meth == "attr_count") { bare = "xml_attr_count"; push(labels, "node") }
|
||||
if (meth == "child_count") { bare = "xml_child_count"; push(labels, "node") }
|
||||
if (meth == "child") { bare = "xml_child"; push(labels, "node"); push(labels, "index") }
|
||||
if (meth == "find") { bare = "xml_find"; push(labels, "node"); push(labels, "tag") }
|
||||
if (meth == "count") { bare = "xml_count"; push(labels, "node"); push(labels, "tag") }
|
||||
}
|
||||
# Base64.* — standard base64 codec (runtime/native/base64.ludic, spliced on
|
||||
# demand). decode/encode round-trip through NUL-terminated strings.
|
||||
if (ns == "Base64") {
|
||||
if (meth == "decode") { bare = "base64_decode"; push(labels, "src") }
|
||||
if (meth == "encode") { bare = "base64_encode"; push(labels, "src") }
|
||||
}
|
||||
# Tiled.* — Tiled map support (runtime/native/tiled.ludic, spliced on demand).
|
||||
# read/read_tsx produce the intermediate Value tree (#68); load/gid/resolve/
|
||||
# draw/prop operate on the loaded map model (#69+).
|
||||
if (ns == "Tiled") {
|
||||
if (meth == "read") { bare = "tiled_read"; push(labels, "path") }
|
||||
if (meth == "read_tsx") { bare = "tiled_read_tsx"; push(labels, "path") }
|
||||
if (meth == "load") { bare = "tiled_load"; push(labels, "path") }
|
||||
if (meth == "gid") { bare = "tmap_gid"; push(labels, "map"); push(labels, "layer"); push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "resolve") { bare = "tmap_resolve"; push(labels, "map"); push(labels, "gid") }
|
||||
if (meth == "width") { bare = "tmap_width"; push(labels, "map") }
|
||||
if (meth == "height") { bare = "tmap_height"; push(labels, "map") }
|
||||
if (meth == "layer_count") { bare = "tmap_layer_count"; push(labels, "map") }
|
||||
if (meth == "layer_name") { bare = "tmap_layer_name"; push(labels, "map"); push(labels, "index") }
|
||||
if (meth == "draw") { bare = "tmap_draw"; push(labels, "map"); push(labels, "camx"); push(labels, "camy") }
|
||||
if (meth == "draw_anim") { bare = "tmap_draw_anim"; push(labels, "map"); push(labels, "camx"); push(labels, "camy"); push(labels, "frame") }
|
||||
if (meth == "frame_gid") { bare = "tmap_frame_gid"; push(labels, "map"); push(labels, "gid"); push(labels, "frame") }
|
||||
if (meth == "animated") { bare = "tmap_is_animated"; push(labels, "map"); push(labels, "gid") }
|
||||
if (meth == "project") { bare = "tmap_project"; push(labels, "map"); push(labels, "layer") }
|
||||
if (meth == "collide") { bare = "tmap_collide"; push(labels, "map"); push(labels, "layer") }
|
||||
if (meth == "collision_kind") { bare = "tmap_collision_kind"; push(labels, "map"); push(labels, "gid") }
|
||||
if (meth == "tree") { bare = "tmap_tree"; push(labels, "map") }
|
||||
if (meth == "tile_prop") { bare = "tmap_tile_prop"; push(labels, "map"); push(labels, "gid"); push(labels, "name") }
|
||||
if (meth == "tile_shapes") { bare = "tmap_tile_has_shapes"; push(labels, "map"); push(labels, "gid") }
|
||||
# P4 (#72): objects / properties / custom types / templates / opt-in spawn
|
||||
if (meth == "object_count") { bare = "tmap_object_count"; push(labels, "map"); push(labels, "layer") }
|
||||
if (meth == "object") { bare = "tmap_object"; push(labels, "map"); push(labels, "layer"); push(labels, "index") }
|
||||
if (meth == "object_shape") { bare = "tiled_object_shape"; push(labels, "object") }
|
||||
if (meth == "prop") { bare = "tiled_prop_str"; push(labels, "container"); push(labels, "name") }
|
||||
if (meth == "prop_int") { bare = "tiled_prop_int"; push(labels, "container"); push(labels, "name") }
|
||||
if (meth == "prop_type") { bare = "tiled_prop_type"; push(labels, "container"); push(labels, "name") }
|
||||
if (meth == "load_types") { bare = "tiled_load_types"; push(labels, "path") }
|
||||
if (meth == "template") { bare = "tiled_read_template"; push(labels, "path") }
|
||||
if (meth == "spawn") { bare = "tiled_spawn_object"; push(labels, "map"); push(labels, "object"); push(labels, "model") }
|
||||
if (meth == "spawn_layer") { bare = "tiled_spawn_layer"; push(labels, "map"); push(labels, "layer"); push(labels, "model") }
|
||||
# P5 (#73): orientation coords + image/group-layer accessors
|
||||
if (meth == "cell_x") { bare = "tmap_cell_sx"; push(labels, "map"); push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "cell_y") { bare = "tmap_cell_sy"; push(labels, "map"); push(labels, "x"); push(labels, "y") }
|
||||
if (meth == "layer_kind") { bare = "tmap_layer_kind"; push(labels, "map"); push(labels, "layer") }
|
||||
if (meth == "layer_opacity") { bare = "tmap_layer_opacity"; push(labels, "map"); push(labels, "layer") }
|
||||
if (meth == "layer_tint") { bare = "tmap_layer_tint"; push(labels, "map"); push(labels, "layer") }
|
||||
if (meth == "layer_offsetx") { bare = "tmap_layer_offsetx"; push(labels, "map"); push(labels, "layer") }
|
||||
if (meth == "layer_offsety") { bare = "tmap_layer_offsety"; push(labels, "map"); push(labels, "layer") }
|
||||
# P6 (#74): .world stitching
|
||||
if (meth == "world") { bare = "tiled_read_world"; push(labels, "path") }
|
||||
if (meth == "world_count") { bare = "tiled_world_count"; push(labels, "world") }
|
||||
if (meth == "world_map") { bare = "tiled_world_map"; push(labels, "world"); push(labels, "index") }
|
||||
}
|
||||
# #62: a package-provided namespace (declared with @Namespace(Foo)) that none
|
||||
# of the hardcoded core blocks matched — alias Foo.method to the bare function
|
||||
# foo_method (positional args), the same generic path the core aliases use.
|
||||
|
|
|
|||
|
|
@ -340,6 +340,8 @@ function static_type(e: Node) -> pointer {
|
|||
if (e.a.a.s == "Map") and ((e.a.s == "random_cell") or (e.a.s == "random_cell_far") or (e.a.s == "to_tile")) { return "IVec2" }
|
||||
if (e.a.a.s == "Collider") and (e.a.s == "center") { return "IVec2" }
|
||||
if (e.a.a.s == "Input") and (e.a.s == "move_i") { return "IVec2" }
|
||||
let al = ns_alias_find(e.a.a.s, e.a.s)
|
||||
if al >= 0 { let af = find_fn(g_al_target[al]); if (af != null) { return af.ty } }
|
||||
let nf = find_fn(ns_lower(e.a.a.s) + ("_") + e.a.s); if (nf != null) { return nf.ty }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,15 @@ function ck_call_fn(e: Node, name: pointer, f: Node) -> pointer {
|
|||
if f.ty == null { return "void" }
|
||||
return f.ty
|
||||
}
|
||||
function ck_call_alias(e: Node, name: pointer, al: int, f: Node) -> pointer {
|
||||
let labels = new []pointer
|
||||
let tys = new []pointer
|
||||
ck_params(f, labels, tys)
|
||||
let alabels = ns_alias_labels(al)
|
||||
if len(alabels) == len(tys) { ck_args(e, name, alabels, tys) } else { ck_args(e, name, labels, tys) }
|
||||
if f.ty == null { return "void" }
|
||||
return f.ty
|
||||
}
|
||||
function ck_call_sig(e: Node, name: pointer, t: pointer) -> pointer {
|
||||
ck_args(e, name, new []pointer, fn_ty_params(t))
|
||||
return fn_ty_ret(t)
|
||||
|
|
@ -97,8 +106,17 @@ function ck_call(e: Node) -> pointer {
|
|||
if c.kind == E_MEMBER {
|
||||
let b = c.a
|
||||
if b.kind == E_ID and ck_local(b.s) < 0 and ck_global(b.s) == null {
|
||||
# Ns.fn: the emitter's own table lowers it, supplying defaults and reordering, so the
|
||||
# Ludic function behind it gives the result's type and nothing about the arguments
|
||||
# an alias (L6) is its target, labels and all, so its arguments are checked in full
|
||||
let al = ns_alias_find(b.s, c.s)
|
||||
if al >= 0 {
|
||||
var tf = ck_fn(g_al_target[al])
|
||||
if tf == null { tf = ck_extern(g_al_target[al]) }
|
||||
if tf != null { return ck_call_alias(e, `{b.s}.{c.s}`, al, tf) }
|
||||
ck_walk_args(e)
|
||||
return "?"
|
||||
}
|
||||
# any other Ns.fn is the emitter's own table, which supplies defaults and reorders,
|
||||
# so the Ludic function behind it gives the result's type and nothing about the arguments
|
||||
ck_walk_args(e)
|
||||
let nf = ck_fn(ns_lower(b.s) + "_" + c.s)
|
||||
if nf != null and nf.ty != null { return nf.ty }
|
||||
|
|
|
|||
74
selfhost/frontend/aliases.ludic
Normal file
74
selfhost/frontend/aliases.ludic
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# aliases.ludic — L6: a namespace method that is a name for a function. Inside a namespace block,
|
||||
# alias fill_rectangle(x, y, width, height, color) = fill_rect
|
||||
# makes `Screen.fill_rectangle(...)` a call to `fill_rect`, taking named arguments by those labels
|
||||
# in the target's parameter order. With no label list the target's own parameter names are the
|
||||
# labels; an empty one, `alias show() = present`, takes no named arguments. The engine declares
|
||||
# its namespaces this way in runtime/native/namespaces.ludic, and a package owns an API the same
|
||||
# way, in its own files - neither needs a line in the compiler.
|
||||
var g_al_ns: []pointer = new []pointer
|
||||
var g_al_meth: []pointer = new []pointer
|
||||
var g_al_target: []pointer = new []pointer
|
||||
var g_al_labels: []pointer = new []pointer # "a|b|c"; "" for none; null for the target's own
|
||||
|
||||
function ns_parse_alias(nsname: pointer, is_exp: int) -> void {
|
||||
pi += 1
|
||||
let meth = eat_id()
|
||||
var labels: pointer = null
|
||||
if is_op("(") {
|
||||
pi += 1
|
||||
labels = ""
|
||||
while not is_op(")") {
|
||||
if len(labels) > 0 { labels = labels + "|" }
|
||||
labels = labels + eat_id()
|
||||
if is_op(",") { pi += 1 }
|
||||
}
|
||||
eat_op(")")
|
||||
}
|
||||
eat_op("=")
|
||||
let target = eat_id()
|
||||
register_namespace(nsname)
|
||||
if is_exp == 0 { return }
|
||||
if ns_alias_find(nsname, meth) >= 0 { perr(`{nsname}.{meth} is declared twice`) }
|
||||
push(g_al_ns, nsname)
|
||||
push(g_al_meth, meth)
|
||||
push(g_al_target, target)
|
||||
push(g_al_labels, labels)
|
||||
}
|
||||
function ns_alias_find(ns: pointer, meth: pointer) -> int {
|
||||
var i = 0
|
||||
while i < len(g_al_ns) {
|
||||
if (g_al_meth[i] == meth) and (g_al_ns[i] == ns) { return i }
|
||||
i += 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
function ns_alias_labels(i: int) -> []pointer {
|
||||
let out = new []pointer
|
||||
let ls = g_al_labels[i]
|
||||
if ls == null {
|
||||
var d = find_fn(g_al_target[i])
|
||||
if d == null { d = find_extern(g_al_target[i]) }
|
||||
if d != null { return param_labels(d) }
|
||||
return out
|
||||
}
|
||||
var a = 0
|
||||
var j = 0
|
||||
let n = len(ls)
|
||||
while j <= n {
|
||||
if j == n or ls[j] == '|' {
|
||||
if j > a { push(out, ls[a .. j]) }
|
||||
a = j + 1
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
function emit_alias_call(i: int, e: Node) -> Val {
|
||||
reorder_named(e, ns_alias_labels(i))
|
||||
let id = node(E_ID)
|
||||
id.s = g_al_target[i]
|
||||
id.file = e.file
|
||||
id.line = e.line
|
||||
e.a = id
|
||||
return emit_call(e)
|
||||
}
|
||||
|
|
@ -594,7 +594,12 @@ function parse_namespace() -> void {
|
|||
var is_exp = 1 # default: exported (public)
|
||||
if is_id("export") { pi += 1 }
|
||||
else { if is_id("internal") { pi += 1; is_exp = 0 } }
|
||||
if not is_id("function") { perr("a namespace body holds functions: expected `function`") }
|
||||
if is_id("alias") {
|
||||
ns_parse_alias(nsname, is_exp)
|
||||
skipnl()
|
||||
continue
|
||||
}
|
||||
if not is_id("function") { perr("a namespace body holds functions or aliases: expected `function` or `alias`") }
|
||||
let f = parse_fn()
|
||||
push(fns, f); push(shorts, f.s); push(exps, is_exp)
|
||||
skipnl()
|
||||
|
|
@ -1130,6 +1135,10 @@ function do_import(rel: pointer) -> void {
|
|||
# Tools (an `entry` block, no ECS) get nothing.
|
||||
function maybe_splice_runtime() -> void {
|
||||
let saved = cur_dir
|
||||
# L6: the engine's namespaces that are aliases of runtime functions, declared in Ludic
|
||||
cur_dir = ""
|
||||
do_import("runtime/native/namespaces.ludic")
|
||||
cur_dir = saved
|
||||
# a game (has systems/components) links the Ludic runtime.
|
||||
if has_ecs() {
|
||||
cur_dir = ""
|
||||
|
|
|
|||
71844
selfhost/ludicc.seed.ll
71844
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -19,6 +19,7 @@ function selfhost_frags() -> []pointer {
|
|||
push(f, "selfhost/frontend/parse.ludic")
|
||||
push(f, "selfhost/frontend/parse_game.ludic")
|
||||
push(f, "selfhost/frontend/generics.ludic")
|
||||
push(f, "selfhost/frontend/aliases.ludic")
|
||||
push(f, "selfhost/backend/emit_core.ludic")
|
||||
push(f, "selfhost/backend/emit_head.ludic")
|
||||
push(f, "selfhost/backend/emit_addr.ludic")
|
||||
|
|
|
|||
|
|
@ -731,6 +731,9 @@ function cmd_dev_test() -> int {
|
|||
reject_case("rejected/generic_mismatch", "words wants a Pool<string> and this is a Pool<int>", "two instances of one generic are two types")
|
||||
reject_case("rejected/generic_arity", "Pool takes 1 type argument(s) and Pool<int, string> gives 2", "a generic takes as many type arguments as it has parameters")
|
||||
reject_case("rejected/private_generic", "grow is private to module kit", "an instance of a private generic is private to its module")
|
||||
feat_case("lang/aliases", "", "10 5 3", "aliases.ludic (L6: a namespace method declared as an alias of a function, labelled or by its parameters)")
|
||||
reject_case("rejected/alias_arity", "Trail.length takes 2 argument(s) and this call gives 1", "an alias's arguments are checked against its target")
|
||||
reject_case("rejected/alias_twice", "Trail.length is declared twice", "a namespace method is declared once")
|
||||
|
||||
# EV2 the world table: the mod reflection ABI, callable from Ludic by name.
|
||||
net_case("ecs/world_get", "50 1 7")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue