vladx 1.0.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/vladpm +9 -0
- package/bin/vladx +10 -0
- package/examples/esempio.vx +79 -0
- package/package.json +23 -0
- package/src/cli/cli.js +191 -0
- package/src/index.js +32 -0
- package/src/interpreter/interpreter.js +455 -0
- package/src/lexer/lexer.js +284 -0
- package/src/lexer/tokens.js +116 -0
- package/src/parser/ast.js +265 -0
- package/src/parser/parser.js +476 -0
- package/src/pm/cli.js +130 -0
- package/src/pm/commands/config.js +76 -0
- package/src/pm/commands/init.js +129 -0
- package/src/pm/commands/install.js +185 -0
- package/src/pm/commands/list.js +70 -0
- package/src/pm/commands/login.js +124 -0
- package/src/pm/commands/publish.js +115 -0
- package/src/pm/commands/search.js +55 -0
- package/src/pm/commands/uninstall.js +68 -0
- package/src/pm/utils/api.js +135 -0
- package/src/pm/utils/config.js +117 -0
- package/src/repl/repl.js +179 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VladX Interpreter
|
|
3
|
+
* Esecutore dell'AST
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class Environment {
|
|
7
|
+
constructor(parent = null) {
|
|
8
|
+
this.values = new Map();
|
|
9
|
+
this.constants = new Set();
|
|
10
|
+
this.parent = parent;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
define(name, value, isConstant = false) {
|
|
14
|
+
if (this.values.has(name)) {
|
|
15
|
+
throw new Error(`Variabile "${name}" già dichiarata in questo scope`);
|
|
16
|
+
}
|
|
17
|
+
this.values.set(name, value);
|
|
18
|
+
if (isConstant) this.constants.add(name);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get(name) {
|
|
22
|
+
if (this.values.has(name)) {
|
|
23
|
+
return this.values.get(name);
|
|
24
|
+
}
|
|
25
|
+
if (this.parent) {
|
|
26
|
+
return this.parent.get(name);
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`Variabile "${name}" non definita`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
set(name, value) {
|
|
32
|
+
if (this.values.has(name)) {
|
|
33
|
+
if (this.constants.has(name)) {
|
|
34
|
+
throw new Error(`Non puoi riassegnare la costante "${name}"`);
|
|
35
|
+
}
|
|
36
|
+
this.values.set(name, value);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (this.parent) {
|
|
40
|
+
this.parent.set(name, value);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Variabile "${name}" non definita`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
has(name) {
|
|
47
|
+
if (this.values.has(name)) return true;
|
|
48
|
+
if (this.parent) return this.parent.has(name);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class ReturnValue {
|
|
54
|
+
constructor(value) {
|
|
55
|
+
this.value = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class BreakSignal { }
|
|
60
|
+
class ContinueSignal { }
|
|
61
|
+
|
|
62
|
+
class VladXFunction {
|
|
63
|
+
constructor(declaration, closure) {
|
|
64
|
+
this.declaration = declaration;
|
|
65
|
+
this.closure = closure;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
call(interpreter, args) {
|
|
69
|
+
const env = new Environment(this.closure);
|
|
70
|
+
for (let i = 0; i < this.declaration.params.length; i++) {
|
|
71
|
+
env.define(this.declaration.params[i], args[i] !== undefined ? args[i] : null);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
interpreter.executeBlock(this.declaration.body.body, env);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
if (e instanceof ReturnValue) {
|
|
78
|
+
return e.value;
|
|
79
|
+
}
|
|
80
|
+
throw e;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class ArrowFunc {
|
|
87
|
+
constructor(params, body, closure) {
|
|
88
|
+
this.params = params;
|
|
89
|
+
this.body = body;
|
|
90
|
+
this.closure = closure;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
call(interpreter, args) {
|
|
94
|
+
const env = new Environment(this.closure);
|
|
95
|
+
for (let i = 0; i < this.params.length; i++) {
|
|
96
|
+
env.define(this.params[i], args[i] !== undefined ? args[i] : null);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.body.type === 'BlockStatement') {
|
|
100
|
+
try {
|
|
101
|
+
interpreter.executeBlock(this.body.body, env);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
if (e instanceof ReturnValue) {
|
|
104
|
+
return e.value;
|
|
105
|
+
}
|
|
106
|
+
throw e;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
} else {
|
|
110
|
+
const previousEnv = interpreter.environment;
|
|
111
|
+
interpreter.environment = env;
|
|
112
|
+
const result = interpreter.evaluate(this.body);
|
|
113
|
+
interpreter.environment = previousEnv;
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class Interpreter {
|
|
120
|
+
constructor() {
|
|
121
|
+
this.globals = new Environment();
|
|
122
|
+
this.environment = this.globals;
|
|
123
|
+
this.output = [];
|
|
124
|
+
|
|
125
|
+
// Built-in functions
|
|
126
|
+
this.globals.define('lunghezza', {
|
|
127
|
+
call: (_, args) => {
|
|
128
|
+
if (args[0] && (Array.isArray(args[0]) || typeof args[0] === 'string')) {
|
|
129
|
+
return args[0].length;
|
|
130
|
+
}
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
this.globals.define('tipo', {
|
|
136
|
+
call: (_, args) => typeof args[0]
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
this.globals.define('numero', {
|
|
140
|
+
call: (_, args) => parseFloat(args[0])
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
this.globals.define('stringa', {
|
|
144
|
+
call: (_, args) => String(args[0])
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
this.globals.define('array', {
|
|
148
|
+
call: (_, args) => Array.isArray(args[0])
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
interpret(program) {
|
|
153
|
+
this.output = [];
|
|
154
|
+
try {
|
|
155
|
+
for (const statement of program.body) {
|
|
156
|
+
this.execute(statement);
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error instanceof ReturnValue) {
|
|
160
|
+
return error.value;
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
return this.output;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
execute(node) {
|
|
168
|
+
switch (node.type) {
|
|
169
|
+
case 'VariableDeclaration':
|
|
170
|
+
return this.executeVariableDeclaration(node);
|
|
171
|
+
case 'FunctionDeclaration':
|
|
172
|
+
return this.executeFunctionDeclaration(node);
|
|
173
|
+
case 'BlockStatement':
|
|
174
|
+
return this.executeBlock(node.body, new Environment(this.environment));
|
|
175
|
+
case 'IfStatement':
|
|
176
|
+
return this.executeIfStatement(node);
|
|
177
|
+
case 'WhileStatement':
|
|
178
|
+
return this.executeWhileStatement(node);
|
|
179
|
+
case 'ForStatement':
|
|
180
|
+
return this.executeForStatement(node);
|
|
181
|
+
case 'ReturnStatement':
|
|
182
|
+
throw new ReturnValue(node.argument ? this.evaluate(node.argument) : null);
|
|
183
|
+
case 'BreakStatement':
|
|
184
|
+
throw new BreakSignal();
|
|
185
|
+
case 'ContinueStatement':
|
|
186
|
+
throw new ContinueSignal();
|
|
187
|
+
case 'PrintStatement':
|
|
188
|
+
return this.executePrintStatement(node);
|
|
189
|
+
case 'ExpressionStatement':
|
|
190
|
+
return this.evaluate(node.expression);
|
|
191
|
+
default:
|
|
192
|
+
throw new Error(`Tipo statement sconosciuto: ${node.type}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
executeVariableDeclaration(node) {
|
|
197
|
+
const value = node.value ? this.evaluate(node.value) : null;
|
|
198
|
+
this.environment.define(node.name, value, node.isConstant);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
executeFunctionDeclaration(node) {
|
|
202
|
+
const func = new VladXFunction(node, this.environment);
|
|
203
|
+
this.environment.define(node.name, func);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
executeBlock(statements, env) {
|
|
207
|
+
const previousEnv = this.environment;
|
|
208
|
+
this.environment = env;
|
|
209
|
+
try {
|
|
210
|
+
for (const statement of statements) {
|
|
211
|
+
this.execute(statement);
|
|
212
|
+
}
|
|
213
|
+
} finally {
|
|
214
|
+
this.environment = previousEnv;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
executeIfStatement(node) {
|
|
219
|
+
if (this.isTruthy(this.evaluate(node.condition))) {
|
|
220
|
+
this.execute(node.consequent);
|
|
221
|
+
} else if (node.alternate) {
|
|
222
|
+
this.execute(node.alternate);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
executeWhileStatement(node) {
|
|
227
|
+
while (this.isTruthy(this.evaluate(node.condition))) {
|
|
228
|
+
try {
|
|
229
|
+
this.execute(node.body);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
if (e instanceof BreakSignal) break;
|
|
232
|
+
if (e instanceof ContinueSignal) continue;
|
|
233
|
+
throw e;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
executeForStatement(node) {
|
|
239
|
+
const forEnv = new Environment(this.environment);
|
|
240
|
+
const previousEnv = this.environment;
|
|
241
|
+
this.environment = forEnv;
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
if (node.init) this.execute(node.init);
|
|
245
|
+
|
|
246
|
+
while (!node.condition || this.isTruthy(this.evaluate(node.condition))) {
|
|
247
|
+
try {
|
|
248
|
+
this.execute(node.body);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
if (e instanceof BreakSignal) break;
|
|
251
|
+
if (e instanceof ContinueSignal) {
|
|
252
|
+
if (node.update) this.evaluate(node.update);
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
throw e;
|
|
256
|
+
}
|
|
257
|
+
if (node.update) this.evaluate(node.update);
|
|
258
|
+
}
|
|
259
|
+
} finally {
|
|
260
|
+
this.environment = previousEnv;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
executePrintStatement(node) {
|
|
265
|
+
const value = this.evaluate(node.argument);
|
|
266
|
+
const output = this.stringify(value);
|
|
267
|
+
console.log(output);
|
|
268
|
+
this.output.push(output);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
evaluate(node) {
|
|
272
|
+
switch (node.type) {
|
|
273
|
+
case 'NumericLiteral':
|
|
274
|
+
return node.value;
|
|
275
|
+
case 'StringLiteral':
|
|
276
|
+
return node.value;
|
|
277
|
+
case 'BooleanLiteral':
|
|
278
|
+
return node.value;
|
|
279
|
+
case 'NullLiteral':
|
|
280
|
+
return null;
|
|
281
|
+
case 'Identifier':
|
|
282
|
+
return this.environment.get(node.name);
|
|
283
|
+
case 'ArrayLiteral':
|
|
284
|
+
return node.elements.map(el => this.evaluate(el));
|
|
285
|
+
case 'ObjectLiteral':
|
|
286
|
+
const obj = {};
|
|
287
|
+
for (const prop of node.properties) {
|
|
288
|
+
obj[prop.key] = this.evaluate(prop.value);
|
|
289
|
+
}
|
|
290
|
+
return obj;
|
|
291
|
+
case 'BinaryExpression':
|
|
292
|
+
return this.evaluateBinaryExpression(node);
|
|
293
|
+
case 'LogicalExpression':
|
|
294
|
+
return this.evaluateLogicalExpression(node);
|
|
295
|
+
case 'UnaryExpression':
|
|
296
|
+
return this.evaluateUnaryExpression(node);
|
|
297
|
+
case 'AssignmentExpression':
|
|
298
|
+
return this.evaluateAssignment(node);
|
|
299
|
+
case 'UpdateExpression':
|
|
300
|
+
return this.evaluateUpdateExpression(node);
|
|
301
|
+
case 'CallExpression':
|
|
302
|
+
return this.evaluateCallExpression(node);
|
|
303
|
+
case 'MemberExpression':
|
|
304
|
+
return this.evaluateMemberExpression(node);
|
|
305
|
+
case 'ArrowFunction':
|
|
306
|
+
return new ArrowFunc(node.params, node.body, this.environment);
|
|
307
|
+
default:
|
|
308
|
+
throw new Error(`Tipo espressione sconosciuto: ${node.type}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
evaluateBinaryExpression(node) {
|
|
313
|
+
const left = this.evaluate(node.left);
|
|
314
|
+
const right = this.evaluate(node.right);
|
|
315
|
+
|
|
316
|
+
switch (node.operator) {
|
|
317
|
+
case '+':
|
|
318
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
319
|
+
return this.stringify(left) + this.stringify(right);
|
|
320
|
+
}
|
|
321
|
+
return left + right;
|
|
322
|
+
case '-': return left - right;
|
|
323
|
+
case '*': return left * right;
|
|
324
|
+
case '/': return left / right;
|
|
325
|
+
case '%': return left % right;
|
|
326
|
+
case '<': return left < right;
|
|
327
|
+
case '>': return left > right;
|
|
328
|
+
case '<=': return left <= right;
|
|
329
|
+
case '>=': return left >= right;
|
|
330
|
+
case '==': return left == right;
|
|
331
|
+
case '===': return left === right;
|
|
332
|
+
case '!=': return left != right;
|
|
333
|
+
case '!==': return left !== right;
|
|
334
|
+
default:
|
|
335
|
+
throw new Error(`Operatore binario sconosciuto: ${node.operator}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
evaluateLogicalExpression(node) {
|
|
340
|
+
const left = this.evaluate(node.left);
|
|
341
|
+
if (node.operator === '||') {
|
|
342
|
+
return this.isTruthy(left) ? left : this.evaluate(node.right);
|
|
343
|
+
} else {
|
|
344
|
+
return !this.isTruthy(left) ? left : this.evaluate(node.right);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
evaluateUnaryExpression(node) {
|
|
349
|
+
const argument = this.evaluate(node.argument);
|
|
350
|
+
switch (node.operator) {
|
|
351
|
+
case '-': return -argument;
|
|
352
|
+
case '!': return !this.isTruthy(argument);
|
|
353
|
+
default:
|
|
354
|
+
throw new Error(`Operatore unario sconosciuto: ${node.operator}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
evaluateAssignment(node) {
|
|
359
|
+
let value = this.evaluate(node.right);
|
|
360
|
+
|
|
361
|
+
if (node.operator === '+=' || node.operator === '-=') {
|
|
362
|
+
const current = this.evaluate(node.left);
|
|
363
|
+
value = node.operator === '+=' ? current + value : current - value;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (node.left.type === 'Identifier') {
|
|
367
|
+
this.environment.set(node.left.name, value);
|
|
368
|
+
} else if (node.left.type === 'MemberExpression') {
|
|
369
|
+
const obj = this.evaluate(node.left.object);
|
|
370
|
+
const prop = node.left.computed
|
|
371
|
+
? this.evaluate(node.left.property)
|
|
372
|
+
: node.left.property.name;
|
|
373
|
+
obj[prop] = value;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return value;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
evaluateUpdateExpression(node) {
|
|
380
|
+
const current = this.evaluate(node.argument);
|
|
381
|
+
const newValue = node.operator === '++' ? current + 1 : current - 1;
|
|
382
|
+
|
|
383
|
+
if (node.argument.type === 'Identifier') {
|
|
384
|
+
this.environment.set(node.argument.name, newValue);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return node.prefix ? newValue : current;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
evaluateCallExpression(node) {
|
|
391
|
+
const callee = this.evaluate(node.callee);
|
|
392
|
+
const args = node.arguments.map(arg => this.evaluate(arg));
|
|
393
|
+
|
|
394
|
+
if (callee && typeof callee.call === 'function') {
|
|
395
|
+
return callee.call(this, args);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
throw new Error(`"${node.callee.name || 'valore'}" non è una funzione`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
evaluateMemberExpression(node) {
|
|
402
|
+
const obj = this.evaluate(node.object);
|
|
403
|
+
if (obj === null || obj === undefined) {
|
|
404
|
+
throw new Error('Impossibile accedere a proprietà di null/undefined');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const prop = node.computed
|
|
408
|
+
? this.evaluate(node.property)
|
|
409
|
+
: node.property.name;
|
|
410
|
+
|
|
411
|
+
// Handle array methods
|
|
412
|
+
if (Array.isArray(obj)) {
|
|
413
|
+
if (prop === 'aggiungi' || prop === 'push') {
|
|
414
|
+
return { call: (_, args) => { obj.push(...args); return obj.length; } };
|
|
415
|
+
}
|
|
416
|
+
if (prop === 'rimuovi' || prop === 'pop') {
|
|
417
|
+
return { call: () => obj.pop() };
|
|
418
|
+
}
|
|
419
|
+
if (prop === 'lunghezza' || prop === 'length') {
|
|
420
|
+
return obj.length;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return obj[prop];
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
isTruthy(value) {
|
|
428
|
+
if (value === null || value === undefined) return false;
|
|
429
|
+
if (typeof value === 'boolean') return value;
|
|
430
|
+
if (typeof value === 'number') return value !== 0;
|
|
431
|
+
if (typeof value === 'string') return value.length > 0;
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
stringify(value) {
|
|
436
|
+
if (value === null) return 'nullo';
|
|
437
|
+
if (value === undefined) return 'indefinito';
|
|
438
|
+
if (typeof value === 'boolean') return value ? 'vero' : 'falso';
|
|
439
|
+
if (Array.isArray(value)) {
|
|
440
|
+
return '[' + value.map(v => this.stringify(v)).join(', ') + ']';
|
|
441
|
+
}
|
|
442
|
+
if (typeof value === 'object') {
|
|
443
|
+
if (value instanceof VladXFunction || value instanceof ArrowFunc) {
|
|
444
|
+
return '[Funzione]';
|
|
445
|
+
}
|
|
446
|
+
const props = Object.entries(value)
|
|
447
|
+
.map(([k, v]) => `${k}: ${this.stringify(v)}`)
|
|
448
|
+
.join(', ');
|
|
449
|
+
return '{ ' + props + ' }';
|
|
450
|
+
}
|
|
451
|
+
return String(value);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
module.exports = { Interpreter, Environment };
|