thyscript 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/bin/thy-node.js +332 -0
  2. package/bin/thy.js +828 -0
  3. package/bin/thys.js +25 -0
  4. package/examples/calculator.thy +274 -0
  5. package/examples/calculator.thy.js +628 -0
  6. package/examples/hello.thy +34 -0
  7. package/examples/login.thy +382 -0
  8. package/examples/login.thy.js +710 -0
  9. package/package.json +54 -0
  10. package/scripts/_class.js +18 -0
  11. package/scripts/_class2.js +11 -0
  12. package/scripts/_class3.js +21 -0
  13. package/scripts/_dstruct.js +20 -0
  14. package/scripts/_dstruct2.js +24 -0
  15. package/scripts/_scaffold.js +16 -0
  16. package/scripts/_scaffold2.js +16 -0
  17. package/scripts/_scaffold3.js +26 -0
  18. package/scripts/_tpl_compile.js +13 -0
  19. package/scripts/_tpl_test.js +9 -0
  20. package/scripts/_typeof.js +26 -0
  21. package/scripts/_typeof2.js +25 -0
  22. package/scripts/patch_genmethod.js +44 -0
  23. package/scripts/smoke_test.js +167 -0
  24. package/scripts/smoke_test2.js +151 -0
  25. package/scripts/test_control_panel.js +58 -0
  26. package/src/compiler/CodeGenerator.js +1449 -0
  27. package/src/compiler/JITCompiler.js +325 -0
  28. package/src/database/DatabaseEngine.js +931 -0
  29. package/src/errors/ErrorDetector.js +305 -0
  30. package/src/errors/ErrorMessages.js +497 -0
  31. package/src/index.js +169 -0
  32. package/src/lexer/Lexer.js +342 -0
  33. package/src/lexer/TokenTypes.js +1234 -0
  34. package/src/memory/MemoryManager.js +283 -0
  35. package/src/parser/AST.js +606 -0
  36. package/src/parser/Parser.js +1942 -0
  37. package/src/runtime/ThyRuntime.js +265 -0
  38. package/src/runtime/stdlib/ArrayLib.js +207 -0
  39. package/src/runtime/stdlib/FileLib.js +168 -0
  40. package/src/runtime/stdlib/MathLib.js +411 -0
  41. package/src/runtime/stdlib/StringLib.js +387 -0
  42. package/src/server/ControlPanel.js +1188 -0
  43. package/src/server/HTMLCSSEngine.js +866 -0
  44. package/src/server/SecurityEngine.js +714 -0
  45. package/src/server/ServerEngine.js +738 -0
  46. package/tests/test1_math.thy +82 -0
  47. package/tests/test2_oop.thy +133 -0
  48. package/tests/test3_server.thy +143 -0
  49. package/tests/test4_functions.thy +159 -0
