naidejs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,573 @@
1
+ export class Generator {
2
+ constructor() {
3
+ this.indent = 0;
4
+ this.output = [];
5
+ this.usesExpress = false;
6
+ this.models = new Map();
7
+ }
8
+
9
+ generate(ast) {
10
+ this.visitProgram(ast);
11
+ return this.output.join('\n');
12
+ }
13
+
14
+ emit(line) {
15
+ this.output.push(' '.repeat(this.indent) + line);
16
+ }
17
+
18
+ emitRaw(line) {
19
+ this.output.push(line);
20
+ }
21
+
22
+ visitProgram(node) {
23
+ for (const stmt of node.body) {
24
+ this.visitStatement(stmt);
25
+ }
26
+ }
27
+
28
+ visitStatement(node) {
29
+ switch (node.type) {
30
+ case 'Use': return this.visitUse(node);
31
+ case 'UseDestructured': return this.visitUseDestructured(node);
32
+ case 'Function': return this.visitFunction(node);
33
+ case 'Return': return this.visitReturn(node);
34
+ case 'ReturnStatus': return this.visitReturnStatus(node);
35
+ case 'TypedVar': return this.visitTypedVar(node);
36
+ case 'If': return this.visitIf(node);
37
+ case 'Each': return this.visitEach(node);
38
+ case 'For': return this.visitFor(node);
39
+ case 'While': return this.visitWhile(node);
40
+ case 'Match': return this.visitMatch(node);
41
+ case 'Try': return this.visitTry(node);
42
+ case 'Server': return this.visitServer(node);
43
+ case 'Model': return this.visitModel(node);
44
+ case 'On': return this.visitOn(node);
45
+ case 'Log': return this.visitLog(node);
46
+ case 'Throw': return this.visitThrow(node);
47
+ case 'Break': this.emit('break;'); return;
48
+ case 'Continue': this.emit('continue;'); return;
49
+ case 'Assignment': return this.visitAssignment(node);
50
+ case 'CompoundAssign': return this.visitCompoundAssign(node);
51
+ case 'ExprStatement': this.emit(this.expr(node.expression) + ';'); return;
52
+ case 'DbConnect': return this.visitDbConnect(node);
53
+ case 'AwaitAll': return this.visitAwaitAllStatement(node);
54
+ default:
55
+ this.emit(`/* unknown: ${node.type} */`);
56
+ }
57
+ }
58
+
59
+ visitUse(node) {
60
+ const source = node.source ? this.stringValue(node.source) : `'${node.name}'`;
61
+ const alias = node.alias || node.name;
62
+ if (source.includes('express')) this.usesExpress = true;
63
+ this.emit(`import ${alias} from ${source};`);
64
+ }
65
+
66
+ visitUseDestructured(node) {
67
+ const source = this.stringValue(node.source);
68
+ const names = node.names.map(n => n.alias ? `${n.name} as ${n.alias}` : n.name).join(', ');
69
+ this.emit(`import { ${names} } from ${source};`);
70
+ }
71
+
72
+ visitFunction(node) {
73
+ const exp = node.isPublic ? 'export ' : '';
74
+ const async = node.isAsync ? 'async ' : '';
75
+ const params = node.params.map(p => {
76
+ let s = p.spread ? '...' : '';
77
+ s += p.name;
78
+ if (p.defaultValue) s += ` = ${this.expr(p.defaultValue)}`;
79
+ return s;
80
+ }).join(', ');
81
+
82
+ this.emit(`${exp}${async}function ${node.name}(${params}) {`);
83
+ this.indent++;
84
+ for (const stmt of node.body) {
85
+ this.visitStatement(stmt);
86
+ }
87
+ this.indent--;
88
+ this.emit('}');
89
+ this.emitRaw('');
90
+ }
91
+
92
+ visitReturn(node) {
93
+ if (node.value === null) {
94
+ this.emit('return;');
95
+ } else {
96
+ this.emit(`return ${this.expr(node.value)};`);
97
+ }
98
+ }
99
+
100
+ visitReturnStatus(node) {
101
+ this.emit(`return res.status(${this.expr(node.statusCode)}).json(${this.expr(node.body)});`);
102
+ }
103
+
104
+ visitTypedVar(node) {
105
+ const keyword = node.isMut ? 'let' : 'const';
106
+ const exp = node.isPublic ? 'export ' : '';
107
+ this.emit(`${exp}${keyword} ${node.name} = ${this.expr(node.value)};`);
108
+ }
109
+
110
+ visitAssignment(node) {
111
+ const target = this.expr(node.target);
112
+ const value = this.expr(node.value);
113
+
114
+ // Destructuring assignment
115
+ if (node.target.type === 'Array') {
116
+ const names = node.target.elements.map(e => this.expr(e)).join(', ');
117
+ this.emit(`const [${names}] = ${value};`);
118
+ } else if (node.target.type === 'Object') {
119
+ const names = node.target.properties.map(p => {
120
+ if (p.type === 'shorthand') return p.key;
121
+ return `${p.key}: ${this.expr(p.value)}`;
122
+ }).join(', ');
123
+ this.emit(`const { ${names} } = ${value};`);
124
+ } else {
125
+ this.emit(`${target} = ${value};`);
126
+ }
127
+ }
128
+
129
+ visitCompoundAssign(node) {
130
+ this.emit(`${this.expr(node.target)} ${node.op} ${this.expr(node.value)};`);
131
+ }
132
+
133
+ visitIf(node) {
134
+ this.emit(`if (${this.expr(node.condition)}) {`);
135
+ this.indent++;
136
+ for (const stmt of node.body) this.visitStatement(stmt);
137
+ this.indent--;
138
+
139
+ for (const elif of node.elifs) {
140
+ this.emit(`} else if (${this.expr(elif.condition)}) {`);
141
+ this.indent++;
142
+ for (const stmt of elif.body) this.visitStatement(stmt);
143
+ this.indent--;
144
+ }
145
+
146
+ if (node.elseBody) {
147
+ this.emit('} else {');
148
+ this.indent++;
149
+ for (const stmt of node.elseBody) this.visitStatement(stmt);
150
+ this.indent--;
151
+ }
152
+ this.emit('}');
153
+ }
154
+
155
+ visitEach(node) {
156
+ const collection = this.expr(node.collection);
157
+ if (node.key) {
158
+ this.emit(`for (const [${node.key}, ${node.value}] of Object.entries(${collection})) {`);
159
+ } else {
160
+ this.emit(`for (const ${node.value} of ${collection}) {`);
161
+ }
162
+ this.indent++;
163
+ for (const stmt of node.body) this.visitStatement(stmt);
164
+ this.indent--;
165
+ this.emit('}');
166
+ }
167
+
168
+ visitFor(node) {
169
+ const varName = node.varName;
170
+ const start = this.expr(node.start);
171
+ const end = this.expr(node.end);
172
+ this.emit(`for (let ${varName} = ${start}; ${varName} < ${end}; ${varName}++) {`);
173
+ this.indent++;
174
+ for (const stmt of node.body) this.visitStatement(stmt);
175
+ this.indent--;
176
+ this.emit('}');
177
+ }
178
+
179
+ visitWhile(node) {
180
+ this.emit(`while (${this.expr(node.condition)}) {`);
181
+ this.indent++;
182
+ for (const stmt of node.body) this.visitStatement(stmt);
183
+ this.indent--;
184
+ this.emit('}');
185
+ }
186
+
187
+ visitMatch(node) {
188
+ this.emit(`switch (${this.expr(node.value)}) {`);
189
+ this.indent++;
190
+ for (const c of node.cases) {
191
+ if (c.pattern.type === 'DefaultPattern') {
192
+ this.emit('default: {');
193
+ } else {
194
+ this.emit(`case ${this.expr(c.pattern)}: {`);
195
+ }
196
+ this.indent++;
197
+ for (const stmt of c.body) this.visitStatement(stmt);
198
+ this.emit('break;');
199
+ this.indent--;
200
+ this.emit('}');
201
+ }
202
+ this.indent--;
203
+ this.emit('}');
204
+ }
205
+
206
+ visitTry(node) {
207
+ this.emit('try {');
208
+ this.indent++;
209
+ for (const stmt of node.body) this.visitStatement(stmt);
210
+ this.indent--;
211
+ if (node.catchBody) {
212
+ const catchParam = node.catchVar || '_err';
213
+ this.emit(`} catch (${catchParam}) {`);
214
+ this.indent++;
215
+ for (const stmt of node.catchBody) this.visitStatement(stmt);
216
+ this.indent--;
217
+ }
218
+ this.emit('}');
219
+ }
220
+
221
+ visitServer(node) {
222
+ this.emit(`import express from 'express';`);
223
+ this.emitRaw('');
224
+ this.emit(`const ${node.name} = express();`);
225
+ this.emit(`${node.name}.use(express.json());`);
226
+ this.emitRaw('');
227
+
228
+ // Middleware
229
+ for (const mid of node.middleware) {
230
+ this.emit(`${node.name}.use(${this.generateMiddleware(mid)});`);
231
+ this.emitRaw('');
232
+ }
233
+
234
+ // Routes
235
+ for (const route of node.routes) {
236
+ if (route.type === 'Route') {
237
+ this.visitRoute(node.name, route);
238
+ } else {
239
+ this.visitStatement(route);
240
+ }
241
+ }
242
+
243
+ // Listen
244
+ const port = node.port ? this.expr(node.port) : '3000';
245
+ this.emitRaw('');
246
+ this.emit(`${node.name}.listen(${port}, () => {`);
247
+ this.indent++;
248
+ this.emit(`console.log(\`Server running on port \${${port}}\`);`);
249
+ this.indent--;
250
+ this.emit('});');
251
+ }
252
+
253
+ visitRoute(appName, route) {
254
+ const method = route.method === 'del' ? 'delete' : route.method;
255
+ const path = this.stringValue(route.path);
256
+ const params = route.params.length > 0 ? route.params.join(', ') : 'req, res';
257
+
258
+ // Check if body uses await
259
+ const needsAsync = this.bodyUsesAwait(route.body);
260
+ const asyncPrefix = needsAsync ? 'async ' : '';
261
+
262
+ this.emit(`${appName}.${method}(${path}, ${asyncPrefix}(${params}) => {`);
263
+ this.indent++;
264
+
265
+ // If params don't include res, inject it
266
+ const hasRes = params.includes('res');
267
+
268
+ // Transform body: last expression with ret becomes res.json
269
+ for (let i = 0; i < route.body.length; i++) {
270
+ const stmt = route.body[i];
271
+ if (stmt.type === 'Return' && stmt.value !== null) {
272
+ if (hasRes || params === 'req, res') {
273
+ this.emit(`res.json(${this.expr(stmt.value)});`);
274
+ } else {
275
+ this.emit(`return ${this.expr(stmt.value)};`);
276
+ }
277
+ } else {
278
+ this.visitStatement(stmt);
279
+ }
280
+ }
281
+
282
+ this.indent--;
283
+ this.emit('});');
284
+ this.emitRaw('');
285
+ }
286
+
287
+ generateMiddleware(mid) {
288
+ const params = mid.params.join(', ');
289
+ let code = `(${params}) => {\n`;
290
+ for (const stmt of mid.body) {
291
+ code += ' ' + this.statementToString(stmt) + '\n';
292
+ }
293
+ code += ' }';
294
+ return code;
295
+ }
296
+
297
+ visitModel(node) {
298
+ this.models.set(node.name, node);
299
+
300
+ const exp = node.isPublic ? 'export ' : '';
301
+ const ext = node.parent ? ` extends ${node.parent}` : '';
302
+ this.emit(`${exp}class ${node.name}${ext} {`);
303
+ this.indent++;
304
+
305
+ if (node.fields.length > 0 || node.parent) {
306
+ const parentModel = node.parent ? this.models.get(node.parent) : null;
307
+ const parentFields = parentModel ? parentModel.fields : [];
308
+ const allFields = [...parentFields, ...node.fields];
309
+
310
+ const constructorParams = allFields.map(f => {
311
+ if (f.defaultValue) return `${f.name} = ${this.expr(f.defaultValue)}`;
312
+ return f.name;
313
+ }).join(', ');
314
+
315
+ this.emit(`constructor(${constructorParams}) {`);
316
+ this.indent++;
317
+ if (node.parent) {
318
+ const superArgs = parentFields.map(f => f.name).join(', ');
319
+ this.emit(`super(${superArgs});`);
320
+ }
321
+ for (const f of node.fields) {
322
+ this.emit(`this.${f.name} = ${f.name};`);
323
+ }
324
+ this.indent--;
325
+ this.emit('}');
326
+ this.emitRaw('');
327
+ }
328
+
329
+ // Methods
330
+ for (const method of node.methods) {
331
+ const async = method.isAsync ? 'async ' : '';
332
+ const params = method.params.map(p => {
333
+ let s = p.spread ? '...' : '';
334
+ s += p.name;
335
+ if (p.defaultValue) s += ` = ${this.expr(p.defaultValue)}`;
336
+ return s;
337
+ }).join(', ');
338
+
339
+ this.emit(`${async}${method.name}(${params}) {`);
340
+ this.indent++;
341
+ for (const stmt of method.body) {
342
+ this.visitStatement(stmt);
343
+ }
344
+ this.indent--;
345
+ this.emit('}');
346
+ this.emitRaw('');
347
+ }
348
+
349
+ this.indent--;
350
+ this.emit('}');
351
+ this.emitRaw('');
352
+ }
353
+
354
+ visitOn(node) {
355
+ const event = this.expr(node.event);
356
+ // Split event into object and event name
357
+ // e.g., process.exit -> process.on('exit', ...)
358
+ if (node.event.type === 'MemberAccess') {
359
+ const obj = this.expr(node.event.object);
360
+ const evt = node.event.property;
361
+ this.emit(`${obj}.on('${evt}', () => {`);
362
+ } else {
363
+ this.emit(`${event}(() => {`);
364
+ }
365
+ this.indent++;
366
+ for (const stmt of node.body) this.visitStatement(stmt);
367
+ this.indent--;
368
+ this.emit('});');
369
+ }
370
+
371
+ visitLog(node) {
372
+ const method = node.level === 'log' ? 'log' : node.level;
373
+ const args = node.args.map(a => this.expr(a)).join(', ');
374
+ this.emit(`console.${method}(${args});`);
375
+ }
376
+
377
+ visitThrow(node) {
378
+ const value = this.expr(node.value);
379
+ // If it's a string, wrap in Error
380
+ if (node.value.type === 'String') {
381
+ this.emit(`throw new Error(${value});`);
382
+ } else {
383
+ this.emit(`throw ${value};`);
384
+ }
385
+ }
386
+
387
+ visitDbConnect(node) {
388
+ this.emit(`const db = new Database(${this.expr(node.connectionString)});`);
389
+ }
390
+
391
+ visitAwaitAllStatement(node) {
392
+ const exprs = node.expressions.map(e => this.expr(e)).join(', ');
393
+ this.emit(`await Promise.all([${exprs}]);`);
394
+ }
395
+
396
+ // Expression generation
397
+ expr(node) {
398
+ if (!node) return 'undefined';
399
+
400
+ switch (node.type) {
401
+ case 'Number': return node.value;
402
+ case 'String': return this.generateString(node.value);
403
+ case 'Bool': return node.value ? 'true' : 'false';
404
+ case 'Null': return 'null';
405
+ case 'Self': return 'this';
406
+ case 'Identifier': return node.name;
407
+
408
+ case 'Binary':
409
+ return `(${this.expr(node.left)} ${node.op} ${this.expr(node.right)})`;
410
+
411
+ case 'Unary':
412
+ return `${node.op}${this.expr(node.expr)}`;
413
+
414
+ case 'Await':
415
+ return `await ${this.expr(node.expr)}`;
416
+
417
+ case 'AwaitAllExpr':
418
+ return `await Promise.all(${this.expr(node.expr)})`;
419
+
420
+ case 'Spread':
421
+ return `...${this.expr(node.expr)}`;
422
+
423
+ case 'New':
424
+ return `new ${this.expr(node.expr)}`;
425
+
426
+ case 'MemberAccess':
427
+ return `${this.expr(node.object)}.${node.property}`;
428
+
429
+ case 'OptionalAccess':
430
+ return `${this.expr(node.object)}?.${node.property}`;
431
+
432
+ case 'Call':
433
+ return `${this.expr(node.callee)}(${node.args.map(a => this.expr(a)).join(', ')})`;
434
+
435
+ case 'IndexAccess':
436
+ return `${this.expr(node.object)}[${this.expr(node.index)}]`;
437
+
438
+ case 'Array':
439
+ return `[${node.elements.map(e => this.expr(e)).join(', ')}]`;
440
+
441
+ case 'Object': {
442
+ const props = node.properties.map(p => {
443
+ if (p.type === 'spread') return `...${this.expr(p.value)}`;
444
+ if (p.type === 'shorthand') return p.key;
445
+ if (typeof p.key === 'string') return `${p.key}: ${this.expr(p.value)}`;
446
+ if (p.key.type === 'String') return `${this.generateString(p.key.value)}: ${this.expr(p.value)}`;
447
+ if (p.key.type === 'Computed') return `[${this.expr(p.key.expr)}]: ${this.expr(p.value)}`;
448
+ return `${this.expr(p.key)}: ${this.expr(p.value)}`;
449
+ }).join(', ');
450
+ return `{ ${props} }`;
451
+ }
452
+
453
+ case 'ArrowFn': {
454
+ const params = node.params.map(p => p.name).join(', ');
455
+ const body = this.expr(node.body);
456
+ const wrappedBody = node.body.type === 'Object' ? `(${body})` : body;
457
+ if (node.params.length === 1) return `${params} => ${wrappedBody}`;
458
+ return `(${params}) => ${wrappedBody}`;
459
+ }
460
+
461
+ case 'Lambda': {
462
+ const async = node.isAsync ? 'async ' : '';
463
+ const params = node.params.map(p => p.name).join(', ');
464
+ const bodyLines = [];
465
+ const savedOutput = this.output;
466
+ const savedIndent = this.indent;
467
+ this.output = bodyLines;
468
+ this.indent = 0;
469
+ for (const stmt of node.body) this.visitStatement(stmt);
470
+ this.output = savedOutput;
471
+ this.indent = savedIndent;
472
+ return `${async}function(${params}) { ${bodyLines.join(' ')} }`;
473
+ }
474
+
475
+ case 'Ternary':
476
+ return `(${this.expr(node.condition)} ? ${this.expr(node.consequent)} : ${this.expr(node.alternate)})`;
477
+
478
+ case 'Pipe':
479
+ return this.generatePipe(node);
480
+
481
+ default:
482
+ return `/* expr:${node.type} */`;
483
+ }
484
+ }
485
+
486
+ generateString(strData) {
487
+ if (!strData || !strData.parts) return '""';
488
+
489
+ const hasInterpolation = strData.parts.some(p => p.type === 'expr');
490
+
491
+ if (!hasInterpolation) {
492
+ const raw = strData.parts.map(p => p.value).join('');
493
+ return JSON.stringify(raw);
494
+ }
495
+
496
+ let result = '`';
497
+ for (const part of strData.parts) {
498
+ if (part.type === 'text') {
499
+ result += part.value.replace(/`/g, '\\`').replace(/\$/g, '\\$');
500
+ } else {
501
+ const exprCode = part.value.replace(/\bself\b/g, 'this');
502
+ result += '${' + exprCode + '}';
503
+ }
504
+ }
505
+ result += '`';
506
+ return result;
507
+ }
508
+
509
+ stringValue(strData) {
510
+ if (!strData || !strData.parts) return '""';
511
+ if (strData.raw !== null && strData.raw !== undefined) {
512
+ return JSON.stringify(strData.raw);
513
+ }
514
+ return this.generateString(strData);
515
+ }
516
+
517
+ generatePipe(node) {
518
+ const steps = [];
519
+ let current = node;
520
+ while (current.type === 'Pipe') {
521
+ steps.unshift(current.right);
522
+ current = current.left;
523
+ }
524
+ steps.unshift(current);
525
+
526
+ const ARRAY_METHODS = new Set(['filter', 'map', 'reduce', 'find', 'findIndex', 'some', 'every', 'flat', 'flatMap', 'sort', 'reverse', 'slice', 'splice', 'join', 'includes', 'indexOf', 'forEach']);
527
+
528
+ let result = this.expr(steps[0]);
529
+ for (let i = 1; i < steps.length; i++) {
530
+ const step = steps[i];
531
+ if (step.type === 'Call' && step.callee.type === 'Identifier' && ARRAY_METHODS.has(step.callee.name)) {
532
+ const args = step.args.map(a => this.expr(a)).join(', ');
533
+ result = `${result}.${step.callee.name}(${args})`;
534
+ } else if (step.type === 'Identifier' && ARRAY_METHODS.has(step.name)) {
535
+ result = `${result}.${step.name}()`;
536
+ } else if (step.type === 'Call') {
537
+ const callee = this.expr(step.callee);
538
+ const args = step.args.map(a => this.expr(a)).join(', ');
539
+ result = `${callee}(${result}${args ? ', ' + args : ''})`;
540
+ } else if (step.type === 'Identifier') {
541
+ result = `${step.name}(${result})`;
542
+ } else {
543
+ result = `(${this.expr(step)})(${result})`;
544
+ }
545
+ }
546
+ return result;
547
+ }
548
+
549
+ bodyUsesAwait(body) {
550
+ for (const stmt of body) {
551
+ if (this.stmtUsesAwait(stmt)) return true;
552
+ }
553
+ return false;
554
+ }
555
+
556
+ stmtUsesAwait(node) {
557
+ if (!node) return false;
558
+ const json = JSON.stringify(node);
559
+ return json.includes('"Await"') || json.includes('"AwaitAll"');
560
+ }
561
+
562
+ statementToString(stmt) {
563
+ const saved = this.output;
564
+ const savedIndent = this.indent;
565
+ this.output = [];
566
+ this.indent = 0;
567
+ this.visitStatement(stmt);
568
+ const result = this.output.join('\n');
569
+ this.output = saved;
570
+ this.indent = savedIndent;
571
+ return result;
572
+ }
573
+ }
package/src/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import { Lexer } from './lexer.js';
2
+ import { Parser } from './parser.js';
3
+ import { Generator } from './generator.js';
4
+ import { preprocess } from './preprocess.js';
5
+
6
+ export function compile(source, { mode = 'naide' } = {}) {
7
+ if (mode === 'x') {
8
+ source = preprocess(source);
9
+ }
10
+ const lexer = new Lexer(source);
11
+ const tokens = lexer.tokenize();
12
+ const parser = new Parser(tokens);
13
+ const ast = parser.parse();
14
+ const generator = new Generator();
15
+ const js = generator.generate(ast);
16
+ return { js, ast, tokens, naide: mode === 'x' ? source : null };
17
+ }
18
+
19
+ export function transpile(source, opts) {
20
+ return compile(source, opts).js;
21
+ }
22
+
23
+ export { preprocess };