starlight-cli 1.1.15 → 1.1.16

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
@@ -10632,10 +10632,17 @@ async evaluate(node, env = this.global) {
10632
10632
  case 'SldeployStatement': return await this.evalSldeploy(node, env);
10633
10633
  case 'AskStatement': return await this.evalAsk(node, env);
10634
10634
  case 'DefineStatement': return await this.evalDefine(node, env);
10635
+ case 'FunctionExpression':
10636
+ return this.evalFunctionExpression(node, env);
10637
+ case 'SliceExpression':
10638
+ return await this.evalSlice(node, env);
10639
+
10635
10640
  case 'ExpressionStatement': return await this.evaluate(node.expression, env);
10636
10641
  case 'BinaryExpression': return await this.evalBinary(node, env);
10637
10642
  case 'LogicalExpression': return await this.evalLogical(node, env);
10638
10643
  case 'UnaryExpression': return await this.evalUnary(node, env);
10644
+ case 'ConditionalExpression':
10645
+ return await this.evalConditional(node, env);
10639
10646
  case 'Literal': return node.value;
10640
10647
  case 'Identifier': return env.get(node.name, node, this.source);
10641
10648
  case 'IfStatement': return await this.evalIf(node, env);
@@ -10738,6 +10745,36 @@ async evalProgram(node, env) {
10738
10745
  }
10739
10746
  return result;
10740
10747
  }
10748
+ async evalSlice(node, env) {
10749
+ try {
10750
+ const arr = await this.evaluate(node.object, env);
10751
+ const start = node.start ? await this.evaluate(node.start, env) : 0;
10752
+ const end = node.end ? await this.evaluate(node.end, env) : arr.length;
10753
+
10754
+ if (!Array.isArray(arr)) {
10755
+ throw new RuntimeError(
10756
+ 'Slice target must be an array',
10757
+ node,
10758
+ this.source,
10759
+ env
10760
+ );
10761
+ }
10762
+
10763
+ const s = start < 0 ? Math.max(arr.length + start, 0) : Math.min(start, arr.length);
10764
+ const e = end < 0 ? Math.max(arr.length + end, 0) : Math.min(end, arr.length);
10765
+
10766
+ return arr.slice(s, e);
10767
+
10768
+ } catch (err) {
10769
+ if (err instanceof RuntimeError) throw err;
10770
+ throw new RuntimeError(
10771
+ err.message || 'Error evaluating slice expression',
10772
+ node,
10773
+ this.source,
10774
+ env
10775
+ );
10776
+ }
10777
+ }
10741
10778
 
10742
10779
  async evalStartStatement(node, env) {
10743
10780
  try {
@@ -10786,6 +10823,24 @@ async evalStartStatement(node, env) {
10786
10823
  );
10787
10824
  }
10788
10825
  }
10826
+ async evalConditional(node, env) {
10827
+ try {
10828
+ const test = await this.evaluate(node.test, env);
10829
+ if (test) {
10830
+ return await this.evaluate(node.consequent, env);
10831
+ } else {
10832
+ return await this.evaluate(node.alternate, env);
10833
+ }
10834
+ } catch (e) {
10835
+ if (e instanceof RuntimeError) throw e;
10836
+ throw new RuntimeError(
10837
+ e.message || 'Error evaluating conditional expression',
10838
+ node,
10839
+ this.source,
10840
+ env
10841
+ );
10842
+ }
10843
+ }
10789
10844
 
10790
10845
  async evalRaceClause(node, env) {
10791
10846
  try {
@@ -11396,6 +11451,41 @@ async evalFor(node, env) {
11396
11451
  );
11397
11452
  }
11398
11453
  }
