naider 1.24.5 → 2.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.
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() {
@@ -4,6 +4,7 @@ export class TypeChecker {
4
4
  this.errors = [];
5
5
  this.warnings = [];
6
6
  this.functions = new Map();
7
+ this.returnTypes = [];
7
8
  }
8
9
 
9
10
  pushScope() { this.scopes.push(new Map()); }
@@ -29,10 +30,21 @@ export class TypeChecker {
29
30
  }
30
31
 
31
32
  check(ast) {
33
+ for (const stmt of ast.body) {
34
+ if (stmt.type === 'Function') this.registerFunction(stmt);
35
+ }
32
36
  this.visitProgram(ast);
33
37
  return { errors: this.errors, warnings: this.warnings };
34
38
  }
35
39
 
40
+ registerFunction(node) {
41
+ this.functions.set(node.name, {
42
+ params: node.params.map(p => ({ name: p.name, type: p.type || 'any', hasDefault: p.defaultValue !== null, spread: p.spread })),
43
+ returnType: node.returnType || 'any',
44
+ isAsync: node.isAsync || false,
45
+ });
46
+ }
47
+
36
48
  visitProgram(node) {
37
49
  for (const stmt of node.body) {
38
50
  this.visitStatement(stmt);
@@ -80,26 +92,19 @@ export class TypeChecker {
80
92
  }
81
93
 
82
94
  visitFunction(node, line) {
83
- const paramTypes = {};
84
- for (const p of node.params) {
85
- paramTypes[p.name] = p.varType || 'any';
86
- }
87
-
88
- this.functions.set(node.name, {
89
- params: node.params.map(p => ({ name: p.name, type: p.varType || 'any' })),
90
- returnType: node.returnType || 'any',
91
- isAsync: node.isAsync || false,
92
- });
95
+ this.registerFunction(node);
93
96
 
94
97
  this.setType(node.name, 'function');
95
98
 
96
99
  this.pushScope();
97
100
  for (const p of node.params) {
98
- this.setType(p.name, p.varType || 'any');
101
+ this.setType(p.name, p.type || 'any');
99
102
  }
103
+ this.returnTypes.push(node.returnType || 'any');
100
104
  for (const stmt of node.body) {
101
105
  this.visitStatement(stmt);
102
106
  }
107
+ this.returnTypes.pop();
103
108
  this.popScope();
104
109
  }
105
110
 
@@ -118,7 +123,22 @@ export class TypeChecker {
118
123
  }
119
124
  }
120
125
 
121
- visitReturn(node, line) {}
126
+ visitReturn(node, line) {
127
+ const expectedType = this.returnTypes[this.returnTypes.length - 1];
128
+ if (!expectedType || expectedType === 'any') return;
129
+
130
+ if (!node.value) {
131
+ if (expectedType !== 'void') {
132
+ this.error(`Return type mismatch: expected ${expectedType} but got void`, line);
133
+ }
134
+ return;
135
+ }
136
+
137
+ const actualType = this.inferType(node.value);
138
+ if (actualType !== 'any' && !this.typesCompatible(expectedType, actualType)) {
139
+ this.error(`Return type mismatch: expected ${expectedType} but got ${actualType}`, line);
140
+ }
141
+ }
122
142
 
123
143
  visitIf(node, line) {
124
144
  this.visitBlock(node.body, line);
@@ -220,7 +240,10 @@ export class TypeChecker {
220
240
  inferCallType(node) {
221
241
  if (node.callee.type === 'Identifier') {
222
242
  const fn = this.functions.get(node.callee.name);
223
- if (fn) return fn.returnType;
243
+ if (fn) {
244
+ this.checkCallArguments(node, fn);
245
+ return fn.returnType;
246
+ }
224
247
 
225
248
  const builtins = {
226
249
  'uuid': 'str', 'hash': 'str', 'verify': 'bool',
@@ -251,6 +274,30 @@ export class TypeChecker {
251
274
  return 'any';
252
275
  }
253
276
 
277
+ checkCallArguments(node, fn) {
278
+ const params = fn.params;
279
+ const line = node._line || 0;
280
+ const requiredCount = params.filter(p => !p.hasDefault && !p.spread).length;
281
+ const hasSpread = params.some(p => p.spread);
282
+
283
+ if (node.args.length < requiredCount || (!hasSpread && node.args.length > params.length)) {
284
+ const expected = hasSpread ? `at least ${requiredCount}` : `${requiredCount}${params.length === requiredCount ? '' : ` to ${params.length}`}`;
285
+ this.error(`Argument count mismatch for '${node.callee.name}': expected ${expected}, got ${node.args.length}`, line);
286
+ }
287
+
288
+ const spreadIndex = params.findIndex(param => param.spread);
289
+ for (let i = 0; i < node.args.length; i++) {
290
+ const paramIndex = spreadIndex >= 0 && i >= spreadIndex ? spreadIndex : i;
291
+ const param = params[paramIndex];
292
+ if (!param) continue;
293
+ const expectedType = param.type;
294
+ const actualType = this.inferType(node.args[i]);
295
+ if (expectedType !== 'any' && actualType !== 'any' && !this.typesCompatible(expectedType, actualType)) {
296
+ this.error(`Argument '${param.name}' of '${node.callee.name}' expects ${expectedType} but got ${actualType}`, line);
297
+ }
298
+ }
299
+ }
300
+
254
301
  typesCompatible(declared, actual) {
255
302
  if (declared === actual) return true;
256
303
  if (declared === 'any' || actual === 'any') return true;
@@ -1,22 +1,20 @@
1
1
  # NAIDE Language Support for VS Code
2
2
 
3
- Syntax highlighting and language support for **NAIDE** (`.naide`) and **NAIDE-X** (`.nx`) files.
3
+ Syntax highlighting and language support for **NAIDE** (`.naide`) files.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - Syntax highlighting for all NAIDE keywords, types, and operators
8
- - Syntax highlighting for NAIDE-X shorthand prefixes (`>`, `?`, `!`, `$`, `^`, `@`, etc.)
9
8
  - String interpolation highlighting (`"Hello {name}"`)
10
9
  - Auto-indentation for block structures
11
10
  - Bracket matching and auto-closing
12
- - Comment toggling (`#` for .naide, `--` for .nx)
11
+ - Comment toggling (`#`)
13
12
 
14
13
  ## File Associations
15
14
 
16
15
  | Extension | Language |
17
16
  |-----------|----------|
18
17
  | `.naide` | NAIDE |
19
- | `.nx` | NAIDE-X |
20
18
 
21
19
  ## Install
22
20
 
@@ -26,7 +24,7 @@ Syntax highlighting and language support for **NAIDE** (`.naide`) and **NAIDE-X*
26
24
  cd vscode-naide
27
25
  npm install -g @vscode/vsce
28
26
  vsce package
29
- code --install-extension naide-lang-1.0.0.vsix
27
+ code --install-extension naide-lang-2.0.0.vsix
30
28
  ```
31
29
 
32
30
  ### Manual
@@ -35,4 +33,4 @@ Copy `vscode-naide/` to `~/.vscode/extensions/naide-lang/` and restart VS Code.
35
33
 
36
34
  ## About NAIDE
37
35
 
38
- NAIDE is an AI-optimized programming language that transpiles to Node.js. See [github.com/irxk/naide](https://github.com/irxk/naide).
36
+ NAIDE is a programming language with one readable syntax, built-in diagnostics, and compiler targets. See [github.com/irxk/naide](https://github.com/irxk/naide).
@@ -18,10 +18,9 @@ function activate(context) {
18
18
  const clientOptions = {
19
19
  documentSelector: [
20
20
  { scheme: 'file', language: 'naide' },
21
- { scheme: 'file', language: 'naidex' },
22
21
  ],
23
22
  synchronize: {
24
- fileEvents: vscode.workspace.createFileSystemWatcher('**/*.{naide,nx}'),
23
+ fileEvents: vscode.workspace.createFileSystemWatcher('**/*.naide'),
25
24
  },
26
25
  };
27
26
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "naide-lang",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "naide-lang",
9
- "version": "1.1.0",
9
+ "version": "2.0.0",
10
10
  "dependencies": {
11
11
  "vscode-languageclient": "^9.0.0"
12
12
  },
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "naide-lang",
3
3
  "displayName": "NAIDE Language",
4
- "description": "Syntax highlighting, diagnostics, and autocomplete for NAIDE (.naide) and NAIDE-X (.nx)",
5
- "version": "1.1.0",
4
+ "description": "Syntax highlighting, diagnostics, and autocomplete for NAIDE (.naide)",
5
+ "version": "2.0.0",
6
6
  "publisher": "irxk",
7
7
  "engines": { "vscode": "^1.75.0" },
8
8
  "categories": ["Programming Languages"],
9
9
  "main": "./extension.js",
10
10
  "activationEvents": [
11
- "onLanguage:naide",
12
- "onLanguage:naidex"
11
+ "onLanguage:naide"
13
12
  ],
14
13
  "repository": {
15
14
  "type": "git",
@@ -22,12 +21,6 @@
22
21
  "aliases": ["NAIDE", "naide"],
23
22
  "extensions": [".naide"],
24
23
  "configuration": "./language-configuration-naide.json"
25
- },
26
- {
27
- "id": "naidex",
28
- "aliases": ["NAIDE-X", "naidex", "nx"],
29
- "extensions": [".nx"],
30
- "configuration": "./language-configuration-nx.json"
31
24
  }
32
25
  ],
33
26
  "grammars": [
@@ -35,11 +28,6 @@
35
28
  "language": "naide",
36
29
  "scopeName": "source.naide",
37
30
  "path": "./syntaxes/naide.tmLanguage.json"
38
- },
39
- {
40
- "language": "naidex",
41
- "scopeName": "source.naidex",
42
- "path": "./syntaxes/naidex.tmLanguage.json"
43
31
  }
44
32
  ],
45
33
  "configuration": {