vladx 1.0.1 → 1.2.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/examples/moduli/main.vx +19 -0
- package/examples/moduli/math.vx +13 -0
- package/package.json +1 -1
- package/programs/1/index.vx +3 -0
- package/programs/1/vladx-lock.json +5 -0
- package/programs/1/vladx.json +14 -0
- package/src/cli/cli.js +1 -0
- package/src/interpreter/interpreter.js +71 -0
- package/src/parser/ast.js +26 -0
- package/src/parser/parser.js +34 -0
- package/src/pm/cli.js +21 -0
- package/src/pm/commands/run.js +77 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* main.vx - Applicazione che usa i moduli
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
importa { somma, sottrai, PI } da "./math.vx";
|
|
6
|
+
|
|
7
|
+
stampa("Test Moduli VladX");
|
|
8
|
+
stampa("-----------------");
|
|
9
|
+
|
|
10
|
+
variabile r1 = somma(10, 5);
|
|
11
|
+
stampa("10 + 5 = " + r1);
|
|
12
|
+
|
|
13
|
+
variabile r2 = sottrai(20, 8);
|
|
14
|
+
stampa("20 - 8 = " + r2);
|
|
15
|
+
|
|
16
|
+
stampa("Valore di PI: " + PI);
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
stampa("Fine del test.");
|
package/package.json
CHANGED
package/src/cli/cli.js
CHANGED
|
@@ -69,6 +69,7 @@ function runFile(filePath) {
|
|
|
69
69
|
const ast = parser.parse();
|
|
70
70
|
|
|
71
71
|
const interpreter = new Interpreter();
|
|
72
|
+
interpreter.currentPath = absolutePath;
|
|
72
73
|
interpreter.interpret(ast);
|
|
73
74
|
} catch (error) {
|
|
74
75
|
console.error(colorize(`✗ Errore: ${error.message}`, 'red'));
|
|
@@ -116,11 +116,19 @@ class ArrowFunc {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
const fs = require('fs');
|
|
120
|
+
const path = require('path');
|
|
121
|
+
const { Lexer } = require('../lexer/lexer.js');
|
|
122
|
+
const { Parser } = require('../parser/parser.js');
|
|
123
|
+
|
|
119
124
|
class Interpreter {
|
|
120
125
|
constructor() {
|
|
121
126
|
this.globals = new Environment();
|
|
122
127
|
this.environment = this.globals;
|
|
123
128
|
this.output = [];
|
|
129
|
+
this.modules = new Map(); // path -> exports
|
|
130
|
+
this.currentPath = process.cwd();
|
|
131
|
+
this.exportedSymbols = new Map(); // symbols exported by the current module
|
|
124
132
|
|
|
125
133
|
// Built-in functions
|
|
126
134
|
this.globals.define('lunghezza', {
|
|
@@ -184,6 +192,10 @@ class Interpreter {
|
|
|
184
192
|
throw new BreakSignal();
|
|
185
193
|
case 'ContinueStatement':
|
|
186
194
|
throw new ContinueSignal();
|
|
195
|
+
case 'ExportNamedDeclaration':
|
|
196
|
+
return this.executeExportNamedDeclaration(node);
|
|
197
|
+
case 'ImportDeclaration':
|
|
198
|
+
return this.executeImportDeclaration(node);
|
|
187
199
|
case 'PrintStatement':
|
|
188
200
|
return this.executePrintStatement(node);
|
|
189
201
|
case 'ExpressionStatement':
|
|
@@ -261,6 +273,65 @@ class Interpreter {
|
|
|
261
273
|
}
|
|
262
274
|
}
|
|
263
275
|
|
|
276
|
+
executeExportNamedDeclaration(node) {
|
|
277
|
+
this.execute(node.declaration);
|
|
278
|
+
const name = node.declaration.name;
|
|
279
|
+
const value = this.environment.get(name);
|
|
280
|
+
this.exportedSymbols.set(name, value);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
executeImportDeclaration(node) {
|
|
284
|
+
const sourcePath = node.source;
|
|
285
|
+
let fullPath;
|
|
286
|
+
|
|
287
|
+
if (sourcePath.startsWith('./') || sourcePath.startsWith('../')) {
|
|
288
|
+
fullPath = path.resolve(path.dirname(this.currentPath), sourcePath);
|
|
289
|
+
} else {
|
|
290
|
+
// Check vladx_modules
|
|
291
|
+
fullPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
|
|
292
|
+
if (!fullPath.endsWith('.vx')) fullPath += '.vx';
|
|
293
|
+
if (!fs.existsSync(fullPath)) {
|
|
294
|
+
// Try index.vx inside the module folder
|
|
295
|
+
const folderPath = path.resolve(process.cwd(), 'vladx_modules', sourcePath);
|
|
296
|
+
fullPath = path.join(folderPath, 'index.vx');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!fs.existsSync(fullPath)) {
|
|
301
|
+
throw new Error(`Modulo non trovato: ${sourcePath}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let moduleExports;
|
|
305
|
+
if (this.modules.has(fullPath)) {
|
|
306
|
+
moduleExports = this.modules.get(fullPath);
|
|
307
|
+
} else {
|
|
308
|
+
// Load and execute module
|
|
309
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
310
|
+
const lexer = new Lexer(content);
|
|
311
|
+
const tokens = lexer.tokenize();
|
|
312
|
+
const parser = new Parser(tokens);
|
|
313
|
+
const program = parser.parse();
|
|
314
|
+
|
|
315
|
+
const subInterpreter = new Interpreter();
|
|
316
|
+
// Sharing the same modules cache
|
|
317
|
+
subInterpreter.modules = this.modules;
|
|
318
|
+
subInterpreter.currentPath = fullPath;
|
|
319
|
+
|
|
320
|
+
subInterpreter.interpret(program);
|
|
321
|
+
moduleExports = subInterpreter.exportedSymbols;
|
|
322
|
+
this.modules.set(fullPath, moduleExports);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Import specified symbols
|
|
326
|
+
for (const specifier of node.specifiers) {
|
|
327
|
+
if (moduleExports.has(specifier)) {
|
|
328
|
+
this.environment.define(specifier, moduleExports.get(specifier));
|
|
329
|
+
} else {
|
|
330
|
+
throw new Error(`Il modulo "${sourcePath}" non esporta "${specifier}"`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
264
335
|
executePrintStatement(node) {
|
|
265
336
|
const value = this.evaluate(node.argument);
|
|
266
337
|
const output = this.stringify(value);
|
package/src/parser/ast.js
CHANGED
|
@@ -232,6 +232,29 @@ class LogicalExpression extends ASTNode {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
class ExportNamedDeclaration {
|
|
236
|
+
constructor(declaration) {
|
|
237
|
+
this.type = 'ExportNamedDeclaration';
|
|
238
|
+
this.declaration = declaration;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
class ImportDeclaration {
|
|
243
|
+
constructor(specifiers, source) {
|
|
244
|
+
this.type = 'ImportDeclaration';
|
|
245
|
+
this.specifiers = specifiers; // Array di nomi
|
|
246
|
+
this.source = source; // StringLiteral path
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
class ImportSpecifier {
|
|
251
|
+
constructor(imported, local) {
|
|
252
|
+
this.type = 'ImportSpecifier';
|
|
253
|
+
this.imported = imported;
|
|
254
|
+
this.local = local;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
235
258
|
module.exports = {
|
|
236
259
|
ASTNode,
|
|
237
260
|
Program,
|
|
@@ -246,6 +269,9 @@ module.exports = {
|
|
|
246
269
|
ContinueStatement,
|
|
247
270
|
ExpressionStatement,
|
|
248
271
|
PrintStatement,
|
|
272
|
+
ExportNamedDeclaration,
|
|
273
|
+
ImportDeclaration,
|
|
274
|
+
ImportSpecifier,
|
|
249
275
|
Identifier,
|
|
250
276
|
NumericLiteral,
|
|
251
277
|
StringLiteral,
|
package/src/parser/parser.js
CHANGED
|
@@ -69,6 +69,8 @@ class Parser {
|
|
|
69
69
|
|
|
70
70
|
declaration() {
|
|
71
71
|
try {
|
|
72
|
+
if (this.match(TokenType.IMPORTA)) return this.importDeclaration();
|
|
73
|
+
if (this.match(TokenType.ESPORTA)) return this.exportDeclaration();
|
|
72
74
|
if (this.match(TokenType.VARIABILE)) return this.variableDeclaration(false);
|
|
73
75
|
if (this.match(TokenType.COSTANTE)) return this.variableDeclaration(true);
|
|
74
76
|
if (this.match(TokenType.FUNZIONE)) return this.functionDeclaration();
|
|
@@ -89,6 +91,38 @@ class Parser {
|
|
|
89
91
|
return new AST.VariableDeclaration(name, value, isConstant);
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
importDeclaration() {
|
|
95
|
+
this.consume(TokenType.GRAFFA_APERTA, 'Atteso "{" dopo "importa"');
|
|
96
|
+
const specifiers = [];
|
|
97
|
+
if (!this.check(TokenType.GRAFFA_CHIUSA)) {
|
|
98
|
+
do {
|
|
99
|
+
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome import atteso').value;
|
|
100
|
+
specifiers.push(name);
|
|
101
|
+
} while (this.match(TokenType.VIRGOLA));
|
|
102
|
+
}
|
|
103
|
+
this.consume(TokenType.GRAFFA_CHIUSA, 'Atteso "}" dopo i nomi importati');
|
|
104
|
+
this.consume(TokenType.DA, 'Attesa parola chiave "da" dopo gli import');
|
|
105
|
+
const source = this.consume(TokenType.STRINGA, 'Percorso modulo atteso come stringa').value;
|
|
106
|
+
this.match(TokenType.PUNTO_VIRGOLA);
|
|
107
|
+
return new AST.ImportDeclaration(specifiers, source);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
exportDeclaration() {
|
|
111
|
+
if (this.match(TokenType.FUNZIONE)) {
|
|
112
|
+
const decl = this.functionDeclaration();
|
|
113
|
+
return new AST.ExportNamedDeclaration(decl);
|
|
114
|
+
}
|
|
115
|
+
if (this.match(TokenType.VARIABILE)) {
|
|
116
|
+
const decl = this.variableDeclaration(false);
|
|
117
|
+
return new AST.ExportNamedDeclaration(decl);
|
|
118
|
+
}
|
|
119
|
+
if (this.match(TokenType.COSTANTE)) {
|
|
120
|
+
const decl = this.variableDeclaration(true);
|
|
121
|
+
return new AST.ExportNamedDeclaration(decl);
|
|
122
|
+
}
|
|
123
|
+
throw new ParserError('Atteso funzione o variabile dopo "esporta"', this.peek());
|
|
124
|
+
}
|
|
125
|
+
|
|
92
126
|
functionDeclaration() {
|
|
93
127
|
const name = this.consume(TokenType.IDENTIFICATORE, 'Nome funzione atteso').value;
|
|
94
128
|
this.consume(TokenType.PARENTESI_APERTA, 'Atteso "(" dopo nome funzione');
|
package/src/pm/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ const uninstall = require('./commands/uninstall.js');
|
|
|
11
11
|
const search = require('./commands/search.js');
|
|
12
12
|
const list = require('./commands/list.js');
|
|
13
13
|
const config = require('./commands/config.js');
|
|
14
|
+
const runCmd = require('./commands/run.js');
|
|
14
15
|
|
|
15
16
|
const VERSION = '1.0.0';
|
|
16
17
|
|
|
@@ -40,6 +41,10 @@ ${colorize('Uso:', 'cyan')}
|
|
|
40
41
|
vladpm uninstall <pkg> Rimuove un pacchetto
|
|
41
42
|
vladpm search <query> Cerca pacchetti
|
|
42
43
|
vladpm list Lista pacchetti installati
|
|
44
|
+
vladpm run <script> Esegue uno script definito in vladx.json
|
|
45
|
+
vladpm start Scorciatoia per "vladpm run start"
|
|
46
|
+
vladpm test Scorciatoia per "vladpm run test"
|
|
47
|
+
vladpm dev Scorciatoia per "vladpm run dev"
|
|
43
48
|
vladpm --help, -h Mostra questo aiuto
|
|
44
49
|
vladpm --version, -v Mostra la versione
|
|
45
50
|
|
|
@@ -116,6 +121,22 @@ async function run(args) {
|
|
|
116
121
|
await config.execute(args.slice(1));
|
|
117
122
|
break;
|
|
118
123
|
|
|
124
|
+
case 'run':
|
|
125
|
+
await runCmd.execute(args.slice(1));
|
|
126
|
+
break;
|
|
127
|
+
|
|
128
|
+
case 'start':
|
|
129
|
+
await runCmd.execute(['start', ...args.slice(1)]);
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
case 'test':
|
|
133
|
+
await runCmd.execute(['test', ...args.slice(1)]);
|
|
134
|
+
break;
|
|
135
|
+
|
|
136
|
+
case 'dev':
|
|
137
|
+
await runCmd.execute(['dev', ...args.slice(1)]);
|
|
138
|
+
break;
|
|
139
|
+
|
|
119
140
|
default:
|
|
120
141
|
console.error(colorize(`✗ Comando sconosciuto: ${command}`, 'red'));
|
|
121
142
|
console.log('Usa "vladpm --help" per vedere i comandi disponibili.');
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VladPM - run command
|
|
3
|
+
* Esegue script definiti in vladx.json
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
const { getVladxJson } = require('../utils/config.js');
|
|
8
|
+
|
|
9
|
+
const COLORS = {
|
|
10
|
+
reset: '\x1b[0m',
|
|
11
|
+
bright: '\x1b[1m',
|
|
12
|
+
red: '\x1b[31m',
|
|
13
|
+
green: '\x1b[32m',
|
|
14
|
+
yellow: '\x1b[33m',
|
|
15
|
+
cyan: '\x1b[36m'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function colorize(text, color) {
|
|
19
|
+
return `${COLORS[color]}${text}${COLORS.reset}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function execute(args) {
|
|
23
|
+
if (args.length === 0) {
|
|
24
|
+
console.error(colorize('✗ Errore: Specificare il nome dello script da eseguire.', 'red'));
|
|
25
|
+
console.log('Uso: vladpm run <script>');
|
|
26
|
+
showScripts();
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const scriptName = args[0];
|
|
31
|
+
const vladxJson = getVladxJson();
|
|
32
|
+
|
|
33
|
+
if (!vladxJson) {
|
|
34
|
+
console.error(colorize('✗ Errore: vladx.json non trovato.', 'red'));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const scripts = vladxJson.scripts || {};
|
|
39
|
+
const command = scripts[scriptName];
|
|
40
|
+
|
|
41
|
+
if (!command) {
|
|
42
|
+
console.error(colorize(`✗ Errore: Script "${scriptName}" non trovato in vladx.json.`, 'red'));
|
|
43
|
+
showScripts(scripts);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(`\n> ${vladxJson.name}@${vladxJson.version} ${scriptName}`);
|
|
48
|
+
console.log(`> ${command}\n`);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Aggiungiamo il path della bin di vladx locale all'ambiente se necessario
|
|
52
|
+
// Ma per ora assumiamo che vladx sia nel path o eseguito tramite node
|
|
53
|
+
|
|
54
|
+
execSync(command, {
|
|
55
|
+
stdio: 'inherit',
|
|
56
|
+
env: { ...process.env, PATH: `${process.env.PATH}:${process.cwd()}/node_modules/.bin` }
|
|
57
|
+
});
|
|
58
|
+
} catch (error) {
|
|
59
|
+
process.exit(error.status || 1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function showScripts(scripts) {
|
|
64
|
+
if (!scripts) {
|
|
65
|
+
const vladxJson = getVladxJson();
|
|
66
|
+
scripts = vladxJson ? vladxJson.scripts : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (scripts && Object.keys(scripts).length > 0) {
|
|
70
|
+
console.log('\nScript disponibili:');
|
|
71
|
+
for (const name of Object.keys(scripts)) {
|
|
72
|
+
console.log(` - ${colorize(name, 'cyan')}: ${scripts[name]}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { execute };
|