starlight-cli 1.0.41 → 1.0.43
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 +60 -19
- package/package.json +1 -1
- package/src/evaluator.js +38 -11
- package/src/parser.js +23 -9
package/dist/index.js
CHANGED
|
@@ -1370,6 +1370,37 @@ class Environment {
|
|
|
1370
1370
|
if (this.parent) return this.parent.get(name);
|
|
1371
1371
|
throw new Error(`Undefined variable: ${name}`);
|
|
1372
1372
|
}
|
|
1373
|
+
formatValue(value, seen = new Set()) {
|
|
1374
|
+
if (typeof value === 'object' && value !== null) {
|
|
1375
|
+
if (seen.has(value)) return '[Circular]';
|
|
1376
|
+
seen.add(value);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
if (value === null) return 'null';
|
|
1380
|
+
if (value === undefined) return 'undefined';
|
|
1381
|
+
|
|
1382
|
+
const t = typeof value;
|
|
1383
|
+
|
|
1384
|
+
if (t === 'string') return value;
|
|
1385
|
+
if (t === 'number' || t === 'boolean') return String(value);
|
|
1386
|
+
|
|
1387
|
+
if (t === 'function') {
|
|
1388
|
+
return '[function]';
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
if (Array.isArray(value)) {
|
|
1392
|
+
return '[' + value.map(v => this.formatValue(v, seen)).join(', ') + ']';
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (t === 'object') {
|
|
1396
|
+
const entries = Object.entries(value).map(
|
|
1397
|
+
([k, v]) => `${k}: ${this.formatValue(v, seen)}`
|
|
1398
|
+
);
|
|
1399
|
+
return '{ ' + entries.join(', ') + ' }';
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
return String(value);
|
|
1403
|
+
}
|
|
1373
1404
|
|
|
1374
1405
|
set(name, value) {
|
|
1375
1406
|
if (name in this.store) { this.store[name] = value; return value; }
|
|
@@ -1720,16 +1751,11 @@ async evalCompoundAssignment(node, env) {
|
|
|
1720
1751
|
|
|
1721
1752
|
async evalSldeploy(node, env) {
|
|
1722
1753
|
const val = await this.evaluate(node.expr, env);
|
|
1723
|
-
|
|
1724
|
-
if (typeof val === 'object' && val !== null) {
|
|
1725
|
-
console.log(JSON.stringify(val, null, 2));
|
|
1726
|
-
} else {
|
|
1727
|
-
console.log(val);
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1754
|
+
console.log(this.formatValue(val));
|
|
1730
1755
|
return val;
|
|
1731
1756
|
}
|
|
1732
1757
|
|
|
1758
|
+
|
|
1733
1759
|
async evalAsk(node, env) {
|
|
1734
1760
|
const prompt = await this.evaluate(node.prompt, env);
|
|
1735
1761
|
const input = readlineSync.question(prompt + ' ');
|
|
@@ -1801,9 +1827,8 @@ async evalWhile(node, env) {
|
|
|
1801
1827
|
return null;
|
|
1802
1828
|
}
|
|
1803
1829
|
async evalFor(node, env) {
|
|
1804
|
-
|
|
1805
1830
|
// -------------------------------
|
|
1806
|
-
// Python-style: for x in iterable
|
|
1831
|
+
// Python-style: for x in iterable (with optional 'let')
|
|
1807
1832
|
// -------------------------------
|
|
1808
1833
|
if (node.type === 'ForInStatement') {
|
|
1809
1834
|
const iterable = await this.evaluate(node.iterable, env);
|
|
@@ -1814,10 +1839,12 @@ async evalFor(node, env) {
|
|
|
1814
1839
|
|
|
1815
1840
|
const loopVar = node.variable; // STRING from parser
|
|
1816
1841
|
|
|
1842
|
+
const createLoopEnv = () => node.letKeyword ? new Environment(env) : env;
|
|
1843
|
+
|
|
1817
1844
|
// Arrays
|
|
1818
1845
|
if (Array.isArray(iterable)) {
|
|
1819
1846
|
for (const value of iterable) {
|
|
1820
|
-
const loopEnv =
|
|
1847
|
+
const loopEnv = createLoopEnv();
|
|
1821
1848
|
loopEnv.define(loopVar, value);
|
|
1822
1849
|
|
|
1823
1850
|
try {
|
|
@@ -1832,7 +1859,7 @@ async evalFor(node, env) {
|
|
|
1832
1859
|
// Objects (keys)
|
|
1833
1860
|
else {
|
|
1834
1861
|
for (const key of Object.keys(iterable)) {
|
|
1835
|
-
const loopEnv =
|
|
1862
|
+
const loopEnv = createLoopEnv();
|
|
1836
1863
|
loopEnv.define(loopVar, key);
|
|
1837
1864
|
|
|
1838
1865
|
try {
|
|
@@ -2396,16 +2423,30 @@ importStatement() {
|
|
|
2396
2423
|
forStatement() {
|
|
2397
2424
|
this.eat('FOR');
|
|
2398
2425
|
|
|
2399
|
-
// --- Python-style: for variable in iterable ---
|
|
2400
|
-
if (this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')
|
|
2401
|
-
|
|
2402
|
-
|
|
2426
|
+
// --- Python-style: for variable in iterable (supports optional 'let') ---
|
|
2427
|
+
if ((this.current.type === 'LET' && this.peekType() === 'IDENTIFIER' && this.peekType(2) === 'IN') ||
|
|
2428
|
+
(this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')) {
|
|
2429
|
+
|
|
2430
|
+
let variable;
|
|
2431
|
+
let iterable;
|
|
2432
|
+
let letKeyword = false;
|
|
2403
2433
|
|
|
2404
|
-
this.
|
|
2405
|
-
|
|
2434
|
+
if (this.current.type === 'LET') {
|
|
2435
|
+
letKeyword = true;
|
|
2436
|
+
this.eat('LET');
|
|
2437
|
+
variable = this.current.value;
|
|
2438
|
+
this.eat('IDENTIFIER');
|
|
2439
|
+
this.eat('IN');
|
|
2440
|
+
iterable = this.expression();
|
|
2441
|
+
} else {
|
|
2442
|
+
variable = this.current.value;
|
|
2443
|
+
this.eat('IDENTIFIER');
|
|
2444
|
+
this.eat('IN');
|
|
2445
|
+
iterable = this.expression();
|
|
2446
|
+
}
|
|
2406
2447
|
|
|
2407
2448
|
const body = this.block();
|
|
2408
|
-
return { type: 'ForInStatement', variable, iterable, body };
|
|
2449
|
+
return { type: 'ForInStatement', variable, iterable, letKeyword, body };
|
|
2409
2450
|
}
|
|
2410
2451
|
|
|
2411
2452
|
// --- C-style: for(init; test; update) ---
|
|
@@ -2431,7 +2472,7 @@ forStatement() {
|
|
|
2431
2472
|
if (this.current.type !== 'RPAREN') update = this.expression();
|
|
2432
2473
|
this.eat('RPAREN');
|
|
2433
2474
|
} else {
|
|
2434
|
-
// fallback: single expression (
|
|
2475
|
+
// fallback: single expression (rare, mostly handled above)
|
|
2435
2476
|
init = this.expression();
|
|
2436
2477
|
if (this.current.type === 'IN') {
|
|
2437
2478
|
this.eat('IN');
|
package/package.json
CHANGED
package/src/evaluator.js
CHANGED
|
@@ -27,6 +27,37 @@ class Environment {
|
|
|
27
27
|
if (this.parent) return this.parent.get(name);
|
|
28
28
|
throw new Error(`Undefined variable: ${name}`);
|
|
29
29
|
}
|
|
30
|
+
formatValue(value, seen = new Set()) {
|
|
31
|
+
if (typeof value === 'object' && value !== null) {
|
|
32
|
+
if (seen.has(value)) return '[Circular]';
|
|
33
|
+
seen.add(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (value === null) return 'null';
|
|
37
|
+
if (value === undefined) return 'undefined';
|
|
38
|
+
|
|
39
|
+
const t = typeof value;
|
|
40
|
+
|
|
41
|
+
if (t === 'string') return value;
|
|
42
|
+
if (t === 'number' || t === 'boolean') return String(value);
|
|
43
|
+
|
|
44
|
+
if (t === 'function') {
|
|
45
|
+
return '[function]';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
return '[' + value.map(v => this.formatValue(v, seen)).join(', ') + ']';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (t === 'object') {
|
|
53
|
+
const entries = Object.entries(value).map(
|
|
54
|
+
([k, v]) => `${k}: ${this.formatValue(v, seen)}`
|
|
55
|
+
);
|
|
56
|
+
return '{ ' + entries.join(', ') + ' }';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
30
61
|
|
|
31
62
|
set(name, value) {
|
|
32
63
|
if (name in this.store) { this.store[name] = value; return value; }
|
|
@@ -377,16 +408,11 @@ async evalCompoundAssignment(node, env) {
|
|
|
377
408
|
|
|
378
409
|
async evalSldeploy(node, env) {
|
|
379
410
|
const val = await this.evaluate(node.expr, env);
|
|
380
|
-
|
|
381
|
-
if (typeof val === 'object' && val !== null) {
|
|
382
|
-
console.log(JSON.stringify(val, null, 2));
|
|
383
|
-
} else {
|
|
384
|
-
console.log(val);
|
|
385
|
-
}
|
|
386
|
-
|
|
411
|
+
console.log(this.formatValue(val));
|
|
387
412
|
return val;
|
|
388
413
|
}
|
|
389
414
|
|
|
415
|
+
|
|
390
416
|
async evalAsk(node, env) {
|
|
391
417
|
const prompt = await this.evaluate(node.prompt, env);
|
|
392
418
|
const input = readlineSync.question(prompt + ' ');
|
|
@@ -458,9 +484,8 @@ async evalWhile(node, env) {
|
|
|
458
484
|
return null;
|
|
459
485
|
}
|
|
460
486
|
async evalFor(node, env) {
|
|
461
|
-
|
|
462
487
|
// -------------------------------
|
|
463
|
-
// Python-style: for x in iterable
|
|
488
|
+
// Python-style: for x in iterable (with optional 'let')
|
|
464
489
|
// -------------------------------
|
|
465
490
|
if (node.type === 'ForInStatement') {
|
|
466
491
|
const iterable = await this.evaluate(node.iterable, env);
|
|
@@ -471,10 +496,12 @@ async evalFor(node, env) {
|
|
|
471
496
|
|
|
472
497
|
const loopVar = node.variable; // STRING from parser
|
|
473
498
|
|
|
499
|
+
const createLoopEnv = () => node.letKeyword ? new Environment(env) : env;
|
|
500
|
+
|
|
474
501
|
// Arrays
|
|
475
502
|
if (Array.isArray(iterable)) {
|
|
476
503
|
for (const value of iterable) {
|
|
477
|
-
const loopEnv =
|
|
504
|
+
const loopEnv = createLoopEnv();
|
|
478
505
|
loopEnv.define(loopVar, value);
|
|
479
506
|
|
|
480
507
|
try {
|
|
@@ -489,7 +516,7 @@ async evalFor(node, env) {
|
|
|
489
516
|
// Objects (keys)
|
|
490
517
|
else {
|
|
491
518
|
for (const key of Object.keys(iterable)) {
|
|
492
|
-
const loopEnv =
|
|
519
|
+
const loopEnv = createLoopEnv();
|
|
493
520
|
loopEnv.define(loopVar, key);
|
|
494
521
|
|
|
495
522
|
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');
|