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}