starlight-cli 1.1.12 → 1.1.13

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/README.md CHANGED
@@ -4,417 +4,122 @@
4
4
  Starlight is a lightweight, developer-oriented programming language designed for **server-side scripting, automation, and general-purpose programming**. It combines a clean, readable syntax inspired by JavaScript and Python with powerful runtime features such as async/await, modules, and interactive I/O.
5
5
 
6
6
  **Official Reference:**
7
- https://dominexmacedon.github.io/starlight-programming-language/learn.html
8
-
9
- ---
10
-
11
- ## Key Features
12
-
13
- - **Modern, Simple Syntax** – Familiar to JavaScript and Python developers
14
- - **Async / Await** – Native support for asynchronous operations
15
- - **Modules & Imports** – Import JavaScript packages or other `.sl` files
16
- - **Server-side Ready** – Built on Node.js, ideal for backend logic
17
- - **Interactive I/O** – Built-in `ask` and `sldeploy`
18
- - **Built-in Utilities** – `sleep`, `len`, `keys`, `values`, `fetch`, `get`, `post`
19
-
20
- ---
21
-
22
- ## Installation
23
-
24
- 1. Install **Node.js** (v18+ recommended)
25
- 2. Install the Starlight CLI:
26
-
27
- ```bash
28
- npm install -g starlight-cli
29
- ````
30
-
31
- ---
32
-
33
- ## Your First Program (`hello.sl`)
34
-
35
- ```sl
36
- let name = ask("What is your name?")
37
- sldeploy "Hello, " + name + "!"
38
- ```
39
-
40
- Run:
41
-
42
- ```bash
43
- starlight hello.sl
44
- ```
45
-
46
- ### Output
47
-
48
- ```
49
- What is your name? Alice
50
- Hello, Alice!
51
- ```
52
-
53
- ---
54
-
55
- ## Core Language Concepts
56
-
57
- ### Variables
58
-
59
- ```sl
60
- let x = 10
61
- let text = "hello"
62
- ```
63
-
64
- ---
65
-
66
- ### Functions
67
-
68
- ```sl
69
- func add(a, b) {
70
- return a + b
71
- }
72
-
73
- sldeploy add(2, 3)
74
- ```
75
-
76
- ---
77
-
78
- ### Async Functions
79
-
80
- ```sl
81
- async func load() {
82
- let data = await get("https://example.com/api")
83
- sldeploy data
84
- }
85
-
86
- load()
87
- ```
88
-
7
+ https://starlight-programming-language.pages.dev/tutorials
89
8
  ---
9
+ ## Error Handling in Starlight
90
10
 
91
- ## Arrow Functions
11
+ Starlight is designed with **modern, developer-friendly error handling** that clearly explains what went wrong, where it happened, and how to fix it.
92
12
 
93
- Arrow functions provide a **concise and expressive** way to define functions.
13
+ ### Modern Diagnostic Style
94
14
 
95
- ### Basic Arrow Function
15
+ Starlight errors follow the same diagnostic standards used by modern languages such as JavaScript, Rust, and Python:
96
16
 
97
- ```sl
98
- let square = x => x * x
99
- sldeploy square(5)
100
- ```
17
+ - Clear error message
18
+ - Exact **line and column number**
19
+ - Source code preview
20
+ - Visual caret (`^`) pointing to the error location
21
+ - Helpful suggestions when possible
101
22
 
102
- ### Multiple Parameters
23
+ Example:
103
24
 
104
- ```sl
105
- let add = (a, b) => a + b
106
- sldeploy add(10, 20)
107
25
  ```
108
26
 
109
- ### Block Body with if / else
27
+ Unterminated block
28
+ Did you forget to close the block with '}'?
29
+ at line 5, column 0
110
30
 
111
- ```sl
112
- let label = p => {
113
- if (p.price > 1000) {
114
- return "Premium"
115
- } else {
116
- return "Standard"
117
- }
118
- }
119
31
  ```
120
-
121
- ### Arrow Functions with Objects
122
-
123
- ```sl
124
- let summarize = p => {
125
- return {
126
- "name": p.name,
127
- "value": p.price * p.stock
128
- }
129
- }
32
+ ^
130
33
  ```
131
34
 
132
- Arrow functions capture their surrounding scope and behave like standard functions while remaining lightweight and readable.
133
-
134
- ---
135
-
136
- ## Conditionals
137
-
138
- ```sl
139
- if (x > 5) {
140
- sldeploy "Greater than 5"
141
- } else {
142
- sldeploy "5 or less"
143
- }
144
- ```
145
-
146
- ---
147
-
148
- ## Loops
149
-
150
- ### While Loop
35
+ ````
151
36
 
152
- ```sl
153
- let i = 0
154
- while (i < 3) {
155
- sldeploy i
156
- i = i + 1
157
- }
158
- ```
37
+ This format makes debugging fast and intuitive, even for beginners.
159
38
 
160
39
  ---
161
40
 
162
- ## Start & Race Statements
163
-
164
- Starlight includes a **start / race** control structure similar to a switch statement, with **fall-through behavior**.
41
+ ### 🧠 Syntax Errors (Parser Errors)
165
42
 
166
- ### Syntax
167
-
168
- ```sl
169
- start (value) {
170
- race (1) {
171
- # body
172
- }
173
-
174
- race (2) {
175
- # body
176
- }
177
- }
178
- ```
43
+ Syntax errors are detected during parsing and reported before execution begins.
179
44
 
180
- ### Example
45
+ Features:
46
+ - Precise location tracking
47
+ - Friendly suggestions
48
+ - No Node.js stack traces
49
+ - Clean termination
181
50
 
51
+ Example:
182
52
  ```sl
