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 +74 -20
- package/package.json +1 -1
- package/src/evaluator.js +33 -15
- package/src/lexer.js +3 -1
- package/src/parser.js +36 -2
- package/src/starlight.js +1 -1
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
|
-
|
|
11082
|
+
let test = await this.evaluate(node.test, env);
|
|
11070
11083
|
|
|
11071
|
-
|
|
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
|
-
|
|
11099
|
+
let test = await this.evaluate(node.test, env);
|
|
11090
11100
|
|
|
11091
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
}
|
|
11834
|
-
|
|
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.
|
|
12709
|
+
const VERSION = '1.1.11';
|
|
12656
12710
|
|
|
12657
12711
|
const COLOR = {
|
|
12658
12712
|
reset: '\x1b[0m',
|
package/package.json
CHANGED
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
|
-
|
|
872
|
+
let test = await this.evaluate(node.test, env);
|
|
860
873
|
|
|
861
|
-
|
|
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
|
-
|
|
889
|
+
let test = await this.evaluate(node.test, env);
|
|
880
890
|
|
|
881
|
-
|
|
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)
|
|
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
|
|
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
|
-
}
|
|
292
|
-
|
|
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');
|