compiler: text keys' follow-up (Master's rulings) - a Key in a template literal's hole is an error (the parser marks each hole's string(), TPL_HOLE; write trf(k"...", ...)), trn takes only a plural key kn"..." and tr / trf refuse one (with or without en.po), and a program with no lang line (a package test) uses key literals unchecked and silently - the warning stays for a lang line whose en.po is missing; rejected/i18n_keys gains key_hole (2) and key_rules (3), key_missing's plural moves to trn; LANGUAGE.md and the changeset say so; reseeded, bootstrap-cfree fixpoint holds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 23:46:48 +03:00
parent 66b8e51026
commit 9f9ac4a7ce
13 changed files with 72839 additions and 72164 deletions

View file

@ -100,6 +100,7 @@ function ck_builtin(e: Node, name: pointer) -> pointer {
ck_walk_args(e)
return "string"
}
if (name == "string") and ck_tpl_hole(e) { return ck_hole(e) }
if (name == "string") or (name == "int") or (name == "float") or (name == "fixed") or (name == "long") or (name == "double") {
ck_walk_args(e)
return name
@ -313,3 +314,12 @@ function ck_emit(s: Node) -> void {
i += 1
}
}
# a template literal's {hole}: text, never a key - a Key there would draw its marked name untranslated
function ck_tpl_hole(e: Node) -> bool {
return e.a != null and (e.a.uns == TPL_HOLE or e.a.uns == TPL_COPY) and len(e.kids) == 1
}
function ck_hole(e: Node) -> pointer {
let t = ck_expr(e.kids[0])
if ck_is_key(t) { ck_err("key", e.kids[0], "a Key in a template's {hole}: a key names a text, it is not one - translate it with trf(k\"...\", ...) (tr(key) for no values), or say string(key) outside the template if the marked name is really wanted") }
return "string"
}

View file

@ -225,15 +225,14 @@ function i18n_ks_name(kind: int) -> pointer {
if kind == KS_DATA { return "data" }
return "code"
}
# a key used: in en.po, and a plural one as a plural there. Nothing to check against: said once
# a key used: in en.po, and a plural one as a plural there. A lang line with no en.po: said once;
# no lang line at all (a package test) checks nothing and says nothing
function i18n_use(key: pointer, plural: bool, kind: int, file: pointer, line: int, col: int) -> void {
if not i18n_site(key, kind, file, line, col) { return }
if g_i18n_en == null {
if kind != KS_TEMPLATE and not g_i18n_nopo_warned {
if g_i18n_on and kind != KS_TEMPLATE and not g_i18n_nopo_warned {
g_i18n_nopo_warned = true
var why = "this program's package.ludic has no `lang \"assets/lang\" en` line"
if g_i18n_on { why = `{g_i18n_en_path} is not there` }
pwarn_at(file, line, col, `k"{key}": no en.po to check against - {why}; no key is checked`)
pwarn_at(file, line, col, `k"{key}": no en.po to check against - {g_i18n_en_path} is not there; no key is checked`)
}
return
}
@ -312,7 +311,7 @@ function i18n_walk(n: Node) -> void {
if i18n_is_key(n) and not i18n_is_data_file(n.file) {
i18n_use(i18n_key_text(n), n.ival == KEY_PLURAL, KS_CODE, n.file, n.line, i18n_col(n))
}
if n.kind == E_CALL and n.a != null and n.a.kind == E_ID and g_i18n_en != null { i18n_call(n) }
if n.kind == E_CALL and n.a != null and n.a.kind == E_ID { i18n_call(n) }
i18n_walk(n.a)
i18n_walk(n.b)
i18n_walk(n.c)
@ -322,15 +321,25 @@ function i18n_walk(n: Node) -> void {
i += 1
}
}
# trf(k"...", a, b) / trn(kn"...", n, a): the values after the key against its holes
# tr(k"...") / trf(k"...", a, b) / trn(kn"...", n, a): a plural key only with trn, and trn only with
# one (en.po or not); then the values after the key against its holes
function i18n_call(n: Node) -> void {
let nm = vis_plain(n.a.s)
if not (nm == "trf") and not (nm == "trn") { return }
if not (nm == "tr") and not (nm == "trf") and not (nm == "trn") { return }
var k = 0
while k < len(n.kids) and not i18n_is_key(n.kids[k]) { k += 1 }
if k >= len(n.kids) { return }
let e = n.kids[k]
i18n_holes(i18n_key_text(e), len(n.kids) - k - 1, nm, e.file, e.line, i18n_col(e))
let key = i18n_key_text(e)
let plural = e.ival == KEY_PLURAL
if nm == "trn" and not plural {
perr_at(e.file, e.line, i18n_col(e), `trn(k"{key}", ...): trn counts, so its key is a plural one - write kn"{key}" (and give en.po a msgid_plural)`)
}
if plural and not (nm == "trn") {
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))
}
# 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

View file

@ -248,7 +248,7 @@ function interp_add(acc: Node, part: Node) -> Node {
return mkbin("+", acc, part)
}
function interp_str(e: Node) -> Node { # wrap a hole in string(...)
let c = node(E_CALL); let id = node(E_ID); id.s = "string"; c.a = id; push(c.kids, e); return c
let c = node(E_CALL); let id = node(E_ID); id.s = "string"; id.uns = TPL_HOLE; c.a = id; push(c.kids, e); return c
}
var g_interp_line: int = 1 # the line the interpolated string sits on
var g_interp_pos: int = -1 # the backtick's place in its file
@ -297,6 +297,7 @@ function parse_interp(raw: pointer) -> Node {
return acc
}
const TPL_COPY: int = 91 # on the callee of a template's lone string(hole)
const TPL_HOLE: int = 92 # on the callee of every other string(hole) (a Key is refused there)
# emit E(field: v, ...) — shared by the statement form and the expression form.
# As an expression it yields a cancellable event's cancelled flag (0/1); a

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff