ludic.i18n: inside a bracket an argument's bytes ride raw - trf escapes a 27 or 31 only outside brackets, and the decoder unescapes only there, so a bracketed line carrying a key with holes (Ada's brief: its legend line's escaped arguments) keeps its own escapes whichever way it was put in (a game's txt_key raw, or trf). 81e1f7d8 unescaped inside the bracket and split the legend's arguments out of the brief. nest_test holds a key whose hole holds a key with two holes: bare, bracketed raw, bracketed through trf, and escaped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-30 02:00:20 +03:00
parent 81e1f7d8e6
commit 986fba3586
3 changed files with 35 additions and 6 deletions

View file

@ -63,16 +63,23 @@ function kx_sep(buf: []byte, at: int) -> int {
buf[at] = I18N_SEP
return at + 1
}
# an argument's bytes, a 27 before each 27 or 31 of its own, so a key with holes can be one
# an argument's bytes, a 27 before each 27 or 31 of its own outside any bracket (a bracketed line's
# bytes are its own and ride raw, 29 .. 30 delimit it), so a key with holes can be one
function kx_len(s: string) -> int {
var n = len(s)
for i in 0 .. len(s) { if s[i] == I18N_SEP or s[i] == I18N_ESC { n += 1 } }
var d = 0
for i in 0 .. len(s) {
d = kx_depth(d, s[i])
if d == 0 and (s[i] == I18N_SEP or s[i] == I18N_ESC) { n += 1 }
}
return n
}
function kx_arg(buf: []byte, at: int, s: string) -> int {
var j = at
var d = 0
for i in 0 .. len(s) {
if s[i] == I18N_SEP or s[i] == I18N_ESC {
d = kx_depth(d, s[i])
if d == 0 and (s[i] == I18N_SEP or s[i] == I18N_ESC) {
buf[j] = I18N_ESC
j += 1
}
@ -81,6 +88,12 @@ function kx_arg(buf: []byte, at: int, s: string) -> int {
}
return j
}
# how deep in brackets a byte leaves the reader
function kx_depth(d: int, c: int) -> int {
if c == I18N_OPEN { return d + 1 }
if c == I18N_CLOSE and d > 0 { return d - 1 }
return d
}
function kx_put(buf: []byte, at: int, s: string) -> int {
for i in 0 .. len(s) { buf[at + i] = s[i] }
return at + len(s)