11454
+ evalFunctionExpression(node, env) {
11455
+ if (!node.body || !Array.isArray(node.params)) {
11456
+ throw new RuntimeError(
11457
+ 'Invalid function expression',
11458
+ node,
11459
+ this.source,
11460
+ env
11461
+ );
11462
+ }
11463
+
11464
+ const evaluator = this;
11465
+
11466
+ const fn = async function (...args) {
11467
+ const localEnv = new Environment(env);
11468
+
11469
+ for (let i = 0; i < node.params.length; i++) {
11470
+ const param = node.params[i];
11471
+ const paramName = typeof param === 'string' ? param : param.name;
11472
+ localEnv.define(paramName, args[i]);
11473
+ }
11474
+
11475
+ try {
11476
+ const result = await evaluator.evaluate(node.body, localEnv);
11477
+ return result === undefined ? null : result;
11478
+ } catch (e) {
11479
+ if (e instanceof ReturnValue) return e.value === undefined ? null : e.value;
11480
+ throw e;
11481
+ }
11482
+ };
11483
+ fn.params = node.params;
11484
+ fn.body = node.body;
11485
+ fn.env = env;
11486
+
11487
+ return fn;
11488
+ }
11399
11489
 
11400
11490
  evalFunctionDeclaration(node, env) {
11401
11491
  try {
@@ -11483,7 +11573,6 @@ async evalCall(node, env) {
11483
11573
  );
11484
11574
  }
11485
11575
  }
11486
-
11487
11576
  async evalIndex(node, env) {
11488
11577
  try {
11489
11578
  const obj = await this.evaluate(node.object, env);
@@ -11516,7 +11605,6 @@ async evalIndex(node, env) {
11516
11605
  );
11517
11606
  }
11518
11607
  }
11519
-
11520
11608
  async evalObject(node, env) {
11521
11609
  try {
11522
11610
  const out = {};
@@ -13161,33 +13249,100 @@ postfix() {
13161
13249
  while (true) {
13162
13250
  const t = this.current;
13163
13251
 
13164
- if (t.type === 'LBRACKET') {
13165
- const startLine = t.line;
13166
- const startCol = t.column;
13167
- this.eat('LBRACKET');
13252
+ if (t.type === 'LBRACKET') {
13253
+ const startLine = t.line;
13254
+ const startCol = t.column;
13255
+ this.eat('LBRACKET');
13256
+
13257
+ let start = null;
13258
+ let end = null;
13259
+ let step = null;
13260
+
13261
+ if (this.current.type === 'COLON') {
13262
+ this.eat('COLON');
13263
+
13264
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13265
+ end = this.expression();
13266
+ }
13267
+
13268
+ if (this.current.type === 'COLON') {
13269
+ this.eat('COLON');
13270
+ if (this.current.type !== 'RBRACKET') {
13271
+ step = this.expression();
13272
+ }
13273
+ }
13274
+
13275
+ if (this.current.type !== 'RBRACKET') {
13276
+ throw new ParseError(
13277
+ "Expected ']' after slice",
13278
+ this.current,
13279
+ this.source
13280
+ );
13281
+ }
13282
+
13283
+ this.eat('RBRACKET');
13284
+
13285
+ node = {
13286
+ type: 'SliceExpression',
13287
+ object: node,
13288
+ start,
13289
+ end,
13290
+ step,
13291
+ line: startLine,
13292
+ column: startCol
13293
+ };
13168
13294
 
13169
- const index = this.expression();
13295
+ } else {
13296
+ start = this.expression();
13297
+
13298
+ if (this.current.type === 'COLON') {
13299
+ this.eat('COLON');
13300
+
13301
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13302
+ end = this.expression();
13303
+ }
13304
+
13305
+ if (this.current.type === 'COLON') {
13306
+ this.eat('COLON');
13307
+ if (this.current.type !== 'RBRACKET') {
13308
+ step = this.expression();
13309
+ }
13310
+ }
13170
13311
 
13171
13312
  if (this.current.type !== 'RBRACKET') {
13172
13313
  throw new ParseError(
13173
- "Expected ']' after index expression",
13314
+ "Expected ']' after slice",
13174
13315
  this.current,
13175
- this.source,
13176
- "Index access must be closed, e.g. arr[0]"
13316
+ this.source
13177
13317
  );
13178
13318
  }
13179
13319
 
13180
13320
  this.eat('RBRACKET');
13181
13321
 
13322
+ node = {
13323
+ type: 'SliceExpression',
13324
+ object: node,
13325
+ start,
13326
+ end,
13327
+ step,
13328
+ line: startLine,
13329
+ column: startCol
13330
+ };
13331
+
13332
+ } else {
13333
+ this.eat('RBRACKET');
13182
13334
  node = {
13183
13335
  type: 'IndexExpression',
13184
13336
  object: node,
13185
- indexer: index,
13337
+ indexer: start,
13186
13338
  line: startLine,
13187
13339
  column: startCol
13188
13340
  };
13189
- continue;
13190
13341
  }
13342
+ }
13343
+ }
13344
+
13345
+
13191
13346
 
13192
13347
  if (t.type === 'LPAREN') {
13193
13348
  const startLine = t.line;
@@ -13319,6 +13474,7 @@ arrowFunction(params) {
13319
13474
  };
13320
13475
  }
13321
13476
 
13477
+
13322
13478
  primary() {
13323
13479
  const t = this.current;
13324
13480
 
@@ -13390,7 +13546,6 @@ primary() {
13390
13546
  return { type: 'NewExpression', callee, arguments: args, line: t.line, column: t.column };
13391
13547
  }
13392
13548
 
13393
- // ---- ask(...) function call ----
13394
13549
  if (t.type === 'ASK') {
13395
13550
  this.eat('ASK');
13396
13551
 
@@ -13431,6 +13586,69 @@ primary() {
13431
13586
  column: t.column
13432
13587
  };
13433
13588
  }
13589
+ if (t.type === 'FUNC') {
13590
+ const funcToken = t;
13591
+ this.eat('FUNC');
13592
+
13593
+ let params = [];
13594
+ if (this.current.type === 'LPAREN') {
13595
+ this.eat('LPAREN');
13596
+
13597
+ if (this.current.type !== 'RPAREN') {
13598
+ if (this.current.type !== 'IDENTIFIER') {
13599
+ throw new ParseError(
13600
+ "Expected parameter name",
13601
+ this.current,
13602
+ this.source
13603
+ );
13604
+ }
13605
+
13606
+ params.push(this.current.value);
13607
+ this.eat('IDENTIFIER');
13608
+
13609
+ while (this.current.type === 'COMMA') {
13610
+ this.eat('COMMA');
13611
+ if (this.current.type !== 'IDENTIFIER') {
13612
+ throw new ParseError(
13613
+ "Expected parameter name",
13614
+ this.current,
13615
+ this.source
13616
+ );
13617
+ }
13618
+ params.push(this.current.value);
13619
+ this.eat('IDENTIFIER');
13620
+ }
13621
+ }
13622
+
13623
+ if (this.current.type !== 'RPAREN') {
13624
+ throw new ParseError(
13625
+ "Expected ')' after function parameters",
13626
+ this.current,
13627
+ this.source
13628
+ );
13629
+ }
13630
+
13631
+ this.eat('RPAREN');
13632
+ }
13633
+
13634
+ if (this.current.type !== 'LBRACE') {
13635
+ throw new ParseError(
13636
+ "Expected '{' to start function body",
13637
+ this.current,
13638
+ this.source
13639
+ );
13640
+ }
13641
+
13642
+ const body = this.block();
13643
+
13644
+ return {
13645
+ type: 'FunctionExpression',
13646
+ params,
13647
+ body,
13648
+ line: funcToken.line,
13649
+ column: funcToken.column
13650
+ };
13651
+ }
13434
13652
 
13435
13653
  if (t.type === 'IDENTIFIER') {
13436
13654
  const name = t.value;
@@ -13567,7 +13785,7 @@ primary() {
13567
13785
  t,
13568
13786
  this.source
13569
13787
  );
13570
- }
13788
+ }
13571
13789
 
13572
13790
  }
13573
13791
 
@@ -13751,7 +13969,7 @@ const Lexer = __nccwpck_require__(211);
13751
13969
  const Parser = __nccwpck_require__(222);
13752
13970
  const Evaluator = __nccwpck_require__(112);
13753
13971
 
13754
- const VERSION = '1.1.15';
13972
+ const VERSION = '1.1.16';
13755
13973
 
13756
13974
  const COLOR = {
13757
13975
  reset: '\x1b[0m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.1.15",
3
+ "version": "1.1.16",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/evaluator.js CHANGED
@@ -422,10 +422,17 @@ async evaluate(node, env = this.global) {
422
422
  case 'SldeployStatement': return await this.evalSldeploy(node, env);
423
423
  case 'AskStatement': return await this.evalAsk(node, env);
424
424
  case 'DefineStatement': return await this.evalDefine(node, env);
425
+ case 'FunctionExpression':
426
+ return this.evalFunctionExpression(node, env);
427
+ case 'SliceExpression':
428
+ return await this.evalSlice(node, env);
429
+
425
430
  case 'ExpressionStatement': return await this.evaluate(node.expression, env);
426
431
  case 'BinaryExpression': return await this.evalBinary(node, env);
427
432
  case 'LogicalExpression': return await this.evalLogical(node, env);
428
433
  case 'UnaryExpression': return await this.evalUnary(node, env);
434
+ case 'ConditionalExpression':
435
+ return await this.evalConditional(node, env);
429
436
  case 'Literal': return node.value;
430
437
  case 'Identifier': return env.get(node.name, node, this.source);
431
438
  case 'IfStatement': return await this.evalIf(node, env);
@@ -528,6 +535,36 @@ async evalProgram(node, env) {
528
535
  }
529
536
  return result;
530
537
  }
538
+ async evalSlice(node, env) {
539
+ try {
540
+ const arr = await this.evaluate(node.object, env);
541
+ const start = node.start ? await this.evaluate(node.start, env) : 0;
542
+ const end = node.end ? await this.evaluate(node.end, env) : arr.length;
543
+
544
+ if (!Array.isArray(arr)) {
545
+ throw new RuntimeError(
546
+ 'Slice target must be an array',
547
+ node,
548
+ this.source,
549
+ env
550
+ );
551
+ }
552
+
553
+ const s = start < 0 ? Math.max(arr.length + start, 0) : Math.min(start, arr.length);
554
+ const e = end < 0 ? Math.max(arr.length + end, 0) : Math.min(end, arr.length);
555
+
556
+ return arr.slice(s, e);
557
+
558
+ } catch (err) {
559
+ if (err instanceof RuntimeError) throw err;
560
+ throw new RuntimeError(
561
+ err.message || 'Error evaluating slice expression',
562
+ node,
563
+ this.source,
564
+ env
565
+ );
566
+ }
567
+ }
531
568
 
532
569
  async evalStartStatement(node, env) {
533
570
  try {
@@ -576,6 +613,24 @@ async evalStartStatement(node, env) {
576
613
  );
577
614
  }
578
615
  }
616
+ async evalConditional(node, env) {
617
+ try {
618
+ const test = await this.evaluate(node.test, env);
619
+ if (test) {
620
+ return await this.evaluate(node.consequent, env);
621
+ } else {
622
+ return await this.evaluate(node.alternate, env);
623
+ }
624
+ } catch (e) {
625
+ if (e instanceof RuntimeError) throw e;
626
+ throw new RuntimeError(
627
+ e.message || 'Error evaluating conditional expression',
628
+ node,
629
+ this.source,
630
+ env
631
+ );
632
+ }
633
+ }
579
634
 
580
635
  async evalRaceClause(node, env) {
581
636
  try {
@@ -1186,6 +1241,41 @@ async evalFor(node, env) {
1186
1241
  );
1187
1242
  }
1188
1243
  }
1244
+ evalFunctionExpression(node, env) {
1245
+ if (!node.body || !Array.isArray(node.params)) {
1246
+ throw new RuntimeError(
1247
+ 'Invalid function expression',
1248
+ node,
1249
+ this.source,
1250
+ env
1251
+ );
1252
+ }
1253
+
1254
+ const evaluator = this;
1255
+
1256
+ const fn = async function (...args) {
1257
+ const localEnv = new Environment(env);
1258
+
1259
+ for (let i = 0; i < node.params.length; i++) {
1260
+ const param = node.params[i];
1261
+ const paramName = typeof param === 'string' ? param : param.name;
1262
+ localEnv.define(paramName, args[i]);
1263
+ }
1264
+
1265
+ try {
1266
+ const result = await evaluator.evaluate(node.body, localEnv);
1267
+ return result === undefined ? null : result;
1268
+ } catch (e) {
1269
+ if (e instanceof ReturnValue) return e.value === undefined ? null : e.value;
1270
+ throw e;
1271
+ }
1272
+ };
1273
+ fn.params = node.params;
1274
+ fn.body = node.body;
1275
+ fn.env = env;
1276
+
1277
+ return fn;
1278
+ }
1189
1279
 
1190
1280
  evalFunctionDeclaration(node, env) {
1191
1281
  try {
@@ -1273,7 +1363,6 @@ async evalCall(node, env) {
1273
1363
  );
1274
1364
  }
1275
1365
  }
1276
-
1277
1366
  async evalIndex(node, env) {
1278
1367
  try {
1279
1368
  const obj = await this.evaluate(node.object, env);
@@ -1306,7 +1395,6 @@ async evalIndex(node, env) {
1306
1395
  );
1307
1396
  }
1308
1397
  }
1309
-
1310
1398
  async evalObject(node, env) {
1311
1399
  try {
1312
1400
  const out = {};
package/src/parser.js CHANGED
@@ -1292,33 +1292,100 @@ postfix() {
1292
1292
  while (true) {
1293
1293
  const t = this.current;
1294
1294
 
1295
- if (t.type === 'LBRACKET') {
1296
- const startLine = t.line;
1297
- const startCol = t.column;
1298
- this.eat('LBRACKET');
1295
+ if (t.type === 'LBRACKET') {
1296
+ const startLine = t.line;
1297
+ const startCol = t.column;
1298
+ this.eat('LBRACKET');
1299
+
1300
+ let start = null;
1301
+ let end = null;
1302
+ let step = null;
1299
1303
 
1300
- const index = this.expression();
1304
+ if (this.current.type === 'COLON') {
1305
+ this.eat('COLON');
1306
+
1307
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
1308
+ end = this.expression();
1309
+ }
1310
+
1311
+ if (this.current.type === 'COLON') {
1312
+ this.eat('COLON');
1313
+ if (this.current.type !== 'RBRACKET') {
1314
+ step = this.expression();
1315
+ }
1316
+ }
1317
+
1318
+ if (this.current.type !== 'RBRACKET') {
1319
+ throw new ParseError(
1320
+ "Expected ']' after slice",
1321
+ this.current,
1322
+ this.source
1323
+ );
1324
+ }
1325
+
1326
+ this.eat('RBRACKET');
1327
+
1328
+ node = {
1329
+ type: 'SliceExpression',
1330
+ object: node,
1331
+ start,
1332
+ end,
1333
+ step,
1334
+ line: startLine,
1335
+ column: startCol
1336
+ };
1337
+
1338
+ } else {
1339
+ start = this.expression();
1340
+
1341
+ if (this.current.type === 'COLON') {
1342
+ this.eat('COLON');
1343
+
1344
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
1345
+ end = this.expression();
1346
+ }
1347
+
1348
+ if (this.current.type === 'COLON') {
1349
+ this.eat('COLON');
1350
+ if (this.current.type !== 'RBRACKET') {
1351
+ step = this.expression();
1352
+ }
1353
+ }
1301
1354
 
1302
1355
  if (this.current.type !== 'RBRACKET') {
1303
1356
  throw new ParseError(
1304
- "Expected ']' after index expression",
1357
+ "Expected ']' after slice",
1305
1358
  this.current,
1306
- this.source,
1307
- "Index access must be closed, e.g. arr[0]"
1359
+ this.source
1308
1360
  );
1309
1361
  }
1310
1362
 
1311
1363
  this.eat('RBRACKET');
1312
1364
 
1365
+ node = {
1366
+ type: 'SliceExpression',
1367
+ object: node,
1368
+ start,
1369
+ end,
1370
+ step,
1371
+ line: startLine,
1372
+ column: startCol
1373
+ };
1374
+
1375
+ } else {
1376
+ this.eat('RBRACKET');
1313
1377
  node = {
1314
1378
  type: 'IndexExpression',
1315
1379
  object: node,
1316
- indexer: index,
1380
+ indexer: start,
1317
1381
  line: startLine,
1318
1382
  column: startCol
1319
1383
  };
1320
- continue;
1321
1384
  }
1385
+ }
1386
+ }
1387
+
1388
+
1322
1389
 
1323
1390
  if (t.type === 'LPAREN') {
1324
1391
  const startLine = t.line;
@@ -1450,6 +1517,7 @@ arrowFunction(params) {
1450
1517
  };
1451
1518
  }
1452
1519
 
1520
+
1453
1521
  primary() {
1454
1522
  const t = this.current;
1455
1523
 
@@ -1521,7 +1589,6 @@ primary() {
1521
1589
  return { type: 'NewExpression', callee, arguments: args, line: t.line, column: t.column };
1522
1590
  }
1523
1591
 
1524
- // ---- ask(...) function call ----
1525
1592
  if (t.type === 'ASK') {
1526
1593
  this.eat('ASK');
1527
1594
 
@@ -1562,6 +1629,69 @@ primary() {
1562
1629
  column: t.column
1563
1630
  };
1564
1631
  }
1632
+ if (t.type === 'FUNC') {
1633
+ const funcToken = t;
1634
+ this.eat('FUNC');
1635
+
1636
+ let params = [];
1637
+ if (this.current.type === 'LPAREN') {
1638
+ this.eat('LPAREN');
1639
+
1640
+ if (this.current.type !== 'RPAREN') {
1641
+ if (this.current.type !== 'IDENTIFIER') {
1642
+ throw new ParseError(
1643
+ "Expected parameter name",
1644
+ this.current,
1645
+ this.source
1646
+ );
1647
+ }
1648
+
1649
+ params.push(this.current.value);
1650
+ this.eat('IDENTIFIER');
1651
+
1652
+ while (this.current.type === 'COMMA') {
1653
+ this.eat('COMMA');
1654
+ if (this.current.type !== 'IDENTIFIER') {
1655
+ throw new ParseError(
1656
+ "Expected parameter name",
1657
+ this.current,
1658
+ this.source
1659
+ );
1660
+ }
1661
+ params.push(this.current.value);
1662
+ this.eat('IDENTIFIER');
1663
+ }
1664
+ }
1665
+
1666
+ if (this.current.type !== 'RPAREN') {
1667
+ throw new ParseError(
1668
+ "Expected ')' after function parameters",
1669
+ this.current,
1670
+ this.source
1671
+ );
1672
+ }
1673
+
1674
+ this.eat('RPAREN');
1675
+ }
1676
+
1677
+ if (this.current.type !== 'LBRACE') {
1678
+ throw new ParseError(
1679
+ "Expected '{' to start function body",
1680
+ this.current,
1681
+ this.source
1682
+ );
1683
+ }
1684
+
1685
+ const body = this.block();
1686
+
1687
+ return {
1688
+ type: 'FunctionExpression',
1689
+ params,
1690
+ body,
1691
+ line: funcToken.line,
1692
+ column: funcToken.column
1693
+ };
1694
+ }
1565
1695
 
1566
1696
  if (t.type === 'IDENTIFIER') {
1567
1697
  const name = t.value;
@@ -1698,7 +1828,7 @@ primary() {
1698
1828
  t,
1699
1829
  this.source
1700
1830
  );
1701
- }
1831
+ }
1702
1832
 
1703
1833
  }
1704
1834
 
package/src/starlight.js CHANGED
@@ -12,7 +12,7 @@ const Lexer = require('./lexer');
12
12
  const Parser = require('./parser');
13
13
  const Evaluator = require('./evaluator');
14
14
 
15
- const VERSION = '1.1.15';
15
+ const VERSION = '1.1.16';
16
16
 
17
17
  const COLOR = {
18
18
  reset: '\x1b[0m',