starlight-cli 1.1.17 → 1.1.19

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
@@ -10515,7 +10517,9 @@ this.global.define('encodeURLComponent', arg => {
10515
10517
  if (arg === null || arg === undefined) return '';
10516
10518
  return encodeURIComponent(String(arg));
10517
10519
  });
10518
-
10520
+ this.global.define("Date", Date);
10521
+ this.global.define("Math", Math);
10522
+ this.global.define("String", String);
10519
10523
  this.global.define('filter', async (array, fn) => {
10520
10524
  if (!Array.isArray(array)) {
10521
10525
  throw new RuntimeError('filter() expects an array', null, evaluator.source);
@@ -10568,6 +10572,9 @@ this.global.define('encodeURLComponent', arg => {
10568
10572
 
10569
10573
  return acc;
10570
10574
  });
10575
+ this.global.define('hasOwn', (obj, prop) => {
10576
+ return Object.prototype.hasOwnProperty.call(obj, prop)
10577
+ });
10571
10578
 
10572
10579
  this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
10573
10580
  this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
@@ -11122,8 +11129,6 @@ async evalProgram(node, env) {
11122
11129
  async evalSlice(node, env) {
11123
11130
  try {
11124
11131
  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
11132
 
11128
11133
  if (!Array.isArray(arr)) {
11129
11134
  throw new RuntimeError(
@@ -11134,10 +11139,37 @@ async evalSlice(node, env) {
11134
11139
  );
11135
11140
  }
11136
11141
 
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);
11142
+ let start = node.start ? await this.evaluate(node.start, env) : 0;
11143
+ let end = node.end ? await this.evaluate(node.end, env) : arr.length;
11144
+ let step = node.step ? await this.evaluate(node.step, env) : 1;
11139
11145
 
11140
- return arr.slice(s, e);
11146
+ if (step === 0) {
11147
+ throw new RuntimeError(
11148
+ 'Slice step cannot be zero',
11149
+ node,
11150
+ this.source,
11151
+ env
11152
+ );
11153
+ }
11154
+ start = start < 0 ? arr.length + start : start;
11155
+ end = end < 0 ? arr.length + end : end;
11156
+
11157
+ start = Math.min(Math.max(start, 0), arr.length);
11158
+ end = Math.min(Math.max(end, 0), arr.length);
11159
+
11160
+ const result = [];
11161
+
11162
+ if (step > 0) {
11163
+ for (let i = start; i < end; i += step) {
11164
+ result.push(arr[i]);
11165
+ }
11166
+ } else {
11167
+ for (let i = start; i > end; i += step) {
11168
+ result.push(arr[i]);
11169
+ }
11170
+ }
11171
+
11172
+ return result;
11141
11173
 
11142
11174
  } catch (err) {
11143
11175
  if (err instanceof RuntimeError) throw err;
@@ -11150,6 +11182,7 @@ async evalSlice(node, env) {
11150
11182
  }
11151
11183
  }
11152
11184
 
11185
+
11153
11186
  async evalStartStatement(node, env) {
11154
11187
  try {
11155
11188
  const value = await this.evaluate(node.discriminant, env);
@@ -11674,12 +11707,49 @@ async evalLogical(node, env) {
11674
11707
 
11675
11708
  async evalUnary(node, env) {
11676
11709
  try {
11710
+ // Handle prefix update operators
11711
+ if (node.type === 'UpdateExpression' && node.prefix) {
11712
+ if (node.argument.type !== 'Identifier') {
11713
+ throw new RuntimeError(
11714
+ `Invalid operand for update operator: ${node.operator}`,
11715
+ node,
11716
+ this.source,
11717
+ env
11718
+ );
11719
+ }
11720
+
11721
+ const varName = node.argument.name;
11722
+ let currentVal = await env.get(varName); // assuming env.get returns the variable value
11723
+
11724
+ switch (node.operator) {
11725
+ case 'PLUSPLUS':
11726
+ currentVal++;
11727
+ await env.set(varName, currentVal);
11728
+ return currentVal;
11729
+ case 'MINUSMINUS':
11730
+ currentVal--;
11731
+ await env.set(varName, currentVal);
11732
+ return currentVal;
11733
+ default:
11734
+ throw new RuntimeError(
11735
+ `Unknown update operator: ${node.operator}`,
11736
+ node,
11737
+ this.source,
11738
+ env
11739
+ );
11740
+ }
11741
+ }
11742
+
11743
+ // Evaluate the argument first
11677
11744
  const val = await this.evaluate(node.argument, env);
11678
11745
 
11679
11746
  switch (node.operator) {
11680
- case 'NOT': return !val;
11681
- case 'MINUS': return -val;
11682
- case 'PLUS': return +val;
11747
+ case 'NOT':
11748
+ return !val;
11749
+ case 'MINUS':
11750
+ return -val;
11751
+ case 'PLUS':
11752
+ return +val;
11683
11753
  default:
11684
11754
  throw new RuntimeError(
11685
11755
  `Unknown unary operator: ${node.operator}`,
@@ -11688,7 +11758,6 @@ async evalUnary(node, env) {
11688
11758
  env
11689
11759
  );
11690
11760
  }
11691
-
11692
11761
  } catch (e) {
11693
11762
  if (e instanceof RuntimeError) throw e;
11694
11763
  throw new RuntimeError(
@@ -11700,6 +11769,51 @@ async evalUnary(node, env) {
11700
11769
  }
11701
11770
  }
11702
11771
 
11772
+ // Handle postfix updates (x++ or x--)
11773
+ async evalPostfixUpdate(node, env) {
11774
+ if (node.type !== 'UpdateExpression' || node.prefix) {
11775
+ throw new RuntimeError(
11776
+ `Invalid postfix update node`,
11777
+ node,
11778
+ this.source,
11779
+ env
11780
+ );
11781
+ }
11782
+
11783
+ if (node.argument.type !== 'Identifier') {
11784
+ throw new RuntimeError(
11785
+ `Invalid operand for postfix update operator: ${node.operator}`,
11786
+ node,
11787
+ this.source,
11788
+ env
11789
+ );
11790
+ }
11791
+
11792
+ const varName = node.argument.name;
11793
+ let currentVal = await env.get(varName);
11794
+
11795
+ let returnVal = currentVal;
11796
+
11797
+ switch (node.operator) {
11798
+ case 'PLUSPLUS':
11799
+ currentVal++;
11800
+ await env.set(varName, currentVal);
11801
+ return returnVal; // return original value
11802
+ case 'MINUSMINUS':
11803
+ currentVal--;
11804
+ await env.set(varName, currentVal);
11805
+ return returnVal; // return original value
11806
+ default:
11807
+ throw new RuntimeError(
11808
+ `Unknown postfix operator: ${node.operator}`,
11809
+ node,
11810
+ this.source,
11811
+ env
11812
+ );
11813
+ }
11814
+ }
11815
+
11816
+
11703
11817
  async evalIf(node, env) {
11704
11818
  let test = await this.evaluate(node.test, env);
11705
11819
  test = !!test; // coerce to boolean
@@ -11978,7 +12092,8 @@ async evalIndex(node, env) {
11978
12092
  env
11979
12093
  );
11980
12094
  }
11981
- }
12095
+ }
12096
+
11982
12097
  async evalObject(node, env) {
11983
12098
  try {
11984
12099
  const out = {};
@@ -13617,192 +13732,111 @@ unary() {
13617
13732
 
13618
13733
  return this.postfix();
13619
13734
  }
13735
+
13620
13736
  postfix() {
13621
13737
  let node = this.primary();
13622
13738
 
13623
13739
  while (true) {
13624
13740
  const t = this.current;
13625
13741
 
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');
13742
+ // Handle indexing / slicing
13743
+ if (t.type === 'LBRACKET') {
13744
+ const startLine = t.line;
13745
+ const startCol = t.column;
13746
+ this.eat('LBRACKET');
13674
13747
 
13675
- if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13676
- end = this.expression();
13677
- }
13748
+ let start = null;
13749
+ let end = null;
13750
+ let step = null;
13678
13751
 
13752
+ // Slice: [:end:step] or [start:end:step]
13679
13753
  if (this.current.type === 'COLON') {
13680
13754
  this.eat('COLON');
13755
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13756
+ end = this.expression();
13757
+ }
13758
+ if (this.current.type === 'COLON') {
13759
+ this.eat('COLON');
13760
+ if (this.current.type !== 'RBRACKET') {
13761
+ step = this.expression();
13762
+ }
13763
+ }
13681
13764
  if (this.current.type !== 'RBRACKET') {
13682
- step = this.expression();
13765
+ throw new ParseError("Expected ']' after slice", this.current, this.source);
13683
13766
  }
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');
13767
+ this.eat('RBRACKET');
13695
13768
 
13696
- node = {
13697
- type: 'SliceExpression',
13698
- object: node,
13699
- start,
13700
- end,
13701
- step,
13702
- line: startLine,
13703
- column: startCol
13704
- };
13769
+ node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
13770
+ } else {
13771
+ // Normal index or slice starting with an expression
13772
+ start = this.expression();
13773
+ if (this.current.type === 'COLON') {
13774
+ this.eat('COLON');
13775
+ if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
13776
+ end = this.expression();
13777
+ }
13778
+ if (this.current.type === 'COLON') {
13779
+ this.eat('COLON');
13780
+ if (this.current.type !== 'RBRACKET') {
13781
+ step = this.expression();
13782
+ }
13783
+ }
13784
+ if (this.current.type !== 'RBRACKET') {
13785
+ throw new ParseError("Expected ']' after slice", this.current, this.source);
13786
+ }
13787
+ this.eat('RBRACKET');
13705
13788
 
13706
- } else {
13707
- this.eat('RBRACKET');
13708
- node = {
13709
- type: 'IndexExpression',
13710
- object: node,
13711
- indexer: start,
13712
- line: startLine,
13713
- column: startCol
13714
- };
13789
+ node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
13790
+ } else {
13791
+ this.eat('RBRACKET');
13792
+ node = { type: 'IndexExpression', object: node, indexer: start, line: startLine, column: startCol };
13793
+ }
13794
+ }
13795
+ continue; // continue to allow . or () after []
13715
13796
  }
13716
- }
13717
- }
13718
-
13719
-
13720
13797
 
13798
+ // Handle function call
13721
13799
  if (t.type === 'LPAREN') {
13722
13800
  const startLine = t.line;
13723
13801
  const startCol = t.column;
13724
13802
  this.eat('LPAREN');
13725
13803
 
13726
13804
  const args = [];
13727
-
13728
13805
  while (this.current.type !== 'RPAREN') {
13729
13806
  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
- );
13807
+ throw new ParseError("Unterminated function call", this.current, this.source, "Did you forget to close ')'?");
13736
13808
  }
13737
-
13738
13809
  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
- );
13810
+ if (this.current.type === 'COMMA') this.eat('COMMA');
13754
13811
  }
13755
13812
 
13756
13813
  this.eat('RPAREN');
13757
13814
 
13758
- node = {
13759
- type: 'CallExpression',
13760
- callee: node,
13761
- arguments: args,
13762
- line: startLine,
13763
- column: startCol
13764
- };
13815
+ node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
13765
13816
  continue;
13766
13817
  }
13767
13818
 
13819
+ // Handle member access
13768
13820
  if (t.type === 'DOT') {
13769
13821
  const startLine = t.line;
13770
13822
  const startCol = t.column;
13771
13823
  this.eat('DOT');
13772
13824
 
13773
13825
  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
- );
13826
+ throw new ParseError("Expected property name after '.'", this.current, this.source, "Member access requires a property name, e.g. obj.value");
13780
13827
  }
13781
13828
 
13782
13829
  const property = this.current.value;
13783
13830
  this.eat('IDENTIFIER');
13784
13831
 
13785
- node = {
13786
- type: 'MemberExpression',
13787
- object: node,
13788
- property,
13789
- line: startLine,
13790
- column: startCol
13791
- };
13832
+ node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
13792
13833
  continue;
13793
13834
  }
13794
13835
 
13836
+ // Handle postfix update operators
13795
13837
  if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
13796
13838
  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
- };
13839
+ node = { type: 'UpdateExpression', operator: t.type, argument: node, prefix: false, line: t.line, column: t.column };
13806
13840
  continue;
13807
13841
  }
13808
13842
 
@@ -13812,6 +13846,7 @@ postfix() {
13812
13846
  return node;
13813
13847
  }
13814
13848
 
13849
+
13815
13850
  arrowFunction(params) {
13816
13851
  const t = this.current;
13817
13852
  this.eat('ARROW');
@@ -14162,8 +14197,8 @@ if (t.type === 'FUNC') {
14162
14197
  }
14163
14198
 
14164
14199
  }
14165
-
14166
- module.exports = Parser;
14200
+ module.exports = Parser;
14201
+
14167
14202
 
14168
14203
  /***/ }),