183
- define x = 2
184
-
185
- start (x) {
186
- race (1) { sldeploy("Race 1 executed") }
187
- race (2) { sldeploy("Race 2 executed") }
188
- race (3) { sldeploy("Race 3 executed") }
189
- }
190
-
191
- sldeploy("Done with start statement")
192
- ```
193
-
194
- ### Expected Output
195
-
196
- ```
197
- Race 2 executed
198
- Race 3 executed
199
- Done with start statement
200
- ```
201
-
202
- ---
203
-
204
- ## Markdown Preview (`starlight filename.md`)
205
-
206
- Starlight can also be used to **preview Markdown files** directly from the CLI.
207
-
208
- ### Usage
209
-
210
- ```bash
211
- starlight README.md
212
- ```
213
-
214
- ### How It Works (Execution Flow)
215
-
216
- 1. The CLI detects the `.md` file extension
217
- 2. The Markdown file is read from disk
218
- 3. Markdown is converted to HTML using a full-featured Markdown renderer
219
- 4. A **temporary HTML file** is created in the system temp directory
220
- 5. The file is opened in your default browser using `file://`
221
- 6. After the browser loads, the temporary file is **automatically deleted**
222
-
223
- This makes Starlight useful not only as a programming language, but also as a **developer productivity tool**.
224
-
225
- ---
226
-
227
- ## Array Helpers: map, filter, reduce
228
-
229
- Starlight provides built-in **functional helpers** for working with arrays: `map`, `filter`, and `reduce`.
230
- These helpers are **async-aware**, work seamlessly with **arrow functions**, and integrate fully with Starlight’s runtime and error handling.
231
-
232
- Unlike JavaScript, these are **global functions** (not methods), keeping the language simple and consistent.
233
-
234
- ---
235
-
236
- ### map(array, fn)
237
-
238
- Transforms each element of an array using a callback function and returns a **new array**.
239
-
240
- **Syntax:**
241
- ```
242
-
243
- map(array, fn)
244
-
245
- ```
246
-
247
- **Example:**
248
- ```
249
-
250
- let numbers = [1, 2, 3, 4]
251
-
252
- let squared = await map(numbers, x => x * x)
253
-
254
- sldeploy squared
255
-
256
- ```
257
-
258
- **Output:**
259
- ```
260
-
261
- [ 1, 4, 9, 16 ]
262
-
263
- ```
264
-
265
- - `fn` receives `(value, index, array)`
266
- - Does **not** modify the original array
267
- - Supports `async` arrow functions
268
-
269
- ---
270
-
271
- ### filter(array, fn)
272
-
273
- Selects elements from an array that satisfy a condition and returns a **new array**.
274
-
275
- **Syntax:**
276
- ```
277
-
278
- filter(array, fn)
279
-
280
- ```
281
-
282
- **Example:**
283
- ```
284
-
285
- let numbers = [1, 2, 3, 4, 5, 6]
286
-
287
- let evens = await filter(numbers, x => x % 2 == 0)
288
-
289
- sldeploy evens
53
+ if true {
54
+ sldeploy("This block is never closed")
55
+ ````
290
56
 
291
- ```
57
+ Output:
292
58
 
