starlight-cli 1.1.12 → 1.1.14
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 +86 -58
- package/package.json +1 -1
- package/src/evaluator.js +64 -49
- 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 {
|
|
@@ -10440,6 +10451,10 @@ this.global.define('random', (min, max) => {
|
|
|
10440
10451
|
}
|
|
10441
10452
|
return result;
|
|
10442
10453
|
});
|
|
10454
|
+
this.global.define('encodeURLComponent', arg => {
|
|
10455
|
+
if (arg === null || arg === undefined) return '';
|
|
10456
|
+
return encodeURIComponent(String(arg));
|
|
10457
|
+
});
|
|
10443
10458
|
|
|
10444
10459
|
this.global.define('filter', async (array, fn) => {
|
|
10445
10460
|
if (!Array.isArray(array)) {
|
|
@@ -11596,7 +11611,7 @@ async evalUpdate(node, env) {
|
|
|
11596
11611
|
|
|
11597
11612
|
}
|
|
11598
11613
|
|
|
11599
|
-
module.exports = Evaluator;
|
|
11614
|
+
module.exports = Evaluator;
|
|
11600
11615
|
|
|
11601
11616
|
/***/ }),
|
|
11602
11617
|
|
|
@@ -11835,25 +11850,39 @@ module.exports = Lexer;
|
|
|
11835
11850
|
/***/ 222:
|
|
11836
11851
|
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
11837
11852
|
|
|
11853
|
+
const COLOR = {
|
|
11854
|
+
reset: '\x1b[0m',
|
|
11855
|
+
red: '\x1b[31m',
|
|
11856
|
+
yellow: '\x1b[33m',
|
|
11857
|
+
white: '\x1b[37m'
|
|
11858
|
+
};
|
|
11859
|
+
|
|
11838
11860
|
class ParseError extends Error {
|
|
11839
11861
|
constructor(message, token, source, suggestion = null) {
|
|
11840
11862
|
const line = token?.line ?? '?';
|
|
11841
11863
|
const column = token?.column ?? '?';
|
|
11842
11864
|
|
|
11843
|
-
let output = `${message}\n`;
|
|
11865
|
+
let output = `${COLOR.red}${message}${COLOR.reset}\n`;
|
|
11844
11866
|
|
|
11845
11867
|
if (suggestion) {
|
|
11846
|
-
output +=
|
|
11868
|
+
output += `${COLOR.yellow}${suggestion}${COLOR.reset}\n`;
|
|
11847
11869
|
}
|
|
11848
11870
|
|
|
11849
11871
|
if (source && token?.line != null) {
|
|
11850
11872
|
const lines = source.split('\n');
|
|
11851
11873
|
const srcLine = lines[token.line - 1] || '';
|
|
11852
|
-
|
|
11853
|
-
output +=
|
|
11854
|
-
|
|
11874
|
+
|
|
11875
|
+
output += `${COLOR.white} at line ${line}, column ${column}\n`;
|
|
11876
|
+
const caretPos =
|
|
11877
|
+
typeof column === 'number' && column > 0
|
|
11878
|
+
? column - 1
|
|
11879
|
+
: 0;
|
|
11880
|
+
|
|
11881
|
+
output += ` ${srcLine}\n`;
|
|
11882
|
+
output += ` ${' '.repeat(caretPos)}^\n${COLOR.reset}`;
|
|
11883
|
+
|
|
11855
11884
|
} else {
|
|
11856
|
-
output +=
|
|
11885
|
+
output += `${COLOR.white} at line ${line}, column ${column}\n${COLOR.reset}`;
|
|
11857
11886
|
}
|
|
11858
11887
|
|
|
11859
11888
|
super(output);
|
|
@@ -11861,7 +11890,6 @@ class ParseError extends Error {
|
|
|
11861
11890
|
}
|
|
11862
11891
|
}
|
|
11863
11892
|
|
|
11864
|
-
|
|
11865
11893
|
class Parser {
|
|
11866
11894
|
constructor(tokens, source = '') {
|
|
11867
11895
|
this.tokens = tokens;
|
|
@@ -13706,7 +13734,7 @@ const Lexer = __nccwpck_require__(211);
|
|
|
13706
13734
|
const Parser = __nccwpck_require__(222);
|
|
13707
13735
|
const Evaluator = __nccwpck_require__(112);
|
|
13708
13736
|
|
|
13709
|
-
const VERSION = '1.1.
|
|
13737
|
+
const VERSION = '1.1.14';
|
|
13710
13738
|
|
|
13711
13739
|
const COLOR = {
|
|
13712
13740
|
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 {
|
|
@@ -230,6 +241,10 @@ this.global.define('random', (min, max) => {
|
|
|
230
241
|
}
|
|
231
242
|
return result;
|
|
232
243
|
});
|
|
244
|
+
this.global.define('encodeURLComponent', arg => {
|
|
245
|
+
if (arg === null || arg === undefined) return '';
|
|
246
|
+
return encodeURIComponent(String(arg));
|
|
247
|
+
});
|
|
233
248
|
|
|
234
249
|
this.global.define('filter', async (array, fn) => {
|
|
235
250
|
if (!Array.isArray(array)) {
|
|
@@ -1386,4 +1401,4 @@ async evalUpdate(node, env) {
|
|
|
1386
1401
|
|
|
1387
1402
|
}
|
|
1388
1403
|
|
|
1389
|
-
module.exports = Evaluator;
|
|
1404
|
+
module.exports = Evaluator;
|
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;
|