vladx 1.3.2 → 1.5.1
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/bin/vladx +4 -1
- package/examples/stdlib/ambiente.vx +8 -0
- package/examples/stdlib/archivio_extra.vx +11 -0
- package/examples/stdlib/asserzione.vx +11 -0
- package/examples/stdlib/client_web.vx +7 -0
- package/examples/stdlib/codifica.vx +8 -0
- package/examples/stdlib/collezioni.vx +11 -0
- package/examples/stdlib/compressione.vx +10 -0
- package/examples/stdlib/console.vx +14 -0
- package/examples/stdlib/cripto.vx +8 -0
- package/examples/stdlib/dati.vx +21 -0
- package/examples/stdlib/eventi.vx +10 -0
- package/examples/stdlib/json_extra.vx +7 -0
- package/examples/stdlib/matematica.vx +9 -0
- package/examples/stdlib/percorso.vx +9 -0
- package/examples/stdlib/processo.vx +9 -0
- package/examples/stdlib/server_web.vx +10 -0
- package/examples/stdlib/sql_helper.vx +7 -0
- package/examples/stdlib/tempo.vx +9 -0
- package/examples/stdlib/testo.vx +10 -0
- package/examples/stdlib/url.vx +11 -0
- package/examples/stdlib/validazione.vx +5 -0
- package/package.json +6 -2
- package/programs/1/index.vx +16 -4
- package/programs/main/index.vx +11 -8
- package/programs/main/passports/Alina.json +1 -0
- package/programs/main/passports/Tanya.json +1 -0
- package/programs/main/passports/vlad.json +1 -0
- package/programs/main/test_async.vx +25 -0
- package/programs/main/test_async_advanced.vx +35 -0
- package/programs/main/test_backend.vx +33 -0
- package/programs/tests/test_new_features.vx +53 -0
- package/programs/tests/test_stdlib.vx +26 -0
- package/programs/tests/test_templates.vx +8 -0
- package/src/cli/cli.js +8 -6
- package/src/interpreter/interpreter.js +388 -170
- package/src/lexer/lexer.js +5 -1
- package/src/lexer/tokens.js +9 -1
- package/src/parser/ast.js +59 -4
- package/src/parser/parser.js +152 -18
- package/src/repl/repl.js +5 -5
- package/src/stdlib/registry.js +301 -0
- /package/programs/main/passports/{Vlad.txt → Vlad.json} +0 -0
|
@@ -59,20 +59,31 @@ class ReturnValue {
|
|
|
59
59
|
class BreakSignal { }
|
|
60
60
|
class ContinueSignal { }
|
|
61
61
|
|
|
62
|
+
class VladXPromise {
|
|
63
|
+
constructor(promise) {
|
|
64
|
+
this.promise = promise;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async wait() {
|
|
68
|
+
return await this.promise;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
class VladXFunction {
|
|
63
|
-
constructor(declaration, closure) {
|
|
73
|
+
constructor(declaration, closure, isAsync = false) {
|
|
64
74
|
this.declaration = declaration;
|
|
65
75
|
this.closure = closure;
|
|
76
|
+
this.isAsync = isAsync;
|
|
66
77
|
}
|
|
67
78
|
|
|
68
|
-
call(interpreter, args) {
|
|
79
|
+
async call(interpreter, args) {
|
|
69
80
|
const env = new Environment(this.closure);
|
|
70
81
|
for (let i = 0; i < this.declaration.params.length; i++) {
|
|
71
82
|
env.define(this.declaration.params[i], args[i] !== undefined ? args[i] : null);
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
try {
|
|
75
|
-
interpreter.executeBlock(this.declaration.body.body, env);
|
|
86
|
+
await interpreter.executeBlock(this.declaration.body.body, env);
|
|
76
87
|
} catch (e) {
|
|
77
88
|
if (e instanceof ReturnValue) {
|
|
78
89
|
return e.value;
|
|
@@ -84,13 +95,15 @@ class VladXFunction {
|
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
class ArrowFunc {
|
|
87
|
-
constructor(params, body, closure) {
|
|
98
|
+
constructor(params, body, closure, isAsync = false) {
|
|
88
99
|
this.params = params;
|
|
89
100
|
this.body = body;
|
|
90
101
|
this.closure = closure;
|
|
102
|
+
this.isAsync = isAsync;
|
|
91
103
|
}
|
|
104
|
+
// ... call unchanged except it returns result ...
|
|
92
105
|
|
|
93
|
-
call(interpreter, args) {
|
|
106
|
+
async call(interpreter, args) {
|
|
94
107
|
const env = new Environment(this.closure);
|
|
95
108
|
for (let i = 0; i < this.params.length; i++) {
|
|
96
109
|
env.define(this.params[i], args[i] !== undefined ? args[i] : null);
|
|
@@ -98,7 +111,7 @@ class ArrowFunc {
|
|
|
98
111
|
|
|
99
112
|
if (this.body.type === 'BlockStatement') {
|
|
100
113
|
try {
|
|
101
|
-
interpreter.executeBlock(this.body.body, env);
|
|
114
|
+
await interpreter.executeBlock(this.body.body, env);
|
|
102
115
|
} catch (e) {
|
|
103
116
|
if (e instanceof ReturnValue) {
|
|
104
117
|
return e.value;
|
|
@@ -107,11 +120,7 @@ class ArrowFunc {
|
|
|
107
120
|
}
|
|
108
121
|
return null;
|
|
109
122
|
} else {
|
|
110
|
-
|
|
111
|
-
interpreter.environment = env;
|
|
112
|
-
const result = interpreter.evaluate(this.body);
|
|
113
|
-
interpreter.environment = previousEnv;
|
|
114
|
-
return result;
|
|
123
|
+
return await interpreter.evaluate(this.body, env);
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
}
|
|
@@ -163,7 +172,11 @@ class BoundMethod {
|
|
|
163
172
|
this.instance = instance;
|
|
164
173
|
}
|
|
165
174
|
|
|
166
|
-
|
|
175
|
+
get isAsync() {
|
|
176
|
+
return this.method.isAsync;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async call(interpreter, args) {
|
|
167
180
|
const env = new Environment(this.method.closure);
|
|
168
181
|
env.define('questo', this.instance);
|
|
169
182
|
for (let i = 0; i < this.method.declaration.params.length; i++) {
|
|
@@ -171,7 +184,7 @@ class BoundMethod {
|
|
|
171
184
|
}
|
|
172
185
|
|
|
173
186
|
try {
|
|
174
|
-
interpreter.executeBlock(this.method.declaration.body.body, env);
|
|
187
|
+
await interpreter.executeBlock(this.method.declaration.body.body, env);
|
|
175
188
|
} catch (e) {
|
|
176
189
|
if (e instanceof ReturnValue) {
|
|
177
190
|
return e.value;
|
|
@@ -187,8 +200,11 @@ const path = require('path');
|
|
|
187
200
|
const http = require('http');
|
|
188
201
|
const https = require('https');
|
|
189
202
|
const { execSync } = require('child_process');
|
|
203
|
+
const sqlite3 = require('better-sqlite3');
|
|
204
|
+
const mysql = require('mysql2/promise');
|
|
190
205
|
const { Lexer } = require('../lexer/lexer.js');
|
|
191
206
|
const { Parser } = require('../parser/parser.js');
|
|
207
|
+
const stdRegistry = require('../stdlib/registry.js');
|
|
192
208
|
|
|
193
209
|
class Interpreter {
|
|
194
210
|
constructor() {
|
|
@@ -210,7 +226,11 @@ class Interpreter {
|
|
|
210
226
|
});
|
|
211
227
|
|
|
212
228
|
this.globals.define('tipo', {
|
|
213
|
-
call: (_, args) =>
|
|
229
|
+
call: (_, args) => {
|
|
230
|
+
const val = args[0];
|
|
231
|
+
if (val instanceof VladXPromise) return 'promessa';
|
|
232
|
+
return typeof val;
|
|
233
|
+
}
|
|
214
234
|
});
|
|
215
235
|
|
|
216
236
|
this.globals.define('numero', {
|
|
@@ -242,6 +262,21 @@ class Interpreter {
|
|
|
242
262
|
}
|
|
243
263
|
});
|
|
244
264
|
|
|
265
|
+
this.globals.define('Buffer', {
|
|
266
|
+
alloc: { call: (_, args) => Buffer.alloc(args[0]) },
|
|
267
|
+
from: { call: (_, args) => Buffer.from(args[0], args[1]) },
|
|
268
|
+
isBuffer: { call: (_, args) => Buffer.isBuffer(args[0]) }
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
this.globals.define('Base64', {
|
|
272
|
+
codifica: {
|
|
273
|
+
call: (_, args) => Buffer.from(String(args[0])).toString('base64')
|
|
274
|
+
},
|
|
275
|
+
decodifica: {
|
|
276
|
+
call: (_, args) => Buffer.from(String(args[0]), 'base64').toString('utf8')
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
245
280
|
this.globals.define('aspetta', {
|
|
246
281
|
call: (_, args) => {
|
|
247
282
|
const ms = args[0];
|
|
@@ -306,6 +341,68 @@ class Interpreter {
|
|
|
306
341
|
};
|
|
307
342
|
this.globals.define('Archivio', Archivio);
|
|
308
343
|
|
|
344
|
+
// --- DATABASE (SQLite) ---
|
|
345
|
+
const SQLite = {
|
|
346
|
+
apri: {
|
|
347
|
+
call: (_, args) => {
|
|
348
|
+
const db = new sqlite3(args[0]);
|
|
349
|
+
return {
|
|
350
|
+
interroga: {
|
|
351
|
+
call: (_, qArgs) => {
|
|
352
|
+
try {
|
|
353
|
+
return db.prepare(qArgs[0]).all(qArgs.slice(1));
|
|
354
|
+
} catch (e) {
|
|
355
|
+
throw new Error(`Errore query SQLite: ${e.message}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
esegui: {
|
|
360
|
+
call: (_, qArgs) => {
|
|
361
|
+
try {
|
|
362
|
+
const result = db.prepare(qArgs[0]).run(qArgs.slice(1));
|
|
363
|
+
return {
|
|
364
|
+
modifiche: result.changes,
|
|
365
|
+
ultimoId: result.lastInsertRowid
|
|
366
|
+
};
|
|
367
|
+
} catch (e) {
|
|
368
|
+
throw new Error(`Errore esecuzione SQLite: ${e.message}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
chiudi: { call: () => db.close() }
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
this.globals.define('SQLite', SQLite);
|
|
378
|
+
|
|
379
|
+
// --- DATABASE (MySQL) ---
|
|
380
|
+
const MySQL = {
|
|
381
|
+
connetti: {
|
|
382
|
+
call: async (_, args) => {
|
|
383
|
+
try {
|
|
384
|
+
const conn = await mysql.createConnection(args[0]);
|
|
385
|
+
return {
|
|
386
|
+
interroga: {
|
|
387
|
+
call: async (_, qArgs) => {
|
|
388
|
+
try {
|
|
389
|
+
const [rows] = await conn.execute(qArgs[0], qArgs.slice(1));
|
|
390
|
+
return rows;
|
|
391
|
+
} catch (e) {
|
|
392
|
+
throw new Error(`Errore query MySQL: ${e.message}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
chiudi: { call: async () => await conn.end() }
|
|
397
|
+
};
|
|
398
|
+
} catch (e) {
|
|
399
|
+
throw new Error(`Errore connessione MySQL: ${e.message}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
this.globals.define('MySQL', MySQL);
|
|
405
|
+
|
|
309
406
|
// --- SISTEMA (Process/OS) ---
|
|
310
407
|
const Sistema = {
|
|
311
408
|
esegui: {
|
|
@@ -374,13 +471,34 @@ class Interpreter {
|
|
|
374
471
|
}
|
|
375
472
|
};
|
|
376
473
|
this.globals.define('Rete', Rete);
|
|
474
|
+
|
|
475
|
+
// --- MATEMATICA ---
|
|
476
|
+
const Matematica = {
|
|
477
|
+
casuale: { call: () => Math.random() },
|
|
478
|
+
rotonda: { call: (_, args) => Math.round(args[0]) },
|
|
479
|
+
radice: { call: (_, args) => Math.sqrt(args[0]) },
|
|
480
|
+
min: { call: (_, args) => Math.min(...args) },
|
|
481
|
+
max: { call: (_, args) => Math.max(...args) },
|
|
482
|
+
piGreco: Math.PI,
|
|
483
|
+
E: Math.E
|
|
484
|
+
};
|
|
485
|
+
this.globals.define('Matematica', Matematica);
|
|
486
|
+
|
|
487
|
+
// --- TEMPO ---
|
|
488
|
+
const Tempo = {
|
|
489
|
+
ora: { call: () => new Date().toLocaleTimeString() },
|
|
490
|
+
data: { call: () => new Date().toLocaleDateString() },
|
|
491
|
+
timestamp: { call: () => Date.now() },
|
|
492
|
+
pausa: { call: async (_, args) => new Promise(res => setTimeout(res, args[0])) }
|
|
493
|
+
};
|
|
494
|
+
this.globals.define('Tempo', Tempo);
|
|
377
495
|
}
|
|
378
496
|
|
|
379
|
-
interpret(program) {
|
|
497
|
+
async interpret(program) {
|
|
380
498
|
this.output = [];
|
|
381
499
|
try {
|
|
382
500
|
for (const statement of program.body) {
|
|
383
|
-
this.execute(statement);
|
|
501
|
+
await this.execute(statement, this.globals);
|
|
384
502
|
}
|
|
385
503
|
} catch (error) {
|
|
386
504
|
if (error instanceof ReturnValue) {
|
|
@@ -391,59 +509,102 @@ class Interpreter {
|
|
|
391
509
|
return this.output;
|
|
392
510
|
}
|
|
393
511
|
|
|
394
|
-
execute(node) {
|
|
512
|
+
async execute(node, env) {
|
|
395
513
|
switch (node.type) {
|
|
396
514
|
case 'VariableDeclaration':
|
|
397
|
-
return this.executeVariableDeclaration(node);
|
|
515
|
+
return await this.executeVariableDeclaration(node, env);
|
|
398
516
|
case 'FunctionDeclaration':
|
|
399
|
-
return this.executeFunctionDeclaration(node);
|
|
517
|
+
return this.executeFunctionDeclaration(node, env);
|
|
400
518
|
case 'ClassDeclaration':
|
|
401
|
-
return this.executeClassDeclaration(node);
|
|
519
|
+
return this.executeClassDeclaration(node, env);
|
|
402
520
|
case 'BlockStatement':
|
|
403
|
-
return this.executeBlock(node.body, new Environment(
|
|
521
|
+
return await this.executeBlock(node.body, new Environment(env));
|
|
404
522
|
case 'IfStatement':
|
|
405
|
-
return this.executeIfStatement(node);
|
|
523
|
+
return await this.executeIfStatement(node, env);
|
|
406
524
|
case 'WhileStatement':
|
|
407
|
-
return this.executeWhileStatement(node);
|
|
525
|
+
return await this.executeWhileStatement(node, env);
|
|
408
526
|
case 'ForStatement':
|
|
409
|
-
return this.executeForStatement(node);
|
|
527
|
+
return await this.executeForStatement(node, env);
|
|
528
|
+
case 'SwitchStatement':
|
|
529
|
+
return await this.executeSwitchStatement(node, env);
|
|
410
530
|
case 'ReturnStatement':
|
|
411
|
-
throw new ReturnValue(node.argument ? this.evaluate(node.argument) : null);
|
|
531
|
+
throw new ReturnValue(node.argument ? await this.evaluate(node.argument, env) : null);
|
|
412
532
|
case 'BreakStatement':
|
|
413
533
|
throw new BreakSignal();
|
|
414
534
|
case 'ContinueStatement':
|
|
415
535
|
throw new ContinueSignal();
|
|
416
536
|
case 'TryStatement':
|
|
417
|
-
return this.executeTryStatement(node);
|
|
537
|
+
return await this.executeTryStatement(node, env);
|
|
418
538
|
case 'ThrowStatement':
|
|
419
|
-
return this.executeThrowStatement(node);
|
|
539
|
+
return await this.executeThrowStatement(node, env);
|
|
420
540
|
case 'ExportNamedDeclaration':
|
|
421
|
-
return this.executeExportNamedDeclaration(node);
|
|
541
|
+
return await this.executeExportNamedDeclaration(node, env);
|
|
422
542
|
case 'ImportDeclaration':
|
|
423
|
-
return this.executeImportDeclaration(node);
|
|
543
|
+
return await this.executeImportDeclaration(node, env);
|
|
424
544
|
case 'PrintStatement':
|
|
425
|
-
return this.executePrintStatement(node);
|
|
545
|
+
return await this.executePrintStatement(node, env);
|
|
426
546
|
case 'ExpressionStatement':
|
|
427
|
-
return this.evaluate(node.expression);
|
|
547
|
+
return await this.evaluate(node.expression, env);
|
|
428
548
|
default:
|
|
429
549
|
throw new Error(`Tipo statement sconosciuto: ${node.type}`);
|
|
430
550
|
}
|
|
431
551
|
}
|
|
432
552
|
|
|
433
|
-
executeVariableDeclaration(node) {
|
|
434
|
-
const value = node.value ? this.evaluate(node.value) : null;
|
|
435
|
-
|
|
553
|
+
async executeVariableDeclaration(node, env) {
|
|
554
|
+
const value = node.value ? await this.evaluate(node.value, env) : null;
|
|
555
|
+
|
|
556
|
+
if (typeof node.name === 'string') {
|
|
557
|
+
env.define(node.name, value, node.isConstant);
|
|
558
|
+
} else if (node.name.type === 'ArrayPattern') {
|
|
559
|
+
if (!Array.isArray(value)) throw new Error('Destrutturazione array fallita: il valore non è un array');
|
|
560
|
+
for (let i = 0; i < node.name.elements.length; i++) {
|
|
561
|
+
env.define(node.name.elements[i], value[i] !== undefined ? value[i] : null, node.isConstant);
|
|
562
|
+
}
|
|
563
|
+
} else if (node.name.type === 'ObjectPattern') {
|
|
564
|
+
if (typeof value !== 'object' || value === null) throw new Error('Destrutturazione oggetto fallita: il valore non è un oggetto');
|
|
565
|
+
for (const prop of node.name.properties) {
|
|
566
|
+
env.define(prop, value[prop] !== undefined ? value[prop] : null, node.isConstant);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
436
569
|
}
|
|
437
570
|
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
|
|
571
|
+
async executeSwitchStatement(node, env) {
|
|
572
|
+
const discriminant = await this.evaluate(node.discriminant, env);
|
|
573
|
+
let matched = false;
|
|
574
|
+
let defaultCase = null;
|
|
575
|
+
|
|
576
|
+
for (const switchCase of node.cases) {
|
|
577
|
+
if (switchCase.test === null) {
|
|
578
|
+
defaultCase = switchCase;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const testValue = await this.evaluate(switchCase.test, env);
|
|
583
|
+
if (matched || discriminant === testValue) {
|
|
584
|
+
matched = true;
|
|
585
|
+
try {
|
|
586
|
+
await this.executeBlock(switchCase.consequent, env);
|
|
587
|
+
} catch (e) {
|
|
588
|
+
if (e instanceof BreakSignal) return;
|
|
589
|
+
throw e;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (!matched && defaultCase) {
|
|
595
|
+
await this.executeBlock(defaultCase.consequent, env);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
executeFunctionDeclaration(node, env) {
|
|
600
|
+
const func = new VladXFunction(node, env, node.isAsync);
|
|
601
|
+
env.define(node.name, func);
|
|
441
602
|
}
|
|
442
603
|
|
|
443
|
-
executeClassDeclaration(node) {
|
|
604
|
+
executeClassDeclaration(node, env) {
|
|
444
605
|
let superclass = null;
|
|
445
606
|
if (node.superclass) {
|
|
446
|
-
const superclassValue =
|
|
607
|
+
const superclassValue = env.get(node.superclass);
|
|
447
608
|
if (!(superclassValue instanceof VladXClass)) {
|
|
448
609
|
throw new Error(`"${node.superclass}" non è una classe`);
|
|
449
610
|
}
|
|
@@ -452,40 +613,34 @@ class Interpreter {
|
|
|
452
613
|
|
|
453
614
|
const methods = new Map();
|
|
454
615
|
if (node.constructor) {
|
|
455
|
-
methods.set('costruttore', new VladXFunction(node.constructor,
|
|
616
|
+
methods.set('costruttore', new VladXFunction(node.constructor, env, false));
|
|
456
617
|
}
|
|
457
618
|
for (const method of node.methods) {
|
|
458
|
-
methods.set(method.name, new VladXFunction(method,
|
|
619
|
+
methods.set(method.name, new VladXFunction(method, env, method.isAsync));
|
|
459
620
|
}
|
|
460
621
|
|
|
461
622
|
const klass = new VladXClass(node.name, superclass, node.constructor, methods);
|
|
462
|
-
|
|
623
|
+
env.define(node.name, klass);
|
|
463
624
|
}
|
|
464
625
|
|
|
465
|
-
executeBlock(statements, env) {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
try {
|
|
469
|
-
for (const statement of statements) {
|
|
470
|
-
this.execute(statement);
|
|
471
|
-
}
|
|
472
|
-
} finally {
|
|
473
|
-
this.environment = previousEnv;
|
|
626
|
+
async executeBlock(statements, env) {
|
|
627
|
+
for (const statement of statements) {
|
|
628
|
+
await this.execute(statement, env);
|
|
474
629
|
}
|
|
475
630
|
}
|
|
476
631
|
|
|
477
|
-
executeIfStatement(node) {
|
|
478
|
-
if (this.isTruthy(this.evaluate(node.condition))) {
|
|
479
|
-
this.execute(node.consequent);
|
|
632
|
+
async executeIfStatement(node, env) {
|
|
633
|
+
if (this.isTruthy(await this.evaluate(node.condition, env))) {
|
|
634
|
+
await this.execute(node.consequent, env);
|
|
480
635
|
} else if (node.alternate) {
|
|
481
|
-
this.execute(node.alternate);
|
|
636
|
+
await this.execute(node.alternate, env);
|
|
482
637
|
}
|
|
483
638
|
}
|
|
484
639
|
|
|
485
|
-
executeWhileStatement(node) {
|
|
486
|
-
while (this.isTruthy(this.evaluate(node.condition))) {
|
|
640
|
+
async executeWhileStatement(node, env) {
|
|
641
|
+
while (this.isTruthy(await this.evaluate(node.condition, env))) {
|
|
487
642
|
try {
|
|
488
|
-
this.execute(node.body);
|
|
643
|
+
await this.execute(node.body, env);
|
|
489
644
|
} catch (e) {
|
|
490
645
|
if (e instanceof BreakSignal) break;
|
|
491
646
|
if (e instanceof ContinueSignal) continue;
|
|
@@ -494,35 +649,33 @@ class Interpreter {
|
|
|
494
649
|
}
|
|
495
650
|
}
|
|
496
651
|
|
|
497
|
-
executeForStatement(node) {
|
|
498
|
-
const forEnv = new Environment(
|
|
499
|
-
const previousEnv = this.environment;
|
|
500
|
-
this.environment = forEnv;
|
|
652
|
+
async executeForStatement(node, env) {
|
|
653
|
+
const forEnv = new Environment(env);
|
|
501
654
|
|
|
502
655
|
try {
|
|
503
|
-
if (node.init) this.execute(node.init);
|
|
656
|
+
if (node.init) await this.execute(node.init, forEnv);
|
|
504
657
|
|
|
505
|
-
while (!node.condition || this.isTruthy(this.evaluate(node.condition))) {
|
|
658
|
+
while (!node.condition || this.isTruthy(await this.evaluate(node.condition, forEnv))) {
|
|
506
659
|
try {
|
|
507
|
-
this.execute(node.body);
|
|
660
|
+
await this.execute(node.body, forEnv);
|
|
508
661
|
} catch (e) {
|
|
509
662
|
if (e instanceof BreakSignal) break;
|
|
510
663
|
if (e instanceof ContinueSignal) {
|
|
511
|
-
if (node.update) this.evaluate(node.update);
|
|
664
|
+
if (node.update) await this.evaluate(node.update, forEnv);
|
|
512
665
|
continue;
|
|
513
666
|
}
|
|
514
667
|
throw e;
|
|
515
668
|
}
|
|
516
|
-
if (node.update) this.evaluate(node.update);
|
|
669
|
+
if (node.update) await this.evaluate(node.update, forEnv);
|
|
517
670
|
}
|
|
518
671
|
} finally {
|
|
519
|
-
this.environment
|
|
672
|
+
// No need to restore since we didn't mutate this.environment
|
|
520
673
|
}
|
|
521
674
|
}
|
|
522
675
|
|
|
523
|
-
executeTryStatement(node) {
|
|
676
|
+
async executeTryStatement(node, env) {
|
|
524
677
|
try {
|
|
525
|
-
this.execute(node.block);
|
|
678
|
+
await this.execute(node.block, env);
|
|
526
679
|
} catch (error) {
|
|
527
680
|
// Se è un ReturnValue o Break/Continue Signal, non catturarlo qui
|
|
528
681
|
if (error instanceof ReturnValue || error instanceof BreakSignal || error instanceof ContinueSignal) {
|
|
@@ -530,100 +683,105 @@ class Interpreter {
|
|
|
530
683
|
}
|
|
531
684
|
|
|
532
685
|
if (node.handler) {
|
|
533
|
-
const catchEnv = new Environment(
|
|
686
|
+
const catchEnv = new Environment(env);
|
|
534
687
|
// Il valore errore può essere quello lanciato da LANCIA o un errore JS
|
|
688
|
+
// Se è un errore JS, potrebbe essere un Error object o stringa
|
|
535
689
|
const errorValue = error instanceof Error ? error.message : error;
|
|
536
690
|
catchEnv.define(node.handler.param, errorValue);
|
|
537
691
|
|
|
538
|
-
|
|
539
|
-
this.environment = catchEnv;
|
|
540
|
-
try {
|
|
541
|
-
this.execute(node.handler.body);
|
|
542
|
-
} finally {
|
|
543
|
-
this.environment = previousEnv;
|
|
544
|
-
}
|
|
692
|
+
await this.execute(node.handler.body, catchEnv);
|
|
545
693
|
} else if (!node.finalizer) {
|
|
546
694
|
throw error;
|
|
547
695
|
}
|
|
548
696
|
} finally {
|
|
549
697
|
if (node.finalizer) {
|
|
550
|
-
this.execute(node.finalizer);
|
|
698
|
+
await this.execute(node.finalizer, env);
|
|
551
699
|
}
|
|
552
700
|
}
|
|
553
701
|
}
|
|
554
702
|
|
|
555
|
-
executeThrowStatement(node) {
|
|
556
|
-
const value = this.evaluate(node.argument);
|
|
703
|
+
async executeThrowStatement(node, env) {
|
|
704
|
+
const value = await this.evaluate(node.argument, env);
|
|
557
705
|
throw value;
|
|
558
706
|
}
|
|
559
707
|
|
|
560
|
-
executeExportNamedDeclaration(node) {
|
|
561
|
-
this.execute(node.declaration);
|
|
708
|
+
async executeExportNamedDeclaration(node, env) {
|
|
709
|
+
await this.execute(node.declaration, env);
|
|
562
710
|
const name = node.declaration.name;
|
|
563
|
-
const value =
|
|
711
|
+
const value = env.get(name);
|
|
564
712
|
this.exportedSymbols.set(name, value);
|
|
565
713
|
}
|
|
566
714
|
|
|
567
|
-
executeImportDeclaration(node) {
|
|
715
|
+
async executeImportDeclaration(node, env) {
|
|
568
716
|
const sourcePath = node.source;
|
|
569
|
-
let
|
|
717
|
+
let moduleExports;
|
|
570
718
|
|
|
571
|
-
if
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
if (!fs.existsSync(fullPath)) {
|
|
578
|
-
// Try index.vx inside the module folder
|
|
579
|
-
const folderPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
|
|
580
|
-
fullPath = path.join(folderPath, 'index.vx');
|
|
719
|
+
// Check if it's a standard library module
|
|
720
|
+
if (sourcePath.startsWith('@std/')) {
|
|
721
|
+
if (stdRegistry[sourcePath]) {
|
|
722
|
+
moduleExports = stdRegistry[sourcePath];
|
|
723
|
+
} else {
|
|
724
|
+
throw new Error(`Modulo standard non trovato: ${sourcePath}`);
|
|
581
725
|
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
if (!fs.existsSync(fullPath)) {
|
|
585
|
-
throw new Error(`Modulo non trovato: ${sourcePath}`);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
let moduleExports;
|
|
589
|
-
if (this.modules.has(fullPath)) {
|
|
590
|
-
moduleExports = this.modules.get(fullPath);
|
|
591
726
|
} else {
|
|
592
|
-
|
|
593
|
-
const content = fs.readFileSync(fullPath, 'utf8');
|
|
594
|
-
const lexer = new Lexer(content);
|
|
595
|
-
const tokens = lexer.tokenize();
|
|
596
|
-
const parser = new Parser(tokens);
|
|
597
|
-
const program = parser.parse();
|
|
727
|
+
let fullPath;
|
|
598
728
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
729
|
+
if (sourcePath.startsWith('./') || sourcePath.startsWith('../')) {
|
|
730
|
+
fullPath = path.resolve(path.dirname(this.currentPath), sourcePath);
|
|
731
|
+
} else {
|
|
732
|
+
// Check vladx_modules
|
|
733
|
+
fullPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
|
|
734
|
+
if (!fullPath.endsWith('.vx')) fullPath += '.vx';
|
|
735
|
+
if (!fs.existsSync(fullPath)) {
|
|
736
|
+
// Try index.vx inside the module folder
|
|
737
|
+
const folderPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
|
|
738
|
+
fullPath = path.join(folderPath, 'index.vx');
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (!fs.existsSync(fullPath)) {
|
|
743
|
+
throw new Error(`Modulo non trovato: ${sourcePath}`);
|
|
744
|
+
}
|
|
603
745
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
746
|
+
if (this.modules.has(fullPath)) {
|
|
747
|
+
moduleExports = this.modules.get(fullPath);
|
|
748
|
+
} else {
|
|
749
|
+
// Load and execute module
|
|
750
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
751
|
+
const lexer = new Lexer(content);
|
|
752
|
+
const tokens = lexer.tokenize();
|
|
753
|
+
const parser = new Parser(tokens);
|
|
754
|
+
const program = parser.parse();
|
|
755
|
+
|
|
756
|
+
const subInterpreter = new Interpreter();
|
|
757
|
+
// Sharing the same modules cache
|
|
758
|
+
subInterpreter.modules = this.modules;
|
|
759
|
+
subInterpreter.currentPath = fullPath;
|
|
760
|
+
|
|
761
|
+
await subInterpreter.interpret(program);
|
|
762
|
+
moduleExports = subInterpreter.exportedSymbols;
|
|
763
|
+
this.modules.set(fullPath, moduleExports);
|
|
764
|
+
}
|
|
607
765
|
}
|
|
608
766
|
|
|
609
767
|
// Import specified symbols
|
|
610
768
|
for (const specifier of node.specifiers) {
|
|
611
769
|
if (moduleExports.has(specifier)) {
|
|
612
|
-
|
|
770
|
+
env.define(specifier, moduleExports.get(specifier));
|
|
613
771
|
} else {
|
|
614
772
|
throw new Error(`Il modulo "${sourcePath}" non esporta "${specifier}"`);
|
|
615
773
|
}
|
|
616
774
|
}
|
|
617
775
|
}
|
|
618
776
|
|
|
619
|
-
executePrintStatement(node) {
|
|
620
|
-
const value = this.evaluate(node.argument);
|
|
777
|
+
async executePrintStatement(node, env) {
|
|
778
|
+
const value = await this.evaluate(node.argument, env);
|
|
621
779
|
const output = this.stringify(value);
|
|
622
780
|
console.log(output);
|
|
623
781
|
this.output.push(output);
|
|
624
782
|
}
|
|
625
783
|
|
|
626
|
-
evaluate(node) {
|
|
784
|
+
async evaluate(node, env) {
|
|
627
785
|
switch (node.type) {
|
|
628
786
|
case 'NumericLiteral':
|
|
629
787
|
return node.value;
|
|
@@ -634,47 +792,59 @@ class Interpreter {
|
|
|
634
792
|
case 'NullLiteral':
|
|
635
793
|
return null;
|
|
636
794
|
case 'Identifier':
|
|
637
|
-
return
|
|
795
|
+
return env.get(node.name);
|
|
638
796
|
case 'ThisExpression':
|
|
639
|
-
return
|
|
797
|
+
return env.get('questo');
|
|
640
798
|
case 'ArrayLiteral':
|
|
641
|
-
return node.elements.map(el => this.evaluate(el));
|
|
799
|
+
return await Promise.all(node.elements.map(el => this.evaluate(el, env)));
|
|
642
800
|
case 'ObjectLiteral':
|
|
643
801
|
const obj = {};
|
|
644
802
|
for (const prop of node.properties) {
|
|
645
|
-
obj[prop.key] = this.evaluate(prop.value);
|
|
803
|
+
obj[prop.key] = await this.evaluate(prop.value, env);
|
|
646
804
|
}
|
|
647
805
|
return obj;
|
|
648
806
|
case 'BinaryExpression':
|
|
649
|
-
return this.evaluateBinaryExpression(node);
|
|
807
|
+
return await this.evaluateBinaryExpression(node, env);
|
|
650
808
|
case 'LogicalExpression':
|
|
651
|
-
return this.evaluateLogicalExpression(node);
|
|
809
|
+
return await this.evaluateLogicalExpression(node, env);
|
|
652
810
|
case 'UnaryExpression':
|
|
653
|
-
return this.evaluateUnaryExpression(node);
|
|
811
|
+
return await this.evaluateUnaryExpression(node, env);
|
|
654
812
|
case 'AssignmentExpression':
|
|
655
|
-
return this.evaluateAssignment(node);
|
|
813
|
+
return await this.evaluateAssignment(node, env);
|
|
656
814
|
case 'UpdateExpression':
|
|
657
|
-
return this.evaluateUpdateExpression(node);
|
|
815
|
+
return await this.evaluateUpdateExpression(node, env);
|
|
658
816
|
case 'CallExpression':
|
|
659
|
-
return this.evaluateCallExpression(node);
|
|
817
|
+
return await this.evaluateCallExpression(node, env);
|
|
660
818
|
case 'NewExpression':
|
|
661
|
-
return this.evaluateNewExpression(node);
|
|
819
|
+
return await this.evaluateNewExpression(node, env);
|
|
662
820
|
case 'MemberExpression':
|
|
663
|
-
return this.evaluateMemberExpression(node);
|
|
821
|
+
return await this.evaluateMemberExpression(node, env);
|
|
664
822
|
case 'ArrowFunction':
|
|
665
|
-
return new ArrowFunc(node.params, node.body,
|
|
823
|
+
return new ArrowFunc(node.params, node.body, env, node.isAsync);
|
|
824
|
+
case 'ConditionalExpression':
|
|
825
|
+
return await this.evaluateConditionalExpression(node, env);
|
|
666
826
|
case 'TemplateLiteral':
|
|
667
|
-
return this.evaluateTemplateLiteral(node);
|
|
827
|
+
return await this.evaluateTemplateLiteral(node, env);
|
|
828
|
+
case 'AwaitExpression':
|
|
829
|
+
return await this.evaluateAwaitExpression(node, env);
|
|
668
830
|
default:
|
|
669
831
|
throw new Error(`Tipo espressione sconosciuto: ${node.type}`);
|
|
670
832
|
}
|
|
671
833
|
}
|
|
672
834
|
|
|
673
|
-
|
|
835
|
+
async evaluateAwaitExpression(node, env) {
|
|
836
|
+
const value = await this.evaluate(node.argument, env);
|
|
837
|
+
if (value instanceof VladXPromise) {
|
|
838
|
+
return await value.wait();
|
|
839
|
+
}
|
|
840
|
+
return value;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
async evaluateTemplateLiteral(node, env) {
|
|
674
844
|
let result = node.quasis[0];
|
|
675
845
|
|
|
676
846
|
for (let i = 0; i < node.expressions.length; i++) {
|
|
677
|
-
const value = this.evaluate(node.expressions[i]);
|
|
847
|
+
const value = await this.evaluate(node.expressions[i], env);
|
|
678
848
|
result += this.stringify(value);
|
|
679
849
|
result += node.quasis[i + 1];
|
|
680
850
|
}
|
|
@@ -682,9 +852,9 @@ class Interpreter {
|
|
|
682
852
|
return result;
|
|
683
853
|
}
|
|
684
854
|
|
|
685
|
-
evaluateBinaryExpression(node) {
|
|
686
|
-
const left = this.evaluate(node.left);
|
|
687
|
-
const right = this.evaluate(node.right);
|
|
855
|
+
async evaluateBinaryExpression(node, env) {
|
|
856
|
+
const left = await this.evaluate(node.left, env);
|
|
857
|
+
const right = await this.evaluate(node.right, env);
|
|
688
858
|
|
|
689
859
|
switch (node.operator) {
|
|
690
860
|
case '+':
|
|
@@ -709,17 +879,27 @@ class Interpreter {
|
|
|
709
879
|
}
|
|
710
880
|
}
|
|
711
881
|
|
|
712
|
-
evaluateLogicalExpression(node) {
|
|
713
|
-
const left = this.evaluate(node.left);
|
|
882
|
+
async evaluateLogicalExpression(node, env) {
|
|
883
|
+
const left = await this.evaluate(node.left, env);
|
|
714
884
|
if (node.operator === '||') {
|
|
715
|
-
return this.isTruthy(left) ? left : this.evaluate(node.right);
|
|
885
|
+
return this.isTruthy(left) ? left : await this.evaluate(node.right, env);
|
|
886
|
+
} else if (node.operator === '&&') {
|
|
887
|
+
return !this.isTruthy(left) ? left : await this.evaluate(node.right, env);
|
|
888
|
+
} else if (node.operator === '??') {
|
|
889
|
+
return (left !== null && left !== undefined) ? left : await this.evaluate(node.right, env);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
async evaluateConditionalExpression(node, env) {
|
|
894
|
+
if (this.isTruthy(await this.evaluate(node.test, env))) {
|
|
895
|
+
return await this.evaluate(node.consequent, env);
|
|
716
896
|
} else {
|
|
717
|
-
return
|
|
897
|
+
return await this.evaluate(node.alternate, env);
|
|
718
898
|
}
|
|
719
899
|
}
|
|
720
900
|
|
|
721
|
-
evaluateUnaryExpression(node) {
|
|
722
|
-
const argument = this.evaluate(node.argument);
|
|
901
|
+
async evaluateUnaryExpression(node, env) {
|
|
902
|
+
const argument = await this.evaluate(node.argument, env);
|
|
723
903
|
switch (node.operator) {
|
|
724
904
|
case '-': return -argument;
|
|
725
905
|
case '!': return !this.isTruthy(argument);
|
|
@@ -728,20 +908,20 @@ class Interpreter {
|
|
|
728
908
|
}
|
|
729
909
|
}
|
|
730
910
|
|
|
731
|
-
evaluateAssignment(node) {
|
|
732
|
-
let value = this.evaluate(node.right);
|
|
911
|
+
async evaluateAssignment(node, env) {
|
|
912
|
+
let value = await this.evaluate(node.right, env);
|
|
733
913
|
|
|
734
914
|
if (node.operator === '+=' || node.operator === '-=') {
|
|
735
|
-
const current = this.evaluate(node.left);
|
|
915
|
+
const current = await this.evaluate(node.left, env);
|
|
736
916
|
value = node.operator === '+=' ? current + value : current - value;
|
|
737
917
|
}
|
|
738
918
|
|
|
739
919
|
if (node.left.type === 'Identifier') {
|
|
740
|
-
|
|
920
|
+
env.set(node.left.name, value);
|
|
741
921
|
} else if (node.left.type === 'MemberExpression') {
|
|
742
|
-
const obj = this.evaluate(node.left.object);
|
|
922
|
+
const obj = await this.evaluate(node.left.object, env);
|
|
743
923
|
const prop = node.left.computed
|
|
744
|
-
? this.evaluate(node.left.property)
|
|
924
|
+
? await this.evaluate(node.left.property, env)
|
|
745
925
|
: node.left.property.name;
|
|
746
926
|
|
|
747
927
|
// Handle class instances
|
|
@@ -755,30 +935,36 @@ class Interpreter {
|
|
|
755
935
|
return value;
|
|
756
936
|
}
|
|
757
937
|
|
|
758
|
-
evaluateUpdateExpression(node) {
|
|
759
|
-
const current = this.evaluate(node.argument);
|
|
938
|
+
async evaluateUpdateExpression(node, env) {
|
|
939
|
+
const current = await this.evaluate(node.argument, env);
|
|
760
940
|
const newValue = node.operator === '++' ? current + 1 : current - 1;
|
|
761
941
|
|
|
762
942
|
if (node.argument.type === 'Identifier') {
|
|
763
|
-
|
|
943
|
+
env.set(node.argument.name, newValue);
|
|
764
944
|
}
|
|
765
945
|
|
|
766
946
|
return node.prefix ? newValue : current;
|
|
767
947
|
}
|
|
768
948
|
|
|
769
|
-
evaluateCallExpression(node) {
|
|
770
|
-
const callee = this.evaluate(node.callee);
|
|
771
|
-
const args = node.arguments.map(
|
|
949
|
+
async evaluateCallExpression(node, env) {
|
|
950
|
+
const callee = await this.evaluate(node.callee, env);
|
|
951
|
+
const args = await Promise.all(node.arguments.map(el => this.evaluate(el, env)));
|
|
772
952
|
|
|
773
953
|
if (callee && typeof callee.call === 'function') {
|
|
774
|
-
|
|
954
|
+
const result = callee.call(this, args);
|
|
955
|
+
if (callee.isAsync) {
|
|
956
|
+
// If async, return the Promise wrapped in a box
|
|
957
|
+
// to prevent the interpreter's automatic await
|
|
958
|
+
return new VladXPromise(result);
|
|
959
|
+
}
|
|
960
|
+
return await result;
|
|
775
961
|
}
|
|
776
962
|
|
|
777
963
|
throw new Error(`"${node.callee.name || 'valore'}" non è una funzione`);
|
|
778
964
|
}
|
|
779
965
|
|
|
780
|
-
evaluateNewExpression(node) {
|
|
781
|
-
const klass = this.evaluate(node.callee);
|
|
966
|
+
async evaluateNewExpression(node, env) {
|
|
967
|
+
const klass = await this.evaluate(node.callee, env);
|
|
782
968
|
|
|
783
969
|
if (!(klass instanceof VladXClass)) {
|
|
784
970
|
throw new Error(`"${node.callee.name || 'valore'}" non è una classe`);
|
|
@@ -789,23 +975,28 @@ class Interpreter {
|
|
|
789
975
|
|
|
790
976
|
if (constructor) {
|
|
791
977
|
const boundConstructor = new BoundMethod(constructor, instance);
|
|
792
|
-
const args = node.arguments.map(
|
|
793
|
-
boundConstructor.call(this, args);
|
|
978
|
+
const args = await Promise.all(node.arguments.map(el => this.evaluate(el, env)));
|
|
979
|
+
await boundConstructor.call(this, args);
|
|
794
980
|
}
|
|
795
981
|
|
|
796
982
|
return instance;
|
|
797
983
|
}
|
|
798
984
|
|
|
799
|
-
evaluateMemberExpression(node) {
|
|
800
|
-
const obj = this.evaluate(node.object);
|
|
801
|
-
|
|
802
|
-
|
|
985
|
+
async evaluateMemberExpression(node, env) {
|
|
986
|
+
const obj = await this.evaluate(node.object, env);
|
|
987
|
+
|
|
988
|
+
if (node.optional && (obj === undefined || obj === null)) {
|
|
989
|
+
return null;
|
|
803
990
|
}
|
|
804
991
|
|
|
805
992
|
const prop = node.computed
|
|
806
|
-
? this.evaluate(node.property)
|
|
993
|
+
? await this.evaluate(node.property, env)
|
|
807
994
|
: node.property.name;
|
|
808
995
|
|
|
996
|
+
if (obj === undefined || obj === null) {
|
|
997
|
+
throw new Error(`Impossibile leggere proprietà "${prop}" di ${obj}`);
|
|
998
|
+
}
|
|
999
|
+
|
|
809
1000
|
// Handle array methods
|
|
810
1001
|
if (Array.isArray(obj)) {
|
|
811
1002
|
if (prop === 'aggiungi' || prop === 'push') {
|
|
@@ -817,9 +1008,36 @@ class Interpreter {
|
|
|
817
1008
|
if (prop === 'lunghezza' || prop === 'length') {
|
|
818
1009
|
return obj.length;
|
|
819
1010
|
}
|
|
1011
|
+
if (prop === 'mappa' || prop === 'map') {
|
|
1012
|
+
return {
|
|
1013
|
+
isAsync: true,
|
|
1014
|
+
call: async (interpreter, args) => {
|
|
1015
|
+
const callback = args[0];
|
|
1016
|
+
const results = [];
|
|
1017
|
+
for (let i = 0; i < obj.length; i++) {
|
|
1018
|
+
results.push(await callback.call(interpreter, [obj[i], i, obj]));
|
|
1019
|
+
}
|
|
1020
|
+
return results;
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
if (prop === 'filtra' || prop === 'filter') {
|
|
1025
|
+
return {
|
|
1026
|
+
isAsync: true,
|
|
1027
|
+
call: async (interpreter, args) => {
|
|
1028
|
+
const callback = args[0];
|
|
1029
|
+
const results = [];
|
|
1030
|
+
for (let i = 0; i < obj.length; i++) {
|
|
1031
|
+
if (interpreter.isTruthy(await callback.call(interpreter, [obj[i], i, obj]))) {
|
|
1032
|
+
results.push(obj[i]);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return results;
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
820
1039
|
}
|
|
821
1040
|
|
|
822
|
-
// Handle class instances
|
|
823
1041
|
if (obj instanceof VladXInstance) {
|
|
824
1042
|
return obj.get(prop);
|
|
825
1043
|
}
|