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 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 = new Environment(env);
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 = new Environment(env);
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
- const variable = this.current.value; // loop variable
2367
- this.eat('IDENTIFIER');
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.eat('IN');
2370
- const iterable = this.expression(); // the array/object to loop over
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 (could be used for Python-style with "in", already handled above)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
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 = new Environment(env);
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 = new Environment(env);
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
- const variable = this.current.value; // loop variable
235
- this.eat('IDENTIFIER');
236
-
237
- this.eat('IN');
238
- const iterable = this.expression(); // the array/object to loop over
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 (could be used for Python-style with "in", already handled above)
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');