starlight-cli 1.1.10 → 1.1.11

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
@@ -10411,6 +10411,20 @@ formatValue(value, seen = new Set()) {
10411
10411
  if (Array.isArray(arg)) return 'array';
10412
10412
  return typeof arg;
10413
10413
  });
10414
+ this.global.define('isNaN', arg => {
10415
+ return typeof arg !== 'number' || Number.isNaN(arg);
10416
+ });
10417
+ this.global.define('random', (min, max) => {
10418
+ if (max === undefined) {
10419
+ // Only one argument → random between 0 and min
10420
+ return Math.floor(Math.random() * min);
10421
+ }
10422
+ min = Number(min);
10423
+ max = Number(max);
10424
+ if (isNaN(min) || isNaN(max)) return 0;
10425
+ return Math.floor(Math.random() * (max - min)) + min;
10426
+ });
10427
+
10414
10428
  this.global.define('map', async (array, fn) => {
10415
10429
  if (!Array.isArray(array)) {
10416
10430
  throw new RuntimeError('map() expects an array', null, evaluator.source);
@@ -11064,13 +11078,10 @@ async evalUnary(node, env) {
11064
11078
 
11065
11079
  }
11066
11080
  }
11067
-
11068
11081
  async evalIf(node, env) {
11069
- const test = await this.evaluate(node.test, env);
11082
+ let test = await this.evaluate(node.test, env);
11070
11083
 
11071
- if (typeof test !== 'boolean') {
11072
- throw new RuntimeError('If condition must evaluate to a boolean', node.test, this.source);
11073
- }
11084
+ test = !!test;
11074
11085
 
11075
11086
  if (test) {
11076
11087
  return await this.evaluate(node.consequent, env);
@@ -11083,27 +11094,26 @@ async evalIf(node, env) {
11083
11094
  return null;
11084
11095
  }
11085
11096
 
11086
-
11087
11097
  async evalWhile(node, env) {
11088
11098
  while (true) {
11089
- const test = await this.evaluate(node.test, env);
11099
+ let test = await this.evaluate(node.test, env);
11090
11100
 
11091
- if (typeof test !== 'boolean') {
11092
- throw new RuntimeError('While condition must evaluate to a boolean', node.test, this.source);
11093
- }
11101
+ test = !!test;
11094
11102
 
11095
11103
  if (!test) break;
11096
11104
 
11097
11105
  try {
11098
11106
  await this.evaluate(node.body, env);
11099
11107
  } catch (e) {
11100
- if (e instanceof BreakSignal) break;
11101
- if (e instanceof ContinueSignal) continue;
11102
- throw e;
11108
+ if (e instanceof BreakSignal) break;
11109
+ if (e instanceof ContinueSignal) continue;
11110
+ throw e;
11103
11111
  }
11104
11112
  }
11113
+
11105
11114
  return null;
11106
11115
  }
11116
+
11107
11117
  async evalFor(node, env) {
11108
11118
  if (node.type === 'ForInStatement') {
11109
11119
  const iterable = await this.evaluate(node.iterable, env);
@@ -11267,9 +11277,17 @@ async evalObject(node, env) {
11267
11277
  async evalMember(node, env) {
11268
11278
  const obj = await this.evaluate(node.object, env);
11269
11279
 
11270
- if (obj == null) throw new RuntimeError('Member access of null or undefined', node, this.source);
11280
+ if (obj == null) {
11281
+ throw new RuntimeError('Member access of null or undefined', node, this.source);
11282
+ }
11283
+
11284
+ const prop = obj[node.property];
11271
11285
 
11272
- return obj[node.property];
11286
+ if (typeof prop === 'function') {
11287
+ return prop.bind(obj);
11288
+ }
11289
+
11290
+ return prop;
11273
11291
  }
11274
11292
 
11275
11293
 
@@ -11340,7 +11358,7 @@ class Lexer {
11340
11358
  'break', 'continue', 'func', 'return',
11341
11359
  'true', 'false', 'null',
11342
11360
  'ask', 'define', 'import', 'from', 'as',
11343
- 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race'
11361
+ 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race', 'not', 'and', 'or'
11344
11362
  ];
11345
11363
  }
11346
11364
 
@@ -11423,6 +11441,8 @@ class Lexer {
11423
11441
  }
11424
11442
 
11425
11443
  if (this.keywords.includes(result)) {
11444
+ if (result === 'and') return { type: 'AND', value: 'and', line: startLine, column: startCol };
11445
+ if (result === 'or') return { type: 'OR', value: 'or', line: startLine, column: startCol };
11426
11446
  return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
11427
11447
  }
11428
11448
 
@@ -11538,7 +11558,7 @@ module.exports = Lexer;
11538
11558
  /***/ }),
11539
11559
 
11540
11560
  /***/ 222:
11541
- /***/ ((module) => {
11561
+ /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
11542
11562
 
11543
11563
  class ParseError extends Error {
11544
11564
  constructor(message, token, source) {
@@ -11820,18 +11840,51 @@ ifStatement() {
11820
11840
  column: t.column
11821
11841
  };
11822
11842
  }
11843
+ parseExpressionOnly() {
11844
+ return this.expression();
11845
+ }
11823
11846
 
11824
11847
  whileStatement() {
11825
11848
  const t = this.current;
11826
11849
  this.eat('WHILE');
11827
11850
 
11828
11851
  let test;
11852
+
11829
11853
  if (this.current.type === 'LPAREN') {
11830
11854
  this.eat('LPAREN');
11831
11855
  test = this.expression();
11832
11856
  this.eat('RPAREN');
11833
- } else {
11834
- test = this.expression();
11857
+ }
11858
+ else {
11859
+ const startPos = this.pos;
11860
+ const startToken = this.current;
11861
+
11862
+ const exprTokens = [];
11863
+ let braceFound = false;
11864
+ let depth = 0;
11865
+
11866
+ while (this.current.type !== 'EOF') {
11867
+ if (this.current.type === 'LBRACE' && depth === 0) {
11868
+ braceFound = true;
11869
+ break;
11870
+ }
11871
+ if (this.current.type === 'LPAREN') depth++;
11872
+ if (this.current.type === 'RPAREN') depth--;
11873
+ exprTokens.push(this.current);
11874
+ this.advance();
11875
+ }
11876
+
11877
+ if (!braceFound) {
11878
+ throw new ParseError(
11879
+ "Expected '{' after while condition",
11880
+ this.current,
11881
+ this.source
11882
+ );
11883
+ }
11884
+
11885
+ const Parser = __nccwpck_require__(222);
11886
+ const exprParser = new Parser(exprTokens, this.source);
11887
+ test = exprParser.parseExpressionOnly();
11835
11888
  }
11836
11889
 
11837
11890
  const body = this.block();
@@ -11844,6 +11897,7 @@ whileStatement() {
11844
11897
  };
11845
11898
  }
11846
11899
 
11900
+
11847
11901
  importStatement() {
11848
11902
  const t = this.current;
11849
11903
  this.eat('IMPORT');
@@ -12652,7 +12706,7 @@ const Lexer = __nccwpck_require__(211);
12652
12706
  const Parser = __nccwpck_require__(222);
12653
12707
  const Evaluator = __nccwpck_require__(112);
12654
12708
 
12655
- const VERSION = '1.1.10';
12709
+ const VERSION = '1.1.11';
12656
12710
 
12657
12711
  const COLOR = {
12658
12712
  reset: '\x1b[0m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.1.10",
3
+ "version": "1.1.11",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/evaluator.js CHANGED
@@ -201,6 +201,20 @@ formatValue(value, seen = new Set()) {
201
201
  if (Array.isArray(arg)) return 'array';
202
202
  return typeof arg;
203
203
  });
204
+ this.global.define('isNaN', arg => {
205
+ return typeof arg !== 'number' || Number.isNaN(arg);
206
+ });
207
+ this.global.define('random', (min, max) => {
208
+ if (max === undefined) {
209
+ // Only one argument → random between 0 and min
210
+ return Math.floor(Math.random() * min);
211
+ }
212
+ min = Number(min);
213
+ max = Number(max);
214
+ if (isNaN(min) || isNaN(max)) return 0;
215
+ return Math.floor(Math.random() * (max - min)) + min;
216
+ });
217
+
204
218
  this.global.define('map', async (array, fn) => {
205
219
  if (!Array.isArray(array)) {
206
220
  throw new RuntimeError('map() expects an array', null, evaluator.source);
@@ -854,13 +868,10 @@ async evalUnary(node, env) {
854
868
 
855
869
  }
856
870
  }
857
-
858
871
  async evalIf(node, env) {
859
- const test = await this.evaluate(node.test, env);
872
+ let test = await this.evaluate(node.test, env);
860
873
 
861
- if (typeof test !== 'boolean') {
862
- throw new RuntimeError('If condition must evaluate to a boolean', node.test, this.source);
863
- }
874
+ test = !!test;
864
875
 
865
876
  if (test) {
866
877
  return await this.evaluate(node.consequent, env);
@@ -873,27 +884,26 @@ async evalIf(node, env) {
873
884
  return null;
874
885
  }
875
886
 
876
-
877
887
  async evalWhile(node, env) {
878
888
  while (true) {
879
- const test = await this.evaluate(node.test, env);
889
+ let test = await this.evaluate(node.test, env);
880
890
 
881
- if (typeof test !== 'boolean') {
882
- throw new RuntimeError('While condition must evaluate to a boolean', node.test, this.source);
883
- }
891
+ test = !!test;
884
892
 
885
893
  if (!test) break;
886
894
 
887
895
  try {
888
896
  await this.evaluate(node.body, env);
889
897
  } catch (e) {
890
- if (e instanceof BreakSignal) break;
891
- if (e instanceof ContinueSignal) continue;
892
- throw e;
898
+ if (e instanceof BreakSignal) break;
899
+ if (e instanceof ContinueSignal) continue;
900
+ throw e;
893
901
  }
894
902
  }
903
+
895
904
  return null;
896
905
  }
906
+
897
907
  async evalFor(node, env) {
898
908
  if (node.type === 'ForInStatement') {
899
909
  const iterable = await this.evaluate(node.iterable, env);
@@ -1057,9 +1067,17 @@ async evalObject(node, env) {
1057
1067
  async evalMember(node, env) {
1058
1068
  const obj = await this.evaluate(node.object, env);
1059
1069
 
1060
- if (obj == null) throw new RuntimeError('Member access of null or undefined', node, this.source);
1070
+ if (obj == null) {
1071
+ throw new RuntimeError('Member access of null or undefined', node, this.source);
1072
+ }
1073
+
1074
+ const prop = obj[node.property];
1075
+
1076
+ if (typeof prop === 'function') {
1077
+ return prop.bind(obj);
1078
+ }
1061
1079
 
1062
- return obj[node.property];
1080
+ return prop;
1063
1081
  }
1064
1082
 
1065
1083
 
package/src/lexer.js CHANGED
@@ -28,7 +28,7 @@ class Lexer {
28
28
  'break', 'continue', 'func', 'return',
29
29
  'true', 'false', 'null',
30
30
  'ask', 'define', 'import', 'from', 'as',
31
- 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race'
31
+ 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race', 'not', 'and', 'or'
32
32
  ];
33
33
  }
34
34
 
@@ -111,6 +111,8 @@ class Lexer {
111
111
  }
112
112
 
113
113
  if (this.keywords.includes(result)) {
114
+ if (result === 'and') return { type: 'AND', value: 'and', line: startLine, column: startCol };
115
+ if (result === 'or') return { type: 'OR', value: 'or', line: startLine, column: startCol };
114
116
  return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
115
117
  }
116
118
 
package/src/parser.js CHANGED
@@ -278,18 +278,51 @@ ifStatement() {
278
278
  column: t.column
279
279
  };
280
280
  }
281
+ parseExpressionOnly() {
282
+ return this.expression();
283
+ }
281
284
 
282
285
  whileStatement() {
283
286
  const t = this.current;
284
287
  this.eat('WHILE');
285
288
 
286
289
  let test;
290
+
287
291
  if (this.current.type === 'LPAREN') {
288
292
  this.eat('LPAREN');
289
293
  test = this.expression();
290
294
  this.eat('RPAREN');
291
- } else {
292
- test = this.expression();
295
+ }
296
+ else {
297
+ const startPos = this.pos;
298
+ const startToken = this.current;
299
+
300
+ const exprTokens = [];
301
+ let braceFound = false;
302
+ let depth = 0;
303
+
304
+ while (this.current.type !== 'EOF') {
305
+ if (this.current.type === 'LBRACE' && depth === 0) {
306
+ braceFound = true;
307
+ break;
308
+ }
309
+ if (this.current.type === 'LPAREN') depth++;
310
+ if (this.current.type === 'RPAREN') depth--;
311
+ exprTokens.push(this.current);
312
+ this.advance();
313
+ }
314
+
315
+ if (!braceFound) {
316
+ throw new ParseError(
317
+ "Expected '{' after while condition",
318
+ this.current,
319
+ this.source
320
+ );
321
+ }
322
+
323
+ const Parser = require('./parser');
324
+ const exprParser = new Parser(exprTokens, this.source);
325
+ test = exprParser.parseExpressionOnly();
293
326
  }
294
327
 
295
328
  const body = this.block();
@@ -302,6 +335,7 @@ whileStatement() {
302
335
  };
303
336
  }
304
337
 
338
+
305
339
  importStatement() {
306
340
  const t = this.current;
307
341
  this.eat('IMPORT');
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.10';
15
+ const VERSION = '1.1.11';
16
16
 
17
17
  const COLOR = {
18
18
  reset: '\x1b[0m',