naidejs 1.2.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naidejs",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "NAIDE - Node AI Development Environment. AI-specialized language with built-in server, auth, DB, WebSocket, and more — transpiles to Node.js. Standard mode (~40% fewer tokens) and X mode (~80% fewer tokens).",
5
5
  "main": "src/index.js",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -4,16 +4,34 @@ import { Generator } from './generator.js';
4
4
  import { preprocess } from './preprocess.js';
5
5
 
6
6
  export function compile(source, { mode = 'naide', runtimePath } = {}) {
7
+ let processedSource = source;
7
8
  if (mode === 'x') {
8
- source = preprocess(source);
9
+ processedSource = preprocess(source);
10
+ }
11
+ try {
12
+ const lexer = new Lexer(processedSource);
13
+ const tokens = lexer.tokenize();
14
+ const parser = new Parser(tokens);
15
+ const ast = parser.parse();
16
+ const generator = new Generator({ runtimePath });
17
+ const js = generator.generate(ast);
18
+ return { js, ast, tokens, naide: mode === 'x' ? processedSource : null };
19
+ } catch (e) {
20
+ const lineMatch = e.message.match(/line (\d+)/);
21
+ if (lineMatch) {
22
+ const lineNum = parseInt(lineMatch[1]);
23
+ const lines = processedSource.split('\n');
24
+ const start = Math.max(0, lineNum - 3);
25
+ const end = Math.min(lines.length, lineNum + 2);
26
+ const context = lines.slice(start, end).map((l, i) => {
27
+ const num = start + i + 1;
28
+ const marker = num === lineNum ? ' >> ' : ' ';
29
+ return `${marker}${num} | ${l}`;
30
+ }).join('\n');
31
+ e.message += `\n\n${context}\n`;
32
+ }
33
+ throw e;
9
34
  }
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({ runtimePath });
15
- const js = generator.generate(ast);
16
- return { js, ast, tokens, naide: mode === 'x' ? source : null };
17
35
  }
18
36
 
