starlight-cli 1.0.50 → 1.1.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/dist/index.js CHANGED
@@ -2187,6 +2187,13 @@ class Lexer {
2187
2187
  this.currentChar = input[0] || null;
2188
2188
  this.line = 1;
2189
2189
  this.column = 1;
2190
+ this.keywords = [
2191
+ 'let', 'sldeploy', 'if', 'else', 'while', 'for',
2192
+ 'break', 'continue', 'func', 'return',
2193
+ 'true', 'false', 'null',
2194
+ 'ask', 'define', 'import', 'from', 'as',
2195
+ 'async', 'await', 'new', 'in', 'do', 'track'
2196
+ ];
2190
2197
  }
2191
2198
 
2192
2199
 
@@ -2204,7 +2211,22 @@ class Lexer {
2204
2211
  peek() {
2205
2212
  return this.pos + 1 < this.input.length ? this.input[this.pos + 1] : null;
2206
2213
  }
2214
+ suggest(word, list) {
2215
+ let best = null;
2216
+ let bestScore = Infinity;
2207
2217
 
2218
+ for (const item of list) {
2219
+ const dist =
2220
+ Math.abs(item.length - word.length) +
2221
+ [...word].filter((c, i) => c !== item[i]).length;
2222
+
2223
+ if (dist < bestScore && dist <= 2) {
2224
+ bestScore = dist;
2225
+ best = item;
2226
+ }
2227
+ }
2228
+ return best;
2229
+ }
2208
2230
  error(msg) {
2209
2231
  throw new LexerError(
2210
2232
  msg,
@@ -2267,20 +2289,28 @@ class Lexer {
2267
2289
  this.advance();
2268
2290
  }
2269
2291
 
2270
- const keywords = [
2271
- 'let', 'sldeploy', 'if', 'else', 'while', 'for',
2272
- 'break', 'continue', 'func', 'return',
2273
- 'true', 'false', 'null',
2274
- 'ask', 'define', 'import', 'from', 'as',
2275
- 'async', 'await', 'new', 'in', 'do', 'track'
2276
- ];
2277
2292
 
2293
+ if (this.keywords.includes(result)) {
2294
+ return {
2295
+ type: result.toUpperCase(),
2296
+ value: result,
2297
+ line: startLine,
2298
+ column: startCol
2299
+ };
2300
+ }
2278
2301
 
2279
- if (keywords.includes(result)) {
2280
- return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
2281
- }
2302
+ const suggestion = this.suggest(result, this.keywords);
2303
+ if (suggestion) {
2304
+ this.error(`Unknown identifier "${result}". Did you mean "${suggestion}"?`);
2305
+ }
2306
+
2307
+ return {
2308
+ type: 'IDENTIFIER',
2309
+ value: result,
2310
+ line: startLine,
2311
+ column: startCol
2312
+ };
2282
2313
 
2283
- return { type: 'IDENTIFIER', value: result, line: startLine, column: startCol };
2284
2314
  }
2285
2315
 
2286
2316
  string() {
@@ -2373,8 +2403,7 @@ if (char === '-' && next === '>') {
2373
2403
  }
2374
2404
  }
2375
2405
 
2376
- module.exports = Lexer;
2377
-
2406
+ module.exports = Lexer;
2378
2407
 
2379
2408
  /***/ }),
2380
2409
 