293
- **Output:**
294
59
  ```
60
+ Unterminated block
61
+ Did you forget to close the block with '}'?
62
+ at line 2, column 0
295
63
 
296
- [ 2, 4, 6 ]
297
-
64
+ ^
298
65
  ```
299
66
 
300
- - `fn` must return `true` or `false`
301
- - `fn` receives `(value, index, array)`
302
- - Original array remains unchanged
303
-
304
67
  ---
305
68
 
306
- ### reduce(array, fn, initial)
69
+ ### ⚙️ Runtime Errors (Evaluator Errors)
307
70
 
308
- Reduces an array to a **single value** by accumulating results using a reducer function.
71
+ Runtime errors occur during execution and include:
309
72
 
310
- **Syntax:**
311
- ```
73
+ * Undefined variables
74
+ * Invalid operations
75
+ * Logical mistakes
312
76
 
313
- reduce(array, fn, initial)
77
+ Starlight also provides **intelligent name suggestions**:
314
78
 
315
79
  ```
316
-
317
- **Example:**
80
+ Undefined variable: "scroe"
81
+ Did you mean "score"?
82
+ at line 10, column 5
83
+ ^
318
84
  ```
319
85
 
320
- let numbers = [1, 2, 3, 4, 5]
321
-
322
- let total = await reduce(numbers, (acc, value) => acc + value, 0)
323
-
324
- sldeploy total
325
-
326
- ```
327
-
328
- **Output:**
329
- ```
330
-
331
- 15
332
-
333
- ```
334
-
335
- - `fn` receives `(accumulator, value, index, array)`
336
- - `initial` is optional, but recommended
337
- - Throws a runtime error if used on an empty array without an initial value
338
-
339
86
  ---
340
87
 
341
- ### Async Support
342
-
343
- All three helpers support `async` functions:
344
-
345
- ```
346
-
347
- let results = await map(items, async item => {
348
- await sleep(100)
349
- return item * 2
350
- })
351
-
352
- ```
353
-
354
- ---
88
+ ### 🎨 Color-Coded Errors
355
89
 
356
- ### Why global helpers?
90
+ Errors are color-coded for clarity:
357
91
 
358
- Starlight keeps `map`, `filter`, and `reduce` as **language-level utilities** instead of object methods to:
92
+ * **Red** Fatal syntax or runtime errors
93
+ * **Yellow** – Warnings or suggestions
94
+ * **White** – Neutral information (source preview, pointers)
359
95
 
360
- - Keep syntax minimal and readable
361
- - Avoid prototype complexity
362
- - Work consistently with all iterables
363
- - Integrate cleanly with the evaluator and runtime
96
+ This ensures errors are readable in all terminals.
364
97
 
365
98
  ---
366
99
 
367
- ### Nullish Coalescing (`??`) Support
100
+ ### Safe Execution Model
368
101
 
369
- Our language now supports the **nullish coalescing operator** `??`, which allows you to provide a default value for `null` or `undefined` expressions.
102
+ * Execution **stops immediately** on error
103
+ * No uncaught exceptions
104
+ * No JavaScript stack traces leaked
105
+ * Clean exit behavior
370
106
 
