novac 2.1.1 → 2.2.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.
- package/LICENSE +1 -1
- package/README.md +0 -0
- package/demo.nv +0 -0
- package/demo_builtins.nv +0 -0
- package/demo_http.nv +0 -0
- package/examples/bf.nv +69 -0
- package/examples/math.nv +21 -0
- package/kits/birdAPI/kitdef.js +954 -0
- package/kits/kitRNG/kitdef.js +740 -0
- package/kits/kitSSH/kitdef.js +1272 -0
- package/kits/kitadb/kitdef.js +606 -0
- package/kits/kitai/kitdef.js +2185 -0
- package/kits/kitcanvas/kitdef.js +914 -0
- package/kits/kitclippy/kitdef.js +925 -0
- package/kits/kitgps/kitdef.js +1862 -0
- package/kits/kitlibproc/kitdef.js +3 -2
- package/kits/kitmorse/kitdef.js +229 -0
- package/kits/kitmpatch/kitdef.js +906 -0
- package/kits/kitnet/kitdef.js +1401 -0
- package/kits/kitproto/kitdef.js +613 -0
- package/kits/kitqr/kitdef.js +637 -0
- package/kits/kitrequire/kitdef.js +1599 -0
- package/kits/libtea/kitdef.js +2691 -0
- package/kits/libterm/kitdef.js +2 -0
- package/novac/LICENSE +21 -0
- package/novac/README.md +1823 -0
- package/novac/bin/novac +950 -0
- package/novac/bin/nvc +522 -0
- package/novac/bin/nvml +542 -0
- package/novac/demo.nv +245 -0
- package/novac/demo_builtins.nv +209 -0
- package/novac/demo_http.nv +62 -0
- package/novac/examples/bf.nv +69 -0
- package/novac/examples/math.nv +21 -0
- package/novac/kits/kitai/kitdef.js +2185 -0
- package/novac/kits/kitansi/kitdef.js +1402 -0
- package/novac/kits/kitformat/kitdef.js +1485 -0
- package/novac/kits/kitgps/kitdef.js +1862 -0
- package/novac/kits/kitlibfs/kitdef.js +231 -0
- package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
- package/novac/kits/kitmatrix/ex.js +19 -0
- package/novac/kits/kitmatrix/kitdef.js +960 -0
- package/novac/kits/kitmpatch/kitdef.js +906 -0
- package/novac/kits/kitnovacweb/README.md +1572 -0
- package/novac/kits/kitnovacweb/demo.nv +12 -0
- package/novac/kits/kitnovacweb/demo.nvml +71 -0
- package/novac/kits/kitnovacweb/index.nova +12 -0
- package/novac/kits/kitnovacweb/kitdef.js +692 -0
- package/novac/kits/kitnovacweb/nova.kit.json +8 -0
- package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
- package/novac/kits/kitnovacweb/nvml/index.js +67 -0
- package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
- package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
- package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
- package/novac/kits/kitparse/kitdef.js +1688 -0
- package/novac/kits/kitregex++/kitdef.js +1353 -0
- package/novac/kits/kitrequire/kitdef.js +1599 -0
- package/novac/kits/kitx11/kitdef.js +1 -0
- package/novac/kits/kitx11/kitx11.js +2472 -0
- package/novac/kits/kitx11/kitx11_conn.js +948 -0
- package/novac/kits/kitx11/kitx11_worker.js +121 -0
- package/novac/kits/libterm/ex.js +285 -0
- package/novac/kits/libterm/kitdef.js +1927 -0
- package/novac/node_modules/chalk/license +9 -0
- package/novac/node_modules/chalk/package.json +83 -0
- package/novac/node_modules/chalk/readme.md +297 -0
- package/novac/node_modules/chalk/source/index.d.ts +325 -0
- package/novac/node_modules/chalk/source/index.js +225 -0
- package/novac/node_modules/chalk/source/utilities.js +33 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/novac/node_modules/commander/LICENSE +22 -0
- package/novac/node_modules/commander/Readme.md +1176 -0
- package/novac/node_modules/commander/esm.mjs +16 -0
- package/novac/node_modules/commander/index.js +24 -0
- package/novac/node_modules/commander/lib/argument.js +150 -0
- package/novac/node_modules/commander/lib/command.js +2777 -0
- package/novac/node_modules/commander/lib/error.js +39 -0
- package/novac/node_modules/commander/lib/help.js +747 -0
- package/novac/node_modules/commander/lib/option.js +380 -0
- package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/novac/node_modules/commander/package-support.json +19 -0
- package/novac/node_modules/commander/package.json +82 -0
- package/novac/node_modules/commander/typings/esm.d.mts +3 -0
- package/novac/node_modules/commander/typings/index.d.ts +1113 -0
- package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
- package/novac/node_modules/node-addon-api/README.md +95 -0
- package/novac/node_modules/node-addon-api/common.gypi +21 -0
- package/novac/node_modules/node-addon-api/except.gypi +25 -0
- package/novac/node_modules/node-addon-api/index.js +14 -0
- package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
- package/novac/node_modules/node-addon-api/napi.h +3364 -0
- package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
- package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
- package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/novac/node_modules/node-addon-api/package-support.json +21 -0
- package/novac/node_modules/node-addon-api/package.json +480 -0
- package/novac/node_modules/node-addon-api/tools/README.md +73 -0
- package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/novac/node_modules/serialize-javascript/LICENSE +27 -0
- package/novac/node_modules/serialize-javascript/README.md +149 -0
- package/novac/node_modules/serialize-javascript/index.js +297 -0
- package/novac/node_modules/serialize-javascript/package.json +33 -0
- package/novac/package.json +27 -0
- package/novac/scripts/update-bin.js +24 -0
- package/novac/src/core/bstd.js +1035 -0
- package/novac/src/core/config.js +155 -0
- package/novac/src/core/describe.js +187 -0
- package/novac/src/core/emitter.js +499 -0
- package/novac/src/core/error.js +86 -0
- package/novac/src/core/executor.js +5606 -0
- package/novac/src/core/formatter.js +686 -0
- package/novac/src/core/lexer.js +1026 -0
- package/novac/src/core/nova_builtins.js +717 -0
- package/novac/src/core/nova_thread_worker.js +166 -0
- package/novac/src/core/parser.js +2181 -0
- package/novac/src/core/types.js +112 -0
- package/novac/src/index.js +28 -0
- package/novac/src/runtime/stdlib.js +244 -0
- package/package.json +3 -2
- package/scripts/update-bin.js +0 -0
- package/src/core/bstd.js +835 -361
- package/src/core/executor.js +427 -246
- package/src/core/lexer.js +19 -2
- package/src/core/parser.js +13 -0
- package/src/index.js +0 -0
- package/examples/example-project/README.md +0 -3
- package/examples/example-project/src/main.nova +0 -3
- package/src/core/environment.js +0 -0
- /package/{kits → novac/kits}/libtea/tf.js +0 -0
- /package/{examples/example-project/bin/example-project.nv → novac/node_modules/node-addon-api/nothing.c} +0 -0
package/src/core/executor.js
CHANGED
|
@@ -4,6 +4,7 @@ const {
|
|
|
4
4
|
NovaStruct, NovaEnum, NOVA_META,
|
|
5
5
|
} = require('./types.js');
|
|
6
6
|
const { CustomError, formatError, NovaException } = require('./error');
|
|
7
|
+
const { atob } = require('buffer');
|
|
7
8
|
|
|
8
9
|
// ── Default sentinel ─────────────────────────────────────────────────────────
|
|
9
10
|
// When `default` is passed as a function argument, the parameter's declared
|
|
@@ -193,8 +194,34 @@ class Executor {
|
|
|
193
194
|
this.novaServers = new Map(); // port -> routes[], for in-process dispatch
|
|
194
195
|
this.dot_commands = {
|
|
195
196
|
print: (...args) => {
|
|
196
|
-
|
|
197
|
-
}
|
|
197
|
+
console.log(...args.map(a => _self.evaluate(a)));
|
|
198
|
+
},
|
|
199
|
+
clear: () => process.stdout.write('\x1Bc'),
|
|
200
|
+
code: () => 0, // no-op, used for doc comments that should be ignored by the executor but visible to external tools
|
|
201
|
+
zap: (target) => {
|
|
202
|
+
const t = this.evaluate(target);
|
|
203
|
+
if (t instanceof NovaObject) {
|
|
204
|
+
for (const k of Object.keys(t.inner)) delete t.inner[k];
|
|
205
|
+
} else if (t instanceof NovaStruct) {
|
|
206
|
+
for (const k of Object.keys(t.inner)) delete t.inner[k];
|
|
207
|
+
} else if (t && typeof t === 'object') {
|
|
208
|
+
for (const k of Object.keys(t)) delete t[k];
|
|
209
|
+
} else {
|
|
210
|
+
throw new Error('.zap target must be an object or struct instance');
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
cookie() {
|
|
214
|
+
const asciiCookie = `(\\_/)
|
|
215
|
+
( •_•)
|
|
216
|
+
/ >🍪 Here's a cookie for you!`;
|
|
217
|
+
console.log(asciiCookie);
|
|
218
|
+
},
|
|
219
|
+
js: (code) => {
|
|
220
|
+
const codeStr = this.evaluate(code);
|
|
221
|
+
if (typeof codeStr === 'string') eval(codeStr);
|
|
222
|
+
else if (codeStr instanceof NovaString) eval(codeStr.value);
|
|
223
|
+
else throw new Error('.js command requires a string argument');
|
|
224
|
+
},
|
|
198
225
|
}
|
|
199
226
|
this.moduleExportsStack = [];
|
|
200
227
|
this.options = {};
|
|
@@ -203,21 +230,44 @@ class Executor {
|
|
|
203
230
|
this.globalScope.variables.std = stdlib;
|
|
204
231
|
const self = this;
|
|
205
232
|
|
|
206
|
-
this.globalScope.set('
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
this.globalScope.set('
|
|
233
|
+
this.globalScope.set('avaibleKits', (() => {
|
|
234
|
+
const kits = [];
|
|
235
|
+
const kitsDir = require('path').join(__dirname, '../..', 'kits');
|
|
236
|
+
try {
|
|
237
|
+
const fs = require('fs');
|
|
238
|
+
for (const kitName of fs.readdirSync(kitsDir)) {
|
|
239
|
+
kits.push(kitName)
|
|
240
|
+
}
|
|
241
|
+
} catch (e) {
|
|
242
|
+
console.warn('Could not load kits:', e.message);
|
|
243
|
+
}
|
|
244
|
+
return kits;
|
|
245
|
+
})());
|
|
246
|
+
this.globalScope.set('setTimeout', setTimeout);
|
|
247
|
+
this.globalScope.set('clearTimeout', clearTimeout);
|
|
248
|
+
this.globalScope.set('setInterval', setInterval);
|
|
249
|
+
this.globalScope.set('clearInterval', clearInterval);
|
|
250
|
+
this.globalScope.set('setImmediate', setImmediate);
|
|
251
|
+
this.globalScope.set('clearImmediate', clearImmediate);
|
|
252
|
+
this.globalScope.set('queueMicrotask', queueMicrotask);
|
|
253
|
+
this.globalScope.set('parseInt', parseInt);
|
|
254
|
+
this.globalScope.set('parseFloat', parseFloat);
|
|
255
|
+
this.globalScope.set('isNaN', isNaN);
|
|
256
|
+
this.globalScope.set('isFinite', isFinite);
|
|
257
|
+
this.globalScope.set('sleepAsync', (ms) => new Promise(r => setTimeout(r, ms)));
|
|
258
|
+
this.globalScope.set('Scope', Scope);
|
|
259
|
+
this.globalScope.set('Atomics', Atomics);
|
|
260
|
+
this.globalScope.set('SharedArrayBuffer', SharedArrayBuffer);
|
|
261
|
+
this.globalScope.set('Buffer', Buffer);
|
|
262
|
+
this.globalScope.set('CError', CustomError);
|
|
263
|
+
this.globalScope.set('Error', Error);
|
|
264
|
+
//remaining built in nodejs variables and objects excluding global, process, console (which we want to control access to), and a few others for safety
|
|
265
|
+
this.globalScope.set('textEncoder', TextEncoder);
|
|
266
|
+
this.globalScope.set('textDecoder', TextDecoder);
|
|
267
|
+
this.globalScope.set('Util', require('util'));
|
|
268
|
+
this.globalScope.set('Date', Date);
|
|
269
|
+
// Expose the default sentinel so runtime code can detect it
|
|
270
|
+
this.globalScope.set('__DEFAULT_SENTINEL__', DEFAULT_SENTINEL);
|
|
221
271
|
|
|
222
272
|
this.globalScope.set('core', {
|
|
223
273
|
getAst: () => this.ast,
|
|
@@ -314,7 +364,7 @@ this.globalScope.set('__DEFAULT_SENTINEL__', DEFAULT_SENTINEL);
|
|
|
314
364
|
check: (a, pred) => typeof pred === 'function' ? pred(a) : (_qae[pred] ? _qae[pred](a) : false),
|
|
315
365
|
};
|
|
316
366
|
this.globalScope.set('qae', _qae);
|
|
317
|
-
this.globalScope.set('
|
|
367
|
+
this.globalScope.set('cregex', (pattern, flags) => this._novaRegex(pattern, flags || ''));
|
|
318
368
|
|
|
319
369
|
// ── Sync HTTP fetch ──
|
|
320
370
|
const _self = this;
|
|
@@ -423,7 +473,7 @@ this.globalScope.set('__DEFAULT_SENTINEL__', DEFAULT_SENTINEL);
|
|
|
423
473
|
info: (msg) => process.stdout.write('INFO: ' + String(msg) + '\n'),
|
|
424
474
|
};
|
|
425
475
|
this.globalScope.set('nvk', nvk);
|
|
426
|
-
|
|
476
|
+
this.globalScope.set('charAt', String.fromCharCode);
|
|
427
477
|
// ── waitsync(fn, ...args) ─────────────────────────────────────────────
|
|
428
478
|
// Calls fn(...args) and blocks synchronously until the result settles,
|
|
429
479
|
// whether the function is:
|
|
@@ -457,14 +507,14 @@ this.globalScope.set('__DEFAULT_SENTINEL__', DEFAULT_SENTINEL);
|
|
|
457
507
|
// Shared memory layout (Int32Array, 3 slots):
|
|
458
508
|
// [0] flag: 0=pending, 1=resolved, 2=rejected
|
|
459
509
|
// [1-2] unused (value is passed via a separate Buffer)
|
|
460
|
-
const sab
|
|
461
|
-
const flag
|
|
510
|
+
const sab = new SharedArrayBuffer(4);
|
|
511
|
+
const flag = new Int32Array(sab);
|
|
462
512
|
|
|
463
513
|
// We pass the resolved/rejected value as a JSON string through a
|
|
464
514
|
// larger shared buffer (up to 4 MB). For values that can't be
|
|
465
515
|
// JSON-serialised we fall back to null.
|
|
466
516
|
const valueBufSize = 4 * 1024 * 1024; // 4 MB
|
|
467
|
-
const valueSab
|
|
517
|
+
const valueSab = new SharedArrayBuffer(valueBufSize + 4); // +4 for length prefix
|
|
468
518
|
const valueMeta = new Int32Array(valueSab, 0, 1); // [0] = byte length
|
|
469
519
|
const valueData = new Uint8Array(valueSab, 4); // rest = UTF-8 bytes
|
|
470
520
|
|
|
@@ -520,9 +570,9 @@ p.then((v) => {
|
|
|
520
570
|
// Atomics.wait call. The main thread does the actual resolution.
|
|
521
571
|
result.then((v) => {
|
|
522
572
|
try {
|
|
523
|
-
const json
|
|
573
|
+
const json = JSON.stringify(v === undefined ? null : v);
|
|
524
574
|
const bytes = Buffer.from(json, 'utf8');
|
|
525
|
-
const len
|
|
575
|
+
const len = Math.min(bytes.length, valueData.length);
|
|
526
576
|
bytes.copy(Buffer.from(valueData.buffer, valueData.byteOffset), 0, 0, len);
|
|
527
577
|
Atomics.store(valueMeta, 0, len);
|
|
528
578
|
Atomics.store(flag, 0, 1);
|
|
@@ -534,9 +584,9 @@ p.then((v) => {
|
|
|
534
584
|
}
|
|
535
585
|
}).catch((e) => {
|
|
536
586
|
try {
|
|
537
|
-
const msg
|
|
587
|
+
const msg = e && e.message ? e.message : String(e);
|
|
538
588
|
const bytes = Buffer.from(JSON.stringify(msg), 'utf8');
|
|
539
|
-
const len
|
|
589
|
+
const len = Math.min(bytes.length, valueData.length);
|
|
540
590
|
bytes.copy(Buffer.from(valueData.buffer, valueData.byteOffset), 0, 0, len);
|
|
541
591
|
Atomics.store(valueMeta, 0, len);
|
|
542
592
|
} catch (_) { Atomics.store(valueMeta, 0, 0); }
|
|
@@ -598,19 +648,19 @@ Atomics.wait(flag, 0, 0);
|
|
|
598
648
|
|
|
599
649
|
const { Worker } = require('worker_threads');
|
|
600
650
|
|
|
601
|
-
const sab
|
|
602
|
-
const flag
|
|
651
|
+
const sab = new SharedArrayBuffer(4);
|
|
652
|
+
const flag = new Int32Array(sab);
|
|
603
653
|
const valueBufSize = 4 * 1024 * 1024;
|
|
604
|
-
const valueSab
|
|
605
|
-
const valueMeta
|
|
606
|
-
const valueData
|
|
654
|
+
const valueSab = new SharedArrayBuffer(valueBufSize + 4);
|
|
655
|
+
const valueMeta = new Int32Array(valueSab, 0, 1);
|
|
656
|
+
const valueData = new Uint8Array(valueSab, 4);
|
|
607
657
|
|
|
608
658
|
// Attach resolution handlers in the main thread — microtasks fire here
|
|
609
659
|
promise.then((v) => {
|
|
610
660
|
try {
|
|
611
|
-
const json
|
|
661
|
+
const json = JSON.stringify(v === undefined ? null : v);
|
|
612
662
|
const bytes = Buffer.from(json, 'utf8');
|
|
613
|
-
const len
|
|
663
|
+
const len = Math.min(bytes.length, valueData.length);
|
|
614
664
|
bytes.copy(Buffer.from(valueData.buffer, valueData.byteOffset), 0, 0, len);
|
|
615
665
|
Atomics.store(valueMeta, 0, len);
|
|
616
666
|
Atomics.store(flag, 0, 1);
|
|
@@ -622,9 +672,9 @@ Atomics.wait(flag, 0, 0);
|
|
|
622
672
|
}
|
|
623
673
|
}).catch((e) => {
|
|
624
674
|
try {
|
|
625
|
-
const msg
|
|
675
|
+
const msg = e && e.message ? e.message : String(e);
|
|
626
676
|
const bytes = Buffer.from(JSON.stringify(msg), 'utf8');
|
|
627
|
-
const len
|
|
677
|
+
const len = Math.min(bytes.length, valueData.length);
|
|
628
678
|
bytes.copy(Buffer.from(valueData.buffer, valueData.byteOffset), 0, 0, len);
|
|
629
679
|
Atomics.store(valueMeta, 0, len);
|
|
630
680
|
} catch (_) { Atomics.store(valueMeta, 0, 0); }
|
|
@@ -643,7 +693,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
643
693
|
Atomics.wait(flag, 0, 0);
|
|
644
694
|
waiter.terminate();
|
|
645
695
|
|
|
646
|
-
const status
|
|
696
|
+
const status = Atomics.load(flag, 0);
|
|
647
697
|
const byteLen = Atomics.load(valueMeta, 0);
|
|
648
698
|
let parsed = null;
|
|
649
699
|
if (byteLen > 0) {
|
|
@@ -777,8 +827,8 @@ Atomics.wait(flag, 0, 0);
|
|
|
777
827
|
// guards, interceptors, tasks/jobs, handlers, services, resources,
|
|
778
828
|
// schemas — all injected as plain named functions/objects in scope
|
|
779
829
|
const injectGroups = [
|
|
780
|
-
'filters','transforms','validators','decorators','reducers','effects',
|
|
781
|
-
'guards','interceptors','handlers','services','resources','schemas',
|
|
830
|
+
'filters', 'transforms', 'validators', 'decorators', 'reducers', 'effects',
|
|
831
|
+
'guards', 'interceptors', 'handlers', 'services', 'resources', 'schemas',
|
|
782
832
|
];
|
|
783
833
|
for (const group of injectGroups) {
|
|
784
834
|
const g = mod[group] || {};
|
|
@@ -813,13 +863,13 @@ Atomics.wait(flag, 0, 0);
|
|
|
813
863
|
// init / setup — called once at load time
|
|
814
864
|
const initFn = mod.init || mod.setup;
|
|
815
865
|
if (typeof initFn === 'function') {
|
|
816
|
-
try { initFn(_self); } catch (_) {}
|
|
866
|
+
try { initFn(_self); } catch (_) { }
|
|
817
867
|
}
|
|
818
868
|
|
|
819
869
|
// teardown — called at process exit
|
|
820
870
|
const teardownFn = mod.teardown;
|
|
821
871
|
if (typeof teardownFn === 'function') {
|
|
822
|
-
process.on('exit', () => { try { teardownFn(); } catch (_) {} });
|
|
872
|
+
process.on('exit', () => { try { teardownFn(); } catch (_) { } });
|
|
823
873
|
}
|
|
824
874
|
|
|
825
875
|
// meta / docs / provides / permissions / dependencies — informational only
|
|
@@ -842,10 +892,10 @@ Atomics.wait(flag, 0, 0);
|
|
|
842
892
|
// merges any exported names directly into it (same as a bare import).
|
|
843
893
|
// Accepts .nova / .nv (parsed + executed) or .js (require()'d as a JS kit).
|
|
844
894
|
this.globalScope.set('include', (filePath) => {
|
|
845
|
-
const _fs
|
|
895
|
+
const _fs = require('fs');
|
|
846
896
|
const _pth = require('path');
|
|
847
|
-
const src
|
|
848
|
-
const abs
|
|
897
|
+
const src = String(filePath);
|
|
898
|
+
const abs = _pth.isAbsolute(src)
|
|
849
899
|
? src
|
|
850
900
|
: _pth.resolve(process.cwd(), src);
|
|
851
901
|
|
|
@@ -890,25 +940,24 @@ Atomics.wait(flag, 0, 0);
|
|
|
890
940
|
// falling back to index.nova if no kitdef.js exists.
|
|
891
941
|
// The kit's exported namespace is merged into global scope AND returned.
|
|
892
942
|
this.globalScope.set('includeKit', (kitName) => {
|
|
893
|
-
const _fs
|
|
943
|
+
const _fs = require('fs');
|
|
894
944
|
const _pth = require('path');
|
|
895
945
|
const name = String(kitName);
|
|
896
946
|
const kitsRoot = _pth.join(__dirname, '..', '..', 'kits');
|
|
897
|
-
const kitDir
|
|
947
|
+
const kitDir = _pth.join(kitsRoot, name);
|
|
898
948
|
|
|
899
949
|
if (!_fs.existsSync(kitDir)) {
|
|
900
950
|
throw new Error(`includeKit: kit "${name}" not found at ${kitDir}`);
|
|
901
951
|
}
|
|
902
952
|
|
|
903
|
-
const kitdefPath
|
|
904
|
-
const indexNova
|
|
905
|
-
const indexNv
|
|
953
|
+
const kitdefPath = _pth.join(kitDir, 'kitdef.js');
|
|
954
|
+
const indexNova = _pth.join(kitDir, 'index.nova');
|
|
955
|
+
const indexNv = _pth.join(kitDir, 'index.nv');
|
|
906
956
|
|
|
907
957
|
if (_fs.existsSync(kitdefPath)) {
|
|
908
958
|
// JS kit — load via require and process all metadata fields
|
|
909
959
|
const mod = require(kitdefPath);
|
|
910
960
|
if (!mod || !mod.kitdef) throw new Error(`includeKit: "${name}/kitdef.js" must export { kitdef }`);
|
|
911
|
-
_self._processKitMod(mod, name);
|
|
912
961
|
return new NovaObject(mod.kitdef);
|
|
913
962
|
}
|
|
914
963
|
|
|
@@ -941,6 +990,16 @@ Atomics.wait(flag, 0, 0);
|
|
|
941
990
|
return '';
|
|
942
991
|
});
|
|
943
992
|
|
|
993
|
+
this.globalScope.set('printr', (...args) => {
|
|
994
|
+
process.stdout.write(args.join(' ') + '\n');
|
|
995
|
+
return '';
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
this.globalScope.set('eprintr', (...args) => {
|
|
999
|
+
process.stderr.write(args.join(' ') + '\n');
|
|
1000
|
+
return '';
|
|
1001
|
+
});
|
|
1002
|
+
|
|
944
1003
|
// ── println(...args) / eprint(...args) ──────────────────────────────
|
|
945
1004
|
this.globalScope.set('println', (...args) => {
|
|
946
1005
|
process.stdout.write(args.map(a => _self.stringify(a)).join(' ') + '\n');
|
|
@@ -955,7 +1014,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
955
1014
|
// Pretty-prints a value with type info — useful for debugging.
|
|
956
1015
|
this.globalScope.set('dump', (v) => {
|
|
957
1016
|
const type = _self._typeOf(v);
|
|
958
|
-
const str
|
|
1017
|
+
const str = _self.stringify(v);
|
|
959
1018
|
process.stdout.write(`[${type}] ${str}\n`);
|
|
960
1019
|
return v;
|
|
961
1020
|
});
|
|
@@ -978,7 +1037,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
978
1037
|
if (st === 0) throw new Error('range: step cannot be 0');
|
|
979
1038
|
const out = [];
|
|
980
1039
|
if (st > 0) { for (let i = s; i < e; i += st) out.push(i); }
|
|
981
|
-
else
|
|
1040
|
+
else { for (let i = s; i > e; i += st) out.push(i); }
|
|
982
1041
|
return new NovaArray(out);
|
|
983
1042
|
});
|
|
984
1043
|
|
|
@@ -1011,17 +1070,17 @@ Atomics.wait(flag, 0, 0);
|
|
|
1011
1070
|
|
|
1012
1071
|
// ── parseInt / parseFloat / isNaN / isFinite ─────────────────────────
|
|
1013
1072
|
// Expose JS globals directly so Nova scripts can use them without prefix.
|
|
1014
|
-
this.globalScope.set('parseInt',
|
|
1015
|
-
this.globalScope.set('parseFloat', (v)
|
|
1016
|
-
this.globalScope.set('isNaN',
|
|
1017
|
-
this.globalScope.set('isFinite',
|
|
1073
|
+
this.globalScope.set('parseInt', (v, radix) => parseInt(String(v), radix));
|
|
1074
|
+
this.globalScope.set('parseFloat', (v) => parseFloat(String(v)));
|
|
1075
|
+
this.globalScope.set('isNaN', (v) => isNaN(v));
|
|
1076
|
+
this.globalScope.set('isFinite', (v) => isFinite(v));
|
|
1018
1077
|
|
|
1019
1078
|
// ── keys(obj) / values(obj) / entries(obj) ──────────────────────────
|
|
1020
|
-
this.globalScope.set('keys',
|
|
1079
|
+
this.globalScope.set('keys', (o) => {
|
|
1021
1080
|
const raw = o instanceof NovaObject ? o.inner : o;
|
|
1022
1081
|
return new NovaArray(Object.keys(raw ?? {}));
|
|
1023
1082
|
});
|
|
1024
|
-
this.globalScope.set('values',
|
|
1083
|
+
this.globalScope.set('values', (o) => {
|
|
1025
1084
|
const raw = o instanceof NovaObject ? o.inner : o;
|
|
1026
1085
|
return new NovaArray(Object.values(raw ?? {}));
|
|
1027
1086
|
});
|
|
@@ -1030,15 +1089,63 @@ Atomics.wait(flag, 0, 0);
|
|
|
1030
1089
|
return new NovaArray(Object.entries(raw ?? {}).map(([k, v]) => new NovaArray([k, v])));
|
|
1031
1090
|
});
|
|
1032
1091
|
|
|
1092
|
+
// ── Wasm ──────────────────────────────────────────────────────────────
|
|
1093
|
+
// Wasm object with many methods
|
|
1094
|
+
this.globalScope.set('Wasm', {
|
|
1095
|
+
run: async (bytes, imports) => {
|
|
1096
|
+
const module = await WebAssembly.compile(bytes instanceof NovaArray ? new Uint8Array(bytes.inner) : bytes);
|
|
1097
|
+
const instance = await WebAssembly.instantiate(module, imports instanceof NovaObject ? imports.inner : imports);
|
|
1098
|
+
if (instance.exports && typeof instance.exports._start === 'function') {
|
|
1099
|
+
return instance.exports._start();
|
|
1100
|
+
}
|
|
1101
|
+
return instance;
|
|
1102
|
+
},
|
|
1103
|
+
...WebAssembly, // re-export all WebAssembly static methods/properties
|
|
1104
|
+
})
|
|
1033
1105
|
// ── len(v) ───────────────────────────────────────────────────────────
|
|
1034
1106
|
this.globalScope.set('len', (v) => {
|
|
1035
|
-
if (v instanceof NovaArray)
|
|
1107
|
+
if (v instanceof NovaArray) return v.length;
|
|
1036
1108
|
if (v instanceof NovaObject) return Object.keys(v.inner).length;
|
|
1037
|
-
if (typeof v === 'string')
|
|
1109
|
+
if (typeof v === 'string') return v.length;
|
|
1038
1110
|
if (v && v.length !== undefined) return v.length;
|
|
1039
1111
|
return 0;
|
|
1040
1112
|
});
|
|
1041
|
-
|
|
1113
|
+
// ── new WaiterObject() ───────────────────────────────────────────────────
|
|
1114
|
+
// Expose the WaiterObject class so Nova scripts can create waiters directly.
|
|
1115
|
+
class WaiterObject { // a waiter is an object like this: { state: Int, value: any }
|
|
1116
|
+
constructor() {
|
|
1117
|
+
this._state = 0; // 0 = pending, 1 = resolved, 2 = rejected
|
|
1118
|
+
this.value = null;
|
|
1119
|
+
this.onStateChange = (newState) => newState; // override this to react to state changes
|
|
1120
|
+
}
|
|
1121
|
+
get state() { return this._state; }
|
|
1122
|
+
set state(v) { this._state = this.onStateChange(Number(v)); }
|
|
1123
|
+
}
|
|
1124
|
+
this.globalScope.set('WaiterObject', WaiterObject);
|
|
1125
|
+
// ── new Mutex() ───────────────────────────────────────────────────────────
|
|
1126
|
+
// Expose a simple Mutex class for mutual exclusion in async code.
|
|
1127
|
+
class Mutex {
|
|
1128
|
+
constructor(...waiters) {
|
|
1129
|
+
this._locked = false;
|
|
1130
|
+
this._waiters = waiters;
|
|
1131
|
+
}
|
|
1132
|
+
lock() {
|
|
1133
|
+
if (!this._locked) {
|
|
1134
|
+
this._locked = true;
|
|
1135
|
+
return Promise.resolve();
|
|
1136
|
+
}
|
|
1137
|
+
return new Promise(resolve => this._waiters.push(resolve));
|
|
1138
|
+
}
|
|
1139
|
+
unlock() {
|
|
1140
|
+
if (this._waiters.length > 0) {
|
|
1141
|
+
const next = this._waiters.shift();
|
|
1142
|
+
next();
|
|
1143
|
+
} else {
|
|
1144
|
+
this._locked = false;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
this.globalScope.set('Mutex', Mutex);
|
|
1042
1149
|
// ── typeOf_(v) ───────────────────────────────────────────────────────
|
|
1043
1150
|
// Returns a string type name for any nova value.
|
|
1044
1151
|
// Named typeOf_ to avoid collision with the `type` keyword (type declarations).
|
|
@@ -1046,8 +1153,8 @@ Atomics.wait(flag, 0, 0);
|
|
|
1046
1153
|
this.globalScope.set('typeOf_', (v) => _self._typeOf(v));
|
|
1047
1154
|
|
|
1048
1155
|
// ── str(v) / num(v) / bool(v) ────────────────────────────────────────
|
|
1049
|
-
this.globalScope.set('str',
|
|
1050
|
-
this.globalScope.set('num',
|
|
1156
|
+
this.globalScope.set('str', (v) => _self.stringify(v));
|
|
1157
|
+
this.globalScope.set('num', (v) => Number(v instanceof NovaValue ? v.valueOf() : v));
|
|
1051
1158
|
this.globalScope.set('bool', (v) => {
|
|
1052
1159
|
const raw = v instanceof NovaBool ? v.valueOf() : v;
|
|
1053
1160
|
return !!raw;
|
|
@@ -1061,8 +1168,8 @@ Atomics.wait(flag, 0, 0);
|
|
|
1061
1168
|
// Named zipArrays to avoid collision with nova's infix `zip` operator.
|
|
1062
1169
|
this.globalScope.set('zipArrays', (...arrays) => {
|
|
1063
1170
|
const arrs = arrays.map(a => a instanceof NovaArray ? a.inner : a);
|
|
1064
|
-
const len
|
|
1065
|
-
const out
|
|
1171
|
+
const len = Math.min(...arrs.map(a => a.length));
|
|
1172
|
+
const out = [];
|
|
1066
1173
|
for (let i = 0; i < len; i++) out.push(new NovaArray(arrs.map(a => a[i])));
|
|
1067
1174
|
return new NovaArray(out);
|
|
1068
1175
|
});
|
|
@@ -1121,13 +1228,13 @@ Atomics.wait(flag, 0, 0);
|
|
|
1121
1228
|
if (t === 'symbol') return v;
|
|
1122
1229
|
if (t !== 'object') return v;
|
|
1123
1230
|
if (seen.has(v)) return seen.get(v);
|
|
1124
|
-
if (v instanceof NovaBool)
|
|
1125
|
-
if (v instanceof NovaNull)
|
|
1231
|
+
if (v instanceof NovaBool) return new NovaBool(v.valueOf());
|
|
1232
|
+
if (v instanceof NovaNull) return new NovaNull();
|
|
1126
1233
|
if (v instanceof NovaNumber) return new NovaNumber(v.valueOf());
|
|
1127
1234
|
if (v instanceof NovaString) return new NovaString(v.valueOf());
|
|
1128
|
-
if (v instanceof NovaRange)
|
|
1235
|
+
if (v instanceof NovaRange) return new NovaRange(v.start, v.end, v.step);
|
|
1129
1236
|
if (v instanceof NovaPointer) return new NovaPointer(_deepClone(v.inner, seen), v.readFn, v.writeFn, v.address);
|
|
1130
|
-
if (v instanceof NovaEnum)
|
|
1237
|
+
if (v instanceof NovaEnum) { const e = new NovaEnum(v.typeName, v.variant, _deepClone(v.inner, seen)); e.value = _deepClone(v.value, seen); return e; }
|
|
1131
1238
|
if (v instanceof NovaStruct) {
|
|
1132
1239
|
const inner = {};
|
|
1133
1240
|
seen.set(v, inner); // register early to handle circular refs
|
|
@@ -1177,10 +1284,10 @@ Atomics.wait(flag, 0, 0);
|
|
|
1177
1284
|
// ── shallowClone(value) ───────────────────────────────────────────────
|
|
1178
1285
|
// One-level clone — top container is new but inner values are shared.
|
|
1179
1286
|
this.globalScope.set('shallowClone', (v) => {
|
|
1180
|
-
if (v instanceof NovaArray)
|
|
1287
|
+
if (v instanceof NovaArray) return new NovaArray([...v.inner]);
|
|
1181
1288
|
if (v instanceof NovaObject) return new NovaObject({ ...v.inner });
|
|
1182
1289
|
if (v instanceof NovaStruct) return new NovaStruct(v.typeName, { ...v.inner });
|
|
1183
|
-
if (Array.isArray(v))
|
|
1290
|
+
if (Array.isArray(v)) return [...v];
|
|
1184
1291
|
if (v && typeof v === 'object') return { ...v };
|
|
1185
1292
|
return v;
|
|
1186
1293
|
});
|
|
@@ -1193,7 +1300,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1193
1300
|
// executor's globalScope since Nova scripts can't pass their own scope ref.
|
|
1194
1301
|
this.globalScope.set('saveCurrentScope', () => {
|
|
1195
1302
|
const snap = new Scope('saved', null, _self.globalScope);
|
|
1196
|
-
const SKIP = new Set(['scope','array','std','globalScope','parent']);
|
|
1303
|
+
const SKIP = new Set(['scope', 'array', 'std', 'globalScope', 'parent']);
|
|
1197
1304
|
for (const [k, v] of Object.entries(_self.globalScope.variables)) {
|
|
1198
1305
|
if (SKIP.has(k)) continue;
|
|
1199
1306
|
try { snap.variables[k] = _deepClone(v); } catch { snap.variables[k] = v; }
|
|
@@ -1214,7 +1321,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1214
1321
|
if (!(scope instanceof Scope))
|
|
1215
1322
|
throw new Error('saveScope: argument must be a Scope (got ' + _self._typeOf(scope) + ')');
|
|
1216
1323
|
const snap = new Scope('saved', null, _self.globalScope);
|
|
1217
|
-
const SKIP = new Set(['scope','array','std','globalScope','parent']);
|
|
1324
|
+
const SKIP = new Set(['scope', 'array', 'std', 'globalScope', 'parent']);
|
|
1218
1325
|
for (const [k, v] of Object.entries(scope.variables)) {
|
|
1219
1326
|
if (SKIP.has(k)) continue;
|
|
1220
1327
|
try { snap.variables[k] = _deepClone(v); } catch { snap.variables[k] = v; }
|
|
@@ -1230,7 +1337,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1230
1337
|
// the current global scope. Good for inspection / serialization.
|
|
1231
1338
|
this.globalScope.set('scopeSnapshot', (scope) => {
|
|
1232
1339
|
const src = (scope instanceof Scope) ? scope : _self.globalScope;
|
|
1233
|
-
const SKIP = new Set(['scope','array','std','globalScope','parent']);
|
|
1340
|
+
const SKIP = new Set(['scope', 'array', 'std', 'globalScope', 'parent']);
|
|
1234
1341
|
const out = {};
|
|
1235
1342
|
for (const [k, v] of Object.entries(src.variables)) {
|
|
1236
1343
|
if (SKIP.has(k)) continue;
|
|
@@ -1285,7 +1392,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1285
1392
|
this.globalScope.set('scopeMerge', (target, source) => {
|
|
1286
1393
|
if (!(target instanceof Scope) || !(source instanceof Scope))
|
|
1287
1394
|
throw new Error('scopeMerge: both args must be Scopes');
|
|
1288
|
-
const SKIP = new Set(['scope','array','std','globalScope','parent']);
|
|
1395
|
+
const SKIP = new Set(['scope', 'array', 'std', 'globalScope', 'parent']);
|
|
1289
1396
|
for (const [k, v] of Object.entries(source.variables)) {
|
|
1290
1397
|
if (SKIP.has(k)) continue;
|
|
1291
1398
|
target.variables[k] = v;
|
|
@@ -1326,7 +1433,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1326
1433
|
// List all user-defined variable names in a scope (own only).
|
|
1327
1434
|
this.globalScope.set('scopeKeys', (scope) => {
|
|
1328
1435
|
const src = (scope instanceof Scope) ? scope : _self.globalScope;
|
|
1329
|
-
const SKIP = new Set(['scope','array','std','globalScope','parent']);
|
|
1436
|
+
const SKIP = new Set(['scope', 'array', 'std', 'globalScope', 'parent']);
|
|
1330
1437
|
return new NovaArray(Object.keys(src.variables).filter(k => !SKIP.has(k)));
|
|
1331
1438
|
});
|
|
1332
1439
|
|
|
@@ -1364,7 +1471,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1364
1471
|
// Freeze all current variables in scope as constants (no further writes).
|
|
1365
1472
|
this.globalScope.set('freezeScope', (scope) => {
|
|
1366
1473
|
const src = (scope instanceof Scope) ? scope : _self.globalScope;
|
|
1367
|
-
const SKIP = new Set(['scope','array','std','globalScope','parent']);
|
|
1474
|
+
const SKIP = new Set(['scope', 'array', 'std', 'globalScope', 'parent']);
|
|
1368
1475
|
for (const k of Object.keys(src.variables)) {
|
|
1369
1476
|
if (!SKIP.has(k)) src.consts[k] = src.variables[k];
|
|
1370
1477
|
}
|
|
@@ -1377,7 +1484,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1377
1484
|
this.globalScope.set('scopeDiff', (a, b) => {
|
|
1378
1485
|
if (!(a instanceof Scope) || !(b instanceof Scope))
|
|
1379
1486
|
throw new Error('scopeDiff: both args must be Scopes');
|
|
1380
|
-
const SKIP = new Set(['scope','array','std','globalScope','parent']);
|
|
1487
|
+
const SKIP = new Set(['scope', 'array', 'std', 'globalScope', 'parent']);
|
|
1381
1488
|
const keysA = new Set(Object.keys(a.variables).filter(k => !SKIP.has(k)));
|
|
1382
1489
|
const keysB = new Set(Object.keys(b.variables).filter(k => !SKIP.has(k)));
|
|
1383
1490
|
const onlyInA = [...keysA].filter(k => !keysB.has(k));
|
|
@@ -1401,13 +1508,13 @@ Atomics.wait(flag, 0, 0);
|
|
|
1401
1508
|
const n = String(name);
|
|
1402
1509
|
let _val = scope.variables[n];
|
|
1403
1510
|
const descriptor = {
|
|
1404
|
-
read:
|
|
1511
|
+
read: () => _val,
|
|
1405
1512
|
write: (v) => {
|
|
1406
1513
|
const old = _val; _val = v;
|
|
1407
1514
|
try {
|
|
1408
1515
|
if (typeof fn === 'function') fn(v, old);
|
|
1409
1516
|
else if (fn && fn.body !== undefined) _self.runFunctionNode(fn, _self.globalScope, [v, old]);
|
|
1410
|
-
} catch (_) {}
|
|
1517
|
+
} catch (_) { }
|
|
1411
1518
|
},
|
|
1412
1519
|
};
|
|
1413
1520
|
scope.variables[n] = descriptor;
|
|
@@ -1463,7 +1570,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1463
1570
|
const _deepFreeze = (v, seen = new WeakSet()) => {
|
|
1464
1571
|
if (!v || typeof v !== 'object' || seen.has(v)) return v;
|
|
1465
1572
|
seen.add(v);
|
|
1466
|
-
if (v instanceof NovaArray)
|
|
1573
|
+
if (v instanceof NovaArray) { Object.freeze(v.inner); v.inner.forEach(x => _deepFreeze(x, seen)); }
|
|
1467
1574
|
if (v instanceof NovaObject) { Object.freeze(v.inner); Object.values(v.inner).forEach(x => _deepFreeze(x, seen)); }
|
|
1468
1575
|
if (v instanceof NovaStruct) { Object.freeze(v.inner); Object.values(v.inner).forEach(x => _deepFreeze(x, seen)); }
|
|
1469
1576
|
return v;
|
|
@@ -1666,7 +1773,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1666
1773
|
if (st === 0) throw new Error('range2: step cannot be 0');
|
|
1667
1774
|
const out = [];
|
|
1668
1775
|
if (st > 0) { for (let i = s; i <= e; i += st) out.push(i); }
|
|
1669
|
-
else
|
|
1776
|
+
else { for (let i = s; i >= e; i += st) out.push(i); }
|
|
1670
1777
|
return new NovaArray(out);
|
|
1671
1778
|
});
|
|
1672
1779
|
|
|
@@ -1919,7 +2026,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1919
2026
|
const d = new Date(ms !== undefined ? Number(ms) : Date.now());
|
|
1920
2027
|
const pad = (n) => String(n).padStart(2, '0');
|
|
1921
2028
|
const tokens = {
|
|
1922
|
-
YYYY: d.getFullYear(), MM: pad(d.getMonth()+1), DD: pad(d.getDate()),
|
|
2029
|
+
YYYY: d.getFullYear(), MM: pad(d.getMonth() + 1), DD: pad(d.getDate()),
|
|
1923
2030
|
HH: pad(d.getHours()), mm: pad(d.getMinutes()), ss: pad(d.getSeconds()),
|
|
1924
2031
|
};
|
|
1925
2032
|
const f = String(fmt || 'YYYY-MM-DD HH:mm:ss');
|
|
@@ -1950,7 +2057,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1950
2057
|
// Return a human-readable debug string with type info.
|
|
1951
2058
|
this.globalScope.set('inspect', (v) => {
|
|
1952
2059
|
const type = _self._typeOf(v);
|
|
1953
|
-
const str
|
|
2060
|
+
const str = _self.stringify(v);
|
|
1954
2061
|
return `[${type}] ${str}`;
|
|
1955
2062
|
});
|
|
1956
2063
|
|
|
@@ -1998,7 +2105,7 @@ Atomics.wait(flag, 0, 0);
|
|
|
1998
2105
|
}
|
|
1999
2106
|
// NovaBool / NovaNumber / NovaString / NovaNull — all have _v
|
|
2000
2107
|
if (v instanceof NovaBool || v instanceof NovaNumber ||
|
|
2001
|
-
|
|
2108
|
+
v instanceof NovaString || v instanceof NovaNull) {
|
|
2002
2109
|
return { _v: v.valueOf !== undefined ? v.valueOf() : null };
|
|
2003
2110
|
}
|
|
2004
2111
|
if (v instanceof NovaRange) return { start: v.start, end: v.end };
|
|
@@ -2026,10 +2133,10 @@ Atomics.wait(flag, 0, 0);
|
|
|
2026
2133
|
if (Array.isArray(v)) return v.map(x => this._rehydrate(x));
|
|
2027
2134
|
if ('__jsfn__' in v) { try { return eval('(' + v.__jsfn__ + ')'); } catch { return null; } }
|
|
2028
2135
|
if ('_v' in v) {
|
|
2029
|
-
if (v._v === null)
|
|
2136
|
+
if (v._v === null) return new NovaNull();
|
|
2030
2137
|
if (typeof v._v === 'boolean') return new NovaBool(v._v);
|
|
2031
|
-
if (typeof v._v === 'number')
|
|
2032
|
-
if (typeof v._v === 'string')
|
|
2138
|
+
if (typeof v._v === 'number') return new NovaNumber(v._v);
|
|
2139
|
+
if (typeof v._v === 'string') return new NovaString(v._v);
|
|
2033
2140
|
return v._v;
|
|
2034
2141
|
}
|
|
2035
2142
|
if ('start' in v && 'end' in v && Object.keys(v).length === 2)
|
|
@@ -2070,12 +2177,12 @@ Atomics.wait(flag, 0, 0);
|
|
|
2070
2177
|
// Also snapshot the global user-defined variables (functions, declared vars)
|
|
2071
2178
|
// but skip built-in keys that are circular, huge, or executor-internal.
|
|
2072
2179
|
const SKIP = new Set([
|
|
2073
|
-
'scope','array','std','core','nova','nvk','qae','
|
|
2074
|
-
'setTimeout','typecheck','satisfies','typeOf','Thread',
|
|
2075
|
-
'ForLoop','WhileLoop','IfBlock','TryCatch','Pipeline','FuncDef',
|
|
2076
|
-
'MatchBlock','Timer','Counter','Stack','Queue','LinkedList',
|
|
2077
|
-
'State','Observable','Validator','DataStream','Transformer',
|
|
2078
|
-
'TransformerJSON','TransformerBase64','Router','EventBus','Memo','Lazy','Signal',
|
|
2180
|
+
'scope', 'array', 'std', 'core', 'nova', 'nvk', 'qae', 'cregex', 'fetch',
|
|
2181
|
+
'setTimeout', 'typecheck', 'satisfies', 'typeOf', 'Thread',
|
|
2182
|
+
'ForLoop', 'WhileLoop', 'IfBlock', 'TryCatch', 'Pipeline', 'FuncDef',
|
|
2183
|
+
'MatchBlock', 'Timer', 'Counter', 'Stack', 'Queue', 'LinkedList',
|
|
2184
|
+
'State', 'Observable', 'Validator', 'DataStream', 'Transformer',
|
|
2185
|
+
'TransformerJSON', 'TransformerBase64', 'Router', 'EventBus', 'Memo', 'Lazy', 'Signal',
|
|
2079
2186
|
]);
|
|
2080
2187
|
for (const [k, v] of Object.entries(this.globalScope.variables || {})) {
|
|
2081
2188
|
if (SKIP.has(k) || snap.hasOwnProperty(k)) continue;
|
|
@@ -2091,20 +2198,20 @@ Atomics.wait(flag, 0, 0);
|
|
|
2091
2198
|
_initThread() {
|
|
2092
2199
|
const _path = require('path');
|
|
2093
2200
|
const _workerScriptPath = _path.join(__dirname, 'nova_thread_worker.js');
|
|
2094
|
-
const _executorPath
|
|
2201
|
+
const _executorPath = _path.join(__dirname, 'executor.js');
|
|
2095
2202
|
const _exe = this;
|
|
2096
2203
|
|
|
2097
2204
|
class NovaThread {
|
|
2098
2205
|
constructor(fnNode, callerScope) {
|
|
2099
|
-
this._fnNode
|
|
2206
|
+
this._fnNode = fnNode;
|
|
2100
2207
|
this._callerScope = callerScope;
|
|
2101
|
-
this._worker
|
|
2102
|
-
this._mutations
|
|
2208
|
+
this._worker = null;
|
|
2209
|
+
this._mutations = []; // { name, value } buffered until join()
|
|
2103
2210
|
this._msgHandlers = [];
|
|
2104
|
-
this._done
|
|
2105
|
-
this._result
|
|
2106
|
-
this._error
|
|
2107
|
-
this._sab
|
|
2211
|
+
this._done = false;
|
|
2212
|
+
this._result = undefined;
|
|
2213
|
+
this._error = null;
|
|
2214
|
+
this._sab = new SharedArrayBuffer(8);
|
|
2108
2215
|
this._flag = new Int32Array(this._sab);
|
|
2109
2216
|
this._proxy = this._makeProxy();
|
|
2110
2217
|
}
|
|
@@ -2112,13 +2219,13 @@ Atomics.wait(flag, 0, 0);
|
|
|
2112
2219
|
_makeProxy() {
|
|
2113
2220
|
const t = this;
|
|
2114
2221
|
return new NovaObject({
|
|
2115
|
-
start:
|
|
2116
|
-
join:
|
|
2117
|
-
send:
|
|
2222
|
+
start: () => { t.start(); return t._proxy; },
|
|
2223
|
+
join: () => t.join(),
|
|
2224
|
+
send: (v) => { t.send(v); return t._proxy; },
|
|
2118
2225
|
on_message: (fn) => { t.on_message(fn); return t._proxy; },
|
|
2119
|
-
get result()
|
|
2120
|
-
get error()
|
|
2121
|
-
get done()
|
|
2226
|
+
get result() { return t._result; },
|
|
2227
|
+
get error() { return t._error; },
|
|
2228
|
+
get done() { return t._done; },
|
|
2122
2229
|
});
|
|
2123
2230
|
}
|
|
2124
2231
|
|
|
@@ -2503,143 +2610,189 @@ Atomics.wait(flag, 0, 0);
|
|
|
2503
2610
|
return content;
|
|
2504
2611
|
}
|
|
2505
2612
|
|
|
2506
|
-
stringify(v, indent = 0, seen = new WeakSet()) {
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
const nl = "\n";
|
|
2521
|
-
|
|
2522
|
-
const next = (x, i = indent + 1) =>
|
|
2523
|
-
this.stringify(x, i, seen);
|
|
2524
|
-
|
|
2525
|
-
if (v === null || v === undefined)
|
|
2526
|
-
return color.null + "null" + color.reset;
|
|
2527
|
-
|
|
2528
|
-
if (v instanceof NovaNull)
|
|
2529
|
-
return color.null + "null" + color.reset;
|
|
2613
|
+
stringify(v, indent = 0, seen = new WeakSet()) {
|
|
2614
|
+
const pad = " ".repeat(indent);
|
|
2615
|
+
|
|
2616
|
+
const color = {
|
|
2617
|
+
reset: "\x1b[0m",
|
|
2618
|
+
key: "\x1b[36m",
|
|
2619
|
+
string: "\x1b[32m",
|
|
2620
|
+
number: "\x1b[33m",
|
|
2621
|
+
bool: "\x1b[35m",
|
|
2622
|
+
null: "\x1b[90m",
|
|
2623
|
+
func: "\x1b[31m",
|
|
2624
|
+
circular: "\x1b[91m",
|
|
2625
|
+
};
|
|
2530
2626
|
|
|
2531
|
-
|
|
2532
|
-
return color.bool + String(v.valueOf()) + color.reset;
|
|
2627
|
+
const nl = "\n";
|
|
2533
2628
|
|
|
2534
|
-
|
|
2535
|
-
|
|
2629
|
+
const next = (x, i = indent + 1) =>
|
|
2630
|
+
this.stringify(x, i, seen);
|
|
2536
2631
|
|
|
2537
|
-
|
|
2538
|
-
|
|
2632
|
+
if (v === null || v === undefined)
|
|
2633
|
+
return color.null + "null" + color.reset;
|
|
2539
2634
|
|
|
2540
|
-
|
|
2541
|
-
|
|
2635
|
+
if (v instanceof NovaNull)
|
|
2636
|
+
return color.null + "null" + color.reset;
|
|
2542
2637
|
|
|
2543
|
-
|
|
2544
|
-
|
|
2638
|
+
if (v instanceof NovaBool)
|
|
2639
|
+
return color.bool + String(v.valueOf()) + color.reset;
|
|
2545
2640
|
|
|
2546
|
-
|
|
2547
|
-
|
|
2641
|
+
if (v instanceof NovaString)
|
|
2642
|
+
return color.string + `"${v.valueOf()}"` + color.reset;
|
|
2548
2643
|
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
return color.circular + "[Circular]" + color.reset;
|
|
2644
|
+
if (v instanceof NovaNumber)
|
|
2645
|
+
return color.number + String(v.valueOf()) + color.reset;
|
|
2552
2646
|
|
|
2553
|
-
|
|
2554
|
-
|
|
2647
|
+
if (v instanceof Promise)
|
|
2648
|
+
return color.func + `Promise [${v.name || "anon"}]` + color.reset;
|
|
2555
2649
|
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
return "[]";
|
|
2559
|
-
|
|
2560
|
-
return "[\n" +
|
|
2561
|
-
v.inner.map(x =>
|
|
2562
|
-
pad + " " + next(x, indent + 1)
|
|
2563
|
-
).join(",\n") +
|
|
2564
|
-
nl + pad + "]";
|
|
2565
|
-
}
|
|
2650
|
+
if (v instanceof NovaRange)
|
|
2651
|
+
return v.toString();
|
|
2566
2652
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2653
|
+
if (v instanceof NovaStruct)
|
|
2654
|
+
return v.toString();
|
|
2569
2655
|
|
|
2570
|
-
|
|
2571
|
-
|
|
2656
|
+
// ── Plain JS getter/setter descriptor { read, write } ────────────────────
|
|
2657
|
+
// Scope variables with modifiers (tracked, lazy, clamp, etc.) are stored as
|
|
2658
|
+
// plain objects with read() / write() functions. Show the live value instead.
|
|
2659
|
+
if (
|
|
2660
|
+
v !== null && typeof v === 'object' &&
|
|
2661
|
+
!(v instanceof NovaValue) && !(v instanceof NovaArray) &&
|
|
2662
|
+
!(v instanceof NovaObject) && !(v instanceof NovaStruct) &&
|
|
2663
|
+
!(v instanceof NovaEnum) && !(v instanceof Scope) &&
|
|
2664
|
+
!(v instanceof Promise) && !Array.isArray(v) &&
|
|
2665
|
+
typeof v.read === 'function' && typeof v.write === 'function'
|
|
2666
|
+
) {
|
|
2667
|
+
return color.key + '[get/set ×]' + color.reset;
|
|
2668
|
+
}
|
|
2572
2669
|
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
color.key + k + color.reset +
|
|
2577
|
-
": " +
|
|
2578
|
-
next(x, indent + 1)
|
|
2579
|
-
).join(",\n") +
|
|
2580
|
-
nl + pad + "}";
|
|
2581
|
-
}
|
|
2670
|
+
if (typeof v === "object" && v !== null) {
|
|
2671
|
+
if (seen.has(v))
|
|
2672
|
+
return color.circular + "[Circular]" + color.reset;
|
|
2582
2673
|
|
|
2583
|
-
|
|
2584
|
-
|
|
2674
|
+
seen.add(v);
|
|
2675
|
+
}
|
|
2585
2676
|
|
|
2586
|
-
|
|
2587
|
-
|
|
2677
|
+
if (v instanceof NovaArray) {
|
|
2678
|
+
if (!v.inner.length)
|
|
2679
|
+
return "[]";
|
|
2588
2680
|
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2681
|
+
return "[\n" +
|
|
2682
|
+
v.inner.map(x =>
|
|
2683
|
+
pad + " " + next(x, indent + 1)
|
|
2684
|
+
).join(",\n") +
|
|
2685
|
+
nl + pad + "]";
|
|
2686
|
+
}
|
|
2593
2687
|
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2688
|
+
if (v instanceof NovaObject) {
|
|
2689
|
+
// ── Build annotation badges ─────────────────────────────────────────────
|
|
2690
|
+
const _annotations = [];
|
|
2691
|
+
|
|
2692
|
+
// Operator overloads stored under __operators
|
|
2693
|
+
const _opsRaw = v.inner.__operators;
|
|
2694
|
+
if (_opsRaw) {
|
|
2695
|
+
const _opsObj = _opsRaw instanceof NovaObject
|
|
2696
|
+
? _opsRaw.inner
|
|
2697
|
+
: (typeof _opsRaw === 'object' && _opsRaw !== null ? _opsRaw : {});
|
|
2698
|
+
const _binOps = Object.keys(
|
|
2699
|
+
_opsObj.binary instanceof NovaObject ? _opsObj.binary.inner : (_opsObj.binary || {})
|
|
2700
|
+
);
|
|
2701
|
+
const _unOps = Object.keys(
|
|
2702
|
+
_opsObj.unary instanceof NovaObject ? _opsObj.unary.inner : (_opsObj.unary || {})
|
|
2703
|
+
).map(k => 'u' + k);
|
|
2704
|
+
const _allOps = [..._binOps, ..._unOps];
|
|
2705
|
+
_annotations.push('\x1b[33m[ops:' + (_allOps.length ? ' ' + _allOps.join(' ') : '') + ']\x1b[0m');
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
// Meta traps attached via attachMeta()
|
|
2709
|
+
if (v._meta) {
|
|
2710
|
+
const _traps = [];
|
|
2711
|
+
if (typeof v._meta.get === 'function') _traps.push('get');
|
|
2712
|
+
if (typeof v._meta.set === 'function') _traps.push('set');
|
|
2713
|
+
if (typeof v._meta.missing === 'function') _traps.push('missing');
|
|
2714
|
+
if (_traps.length) _annotations.push('\x1b[36m[meta:' + _traps.join(',') + ']\x1b[0m');
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
const _prefix = _annotations.length ? _annotations.join(' ') + ' ' : '';
|
|
2718
|
+
|
|
2719
|
+
// Hide internal plumbing keys from display
|
|
2720
|
+
const _HIDE = new Set(['__operators']);
|
|
2721
|
+
const _entries = Object.entries(v.inner).filter(([k]) => !_HIDE.has(k));
|
|
2722
|
+
|
|
2723
|
+
if (!_entries.length)
|
|
2724
|
+
return _prefix + "{}";
|
|
2725
|
+
|
|
2726
|
+
return _prefix + "{\n" +
|
|
2727
|
+
_entries.map(([k, x]) =>
|
|
2728
|
+
pad + " " +
|
|
2729
|
+
color.key + k + color.reset +
|
|
2730
|
+
": " +
|
|
2731
|
+
next(x, indent + 1)
|
|
2732
|
+
).join(",\n") +
|
|
2733
|
+
nl + pad + "}";
|
|
2734
|
+
}
|
|
2598
2735
|
|
|
2599
|
-
|
|
2600
|
-
|
|
2736
|
+
if (v instanceof NovaValue)
|
|
2737
|
+
return v.toString();
|
|
2601
2738
|
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
return color.number +
|
|
2605
|
-
(isNaN(v) ? "NaN" : String(v)) +
|
|
2606
|
-
color.reset;
|
|
2739
|
+
if (v instanceof Scope)
|
|
2740
|
+
return v.toString();
|
|
2607
2741
|
|
|
2608
|
-
|
|
2609
|
-
return color.
|
|
2610
|
-
`
|
|
2742
|
+
if (v && v.kind === "function")
|
|
2743
|
+
return color.func +
|
|
2744
|
+
`Function [${v.name || "anon"}]` +
|
|
2611
2745
|
color.reset;
|
|
2612
2746
|
|
|
2613
|
-
|
|
2614
|
-
return color.
|
|
2615
|
-
|
|
2747
|
+
if (v && v.kind === "class")
|
|
2748
|
+
return color.func +
|
|
2749
|
+
`Class [${v.node?.name || "Unknown"}]` +
|
|
2616
2750
|
color.reset;
|
|
2617
2751
|
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
if (!entries.length)
|
|
2622
|
-
return "{}";
|
|
2752
|
+
if (v instanceof NovaEnum)
|
|
2753
|
+
return v.toString();
|
|
2623
2754
|
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2755
|
+
switch (typeof v) {
|
|
2756
|
+
case "number":
|
|
2757
|
+
return color.number +
|
|
2758
|
+
(isNaN(v) ? "NaN" : String(v)) +
|
|
2759
|
+
color.reset;
|
|
2760
|
+
|
|
2761
|
+
case "string":
|
|
2762
|
+
return color.string +
|
|
2763
|
+
`"${v}"` +
|
|
2764
|
+
color.reset;
|
|
2765
|
+
|
|
2766
|
+
case "boolean":
|
|
2767
|
+
return color.bool +
|
|
2768
|
+
String(v) +
|
|
2769
|
+
color.reset;
|
|
2770
|
+
|
|
2771
|
+
case "object": {
|
|
2772
|
+
const entries = Object.entries(v);
|
|
2773
|
+
|
|
2774
|
+
if (!entries.length)
|
|
2775
|
+
return "Native {}";
|
|
2776
|
+
|
|
2777
|
+
return "Native {\n" +
|
|
2778
|
+
entries.map(([k, val]) =>
|
|
2779
|
+
pad + " " +
|
|
2780
|
+
color.key + k + color.reset +
|
|
2781
|
+
": " +
|
|
2782
|
+
next(val, indent + 1)
|
|
2783
|
+
).join(",\n") +
|
|
2784
|
+
nl + pad + "}";
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
case "function":
|
|
2788
|
+
return color.func +
|
|
2789
|
+
`Function [${v.registered ? "Registered" : "NativeJs"}]` +
|
|
2790
|
+
color.reset;
|
|
2632
2791
|
}
|
|
2633
2792
|
|
|
2634
|
-
|
|
2635
|
-
return color.func +
|
|
2636
|
-
`Function [${v.registered ? "Registered" : "NativeJs"}]` +
|
|
2637
|
-
color.reset;
|
|
2793
|
+
return String(v);
|
|
2638
2794
|
}
|
|
2639
2795
|
|
|
2640
|
-
return String(v);
|
|
2641
|
-
}
|
|
2642
|
-
|
|
2643
2796
|
runFunctionNode(fn, scope, args = []) {
|
|
2644
2797
|
if (!fn.execCount) fn.execCount = 0;
|
|
2645
2798
|
fn.execCount++;
|
|
@@ -2788,12 +2941,20 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
2788
2941
|
switch (current.type) {
|
|
2789
2942
|
case 'if': {
|
|
2790
2943
|
if (this.evaluate(current.args, scope)) {
|
|
2791
|
-
this._runBodyWithYields(current.body, scope, collector);
|
|
2944
|
+
const _sig = this._runBodyWithYields(current.body, scope, collector);
|
|
2792
2945
|
executed = true;
|
|
2946
|
+
if (_sig === 'break') throw { __break: true };
|
|
2947
|
+
if (_sig === 'continue') throw { __continue: true };
|
|
2793
2948
|
} else current = current.next;
|
|
2794
2949
|
break;
|
|
2795
2950
|
}
|
|
2796
|
-
case 'else': {
|
|
2951
|
+
case 'else': {
|
|
2952
|
+
const _sig = this._runBodyWithYields(current.body, scope, collector);
|
|
2953
|
+
executed = true;
|
|
2954
|
+
if (_sig === 'break') throw { __break: true };
|
|
2955
|
+
if (_sig === 'continue') throw { __continue: true };
|
|
2956
|
+
break;
|
|
2957
|
+
}
|
|
2797
2958
|
case 'while': {
|
|
2798
2959
|
const c = Array.isArray(current.args) ? current.args[0] : current.args;
|
|
2799
2960
|
while (this.evaluate(c, scope)) {
|
|
@@ -2811,10 +2972,10 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
2811
2972
|
executed = true; break;
|
|
2812
2973
|
}
|
|
2813
2974
|
default: {
|
|
2814
|
-
|
|
2975
|
+
try { this.execute(node, scope); }
|
|
2815
2976
|
catch (e) { if (e && '__yield' in e) { collector.push(e.__yield); } else throw e; }
|
|
2816
|
-
|
|
2817
|
-
|
|
2977
|
+
executed = true; break;
|
|
2978
|
+
}
|
|
2818
2979
|
}
|
|
2819
2980
|
}
|
|
2820
2981
|
return;
|
|
@@ -2985,7 +3146,8 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
2985
3146
|
// ── declare ──
|
|
2986
3147
|
case 'declare': {
|
|
2987
3148
|
if (node.destructure) {
|
|
2988
|
-
const
|
|
3149
|
+
const evaled = this.evaluate(node.value, scope);
|
|
3150
|
+
const val = evaled instanceof NovaObject ? evaled.inner : evaled;
|
|
2989
3151
|
if (node.destructure.kind === 'objpattern') {
|
|
2990
3152
|
for (const { key, alias, defaultValue } of node.destructure.props) {
|
|
2991
3153
|
let v = val && Object.prototype.hasOwnProperty.call(val, key) ? val[key] : undefined;
|
|
@@ -3062,7 +3224,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3062
3224
|
}
|
|
3063
3225
|
return false;
|
|
3064
3226
|
});
|
|
3065
|
-
if (!ok) throw new Error("Variable '" + varName + "': value must be one of the declared union variants, got " + self._typeOf(v) + "("+v+")");
|
|
3227
|
+
if (!ok) throw new Error("Variable '" + varName + "': value must be one of the declared union variants, got " + self._typeOf(v) + "(" + v + ")");
|
|
3066
3228
|
}
|
|
3067
3229
|
};
|
|
3068
3230
|
}
|
|
@@ -3152,18 +3314,27 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3152
3314
|
case 'if': {
|
|
3153
3315
|
if (this.evaluate(current.args, scope)) {
|
|
3154
3316
|
const bs = new Scope('block', scope, this.globalScope);
|
|
3155
|
-
this.runLoop(current.body, bs);
|
|
3317
|
+
const _sig = this.runLoop(current.body, bs);
|
|
3318
|
+
executed = true;
|
|
3319
|
+
if (_sig === 'break') throw { __break: true };
|
|
3320
|
+
if (_sig === 'continue') throw { __continue: true };
|
|
3156
3321
|
} else current = current.next;
|
|
3157
3322
|
break;
|
|
3158
3323
|
}
|
|
3159
3324
|
case 'else': {
|
|
3160
3325
|
const bs = new Scope('block', scope, this.globalScope);
|
|
3161
|
-
this.runLoop(current.body, bs);
|
|
3326
|
+
const _sig = this.runLoop(current.body, bs);
|
|
3327
|
+
executed = true;
|
|
3328
|
+
if (_sig === 'break') throw { __break: true };
|
|
3329
|
+
if (_sig === 'continue') throw { __continue: true };
|
|
3330
|
+
break;
|
|
3162
3331
|
}
|
|
3163
3332
|
case 'unless': {
|
|
3164
3333
|
if (!this.evaluate(current.args, scope)) {
|
|
3165
3334
|
const bs = new Scope('block', scope, this.globalScope);
|
|
3166
|
-
this.runLoop(current.body, bs);
|
|
3335
|
+
const _sig = this.runLoop(current.body, bs);
|
|
3336
|
+
if (_sig === 'break') throw { __break: true };
|
|
3337
|
+
if (_sig === 'continue') throw { __continue: true };
|
|
3167
3338
|
}
|
|
3168
3339
|
executed = true; break;
|
|
3169
3340
|
}
|
|
@@ -3290,7 +3461,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3290
3461
|
const val = node.value ? this.evaluate(node.value, scope) : undefined;
|
|
3291
3462
|
for (const h of (this.eventBus.get(name) || [])) {
|
|
3292
3463
|
if (h._native && typeof h._native === 'function') {
|
|
3293
|
-
try { h._native(val); } catch (_) {}
|
|
3464
|
+
try { h._native(val); } catch (_) { }
|
|
3294
3465
|
} else {
|
|
3295
3466
|
const hs = new Scope('function', scope, this.globalScope);
|
|
3296
3467
|
if (h.param) hs.set(h.param, val);
|
|
@@ -3417,7 +3588,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3417
3588
|
return undefined;
|
|
3418
3589
|
}
|
|
3419
3590
|
|
|
3420
|
-
|
|
3591
|
+
case 'import': {
|
|
3421
3592
|
const exports = this._loadModuleExports(node.source, scope);
|
|
3422
3593
|
if (Array.isArray(node.names) && node.names.length > 0) {
|
|
3423
3594
|
for (const name of node.names) scope.set(name, exports[name]);
|
|
@@ -3488,6 +3659,10 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3488
3659
|
switch (expr.kind) {
|
|
3489
3660
|
case 'EOF': return undefined;
|
|
3490
3661
|
|
|
3662
|
+
case 'regex_literal':
|
|
3663
|
+
let pattern = this.evaluate(expr.pattern);
|
|
3664
|
+
return new RegExp(typeof pattern === 'string' ? pattern : pattern.inner, expr.flags);
|
|
3665
|
+
|
|
3491
3666
|
case 'url_literal':
|
|
3492
3667
|
return expr.value;
|
|
3493
3668
|
|
|
@@ -3500,7 +3675,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3500
3675
|
case 'fetch_expr': {
|
|
3501
3676
|
const url = this.evaluate(expr.url, scope);
|
|
3502
3677
|
const options = expr.options ? this.evaluate(expr.options, scope) : null;
|
|
3503
|
-
return
|
|
3678
|
+
return fetch(url, options);
|
|
3504
3679
|
}
|
|
3505
3680
|
|
|
3506
3681
|
case 'value': {
|
|
@@ -3517,7 +3692,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3517
3692
|
|
|
3518
3693
|
// ── Dynamic / meta variables ──────────────────────────────────────────
|
|
3519
3694
|
case 'dvar': {
|
|
3520
|
-
const _os
|
|
3695
|
+
const _os = require('os');
|
|
3521
3696
|
const _pth = require('path');
|
|
3522
3697
|
const _cry = require('crypto');
|
|
3523
3698
|
const dname = expr.name;
|
|
@@ -3728,11 +3903,11 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3728
3903
|
if (key === '__meta__' || (entry.key.kind === 'ref' && entry.key.name === 'method')) {
|
|
3729
3904
|
metaDescriptor = this.evaluate(entry.value, scope);
|
|
3730
3905
|
} else if (key === '__expr__' || (entry.key.kind === 'ref' && entry.key.name === 'expr')) {
|
|
3731
|
-
|
|
3732
|
-
|
|
3906
|
+
//detect [expr] key
|
|
3907
|
+
obj['.expr'] = this.evaluate(entry.value, scope);
|
|
3733
3908
|
} else if (key === '__op__' || (entry.key.kind === 'ref' && entry.key.name === 'op')) {
|
|
3734
|
-
|
|
3735
|
-
|
|
3909
|
+
//detect [operator] key
|
|
3910
|
+
obj['__operators'] = this.evaluate(entry.value, scope);
|
|
3736
3911
|
} else {
|
|
3737
3912
|
obj[key] = this.evaluate(entry.value, scope);
|
|
3738
3913
|
}
|
|
@@ -3804,7 +3979,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3804
3979
|
case 'assign': {
|
|
3805
3980
|
const val = this.evaluate(expr.value, scope);
|
|
3806
3981
|
let n = expr.name;
|
|
3807
|
-
if (n.kind === 'dexpr') n = { name: this.evaluate(n.code, scope)
|
|
3982
|
+
if (n.kind === 'dexpr') n = { name: this.evaluate(n.code, scope) }
|
|
3808
3983
|
if (n.kind === 'deref') {
|
|
3809
3984
|
const ptr = this.evaluate(n.operand, scope);
|
|
3810
3985
|
if (!(ptr instanceof NovaPointer)) this.error('Deref non-pointer', expr);
|
|
@@ -3849,22 +4024,28 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3849
4024
|
? this.evaluate(expr.consequent, scope) : this.evaluate(expr.alternate, scope);
|
|
3850
4025
|
|
|
3851
4026
|
case 'if_ternary': {
|
|
3852
|
-
|
|
4027
|
+
let cond = this.evaluate(expr.cond, scope);
|
|
3853
4028
|
if (cond) { return this.evaluate(expr.operand, scope) }
|
|
3854
4029
|
else if (expr.elseExpr) { return this.evaluate(expr.elseExpr, scope) }
|
|
3855
4030
|
}
|
|
3856
4031
|
|
|
3857
4032
|
case 'run_expr': return this.execute(expr.code, scope)
|
|
3858
4033
|
|
|
4034
|
+
case 'native':
|
|
4035
|
+
let val = this.evaluate(expr.value);
|
|
4036
|
+
return val instanceof NovaValue ? val.inner : val;
|
|
4037
|
+
|
|
4038
|
+
case 'ast': return { kind:"ast", ast: expr.ast };
|
|
4039
|
+
|
|
3859
4040
|
case 'link': return {
|
|
3860
4041
|
read: () => this.evaluate(expr.linked),
|
|
3861
4042
|
write: (val) =>
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
4043
|
+
this.evaluate({
|
|
4044
|
+
kind: 'assign',
|
|
4045
|
+
name: expr.linked,
|
|
4046
|
+
value: { kind: 'value', value: val }
|
|
4047
|
+
}, scope)
|
|
4048
|
+
};
|
|
3868
4049
|
|
|
3869
4050
|
case 'dexpr': return this.evaluate(expr.code, scope);
|
|
3870
4051
|
|
|
@@ -3993,7 +4174,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
3993
4174
|
const m = this._objectMethod(obj, pn);
|
|
3994
4175
|
return m !== undefined ? m : null;
|
|
3995
4176
|
}
|
|
3996
|
-
if (!(Object.hasOwnProperty(obj, 'name'))) return (typeof expr.object.name === 'string') ? expr.object.name : 'anon';
|
|
4177
|
+
if (!(Object.hasOwnProperty(obj, 'name')) && pn === 'name') return (typeof expr.object.name === 'string') ? expr.object.name : 'anon';
|
|
3997
4178
|
return (obj != null ? obj[pn] : null);
|
|
3998
4179
|
}
|
|
3999
4180
|
|
|
@@ -4188,7 +4369,7 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
4188
4369
|
else if (obj instanceof Scope) obj.delete(o.name);
|
|
4189
4370
|
else delete obj[o.name];
|
|
4190
4371
|
return true;
|
|
4191
|
-
}
|
|
4372
|
+
} if (o.kind === 'subscript') {
|
|
4192
4373
|
const obj = this.evaluate(o.object, scope); const idx = this.evaluate(o.index, scope);
|
|
4193
4374
|
if (obj instanceof NovaObject || obj instanceof NovaArray) obj.delete(idx);
|
|
4194
4375
|
else if (obj instanceof Scope) obj.delete(idx);
|
|
@@ -4199,8 +4380,8 @@ stringify(v, indent = 0, seen = new WeakSet()) {
|
|
|
4199
4380
|
}
|
|
4200
4381
|
}
|
|
4201
4382
|
let operand = this.evaluate(expr.operand, scope);
|
|
4202
|
-
|
|
4203
|
-
|
|
4383
|
+
if (operand instanceof NovaObject) {
|
|
4384
|
+
let overload = operand?.get?.('__operators')?.get?.('unary')?.get?.(expr.operator);
|
|
4204
4385
|
if (overload) return overload();
|
|
4205
4386
|
}
|
|
4206
4387
|
switch (expr.operator) {
|