code wiki / _hdl_build / nx_xlate_mt.nx

nx_xlate_mt.nx source

↩ module page · 383 lines · 15112 B

1// nx_xlate_mt.nx -- sovereign DATA-DRIVEN RU<->EN machine-translation engine (the Belarus<->Texas pair). 2// Replaces the 16-word hardcoded nx_mt_ruen stub. The lexicon + phrase table + morphology are DATA loaded 3// from TSV at build time (NOT hardcoded if-else): greedy LONGEST-match over words AND multi-word phrases, 4// Cyrillic-correct case folding (fixes "Привет"=="привет"), ё->е normalization, and Russian inflectional 5// suffix-stripping so inflected surface forms map to their lemma. NO float, deterministic. 6// dir 0 = EN->RU (emit Cyrillic), dir 1 = RU->EN. Unknown tokens pass through verbatim. 7// This is the R2 data-driven lexicon rung; open-domain neural MT stays the long pole. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9const XL_MAGIC_8192: i64 = 8192 10const XL_MAGIC_16384: i64 = 16384 11const XL_MAGIC_2048: i64 = 2048 12 13const XL_MAXENT: i64 = 8192 14const XL_MAXSUF: i64 = 128 15const XL_NORMCAP: i64 = 524288 // normalized-text arena bytes (holds normalized copies of every lexicon side) 16 17// engine handle = *i64 with >=32 slots (caller: sys_mmap(256)) 18const XLE_NENT: i64 = 0 19const XLE_TYPE: i64 = 1 20const XLE_RUOFF: i64 = 2 21const XLE_RULEN: i64 = 3 22const XLE_ENOFF: i64 = 4 23const XLE_ENLEN: i64 = 5 24const XLE_NRUOFF: i64 = 6 25const XLE_NRULEN: i64 = 7 26const XLE_NENOFF: i64 = 8 27const XLE_NENLEN: i64 = 9 28const XLE_NORM: i64 = 10 29const XLE_LEX: i64 = 11 30const XLE_NSUF: i64 = 12 31const XLE_SUFOFF: i64 = 13 32const XLE_SUFLEN: i64 = 14 33const XLE_SUF: i64 = 15 34 35func xl_putb(out: *u8, op: *i64, cap: i64, b: i64) -> i64 { 36 let p: i64 = *op 37 if p < cap { out[p] = b as u8; *op = p + 1 } 38 return 0 39} 40// token char = ASCII letter/digit OR any UTF-8 continuation/lead byte (Cyrillic >= 0x80) 41func xl_is_tok(c: i64) -> i64 { 42 if c >= 48 { if c <= 57 { return 1 } } 43 if c >= 65 { if c <= 90 { return 1 } } 44 if c >= 97 { if c <= 122 { return 1 } } 45 if c >= 128 { return 1 } 46 return 0 47} 48// normalize+append ONE token [s,e) of src to out (ASCII lowercase; Cyrillic uppercase->lowercase; Ё/ё -> е) 49func xl_norm_tok(src: *u8, s: i64, e: i64, out: *u8, op: *i64, cap: i64) -> i64 { 50 var i: i64 = s 51 while i < e { 52 let c: i64 = src[i] as i64 53 if c < 128 { 54 var lc: i64 = c 55 if c >= 65 { if c <= 90 { lc = c + 32 } } 56 xl_putb(out, op, cap, lc) 57 i = i + 1 58 } else { 59 if c == 208 { 60 if i + 1 < e { 61 let d: i64 = src[i+1] as i64 62 var o1: i64 = 208 63 var o2: i64 = d 64 if d == 129 { o2 = 181 } // Ё (D0 81) -> е 65 if d >= 144 { if d <= 159 { o2 = d + 32 } } // А-П (D0 90..9F) -> а-п 66 if d >= 160 { if d <= 175 { o1 = 209; o2 = d - 32 } } // Р-Я (D0 A0..AF) -> р-я (D1 80..8F) 67 xl_putb(out, op, cap, o1); xl_putb(out, op, cap, o2) 68 i = i + 2 69 } else { xl_putb(out, op, cap, c); i = i + 1 } 70 } else { 71 if c == 209 { 72 if i + 1 < e { 73 let d: i64 = src[i+1] as i64 74 var o1: i64 = 209 75 var o2: i64 = d 76 if d == 145 { o1 = 208; o2 = 181 } // ё (D1 91) -> е 77 xl_putb(out, op, cap, o1); xl_putb(out, op, cap, o2) 78 i = i + 2 79 } else { xl_putb(out, op, cap, c); i = i + 1 } 80 } else { 81 xl_putb(out, op, cap, c); i = i + 1 82 } 83 } 84 } 85 } 86 return 0 87} 88// normalize a whole field [s,e) (may hold multiple tokens = a phrase) into out, space-joined 89func xl_norm_phrase(src: *u8, s: i64, e: i64, out: *u8, op: *i64, cap: i64) -> i64 { 90 var i: i64 = s 91 var first: i64 = 1 92 while i < e { 93 while i < e { if xl_is_tok(src[i] as i64) == 1 { break } i = i + 1 } 94 if i >= e { break } 95 let ts: i64 = i 96 while i < e { if xl_is_tok(src[i] as i64) == 1 { i = i + 1 } else { break } } 97 let te: i64 = i 98 if first == 0 { xl_putb(out, op, cap, 32) } 99 first = 0 100 xl_norm_tok(src, ts, te, out, op, cap) 101 } 102 return 0 103} 104// normalize full input into nbuf (space-joined), record each token's start offset into tstart[]; -> normalized length 105func xl_norm_str(src: *u8, n: i64, nbuf: *u8, ncap: i64, tstart: *i64, maxtok: i64, ntok_out: *i64) -> i64 { 106 let op: *i64 = sys_mmap(8) as *i64 107 *op = 0 108 var ntok: i64 = 0 109 var i: i64 = 0 110 var first: i64 = 1 111 while i < n { 112 while i < n { if xl_is_tok(src[i] as i64) == 1 { break } i = i + 1 } 113 if i >= n { break } 114 let s: i64 = i 115 while i < n { if xl_is_tok(src[i] as i64) == 1 { i = i + 1 } else { break } } 116 let e: i64 = i 117 if first == 0 { xl_putb(nbuf, op, ncap, 32) } 118 first = 0 119 if ntok < maxtok { tstart[ntok] = *op } 120 ntok = ntok + 1 121 xl_norm_tok(src, s, e, nbuf, op, ncap) 122 } 123 *ntok_out = ntok 124 let r: i64 = *op 125 sys_munmap(op, 8) 126 return r 127} 128// read one TAB/newline field from buf at *ip; set [*fs,*fe); *term = 9|10|-1; advance *ip; return 1 if read 129func xl_field(buf: *u8, n: i64, ip: *i64, fs: *i64, fe: *i64, term: *i64) -> i64 { 130 var i: i64 = *ip 131 if i >= n { return 0 } 132 *fs = i 133 while i < n { 134 let c: i64 = buf[i] as i64 135 if c == 9 { break } 136 if c == 10 { break } 137 i = i + 1 138 } 139 *fe = i 140 if i < n { *term = buf[i] as i64; i = i + 1 } else { *term = 0 - 1 } 141 *ip = i 142 return 1 143} 144// nbuf[pos..pos+alen) == arena[aoff..aoff+alen) ? 145func xl_meq(nbuf: *u8, pos: i64, nlen: i64, arena: *u8, aoff: i64, alen: i64) -> i64 { 146 if alen <= 0 { return 0 } 147 if pos + alen > nlen { return 0 } 148 var k: i64 = 0 149 while k < alen { if (nbuf[pos+k] as i64) != (arena[aoff+k] as i64) { return 0 } k = k + 1 } 150 return 1 151} 152func xl_ntok_of(arena: *u8, aoff: i64, alen: i64) -> i64 { 153 if alen <= 0 { return 0 } 154 var c: i64 = 1 155 var k: i64 = 0 156 while k < alen { if (arena[aoff+k] as i64) == 32 { c = c + 1 } k = k + 1 } 157 return c 158} 159func xl_tok_end(nlen: i64, tstart: *i64, ntok: i64, t: i64) -> i64 { 160 if t + 1 < ntok { return tstart[t+1] - 1 } 161 return nlen 162} 163// find a WORD-type entry whose normalized source (per dir) exactly equals nbuf[pos..pos+len) 164func xl_find_word(eng: *i64, dir: i64, nbuf: *u8, pos: i64, len: i64) -> i64 { 165 let nent: i64 = eng[XLE_NENT] 166 let t_type: *u8 = eng[XLE_TYPE] as *u8 167 let nru_off: *i64 = eng[XLE_NRUOFF] as *i64 168 let nru_len: *i64 = eng[XLE_NRULEN] as *i64 169 let nen_off: *i64 = eng[XLE_NENOFF] as *i64 170 let nen_len: *i64 = eng[XLE_NENLEN] as *i64 171 let norm: *u8 = eng[XLE_NORM] as *u8 172 if len <= 0 { return 0 - 1 } 173 var ei: i64 = 0 174 while ei < nent { 175 if (t_type[ei] as i64) == 119 { 176 var soff: i64 = nru_off[ei] 177 var slen: i64 = nru_len[ei] 178 if dir == 0 { soff = nen_off[ei]; slen = nen_len[ei] } 179 if slen == len { 180 if xl_meq(nbuf, pos, pos + len, norm, soff, slen) == 1 { return ei } 181 } 182 } 183 ei = ei + 1 184 } 185 return 0 - 1 186} 187// exact word, else strip one inflectional suffix and retry (RU: loaded suffixes; EN: trailing 's') 188func xl_morph_lookup(eng: *i64, dir: i64, nbuf: *u8, pos: i64, tlen: i64) -> i64 { 189 let ex: i64 = xl_find_word(eng, dir, nbuf, pos, tlen) 190 if ex >= 0 { return ex } 191 if dir == 1 { 192 let nsuf: i64 = eng[XLE_NSUF] 193 let suf: *u8 = eng[XLE_SUF] as *u8 194 let suf_off: *i64 = eng[XLE_SUFOFF] as *i64 195 let suf_len: *i64 = eng[XLE_SUFLEN] as *i64 196 var si: i64 = 0 197 while si < nsuf { 198 let sl: i64 = suf_len[si] 199 if sl < tlen { 200 var m: i64 = 1 201 var k: i64 = 0 202 while k < sl { 203 if (nbuf[pos + tlen - sl + k] as i64) != (suf[suf_off[si] + k] as i64) { m = 0; break } 204 k = k + 1 205 } 206 if m == 1 { 207 let w: i64 = xl_find_word(eng, 1, nbuf, pos, tlen - sl) 208 if w >= 0 { return w } 209 } 210 } 211 si = si + 1 212 } 213 } else { 214 if tlen > 2 { 215 if (nbuf[pos + tlen - 1] as i64) == 115 { 216 let w: i64 = xl_find_word(eng, 0, nbuf, pos, tlen - 1) 217 if w >= 0 { return w } 218 } 219 } 220 } 221 return 0 - 1 222} 223// parse lexicon TSV (type\tru\ten\tweight) + morphology TSV (suffix\tnote) into the engine arena 224func xl_build(eng: *i64, lex: *u8, lexn: i64, morph: *u8, morphn: i64) -> i64 { 225 let maxe: i64 = XL_MAXENT 226 let t_type: *u8 = sys_mmap(maxe) 227 let ru_off: *i64 = sys_mmap(maxe * 8) as *i64 228 let ru_len: *i64 = sys_mmap(maxe * 8) as *i64 229 let en_off: *i64 = sys_mmap(maxe * 8) as *i64 230 let en_len: *i64 = sys_mmap(maxe * 8) as *i64 231 let nru_off: *i64 = sys_mmap(maxe * 8) as *i64 232 let nru_len: *i64 = sys_mmap(maxe * 8) as *i64 233 let nen_off: *i64 = sys_mmap(maxe * 8) as *i64 234 let nen_len: *i64 = sys_mmap(maxe * 8) as *i64 235 let norm: *u8 = sys_mmap(XL_NORMCAP) 236 let np: *i64 = sys_mmap(8) as *i64 237 let fs: *i64 = sys_mmap(8) as *i64 238 let fe: *i64 = sys_mmap(8) as *i64 239 let term: *i64 = sys_mmap(8) as *i64 240 let ip: *i64 = sys_mmap(8) as *i64 241 *np = 0 242 *ip = 0 243 var nent: i64 = 0 244 while nent < maxe { 245 if xl_field(lex, lexn, ip, fs, fe, term) == 0 { break } 246 let tlen0: i64 = *fe - *fs 247 var ty: i64 = 119 248 if tlen0 > 0 { ty = lex[*fs] as i64 } 249 if (*term) != 9 { continue } 250 if xl_field(lex, lexn, ip, fs, fe, term) == 0 { break } 251 let ru_s: i64 = *fs 252 let ru_e: i64 = *fe 253 if (*term) != 9 { continue } 254 if xl_field(lex, lexn, ip, fs, fe, term) == 0 { break } 255 let en_s: i64 = *fs 256 let en_e: i64 = *fe 257 if (*term) == 9 { xl_field(lex, lexn, ip, fs, fe, term) } 258 if en_e <= en_s { continue } 259 if ru_e <= ru_s { continue } 260 t_type[nent] = ty as u8 261 ru_off[nent] = ru_s 262 ru_len[nent] = ru_e - ru_s 263 en_off[nent] = en_s 264 en_len[nent] = en_e - en_s 265 nru_off[nent] = *np 266 xl_norm_phrase(lex, ru_s, ru_e, norm, np, XL_NORMCAP) 267 nru_len[nent] = *np - nru_off[nent] 268 nen_off[nent] = *np 269 xl_norm_phrase(lex, en_s, en_e, norm, np, XL_NORMCAP) 270 nen_len[nent] = *np - nen_off[nent] 271 nent = nent + 1 272 } 273 let suf: *u8 = sys_mmap(XL_MAGIC_8192) 274 let suf_off: *i64 = sys_mmap(XL_MAXSUF * 8) as *i64 275 let suf_len: *i64 = sys_mmap(XL_MAXSUF * 8) as *i64 276 let sp: *i64 = sys_mmap(8) as *i64 277 *sp = 0 278 *ip = 0 279 var nsuf: i64 = 0 280 while nsuf < XL_MAXSUF { 281 if xl_field(morph, morphn, ip, fs, fe, term) == 0 { break } 282 let sl: i64 = *fe - *fs 283 if sl > 0 { 284 suf_off[nsuf] = *sp 285 var k: i64 = 0 286 while k < sl { suf[*sp] = morph[*fs + k]; *sp = *sp + 1; k = k + 1 } 287 suf_len[nsuf] = sl 288 nsuf = nsuf + 1 289 } 290 if (*term) == 9 { xl_field(morph, morphn, ip, fs, fe, term) } 291 } 292 eng[XLE_NENT] = nent 293 eng[XLE_TYPE] = t_type as i64 294 eng[XLE_RUOFF] = ru_off as i64 295 eng[XLE_RULEN] = ru_len as i64 296 eng[XLE_ENOFF] = en_off as i64 297 eng[XLE_ENLEN] = en_len as i64 298 eng[XLE_NRUOFF] = nru_off as i64 299 eng[XLE_NRULEN] = nru_len as i64 300 eng[XLE_NENOFF] = nen_off as i64 301 eng[XLE_NENLEN] = nen_len as i64 302 eng[XLE_NORM] = norm as i64 303 eng[XLE_LEX] = lex as i64 304 eng[XLE_NSUF] = nsuf 305 eng[XLE_SUFOFF] = suf_off as i64 306 eng[XLE_SUFLEN] = suf_len as i64 307 eng[XLE_SUF] = suf as i64 308 sys_munmap(np, 8); sys_munmap(fs, 8); sys_munmap(fe, 8); sys_munmap(term, 8); sys_munmap(ip, 8); sys_munmap(sp, 8) 309 return nent 310} 311// translate src[0..n) dir(0=EN->RU,1=RU->EN) into dst; greedy longest phrase/word match + morphology; -> dst length 312func xl_translate(eng: *i64, src: *u8, n: i64, dir: i64, dst: *u8, cap: i64) -> i64 { 313 let nent: i64 = eng[XLE_NENT] 314 let ru_off: *i64 = eng[XLE_RUOFF] as *i64 315 let ru_len: *i64 = eng[XLE_RULEN] as *i64 316 let en_off: *i64 = eng[XLE_ENOFF] as *i64 317 let en_len: *i64 = eng[XLE_ENLEN] as *i64 318 let nru_off: *i64 = eng[XLE_NRUOFF] as *i64 319 let nru_len: *i64 = eng[XLE_NRULEN] as *i64 320 let nen_off: *i64 = eng[XLE_NENOFF] as *i64 321 let nen_len: *i64 = eng[XLE_NENLEN] as *i64 322 let norm: *u8 = eng[XLE_NORM] as *u8 323 let lex: *u8 = eng[XLE_LEX] as *u8 324 let nbuf: *u8 = sys_mmap(XL_MAGIC_16384) 325 let tstart: *i64 = sys_mmap(XL_MAGIC_2048 * 8) as *i64 326 let ntokp: *i64 = sys_mmap(8) as *i64 327 let op: *i64 = sys_mmap(8) as *i64 328 let nlen: i64 = xl_norm_str(src, n, nbuf, XL_MAGIC_16384, tstart, XL_MAGIC_2048, ntokp) 329 let ntok: i64 = *ntokp 330 *op = 0 331 var first: i64 = 1 332 var t: i64 = 0 333 while t < ntok { 334 let pos: i64 = tstart[t] 335 var best: i64 = 0 - 1 336 var best_bytes: i64 = 0 - 1 337 var best_toks: i64 = 0 338 var ei: i64 = 0 339 while ei < nent { 340 var soff: i64 = nru_off[ei] 341 var slen: i64 = nru_len[ei] 342 if dir == 0 { soff = nen_off[ei]; slen = nen_len[ei] } 343 if slen > best_bytes { 344 if xl_meq(nbuf, pos, nlen, norm, soff, slen) == 1 { 345 var okb: i64 = 0 346 if pos + slen == nlen { okb = 1 } else { if (nbuf[pos+slen] as i64) == 32 { okb = 1 } } 347 if okb == 1 { best = ei; best_bytes = slen; best_toks = xl_ntok_of(norm, soff, slen) } 348 } 349 } 350 ei = ei + 1 351 } 352 if best >= 0 { 353 var ooff: i64 = en_off[best] 354 var olen: i64 = en_len[best] 355 if dir == 0 { ooff = ru_off[best]; olen = ru_len[best] } 356 if first == 0 { xl_putb(dst, op, cap, 32) } 357 first = 0 358 var k: i64 = 0 359 while k < olen { xl_putb(dst, op, cap, lex[ooff+k] as i64); k = k + 1 } 360 t = t + best_toks 361 } else { 362 let tend: i64 = xl_tok_end(nlen, tstart, ntok, t) 363 let tlen: i64 = tend - pos 364 let m: i64 = xl_morph_lookup(eng, dir, nbuf, pos, tlen) 365 if first == 0 { xl_putb(dst, op, cap, 32) } 366 first = 0 367 if m >= 0 { 368 var ooff2: i64 = en_off[m] 369 var olen2: i64 = en_len[m] 370 if dir == 0 { ooff2 = ru_off[m]; olen2 = ru_len[m] } 371 var k2: i64 = 0 372 while k2 < olen2 { xl_putb(dst, op, cap, lex[ooff2+k2] as i64); k2 = k2 + 1 } 373 } else { 374 var k3: i64 = 0 375 while k3 < tlen { xl_putb(dst, op, cap, nbuf[pos+k3] as i64); k3 = k3 + 1 } 376 } 377 t = t + 1 378 } 379 } 380 let r: i64 = *op 381 sys_munmap(nbuf, XL_MAGIC_16384); sys_munmap(tstart, XL_MAGIC_2048 * 8); sys_munmap(ntokp, 8); sys_munmap(op, 8) 382 return r 383}