371
- **Syntax:**
372
-
373
- ```sl
374
- value = expression1 ?? expression2
375
- ```
376
-
377
- * `expression1` is evaluated first.
378
- * If it is **not null or undefined**, its value is used.
379
- * Otherwise, the value of `expression2` is used.
380
-
381
- **Example:**
382
-
383
- ```sl
384
- define a = null
385
- define b = a ?? 42
386
- sldeploy b # Output: 42
387
- ```
388
-
389
- **Notes:**
390
-
391
- * Only `null` and `undefined` are treated as "empty" values. Other falsy values like `0`, `false`, or `""` will **not** trigger the default.
392
- * This operator is optional. If you prefer, you can achieve the same result using a simple `if` statement:
393
-
394
- ```sl
395
- define a = null
396
- define b = if a != null then a else 42
397
- sldeploy b # Output: 42
398
- ```
399
-
400
- This operator simplifies code that needs default values and makes your scripts cleaner and more readable.
107
+ This keeps Starlight predictable and safe for learning and production use.
401
108
 
402
109
  ---
403
110
 
404
- ## Why Starlight?
111
+ ### Summary
405
112
 
406
- * A **simple but powerful** scripting language
407
- * **Fast server-side automation**
408
- * Familiar syntax with **less boilerplate**
409
- * Full access to the **Node.js ecosystem**
113
+ Starlight’s error handling is:
410
114
 
411
- ---
115
+ * Modern
116
+ * Precise
117
+ * Beginner-friendly
118
+ * Professional-grade
412
119
 
413
- ## License
120
+ By using caret-based diagnostics and intelligent suggestions, Starlight provides a first-class developer experience from day one.
414
121
 
415
- MIT License
416
122
 
417
- ---
418
123
 
419
124
  ## Author and Developer
420
125
  Dominex Macedon
package/dist/index.js CHANGED
@@ -10219,65 +10219,76 @@ class ReturnValue {
10219
10219
  }
10220
10220
  class BreakSignal {}
10221
10221
  class ContinueSignal {}
