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 +62 -357
- package/dist/index.js +81 -57
- package/package.json +1 -1
- package/src/evaluator.js +59 -48
- package/src/parser.js +20 -7
- package/src/starlight.js +1 -1
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://
|
|
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
|
-
|
|
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
|
-
|
|
13
|
+
### Modern Diagnostic Style
|
|
94
14
|
|
|
95
|
-
|
|
15
|
+
Starlight errors follow the same diagnostic standards used by modern languages such as JavaScript, Rust, and Python:
|
|
96
16
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
23
|
+
Example:
|
|
103
24
|
|
|
104
|
-
```sl
|
|
105
|
-
let add = (a, b) => a + b
|
|
106
|
-
sldeploy add(10, 20)
|
|
107
25
|
```
|
|
108
26
|
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
69
|
+
### ⚙️ Runtime Errors (Evaluator Errors)
|
|
307
70
|
|
|
308
|
-
|
|
71
|
+
Runtime errors occur during execution and include:
|
|
309
72
|
|
|
310
|
-
|
|
311
|
-
|
|
73
|
+
* Undefined variables
|
|
74
|
+
* Invalid operations
|
|
75
|
+
* Logical mistakes
|
|
312
76
|
|
|
313
|
-
|
|
77
|
+
Starlight also provides **intelligent name suggestions**:
|
|
314
78
|
|
|
315
79
|
```
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
90
|
+
Errors are color-coded for clarity:
|
|
357
91
|
|
|
358
|
-
|
|
92
|
+
* **Red** – Fatal syntax or runtime errors
|
|
93
|
+
* **Yellow** – Warnings or suggestions
|
|
94
|
+
* **White** – Neutral information (source preview, pointers)
|
|
359
95
|
|
|
360
|
-
|
|
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
|
-
###
|
|
100
|
+
### Safe Execution Model
|
|
368
101
|
|
|
369
|
-
|
|
102
|
+
* Execution **stops immediately** on error
|
|
103
|
+
* No uncaught exceptions
|
|
104
|
+
* No JavaScript stack traces leaked
|
|
105
|
+
* Clean exit behavior
|
|
370
106
|
|
|
371
|
-
|
|
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
|
-
|
|
111
|
+
### Summary
|
|
405
112
|
|
|
406
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
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
|
-
|
|
10240
|
-
|
|
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
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
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
|
-
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
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
|
-
|
|
10267
|
-
|
|
10265
|
+
static suggest(name, env) {
|
|
10266
|
+
const names = new Set();
|
|
10267
|
+
let current = env;
|
|
10268
10268
|
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
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
|
-
|
|
10274
|
-
|
|
10275
|
-
best = item;
|
|
10276
|
-
}
|
|
10277
|
-
}
|
|
10276
|
+
let best = null;
|
|
10277
|
+
let bestScore = Infinity;
|
|
10278
10278
|
|
|
10279
|
-
|
|
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 +=
|
|
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
|
-
|
|
11853
|
-
output +=
|
|
11854
|
-
|
|
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 +=
|
|
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.
|
|
13733
|
+
const VERSION = '1.1.13';
|
|
13710
13734
|
|
|
13711
13735
|
const COLOR = {
|
|
13712
13736
|
reset: '\x1b[0m',
|
package/package.json
CHANGED
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
|
|
49
|
+
super(output);
|
|
50
|
+
this.name = 'RuntimeError';
|
|
51
|
+
this.line = line;
|
|
52
|
+
this.column = column;
|
|
53
|
+
}
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
static suggest(name, env) {
|
|
56
|
+
const names = new Set();
|
|
57
|
+
let current = env;
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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 +=
|
|
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
|
-
|
|
16
|
-
output +=
|
|
17
|
-
|
|
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 +=
|
|
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;
|