14169
14204
 
@@ -14343,7 +14378,7 @@ const Lexer = __nccwpck_require__(211);
14343
14378
  const Parser = __nccwpck_require__(222);
14344
14379
  const Evaluator = __nccwpck_require__(112);
14345
14380
 
14346
- const VERSION = '1.1.17';
14381
+ const VERSION = '1.1.19';
14347
14382
 
14348
14383
  const COLOR = {
14349
14384
  reset: '\x1b[0m',
@@ -14503,7 +14538,7 @@ Usage:
14503
14538
 
14504
14539
  if (args[0] === '-v' || args[0] === '--version') {
14505
14540
  console.log(`${COLOR.bold}Starlight CLI v${VERSION}${COLOR.reset}`);
14506
- console.log(`${COLOR.magenta}Developed by Macedon${COLOR.reset}`);
14541
+ console.log(`${COLOR.magenta}Developed by Dominex Macedon${COLOR.reset}`);
14507
14542
  process.exit(0);
14508
14543
  }
14509
14544
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.1.17",
3
+ "version": "1.1.19",
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
@@ -305,7 +307,9 @@ this.global.define('encodeURLComponent', arg => {
305
307
  if (arg === null || arg === undefined) return '';
306
308
  return encodeURIComponent(String(arg));
307
309
  });
308
-
310
+ this.global.define("Date", Date);
311
+ this.global.define("Math", Math);
312
+ this.global.define("String", String);
309
313
  this.global.define('filter', async (array, fn) => {
310
314
  if (!Array.isArray(array)) {
311
315
  throw new RuntimeError('filter() expects an array', null, evaluator.source);
@@ -358,6 +362,9 @@ this.global.define('encodeURLComponent', arg => {
358
362
 
359
363
  return acc;
360
364
  });
365
+ this.global.define('hasOwn', (obj, prop) => {
366
+ return Object.prototype.hasOwnProperty.call(obj, prop)
367
+ });
361
368
 
362
369
  this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
363
370
  this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
@@ -912,8 +919,6 @@ async evalProgram(node, env) {
912
919
  async evalSlice(node, env) {
913
920
  try {
914
921
  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
922
 
918
923
  if (!Array.isArray(arr)) {
919
924
  throw new RuntimeError(
@@ -924,10 +929,37 @@ async evalSlice(node, env) {
924
929
  );
925
930
  }
926
931
 
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);
932
+ let start = node.start ? await this.evaluate(node.start, env) : 0;
933
+ let end = node.end ? await this.evaluate(node.end, env) : arr.length;
934
+ let step = node.step ? await this.evaluate(node.step, env) : 1;
935
+
936
+ if (step === 0) {
937
+ throw new RuntimeError(
938
+ 'Slice step cannot be zero',
939
+ node,
940
+ this.source,
941
+ env
942
+ );
943
+ }
944
+ start = start < 0 ? arr.length + start : start;
945
+ end = end < 0 ? arr.length + end : end;
946
+
947
+ start = Math.min(Math.max(start, 0), arr.length);
948
+ end = Math.min(Math.max(end, 0), arr.length);
929
949
 
930
- return arr.slice(s, e);
950
+ const result = [];
951
+
952
+ if (step > 0) {
953
+ for (let i = start; i < end; i += step) {
954
+ result.push(arr[i]);
955
+ }
956
+ } else {
957
+ for (let i = start; i > end; i += step) {
958
+ result.push(arr[i]);
959
+ }
960
+ }
961
+
962
+ return result;
931
963
 
932
964
  } catch (err) {
933
965
  if (err instanceof RuntimeError) throw err;
@@ -940,6 +972,7 @@ async evalSlice(node, env) {
940
972
  }
941
973
  }
942
974
 
975
+
943
976
  async evalStartStatement(node, env) {
944
977
  try {
945
978
  const value = await this.evaluate(node.discriminant, env);
@@ -1464,12 +1497,49 @@ async evalLogical(node, env) {
1464
1497
 
1465
1498
  async evalUnary(node, env) {
1466
1499
  try {
1500
+ // Handle prefix update operators
1501
+ if (node.type === 'UpdateExpression' && node.prefix) {
1502
+ if (node.argument.type !== 'Identifier') {
1503
+ throw new RuntimeError(
1504
+ `Invalid operand for update operator: ${node.operator}`,
1505
+ node,
1506
+ this.source,
1507
+ env
1508
+ );
1509
+ }
1510
+
1511
+ const varName = node.argument.name;
1512
+ let currentVal = await env.get(varName); // assuming env.get returns the variable value
1513
+
1514
+ switch (node.operator) {
1515
+ case 'PLUSPLUS':
1516
+ currentVal++;
1517
+ await env.set(varName, currentVal);
1518
+ return currentVal;
1519
+ case 'MINUSMINUS':
1520
+ currentVal--;
1521
+ await env.set(varName, currentVal);
1522
+ return currentVal;
1523
+ default:
1524
+ throw new RuntimeError(
1525
+ `Unknown update operator: ${node.operator}`,
1526
+ node,
1527
+ this.source,
1528
+ env
1529
+ );
1530
+ }
1531
+ }
1532
+
1533
+ // Evaluate the argument first
1467
1534
  const val = await this.evaluate(node.argument, env);
1468
1535
 
1469
1536
  switch (node.operator) {
1470
- case 'NOT': return !val;
1471
- case 'MINUS': return -val;
1472
- case 'PLUS': return +val;
1537
+ case 'NOT':
1538
+ return !val;
1539
+ case 'MINUS':
1540
+ return -val;
1541
+ case 'PLUS':
1542
+ return +val;
1473
1543
  default:
1474
1544
  throw new RuntimeError(
1475
1545
  `Unknown unary operator: ${node.operator}`,
@@ -1478,7 +1548,6 @@ async evalUnary(node, env) {
1478
1548
  env
1479
1549
  );
1480
1550
  }
1481
-
1482
1551
  } catch (e) {
1483
1552
  if (e instanceof RuntimeError) throw e;
1484
1553
  throw new RuntimeError(
@@ -1490,6 +1559,51 @@ async evalUnary(node, env) {
1490
1559
  }
1491
1560
  }
1492
1561
 
1562
+ // Handle postfix updates (x++ or x--)
1563
+ async evalPostfixUpdate(node, env) {
1564
+ if (node.type !== 'UpdateExpression' || node.prefix) {
1565
+ throw new RuntimeError(
1566
+ `Invalid postfix update node`,
1567
+ node,
1568
+ this.source,
1569
+ env
1570
+ );
1571
+ }
1572
+
1573
+ if (node.argument.type !== 'Identifier') {
1574
+ throw new RuntimeError(
1575
+ `Invalid operand for postfix update operator: ${node.operator}`,
1576
+ node,
1577
+ this.source,
1578
+ env
1579
+ );
1580
+ }
1581
+
1582
+ const varName = node.argument.name;
1583
+ let currentVal = await env.get(varName);
1584
+
1585
+ let returnVal = currentVal;
1586
+
1587
+ switch (node.operator) {
1588
+ case 'PLUSPLUS':
1589
+ currentVal++;
1590
+ await env.set(varName, currentVal);
1591
+ return returnVal; // return original value
1592
+ case 'MINUSMINUS':
1593
+ currentVal--;
1594
+ await env.set(varName, currentVal);
1595
+ return returnVal; // return original value
1596
+ default:
1597
+ throw new RuntimeError(
1598
+ `Unknown postfix operator: ${node.operator}`,
1599
+ node,
1600
+ this.source,
1601
+ env
1602
+ );
1603
+ }
1604
+ }
1605
+
1606
+
1493
1607
  async evalIf(node, env) {
1494
1608
  let test = await this.evaluate(node.test, env);
1495
1609
  test = !!test; // coerce to boolean
@@ -1768,7 +1882,8 @@ async evalIndex(node, env) {
1768
1882
  env
1769
1883
  );
1770
1884
  }
1771
- }
1885
+ }
1886
+
1772
1887
  async evalObject(node, env) {
1773
1888
  try {
1774
1889
  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.19';
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