starlight-cli 1.1.14 → 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 +250 -15
- package/package.json +1 -1
- package/src/evaluator.js +107 -2
- package/src/parser.js +142 -12
- package/src/starlight.js +1 -1
package/dist/index.js
CHANGED
|
@@ -10433,7 +10433,24 @@ this.global.define('random', (min, max) => {
|
|
|
10433
10433
|
if (isNaN(min) || isNaN(max)) return 0;
|
|
10434
10434
|
return Math.floor(Math.random() * (max - min)) + min;
|
|
10435
10435
|
});
|
|
10436
|
+
this.global.define('JSONParse', arg => {
|
|
10437
|
+
if (typeof arg !== 'string') {
|
|
10438
|
+
throw new RuntimeError('JSONParse expects a string', null, evaluator.source);
|
|
10439
|
+
}
|
|
10440
|
+
try {
|
|
10441
|
+
return JSON.parse(arg);
|
|
10442
|
+
} catch (e) {
|
|
10443
|
+
throw new RuntimeError('Invalid JSON string: ' + e.message, null, evaluator.source);
|
|
10444
|
+
}
|
|
10445
|
+
});
|
|
10436
10446
|
|
|
10447
|
+
this.global.define('JSONStringify', arg => {
|
|
10448
|
+
try {
|
|
10449
|
+
return JSON.stringify(arg);
|
|
10450
|
+
} catch (e) {
|
|
10451
|
+
throw new RuntimeError('Cannot stringify value: ' + e.message, null, evaluator.source);
|
|
10452
|
+
}
|
|
10453
|
+
});
|
|
10437
10454
|
this.global.define('map', async (array, fn) => {
|
|
10438
10455
|
if (!Array.isArray(array)) {
|
|
10439
10456
|
throw new RuntimeError('map() expects an array', null, evaluator.source);
|
|
@@ -10615,10 +10632,17 @@ async evaluate(node, env = this.global) {
|
|
|
10615
10632
|
case 'SldeployStatement': return await this.evalSldeploy(node, env);
|
|
10616
10633
|
case 'AskStatement': return await this.evalAsk(node, env);
|
|
10617
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
|
+
|
|
10618
10640
|
case 'ExpressionStatement': return await this.evaluate(node.expression, env);
|
|
10619
10641
|
case 'BinaryExpression': return await this.evalBinary(node, env);
|
|
10620
10642
|
case 'LogicalExpression': return await this.evalLogical(node, env);
|
|
10621
10643
|
case 'UnaryExpression': return await this.evalUnary(node, env);
|
|
10644
|
+
case 'ConditionalExpression':
|
|
10645
|
+
return await this.evalConditional(node, env);
|
|
10622
10646
|
case 'Literal': return node.value;
|
|
10623
10647
|
case 'Identifier': return env.get(node.name, node, this.source);
|
|
10624
10648
|
case 'IfStatement': return await this.evalIf(node, env);
|
|
@@ -10721,6 +10745,36 @@ async evalProgram(node, env) {
|
|
|
10721
10745
|
}
|
|
10722
10746
|
return result;
|
|
10723
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
|
+
}
|
|
10724
10778
|
|
|
10725
10779
|
async evalStartStatement(node, env) {
|
|
10726
10780
|
try {
|
|
@@ -10769,6 +10823,24 @@ async evalStartStatement(node, env) {
|
|
|
10769
10823
|
);
|
|
10770
10824
|
}
|
|
10771
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
|
+
}
|
|
10772
10844
|
|
|
10773
10845
|
async evalRaceClause(node, env) {
|
|
10774
10846
|
try {
|
|
@@ -11379,6 +11451,41 @@ async evalFor(node, env) {
|
|
|
11379
11451
|
);
|
|
11380
11452
|
}
|
|
11381
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
|
+
}
|
|
11382
11489
|
|
|
11383
11490
|
evalFunctionDeclaration(node, env) {
|
|
11384
11491
|
try {
|
|
@@ -11466,7 +11573,6 @@ async evalCall(node, env) {
|
|
|
11466
11573
|
);
|
|
11467
11574
|
}
|
|
11468
11575
|
}
|
|
11469
|
-
|
|
11470
11576
|
async evalIndex(node, env) {
|
|
11471
11577
|
try {
|
|
11472
11578
|
const obj = await this.evaluate(node.object, env);
|
|
@@ -11499,7 +11605,6 @@ async evalIndex(node, env) {
|
|
|
11499
11605
|
);
|
|
11500
11606
|
}
|
|
11501
11607
|
}
|
|
11502
|
-
|
|
11503
11608
|
async evalObject(node, env) {
|
|
11504
11609
|
try {
|
|
11505
11610
|
const out = {};
|
|
@@ -13144,33 +13249,100 @@ postfix() {
|
|
|
13144
13249
|
while (true) {
|
|
13145
13250
|
const t = this.current;
|
|
13146
13251
|
|
|
13147
|
-
|
|
13148
|
-
|
|
13149
|
-
|
|
13150
|
-
|
|
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;
|
|
13151
13260
|
|
|
13152
|
-
|
|
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
|
+
};
|
|
13294
|
+
|
|
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
|
+
}
|
|
13153
13311
|
|
|
13154
13312
|
if (this.current.type !== 'RBRACKET') {
|
|
13155
13313
|
throw new ParseError(
|
|
13156
|
-
"Expected ']' after
|
|
13314
|
+
"Expected ']' after slice",
|
|
13157
13315
|
this.current,
|
|
13158
|
-
this.source
|
|
13159
|
-
"Index access must be closed, e.g. arr[0]"
|
|
13316
|
+
this.source
|
|
13160
13317
|
);
|
|
13161
13318
|
}
|
|
13162
13319
|
|
|
13163
13320
|
this.eat('RBRACKET');
|
|
13164
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');
|
|
13165
13334
|
node = {
|
|
13166
13335
|
type: 'IndexExpression',
|
|
13167
13336
|
object: node,
|
|
13168
|
-
indexer:
|
|
13337
|
+
indexer: start,
|
|
13169
13338
|
line: startLine,
|
|
13170
13339
|
column: startCol
|
|
13171
13340
|
};
|
|
13172
|
-
continue;
|
|
13173
13341
|
}
|
|
13342
|
+
}
|
|
13343
|
+
}
|
|
13344
|
+
|
|
13345
|
+
|
|
13174
13346
|
|
|
13175
13347
|
if (t.type === 'LPAREN') {
|
|
13176
13348
|
const startLine = t.line;
|
|
@@ -13302,6 +13474,7 @@ arrowFunction(params) {
|
|
|
13302
13474
|
};
|
|
13303
13475
|
}
|
|
13304
13476
|
|
|
13477
|
+
|
|
13305
13478
|
primary() {
|
|
13306
13479
|
const t = this.current;
|
|
13307
13480
|
|
|
@@ -13373,7 +13546,6 @@ primary() {
|
|
|
13373
13546
|
return { type: 'NewExpression', callee, arguments: args, line: t.line, column: t.column };
|
|
13374
13547
|
}
|
|
13375
13548
|
|
|
13376
|
-
// ---- ask(...) function call ----
|
|
13377
13549
|
if (t.type === 'ASK') {
|
|
13378
13550
|
this.eat('ASK');
|
|
13379
13551
|
|
|
@@ -13414,6 +13586,69 @@ primary() {
|
|
|
13414
13586
|
column: t.column
|
|
13415
13587
|
};
|
|
13416
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
|
+
}
|
|
13417
13652
|
|
|
13418
13653
|
if (t.type === 'IDENTIFIER') {
|
|
13419
13654
|
const name = t.value;
|
|
@@ -13550,7 +13785,7 @@ primary() {
|
|
|
13550
13785
|
t,
|
|
13551
13786
|
this.source
|
|
13552
13787
|
);
|
|
13553
|
-
}
|
|
13788
|
+
}
|
|
13554
13789
|
|
|
13555
13790
|
}
|
|
13556
13791
|
|
|
@@ -13734,7 +13969,7 @@ const Lexer = __nccwpck_require__(211);
|
|
|
13734
13969
|
const Parser = __nccwpck_require__(222);
|
|
13735
13970
|
const Evaluator = __nccwpck_require__(112);
|
|
13736
13971
|
|
|
13737
|
-
const VERSION = '1.1.
|
|
13972
|
+
const VERSION = '1.1.16';
|
|
13738
13973
|
|
|
13739
13974
|
const COLOR = {
|
|
13740
13975
|
reset: '\x1b[0m',
|
package/package.json
CHANGED
package/src/evaluator.js
CHANGED
|
@@ -223,7 +223,24 @@ this.global.define('random', (min, max) => {
|
|
|
223
223
|
if (isNaN(min) || isNaN(max)) return 0;
|
|
224
224
|
return Math.floor(Math.random() * (max - min)) + min;
|
|
225
225
|
});
|
|
226
|
+
this.global.define('JSONParse', arg => {
|
|
227
|
+
if (typeof arg !== 'string') {
|
|
228
|
+
throw new RuntimeError('JSONParse expects a string', null, evaluator.source);
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
return JSON.parse(arg);
|
|
232
|
+
} catch (e) {
|
|
233
|
+
throw new RuntimeError('Invalid JSON string: ' + e.message, null, evaluator.source);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
226
236
|
|
|
237
|
+
this.global.define('JSONStringify', arg => {
|
|
238
|
+
try {
|
|
239
|
+
return JSON.stringify(arg);
|
|
240
|
+
} catch (e) {
|
|
241
|
+
throw new RuntimeError('Cannot stringify value: ' + e.message, null, evaluator.source);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
227
244
|
this.global.define('map', async (array, fn) => {
|
|
228
245
|
if (!Array.isArray(array)) {
|
|
229
246
|
throw new RuntimeError('map() expects an array', null, evaluator.source);
|
|
@@ -405,10 +422,17 @@ async evaluate(node, env = this.global) {
|
|
|
405
422
|
case 'SldeployStatement': return await this.evalSldeploy(node, env);
|
|
406
423
|
case 'AskStatement': return await this.evalAsk(node, env);
|
|
407
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
|
+
|
|
408
430
|
case 'ExpressionStatement': return await this.evaluate(node.expression, env);
|
|
409
431
|
case 'BinaryExpression': return await this.evalBinary(node, env);
|
|
410
432
|
case 'LogicalExpression': return await this.evalLogical(node, env);
|
|
411
433
|
case 'UnaryExpression': return await this.evalUnary(node, env);
|
|
434
|
+
case 'ConditionalExpression':
|
|
435
|
+
return await this.evalConditional(node, env);
|
|
412
436
|
case 'Literal': return node.value;
|
|
413
437
|
case 'Identifier': return env.get(node.name, node, this.source);
|
|
414
438
|
case 'IfStatement': return await this.evalIf(node, env);
|
|
@@ -511,6 +535,36 @@ async evalProgram(node, env) {
|
|
|
511
535
|
}
|
|
512
536
|
return result;
|
|
513
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
|
+
}
|
|
514
568
|
|
|
515
569
|
async evalStartStatement(node, env) {
|
|
516
570
|
try {
|
|
@@ -559,6 +613,24 @@ async evalStartStatement(node, env) {
|
|
|
559
613
|
);
|
|
560
614
|
}
|
|
561
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
|
+
}
|
|
562
634
|
|
|
563
635
|
async evalRaceClause(node, env) {
|
|
564
636
|
try {
|
|
@@ -1169,6 +1241,41 @@ async evalFor(node, env) {
|
|
|
1169
1241
|
);
|
|
1170
1242
|
}
|
|
1171
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
|
+
}
|
|
1172
1279
|
|
|
1173
1280
|
evalFunctionDeclaration(node, env) {
|
|
1174
1281
|
try {
|
|
@@ -1256,7 +1363,6 @@ async evalCall(node, env) {
|
|
|
1256
1363
|
);
|
|
1257
1364
|
}
|
|
1258
1365
|
}
|
|
1259
|
-
|
|
1260
1366
|
async evalIndex(node, env) {
|
|
1261
1367
|
try {
|
|
1262
1368
|
const obj = await this.evaluate(node.object, env);
|
|
@@ -1289,7 +1395,6 @@ async evalIndex(node, env) {
|
|
|
1289
1395
|
);
|
|
1290
1396
|
}
|
|
1291
1397
|
}
|
|
1292
|
-
|
|
1293
1398
|
async evalObject(node, env) {
|
|
1294
1399
|
try {
|
|
1295
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
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|