10222
+ const COLOR = {
10223
+ reset: '\x1b[0m',
10224
+ red: '\x1b[31m',
10225
+ yellow: '\x1b[33m',
10226
+ white: '\x1b[37m'
10227
+ };
10228
+
10222
10229
  class RuntimeError extends Error {
10223
- constructor(message, node, source, env = null) {
10224
- const line = node?.line ?? '?';
10225
- const column = node?.column ?? '?';
10226
- let output = ` ${message} at line ${line}, column ${column}\n`;
10227
-
10228
- if (source && node?.line != null) {
10229
- const lines = source.split('\n');
10230
- const srcLine = lines[node.line - 1] || '';
10231
- output += ` ${srcLine}\n`;
10232
- const caretPos =
10233
- typeof column === 'number' && column > 0
10234
- ? column - 1
10235
- : 0;
10236
- output += ` ${' '.repeat(caretPos)}^\n`;
10237
- }
10230
+ constructor(message, node, source, env = null) {
10231
+ const line = node?.line ?? '?';
10232
+ const column = node?.column ?? '?';
10238
10233
 
10239
- if (env && message.startsWith('Undefined variable:')) {
10240
- const nameMatch = message.match(/"(.+?)"/);
10241
- if (nameMatch) {
10242
- const name = nameMatch[1];
10243
- const suggestion = RuntimeError.suggest(name, env);
10244
- if (suggestion) {
10245
- output += `Did you mean "${suggestion}"?\n`;
10246
- }
10247
- }
10248
- }
10234
+ let output =
10235
+ `${COLOR.red}${message}${COLOR.reset} at line ${line}, column ${column}\n`;
10249
10236
 
10250
- super(output);
10251
- this.name = 'RuntimeError';
10252
- this.line = line;
10253
- this.column = column;
10237
+ if (source && node?.line != null) {
10238
+ const lines = source.split('\n');
10239
+ const srcLine = lines[node.line - 1] || '';
10240
+ const caretPos =
10241
+ typeof column === 'number' && column > 0 ? column - 1 : 0;
10242
+
10243
+ output += `${COLOR.white} ${srcLine}\n`;
10244
+ output += ` ${' '.repeat(caretPos)}^\n${COLOR.reset}`;
10245
+ }
10246
+
10247
+ if (env && message.startsWith('Undefined variable:')) {
10248
+ const nameMatch = message.match(/"(.+?)"/);
10249
+ if (nameMatch) {
10250
+ const name = nameMatch[1];
10251
+ const suggestion = RuntimeError.suggest(name, env);
10252
+ if (suggestion) {
10253
+ output +=
10254
+ `${COLOR.yellow}Did you mean "${suggestion}"?${COLOR.reset}\n`;
10255
+ }
10256
+ }
10254
10257
  }
10255
10258
 
10256
- static suggest(name, env) {
10257
- const names = new Set();
10258
- let current = env;
10259
- while (current) {
10260
- for (const key of Object.keys(current.store)) {
10261
- names.add(key);
10262
- }
10263
- current = current.parent;
10264
- }
10259
+ super(output);
10260
+ this.name = 'RuntimeError';
10261
+ this.line = line;
10262
+ this.column = column;
10263
+ }
10265
10264
 
10266
- let best = null;
10267
- let bestScore = Infinity;
10265
+ static suggest(name, env) {
10266
+ const names = new Set();
10267
+ let current = env;
10268
10268
 
10269
- for (const item of names) {
10270
- const dist = Math.abs(item.length - name.length) +
10271
- [...name].filter((c, i) => c !== item[i]).length;
10269
+ while (current) {
10270
+ for (const key of Object.keys(current.store)) {
10271
+ names.add(key);
10272
+ }
10273
+ current = current.parent;
10274
+ }
10272
10275
 
10273
- if (dist < bestScore && dist <= 2) {
10274
- bestScore = dist;
10275
- best = item;
10276
- }
10277
- }
10276
+ let best = null;
10277
+ let bestScore = Infinity;
10278
10278
 
10279
- return best;
10279
+ for (const item of names) {
10280
+ const dist =
10281
+ Math.abs(item.length - name.length) +
10282
+ [...name].filter((c, i) => c !== item[i]).length;
10283
+
10284
+ if (dist < bestScore && dist <= 2) {
10285
+ bestScore = dist;
10286
+ best = item;
10287
+ }
10280
10288
  }
10289
+
10290
+ return best;
10291
+ }
10281
10292
  }
10282
10293
 
10283
10294
  class Environment {
@@ -11835,25 +11846,39 @@ module.exports = Lexer;
11835
11846
  /***/ 222:
11836
11847
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
11837
11848
 
11849
+ const COLOR = {
11850
+ reset: '\x1b[0m',
11851
+ red: '\x1b[31m',
11852
+ yellow: '\x1b[33m',
11853
+ white: '\x1b[37m'
11854
+ };
11855
+
11838
11856
  class ParseError extends Error {
11839
11857
  constructor(message, token, source, suggestion = null) {
11840
11858
  const line = token?.line ?? '?';
11841
11859
  const column = token?.column ?? '?';
11842
11860
 
11843
- let output = `${message}\n`;
11861
+ let output = `${COLOR.red}${message}${COLOR.reset}\n`;
11844
11862
 
11845
11863
  if (suggestion) {
11846
- output += `Hint: ${suggestion}\n`;
11864
+ output += `${COLOR.yellow}${suggestion}${COLOR.reset}\n`;
11847
11865
  }
11848
11866
 
11849
11867
  if (source && token?.line != null) {
11850
11868
  const lines = source.split('\n');
11851
11869
  const srcLine = lines[token.line - 1] || '';
11852
- output += ` at line ${line}, column ${column}\n`;
11853
- output += ` ${srcLine}\n`;
11854
- output += ` ${' '.repeat(column - 1)}^\n`;
11870
+
11871
+ output += `${COLOR.white} at line ${line}, column ${column}\n`;
11872
+ const caretPos =
11873
+ typeof column === 'number' && column > 0
11874
+ ? column - 1
11875
+ : 0;
11876
+
11877
+ output += ` ${srcLine}\n`;
11878
+ output += ` ${' '.repeat(caretPos)}^\n${COLOR.reset}`;
11879
+
11855
11880
  } else {
11856
- output += ` at line ${line}, column ${column}\n`;
11881
+ output += `${COLOR.white} at line ${line}, column ${column}\n${COLOR.reset}`;
11857
11882
  }
11858
11883
 
11859
11884
  super(output);
@@ -11861,7 +11886,6 @@ class ParseError extends Error {
11861
11886
  }
11862
11887
  }
11863
11888
 
11864
-
11865
11889
  class Parser {
11866
11890
  constructor(tokens, source = '') {
11867
11891
  this.tokens = tokens;
@@ -13706,7 +13730,7 @@ const Lexer = __nccwpck_require__(211);
13706
13730
  const Parser = __nccwpck_require__(222);
13707
13731
  const Evaluator = __nccwpck_require__(112);
13708
13732
 
13709
- const VERSION = '1.1.12';
13733
+ const VERSION = '1.1.13';
13710
13734
 
13711
13735
  const COLOR = {
13712
13736
  reset: '\x1b[0m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.1.12",
3
+ "version": "1.1.13",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/evaluator.js CHANGED
@@ -9,65 +9,76 @@ class ReturnValue {
9
9
  }
10
10
  class BreakSignal {}
11
11
  class ContinueSignal {}
12
+ const COLOR = {
13
+ reset: '\x1b[0m',
14
+ red: '\x1b[31m',
15
+ yellow: '\x1b[33m',
16
+ white: '\x1b[37m'
17
+ };
18
+
12
19
  class RuntimeError extends Error {
13
- constructor(message, node, source, env = null) {
14
- const line = node?.line ?? '?';
15
- const column = node?.column ?? '?';
16
- let output = ` ${message} at line ${line}, column ${column}\n`;
17
-
18
- if (source && node?.line != null) {
19
- const lines = source.split('\n');
20
- const srcLine = lines[node.line - 1] || '';
21
- output += ` ${srcLine}\n`;
22
- const caretPos =
23
- typeof column === 'number' && column > 0
24
- ? column - 1
25
- : 0;
26
- output += ` ${' '.repeat(caretPos)}^\n`;
27
- }
20
+ constructor(message, node, source, env = null) {
21
+ const line = node?.line ?? '?';
22
+ const column = node?.column ?? '?';
28
23
 
29
- if (env && message.startsWith('Undefined variable:')) {
30
- const nameMatch = message.match(/"(.+?)"/);
31
- if (nameMatch) {
32
- const name = nameMatch[1];
33
- const suggestion = RuntimeError.suggest(name, env);
34
- if (suggestion) {
35
- output += `Did you mean "${suggestion}"?\n`;
36
- }
37
- }
38
- }
24
+ let output =
25
+ `${COLOR.red}${message}${COLOR.reset} at line ${line}, column ${column}\n`;
26
+
27
+ if (source && node?.line != null) {
28
+ const lines = source.split('\n');
29
+ const srcLine = lines[node.line - 1] || '';
30
+ const caretPos =
31
+ typeof column === 'number' && column > 0 ? column - 1 : 0;
39
32
 
40
- super(output);
41
- this.name = 'RuntimeError';
42
- this.line = line;
43
- this.column = column;
33
+ output += `${COLOR.white} ${srcLine}\n`;
34
+ output += ` ${' '.repeat(caretPos)}^\n${COLOR.reset}`;
44
35
  }
45
36
 
46
- static suggest(name, env) {
47
- const names = new Set();
48
- let current = env;
49
- while (current) {
50
- for (const key of Object.keys(current.store)) {
51
- names.add(key);
52
- }
53
- current = current.parent;
37
+ if (env && message.startsWith('Undefined variable:')) {
38
+ const nameMatch = message.match(/"(.+?)"/);
39
+ if (nameMatch) {
40
+ const name = nameMatch[1];
41
+ const suggestion = RuntimeError.suggest(name, env);
42
+ if (suggestion) {
43
+ output +=
44
+ `${COLOR.yellow}Did you mean "${suggestion}"?${COLOR.reset}\n`;
54
45
  }
46
+ }
47
+ }
55
48
 
56
- let best = null;
57
- let bestScore = Infinity;
49
+ super(output);
50
+ this.name = 'RuntimeError';
51
+ this.line = line;
52
+ this.column = column;
53
+ }
58
54
 
59
- for (const item of names) {
60
- const dist = Math.abs(item.length - name.length) +
61
- [...name].filter((c, i) => c !== item[i]).length;
55
+ static suggest(name, env) {
56
+ const names = new Set();
57
+ let current = env;
62
58
 
63
- if (dist < bestScore && dist <= 2) {
64
- bestScore = dist;
65
- best = item;
66
- }
67
- }
59
+ while (current) {
60
+ for (const key of Object.keys(current.store)) {
61
+ names.add(key);
62
+ }
63
+ current = current.parent;
64
+ }
65
+
66
+ let best = null;
67
+ let bestScore = Infinity;
68
+
69
+ for (const item of names) {
70
+ const dist =
71
+ Math.abs(item.length - name.length) +
72
+ [...name].filter((c, i) => c !== item[i]).length;
68
73
 
69
- return best;
74
+ if (dist < bestScore && dist <= 2) {
75
+ bestScore = dist;
76
+ best = item;
77
+ }
70
78
  }
79
+
80
+ return best;
81
+ }
71
82
  }
72
83
 
73
84
  class Environment {
package/src/parser.js CHANGED
@@ -1,22 +1,36 @@
1
+ const COLOR = {
2
+ reset: '\x1b[0m',
3
+ red: '\x1b[31m',
4
+ yellow: '\x1b[33m',
5
+ white: '\x1b[37m'
6
+ };
7
+
1
8
  class ParseError extends Error {
2
9
  constructor(message, token, source, suggestion = null) {
3
10
  const line = token?.line ?? '?';
4
11
  const column = token?.column ?? '?';
5
12
 
6
- let output = `${message}\n`;
13
+ let output = `${COLOR.red}${message}${COLOR.reset}\n`;
7
14
 
8
15
  if (suggestion) {
9
- output += `Hint: ${suggestion}\n`;
16
+ output += `${COLOR.yellow}${suggestion}${COLOR.reset}\n`;
10
17
  }
11
18
 
12
19
  if (source && token?.line != null) {
13
20
  const lines = source.split('\n');
14
21
  const srcLine = lines[token.line - 1] || '';
15
- output += ` at line ${line}, column ${column}\n`;
16
- output += ` ${srcLine}\n`;
17
- output += ` ${' '.repeat(column - 1)}^\n`;
22
+
23
+ output += `${COLOR.white} at line ${line}, column ${column}\n`;
24
+ const caretPos =
25
+ typeof column === 'number' && column > 0
26
+ ? column - 1
27
+ : 0;
28
+
29
+ output += ` ${srcLine}\n`;
30
+ output += ` ${' '.repeat(caretPos)}^\n${COLOR.reset}`;
31
+
18
32
  } else {
19
- output += ` at line ${line}, column ${column}\n`;
33
+ output += `${COLOR.white} at line ${line}, column ${column}\n${COLOR.reset}`;
20
34
  }
21
35
 
22
36
  super(output);
@@ -24,7 +38,6 @@ class ParseError extends Error {
24
38
  }
25
39
  }
26
40
 
27
-
28
41
  class Parser {
29
42
  constructor(tokens, source = '') {
30
43
  this.tokens = tokens;
package/src/starlight.js CHANGED
@@ -12,7 +12,7 @@ const Lexer = require('./lexer');
12
12
  const Parser = require('./parser');
13
13
  const Evaluator = require('./evaluator');
14
14
 
15
- const VERSION = '1.1.12';
15
+ const VERSION = '1.1.13';
16
16
 
17
17
  const COLOR = {
18
18
  reset: '\x1b[0m',