ludic/selfhost/frontend/permap_consts.ludic
Orkuncakilkaya 2f4860ae71 @PerMap and @Chunked(n) registries: rows read per map, a state and verbs written, every map checked
- @PerMap registry R of T from "file.lres" reads <maps root>/<map>/file.lres at run time
  (package.ludic's new `maps` line, default assets/maps); @Chunked(n) one file per chunk,
  {cx}/{cz} in its name. No as PREFIX, no constants, no def, never open.
- permap_gen: state R, r_load/_clear/_find/_path; chunked: RChunk, r_in/_out/_slot/_find/
  _clear/_path; a typed reset and fill per record over lres.ludic, rows, lists and list
  records pooled per table / chunk slot, @alloc_ok on what grows at high water.
- permap_consts: lres_consts__(), the program's int and float constants by name.
- permap_check: ludicc --check reads every map directory (fields, types, constants, fn,
  @OneOf/@Range/@Ref, cross-row @Ref keys in the same map, a key twice); --no-maps skips.
- @Ref(PerMapTable) is refused on a non-string field; the schema says scope/chunk per
  registry and scope map on such a Ref, and lists the canonical @Unit spellings; any other
  @Unit spelling is a warning naming the canonical one.
- reseeded (mac + win seeds; bootstrap-cfree out.ll == seed.ll).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:03:56 +03:00

182 lines
5.9 KiB
Text

# permap_consts.ludic — a map's row names a constant (MDL_TENT2, SP_DEER, a registry's KEY) and it is
# resolved when the map loads. So a program with a @PerMap registry carries its int and float
# constants by name: `lres_consts__()` returns them as one text, "NAME i 12;NAME f 1.5;", sorted by
# name, which lres.ludic indexes once and searches in place. A constant is in it when the compiler
# can work out its value here - a literal, a sum or a shift of literals and other constants, a
# registry's index; anything else (a call, a field) is left out, and a row naming it fails to load
# as "unknown constant", as `ludicc --check` says it will. When two modules each have a private
# constant of one name, the first declared is the one a map reads.
var g_pmc_ival: int = 0
var g_pmc_fval: pointer = null
var g_pmc_names: []pointer = new []pointer # the table: plain name, whether a float, its value
var g_pmc_isf: []bool = new []bool
var g_pmc_iv: []int = new []int
var g_pmc_ftext: []pointer = new []pointer
# every constant of the program's own files by name (as written), sorted; the first declared first
var g_pmc_all: []pointer = new []pointer
var g_pmc_node: []Node = new []Node
function pmc_index() -> void {
let xs = new []int
let keys = new []pointer
let nodes = new []Node
var i = 0
while i < len(prog) {
let d = prog[i]
if d.kind == N_CONST and d.s != null and d.file != null and not is_runtime_file(d.file) {
push(xs, len(nodes))
push(keys, vis_plain(d.s))
push(nodes, d)
}
i += 1
}
sc_sort(xs, keys)
g_pmc_all = new []pointer
g_pmc_node = new []Node
i = 0
while i < len(xs) {
push(g_pmc_all, keys[xs[i]])
push(g_pmc_node, nodes[xs[i]])
i += 1
}
}
# the const declared as name (as written), or null
function pmc_find(name: pointer) -> Node {
var lo = 0
var hi = len(g_pmc_all) - 1
var found = -1
while lo <= hi {
let mid = (lo + hi) / 2
let m = g_pmc_all[mid]
if (m == name) {
found = mid
hi = mid - 1
} else if reg_str_less(name, m) { hi = mid - 1 } else { lo = mid + 1 }
}
if found < 0 { return null }
return g_pmc_node[found]
}
# an int expression's value into g_pmc_ival; false when it is not one this can work out
function pmc_int(e: Node, depth: int) -> bool {
if e == null or depth > 24 { return false }
if e.kind == E_INT {
g_pmc_ival = e.ival
return true
}
if e.kind == E_UN and (e.s == "-") {
if not pmc_int(e.a, depth + 1) { return false }
g_pmc_ival = 0 - g_pmc_ival
return true
}
if e.kind == E_UN and (e.s == "~") {
if not pmc_int(e.a, depth + 1) { return false }
g_pmc_ival = ~g_pmc_ival
return true
}
if e.kind == E_ID {
let d = pmc_find(e.s)
if d == null or not (d.ty == "int") { return false }
return pmc_int(d.a, depth + 1)
}
if e.kind == E_BIN {
if not pmc_int(e.a, depth + 1) { return false }
let a = g_pmc_ival
if not pmc_int(e.b, depth + 1) { return false }
let b = g_pmc_ival
let op = e.s
if (op == "+") { g_pmc_ival = a + b; return true }
if (op == "-") { g_pmc_ival = a - b; return true }
if (op == "*") { g_pmc_ival = a * b; return true }
if (op == "/") and b != 0 { g_pmc_ival = a / b; return true }
if (op == "%") and b != 0 { g_pmc_ival = a % b; return true }
if (op == "<<") { g_pmc_ival = a << b; return true }
if (op == ">>") { g_pmc_ival = a >> b; return true }
if (op == "&") { g_pmc_ival = a & b; return true }
if (op == "|") { g_pmc_ival = a | b; return true }
if (op == "^") { g_pmc_ival = a ^ b; return true }
}
return false
}
# a float constant's value as text (strtod reads it at run time): a literal, maybe negated, or
# another float constant
function pmc_float(e: Node, depth: int) -> bool {
if e == null or depth > 24 { return false }
if e.kind == E_FLOAT and e.s != null {
g_pmc_fval = e.s
return true
}
if e.kind == E_INT {
g_pmc_fval = itoa(e.ival)
return true
}
if e.kind == E_UN and (e.s == "-") {
if not pmc_float(e.a, depth + 1) { return false }
if g_pmc_fval[0] == '-' { g_pmc_fval = g_pmc_fval[1 .. len(g_pmc_fval)] } else { g_pmc_fval = "-" + g_pmc_fval }
return true
}
if e.kind == E_ID {
let d = pmc_find(e.s)
if d == null or not (d.ty == "float") { return false }
return pmc_float(d.a, depth + 1)
}
return false
}
# every constant a map may name, sorted, first of each name kept
function pmc_collect() -> void {
pmc_index()
g_pmc_names = new []pointer
g_pmc_isf = new []bool
g_pmc_iv = new []int
g_pmc_ftext = new []pointer
var i = 0
while i < len(g_pmc_all) {
let d = g_pmc_node[i]
let nm = g_pmc_all[i]
let first = i == 0 or not (g_pmc_all[i - 1] == nm)
if first and (d.ty == "int") and pmc_int(d.a, 0) {
push(g_pmc_names, nm)
push(g_pmc_isf, false)
push(g_pmc_iv, g_pmc_ival)
push(g_pmc_ftext, "")
} else if first and (d.ty == "float") and pmc_float(d.a, 0) {
push(g_pmc_names, nm)
push(g_pmc_isf, true)
push(g_pmc_iv, 0)
push(g_pmc_ftext, g_pmc_fval)
}
i += 1
}
}
# the index of name in the sorted table, or -1
function pmc_lookup(name: pointer) -> int {
var lo = 0
var hi = len(g_pmc_names) - 1
while lo <= hi {
let mid = (lo + hi) / 2
let m = g_pmc_names[mid]
if (m == name) { return mid }
if reg_str_less(name, m) { hi = mid - 1 } else { lo = mid + 1 }
}
return -1
}
# lres_consts__(), in the runtime's name space
function pm_consts_gen() -> void {
pmc_collect()
let b = buf_new()
buf_puts(b, "function lres_consts__() -> string {\n return \"")
var i = 0
while i < len(g_pmc_names) {
buf_puts(b, g_pmc_names[i])
if g_pmc_isf[i] {
buf_puts(b, " f ")
buf_puts(b, g_pmc_ftext[i])
} else {
buf_puts(b, " i ")
buf_puts(b, itoa(g_pmc_iv[i]))
}
buf_puts(b, ";")
i += 1
}
buf_puts(b, "\"\n}\n")
pm_parse_src(buf_str(b), "runtime/native/lres_consts.ludic", 1, 0)
}