@@ -2386,7 +2415,7 @@ class ParseError extends Error {
2386
2415
  const line = token?.line ?? '?';
2387
2416
  const column = token?.column ?? '?';
2388
2417
 
2389
- let output = `SyntaxError: ${message}\n`;
2418
+ let output = `${message}\n`;
2390
2419
 
2391
2420
  if (source && token?.line != null) {
2392
2421
  const lines = source.split('\n');
@@ -3319,7 +3348,7 @@ const Lexer = __nccwpck_require__(211);
3319
3348
  const Parser = __nccwpck_require__(222);
3320
3349
  const Evaluator = __nccwpck_require__(112);
3321
3350
 
3322
- const VERSION = '1.0.50';
3351
+ const VERSION = '1.1.0';
3323
3352
 
3324
3353
  const COLOR = {
3325
3354
  reset: '\x1b[0m',
@@ -3484,12 +3513,26 @@ function savePrompt(lines) {
3484
3513
  }
3485
3514
 
3486
3515
  async function runFile(filePath, isTemp = false, callback) {
3487
- const code = fs.readFileSync(filePath, 'utf8');
3516
+ let code;
3517
+ try {
3518
+ code = fs.readFileSync(filePath, 'utf8');
3519
+ } catch (e) {
3520
+ console.error(COLOR.white + `Failed to read file: ${e.message}` + COLOR.reset);
3521
+ return waitAndExit(1);
3522
+ }
3523
+
3524
+ let tokens, ast;
3525
+ try {
3526
+ const lexer = new Lexer(code);
3527
+ tokens = lexer.getTokens();
3528
+
3529
+ const parser = new Parser(tokens, code);
3530
+ ast = parser.parse(); // <-- parser errors caught here
3531
+ } catch (e) {
3532
+ console.error(COLOR.white + ` ${e.message}` + COLOR.reset);
3533
+ return waitAndExit(1); // stop execution without Node.js stack trace
3534
+ }
3488
3535
 
3489
- const lexer = new Lexer(code);
3490
- const tokens = lexer.getTokens();
3491
- const parser = new Parser(tokens, code);
3492
- const ast = parser.parse();
3493
3536
  const evaluator = new Evaluator(code);
3494
3537
 
3495
3538
  try {
@@ -3511,7 +3554,6 @@ async function runFile(filePath, isTemp = false, callback) {
3511
3554
  }
3512
3555
 
3513
3556
 
3514
-
3515
3557
  if (!args[0].startsWith('--')) {
3516
3558
  runFile(path.resolve(args[0]));
3517
3559
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.0.50",
3
+ "version": "1.1.0",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/lexer.js CHANGED
@@ -23,6 +23,13 @@ class Lexer {
23
23
  this.currentChar = input[0] || null;
24
24
  this.line = 1;
25
25
  this.column = 1;
26
+ this.keywords = [
27
+ 'let', 'sldeploy', 'if', 'else', 'while', 'for',
28
+ 'break', 'continue', 'func', 'return',
29
+ 'true', 'false', 'null',
30
+ 'ask', 'define', 'import', 'from', 'as',
31
+ 'async', 'await', 'new', 'in', 'do', 'track'
32
+ ];
26
33
  }
27
34
 
28
35
 
@@ -40,7 +47,22 @@ class Lexer {
40
47
  peek() {
41
48
  return this.pos + 1 < this.input.length ? this.input[this.pos + 1] : null;
42
49
  }
43
-
50
+ suggest(word, list) {
51
+ let best = null;
52
+ let bestScore = Infinity;
53
+
54
+ for (const item of list) {
55
+ const dist =
56
+ Math.abs(item.length - word.length) +
57
+ [...word].filter((c, i) => c !== item[i]).length;
58
+
59
+ if (dist < bestScore && dist <= 2) {
60
+ bestScore = dist;
61
+ best = item;
62
+ }
63
+ }
64
+ return best;
65
+ }
44
66
  error(msg) {
45
67
  throw new LexerError(
46
68
  msg,
@@ -103,20 +125,28 @@ class Lexer {
103
125
  this.advance();
104
126
  }
105
127
 
106
- const keywords = [
107
- 'let', 'sldeploy', 'if', 'else', 'while', 'for',
108
- 'break', 'continue', 'func', 'return',
109
- 'true', 'false', 'null',
110
- 'ask', 'define', 'import', 'from', 'as',
111
- 'async', 'await', 'new', 'in', 'do', 'track'
112
- ];
113
128
 
129
+ if (this.keywords.includes(result)) {
130
+ return {
131
+ type: result.toUpperCase(),
132
+ value: result,
133
+ line: startLine,
134
+ column: startCol
135
+ };
136
+ }
114
137
 
115
- if (keywords.includes(result)) {
116
- return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
117
- }
138
+ const suggestion = this.suggest(result, this.keywords);
139
+ if (suggestion) {
140
+ this.error(`Unknown identifier "${result}". Did you mean "${suggestion}"?`);
141
+ }
142
+
143
+ return {
144
+ type: 'IDENTIFIER',
145
+ value: result,
146
+ line: startLine,
147
+ column: startCol
148
+ };
118
149
 
119
- return { type: 'IDENTIFIER', value: result, line: startLine, column: startCol };
120
150
  }
121
151
 
122
152
  string() {
@@ -209,4 +239,4 @@ if (char === '-' && next === '>') {
209
239
  }
210
240
  }
211
241
 
212
- module.exports = Lexer;
242
+ module.exports = Lexer;
package/src/parser.js CHANGED
@@ -3,7 +3,7 @@ class ParseError extends Error {
3
3
  const line = token?.line ?? '?';
4
4
  const column = token?.column ?? '?';
5
5
 
6
- let output = `SyntaxError: ${message}\n`;
6
+ let output = `${message}\n`;
7
7
 
8
8
  if (source && token?.line != null) {
9
9
  const lines = source.split('\n');
package/src/starlight.js CHANGED
@@ -9,7 +9,7 @@ const Lexer = require('./lexer');
9
9
  const Parser = require('./parser');
10
10
  const Evaluator = require('./evaluator');
11
11
 
12
- const VERSION = '1.0.50';
12
+ const VERSION = '1.1.0';
13
13
 
14
14
  const COLOR = {
15
15
  reset: '\x1b[0m',
@@ -174,12 +174,26 @@ function savePrompt(lines) {
174
174
  }
175
175
 
176
176
  async function runFile(filePath, isTemp = false, callback) {
177
- const code = fs.readFileSync(filePath, 'utf8');
177
+ let code;
178
+ try {
179
+ code = fs.readFileSync(filePath, 'utf8');
180
+ } catch (e) {
181
+ console.error(COLOR.white + `Failed to read file: ${e.message}` + COLOR.reset);
182
+ return waitAndExit(1);
183
+ }
184
+
185
+ let tokens, ast;
186
+ try {
187
+ const lexer = new Lexer(code);
188
+ tokens = lexer.getTokens();
189
+
190
+ const parser = new Parser(tokens, code);
191
+ ast = parser.parse(); // <-- parser errors caught here
192
+ } catch (e) {
193
+ console.error(COLOR.white + ` ${e.message}` + COLOR.reset);
194
+ return waitAndExit(1); // stop execution without Node.js stack trace
195
+ }
178
196
 
179
- const lexer = new Lexer(code);
180
- const tokens = lexer.getTokens();
181
- const parser = new Parser(tokens, code);
182
- const ast = parser.parse();
183
197
  const evaluator = new Evaluator(code);
184
198
 
185
199
  try {
@@ -201,7 +215,6 @@ async function runFile(filePath, isTemp = false, callback) {
201
215
  }
202
216
 
203
217
 
204
-
205
218
  if (!args[0].startsWith('--')) {
206
219
  runFile(path.resolve(args[0]));
207
220
  }