naider 1.24.5 → 2.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.
@@ -8,6 +8,7 @@ export class CGenerator {
8
8
  this.typedefs = [];
9
9
  this.functions = [];
10
10
  this.mainBody = [];
11
+ this.variableTypes = new Map();
11
12
  this.sourceMap = [];
12
13
  this.currentSourceLine = 0;
13
14
  this.sourceFile = options.sourceFile || null;
@@ -252,9 +253,39 @@ export class CGenerator {
252
253
 
253
254
  visitTypedVar(node) {
254
255
  const type = node.varType ? this.mapType(node.varType) : this.inferCType(node.value);
256
+ this.variableTypes.set(node.name, type);
255
257
  this.emit(`${type} ${node.name} = ${this.expr(node.value)};`);
256
258
  }
257
259
 
260
+ formatSpecifierForType(type) {
261
+ if (type === 'int') return '%d';
262
+ if (type === 'double') return '%g';
263
+ if (type === 'const char*') return '%s';
264
+ return '%p';
265
+ }
266
+
267
+ formatSpecifierForExpression(node) {
268
+ if (!node) return '%p';
269
+ if (node.type === 'String' || node.type === 'InterpolatedString') return '%s';
270
+ if (node.type === 'Bool') return '%d';
271
+ if (node.type === 'Number') {
272
+ return Number.isInteger(Number(node.value)) ? '%d' : '%g';
273
+ }
274
+ if (node.type === 'Identifier') {
275
+ return this.formatSpecifierForType(this.variableTypes.get(node.name));
276
+ }
277
+ if (node.type === 'Binary') {
278
+ if (['==', '!=', '<', '<=', '>', '>=', '&&', '||'].includes(node.op)) {
279
+ return '%d';
280
+ }
281
+ return this.formatSpecifierForExpression(node.left) === '%g'
282
+ || this.formatSpecifierForExpression(node.right) === '%g'
283
+ ? '%g'
284
+ : '%d';
285
+ }
286
+ return '%p';
287
+ }
288
+
258
289
  inferCType(valueNode) {
259
290
  if (!valueNode) return 'void*';
260
291
  switch (valueNode.type) {
@@ -421,25 +452,14 @@ export class CGenerator {
421
452
  visitLog(node) {
422
453
  this.includes.add('<stdio.h>');
423
454
  const args = node.args.map(a => this.expr(a));
424
- if (args.length === 1) {
425
- const arg = args[0];
426
- if (node.args[0] && node.args[0].type === 'String') {
427
- this.emit(`printf(${arg});`);
428
- this.emit(`printf("\\n");`);
429
- } else {
430
- this.emit(`printf("%s\\n", ${arg});`);
431
- }
432
- } else {
433
- // Multiple args: print each separated by space
434
- const fmtParts = args.map(() => '%s').join(' ');
435
- this.emit(`printf("${fmtParts}\\n", ${args.join(', ')});`);
436
- }
455
+ const format = node.args.map(arg => this.formatSpecifierForExpression(arg)).join(' ');
456
+ this.emit(`printf("${format}\\n", ${args.join(', ')});`);
437
457
  }
438
458
 
439
459
  visitThrow(node) {
440
460
  this.includes.add('<stdio.h>');
441
461
  this.includes.add('<stdlib.h>');
442
- this.emit(`fprintf(stderr, "Error: %s\\n", ${this.expr(node.value)});`);
462
+ this.emit(`fprintf(stderr, "Error: ${this.formatSpecifierForExpression(node.value)}\\n", ${this.expr(node.value)});`);
443
463
  this.emit('exit(1);');
444
464
  }
445
465
 
@@ -2,6 +2,7 @@ export class GoGenerator {
2
2
  constructor(options = {}) {
3
3
  this.indent = 0;
4
4
  this.output = [];
5
+ this.mainBody = [];
5
6
  this.imports = new Set();
6
7
  this.models = new Map();
7
8
  this.schemas = new Map();
@@ -16,6 +17,9 @@ export class GoGenerator {
16
17
 
17
18
  generate(ast) {
18
19
  this.visitProgram(ast);
20
+ this.output.push('func main() {');
21
+ this.output.push(...this.mainBody);
22
+ this.output.push('}');
19
23
 
20
24
  const preamble = ['package main', ''];
21
25
  if (this.imports.size > 0) {
@@ -46,8 +50,30 @@ export class GoGenerator {
46
50
  }
47
51
 
48
52
  visitProgram(node) {
53
+ const declarations = new Set([
54
+ 'Use',
55
+ 'UseDestructured',
56
+ 'Function',
57
+ 'TypedVar',
58
+ 'Model',
59
+ 'SchemaDecl',
60
+ 'EnumDecl',
61
+ 'ClassDecl',
62
+ ]);
63
+
49
64
  for (const stmt of node.body) {
65
+ if (declarations.has(stmt.type)) {
66
+ this.visitStatement(stmt);
67
+ continue;
68
+ }
69
+
70
+ const output = this.output;
71
+ const indent = this.indent;
72
+ this.output = this.mainBody;
73
+ this.indent = 1;
50
74
  this.visitStatement(stmt);
75
+ this.output = output;
76
+ this.indent = indent;
51
77
  }
52
78
  }
53
79
 
package/src/generator.js CHANGED
@@ -22,6 +22,10 @@ export class Generator {
22
22
  }
23
23
 
24
24
  generate(ast) {
25
+ for (const node of ast.body) {
26
+ if (node.type === 'SchemaDecl') this.schemas.set(node.name, node);
27
+ }
28
+
25
29
  this.visitProgram(ast);
26
30
 
27
31
  const preamble = [];
@@ -185,7 +189,7 @@ export class Generator {
185
189
  }
186
190
 
187
191
  rewriteNaideImport(source) {
188
- return source.replace(/\.(naide|nx)(['"])/g, '.mjs$2');
192
+ return source.replace(/\.naide(['"])/g, '.mjs$1');
189
193
  }
190
194
 
191
195
  visitFunction(node) {
package/src/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Lexer } from './lexer.js';
2
2
  import { Parser } from './parser.js';
3
3
  import { Generator } from './generator.js';
4
- import { preprocess } from './preprocess.js';
5
4
  import { TypeChecker } from './typechecker.js';
6
5
  import { generate, generateProject, updateProject, expandDirectives } from './gen.js';
7
6
 
@@ -76,11 +75,35 @@ async function getGenerator(target, options) {
76
75
  }
77
76
  }
78
77
 
79
- export function compile(source, { mode = 'naide', runtimePath, sourceFile, typeCheck = false, target = 'node' } = {}) {
80
- let processedSource = source;
81
- if (mode === 'x') {
82
- processedSource = preprocess(source);
78
+ function resolveSchemaInheritance(ast) {
79
+ const schemas = new Map(ast.body.filter(node => node.type === 'SchemaDecl').map(node => [node.name, node]));
80
+ const resolved = new Map();
81
+ const resolving = new Set();
82
+
83
+ function resolveFields(schema) {
84
+ if (resolved.has(schema.name)) return resolved.get(schema.name);
85
+ if (resolving.has(schema.name)) {
86
+ throw new Error(`Circular schema inheritance involving '${schema.name}'`);
87
+ }
88
+
89
+ resolving.add(schema.name);
90
+ const parent = schema.parent ? schemas.get(schema.parent) : null;
91
+ const fields = new Map((parent ? resolveFields(parent) : []).map(field => [field.name, field]));
92
+ for (const field of schema.fields) fields.set(field.name, field);
93
+ const result = [...fields.values()];
94
+ resolving.delete(schema.name);
95
+ resolved.set(schema.name, result);
96
+ return result;
97
+ }
98
+
99
+ for (const schema of schemas.values()) {
100
+ schema.fields = resolveFields(schema);
83
101
  }
102
+ return ast;
103
+ }
104
+
105
+ export function compile(source, { runtimePath, sourceFile, typeCheck = false, target = 'node' } = {}) {
106
+ let processedSource = source;
84
107
  if (processedSource.includes('~~')) {
85
108
  processedSource = expandDirectives(processedSource).source;
86
109
  }
@@ -88,7 +111,7 @@ export function compile(source, { mode = 'naide', runtimePath, sourceFile, typeC
88
111
  const lexer = new Lexer(processedSource);
89
112
  const tokens = lexer.tokenize();
90
113
  const parser = new Parser(tokens);
91
- const ast = parser.parse();
114
+ const ast = resolveSchemaInheritance(parser.parse());
92
115
 
93
116
  let typeErrors = null;
94
117
  if (typeCheck) {
@@ -98,7 +121,7 @@ export function compile(source, { mode = 'naide', runtimePath, sourceFile, typeC
98
121
 
99
122
  const generator = new Generator({ runtimePath, sourceFile });
100
123
  const js = generator.generate(ast);
101
- return { js, ast, tokens, sourceMap: generator.sourceMap, naide: mode === 'x' ? processedSource : null, typeErrors };
124
+ return { js, ast, tokens, sourceMap: generator.sourceMap, typeErrors };
102
125
  } catch (e) {
103
126
  const lineMatch = e.message.match(/line (\d+)/);
104
127
  if (lineMatch) {
@@ -117,11 +140,8 @@ export function compile(source, { mode = 'naide', runtimePath, sourceFile, typeC
117
140
  }
118
141
  }
119
142
 
120
- export async function compileAsync(source, { mode = 'naide', runtimePath, sourceFile, typeCheck = false, target = 'node' } = {}) {
143
+ export async function compileAsync(source, { runtimePath, sourceFile, typeCheck = false, target = 'node' } = {}) {
121
144
  let processedSource = source;
122
- if (mode === 'x') {
123
- processedSource = preprocess(source);
124
- }
125
145
  if (processedSource.includes('~~')) {
126
146
  processedSource = expandDirectives(processedSource).source;
127
147
  }
@@ -129,7 +149,7 @@ export async function compileAsync(source, { mode = 'naide', runtimePath, source
129
149
  const lexer = new Lexer(processedSource);
130
150
  const tokens = lexer.tokenize();
131
151
  const parser = new Parser(tokens);
132
- const ast = parser.parse();
152
+ const ast = resolveSchemaInheritance(parser.parse());
133
153
 
134
154
  let typeErrors = null;
135
155
  if (typeCheck) {
@@ -139,7 +159,7 @@ export async function compileAsync(source, { mode = 'naide', runtimePath, source
139
159
 
140
160
  const generator = await getGenerator(target, { runtimePath, sourceFile });
141
161
  const code = generator.generate(ast);
142
- return { js: code, code, ast, tokens, sourceMap: generator.sourceMap, naide: mode === 'x' ? processedSource : null, typeErrors, target };
162
+ return { js: code, code, ast, tokens, sourceMap: generator.sourceMap, typeErrors, target };
143
163
  } catch (e) {
144
164
  const lineMatch = e.message.match(/line (\d+)/);
145
165
  if (lineMatch) {
@@ -162,4 +182,4 @@ export function transpile(source, opts) {
162
182
  return compile(source, opts).js;
163
183
  }
164
184
 
165
- export { preprocess, generate, generateProject, updateProject };
185
+ export { generate, generateProject, updateProject };
package/src/parser.js CHANGED
@@ -345,13 +345,13 @@ export class Parser {
345
345
 
346
346
  if (this.match(T.ASSIGN)) {
347
347
  const expr = this.parseExpression();
348
- return new ASTNode('Function', { name, params, returnType, body: [new ASTNode('Return', { value: expr })], isAsync, isPublic });
348
+ return new ASTNode('Function', { name, params, returnType, body: [new ASTNode('Return', { value: expr }, tok.line)], isAsync, isPublic }, tok.line);
349
349
  }
350
350
 
351
351
  this.expect(T.COLON);
352
352
  const body = this.parseBlock();
353
353
 
354
- return new ASTNode('Function', { name, params, returnType, body, isAsync, isPublic });
354
+ return new ASTNode('Function', { name, params, returnType, body, isAsync, isPublic }, tok.line);
355
355
  }
356
356
 
357
357
  parseFnParams() {
@@ -418,9 +418,9 @@ export class Parser {
418
418
  }
419
419
 
420
420
  parseReturn() {
421
- this.advance(); // ret
421
+ const returnToken = this.advance(); // ret
422
422
  if (this.at(T.NEWLINE) || this.at(T.EOF) || this.at(T.DEDENT)) {
423
- return new ASTNode('Return', { value: null });
423
+ return new ASTNode('Return', { value: null }, returnToken.line);
424
424
  }
425
425
  if (this.match(T.DOT)) {
426
426
  const method = this.expect(T.IDENT).value;
@@ -457,7 +457,7 @@ export class Parser {
457
457
  return new ASTNode('ReturnMethod', { method, value });
458
458
  }
459
459
  const value = this.parseExpression();
460
- return new ASTNode('Return', { value });
460
+ return new ASTNode('Return', { value }, returnToken.line);
461
461
  }
462
462
 
463
463
  expectIdentLike() {
@@ -467,7 +467,8 @@ export class Parser {
467
467
  }
468
468
 
469
469
  parseTypedVariable() {
470
- const varType = this.advance().value;
470
+ const typeToken = this.advance();
471
+ const varType = typeToken.value;
471
472
  if (this.at(T.LBRACE)) {
472
473
  return this.parseDestructure(varType, false);
473
474
  }
@@ -477,11 +478,11 @@ export class Parser {
477
478
  const name = this.expectIdentLike();
478
479
  this.expect(T.ASSIGN);
479
480
  const value = this.parseExpression();
480
- return new ASTNode('TypedVar', { varType, name, value, isMut: false, isPublic: false });
481
+ return new ASTNode('TypedVar', { varType, name, value, isMut: false, isPublic: false }, typeToken.line);
481
482
  }
482
483
 
483
484
  parseMutVariable() {
484
- this.advance(); // mut
485
+ const mutToken = this.advance(); // mut
485
486
  if (TYPE_TOKENS.has(this.peek().type)) {
486
487
  const node = this.parseTypedVariable();
487
488
  node.isMut = true;
@@ -493,7 +494,7 @@ export class Parser {
493
494
  const name = this.expectIdentLike();
494
495
  this.expect(T.ASSIGN);
495
496
  const value = this.parseExpression();
496
- return new ASTNode('TypedVar', { varType: null, name, value, isMut: true });
497
+ return new ASTNode('TypedVar', { varType: null, name, value, isMut: true }, mutToken.line);
497
498
  }
498
499
 
499
500
  parseDestructure(varType, isMut) {
@@ -865,8 +866,7 @@ export class Parser {
865
866
  const name = this.expect(T.IDENT).value;
866
867
 
867
868
  let parent = null;
868
- if (this.at(T.IDENT) && this.peek().value === 'extends') {
869
- this.advance();
869
+ if (this.match(T.EXTENDS)) {
870
870
  parent = this.expect(T.IDENT).value;
871
871
  }
872
872
 
@@ -1161,10 +1161,11 @@ export class Parser {
1161
1161
  const prop = this.expectPropertyName();
1162
1162
  expr = new ASTNode('OptionalAccess', { object: expr, property: prop });
1163
1163
  } else if (this.at(T.LPAREN)) {
1164
+ const callLine = this.peek().line;
1164
1165
  this.advance();
1165
1166
  const args = this.parseArgList();
1166
1167
  this.expect(T.RPAREN);
1167
- expr = new ASTNode('Call', { callee: expr, args });
1168
+ expr = new ASTNode('Call', { callee: expr, args }, callLine);
1168
1169
  } else if (this.at(T.LBRACKET)) {
1169
1170
  this.advance();
1170
1171
  const index = this.parseExpression();
@@ -1466,10 +1467,10 @@ export class Parser {
1466
1467
  }
1467
1468
  this.match(T.DEDENT);
1468
1469
 
1469
- if (parent || methods.length > 0 || init) {
1470
+ if (methods.length > 0 || init) {
1470
1471
  return new ASTNode('ClassDecl', { name, parent, fields, methods, init });
1471
1472
  }
1472
- return new ASTNode('SchemaDecl', { name, fields });
1473
+ return new ASTNode('SchemaDecl', { name, parent, fields });
1473
1474
  }
1474
1475
 
1475
1476
  parseSchemaField() {