@@ -0,0 +1,1449 @@
1
+ 'use strict';
2
+ /**
3
+ * ThyScript CodeGenerator
4
+ * Translates a ThyScript AST into optimized, runnable Node.js JavaScript.
5
+ *
6
+ * Every compiled file receives:
7
+ * - 'use strict' directive
8
+ * - Full __thy_stdlib object (all built-ins)
9
+ * - All necessary require/import stubs
10
+ * - Optimised code for every ThyScript AST node type
11
+ */
12
+
13
+ class CodeGenerator {
14
+ constructor(options = {}) {
15
+ this.options = Object.assign({
16
+ optimise: true,
17
+ sourceMap: false,
18
+ minify: false,
19
+ targetES: 2022,
20
+ }, options);
21
+
22
+ /** Indentation depth */
23
+ this._indent = 0;
24
+ /** Unique counter for generated identifiers */
25
+ this._uid = 0;
26
+ /** Whether express app has been declared in this file */
27
+ this._expressAppDeclared = false;
28
+ /** Whether readline interface has been declared */
29
+ this._readlineDeclared = false;
30
+ /** Whether tf (TensorFlow.js) has been declared */
31
+ this._tfDeclared = false;
32
+ /** Whether Matter.js has been declared */
33
+ this._matterDeclared = false;
34
+ /** Collected top-level require statements */
35
+ this._requires = new Set();
36
+ /** Lines of the header section (stdlib + requires) */
37
+ this._headerLines = [];
38
+ /** Lines of the body section (compiled statements) */
39
+ this._bodyLines = [];
40
+ }
41
+
42
+ // ─── helpers ────────────────────────────────────────────────────────────────
43
+
44
+ _uid_() { return `__thy_${this._uid++}`; }
45
+
46
+ _tab() { return ' '.repeat(this._indent); }
47
+
48
+ _emit(line) {
49
+ this._bodyLines.push(this._tab() + line);
50
+ }
51
+
52
+ _emitRaw(line) {
53
+ this._bodyLines.push(line);
54
+ }
55
+
56
+ _push() { this._indent++; }
57
+ _pop() { if (this._indent > 0) this._indent--; }
58
+
59
+ _needRequire(pkg) { this._requires.add(pkg); }
60
+
61
+ _escStr(s) {
62
+ return String(s)
63
+ .replace(/\\/g, '\\\\')
64
+ .replace(/'/g, "\\'")
65
+ .replace(/\n/g, '\\n')
66
+ .replace(/\r/g, '\\r')
67
+ .replace(/\t/g, '\\t');
68
+ }
69
+
70
+ // ─── stdlib header ───────────────────────────────────────────────────────────
71
+
72
+ _buildStdlib() {
73
+ return `
74
+ // ═══════════════════════════════════════════════════════════════
75
+ // ThyScript Standard Library – __thy_stdlib
76
+ // Auto-generated by ThyScript CodeGenerator
77
+ // ═══════════════════════════════════════════════════════════════
78
+ const __thy_stdlib = (() => {
79
+ 'use strict';
80
+ const _fs = require('fs');
81
+ const _path = require('path');
82
+ const _os = require('os');
83
+ const _crypto = require('crypto');
84
+
85
+ // ── math ──────────────────────────────────────────────────────
86
+ const math = {
87
+ pi: Math.PI,
88
+ e: Math.E,
89
+ inf: Infinity,
90
+ nan: NaN,
91
+ epsilon: Number.EPSILON,
92
+ abs: (x) => Math.abs(x),
93
+ sqrt: (x) => Math.sqrt(x),
94
+ pow: (x, y) => Math.pow(x, y),
95
+ floor: (x) => Math.floor(x),
96
+ ceil: (x) => Math.ceil(x),
97
+ round: (x) => Math.round(x),
98
+ sin: (x) => Math.sin(x),
99
+ cos: (x) => Math.cos(x),
100
+ tan: (x) => Math.tan(x),
101
+ asin: (x) => Math.asin(x),
102
+ acos: (x) => Math.acos(x),
103
+ atan: (x) => Math.atan(x),
104
+ atan2: (y, x) => Math.atan2(y, x),
105
+ log: (x) => Math.log(x),
106
+ log2: (x) => Math.log2(x),
107
+ log10: (x) => Math.log10(x),
108
+ exp: (x) => Math.exp(x),
109
+ random: () => Math.random(),
110
+ min: (...args) => Math.min(...args),
111
+ max: (...args) => Math.max(...args),
112
+ clamp: (v, lo, hi) => Math.max(lo, Math.min(hi, v)),
113
+ lerp: (a, b, t) => a + (b - a) * t,
114
+ hypot: (...args) => Math.hypot(...args),
115
+ sign: (x) => Math.sign(x),
116
+ trunc: (x) => Math.trunc(x),
117
+ cbrt: (x) => Math.cbrt(x),
118
+ // Fast integer math via typed arrays
119
+ imul: (a, b) => Math.imul(a, b),
120
+ fround: (x) => Math.fround(x),
121
+ clz32: (x) => Math.clz32(x),
122
+ };
123
+
124
+ // Fast-math helpers using Float64Array for type stability
125
+ const _buf64 = new Float64Array(1);
126
+ const _buf32 = new Float32Array(1);
127
+ const fastFloor = (x) => { _buf64[0] = x; return x | 0; };
128
+ const fastAbs = (x) => x < 0 ? -x : x;
129
+ const fastMod = (a, b) => a - b * ((a / b) | 0);
130
+
131
+ // ── vectors ───────────────────────────────────────────────────
132
+ const vec = {
133
+ add: (a, b) => a.map((v, i) => v + b[i]),
134
+ sub: (a, b) => a.map((v, i) => v - b[i]),
135
+ scale: (a, s) => a.map(v => v * s),
136
+ dot: (a, b) => a.reduce((acc, v, i) => acc + v * b[i], 0),
137
+ cross3: (a, b) => [
138
+ a[1]*b[2] - a[2]*b[1],
139
+ a[2]*b[0] - a[0]*b[2],
140
+ a[0]*b[1] - a[1]*b[0],
141
+ ],
142
+ length: (a) => Math.hypot(...a),
143
+ normalize: (a) => { const len = Math.hypot(...a) || 1; return a.map(v => v / len); },
144
+ distance: (a, b) => Math.hypot(...a.map((v, i) => v - b[i])),
145
+ };
146
+
147
+ // ── string helpers ────────────────────────────────────────────
148
+ const str = {
149
+ upper: (s) => String(s).toUpperCase(),
150
+ lower: (s) => String(s).toLowerCase(),
151
+ trim: (s) => String(s).trim(),
152
+ split: (s, sep) => String(s).split(sep),
153
+ replace: (s, pat, rep) => String(s).replace(pat, rep),
154
+ includes:(s, sub) => String(s).includes(sub),
155
+ startsWith:(s, pre) => String(s).startsWith(pre),
156
+ endsWith: (s, suf) => String(s).endsWith(suf),
157
+ repeat: (s, n) => String(s).repeat(n),
158
+ pad: (s, n, ch = ' ') => String(s).padStart(n, ch),
159
+ padEnd: (s, n, ch = ' ') => String(s).padEnd(n, ch),
160
+ indexOf: (s, sub) => String(s).indexOf(sub),
161
+ slice: (s, start, end) => String(s).slice(start, end),
162
+ charCode:(s, i = 0) => String(s).charCodeAt(i),
163
+ fromCode:(n) => String.fromCharCode(n),
164
+ reverse: (s) => String(s).split('').reverse().join(''),
165
+ count: (s, sub) => { let c = 0, i = 0; while ((i = s.indexOf(sub, i)) !== -1) { c++; i++; } return c; },
166
+ format: (tpl, ...args) => tpl.replace(/{(\\d+)}/g, (_, i) => args[i] ?? ''),
167
+ escape: (s) => String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'),
168
+ };
169
+
170
+ // ── array helpers ─────────────────────────────────────────────
171
+ const arr = {
172
+ map: (a, fn) => Array.from(a).map(fn),
173
+ filter: (a, fn) => Array.from(a).filter(fn),
174
+ reduce: (a, fn, init) => Array.from(a).reduce(fn, init),
175
+ find: (a, fn) => Array.from(a).find(fn),
176
+ findIdx: (a, fn) => Array.from(a).findIndex(fn),
177
+ sort: (a, fn) => [...a].sort(fn),
178
+ flat: (a, d = 1) => Array.from(a).flat(d),
179
+ flatMap: (a, fn) => Array.from(a).flatMap(fn),
180
+ unique: (a) => [...new Set(a)],
181
+ zip: (...arrs) => arrs[0].map((_, i) => arrs.map(a => a[i])),
182
+ chunk: (a, n) => { const res = []; for (let i = 0; i < a.length; i += n) res.push(a.slice(i, i+n)); return res; },
183
+ shuffle: (a) => { const r = [...a]; for (let i = r.length-1; i > 0; i--) { const j = Math.floor(Math.random()*(i+1)); [r[i],r[j]]=[r[j],r[i]]; } return r; },
184
+ range: (start, end, step = 1) => { const r = []; for (let i = start; i < end; i += step) r.push(i); return r; },
185
+ sum: (a) => a.reduce((acc, v) => acc + v, 0),
186
+ avg: (a) => a.reduce((acc, v) => acc + v, 0) / a.length,
187
+ min: (a) => Math.min(...a),
188
+ max: (a) => Math.max(...a),
189
+ includes:(a, v) => Array.from(a).includes(v),
190
+ count: (a, v) => Array.from(a).filter(x => x === v).length,
191
+ first: (a) => a[0],
192
+ last: (a) => a[a.length - 1],
193
+ take: (a, n) => a.slice(0, n),
194
+ drop: (a, n) => a.slice(n),
195
+ reverse: (a) => [...a].reverse(),
196
+ };
197
+
198
+ // ── object helpers ────────────────────────────────────────────
199
+ const obj = {
200
+ keys: (o) => Object.keys(o),
201
+ values: (o) => Object.values(o),
202
+ entries: (o) => Object.entries(o),
203
+ merge: (...os) => Object.assign({}, ...os),
204
+ clone: (o) => JSON.parse(JSON.stringify(o)),
205
+ pick: (o, keys) => keys.reduce((acc, k) => { if (k in o) acc[k] = o[k]; return acc; }, {}),
206
+ omit: (o, keys) => { const r = {...o}; keys.forEach(k => delete r[k]); return r; },
207
+ has: (o, k) => Object.prototype.hasOwnProperty.call(o, k),
208
+ freeze: (o) => Object.freeze(o),
209
+ seal: (o) => Object.seal(o),
210
+ fromEntries: (e) => Object.fromEntries(e),
211
+ assign: (target, ...sources) => Object.assign(target, ...sources),
212
+ flatten: (o, prefix = '') => {
213
+ return Object.keys(o).reduce((acc, k) => {
214
+ const key = prefix ? prefix + '.' + k : k;
215
+ if (typeof o[k] === 'object' && o[k] !== null && !Array.isArray(o[k]))
216
+ Object.assign(acc, obj.flatten(o[k], key));
217
+ else acc[key] = o[k];
218
+ return acc;
219
+ }, {});
220
+ },
221
+ };
222
+
223
+ // ── I/O helpers ───────────────────────────────────────────────
224
+ const io = {
225
+ print: (...args) => { process.stdout.write(args.map(a => __thy_stringify(a)).join(' ') + '\\n'); },
226
+ printErr:(...args) => { process.stderr.write(args.map(a => __thy_stringify(a)).join(' ') + '\\n'); },
227
+ write: (s) => { process.stdout.write(String(s)); },
228
+ read: (fp) => _fs.readFileSync(fp, 'utf8'),
229
+ readLines:(fp) => _fs.readFileSync(fp, 'utf8').split('\\n'),
230
+ write_: (fp, data) => _fs.writeFileSync(fp, data, 'utf8'),
231
+ append: (fp, data) => _fs.appendFileSync(fp, data, 'utf8'),
232
+ exists: (fp) => _fs.existsSync(fp),
233
+ mkdir: (fp) => _fs.mkdirSync(fp, { recursive: true }),
234
+ readdir: (fp) => _fs.readdirSync(fp),
235
+ rm: (fp) => _fs.rmSync(fp, { recursive: true, force: true }),
236
+ stat: (fp) => _fs.statSync(fp),
237
+ isFile: (fp) => _fs.existsSync(fp) && _fs.statSync(fp).isFile(),
238
+ isDir: (fp) => _fs.existsSync(fp) && _fs.statSync(fp).isDirectory(),
239
+ cwd: () => process.cwd(),
240
+ home: () => _os.homedir(),
241
+ tempDir: () => _os.tmpdir(),
242
+ sep: _path.sep,
243
+ join: (...parts) => _path.join(...parts),
244
+ resolve: (...parts) => _path.resolve(...parts),
245
+ dirname: (p) => _path.dirname(p),
246
+ basename:(p, ext) => _path.basename(p, ext),
247
+ ext: (p) => _path.extname(p),
248
+ };
249
+
250
+ // ── crypto helpers ────────────────────────────────────────────
251
+ const security = {
252
+ hash: (data, alg = 'sha256') => _crypto.createHash(alg).update(String(data)).digest('hex'),
253
+ hmac: (key, data, alg = 'sha256') => _crypto.createHmac(alg, key).update(String(data)).digest('hex'),
254
+ random: (n = 16) => _crypto.randomBytes(n).toString('hex'),
255
+ uuid: () => _crypto.randomUUID ? _crypto.randomUUID() : _crypto.randomBytes(16).toString('hex').replace(/(........)(....)(....)(....)(............)/, '$1-$2-$3-$4-$5'),
256
+ encrypt: (text, key) => {
257
+ const iv = _crypto.randomBytes(16);
258
+ const k = _crypto.createHash('sha256').update(key).digest();
259
+ const c = _crypto.createCipheriv('aes-256-cbc', k, iv);
260
+ return iv.toString('hex') + ':' + Buffer.concat([c.update(text), c.final()]).toString('hex');
261
+ },
262
+ decrypt: (enc, key) => {
263
+ const [ivHex, data] = enc.split(':');
264
+ const iv = Buffer.from(ivHex, 'hex');
265
+ const k = _crypto.createHash('sha256').update(key).digest();
266
+ const d = _crypto.createDecipheriv('aes-256-cbc', k, iv);
267
+ return Buffer.concat([d.update(Buffer.from(data, 'hex')), d.final()]).toString();
268
+ },
269
+ base64encode: (s) => Buffer.from(String(s)).toString('base64'),
270
+ base64decode: (s) => Buffer.from(String(s), 'base64').toString(),
271
+ };
272
+
273
+ // ── type helpers ──────────────────────────────────────────────
274
+ const types = {
275
+ isNum: (v) => typeof v === 'number' && !isNaN(v),
276
+ isStr: (v) => typeof v === 'string',
277
+ isBool: (v) => typeof v === 'boolean',
278
+ isNull: (v) => v === null,
279
+ isUndef: (v) => v === undefined,
280
+ isArr: (v) => Array.isArray(v),
281
+ isObj: (v) => v !== null && typeof v === 'object' && !Array.isArray(v),
282
+ isFn: (v) => typeof v === 'function',
283
+ type: (v) => v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v,
284
+ toNum: (v) => Number(v),
285
+ toStr: (v) => String(v),
286
+ toBool: (v) => Boolean(v),
287
+ toInt: (v) => parseInt(v, 10),
288
+ toFloat: (v) => parseFloat(v),
289
+ toArr: (v) => Array.isArray(v) ? v : [v],
290
+ toJson: (v) => JSON.stringify(v, null, 2),
291
+ fromJson:(v) => JSON.parse(v),
292
+ };
293
+
294
+ // ── time helpers ──────────────────────────────────────────────
295
+ const time = {
296
+ now: () => Date.now(),
297
+ date: () => new Date(),
298
+ sleep: (ms) => new Promise(r => setTimeout(r, ms)),
299
+ perf: () => typeof performance !== 'undefined' ? performance.now() : Number(process.hrtime.bigint()) / 1e6,
300
+ format: (d = new Date()) => d.toISOString(),
301
+ unix: () => Math.floor(Date.now() / 1000),
302
+ };
303
+
304
+ // ── async/process helpers ─────────────────────────────────────
305
+ const proc = {
306
+ env: (k, def = '') => process.env[k] || def,
307
+ args: () => process.argv.slice(2),
308
+ exit: (code = 0) => process.exit(code),
309
+ pid: () => process.pid,
310
+ memUsage:() => process.memoryUsage(),
311
+ cpuUsage:() => process.cpuUsage(),
312
+ platform:() => process.platform,
313
+ arch: () => process.arch,
314
+ version: () => process.version,
315
+ hrtime: () => process.hrtime.bigint(),
316
+ nextTick:(fn) => process.nextTick(fn),
317
+ };
318
+
319
+ // ── http helpers ──────────────────────────────────────────────
320
+ const http = {
321
+ fetch: async (url, opts = {}) => {
322
+ const https = url.startsWith('https') ? require('https') : require('http');
323
+ return new Promise((resolve, reject) => {
324
+ const req = https.request(url, { method: opts.method || 'GET', headers: opts.headers || {} }, (res) => {
325
+ let data = '';
326
+ res.on('data', chunk => data += chunk);
327
+ res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body: data,
328
+ json: () => JSON.parse(data), text: () => data }));
329
+ });
330
+ req.on('error', reject);
331
+ if (opts.body) req.write(typeof opts.body === 'string' ? opts.body : JSON.stringify(opts.body));
332
+ req.end();
333
+ });
334
+ },
335
+ get: async (url, headers = {}) => http.fetch(url, { method: 'GET', headers }),
336
+ post: async (url, body, headers = {}) => http.fetch(url, { method: 'POST', body, headers: { 'Content-Type': 'application/json', ...headers } }),
337
+ };
338
+
339
+ // ── persist helpers ───────────────────────────────────────────
340
+ const persist = {
341
+ save: (name, data) => {
342
+ const fp = _path.join(process.cwd(), name + '.thysave');
343
+ _fs.writeFileSync(fp, JSON.stringify(data, null, 2), 'utf8');
344
+ },
345
+ load: (name) => {
346
+ const fp = _path.join(process.cwd(), name + '.thysave');
347
+ if (!_fs.existsSync(fp)) return null;
348
+ return JSON.parse(_fs.readFileSync(fp, 'utf8'));
349
+ },
350
+ del: (name) => {
351
+ const fp = _path.join(process.cwd(), name + '.thysave');
352
+ if (_fs.existsSync(fp)) _fs.unlinkSync(fp);
353
+ },
354
+ };
355
+
356
+ // ── optimised calculate ───────────────────────────────────────
357
+ const __thy_calculate = (expr) => {
358
+ // expr is already a JS expression string – evaluate safely
359
+ // This is called at runtime for dynamic calculate() statements
360
+ try {
361
+ // Use Function constructor for safe, optimisable evaluation
362
+ return (new Function('Math', \`return (\${expr});\`))(Math);
363
+ } catch(e) {
364
+ throw new TypeError('[ThyScript] calculate error: ' + e.message);
365
+ }
366
+ };
367
+
368
+ return Object.freeze({
369
+ math, vec, str, arr, obj, io, security, types, time, proc, http, persist,
370
+ fastFloor, fastAbs, fastMod,
371
+ __thy_calculate,
372
+ // convenience top-level aliases
373
+ print: io.print,
374
+ len: (v) => (Array.isArray(v) || typeof v === 'string') ? v.length : Object.keys(v).length,
375
+ range: arr.range,
376
+ type: types.type,
377
+ hash: security.hash,
378
+ sleep: time.sleep,
379
+ now: time.now,
380
+ env: proc.env,
381
+ fetch: http.fetch,
382
+ save: persist.save,
383
+ load: persist.load,
384
+ });
385
+ })();
386
+
387
+ // Convenience globals injected from stdlib
388
+ const __thy_print = __thy_stdlib.print;
389
+ const __thy_len = __thy_stdlib.len;
390
+ const __thy_range = __thy_stdlib.range;
391
+ const __thy_sleep = __thy_stdlib.time.sleep;
392
+
393
+ function __thy_stringify(v) {
394
+ if (v === null) return 'null';
395
+ if (v === undefined) return 'undefined';
396
+ if (typeof v === 'object') { try { return JSON.stringify(v); } catch(_) { return String(v); } }
397
+ return String(v);
398
+ }
399
+
400
+ // Shared typed buffers for hot numeric paths
401
+ const __thy_f64 = new Float64Array(256);
402
+ const __thy_i32 = new Int32Array(256);
403
+ const __thy_sab = typeof SharedArrayBuffer !== 'undefined' ? new SharedArrayBuffer(4096) : null;
404
+ `.trimStart();
405
+ }
406
+
407
+ // ─── entry point ─────────────────────────────────────────────────────────────
408
+
409
+ /**
410
+ * Generate optimised JS source from a ThyScript AST.
411
+ * @param {Object} ast – The parsed AST (Program node)
412
+ * @returns {string} – Runnable Node.js JavaScript source
413
+ */
414
+ generate(ast) {
415
+ // Unwrap { ast, errors } if returned directly from parser
416
+ if (ast && ast.ast && typeof ast.ast === 'object') {
417
+ ast = ast.ast;
418
+ }
419
+
420
+ // Reset state
421
+ this._indent = 0;
422
+ this._uid = 0;
423
+ this._expressAppDeclared = false;
424
+ this._readlineDeclared = false;
425
+ this._tfDeclared = false;
426
+ this._matterDeclared = false;
427
+ this._requires = new Set();
428
+ this._headerLines = [];
429
+ this._bodyLines = [];
430
+
431
+ // Walk the AST
432
+ this._genNode(ast);
433
+
434
+ // Build final source
435
+ const parts = [
436
+ "'use strict';",
437
+ '// Auto-generated by ThyScript CodeGenerator – do not edit manually',
438
+ this._buildStdlib(),
439
+ ];
440
+
441
+ // Extra requires collected during generation
442
+ for (const pkg of this._requires) {
443
+ parts.push(`const ${this._requireVarName(pkg)} = (() => { try { return require('${pkg}'); } catch(_) { return null; } })();`);
444
+ }
445
+
446
+ if (this._expressAppDeclared) {
447
+ parts.push(`const __thy_express = (() => { try { return require('express'); } catch(_) { return null; } })();`);
448
+ parts.push(`const __thy_app = __thy_express ? __thy_express() : null;`);
449
+ }
450
+
451
+ parts.push('');
452
+ parts.push(this._bodyLines.join('\n'));
453
+
454
+ return parts.join('\n');
455
+ }
456
+
457
+ _requireVarName(pkg) {
458
+ return '__thy_req_' + pkg.replace(/[^a-zA-Z0-9]/g, '_');
459
+ }
460
+
461
+ // ─── node dispatcher ─────────────────────────────────────────────────────────
462
+
463
+ _genNode(node) {
464
+ if (!node) return 'undefined';
465
+ switch (node.type) {
466
+ case 'Program': return this._genProgram(node);
467
+ case 'VarDecl': return this._genVarDecl(node);
468
+ case 'DestructureDecl': return this._genDestructureDecl(node);
469
+ case 'PrintStmt': return this._genPrintStmt(node);
470
+ case 'ClassDecl': return this._genClassDecl(node);
471
+ case 'HtmlClassDecl': return this._genHtmlClassDecl(node);
472
+ case 'IfStmt': return this._genIfStmt(node);
473
+ case 'WhileStmt': return this._genWhileStmt(node);
474
+ case 'ForStmt': return this._genForStmt(node);
475
+ case 'AddStmt': return this._genAddStmt(node);
476
+ case 'ChangeStmt': return this._genChangeStmt(node);
477
+ case 'CalculateStmt': return this._genCalculateStmt(node);
478
+ case 'InputStmt': return this._genInputStmt(node);
479
+ case 'SaveStmt': return this._genSaveStmt(node);
480
+ case 'FreeStmt': return this._genFreeStmt(node);
481
+ case 'FunctionDecl': return this._genFunctionDecl(node);
482
+ case 'ReturnStmt': return this._genReturnStmt(node);
483
+ case 'TryCatch': return this._genTryCatch(node);
484
+ case 'ImportDecl': return this._genImportDecl(node);
485
+ case 'ExportDecl': return this._genExportDecl(node);
486
+ case 'ServerDecl': return this._genServerDecl(node);
487
+ case 'RouteDecl': return this._genRouteDecl(node);
488
+ case 'ComponentDecl': return this._genComponentDecl(node);
489
+ case 'NeuralDecl': return this._genNeuralDecl(node);
490
+ case 'PhysicsDecl': return this._genPhysicsDecl(node);
491
+ case 'ParallelDecl': return this._genParallelDecl(node);
492
+ case 'StreamDecl': return this._genStreamDecl(node);
493
+ case 'BreakStmt': return this._emit('break;');
494
+ case 'ContinueStmt': return this._emit('continue;');
495
+ case 'ThrowStmt': return this._emit(`throw ${this._genExpr(node.argument || node.value)};`);
496
+ case 'ExprStmt': return this._emit(`${this._genExpr(node.expression)};`);
497
+ case 'ExpressionStmt': return this._emit(`${this._genExpr(node.expression)};`);
498
+ case 'Block': return node.body.forEach(n => this._genNode(n));
499
+ case 'SwitchStmt': return this._genSwitchStmt(node);
500
+ case 'MatchStmt': return this._genMatchStmt(node);
501
+ case 'AwaitExpr': return this._genExpr(node); // handled in expr
502
+ default:
503
+ // Fallback: try to generate as expression statement
504
+ try {
505
+ const expr = this._genExpr(node);
506
+ if (expr && expr !== 'undefined') this._emit(`${expr};`);
507
+ } catch (_) {
508
+ this._emit(`/* [ThyScript] unhandled node type: ${node.type} */`);
509
+ }
510
+ }
511
+ }
512
+
513
+ // ─── program ─────────────────────────────────────────────────────────────────
514
+
515
+ _genProgram(node) {
516
+ this._emit('// ── program body ──────────────────────────────────────────────');
517
+ this._emit('(async () => {');
518
+ this._push();
519
+ for (const stmt of (node.body || [])) {
520
+ this._genNode(stmt);
521
+ }
522
+ this._pop();
523
+ this._emit('})().catch(e => { console.error("[ThyScript Runtime Error]", e); process.exit(1); });');
524
+ }
525
+
526
+ // ─── variable declaration ────────────────────────────────────────────────────
527
+
528
+ _genVarDecl(node) {
529
+ // `const table x = 0` → `let x = 0;`
530
+ // Always use `let` so ThyScript variables are mutable by default
531
+ const name = node.name;
532
+ const init = node.init != null ? this._genExpr(node.init) : 'undefined';
533
+ this._emit(`let ${name} = ${init};`);
534
+ }
535
+
536
+ // ─── destructuring declaration ──────────────────────────────────────────────
537
+
538
+ _genDestructureDecl(node) {
539
+ // `const table [a, b, ...rest] = init` → `let [a, b, ...rest] = init;`
540
+ // `const table {x, y: z} = init` → `let { x, y: z } = init;`
541
+ const init = this._genExpr(node.init);
542
+ if (node.kind === 'array') {
543
+ const elems = (node.targets || []).map(t => (t.rest ? `...${t.name}` : t.name));
544
+ this._emit(`let [${elems.join(', ')}] = ${init};`);
545
+ } else {
546
+ const props = (node.targets || []).map(({ key, alias, default: def }) => {
547
+ const target = key === alias ? key : `${key}: ${alias}`;
548
+ return def != null ? `${target} = ${this._genExpr(def)}` : target;
549
+ });
550
+ this._emit(`let { ${props.join(', ')} } = ${init};`);
551
+ }
552
+ }
553
+
554
+ // ─── print ───────────────────────────────────────────────────────────────────
555
+
556
+ _genPrintStmt(node) {
557
+ const args = (node.args || [node.value]).map(a => this._genExpr(a)).join(', ');
558
+ this._emit(`__thy_print(${args});`);
559
+ }
560
+
561
+ // ─── class ───────────────────────────────────────────────────────────────────
562
+
563
+ _genBlockOrList(body) {
564
+ if (!body) return;
565
+ if (Array.isArray(body)) {
566
+ for (const s of body) this._genNode(s);
567
+ } else if (body.type === 'Block') {
568
+ for (const s of (body.body || [])) this._genNode(s);
569
+ } else {
570
+ this._genNode(body);
571
+ }
572
+ }
573
+
574
+ // ─── class ───────────────────────────────────────────────────────────────────
575
+
576
+ _genClassDecl(node) {
577
+ const name = node.name;
578
+ const parent = node.superClass ? ` extends ${node.superClass}` : '';
579
+ const access = node.access || 'public'; // public / private
580
+ this._emit(`// [${access}] class`);
581
+ this._emit(`class ${name}${parent} {`);
582
+ this._push();
583
+
584
+ const stmts = Array.isArray(node.body) ? node.body : (node.body && node.body.body ? node.body.body : []);
585
+ const props = [];
586
+ const methods = [...(node.methods || [])];
587
+ const constructorStmts = [...(node.constructorBody || [])];
588
+
589
+ for (const stmt of stmts) {
590
+ if (stmt.type === 'VarDecl') {
591
+ props.push(stmt);
592
+ } else if (stmt.type === 'FunctionDecl') {
593
+ methods.push(stmt);
594
+ } else {
595
+ constructorStmts.push(stmt);
596
+ }
597
+ }
598
+
599
+ this._emit(`constructor(${(node.constructorParams || []).join(', ')}) {`);
600
+ this._push();
601
+ if (node.superClass) this._emit('super();');
602
+ for (const prop of props) {
603
+ const init = prop.init != null ? this._genExpr(prop.init) : 'undefined';
604
+ this._emit(`this.${prop.name} = ${init};`);
605
+ }
606
+ for (const stmt of constructorStmts) this._genNode(stmt);
607
+ this._pop();
608
+ this._emit('}');
609
+
610
+ const propNames = props.map(p => p.name);
611
+ const methodNames = methods.map(m => m.name);
612
+ for (const method of methods) {
613
+ this._genMethod(method, propNames, methodNames);
614
+ }
615
+ for (const prop of (node.staticProps || [])) {
616
+ this._emit(`static ${prop.name} = ${this._genExpr(prop.value)};`);
617
+ }
618
+ this._pop();
619
+ this._emit('}');
620
+ }
621
+
622
+ _genMethod(method, classProps = [], classMethods = []) {
623
+ const isStatic = method.isStatic ? 'static ' : '';
624
+ const isAsync = method.isAsync ? 'async ' : '';
625
+ const name = method.name;
626
+ const params = (method.params || []).map(p => typeof p === 'string' ? p : p.name).join(', ');
627
+ this._emit(`${isStatic}${isAsync}${name}(${params}) {`);
628
+ this._push();
629
+ if (!isStatic && classMethods.length > 0) {
630
+ for (const m of classMethods) {
631
+ if (m !== name) {
632
+ this._emit(`const ${m} = (...args) => this.${m}(...args);`);
633
+ }
634
+ }
635
+ }
636
+ if (!isStatic && classProps.length > 0) {
637
+ // Destructure class props as mutable lets (allows += inside methods)
638
+ this._emit('let { ' + classProps.join(', ') + ' } = this;');
639
+ this._genBlockOrList(method.body);
640
+ // Write any mutations back to the instance
641
+ this._emit('Object.assign(this, { ' + classProps.join(', ') + ' });');
642
+ } else {
643
+ this._genBlockOrList(method.body);
644
+ }
645
+ this._pop();
646
+ this._emit('}');
647
+ }
648
+
649
+ // ─── HTML class (express route serving HTML) ──────────────────────────────────
650
+
651
+ _genHtmlClassDecl(node) {
652
+ this._expressAppDeclared = true;
653
+ const route = node.route || `/${node.name}`;
654
+ const name = (node.name || 'html_page').replace(/[^a-zA-Z0-9_]/g, '_');
655
+ const safeHtml = (node.html || '').replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '\\${');
656
+ this._emit(`// HtmlClass: ${node.name}`);
657
+ this._emit(`const __html_${name} = \`${safeHtml}\`;`);
658
+ this._emit(`if (typeof response !== 'undefined' && response && typeof response.send === 'function') {`);
659
+ this._push();
660
+ this._emit(`response.setHeader('Content-Type', 'text/html; charset=utf-8');`);
661
+ this._emit(`response.send(__html_${name});`);
662
+ this._pop();
663
+ this._emit(`} else if (typeof __thy_app !== 'undefined' && __thy_app) {`);
664
+ this._push();
665
+ this._emit(`__thy_app.get('${route}', (req, res) => {`);
666
+ this._push();
667
+ this._emit(`res.setHeader('Content-Type', 'text/html; charset=utf-8');`);
668
+ this._emit(`res.send(__html_${name});`);
669
+ this._pop();
670
+ this._emit(`});`);
671
+ this._pop();
672
+ this._emit(`}`);
673
+ }
674
+
675
+ // ─── if / elif / else ────────────────────────────────────────────────────────
676
+
677
+ _genIfStmt(node) {
678
+ const cond = this._genExpr(node.condition || node.test);
679
+ this._emit(`if (${cond}) {`);
680
+ this._push();
681
+ this._genBlockOrList(node.consequent);
682
+ this._pop();
683
+ const alternates = node.alternates || node.elseifs || [];
684
+ if (alternates.length > 0) {
685
+ for (const alt of alternates) {
686
+ const altCond = this._genExpr(alt.condition || alt.test);
687
+ this._emit(`} else if (${altCond}) {`);
688
+ this._push();
689
+ this._genBlockOrList(alt.body || alt.consequent);
690
+ this._pop();
691
+ }
692
+ }
693
+ if (node.alternate) {
694
+ this._emit('} else {');
695
+ this._push();
696
+ this._genBlockOrList(node.alternate);
697
+ this._pop();
698
+ }
699
+ this._emit('}');
700
+ }
701
+
702
+ // ─── while ───────────────────────────────────────────────────────────────────
703
+
704
+ _genWhileStmt(node) {
705
+ const cond = this._genExpr(node.condition || node.test);
706
+ this._emit(`while (${cond}) {`);
707
+ this._push();
708
+ this._genBlockOrList(node.body);
709
+ this._pop();
710
+ this._emit('}');
711
+ }
712
+
713
+ // ─── for ─────────────────────────────────────────────────────────────────────
714
+
715
+ _genForStmt(node) {
716
+ if (node.style === 'range' || node.to != null) {
717
+ const varName = node.var || node.variable;
718
+ const from_ = this._genExpr(node.from || { type: 'Literal', value: 0 });
719
+ const to_ = this._genExpr(node.to);
720
+ const step_ = node.step ? this._genExpr(node.step) : '1';
721
+ this._emit(`for (let ${varName} = ${from_}; ${varName} < ${to_}; ${varName} += ${step_}) {`);
722
+ } else {
723
+ const varName = node.var || node.variable || (node.left && node.left.name ? node.left.name : 'item');
724
+ const iterExpr = this._genExpr(node.iterable || node.right);
725
+ this._emit(`for (const ${varName} of ${iterExpr}) {`);
726
+ }
727
+ this._push();
728
+ this._genBlockOrList(node.body);
729
+ this._pop();
730
+ this._emit('}');
731
+ }
732
+
733
+ // ─── add stmt `add 10 in x` ─────────────────────────────────────────────────
734
+
735
+ _genAddStmt(node) {
736
+ const target = typeof node.target === 'string' ? node.target : this._genExpr(node.target);
737
+ const value = this._genExpr(node.value);
738
+ this._emit(`if (Array.isArray(${target})) { ${target}.push(${value}); } else { ${target} += ${value}; }`);
739
+ }
740
+
741
+ // ─── change stmt `change(x to(0))` ─────────────────────────────────────────
742
+
743
+ _genChangeStmt(node) {
744
+ const target = typeof node.target === 'string' ? node.target : this._genExpr(node.target);
745
+ const value = this._genExpr(node.value);
746
+ this._emit(`${target} = ${value};`);
747
+ }
748
+
749
+ // ─── calculate `calculate(x + y)` ───────────────────────────────────────────
750
+
751
+ _genCalculateStmt(node) {
752
+ if (node.result) {
753
+ // calculate(expr) stored in a variable
754
+ const expr = this._genExpr(node.expression);
755
+ const target = node.result;
756
+ this._emit(`${target} = (${expr});`);
757
+ } else {
758
+ const expr = this._genExpr(node.expression);
759
+ this._emit(`(${expr});`);
760
+ }
761
+ }
762
+
763
+ // ─── input `input(prompt)` ──────────────────────────────────────────────────
764
+
765
+ _genInputStmt(node) {
766
+ if (!this._readlineDeclared) {
767
+ this._readlineDeclared = true;
768
+ this._emit(`const __thy_rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });`);
769
+ this._emit(`const __thy_input = (prompt) => new Promise(resolve => __thy_rl.question(prompt, resolve));`);
770
+ }
771
+ const prompt = node.prompt ? this._genExpr(node.prompt) : "''";
772
+ if (node.result) {
773
+ this._emit(`const ${node.result} = await __thy_input(${prompt});`);
774
+ } else {
775
+ this._emit(`await __thy_input(${prompt});`);
776
+ }
777
+ }
778
+
779
+ // ─── save `save(var)` ───────────────────────────────────────────────────────
780
+
781
+ _genSaveStmt(node) {
782
+ const varName = typeof node.value === 'string' ? node.value : this._genExpr(node.value);
783
+ const filename = node.filename ? this._genExpr(node.filename) : `'${varName}'`;
784
+ this._emit(`__thy_stdlib.persist.save(${filename}, ${varName});`);
785
+ }
786
+
787
+ // ─── free `free()` ──────────────────────────────────────────────────────────
788
+
789
+ _genFreeStmt(node) {
790
+ for (const v of (node.vars || (node.value ? [node.value] : []))) {
791
+ const name = typeof v === 'string' ? v : this._genExpr(v);
792
+ this._emit(`${name} = null; // GC hint`);
793
+ }
794
+ if (node.gc) {
795
+ this._emit(`if (global.gc) global.gc(); // expose-gc`);
796
+ }
797
+ }
798
+
799
+ // ─── function declaration ────────────────────────────────────────────────────
800
+
801
+ _genFunctionDecl(node) {
802
+ const name = node.name;
803
+ const params = (node.params || []).map(p => {
804
+ if (typeof p === 'string') return p;
805
+ if (p.default != null) return `${p.name} = ${this._genExpr(p.default)}`;
806
+ if (p.rest) return `...${p.name}`;
807
+ return p.name;
808
+ }).join(', ');
809
+ const isAsync = node.isAsync !== false ? 'async ' : '';
810
+ const isGen = node.isGenerator ? '*' : '';
811
+ this._emit(`${isAsync}function${isGen} ${name}(${params}) {`);
812
+ this._push();
813
+ // V8 hint: mark as optimisable by keeping hot paths monomorphic
814
+ this._emit(`// [ThyScript JIT] function "${name}" – V8 optimisation target`);
815
+ this._genBlockOrList(node.body);
816
+ this._pop();
817
+ this._emit('}');
818
+ }
819
+
820
+ // ─── return ──────────────────────────────────────────────────────────────────
821
+
822
+ _genReturnStmt(node) {
823
+ const val = node.argument != null ? node.argument : node.value;
824
+ if (val != null) {
825
+ this._emit(`return ${this._genExpr(val)};`);
826
+ } else {
827
+ this._emit('return;');
828
+ }
829
+ }
830
+
831
+ // ─── try / catch / finally ───────────────────────────────────────────────────
832
+
833
+ _genTryCatch(node) {
834
+ this._emit('try {');
835
+ this._push();
836
+ this._genBlockOrList(node.tryBody || node.block);
837
+ this._pop();
838
+ if (node.catchBody || node.handler) {
839
+ const errName = node.catchVar || (node.handler && node.handler.param ? node.handler.param.name : '__thy_err');
840
+ this._emit(`} catch (${errName}) {`);
841
+ this._push();
842
+ this._genBlockOrList(node.catchBody || (node.handler && node.handler.body));
843
+ this._pop();
844
+ }
845
+ if (node.finallyBody || node.finalizer) {
846
+ this._emit('} finally {');
847
+ this._push();
848
+ this._genBlockOrList(node.finallyBody || node.finalizer);
849
+ this._pop();
850
+ }
851
+ this._emit('}');
852
+ }
853
+
854
+ // ─── import ──────────────────────────────────────────────────────────────────
855
+
856
+ _genImportDecl(node) {
857
+ const src = node.source;
858
+ if (node.default) {
859
+ this._emit(`const ${node.default} = require(${JSON.stringify(src)});`);
860
+ } else if (node.named && node.named.length) {
861
+ const bindings = node.named.map(n => typeof n === 'string' ? n : `${n.imported}: ${n.local}`).join(', ');
862
+ this._emit(`const { ${bindings} } = require(${JSON.stringify(src)});`);
863
+ } else if (node.namespace) {
864
+ this._emit(`const ${node.namespace} = require(${JSON.stringify(src)});`);
865
+ } else {
866
+ this._emit(`require(${JSON.stringify(src)});`);
867
+ }
868
+ }
869
+
870
+ // ─── export ──────────────────────────────────────────────────────────────────
871
+
872
+ _genExportDecl(node) {
873
+ if (node.default) {
874
+ this._emit(`module.exports = ${this._genExpr(node.value)};`);
875
+ } else if (node.named) {
876
+ const bindings = (node.named || []).map(n => typeof n === 'string' ? n : `${n}: ${n}`).join(', ');
877
+ this._emit(`Object.assign(module.exports, { ${bindings} });`);
878
+ }
879
+ }
880
+
881
+ // ─── server declaration ──────────────────────────────────────────────────────
882
+
883
+ _genServerDecl(node) {
884
+ this._expressAppDeclared = true;
885
+ const port = node.port ? this._genExpr(node.port) : '3000';
886
+ const host = node.host ? this._genExpr(node.host) : "'0.0.0.0'";
887
+ const name = node.name || '__thy_server';
888
+ this._emit(`// [ThyScript] Server declaration`);
889
+ this._emit(`if (!__thy_express) throw new Error('[ThyScript] express is not installed. Run: npm install express');`);
890
+ this._emit(`__thy_app.use(require('express').json());`);
891
+ this._emit(`__thy_app.use(require('express').urlencoded({ extended: true }));`);
892
+ // Body
893
+ this._genBlockOrList(node.body);
894
+ // listen
895
+ this._emit(`const ${name} = __thy_app.listen(${port}, ${host}, () => {`);
896
+ this._push();
897
+ this._emit(`__thy_print(\`[ThyScript] Server running on http://\${${host}}:\${${port}}\`);`);
898
+ this._pop();
899
+ this._emit(`});`);
900
+ // Socket.IO support
901
+ if (node.socket) {
902
+ this._emit(`const __thy_io = (() => { try { const { Server } = require('socket.io'); return new Server(${name}); } catch(_) { return null; } })();`);
903
+ this._emit(`if (__thy_io) { __thy_print('[ThyScript] Socket.IO attached'); }`);
904
+ }
905
+ }
906
+
907
+ // ─── route declaration ───────────────────────────────────────────────────────
908
+
909
+ _genRouteDecl(node) {
910
+ this._expressAppDeclared = true;
911
+ const method = (node.method || 'get').toLowerCase();
912
+ const path_ = this._genExpr(node.path);
913
+ const reqVar = node.reqVar || 'request';
914
+ const resVar = node.resVar || 'response';
915
+ const isAsync = node.isAsync !== false;
916
+ this._emit(`if (__thy_app) {`);
917
+ this._push();
918
+ this._emit(`__thy_app.${method}(${path_}, ${isAsync ? 'async ' : ''}(${reqVar}, ${resVar}) => {`);
919
+ this._push();
920
+ this._emit(`const req = ${reqVar}; const res = ${resVar};`);
921
+ this._genBlockOrList(node.body);
922
+ this._pop();
923
+ this._emit(`});`);
924
+ this._pop();
925
+ this._emit(`}`);
926
+ }
927
+
928
+ // ─── component (React-like) ──────────────────────────────────────────────────
929
+
930
+ _genComponentDecl(node) {
931
+ const name = node.name;
932
+ const props = (node.props || []).join(', ');
933
+ this._emit(`// [ThyScript] Component: ${name}`);
934
+ this._emit(`function ${name}({ ${props} }) {`);
935
+ this._push();
936
+ this._emit(`// state`);
937
+ for (const s of (node.state || [])) {
938
+ this._emit(`let __state_${s.name} = ${this._genExpr(s.init)};`);
939
+ }
940
+ this._emit(`// render`);
941
+ this._emit(`const render = () => {`);
942
+ this._push();
943
+ this._genBlockOrList(node.body);
944
+ this._pop();
945
+ this._emit(`};`);
946
+ this._emit(`render();`);
947
+ this._emit(`return { render, getState: () => ({ ${(node.state || []).map(s => s.name + ': __state_' + s.name).join(', ')} }) };`);
948
+ this._pop();
949
+ this._emit(`}`);
950
+ }
951
+
952
+ // ─── neural declaration (TensorFlow.js) ─────────────────────────────────────
953
+
954
+ _genNeuralDecl(node) {
955
+ if (!this._tfDeclared) {
956
+ this._tfDeclared = true;
957
+ this._emit(`const __thy_tf = (() => { try { return require('@tensorflow/tfjs-node'); } catch(_) { try { return require('@tensorflow/tfjs'); } catch(__) { return null; } } })();`);
958
+ }
959
+ const name = node.name || '__thy_model';
960
+ const layers = node.layers || [];
961
+ const optName = (node.optimizer || 'adam').toLowerCase();
962
+ const loss = node.loss || 'meanSquaredError';
963
+ const lr = node.learningRate || 0.001;
964
+
965
+ this._emit(`// [ThyScript] Neural network: ${name}`);
966
+ this._emit(`let ${name} = null;`);
967
+ this._emit(`if (__thy_tf) {`);
968
+ this._push();
969
+ this._emit(`${name} = __thy_tf.sequential();`);
970
+ for (const layer of layers) {
971
+ const ltype = layer.kind || 'dense';
972
+ const units = layer.units || 1;
973
+ const activ = layer.activation || 'relu';
974
+ const inputSh = layer.inputShape ? `, inputShape: [${layer.inputShape}]` : '';
975
+ this._emit(`${name}.add(__thy_tf.layers.${ltype}({ units: ${units}, activation: '${activ}'${inputSh} }));`);
976
+ }
977
+ const optStr = optName === 'adam' ?
978
+ `__thy_tf.train.adam(${lr})` :
979
+ optName === 'sgd' ? `__thy_tf.train.sgd(${lr})` :
980
+ `__thy_tf.train.${optName}(${lr})`;
981
+ this._emit(`${name}.compile({ optimizer: ${optStr}, loss: '${loss}', metrics: ['accuracy'] });`);
982
+ this._emit(`__thy_print('[ThyScript Neural] Model "${name}" compiled');`);
983
+ this._pop();
984
+ this._emit(`} else { __thy_print('[ThyScript Neural] TensorFlow.js not installed – model "${name}" skipped'); }`);
985
+
986
+ // train / predict helpers
987
+ this._emit(`const __train_${name} = async (xs, ys, epochs = 10, batchSize = 32) => {`);
988
+ this._push();
989
+ this._emit(`if (!${name}) return null;`);
990
+ this._emit(`const xsTensor = __thy_tf.tensor(xs);`);
991
+ this._emit(`const ysTensor = __thy_tf.tensor(ys);`);
992
+ this._emit(`const hist = await ${name}.fit(xsTensor, ysTensor, { epochs, batchSize });`);
993
+ this._emit(`xsTensor.dispose(); ysTensor.dispose();`);
994
+ this._emit(`return hist;`);
995
+ this._pop();
996
+ this._emit(`};`);
997
+
998
+ this._emit(`const __predict_${name} = (xs) => {`);
999
+ this._push();
1000
+ this._emit(`if (!${name}) return null;`);
1001
+ this._emit(`return ${name}.predict(__thy_tf.tensor(xs)).arraySync();`);
1002
+ this._pop();
1003
+ this._emit(`};`);
1004
+ }
1005
+
1006
+ // ─── physics declaration (matter.js) ────────────────────────────────────────
1007
+
1008
+ _genPhysicsDecl(node) {
1009
+ if (!this._matterDeclared) {
1010
+ this._matterDeclared = true;
1011
+ this._emit(`const __thy_matter = (() => { try { return require('matter-js'); } catch(_) { return null; } })();`);
1012
+ }
1013
+ const name = node.name || '__thy_physics';
1014
+ const gravity = node.gravity != null ? node.gravity : 1;
1015
+ const bodies = node.bodies || [];
1016
+
1017
+ this._emit(`// [ThyScript] Physics world: ${name}`);
1018
+ this._emit(`let ${name} = null;`);
1019
+ this._emit(`if (__thy_matter) {`);
1020
+ this._push();
1021
+ this._emit(`const { Engine, World, Bodies, Runner } = __thy_matter;`);
1022
+ this._emit(`const __engine_${name} = Engine.create({ gravity: { y: ${gravity} } });`);
1023
+ this._emit(`${name} = __engine_${name}.world;`);
1024
+ for (const body of bodies) {
1025
+ const bname = body.name || this._uid_();
1026
+ const btype = body.kind || 'rectangle';
1027
+ const bx = body.x || 0;
1028
+ const by = body.y || 0;
1029
+ const bw = body.width || 100;
1030
+ const bh = body.height || 100;
1031
+ const bopts = body.isStatic ? ', { isStatic: true }' : '';
1032
+ this._emit(`const __body_${bname} = Bodies.${btype}(${bx}, ${by}, ${bw}, ${bh}${bopts});`);
1033
+ this._emit(`World.add(${name}, __body_${bname});`);
1034
+ }
1035
+ this._emit(`Runner.run(__engine_${name});`);
1036
+ this._emit(`__thy_print('[ThyScript Physics] World "${name}" running');`);
1037
+ this._pop();
1038
+ this._emit(`} else { __thy_print('[ThyScript Physics] matter-js not installed'); }`);
1039
+ }
1040
+
1041
+ // ─── parallel declaration (worker_threads) ───────────────────────────────────
1042
+
1043
+ _genParallelDecl(node) {
1044
+ this._emit(`// [ThyScript] Parallel execution – worker_threads`);
1045
+ const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
1046
+ const tasks = node.tasks || [];
1047
+ const workers = node.workers || 4;
1048
+ const name = node.name || '__thy_parallel';
1049
+
1050
+ this._emit(`const { Worker: __W, isMainThread: __IMT } = require('worker_threads');`);
1051
+ this._emit(`const __thy_parallel_run = async (fn, data, nWorkers = ${workers}) => {`);
1052
+ this._push();
1053
+ this._emit(`const chunks = [];`);
1054
+ this._emit(`const chunkSize = Math.ceil(data.length / nWorkers);`);
1055
+ this._emit(`for (let i = 0; i < data.length; i += chunkSize) chunks.push(data.slice(i, i + chunkSize));`);
1056
+ this._emit(`const code = \`'use strict'; const { parentPort, workerData } = require('worker_threads'); parentPort.postMessage((\${fn.toString()})(workerData));\`;`);
1057
+ this._emit(`const results = await Promise.all(chunks.map(chunk => new Promise((res, rej) => {`);
1058
+ this._push();
1059
+ this._emit(`const w = new __W(code, { eval: true, workerData: chunk });`);
1060
+ this._emit(`w.on('message', res); w.on('error', rej);`);
1061
+ this._pop();
1062
+ this._emit(`})));`);
1063
+ this._emit(`return results.flat();`);
1064
+ this._pop();
1065
+ this._emit(`};`);
1066
+
1067
+ for (const task of tasks) {
1068
+ const tname = task.name || this._uid_();
1069
+ const tdata = this._genExpr(task.data || { type: 'Literal', value: [] });
1070
+ this._emit(`const __parallel_result_${tname} = await __thy_parallel_run(${task.fn || 'x => x'}, ${tdata}, ${workers});`);
1071
+ }
1072
+ }
1073
+
1074
+ // ─── stream declaration ──────────────────────────────────────────────────────
1075
+
1076
+ _genStreamDecl(node) {
1077
+ this._emit(`// [ThyScript] Stream`);
1078
+ const name = node.name || '__thy_stream';
1079
+ const src = node.source ? this._genExpr(node.source) : "process.stdin";
1080
+ const dest = node.dest ? this._genExpr(node.dest) : "process.stdout";
1081
+ const transforms = node.transforms || [];
1082
+
1083
+ this._emit(`const { Transform: __TransformStream, pipeline: __pipeline } = require('stream');`);
1084
+ this._emit(`const { promisify: __promisify } = require('util');`);
1085
+ this._emit(`const __pipelineAsync = __promisify(__pipeline);`);
1086
+
1087
+ for (const t of transforms) {
1088
+ const tname = t.name || this._uid_();
1089
+ const logic = t.fn || '(chunk, enc, cb) => cb(null, chunk)';
1090
+ this._emit(`const __transform_${tname} = new __TransformStream({ transform: ${logic} });`);
1091
+ }
1092
+
1093
+ const pipeChain = transforms.length > 0
1094
+ ? [src, ...transforms.map(t => `__transform_${t.name || '__t'}`), dest].join(', ')
1095
+ : `${src}, ${dest}`;
1096
+ this._emit(`await __pipelineAsync(${pipeChain}).catch(e => console.error('[ThyScript Stream]', e));`);
1097
+ }
1098
+
1099
+ // ─── switch ──────────────────────────────────────────────────────────────────
1100
+
1101
+ _genSwitchStmt(node) {
1102
+ const disc = this._genExpr(node.discriminant);
1103
+ this._emit(`switch (${disc}) {`);
1104
+ this._push();
1105
+ for (const c of (node.cases || [])) {
1106
+ if (c.test) {
1107
+ this._emit(`case ${this._genExpr(c.test)}:`);
1108
+ } else {
1109
+ this._emit('default:');
1110
+ }
1111
+ this._push();
1112
+ this._genBlockOrList(c.body);
1113
+ if (!c.fallthrough) this._emit('break;');
1114
+ this._pop();
1115
+ }
1116
+ this._pop();
1117
+ this._emit('}');
1118
+ }
1119
+
1120
+ // ─── match statement (pattern matching) ──────────────────────────────────────
1121
+
1122
+ _genMatchStmt(node) {
1123
+ const disc = this._genExpr(node.discriminant);
1124
+ const tmp = this._uid_();
1125
+ this._emit(`const ${tmp} = ${disc};`);
1126
+ let first = true;
1127
+ for (const arm of (node.arms || [])) {
1128
+ const kw = first ? 'if' : '} else if';
1129
+ first = false;
1130
+ if (arm.pattern === '_' || arm.isOtherwise) {
1131
+ this._emit('} else {');
1132
+ } else {
1133
+ const pat = this._genExpr(arm.pattern);
1134
+ this._emit(`${kw} (${tmp} === ${pat}) {`);
1135
+ }
1136
+ this._push();
1137
+ this._genBlockOrList(arm.body);
1138
+ this._pop();
1139
+ }
1140
+ this._emit('}');
1141
+ }
1142
+
1143
+ // ─── expression generator ────────────────────────────────────────────────────
1144
+
1145
+ _genExpr(node) {
1146
+ if (node == null) return 'undefined';
1147
+
1148
+ // Raw string shorthand
1149
+ if (typeof node === 'string') return node;
1150
+ if (typeof node === 'number') return String(node);
1151
+ if (typeof node === 'boolean') return String(node);
1152
+
1153
+ switch (node.type) {
1154
+ case 'Literal': return this._genLiteral(node);
1155
+ case 'Identifier': return this._genIdentifier(node);
1156
+ case 'BinaryExpr': return this._genBinaryExpr(node);
1157
+ case 'UnaryExpr': return this._genUnaryExpr(node);
1158
+ case 'LogicalExpr': return this._genLogicalExpr(node);
1159
+ case 'AssignExpr': return this._genAssignExpr(node);
1160
+ case 'CallExpr': return this._genCallExpr(node);
1161
+ case 'MemberExpr': return this._genMemberExpr(node);
1162
+ case 'IndexExpr': return this._genIndexExpr(node);
1163
+ case 'ArrayExpr': return this._genArrayExpr(node);
1164
+ case 'ObjectExpr': return this._genObjectExpr(node);
1165
+ case 'ArrowFunc':
1166
+ case 'ArrowFunctionExpr':return this._genArrowFunc(node);
1167
+ case 'FunctionExpr': return this._genFunctionExpr(node);
1168
+ case 'ConditionalExpr': return this._genConditionalExpr(node);
1169
+ case 'TemplateLiteral': return this._genTemplateLiteral(node);
1170
+ case 'SpreadExpr': return `...${this._genExpr(node.argument)}`;
1171
+ case 'AwaitExpr': return `await ${this._genExpr(node.argument)}`;
1172
+ case 'YieldExpr': return `yield ${this._genExpr(node.argument)}`;
1173
+ case 'NewExpr': return this._genNewExpr(node);
1174
+ case 'InputExpr': return `await __thy_input(${node.prompt ? this._genExpr(node.prompt) : "''"})`;
1175
+ case 'TypeofExpr': return `typeof ${this._genExpr(node.argument)}`;
1176
+ case 'InstanceofExpr': return `(${this._genExpr(node.left)} instanceof ${this._genExpr(node.right)})`;
1177
+ case 'NullCoalesce': return `(${this._genExpr(node.left)} ?? ${this._genExpr(node.right)})`;
1178
+ case 'OptionalChain': return `${this._genExpr(node.object)}?.${this._genExpr(node.property)}`;
1179
+ case 'DeleteExpr': return `delete ${this._genExpr(node.argument)}`;
1180
+ case 'VoidExpr': return `void ${this._genExpr(node.argument)}`;
1181
+ case 'SequenceExpr': return `(${(node.expressions || []).map(e => this._genExpr(e)).join(', ')})`;
1182
+ case 'TaggedTemplate': return `${this._genExpr(node.tag)}\`${this._genExpr(node.quasi)}\``;
1183
+ case 'CalculateExpr': return `(${this._genExpr(node.expression)})`;
1184
+ case 'MathExpr': return this._genMathExpr(node);
1185
+ case 'VectorExpr': return this._genVectorExpr(node);
1186
+ case 'MatrixExpr': return this._genMatrixExpr(node);
1187
+
1188
+ default:
1189
+ // Try to coerce unknown expression nodes
1190
+ if (node.value !== undefined && Object.keys(node).length <= 3) return this._genLiteral(node);
1191
+ return `/* unknown expr: ${node.type} */ undefined`;
1192
+ }
1193
+ }
1194
+
1195
+ _genLiteral(node) {
1196
+ const v = node.value;
1197
+ if (v === null) return 'null';
1198
+ if (v === undefined) return 'undefined';
1199
+ if (typeof v === 'string') return `'${this._escStr(v)}'`;
1200
+ if (typeof v === 'boolean') return String(v);
1201
+ if (typeof v === 'number') return String(v);
1202
+ // Array / object literals embedded
1203
+ try { return JSON.stringify(v); } catch(_) { return String(v); }
1204
+ }
1205
+
1206
+ _genIdentifier(node) {
1207
+ // Map ThyScript built-in identifiers to stdlib aliases
1208
+ const aliases = {
1209
+ 'true': 'true',
1210
+ 'false': 'false',
1211
+ 'null': 'null',
1212
+ 'undefined': 'undefined',
1213
+ 'NaN': 'NaN',
1214
+ 'Infinity': 'Infinity',
1215
+ 'this': 'this',
1216
+ 'self': 'this',
1217
+ 'super': 'super',
1218
+ 'print': '__thy_print',
1219
+ 'len': '__thy_len',
1220
+ 'range': '__thy_range',
1221
+ 'sleep': '__thy_stdlib.time.sleep',
1222
+ 'now': '__thy_stdlib.time.now',
1223
+ 'env': '__thy_stdlib.proc.env',
1224
+ 'fetch': '__thy_stdlib.http.fetch',
1225
+ 'save': '__thy_stdlib.persist.save',
1226
+ 'load': '__thy_stdlib.persist.load',
1227
+ };
1228
+ return aliases[node.name] || node.name;
1229
+ }
1230
+
1231
+ _genBinaryExpr(node) {
1232
+ const l = this._genExpr(node.left);
1233
+ const r = this._genExpr(node.right);
1234
+ const opMap = {
1235
+ '+': '+', '-': '-', '*': '*', '/': '/',
1236
+ '%': '%', '**': '**', '&': '&', '|': '|',
1237
+ '^': '^', '<<': '<<', '>>': '>>',
1238
+ '==': '===', '!=': '!==', '<': '<', '>': '>',
1239
+ '<=': '<=', '>=': '>=',
1240
+ 'and': '&&', 'or': '||',
1241
+ 'is': '===', 'isnt': '!==',
1242
+ };
1243
+ const rawOp = node.operator || node.op;
1244
+ const op = opMap[rawOp] || rawOp;
1245
+
1246
+ if (this.options.optimise && (op === '+' || op === '-' || op === '*' || op === '/')) {
1247
+ return `(${l} ${op} ${r})`;
1248
+ }
1249
+ return `(${l} ${op} ${r})`;
1250
+ }
1251
+
1252
+ _genUnaryExpr(node) {
1253
+ const opMap = { 'not': '!', '!': '!', '-': '-', '+': '+', '~': '~', '++': '++', '--': '--' };
1254
+ const rawOp = node.operator || node.op;
1255
+ const op = opMap[rawOp] || rawOp;
1256
+ const arg = this._genExpr(node.argument || node.operand);
1257
+ // Word-like unary operators (typeof/await/yield/delete/void) need a space.
1258
+ const wordOps = new Set(['typeof', 'await', 'yield', 'delete', 'void', 'new', 'instanceof', 'not', 'in']);
1259
+ const sep = wordOps.has(op) ? ' ' : '';
1260
+ return node.prefix !== false ? `${op}${sep}${arg}` : `${arg}${op}`;
1261
+ }
1262
+
1263
+ _genLogicalExpr(node) {
1264
+ const l = this._genExpr(node.left);
1265
+ const r = this._genExpr(node.right);
1266
+ const opMap = { 'and': '&&', 'or': '||', '&&': '&&', '||': '||' };
1267
+ const rawOp = node.operator || node.op;
1268
+ const op = opMap[rawOp] || rawOp;
1269
+ return `(${l} ${op} ${r})`;
1270
+ }
1271
+
1272
+ _genAssignExpr(node) {
1273
+ const l = this._genExpr(node.left);
1274
+ const r = this._genExpr(node.right);
1275
+ const op = node.operator || node.op || '=';
1276
+ return `${l} ${op} ${r}`;
1277
+ }
1278
+
1279
+ _genCallExpr(node) {
1280
+ const callee = this._genExpr(node.callee);
1281
+ const args = (node.args || node.arguments || []).map(a => this._genExpr(a)).join(', ');
1282
+ const opt = node.optional ? '?.' : '';
1283
+
1284
+ // Intercept ThyScript built-in calls and map them
1285
+ const builtins = {
1286
+ 'print': `__thy_print(${args})`,
1287
+ 'len': `__thy_len(${args})`,
1288
+ 'range': `__thy_range(${args})`,
1289
+ 'sleep': `await __thy_stdlib.time.sleep(${args})`,
1290
+ 'fetch': `await __thy_stdlib.http.fetch(${args})`,
1291
+ 'hash': `__thy_stdlib.security.hash(${args})`,
1292
+ 'save': `__thy_stdlib.persist.save(${args})`,
1293
+ 'load': `__thy_stdlib.persist.load(${args})`,
1294
+ 'type': `__thy_stdlib.types.type(${args})`,
1295
+ 'int': `parseInt(${args})`,
1296
+ 'float': `parseFloat(${args})`,
1297
+ 'str': `String(${args})`,
1298
+ 'bool': `Boolean(${args})`,
1299
+ 'abs': `Math.abs(${args})`,
1300
+ 'sqrt': `Math.sqrt(${args})`,
1301
+ 'floor': `Math.floor(${args})`,
1302
+ 'ceil': `Math.ceil(${args})`,
1303
+ 'round': `Math.round(${args})`,
1304
+ 'min': `Math.min(${args})`,
1305
+ 'max': `Math.max(${args})`,
1306
+ 'pow': `Math.pow(${args})`,
1307
+ 'random': `Math.random()`,
1308
+ 'input': `await __thy_input(${args})`,
1309
+ 'calculate': `__thy_stdlib.__thy_calculate(${args})`,
1310
+ };
1311
+
1312
+ const calleeStr = typeof node.callee === 'object' && node.callee.type === 'Identifier'
1313
+ ? node.callee.name : null;
1314
+
1315
+ if (calleeStr && builtins[calleeStr]) return builtins[calleeStr];
1316
+
1317
+ const awaitPrefix = node.isAwaited ? 'await ' : '';
1318
+ return `${awaitPrefix}${callee}${opt}(${args})`;
1319
+ }
1320
+
1321
+ _genMemberExpr(node) {
1322
+ const obj_ = this._genExpr(node.object);
1323
+ const prop = node.computed
1324
+ ? `[${this._genExpr(node.property)}]`
1325
+ : `.${typeof node.property === 'string' ? node.property : this._genExpr(node.property)}`;
1326
+ const opt = node.optional ? '?' : '';
1327
+ return `${obj_}${opt}${prop}`;
1328
+ }
1329
+
1330
+ _genIndexExpr(node) {
1331
+ const obj_ = this._genExpr(node.object);
1332
+ const idx = this._genExpr(node.index);
1333
+ return `${obj_}[${idx}]`;
1334
+ }
1335
+
1336
+ _genArrayExpr(node) {
1337
+ const elems = (node.elements || []).map(e => e ? this._genExpr(e) : '').join(', ');
1338
+ return `[${elems}]`;
1339
+ }
1340
+
1341
+ _genObjectExpr(node) {
1342
+ const props = (node.properties || []).map(p => {
1343
+ if (p.spread) return `...${this._genExpr(p.argument)}`;
1344
+ const key = p.computed ? `[${this._genExpr(p.key)}]` :
1345
+ (typeof p.key === 'string' ? p.key : this._genExpr(p.key));
1346
+ if (p.shorthand) return key;
1347
+ const val = this._genExpr(p.value);
1348
+ if (p.method) {
1349
+ const isAsync = p.async ? 'async ' : '';
1350
+ const isGen = p.generator ? '*' : '';
1351
+ const params = (p.params || []).join(', ');
1352
+ return `${isAsync}${isGen}${key}(${params}) { ${val} }`;
1353
+ }
1354
+ return `${key}: ${val}`;
1355
+ }).join(', ');
1356
+ return `{ ${props} }`;
1357
+ }
1358
+
1359
+ _genArrowFunc(node) {
1360
+ const params = (node.params || []).map(p =>
1361
+ typeof p === 'string' ? p : p.name || this._genExpr(p)).join(', ');
1362
+ const isAsync = node.isAsync ? 'async ' : '';
1363
+ if (node.expression || (node.body && node.body.type !== 'Block' && !Array.isArray(node.body))) {
1364
+ return `${isAsync}(${params}) => ${this._genExpr(node.body)}`;
1365
+ }
1366
+ const lines = [];
1367
+ const savedBody = this._bodyLines;
1368
+ this._bodyLines = lines;
1369
+ const savedIndent = this._indent;
1370
+ this._indent = 0;
1371
+ this._genBlockOrList(node.body);
1372
+ this._bodyLines = savedBody;
1373
+ this._indent = savedIndent;
1374
+ return `${isAsync}(${params}) => { ${lines.join(' ')} }`;
1375
+ }
1376
+
1377
+ _genFunctionExpr(node) {
1378
+ const name = node.name ? ` ${node.name}` : '';
1379
+ const params = (node.params || []).map(p =>
1380
+ typeof p === 'string' ? p : p.name || this._genExpr(p)).join(', ');
1381
+ const isAsync = node.isAsync ? 'async ' : '';
1382
+ const lines = [];
1383
+ const savedBody = this._bodyLines;
1384
+ this._bodyLines = lines;
1385
+ const savedIndent = this._indent;
1386
+ this._indent = 0;
1387
+ this._genBlockOrList(node.body);
1388
+ this._bodyLines = savedBody;
1389
+ this._indent = savedIndent;
1390
+ return `${isAsync}function${name}(${params}) { ${lines.join(' ')} }`;
1391
+ }
1392
+
1393
+ _genConditionalExpr(node) {
1394
+ const test = this._genExpr(node.test);
1395
+ const cons = this._genExpr(node.consequent);
1396
+ const alt = this._genExpr(node.alternate);
1397
+ return `(${test} ? ${cons} : ${alt})`;
1398
+ }
1399
+
1400
+ _genTemplateLiteral(node) {
1401
+ // node.parts is an array of { type: 'str'|'expr', value }
1402
+ const parts = (node.parts || node.quasis || []).map(p => {
1403
+ if (p.type === 'str' || p.type === 'TemplateElement') {
1404
+ const raw = (p.value && p.value.raw) ? p.value.raw : String(p.value || '');
1405
+ return raw;
1406
+ }
1407
+ // expr part: the embedded expression is stored in p.value (sub-AST)
1408
+ // or p.expression (ESTree-style). Fall back to the part itself.
1409
+ const exprNode = (p.expression != null) ? p.expression : (p.value != null ? p.value : p);
1410
+ return `\${${this._genExpr(exprNode)}}`;
1411
+ });
1412
+ return '`' + parts.join('') + '`';
1413
+ }
1414
+
1415
+ _genNewExpr(node) {
1416
+ let ctorNode = node.callee;
1417
+ let argsList = node.args || node.arguments || [];
1418
+ if (ctorNode && ctorNode.type === 'CallExpr') {
1419
+ argsList = ctorNode.args || ctorNode.arguments || [];
1420
+ ctorNode = ctorNode.callee;
1421
+ }
1422
+ const ctor = this._genExpr(ctorNode);
1423
+ const args = argsList.map(a => this._genExpr(a)).join(', ');
1424
+ return `new ${ctor}(${args})`;
1425
+ }
1426
+
1427
+ // ─── math expression (fast-math) ─────────────────────────────────────────────
1428
+
1429
+ _genMathExpr(node) {
1430
+ // Use typed-array backed computation for numeric heavy paths
1431
+ // V8 will emit efficient machine code for monomorphic Float64 ops
1432
+ const expr = this._genExpr(node.expression);
1433
+ return `(__thy_f64[0] = ${expr}, __thy_f64[0])`;
1434
+ }
1435
+
1436
+ _genVectorExpr(node) {
1437
+ const components = (node.components || []).map(c => this._genExpr(c)).join(', ');
1438
+ return `new Float64Array([${components}])`;
1439
+ }
1440
+
1441
+ _genMatrixExpr(node) {
1442
+ const rows = (node.rows || []).map(r =>
1443
+ `[${(r || []).map(c => this._genExpr(c)).join(', ')}]`
1444
+ ).join(', ');
1445
+ return `[${rows}]`;
1446
+ }
1447
+ }
1448
+
1449
+ module.exports = CodeGenerator;