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 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
- if (t.type === 'LBRACKET') {
13148
- const startLine = t.line;
13149
- const startCol = t.column;
13150
- this.eat('LBRACKET');
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
- const index = this.expression();
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 index expression",
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: index,
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.14';
13972
+ const VERSION = '1.1.16';
13738
13973
 
13739
13974
  const COLOR = {
13740
13975
  reset: '\x1b[0m',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
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
- if (t.type === 'LBRACKET') {
1296
- const startLine = t.line;
1297
- const startCol = t.column;
1298
- this.eat('LBRACKET');
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
- const index = this.expression();
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 index expression",
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: index,
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
 
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.14';
15
+ const VERSION = '1.1.16';
16
16
 
17
17
  const COLOR = {
18
18
  reset: '\x1b[0m',