chunked tables: a key is unique across its map and the slot's own, not interned - a slot keeps row i's key in its own buffer i, rewritten when the slot is refilled (interning ~92k map-unique keys as a player walks would fill the bounded intern table and keep them all); ludicc --check refuses a key written in two of a map's chunk files, naming both; reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0634116d59
commit
49e32f9b47
11 changed files with 75842 additions and 74646 deletions
|
|
@ -43,6 +43,9 @@ property LresTree {
|
|||
fval: []float = new []float # a float (an int's value too)
|
||||
sval: []string = new []string # a string, a name, a fn's name (interned)
|
||||
name: []string = new []string # an entry's key, a field's name ("" in a list)
|
||||
local: bool = false # a chunk's file: an entry's key is left in the file (koff, klen), never interned
|
||||
koff: []int = new []int # per entry, when local: where its key starts in buf ...
|
||||
klen: []int = new []int # ... and how long it is
|
||||
kid: []int = new []int # the first child of a record or a list, -1
|
||||
nxt: []int = new []int # the next sibling, -1
|
||||
line: []int = new []int
|
||||
|
|
@ -64,6 +67,42 @@ property LresTree {
|
|||
cfv: []float = new []float
|
||||
}
|
||||
|
||||
# a chunk slot's own keys: a chunked table's keys are unique across a map (t0 .. t91842), so interning
|
||||
# them would fill the intern table as a player walks and keep every one for good. Row i's key lives
|
||||
# in buffer i, rewritten in place when the slot is refilled and grown only past the longest key it held.
|
||||
property LresKeys {
|
||||
bufs: []pointer = new []pointer
|
||||
caps: []int = new []int
|
||||
}
|
||||
# entry e's key as row i of a slot: in the slot's own buffer, or interned for a whole-map table (ks null)
|
||||
@alloc_ok("a slot's key buffer past the most rows or the longest key it has held (high water)")
|
||||
function lres_key_of(t: LresTree, e: int, ks: LresKeys, i: int) -> string {
|
||||
if ks == null or not t.local { return t.name[e] }
|
||||
let n = t.klen[e]
|
||||
if i >= len(ks.bufs) {
|
||||
var c = 16
|
||||
while c < n + 1 { c = c * 2 }
|
||||
push(ks.bufs, bytes(c))
|
||||
push(ks.caps, c)
|
||||
} else if ks.caps[i] < n + 1 {
|
||||
var c = ks.caps[i] * 2
|
||||
while c < n + 1 { c = c * 2 }
|
||||
ks.bufs[i] = resize(ks.bufs[i], c)
|
||||
ks.caps[i] = c
|
||||
}
|
||||
let kb = ks.bufs[i]
|
||||
let buf = t.buf
|
||||
let a = t.koff[e]
|
||||
var j = 0
|
||||
while j < n {
|
||||
kb[j] = buf[a + j]
|
||||
j += 1
|
||||
}
|
||||
kb[n] = 0
|
||||
let k: string = kb
|
||||
return k
|
||||
}
|
||||
|
||||
# a key -> row index table over interned keys: open addressing, rebuilt in place on every load
|
||||
property LresHash {
|
||||
slot: []int = new []int # a row's index + 1; 0 is empty
|
||||
|
|
@ -203,6 +242,8 @@ function lres_node(t: LresTree, kind: int, line: int, col: int) -> int {
|
|||
t.nxt[i] = -1
|
||||
t.line[i] = line
|
||||
t.col[i] = col
|
||||
t.koff[i] = 0
|
||||
t.klen[i] = 0
|
||||
} else {
|
||||
push(t.kind, kind)
|
||||
push(t.ival, 0)
|
||||
|
|
@ -213,6 +254,8 @@ function lres_node(t: LresTree, kind: int, line: int, col: int) -> int {
|
|||
push(t.nxt, -1)
|
||||
push(t.line, line)
|
||||
push(t.col, col)
|
||||
push(t.koff, 0)
|
||||
push(t.klen, 0)
|
||||
}
|
||||
t.nn = i + 1
|
||||
return i
|
||||
|
|
@ -267,15 +310,21 @@ function lres_intern(t: LresTree, a: int, b: int) -> string {
|
|||
return intern(sb)
|
||||
}
|
||||
function lres_ident(t: LresTree) -> string {
|
||||
let a = lres_ident_at(t)
|
||||
if a < 0 { return "" }
|
||||
return lres_intern(t, a, t.p)
|
||||
}
|
||||
# a name's first byte, the cursor left past it; -1 (and the tree bad) when there is none
|
||||
function lres_ident_at(t: LresTree) -> int {
|
||||
let c = lres_ch(t)
|
||||
if not lres_is_alpha(c) {
|
||||
lres_fail(t, "a name was expected here")
|
||||
return ""
|
||||
return -1
|
||||
}
|
||||
let buf = t.buf
|
||||
let a = t.p
|
||||
while t.p < t.n and (lres_is_alpha(buf[t.p]) or lres_is_digit(buf[t.p])) { t.p += 1 }
|
||||
return lres_intern(t, a, t.p)
|
||||
return a
|
||||
}
|
||||
function lres_link(t: LresTree, parent: int, last: int, k: int) -> void {
|
||||
if last < 0 { t.kid[parent] = k } else { t.nxt[last] = k }
|
||||
|
|
@ -291,16 +340,22 @@ function lres_parse(t: LresTree) -> void {
|
|||
if t.p >= t.n { return }
|
||||
let line = t.ln
|
||||
let col = t.p - t.ls + 1
|
||||
let key = lres_ident(t)
|
||||
let ka = lres_ident_at(t)
|
||||
if t.bad { return }
|
||||
let kb = t.p
|
||||
var key = ""
|
||||
if not t.local { key = lres_intern(t, ka, kb) }
|
||||
lres_skip(t)
|
||||
if lres_ch(t) != '{' {
|
||||
if t.local { key = lres_intern(t, ka, kb) } # a wrong file only: its message names the key
|
||||
lres_fail(t, `the entry {key} is its key and a record: {key} {{ field: value }}`)
|
||||
return
|
||||
}
|
||||
let e = lres_record(t)
|
||||
if t.bad { return }
|
||||
t.name[e] = key
|
||||
t.koff[e] = ka
|
||||
t.klen[e] = kb - ka
|
||||
t.line[e] = line
|
||||
t.col[e] = col
|
||||
if last < 0 { t.top = e } else { t.nxt[last] = e }
|
||||
|
|
@ -614,7 +669,9 @@ function lres_no_fn(t: LresTree, n: int, ty: string) -> void {
|
|||
lres_fail_node(t, n, `there is no function {t.sval[n]} of type {ty}`)
|
||||
}
|
||||
function lres_dup(t: LresTree, n: int) -> void {
|
||||
lres_fail_node(t, n, `the key {t.name[n]} is written twice`)
|
||||
var key = t.name[n]
|
||||
if t.local { key = lres_intern(t, t.koff[n], t.koff[n] + t.klen[n]) } # a wrong file only
|
||||
lres_fail_node(t, n, `the key {key} is written twice`)
|
||||
}
|
||||
|
||||
# ---- the program's constants, by name ------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue