starlight-cli 1.1.17 → 1.1.18

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
@@ -10466,6 +10466,8 @@ this.global.define('toStr', arg => String(arg));
10466
10466
  this.global.define('isNaN', arg => {
10467
10467
  return typeof arg !== 'number' || Number.isNaN(arg);
10468
10468
  });
10469
+
10470
+
10469
10471
  this.global.define('random', (min, max) => {
10470
10472
  if (max === undefined) {
10471
10473
  // Only one argument → random between 0 and min
@@ -10568,6 +10570,9 @@ this.global.define('encodeURLComponent', arg => {
10568
10570
 
10569
10571
  return acc;
10570
10572
  });
10573
+ this.global.define('hasOwn', (obj, prop) => {
10574
+ return Object.prototype.hasOwnProperty.call(obj, prop)
10575
+ });
10571
10576
 
10572
10577
  this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
10573
10578
  this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
@@ -11122,8 +11127,6 @@ async evalProgram(node, env) {
11122
11127
  async evalSlice(node, env) {
11123
11128
  try {
11124
11129
  const arr = await this.evaluate(node.object, env);
11125
- const start = node.start ? await this.evaluate(node.start, env) : 0;
11126
- const end = node.end ? await this.evaluate(node.end, env) : arr.length;
11127
11130
 
11128
11131
  if (!Array.isArray(arr)) {
11129
11132
  throw new RuntimeError(
@@ -11134,10 +11137,37 @@ async evalSlice(node, env) {
11134
11137
  );
11135
11138
  }
11136
11139
 
11137
- const s = start < 0 ? Math.max(arr.length + start, 0) : Math.min(start, arr.length);
11138
- const e = end < 0 ? Math.max(arr.length + end, 0) : Math.min(end, arr.length);
11140
+ let start = node.start ? await this.evaluate(node.start, env) : 0;
11141
+ let end = node.end ? await this.evaluate(node.end, env) : arr.length;
11142
+ let step = node.step ? await this.evaluate(node.step, env) : 1;
11143
+
11144
+ if (step === 0) {
11145
+ throw new RuntimeError(
11146
+ 'Slice step cannot be zero',
11147
+ node,
11148
+ this.source,
11149
+ env
11150
+ );
11151
+ }
11152
+ start = start < 0 ? arr.length + start : start;
11153
+ end = end < 0 ? arr.length + end : end;
11154
+
11155
+ start = Math.min(Math.max(start, 0), arr.length);
11156
+ end = Math.min(Math.max(end, 0), arr.length);
11157
+
11158
+ const result = [];
11159
+
11160
+ if (step > 0) {
11161
+ for (let i = start; i < end; i += step) {
11162
+ result.push(arr[i]);
11163
+ }
11164
+ } else {
11165
+ for (let i = start; i > end; i += step) {
11166
+ result.push(arr[i]);
11167
+ }
11168
+ }
11139
11169
 
11140
- return arr.slice(s, e);
11170
+ return result;
11141
11171
 
11142
11172
  } catch (err) {
11143
11173
  if (err instanceof RuntimeError) throw err;
@@ -11150,6 +11180,7 @@ async evalSlice(node, env) {
11150
11180
  }
11151
11181
  }
11152
11182
 
11183
+
11153
11184
  async evalStartStatement(node, env) {
11154
11185
  try {
11155
11186
  const value = await this.evaluate(node.discriminant, env);
@@ -11674,12 +11705,49 @@ async evalLogical(node, env) {
11674
11705
 
11675
11706
  async evalUnary(node, env) {
11676
11707
  try {
11708
+ // Handle prefix update operators
11709
+ if (node.type === 'UpdateExpression' && node.prefix) {
11710
+ if (node.argument.type !== 'Identifier') {
11711
+ throw new RuntimeError(
11712
+ `Invalid operand for update operator: ${node.operator}`,
11713
+ node,
11714
+ this.source,
11715
+ env
11716
+ );
11717
+ }
11718
+
11719
+ const varName = node.argument.name;
11720
+ let currentVal = await env.get(varName); // assuming env.get returns the variable value
11721
+
11722
+ switch (node.operator) {
11723
+ case 'PLUSPLUS':
11724
+ currentVal++;
11725
+ await env.set(varName, currentVal);
11726
+ return currentVal;
11727
+ case 'MINUSMINUS':
11728
+ currentVal--;
11729
+ await env.set(varName, currentVal);
11730
+ return currentVal;
11731
+ default:
11732
+ throw new RuntimeError(
11733
+ `Unknown update operator: ${node.operator}`,
11734
+ node,
11735
+ this.source,
11736
+ env
11737
+ );
11738
+ }
11739
+ }
11740
+
11741
+ // Evaluate the argument first
11677
11742
  const val = await this.evaluate(node.argument, env);
11678
11743
 
11679
11744
  switch (node.operator) {
11680
- case 'NOT': return !val;
11681
- case 'MINUS': return -val;
11682
- case 'PLUS': return +val;
11745
+ case 'NOT':
11746
+ return !val;
11747
+ case 'MINUS':
11748
+ return -val;
11749
+ case 'PLUS':
11750
+ return +val;
11683
11751
  default:
11684
11752
  throw new RuntimeError(
11685
11753
  `Unknown unary operator: ${node.operator}`,
@@ -11688,7 +11756,6 @@ async evalUnary(node, env) {
11688
11756
  env
11689
11757
  );
11690
11758
  }
11691
-
11692
11759
  } catch (e) {
11693
11760
  if (e instanceof RuntimeError) throw e;
11694
11761
  throw new RuntimeError(
@@ -11700,6 +11767,51 @@ async evalUnary(node, env) {
11700
11767
  }
11701
11768
  }
11702
11769
 
11770
+ // Handle postfix updates (x++ or x--)
11771
+ async evalPostfixUpdate(node, env) {
11772
+ if (node.type !== 'UpdateExpression' || node.prefix) {
11773
+ throw new RuntimeError(
11774
+ `Invalid postfix update node`,
11775
+ node,
11776
+ this.source,
11777
+ env
11778
+ );
11779
+ }
11780
+
11781
+ if (node.argument.type !== 'Identifier') {
11782
+ throw new RuntimeError(
11783
+ `Invalid operand for postfix update operator: ${node.operator}`,
11784
+ node,
11785
+ this.source,
11786
+ env
11787
+ );
11788
+ }
11789
+
11790
+ const varName = node.argument.name;
11791
+ let currentVal = await env.get(varName);
11792
+
11793
+ let returnVal = currentVal;
11794
+
11795
+ switch (node.operator) {
11796
+ case 'PLUSPLUS':
11797
+ currentVal++;
11798
+ await env.set(varName, currentVal);
11799
+ return returnVal; // return original value
11800
+ case 'MINUSMINUS':
11801
+ currentVal--;
11802
+ await env.set(varName, currentVal);
11803
+ return returnVal; // return original value
11804
+ default:
11805
+ throw new RuntimeError(
11806
+ `Unknown postfix operator: ${node.operator}`,
11807
+ node,
11808
+ this.source,
11809
+ env
11810
+ );
11811
+ }
11812
+ }
11813
+
11814
+
11703
11815
  async evalIf(node, env) {
11704
11816
  let test = await this.evaluate(node.test, env);
11705
11817
  test = !!test; // coerce to boolean
@@ -11978,7 +12090,8 @@ async evalIndex(node, env) {
11978
12090
  env
11979
12091
  );
11980
12092
  }
11981
- }
12093
+ }
12094
+
11982
12095
  async evalObject(node, env) {
11983
12096
  try {
11984
12097
  const out = {};
@@ -13617,192 +13730,111 @@ unary() {
13617
13730
 
13618
13731
  return this.postfix();
13619
13732
  }
13733
+
13620
13734
  postfix() {
13621
13735
  let node = this.primary();
13622
13736
 
13623
13737
  while (true) {
13624
13738
  const t = this.current;
13625
13739
 
13626
- if (t.type === 'LBRACKET') {
13627
- const startLine = t.line;
13628
- const startCol = t.column;
13629
- this.eat('LBRACKET');
13630
-
13631
- let start = null;
13632
- let end = null;
13633
- let step = null;
13634
-
13635
- if (this.current.type === 'COLON') {
13636
- this.eat('COLON');
13637
-
13638
- if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13639
- end = this.expression();
13640
- }
13641
-
13642
- if (this.current.type === 'COLON') {
13643
- this.eat('COLON');
13644
- if (this.current.type !== 'RBRACKET') {
13645
- step = this.expression();
13646
- }
13647
- }
13648
-
13649
- if (this.current.type !== 'RBRACKET') {
13650
- throw new ParseError(
13651
- "Expected ']' after slice",
13652
- this.current,
13653
- this.source
13654
- );
13655
- }
13656
-
13657
- this.eat('RBRACKET');
13658
-
13659
- node = {
13660
- type: 'SliceExpression',
13661
- object: node,
13662
- start,
13663
- end,
13664
- step,
13665
- line: startLine,
13666
- column: startCol
13667
- };
13668
-
13669
- } else {
13670
- start = this.expression();
13671
-
13672
- if (this.current.type === 'COLON') {
13673
- this.eat('COLON');
13740
+ // Handle indexing / slicing
13741
+ if (t.type === 'LBRACKET') {
13742
+ const startLine = t.line;
13743
+ const startCol = t.column;
13744
+ this.eat('LBRACKET');
13674
13745
 
13675
- if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13676
- end = this.expression();
13677
- }
13746
+ let start = null;
13747
+ let end = null;
13748
+ let step = null;
13678
13749
 
13750
+ // Slice: [:end:step] or [start:end:step]
13679
13751
  if (this.current.type === 'COLON') {
13680
13752
  this.eat('COLON');
13753
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13754
+ end = this.expression();
13755
+ }
13756
+ if (this.current.type === 'COLON') {
13757
+ this.eat('COLON');
13758
+ if (this.current.type !== 'RBRACKET') {
13759
+ step = this.expression();
13760
+ }
13761
+ }
13681
13762
  if (this.current.type !== 'RBRACKET') {
13682
- step = this.expression();
13763
+ throw new ParseError("Expected ']' after slice", this.current, this.source);
13683
13764
  }
13684
- }
13685
-
13686
- if (this.current.type !== 'RBRACKET') {
13687
- throw new ParseError(
13688
- "Expected ']' after slice",
13689
- this.current,
13690
- this.source
13691
- );
13692
- }
13693
-
13694
- this.eat('RBRACKET');
13765
+ this.eat('RBRACKET');
13695
13766
 
13696
- node = {
13697
- type: 'SliceExpression',
13698
- object: node,
13699
- start,
13700
- end,
13701
- step,
13702
- line: startLine,
13703
- column: startCol
13704
- };
13767
+ node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
13768
+ } else {
13769
+ // Normal index or slice starting with an expression
13770
+ start = this.expression();
13771
+ if (this.current.type === 'COLON') {
13772
+ this.eat('COLON');
13773
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13774
+ end = this.expression();
13775
+ }
13776
+ if (this.current.type === 'COLON') {
13777
+ this.eat('COLON');
13778
+ if (this.current.type !== 'RBRACKET') {
13779
+ step = this.expression();
13780
+ }
13781
+ }
13782
+ if (this.current.type !== 'RBRACKET') {
13783
+ throw new ParseError("Expected ']' after slice", this.current, this.source);
13784
+ }
13785
+ this.eat('RBRACKET');
13705
13786
 
13706
- } else {
13707
- this.eat('RBRACKET');
13708
- node = {
13709
- type: 'IndexExpression',
13710
- object: node,
13711
- indexer: start,
13712
- line: startLine,
13713
- column: startCol
13714
- };
13787
+ node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
13788
+ } else {
13789
+ this.eat('RBRACKET');
13790
+ node = { type: 'IndexExpression', object: node, indexer: start, line: startLine, column: startCol };
13791
+ }
13792
+ }
13793
+ continue; // continue to allow . or () after []
13715
13794
  }
13716
- }
13717
- }
13718
-
13719
-
13720
13795
 
13796
+ // Handle function call
13721
13797
  if (t.type === 'LPAREN') {
13722
13798
  const startLine = t.line;
13723
13799
  const startCol = t.column;
13724
13800
  this.eat('LPAREN');
13725
13801
 
13726
13802
  const args = [];
13727
-
13728
13803
  while (this.current.type !== 'RPAREN') {
13729
13804
  if (this.current.type === 'EOF') {
13730
- throw new ParseError(
13731
- "Unterminated function call",
13732
- this.current,
13733
- this.source,
13734
- "Did you forget to close ')'?"
13735
- );
13805
+ throw new ParseError("Unterminated function call", this.current, this.source, "Did you forget to close ')'?");
13736
13806
  }
13737
-
13738
13807
  args.push(this.expression());
13739
-
13740
- if (this.current.type === 'COMMA') {
13741
- this.eat('COMMA');
13742
- } else {
13743
- break;
13744
- }
13745
- }
13746
-
13747
- if (this.current.type !== 'RPAREN') {
13748
- throw new ParseError(
13749
- "Expected ')' after function arguments",
13750
- this.current,
13751
- this.source,
13752
- "Function calls must be closed with ')'"
13753
- );
13808
+ if (this.current.type === 'COMMA') this.eat('COMMA');
13754
13809
  }
13755
13810
 
13756
13811
  this.eat('RPAREN');
13757
13812
 
13758
- node = {
13759
- type: 'CallExpression',
13760
- callee: node,
13761
- arguments: args,
13762
- line: startLine,
13763
- column: startCol
13764
- };
13813
+ node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
13765
13814
  continue;
13766
13815
  }
13767
13816
 
13817
+ // Handle member access
13768
13818
  if (t.type === 'DOT') {
13769
13819
  const startLine = t.line;
13770
13820
  const startCol = t.column;
13771
13821
  this.eat('DOT');
13772
13822
 
13773
13823
  if (this.current.type !== 'IDENTIFIER') {
13774
- throw new ParseError(
13775
- "Expected property name after '.'",
13776
- this.current,
13777
- this.source,
13778
- "Member access requires a property name, e.g. obj.value"
13779
- );
13824
+ throw new ParseError("Expected property name after '.'", this.current, this.source, "Member access requires a property name, e.g. obj.value");
13780
13825
  }
13781
13826
 
13782
13827
  const property = this.current.value;
13783
13828
  this.eat('IDENTIFIER');
13784
13829
 
13785
- node = {
13786
- type: 'MemberExpression',
13787
- object: node,
13788
- property,
13789
- line: startLine,
13790
- column: startCol
13791
- };
13830
+ node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
13792
13831
  continue;
13793
13832
  }
13794
13833
 
13834
+ // Handle postfix update operators
13795
13835
  if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
13796
13836
  this.eat(t.type);
13797
-
13798
- node = {
13799
- type: 'UpdateExpression',
13800
- operator: t.type,
13801
- argument: node,
13802
- prefix: false,
13803
- line: t.line,
13804
- column: t.column
13805
- };
13837
+ node = { type: 'UpdateExpression', operator: t.type, argument: node, prefix: false, line: t.line, column: t.column };
13806
13838
  continue;
13807
13839
  }
13808
13840
 
@@ -13812,6 +13844,7 @@ postfix() {
13812
13844
  return node;
13813
13845
  }
13814
13846
 
13847
+
13815
13848
  arrowFunction(params) {
13816
13849
  const t = this.current;
13817
13850
  this.eat('ARROW');
@@ -14162,8 +14195,8 @@ if (t.type === 'FUNC') {
14162
14195
  }
14163
14196
 
14164
14197
  }
14165
-
14166
- module.exports = Parser;
14198
+ module.exports = Parser;
14199
+
14167
14200
 
14168
14201
  /***/ }),
14169
14202
 
@@ -14343,7 +14376,7 @@ const Lexer = __nccwpck_require__(211);
14343
14376
  const Parser = __nccwpck_require__(222);
14344
14377
  const Evaluator = __nccwpck_require__(112);
14345
14378
 
14346
- const VERSION = '1.1.17';
14379
+ const VERSION = '1.1.18';
14347
14380
 
14348
14381
  const COLOR = {
14349
14382
  reset: '\x1b[0m',
@@ -14503,7 +14536,7 @@ Usage:
14503
14536
 
14504
14537
  if (args[0] === '-v' || args[0] === '--version') {
14505
14538
  console.log(`${COLOR.bold}Starlight CLI v${VERSION}${COLOR.reset}`);
14506
- console.log(`${COLOR.magenta}Developed by Macedon${COLOR.reset}`);
14539
+ console.log(`${COLOR.magenta}Developed by Dominex Macedon${COLOR.reset}`);
14507
14540
  process.exit(0);
14508
14541
  }
14509
14542
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.1.17",
3
+ "version": "1.1.18",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/evaluator.js CHANGED
@@ -256,6 +256,8 @@ this.global.define('toStr', arg => String(arg));
256
256
  this.global.define('isNaN', arg => {
257
257
  return typeof arg !== 'number' || Number.isNaN(arg);
258
258
  });
259
+
260
+
259
261
  this.global.define('random', (min, max) => {
260
262
  if (max === undefined) {
261
263
  // Only one argument → random between 0 and min
@@ -358,6 +360,9 @@ this.global.define('encodeURLComponent', arg => {
358
360
 
359
361
  return acc;
360
362
  });
363
+ this.global.define('hasOwn', (obj, prop) => {
364
+ return Object.prototype.hasOwnProperty.call(obj, prop)
365
+ });
361
366
 
362
367
  this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
363
368
  this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
@@ -912,8 +917,6 @@ async evalProgram(node, env) {
912
917
  async evalSlice(node, env) {
913
918
  try {
914
919
  const arr = await this.evaluate(node.object, env);
915
- const start = node.start ? await this.evaluate(node.start, env) : 0;
916
- const end = node.end ? await this.evaluate(node.end, env) : arr.length;
917
920
 
918
921
  if (!Array.isArray(arr)) {
919
922
  throw new RuntimeError(
@@ -924,10 +927,37 @@ async evalSlice(node, env) {
924
927
  );
925
928
  }
926
929
 
927
- const s = start < 0 ? Math.max(arr.length + start, 0) : Math.min(start, arr.length);
928
- const e = end < 0 ? Math.max(arr.length + end, 0) : Math.min(end, arr.length);
930
+ let start = node.start ? await this.evaluate(node.start, env) : 0;
931
+ let end = node.end ? await this.evaluate(node.end, env) : arr.length;
932
+ let step = node.step ? await this.evaluate(node.step, env) : 1;
933
+
934
+ if (step === 0) {
935
+ throw new RuntimeError(
936
+ 'Slice step cannot be zero',
937
+ node,
938
+ this.source,
939
+ env
940
+ );
941
+ }
942
+ start = start < 0 ? arr.length + start : start;
943
+ end = end < 0 ? arr.length + end : end;
944
+
945
+ start = Math.min(Math.max(start, 0), arr.length);
946
+ end = Math.min(Math.max(end, 0), arr.length);
947
+
948
+ const result = [];
949
+
950
+ if (step > 0) {
951
+ for (let i = start; i < end; i += step) {
952
+ result.push(arr[i]);
953
+ }
954
+ } else {
955
+ for (let i = start; i > end; i += step) {
956
+ result.push(arr[i]);
957
+ }
958
+ }
929
959
 
930
- return arr.slice(s, e);
960
+ return result;
931
961
 
932
962
  } catch (err) {
933
963
  if (err instanceof RuntimeError) throw err;
@@ -940,6 +970,7 @@ async evalSlice(node, env) {
940
970
  }
941
971
  }
942
972
 
973
+
943
974
  async evalStartStatement(node, env) {
944
975
  try {
945
976
  const value = await this.evaluate(node.discriminant, env);
@@ -1464,12 +1495,49 @@ async evalLogical(node, env) {
1464
1495
 
1465
1496
  async evalUnary(node, env) {
1466
1497
  try {
1498
+ // Handle prefix update operators
1499
+ if (node.type === 'UpdateExpression' && node.prefix) {
1500
+ if (node.argument.type !== 'Identifier') {
1501
+ throw new RuntimeError(
1502
+ `Invalid operand for update operator: ${node.operator}`,
1503
+ node,
1504
+ this.source,
1505
+ env
1506
+ );
1507
+ }
1508
+
1509
+ const varName = node.argument.name;
1510
+ let currentVal = await env.get(varName); // assuming env.get returns the variable value
1511
+
1512
+ switch (node.operator) {
1513
+ case 'PLUSPLUS':
1514
+ currentVal++;
1515
+ await env.set(varName, currentVal);
1516
+ return currentVal;
1517
+ case 'MINUSMINUS':
1518
+ currentVal--;
1519
+ await env.set(varName, currentVal);
1520
+ return currentVal;
1521
+ default:
1522
+ throw new RuntimeError(
1523
+ `Unknown update operator: ${node.operator}`,
1524
+ node,
1525
+ this.source,
1526
+ env
1527
+ );
1528
+ }
1529
+ }
1530
+
1531
+ // Evaluate the argument first
1467
1532
  const val = await this.evaluate(node.argument, env);
1468
1533
 
1469
1534
  switch (node.operator) {
1470
- case 'NOT': return !val;
1471
- case 'MINUS': return -val;
1472
- case 'PLUS': return +val;
1535
+ case 'NOT':
1536
+ return !val;
1537
+ case 'MINUS':
1538
+ return -val;
1539
+ case 'PLUS':
1540
+ return +val;
1473
1541
  default:
1474
1542
  throw new RuntimeError(
1475
1543
  `Unknown unary operator: ${node.operator}`,
@@ -1478,7 +1546,6 @@ async evalUnary(node, env) {
1478
1546
  env
1479
1547
  );
1480
1548
  }
1481
-
1482
1549
  } catch (e) {
1483
1550
  if (e instanceof RuntimeError) throw e;
1484
1551
  throw new RuntimeError(
@@ -1490,6 +1557,51 @@ async evalUnary(node, env) {
1490
1557
  }
1491
1558
  }
1492
1559
 
1560
+ // Handle postfix updates (x++ or x--)
1561
+ async evalPostfixUpdate(node, env) {
1562
+ if (node.type !== 'UpdateExpression' || node.prefix) {
1563
+ throw new RuntimeError(
1564
+ `Invalid postfix update node`,
1565
+ node,
1566
+ this.source,
1567
+ env
1568
+ );
1569
+ }
1570
+
1571
+ if (node.argument.type !== 'Identifier') {
1572
+ throw new RuntimeError(
1573
+ `Invalid operand for postfix update operator: ${node.operator}`,
1574
+ node,
1575
+ this.source,
1576
+ env
1577
+ );
1578
+ }
1579
+
1580
+ const varName = node.argument.name;
1581
+ let currentVal = await env.get(varName);
1582
+
1583
+ let returnVal = currentVal;
1584
+
1585
+ switch (node.operator) {
1586
+ case 'PLUSPLUS':
1587
+ currentVal++;
1588
+ await env.set(varName, currentVal);
1589
+ return returnVal; // return original value
1590
+ case 'MINUSMINUS':
1591
+ currentVal--;
1592
+ await env.set(varName, currentVal);
1593
+ return returnVal; // return original value
1594
+ default:
1595
+ throw new RuntimeError(
1596
+ `Unknown postfix operator: ${node.operator}`,
1597
+ node,
1598
+ this.source,
1599
+ env
1600
+ );
1601
+ }
1602
+ }
1603
+
1604
+
1493
1605
  async evalIf(node, env) {
1494
1606
  let test = await this.evaluate(node.test, env);
1495
1607
  test = !!test; // coerce to boolean
@@ -1768,7 +1880,8 @@ async evalIndex(node, env) {
1768
1880
  env
1769
1881
  );
1770
1882
  }
1771
- }
1883
+ }
1884
+
1772
1885
  async evalObject(node, env) {
1773
1886
  try {
1774
1887
  const out = {};
package/src/parser.js CHANGED
@@ -1286,192 +1286,111 @@ unary() {
1286
1286
 
1287
1287
  return this.postfix();
1288
1288
  }
1289
+
1289
1290
  postfix() {
1290
1291
  let node = this.primary();
1291
1292
 
1292
1293
  while (true) {
1293
1294
  const t = this.current;
1294
1295
 
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;
1303
-
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');
1296
+ // Handle indexing / slicing
1297
+ if (t.type === 'LBRACKET') {
1298
+ const startLine = t.line;
1299
+ const startCol = t.column;
1300
+ this.eat('LBRACKET');
1343
1301
 
1344
- if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
1345
- end = this.expression();
1346
- }
1302
+ let start = null;
1303
+ let end = null;
1304
+ let step = null;
1347
1305
 
1306
+ // Slice: [:end:step] or [start:end:step]
1348
1307
  if (this.current.type === 'COLON') {
1349
1308
  this.eat('COLON');
1309
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
1310
+ end = this.expression();
1311
+ }
1312
+ if (this.current.type === 'COLON') {
1313
+ this.eat('COLON');
1314
+ if (this.current.type !== 'RBRACKET') {
1315
+ step = this.expression();
1316
+ }
1317
+ }
1350
1318
  if (this.current.type !== 'RBRACKET') {
1351
- step = this.expression();
1319
+ throw new ParseError("Expected ']' after slice", this.current, this.source);
1352
1320
  }
1353
- }
1321
+ this.eat('RBRACKET');
1322
+
1323
+ node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
1324
+ } else {
1325
+ // Normal index or slice starting with an expression
1326
+ start = this.expression();
1327
+ if (this.current.type === 'COLON') {
1328
+ this.eat('COLON');
1329
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
1330
+ end = this.expression();
1331
+ }
1332
+ if (this.current.type === 'COLON') {
1333
+ this.eat('COLON');
1334
+ if (this.current.type !== 'RBRACKET') {
1335
+ step = this.expression();
1336
+ }
1337
+ }
1338
+ if (this.current.type !== 'RBRACKET') {
1339
+ throw new ParseError("Expected ']' after slice", this.current, this.source);
1340
+ }
1341
+ this.eat('RBRACKET');
1354
1342
 
1355
- if (this.current.type !== 'RBRACKET') {
1356
- throw new ParseError(
1357
- "Expected ']' after slice",
1358
- this.current,
1359
- this.source
1360
- );
1343
+ node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
1344
+ } else {
1345
+ this.eat('RBRACKET');
1346
+ node = { type: 'IndexExpression', object: node, indexer: start, line: startLine, column: startCol };
1347
+ }
1361
1348
  }
1362
-
1363
- this.eat('RBRACKET');
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');
1377
- node = {
1378
- type: 'IndexExpression',
1379
- object: node,
1380
- indexer: start,
1381
- line: startLine,
1382
- column: startCol
1383
- };
1349
+ continue; // continue to allow . or () after []
1384
1350
  }
1385
- }
1386
- }
1387
-
1388
-
1389
1351
 
1352
+ // Handle function call
1390
1353
  if (t.type === 'LPAREN') {
1391
1354
  const startLine = t.line;
1392
1355
  const startCol = t.column;
1393
1356
  this.eat('LPAREN');
1394
1357
 
1395
1358
  const args = [];
1396
-
1397
1359
  while (this.current.type !== 'RPAREN') {
1398
1360
  if (this.current.type === 'EOF') {
1399
- throw new ParseError(
1400
- "Unterminated function call",
1401
- this.current,
1402
- this.source,
1403
- "Did you forget to close ')'?"
1404
- );
1361
+ throw new ParseError("Unterminated function call", this.current, this.source, "Did you forget to close ')'?");
1405
1362
  }
1406
-
1407
1363
  args.push(this.expression());
1408
-
1409
- if (this.current.type === 'COMMA') {
1410
- this.eat('COMMA');
1411
- } else {
1412
- break;
1413
- }
1414
- }
1415
-
1416
- if (this.current.type !== 'RPAREN') {
1417
- throw new ParseError(
1418
- "Expected ')' after function arguments",
1419
- this.current,
1420
- this.source,
1421
- "Function calls must be closed with ')'"
1422
- );
1364
+ if (this.current.type === 'COMMA') this.eat('COMMA');
1423
1365
  }
1424
1366
 
1425
1367
  this.eat('RPAREN');
1426
1368
 
1427
- node = {
1428
- type: 'CallExpression',
1429
- callee: node,
1430
- arguments: args,
1431
- line: startLine,
1432
- column: startCol
1433
- };
1369
+ node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
1434
1370
  continue;
1435
1371
  }
1436
1372
 
1373
+ // Handle member access
1437
1374
  if (t.type === 'DOT') {
1438
1375
  const startLine = t.line;
1439
1376
  const startCol = t.column;
1440
1377
  this.eat('DOT');
1441
1378
 
1442
1379
  if (this.current.type !== 'IDENTIFIER') {
1443
- throw new ParseError(
1444
- "Expected property name after '.'",
1445
- this.current,
1446
- this.source,
1447
- "Member access requires a property name, e.g. obj.value"
1448
- );
1380
+ throw new ParseError("Expected property name after '.'", this.current, this.source, "Member access requires a property name, e.g. obj.value");
1449
1381
  }
1450
1382
 
1451
1383
  const property = this.current.value;
1452
1384
  this.eat('IDENTIFIER');
1453
1385
 
1454
- node = {
1455
- type: 'MemberExpression',
1456
- object: node,
1457
- property,
1458
- line: startLine,
1459
- column: startCol
1460
- };
1386
+ node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
1461
1387
  continue;
1462
1388
  }
1463
1389
 
1390
+ // Handle postfix update operators
1464
1391
  if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
1465
1392
  this.eat(t.type);
1466
-
1467
- node = {
1468
- type: 'UpdateExpression',
1469
- operator: t.type,
1470
- argument: node,
1471
- prefix: false,
1472
- line: t.line,
1473
- column: t.column
1474
- };
1393
+ node = { type: 'UpdateExpression', operator: t.type, argument: node, prefix: false, line: t.line, column: t.column };
1475
1394
  continue;
1476
1395
  }
1477
1396
 
@@ -1481,6 +1400,7 @@ postfix() {
1481
1400
  return node;
1482
1401
  }
1483
1402
 
1403
+
1484
1404
  arrowFunction(params) {
1485
1405
  const t = this.current;
1486
1406
  this.eat('ARROW');
@@ -1831,5 +1751,4 @@ if (t.type === 'FUNC') {
1831
1751
  }
1832
1752
 
1833
1753
  }
1834
-
1835
- module.exports = Parser;
1754
+ module.exports = Parser;
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.17';
15
+ const VERSION = '1.1.18';
16
16
 
17
17
  const COLOR = {
18
18
  reset: '\x1b[0m',
@@ -172,7 +172,7 @@ Usage:
172
172
 
173
173
  if (args[0] === '-v' || args[0] === '--version') {
174
174
  console.log(`${COLOR.bold}Starlight CLI v${VERSION}${COLOR.reset}`);
175
- console.log(`${COLOR.magenta}Developed by Macedon${COLOR.reset}`);
175
+ console.log(`${COLOR.magenta}Developed by Dominex Macedon${COLOR.reset}`);
176
176
  process.exit(0);
177
177
  }
178
178