starlight-cli 1.0.40 → 1.0.42
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 +62 -12
- package/package.json +1 -1
- package/src/evaluator.js +40 -4
- package/src/parser.js +23 -9
package/dist/index.js
CHANGED
|
@@ -1404,6 +1404,41 @@ class Evaluator {
|
|
|
1404
1404
|
});
|
|
1405
1405
|
this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
|
|
1406
1406
|
this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
|
|
1407
|
+
this.global.define('range', (...args) => {
|
|
1408
|
+
let start = 0;
|
|
1409
|
+
let end = 0;
|
|
1410
|
+
let step = 1;
|
|
1411
|
+
|
|
1412
|
+
if (args.length === 1) {
|
|
1413
|
+
end = Number(args[0]);
|
|
1414
|
+
} else if (args.length === 2) {
|
|
1415
|
+
start = Number(args[0]);
|
|
1416
|
+
end = Number(args[1]);
|
|
1417
|
+
} else if (args.length === 3) {
|
|
1418
|
+
start = Number(args[0]);
|
|
1419
|
+
end = Number(args[1]);
|
|
1420
|
+
step = Number(args[2]);
|
|
1421
|
+
} else {
|
|
1422
|
+
throw new Error('range() expects 1 to 3 arguments');
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
if (step === 0) {
|
|
1426
|
+
throw new Error('range() step cannot be 0');
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
const result = [];
|
|
1430
|
+
if (step > 0) {
|
|
1431
|
+
for (let i = start; i < end; i += step) {
|
|
1432
|
+
result.push(i);
|
|
1433
|
+
}
|
|
1434
|
+
} else {
|
|
1435
|
+
for (let i = start; i > end; i += step) {
|
|
1436
|
+
result.push(i);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
return result;
|
|
1441
|
+
});
|
|
1407
1442
|
|
|
1408
1443
|
this.global.define('ask', prompt => {
|
|
1409
1444
|
const readlineSync = __nccwpck_require__(552);
|
|
@@ -1766,9 +1801,8 @@ async evalWhile(node, env) {
|
|
|
1766
1801
|
return null;
|
|
1767
1802
|
}
|
|
1768
1803
|
async evalFor(node, env) {
|
|
1769
|
-
|
|
1770
1804
|
// -------------------------------
|
|
1771
|
-
// Python-style: for x in iterable
|
|
1805
|
+
// Python-style: for x in iterable (with optional 'let')
|
|
1772
1806
|
// -------------------------------
|
|
1773
1807
|
if (node.type === 'ForInStatement') {
|
|
1774
1808
|
const iterable = await this.evaluate(node.iterable, env);
|
|
@@ -1779,10 +1813,12 @@ async evalFor(node, env) {
|
|
|
1779
1813
|
|
|
1780
1814
|
const loopVar = node.variable; // STRING from parser
|
|
1781
1815
|
|
|
1816
|
+
const createLoopEnv = () => node.letKeyword ? new Environment(env) : env;
|
|
1817
|
+
|
|
1782
1818
|
// Arrays
|
|
1783
1819
|
if (Array.isArray(iterable)) {
|
|
1784
1820
|
for (const value of iterable) {
|
|
1785
|
-
const loopEnv =
|
|
1821
|
+
const loopEnv = createLoopEnv();
|
|
1786
1822
|
loopEnv.define(loopVar, value);
|
|
1787
1823
|
|
|
1788
1824
|
try {
|
|
@@ -1797,7 +1833,7 @@ async evalFor(node, env) {
|
|
|
1797
1833
|
// Objects (keys)
|
|
1798
1834
|
else {
|
|
1799
1835
|
for (const key of Object.keys(iterable)) {
|
|
1800
|
-
const loopEnv =
|
|
1836
|
+
const loopEnv = createLoopEnv();
|
|
1801
1837
|
loopEnv.define(loopVar, key);
|
|
1802
1838
|
|
|
1803
1839
|
try {
|
|
@@ -2361,16 +2397,30 @@ importStatement() {
|
|
|
2361
2397
|
forStatement() {
|
|
2362
2398
|
this.eat('FOR');
|
|
2363
2399
|
|
|
2364
|
-
// --- Python-style: for variable in iterable ---
|
|
2365
|
-
if (this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')
|
|
2366
|
-
|
|
2367
|
-
|
|
2400
|
+
// --- Python-style: for variable in iterable (supports optional 'let') ---
|
|
2401
|
+
if ((this.current.type === 'LET' && this.peekType() === 'IDENTIFIER' && this.peekType(2) === 'IN') ||
|
|
2402
|
+
(this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')) {
|
|
2403
|
+
|
|
2404
|
+
let variable;
|
|
2405
|
+
let iterable;
|
|
2406
|
+
let letKeyword = false;
|
|
2368
2407
|
|
|
2369
|
-
this.
|
|
2370
|
-
|
|
2408
|
+
if (this.current.type === 'LET') {
|
|
2409
|
+
letKeyword = true;
|
|
2410
|
+
this.eat('LET');
|
|
2411
|
+
variable = this.current.value;
|
|
2412
|
+
this.eat('IDENTIFIER');
|
|
2413
|
+
this.eat('IN');
|
|
2414
|
+
iterable = this.expression();
|
|
2415
|
+
} else {
|
|
2416
|
+
variable = this.current.value;
|
|
2417
|
+
this.eat('IDENTIFIER');
|
|
2418
|
+
this.eat('IN');
|
|
2419
|
+
iterable = this.expression();
|
|
2420
|
+
}
|
|
2371
2421
|
|
|
2372
2422
|
const body = this.block();
|
|
2373
|
-
return { type: 'ForInStatement', variable, iterable, body };
|
|
2423
|
+
return { type: 'ForInStatement', variable, iterable, letKeyword, body };
|
|
2374
2424
|
}
|
|
2375
2425
|
|
|
2376
2426
|
// --- C-style: for(init; test; update) ---
|
|
@@ -2396,7 +2446,7 @@ forStatement() {
|
|
|
2396
2446
|
if (this.current.type !== 'RPAREN') update = this.expression();
|
|
2397
2447
|
this.eat('RPAREN');
|
|
2398
2448
|
} else {
|
|
2399
|
-
// fallback: single expression (
|
|
2449
|
+
// fallback: single expression (rare, mostly handled above)
|
|
2400
2450
|
init = this.expression();
|
|
2401
2451
|
if (this.current.type === 'IN') {
|
|
2402
2452
|
this.eat('IN');
|
package/package.json
CHANGED
package/src/evaluator.js
CHANGED
|
@@ -61,6 +61,41 @@ class Evaluator {
|
|
|
61
61
|
});
|
|
62
62
|
this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
|
|
63
63
|
this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
|
|
64
|
+
this.global.define('range', (...args) => {
|
|
65
|
+
let start = 0;
|
|
66
|
+
let end = 0;
|
|
67
|
+
let step = 1;
|
|
68
|
+
|
|
69
|
+
if (args.length === 1) {
|
|
70
|
+
end = Number(args[0]);
|
|
71
|
+
} else if (args.length === 2) {
|
|
72
|
+
start = Number(args[0]);
|
|
73
|
+
end = Number(args[1]);
|
|
74
|
+
} else if (args.length === 3) {
|
|
75
|
+
start = Number(args[0]);
|
|
76
|
+
end = Number(args[1]);
|
|
77
|
+
step = Number(args[2]);
|
|
78
|
+
} else {
|
|
79
|
+
throw new Error('range() expects 1 to 3 arguments');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (step === 0) {
|
|
83
|
+
throw new Error('range() step cannot be 0');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = [];
|
|
87
|
+
if (step > 0) {
|
|
88
|
+
for (let i = start; i < end; i += step) {
|
|
89
|
+
result.push(i);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
for (let i = start; i > end; i += step) {
|
|
93
|
+
result.push(i);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return result;
|
|
98
|
+
});
|
|
64
99
|
|
|
65
100
|
this.global.define('ask', prompt => {
|
|
66
101
|
const readlineSync = require('readline-sync');
|
|
@@ -423,9 +458,8 @@ async evalWhile(node, env) {
|
|
|
423
458
|
return null;
|
|
424
459
|
}
|
|
425
460
|
async evalFor(node, env) {
|
|
426
|
-
|
|
427
461
|
// -------------------------------
|
|
428
|
-
// Python-style: for x in iterable
|
|
462
|
+
// Python-style: for x in iterable (with optional 'let')
|
|
429
463
|
// -------------------------------
|
|
430
464
|
if (node.type === 'ForInStatement') {
|
|
431
465
|
const iterable = await this.evaluate(node.iterable, env);
|
|
@@ -436,10 +470,12 @@ async evalFor(node, env) {
|
|
|
436
470
|
|
|
437
471
|
const loopVar = node.variable; // STRING from parser
|
|
438
472
|
|
|
473
|
+
const createLoopEnv = () => node.letKeyword ? new Environment(env) : env;
|
|
474
|
+
|
|
439
475
|
// Arrays
|
|
440
476
|
if (Array.isArray(iterable)) {
|
|
441
477
|
for (const value of iterable) {
|
|
442
|
-
const loopEnv =
|
|
478
|
+
const loopEnv = createLoopEnv();
|
|
443
479
|
loopEnv.define(loopVar, value);
|
|
444
480
|
|
|
445
481
|
try {
|
|
@@ -454,7 +490,7 @@ async evalFor(node, env) {
|
|
|
454
490
|
// Objects (keys)
|
|
455
491
|
else {
|
|
456
492
|
for (const key of Object.keys(iterable)) {
|
|
457
|
-
const loopEnv =
|
|
493
|
+
const loopEnv = createLoopEnv();
|
|
458
494
|
loopEnv.define(loopVar, key);
|
|
459
495
|
|
|
460
496
|
try {
|
package/src/parser.js
CHANGED
|
@@ -229,16 +229,30 @@ importStatement() {
|
|
|
229
229
|
forStatement() {
|
|
230
230
|
this.eat('FOR');
|
|
231
231
|
|
|
232
|
-
// --- Python-style: for variable in iterable ---
|
|
233
|
-
if (this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
232
|
+
// --- Python-style: for variable in iterable (supports optional 'let') ---
|
|
233
|
+
if ((this.current.type === 'LET' && this.peekType() === 'IDENTIFIER' && this.peekType(2) === 'IN') ||
|
|
234
|
+
(this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')) {
|
|
235
|
+
|
|
236
|
+
let variable;
|
|
237
|
+
let iterable;
|
|
238
|
+
let letKeyword = false;
|
|
239
|
+
|
|
240
|
+
if (this.current.type === 'LET') {
|
|
241
|
+
letKeyword = true;
|
|
242
|
+
this.eat('LET');
|
|
243
|
+
variable = this.current.value;
|
|
244
|
+
this.eat('IDENTIFIER');
|
|
245
|
+
this.eat('IN');
|
|
246
|
+
iterable = this.expression();
|
|
247
|
+
} else {
|
|
248
|
+
variable = this.current.value;
|
|
249
|
+
this.eat('IDENTIFIER');
|
|
250
|
+
this.eat('IN');
|
|
251
|
+
iterable = this.expression();
|
|
252
|
+
}
|
|
239
253
|
|
|
240
254
|
const body = this.block();
|
|
241
|
-
return { type: 'ForInStatement', variable, iterable, body };
|
|
255
|
+
return { type: 'ForInStatement', variable, iterable, letKeyword, body };
|
|
242
256
|
}
|
|
243
257
|
|
|
244
258
|
// --- C-style: for(init; test; update) ---
|
|
@@ -264,7 +278,7 @@ forStatement() {
|
|
|
264
278
|
if (this.current.type !== 'RPAREN') update = this.expression();
|
|
265
279
|
this.eat('RPAREN');
|
|
266
280
|
} else {
|
|
267
|
-
// fallback: single expression (
|
|
281
|
+
// fallback: single expression (rare, mostly handled above)
|
|
268
282
|
init = this.expression();
|
|
269
283
|
if (this.current.type === 'IN') {
|
|
270
284
|
this.eat('IN');
|