ludic/selfhost/frontend/resource.ludic
Orkuncakilkaya 8cf4b3fed6 fix(lang): the action drain is the program's; --check runs every check a build makes; a registry key named count is refused; name lookups are tables
- the function that calls every reducer (and the action queue) is written in the program's own
  file: in the first action's file it belonged to that module, depended on every module with a
  reducer, and joined a game's modules into one 69-module cycle (examples/actions/modules and a
  ludic deps case hold it); the state instances, the queue and the reducers make no deps edges
- ludicc --check / ludic build --check lower the program too and write nothing, so the code
  writer's refusals are in it: a bind to a function that is gone, an unknown name (and the checker
  now refuses fn <missing> itself); rejects bind_missing_fn, unknown_name, registry_count_key
- def R count is refused: its constant would be PREFIX_COUNT, the registry's size
- a file's module, package, trust and numbers-float are tables, and from the check on the lookups
  of functions, enums, records, globals and externs are too (tagged enums kept as a list): Maroon
  Lake's check-only build went from about 20 s to 7 s including lowering, its IR from about 2
  minutes to under 10 s; duplicate declarations are found by table, not a pair of loops
- threads.ludic's pool check gives each call a little work, so a busy machine cannot run them all
  on the caller before a worker wakes (it failed one run in three under load)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 23:34:43 +03:00

92 lines
3.1 KiB
Text

# resource.ludic — L9: a registry's entries can live in a resource file.
#
# registry Items of Item as IT from "assets/data/items.lres"
#
# The file is a list of entries, each a key and a record, `rope { name: "Rope", weight: 0.4 }`, with
# `#` comments; a field whose type is a record, or a list of records, takes a bare `{ ... }` or a
# `[{ ... }, ...]`, because the schema - the registry's record - says what it is. The file is read
# when the program is: every entry is checked like a `def` (a misspelt field, a string for an int,
# a key twice) and an error names the line in the resource file. Its values are Ludic expressions
# in the registry's module, so an entry names another table's entry by its constant (VD_STORE).
# The path is the project's (the directory the build runs in), else beside the declaring file.
var g_res_mode: bool = false
function res_path(from: pointer, declaring: pointer) -> pointer {
if read_file(from) != null { return from }
return join_path(dir_of(declaring), from)
}
function res_read(v: Node, from: pointer) -> void {
res_read_into(v.s, from, `registry {v.s}`)
}
# the entries of a resource file into registry `reg`, as defs of the reading file's module: a
# registry's own file, or `def REG from "file.lres"` in a module that extends an open registry
function res_read_into(reg: pointer, from: pointer, who: pointer) -> void {
let path = res_path(from, g_parse_file)
let src = read_file(path)
if src == null { perr(`{who}: cannot read the resource file {from}`) }
let saved_toks = toks
let saved_pi = pi
let saved_file = g_parse_file
let saved_parsing = g_parsing
if not (module_of(saved_file) == "") { module_set(path, module_of(saved_file)) }
if is_float_file(saved_file) and not is_float_file(path) { float_file_add(path) }
g_parse_file = path
g_parsing = true
g_res_mode = true
lex_at(src, 1)
pi = 0
while true {
skipnl()
if toks[pi].kind == TK_EOF { break }
let key = eat_id()
skipnl()
let rec = record()
push(g_df_reg, reg)
push(g_df_key, key)
push(g_df_rec, rec)
}
g_res_mode = false
toks = saved_toks
pi = saved_pi
g_parse_file = saved_file
g_parsing = saved_parsing
}
# a bare record, or a list of them, becomes a `new` of the type its field declares
function res_type(e: Node, ty: pointer) -> Node {
if e == null or ty == null { return e }
if e.kind == E_REC {
let comp = find_comp(ty)
if comp == null { return e }
res_type_fields(e, comp)
let nw = node(E_NEW)
nw.s = ty
nw.a = e
nw.file = e.file
nw.line = e.line
return nw
}
if e.kind == E_LIST and is_slice_ty(ty) {
if len(e.kids) == 0 {
let empty = node(E_NEW)
empty.s = ty
empty.file = e.file
empty.line = e.line
return empty
}
var i = 0
while i < len(e.kids) {
e.kids[i] = res_type(e.kids[i], slice_elem(ty))
i += 1
}
}
return e
}
function res_type_fields(rec: Node, comp: Node) -> void {
var i = 0
while i < len(rec.kids) {
let fi = rec.kids[i]
let fx = field_index(comp, fi.s)
if fx >= 0 { fi.a = res_type(fi.a, comp.kids[fx].ty) }
i += 1
}
}