19
37
  export function transpile(source, opts) {
package/src/lexer.js CHANGED
@@ -11,7 +11,7 @@ class Token {
11
11
 
12
12
  export class Lexer {
13
13
  constructor(source) {
14
- this.source = source;
14
+ this.source = source.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
15
15
  this.pos = 0;
16
16
  this.line = 1;
17
17
  this.col = 1;
@@ -78,6 +78,11 @@ export class Lexer {
78
78
  continue;
79
79
  }
80
80
 
81
+ if (ch === ';') {
82
+ this.advance();
83
+ continue;
84
+ }
85
+
81
86
  if (ch === '"' || ch === "'") {
82
87
  this.readString(ch);
83
88
  continue;
@@ -195,7 +200,11 @@ export class Lexer {
195
200
  }
196
201
  }
197
202
 
198
- if (this.peek() === quote) this.advance(); // skip closing quote
203
+ if (this.peek() === quote) {
204
+ this.advance();
205
+ } else {
206
+ throw new Error(`[NAIDE Lexer Error] Unterminated string starting at line ${startLine}:${startCol}`);
207
+ }
199
208
 
200
209
  if (current) parts.push({ type: 'text', value: current });
201
210
 
@@ -221,7 +230,11 @@ export class Lexer {
221
230
  value += this.advance();
222
231
  }
223
232
  }
224
- if (this.peek() === '`') this.advance();
233
+ if (this.peek() === '`') {
234
+ this.advance();
235
+ } else {
236
+ throw new Error(`[NAIDE Lexer Error] Unterminated template string starting at line ${startLine}:${startCol}`);
237
+ }
225
238
  this.tokens.push(new Token(T.STRING, { parts: [{ type: 'text', value }], raw: value }, startLine, startCol));
226
239
  }
227
240
 
@@ -371,7 +384,7 @@ export class Lexer {
371
384
  case ':': this.tokens.push(new Token(T.COLON, ':', startLine, startCol)); break;
372
385
  case ',': this.tokens.push(new Token(T.COMMA, ',', startLine, startCol)); break;
373
386
  default:
374
- throw new Error(`Unexpected character '${ch}' at line ${startLine}:${startCol}`);
387
+ throw new Error(`[NAIDE Lexer Error] Unexpected character '${ch}' at line ${startLine}:${startCol}`);
375
388
  }
376
389
  }
377
390
 
package/src/parser.js CHANGED
@@ -61,7 +61,16 @@ export class Parser {
61
61
  expect(type) {
62
62
  const tok = this.advance();
63
63
  if (tok.type !== type) {
64
- throw this.error(`Expected ${type} but got ${tok.type} ('${tok.value}')`, tok);
64
+ const hints = {
65
+ COLON: "Missing ':' — blocks (if, fn, server, etc.) need a colon at the end",
66
+ INDENT: "Expected an indented block — check your indentation (use 2 spaces)",
67
+ RPAREN: "Missing closing ')'",
68
+ RBRACKET: "Missing closing ']'",
69
+ RBRACE: "Missing closing '}'",
70
+ IDENT: "Expected an identifier (variable/function name)",
71
+ };
72
+ const hint = hints[type] ? `\n Hint: ${hints[type]}` : '';
73
+ throw this.error(`Expected ${type} but got ${tok.type} ('${tok.value}')${hint}`, tok);
65
74
  }
66
75
  return tok;
67
76
  }
@@ -81,18 +90,24 @@ export class Parser {
81
90
  return types.includes(this.peek().type);
82
91
  }
83
92
 
93
+ isIdentLike(type) {
94
+ return type === T.IDENT || TYPE_TOKENS.has(type) ||
95
+ type === T.LOG || type === T.DB || type === T.GET ||
96
+ type === T.POST || type === T.PUT || type === T.DEL ||
97
+ type === T.MATCH || type === T.ON || type === T.NEW ||
98
+ type === T.FROM || type === T.AS || type === T.SELF ||
99
+ type === T.SCHEMA || type === T.CRUD || type === T.AUTH ||
100
+ type === T.CORS || type === T.LIMIT || type === T.ENV ||
101
+ type === T.EVERY || type === T.WATCH || type === T.STATIC ||
102
+ type === T.WS || type === T.GROUP || type === T.COOKIE ||
103
+ type === T.NOT || type === T.AND || type === T.OR ||
104
+ type === T.IN || type === T.BREAK || type === T.CONTINUE ||
105
+ type === T.THROW || type === T.MUT || type === T.PUB;
106
+ }
107
+
84
108
  expectPropertyName() {
85
109
  const tok = this.advance();
86
- if (tok.type === T.IDENT || TYPE_TOKENS.has(tok.type) ||
87
- tok.type === T.LOG || tok.type === T.DB || tok.type === T.GET ||
88
- tok.type === T.POST || tok.type === T.PUT || tok.type === T.DEL ||
89
- tok.type === T.MATCH || tok.type === T.ON || tok.type === T.NEW ||
90
- tok.type === T.FROM || tok.type === T.AS || tok.type === T.SELF ||
91
- tok.type === T.SCHEMA || tok.type === T.CRUD || tok.type === T.AUTH ||
92
- tok.type === T.CORS || tok.type === T.LIMIT || tok.type === T.ENV ||
93
- tok.type === T.EVERY || tok.type === T.WATCH ||
94
- tok.type === T.STATIC || tok.type === T.WS ||
95
- tok.type === T.GROUP || tok.type === T.COOKIE) {
110
+ if (this.isIdentLike(tok.type)) {
96
111
  return tok.value;
97
112
  }
98
113
  throw this.error(`Expected property name but got ${tok.type} ('${tok.value}')`, tok);
@@ -726,6 +741,18 @@ export class Parser {
726
741
  }
727
742
 
728
743
  parseTernary() {
744
+ if (this.at(T.IF)) {
745
+ const savedPos = this.pos;
746
+ this.advance();
747
+ const condition = this.parseNullish();
748
+ if (this.match(T.THEN)) {
749
+ const consequent = this.parseNullish();
750
+ this.expect(T.ELSE);
751
+ const alternate = this.parseNullish();
752
+ return new ASTNode('Ternary', { condition, consequent, alternate });
753
+ }
754
+ this.pos = savedPos;
755
+ }
729
756
  let expr = this.parseNullish();
730
757
  if (this.at(T.IF)) {
731
758
  const savedPos = this.pos;
@@ -906,6 +933,7 @@ export class Parser {
906
933
  case T.SCHEMA: case T.CRUD: case T.AUTH: case T.CORS:
907
934
  case T.LIMIT: case T.ENV: case T.EVERY: case T.WATCH:
908
935
  case T.STATIC: case T.WS: case T.GROUP: case T.COOKIE:
936
+ case T.FROM: case T.AS: case T.IN:
909
937
  this.advance();
910
938
  return new ASTNode('Identifier', { name: tok.value });
911
939