- @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>
182 lines
5.9 KiB
Text
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)
|
|
}
|