compiler: text keys below a row, trf padding and tr's cast (ECS's 26.4 gaps) - a @Text Key in a record nested in a row, or in each item of a list of them, is filled with its derived key <registry>.<row>.<field>.<i>.<field> as a top-level one is, and a @Text []Key a row leaves out takes <...>.0, .1, ... for as many as en.po has (so no .lres spells a key; the manifest and en.po are now read before the parse that fills the rows); field: null is no text; trf / trn's trailing "" literals are padding, not counted against the English's holes; and string(x) of a string or a Key is x itself to the escape analysis, no allocation site, so tr(key) passes arena strict (a template's lone hole still copies); examples/lang/i18n_nested and its feat_case (added, not run); LANGUAGE.md and the changeset; reseeded, bootstrap-cfree fixpoint holds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9a0011c38f
commit
10a394ae6c
12 changed files with 75642 additions and 73902 deletions
|
|
@ -183,29 +183,72 @@ function i18n_has_text(f: Node) -> bool {
|
|||
function i18n_is_key_ty(t: pointer) -> bool { return t != null and (t == "Key") and find_comp(t) == null }
|
||||
# a compiled registry's row (reg_fill): a @Text Key field it gives no value takes its derived key
|
||||
function i18n_fill_row(r: int, key: pointer, rec: Node, comp: Node, kline: int, kcol: int) -> void {
|
||||
if comp == null { return }
|
||||
i18n_fill_rec(rec, comp, `{i18n_prefix(r)}.{key}`, kline, kcol)
|
||||
}
|
||||
# a record at `base` (a row, or a record nested in one): a @Text Key it leaves out takes base.field,
|
||||
# a @Text []Key it leaves out takes base.field.0, .1, ... as many as en.po has; given ones are walked
|
||||
function i18n_fill_rec(rec: Node, comp: Node, base: pointer, kline: int, kcol: int) -> void {
|
||||
if rec == null or comp == null { return }
|
||||
var i = 0
|
||||
while i < len(comp.kids) {
|
||||
let f = comp.kids[i]
|
||||
if f.kind == N_FIELD and i18n_has_text(f) and i18n_is_key_ty(f.ty) and not reg_rec_has(rec, f.s) {
|
||||
let fi = node(E_FINIT)
|
||||
fi.s = f.s
|
||||
fi.file = rec.file
|
||||
fi.line = kline
|
||||
fi.col = kcol
|
||||
let ks = node(E_STR)
|
||||
ks.s = key_marked(KEY_LIT, `{i18n_prefix(r)}.{key}.{f.s}`)
|
||||
ks.ival = KEY_LIT
|
||||
ks.ty = "derived" # the checks know it was not written
|
||||
ks.file = rec.file
|
||||
ks.line = kline
|
||||
ks.col = kcol
|
||||
fi.a = ks
|
||||
push(rec.kids, fi)
|
||||
if f.kind == N_FIELD {
|
||||
let path = `{base}.{f.s}`
|
||||
let fi = i18n_field_init(rec, f.s)
|
||||
if fi != null { i18n_fill_val(fi.a, f.ty, path, kline, kcol) }
|
||||
else if i18n_has_text(f) and i18n_is_key_ty(f.ty) { push(rec.kids, i18n_finit(f.s, i18n_derived(path, rec.file, kline, kcol), rec.file, kline, kcol)) }
|
||||
else if i18n_has_text(f) and i18n_is_keys_ty(f.ty) { i18n_fill_list(rec, f.s, path, kline, kcol) }
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
function i18n_fill_val(e: Node, ty: pointer, path: pointer, kline: int, kcol: int) -> void {
|
||||
if e == null { return }
|
||||
if e.kind == E_NEW and e.a != null and e.a.kind == E_REC { i18n_fill_rec(e.a, find_comp(e.s), path, kline, kcol) }
|
||||
else if e.kind == E_REC and ty != null { i18n_fill_rec(e, find_comp(ty), path, kline, kcol) }
|
||||
else if e.kind == E_LIST {
|
||||
var el: pointer = null
|
||||
if ty != null and is_slice_ty(ty) { el = slice_elem(ty) }
|
||||
var i = 0
|
||||
while i < len(e.kids) {
|
||||
i18n_fill_val(e.kids[i], el, `{path}.{itoa(i)}`, kline, kcol)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
# a []Key the row leaves out: en.po's path.0, path.1, ... while they last (none without an en.po)
|
||||
function i18n_fill_list(rec: Node, name: pointer, path: pointer, kline: int, kcol: int) -> void {
|
||||
if g_i18n_en == null { return }
|
||||
let lst = node(E_LIST)
|
||||
lst.file = rec.file
|
||||
lst.line = kline
|
||||
var i = 0
|
||||
while po_find(g_i18n_en, `{path}.{itoa(i)}`) >= 0 {
|
||||
push(lst.kids, i18n_derived(`{path}.{itoa(i)}`, rec.file, kline, kcol))
|
||||
i += 1
|
||||
}
|
||||
if i > 0 { push(rec.kids, i18n_finit(name, lst, rec.file, kline, kcol)) }
|
||||
}
|
||||
function i18n_is_keys_ty(t: pointer) -> bool { return t != null and is_slice_ty(t) and i18n_is_key_ty(slice_elem(t)) }
|
||||
function i18n_derived(key: pointer, file: pointer, line: int, col: int) -> Node {
|
||||
let ks = node(E_STR)
|
||||
ks.s = key_marked(KEY_LIT, key)
|
||||
ks.ival = KEY_LIT
|
||||
ks.ty = "derived" # the checks know it was not written
|
||||
ks.file = file
|
||||
ks.line = line
|
||||
ks.col = col
|
||||
return ks
|
||||
}
|
||||
function i18n_finit(name: pointer, v: Node, file: pointer, line: int, col: int) -> Node {
|
||||
let fi = node(E_FINIT)
|
||||
fi.s = name
|
||||
fi.file = file
|
||||
fi.line = line
|
||||
fi.col = col
|
||||
fi.a = v
|
||||
return fi
|
||||
}
|
||||
|
||||
# ---- the sites and the key checks ----------------------------------------------------------------
|
||||
function i18n_site(key: pointer, kind: int, file: pointer, line: int, col: int) -> bool {
|
||||
|
|
@ -339,8 +382,11 @@ function i18n_call(n: Node) -> void {
|
|||
perr_at(e.file, e.line, i18n_col(e), `{nm}(kn"{key}", ...): a plural key needs its count - write trn(kn"{key}", n, ...)`)
|
||||
}
|
||||
if nm == "tr" or g_i18n_en == null { return }
|
||||
i18n_holes(key, len(n.kids) - k - 1, nm, e.file, e.line, i18n_col(e))
|
||||
var last = len(n.kids)
|
||||
while last > k + 1 and i18n_is_pad(n.kids[last - 1]) { last -= 1 } # trailing "" fill trf / trn's unused slots
|
||||
i18n_holes(key, last - k - 1, nm, e.file, e.line, i18n_col(e))
|
||||
}
|
||||
function i18n_is_pad(e: Node) -> bool { return e != null and e.kind == E_STR and not i18n_is_key(e) and (e.s == null or len(e.s) == 0) }
|
||||
# a node's column in its file: a template literal's hole is lexed on its own, so its tokens' columns
|
||||
# are the hole's - the file offset says where it really is
|
||||
function i18n_col(n: Node) -> int {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue