starlight-cli 1.0.47 → 1.0.49
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 +421 -169
- package/package.json +1 -1
- package/src/evaluator.js +156 -70
- package/src/parser.js +248 -85
- package/src/starlight.js +17 -14
package/src/parser.js
CHANGED
|
@@ -83,10 +83,11 @@ class Parser {
|
|
|
83
83
|
return this.expressionStatement();
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
varDeclaration() {
|
|
87
|
+
const t = this.current; // LET token
|
|
88
88
|
this.eat('LET');
|
|
89
|
-
const
|
|
89
|
+
const idToken = this.current; // identifier token
|
|
90
|
+
const id = idToken.value;
|
|
90
91
|
this.eat('IDENTIFIER');
|
|
91
92
|
|
|
92
93
|
let expr = null;
|
|
@@ -98,16 +99,30 @@ class Parser {
|
|
|
98
99
|
// semicolon optional
|
|
99
100
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
100
101
|
|
|
101
|
-
return {
|
|
102
|
+
return {
|
|
103
|
+
type: 'VarDeclaration',
|
|
104
|
+
id,
|
|
105
|
+
expr,
|
|
106
|
+
line: t.line,
|
|
107
|
+
column: t.column
|
|
108
|
+
};
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
sldeployStatement() {
|
|
112
|
+
const t = this.current; // SLDEPLOY token
|
|
105
113
|
this.eat('SLDEPLOY');
|
|
106
114
|
const expr = this.expression();
|
|
107
115
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
108
|
-
return {
|
|
116
|
+
return {
|
|
117
|
+
type: 'SldeployStatement',
|
|
118
|
+
expr,
|
|
119
|
+
line: t.line,
|
|
120
|
+
column: t.column
|
|
121
|
+
};
|
|
109
122
|
}
|
|
123
|
+
|
|
110
124
|
doTrackStatement() {
|
|
125
|
+
const t = this.current; // DO token
|
|
111
126
|
this.eat('DO');
|
|
112
127
|
|
|
113
128
|
const body = this.block();
|
|
@@ -121,13 +136,17 @@ doTrackStatement() {
|
|
|
121
136
|
return {
|
|
122
137
|
type: 'DoTrackStatement',
|
|
123
138
|
body,
|
|
124
|
-
handler
|
|
139
|
+
handler,
|
|
140
|
+
line: t.line,
|
|
141
|
+
column: t.column
|
|
125
142
|
};
|
|
126
143
|
}
|
|
127
144
|
|
|
128
145
|
defineStatement() {
|
|
146
|
+
const t = this.current; // DEFINE token
|
|
129
147
|
this.eat('DEFINE');
|
|
130
|
-
const
|
|
148
|
+
const idToken = this.current; // identifier token
|
|
149
|
+
const id = idToken.value;
|
|
131
150
|
this.eat('IDENTIFIER');
|
|
132
151
|
|
|
133
152
|
let expr = null;
|
|
@@ -138,10 +157,17 @@ defineStatement() {
|
|
|
138
157
|
|
|
139
158
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
140
159
|
|
|
141
|
-
return {
|
|
160
|
+
return {
|
|
161
|
+
type: 'DefineStatement',
|
|
162
|
+
id,
|
|
163
|
+
expr,
|
|
164
|
+
line: t.line,
|
|
165
|
+
column: t.column
|
|
166
|
+
};
|
|
142
167
|
}
|
|
143
168
|
|
|
144
169
|
asyncFuncDeclaration() {
|
|
170
|
+
const t = this.current; // first token of function (identifier)
|
|
145
171
|
const name = this.current.value;
|
|
146
172
|
this.eat('IDENTIFIER');
|
|
147
173
|
|
|
@@ -167,10 +193,19 @@ asyncFuncDeclaration() {
|
|
|
167
193
|
}
|
|
168
194
|
|
|
169
195
|
const body = this.block();
|
|
170
|
-
return {
|
|
196
|
+
return {
|
|
197
|
+
type: 'FunctionDeclaration',
|
|
198
|
+
name,
|
|
199
|
+
params,
|
|
200
|
+
body,
|
|
201
|
+
async: true,
|
|
202
|
+
line: t.line,
|
|
203
|
+
column: t.column
|
|
204
|
+
};
|
|
171
205
|
}
|
|
172
206
|
|
|
173
207
|
ifStatement() {
|
|
208
|
+
const t = this.current; // IF token
|
|
174
209
|
this.eat('IF');
|
|
175
210
|
|
|
176
211
|
let test;
|
|
@@ -179,7 +214,7 @@ ifStatement() {
|
|
|
179
214
|
test = this.expression();
|
|
180
215
|
this.eat('RPAREN');
|
|
181
216
|
} else {
|
|
182
|
-
//
|
|
217
|
+
// Python style: no parentheses
|
|
183
218
|
test = this.expression();
|
|
184
219
|
}
|
|
185
220
|
|
|
@@ -192,10 +227,18 @@ ifStatement() {
|
|
|
192
227
|
else alternate = this.block();
|
|
193
228
|
}
|
|
194
229
|
|
|
195
|
-
return {
|
|
230
|
+
return {
|
|
231
|
+
type: 'IfStatement',
|
|
232
|
+
test,
|
|
233
|
+
consequent,
|
|
234
|
+
alternate,
|
|
235
|
+
line: t.line,
|
|
236
|
+
column: t.column
|
|
237
|
+
};
|
|
196
238
|
}
|
|
197
239
|
|
|
198
240
|
whileStatement() {
|
|
241
|
+
const t = this.current; // WHILE token
|
|
199
242
|
this.eat('WHILE');
|
|
200
243
|
|
|
201
244
|
let test;
|
|
@@ -208,10 +251,17 @@ whileStatement() {
|
|
|
208
251
|
}
|
|
209
252
|
|
|
210
253
|
const body = this.block();
|
|
211
|
-
return {
|
|
254
|
+
return {
|
|
255
|
+
type: 'WhileStatement',
|
|
256
|
+
test,
|
|
257
|
+
body,
|
|
258
|
+
line: t.line,
|
|
259
|
+
column: t.column
|
|
260
|
+
};
|
|
212
261
|
}
|
|
213
262
|
|
|
214
263
|
importStatement() {
|
|
264
|
+
const t = this.current; // IMPORT token
|
|
215
265
|
this.eat('IMPORT');
|
|
216
266
|
|
|
217
267
|
let specifiers = [];
|
|
@@ -220,11 +270,13 @@ importStatement() {
|
|
|
220
270
|
this.eat('AS');
|
|
221
271
|
const name = this.current.value;
|
|
222
272
|
this.eat('IDENTIFIER');
|
|
223
|
-
specifiers.push({ type: 'NamespaceImport', local: name });
|
|
273
|
+
specifiers.push({ type: 'NamespaceImport', local: name, line: t.line, column: t.column });
|
|
224
274
|
} else if (this.current.type === 'LBRACE') {
|
|
225
275
|
this.eat('LBRACE');
|
|
226
276
|
while (this.current.type !== 'RBRACE') {
|
|
227
277
|
const importedName = this.current.value;
|
|
278
|
+
const importedLine = this.current.line;
|
|
279
|
+
const importedColumn = this.current.column;
|
|
228
280
|
this.eat('IDENTIFIER');
|
|
229
281
|
|
|
230
282
|
let localName = importedName;
|
|
@@ -234,14 +286,16 @@ importStatement() {
|
|
|
234
286
|
this.eat('IDENTIFIER');
|
|
235
287
|
}
|
|
236
288
|
|
|
237
|
-
specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
|
|
289
|
+
specifiers.push({ type: 'NamedImport', imported: importedName, local: localName, line: importedLine, column: importedColumn });
|
|
238
290
|
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
239
291
|
}
|
|
240
292
|
this.eat('RBRACE');
|
|
241
293
|
} else if (this.current.type === 'IDENTIFIER') {
|
|
242
294
|
const localName = this.current.value;
|
|
295
|
+
const localLine = this.current.line;
|
|
296
|
+
const localColumn = this.current.column;
|
|
243
297
|
this.eat('IDENTIFIER');
|
|
244
|
-
specifiers.push({ type: 'DefaultImport', local: localName });
|
|
298
|
+
specifiers.push({ type: 'DefaultImport', local: localName, line: localLine, column: localColumn });
|
|
245
299
|
} else {
|
|
246
300
|
throw new Error(`Unexpected token in import at line ${this.current.line}, column ${this.current.column}`);
|
|
247
301
|
}
|
|
@@ -253,9 +307,17 @@ importStatement() {
|
|
|
253
307
|
|
|
254
308
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
255
309
|
|
|
256
|
-
return {
|
|
310
|
+
return {
|
|
311
|
+
type: 'ImportStatement',
|
|
312
|
+
path: pathToken.value,
|
|
313
|
+
specifiers,
|
|
314
|
+
line: t.line,
|
|
315
|
+
column: t.column
|
|
316
|
+
};
|
|
257
317
|
}
|
|
318
|
+
|
|
258
319
|
forStatement() {
|
|
320
|
+
const t = this.current; // FOR token
|
|
259
321
|
this.eat('FOR');
|
|
260
322
|
|
|
261
323
|
// --- Python-style: for variable in iterable (supports optional 'let') ---
|
|
@@ -263,17 +325,22 @@ forStatement() {
|
|
|
263
325
|
(this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')) {
|
|
264
326
|
|
|
265
327
|
let variable;
|
|
328
|
+
let variableLine, variableColumn;
|
|
266
329
|
let iterable;
|
|
267
330
|
let letKeyword = false;
|
|
268
331
|
|
|
269
332
|
if (this.current.type === 'LET') {
|
|
270
333
|
letKeyword = true;
|
|
271
334
|
this.eat('LET');
|
|
335
|
+
variableLine = this.current.line;
|
|
336
|
+
variableColumn = this.current.column;
|
|
272
337
|
variable = this.current.value;
|
|
273
338
|
this.eat('IDENTIFIER');
|
|
274
339
|
this.eat('IN');
|
|
275
340
|
iterable = this.expression();
|
|
276
341
|
} else {
|
|
342
|
+
variableLine = this.current.line;
|
|
343
|
+
variableColumn = this.current.column;
|
|
277
344
|
variable = this.current.value;
|
|
278
345
|
this.eat('IDENTIFIER');
|
|
279
346
|
this.eat('IN');
|
|
@@ -281,7 +348,17 @@ forStatement() {
|
|
|
281
348
|
}
|
|
282
349
|
|
|
283
350
|
const body = this.block();
|
|
284
|
-
return {
|
|
351
|
+
return {
|
|
352
|
+
type: 'ForInStatement',
|
|
353
|
+
variable,
|
|
354
|
+
variableLine,
|
|
355
|
+
variableColumn,
|
|
356
|
+
iterable,
|
|
357
|
+
letKeyword,
|
|
358
|
+
body,
|
|
359
|
+
line: t.line,
|
|
360
|
+
column: t.column
|
|
361
|
+
};
|
|
285
362
|
}
|
|
286
363
|
|
|
287
364
|
// --- C-style: for(init; test; update) ---
|
|
@@ -316,37 +393,51 @@ forStatement() {
|
|
|
316
393
|
}
|
|
317
394
|
|
|
318
395
|
const body = this.block();
|
|
319
|
-
return {
|
|
396
|
+
return {
|
|
397
|
+
type: 'ForStatement',
|
|
398
|
+
init,
|
|
399
|
+
test,
|
|
400
|
+
update,
|
|
401
|
+
body,
|
|
402
|
+
line: t.line,
|
|
403
|
+
column: t.column
|
|
404
|
+
};
|
|
320
405
|
}
|
|
321
406
|
|
|
322
407
|
breakStatement() {
|
|
408
|
+
const t = this.current; // BREAK token
|
|
323
409
|
this.eat('BREAK');
|
|
324
410
|
// Python-style: no semicolon needed, ignore if present
|
|
325
411
|
if (this.current.type === 'SEMICOLON') this.advance();
|
|
326
|
-
return { type: 'BreakStatement' };
|
|
412
|
+
return { type: 'BreakStatement', line: t.line, column: t.column };
|
|
327
413
|
}
|
|
328
414
|
|
|
329
415
|
continueStatement() {
|
|
416
|
+
const t = this.current; // CONTINUE token
|
|
330
417
|
this.eat('CONTINUE');
|
|
331
418
|
// Python-style: no semicolon needed, ignore if present
|
|
332
419
|
if (this.current.type === 'SEMICOLON') this.advance();
|
|
333
|
-
return { type: 'ContinueStatement' };
|
|
420
|
+
return { type: 'ContinueStatement', line: t.line, column: t.column };
|
|
334
421
|
}
|
|
335
422
|
|
|
336
423
|
funcDeclaration() {
|
|
424
|
+
const t = this.current; // FUNC token
|
|
337
425
|
this.eat('FUNC');
|
|
338
|
-
const
|
|
426
|
+
const nameToken = this.current;
|
|
427
|
+
const name = nameToken.value;
|
|
339
428
|
this.eat('IDENTIFIER');
|
|
340
429
|
|
|
341
430
|
let params = [];
|
|
342
431
|
if (this.current.type === 'LPAREN') {
|
|
343
432
|
this.eat('LPAREN');
|
|
344
433
|
if (this.current.type !== 'RPAREN') {
|
|
345
|
-
|
|
434
|
+
const paramToken = this.current;
|
|
435
|
+
params.push({ name: paramToken.value, line: paramToken.line, column: paramToken.column });
|
|
346
436
|
this.eat('IDENTIFIER');
|
|
347
437
|
while (this.current.type === 'COMMA') {
|
|
348
438
|
this.eat('COMMA');
|
|
349
|
-
|
|
439
|
+
const paramToken2 = this.current;
|
|
440
|
+
params.push({ name: paramToken2.value, line: paramToken2.line, column: paramToken2.column });
|
|
350
441
|
this.eat('IDENTIFIER');
|
|
351
442
|
}
|
|
352
443
|
}
|
|
@@ -354,16 +445,18 @@ funcDeclaration() {
|
|
|
354
445
|
} else {
|
|
355
446
|
// Python-style: single param without parentheses
|
|
356
447
|
if (this.current.type === 'IDENTIFIER') {
|
|
357
|
-
|
|
448
|
+
const paramToken = this.current;
|
|
449
|
+
params.push({ name: paramToken.value, line: paramToken.line, column: paramToken.column });
|
|
358
450
|
this.eat('IDENTIFIER');
|
|
359
451
|
}
|
|
360
452
|
}
|
|
361
453
|
|
|
362
454
|
const body = this.block();
|
|
363
|
-
return { type: 'FunctionDeclaration', name, params, body };
|
|
455
|
+
return { type: 'FunctionDeclaration', name, params, body, line: t.line, column: t.column };
|
|
364
456
|
}
|
|
365
457
|
|
|
366
458
|
returnStatement() {
|
|
459
|
+
const t = this.current; // RETURN token
|
|
367
460
|
this.eat('RETURN');
|
|
368
461
|
|
|
369
462
|
let argument = null;
|
|
@@ -374,24 +467,26 @@ returnStatement() {
|
|
|
374
467
|
// semicolon optional
|
|
375
468
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
376
469
|
|
|
377
|
-
return { type: 'ReturnStatement', argument };
|
|
470
|
+
return { type: 'ReturnStatement', argument, line: t.line, column: t.column };
|
|
378
471
|
}
|
|
379
472
|
|
|
380
473
|
block() {
|
|
474
|
+
const t = this.current; // LBRACE token
|
|
381
475
|
this.eat('LBRACE');
|
|
382
476
|
const body = [];
|
|
383
477
|
while (this.current.type !== 'RBRACE') {
|
|
384
478
|
body.push(this.statement());
|
|
385
479
|
}
|
|
386
480
|
this.eat('RBRACE');
|
|
387
|
-
return { type: 'BlockStatement', body };
|
|
481
|
+
return { type: 'BlockStatement', body, line: t.line, column: t.column };
|
|
388
482
|
}
|
|
389
483
|
|
|
390
484
|
expressionStatement() {
|
|
485
|
+
const exprToken = this.current; // first token of the expression
|
|
391
486
|
const expr = this.expression();
|
|
392
487
|
// semicolon optional
|
|
393
488
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
394
|
-
return { type: 'ExpressionStatement', expression: expr };
|
|
489
|
+
return { type: 'ExpressionStatement', expression: expr, line: exprToken.line, column: exprToken.column };
|
|
395
490
|
}
|
|
396
491
|
|
|
397
492
|
expression() {
|
|
@@ -402,17 +497,19 @@ assignment() {
|
|
|
402
497
|
const node = this.logicalOr();
|
|
403
498
|
const compoundOps = ['PLUSEQ', 'MINUSEQ', 'STAREQ', 'SLASHEQ', 'MODEQ'];
|
|
404
499
|
|
|
405
|
-
|
|
406
|
-
|
|
500
|
+
const t = this.current;
|
|
501
|
+
|
|
502
|
+
if (compoundOps.includes(t.type)) {
|
|
503
|
+
const op = t.type;
|
|
407
504
|
this.eat(op);
|
|
408
505
|
const right = this.assignment();
|
|
409
|
-
return { type: 'CompoundAssignment', operator: op, left: node, right };
|
|
506
|
+
return { type: 'CompoundAssignment', operator: op, left: node, right, line: t.line, column: t.column };
|
|
410
507
|
}
|
|
411
508
|
|
|
412
|
-
if (
|
|
509
|
+
if (t.type === 'EQUAL') {
|
|
413
510
|
this.eat('EQUAL');
|
|
414
511
|
const right = this.assignment();
|
|
415
|
-
return { type: 'AssignmentExpression', left: node, right };
|
|
512
|
+
return { type: 'AssignmentExpression', left: node, right, line: t.line, column: t.column };
|
|
416
513
|
}
|
|
417
514
|
|
|
418
515
|
return node;
|
|
@@ -421,9 +518,10 @@ assignment() {
|
|
|
421
518
|
logicalOr() {
|
|
422
519
|
let node = this.logicalAnd();
|
|
423
520
|
while (this.current.type === 'OR') {
|
|
424
|
-
const
|
|
521
|
+
const t = this.current;
|
|
522
|
+
const op = t.type;
|
|
425
523
|
this.eat(op);
|
|
426
|
-
node = { type: 'LogicalExpression', operator: op, left: node, right: this.logicalAnd() };
|
|
524
|
+
node = { type: 'LogicalExpression', operator: op, left: node, right: this.logicalAnd(), line: t.line, column: t.column };
|
|
427
525
|
}
|
|
428
526
|
return node;
|
|
429
527
|
}
|
|
@@ -431,18 +529,21 @@ logicalOr() {
|
|
|
431
529
|
logicalAnd() {
|
|
432
530
|
let node = this.equality();
|
|
433
531
|
while (this.current.type === 'AND') {
|
|
434
|
-
const
|
|
532
|
+
const t = this.current;
|
|
533
|
+
const op = t.type;
|
|
435
534
|
this.eat(op);
|
|
436
|
-
node = { type: 'LogicalExpression', operator: op, left: node, right: this.equality() };
|
|
535
|
+
node = { type: 'LogicalExpression', operator: op, left: node, right: this.equality(), line: t.line, column: t.column };
|
|
437
536
|
}
|
|
438
537
|
return node;
|
|
439
538
|
}
|
|
539
|
+
|
|
440
540
|
equality() {
|
|
441
541
|
let node = this.comparison();
|
|
442
542
|
while (['EQEQ', 'NOTEQ'].includes(this.current.type)) {
|
|
443
|
-
const
|
|
543
|
+
const t = this.current;
|
|
544
|
+
const op = t.type;
|
|
444
545
|
this.eat(op);
|
|
445
|
-
node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison() };
|
|
546
|
+
node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison(), line: t.line, column: t.column };
|
|
446
547
|
}
|
|
447
548
|
return node;
|
|
448
549
|
}
|
|
@@ -450,9 +551,10 @@ equality() {
|
|
|
450
551
|
comparison() {
|
|
451
552
|
let node = this.term();
|
|
452
553
|
while (['LT', 'LTE', 'GT', 'GTE'].includes(this.current.type)) {
|
|
453
|
-
const
|
|
554
|
+
const t = this.current;
|
|
555
|
+
const op = t.type;
|
|
454
556
|
this.eat(op);
|
|
455
|
-
node = { type: 'BinaryExpression', operator: op, left: node, right: this.term() };
|
|
557
|
+
node = { type: 'BinaryExpression', operator: op, left: node, right: this.term(), line: t.line, column: t.column };
|
|
456
558
|
}
|
|
457
559
|
return node;
|
|
458
560
|
}
|
|
@@ -460,9 +562,10 @@ comparison() {
|
|
|
460
562
|
term() {
|
|
461
563
|
let node = this.factor();
|
|
462
564
|
while (['PLUS', 'MINUS'].includes(this.current.type)) {
|
|
463
|
-
const
|
|
565
|
+
const t = this.current;
|
|
566
|
+
const op = t.type;
|
|
464
567
|
this.eat(op);
|
|
465
|
-
node = { type: 'BinaryExpression', operator: op, left: node, right: this.factor() };
|
|
568
|
+
node = { type: 'BinaryExpression', operator: op, left: node, right: this.factor(), line: t.line, column: t.column };
|
|
466
569
|
}
|
|
467
570
|
return node;
|
|
468
571
|
}
|
|
@@ -470,26 +573,43 @@ term() {
|
|
|
470
573
|
factor() {
|
|
471
574
|
let node = this.unary();
|
|
472
575
|
while (['STAR', 'SLASH', 'MOD'].includes(this.current.type)) {
|
|
473
|
-
const
|
|
576
|
+
const t = this.current;
|
|
577
|
+
const op = t.type;
|
|
474
578
|
this.eat(op);
|
|
475
|
-
node = { type: 'BinaryExpression', operator: op, left: node, right: this.unary() };
|
|
579
|
+
node = { type: 'BinaryExpression', operator: op, left: node, right: this.unary(), line: t.line, column: t.column };
|
|
476
580
|
}
|
|
477
581
|
return node;
|
|
478
582
|
}
|
|
479
583
|
|
|
584
|
+
|
|
480
585
|
unary() {
|
|
481
|
-
|
|
482
|
-
|
|
586
|
+
const t = this.current;
|
|
587
|
+
|
|
588
|
+
if (['NOT', 'MINUS', 'PLUS'].includes(t.type)) {
|
|
589
|
+
const op = t.type;
|
|
483
590
|
this.eat(op);
|
|
484
|
-
return {
|
|
591
|
+
return {
|
|
592
|
+
type: 'UnaryExpression',
|
|
593
|
+
operator: op,
|
|
594
|
+
argument: this.unary(),
|
|
595
|
+
line: t.line,
|
|
596
|
+
column: t.column
|
|
597
|
+
};
|
|
485
598
|
}
|
|
486
599
|
|
|
487
600
|
// Python-like: ignore ++ and -- if not used
|
|
488
|
-
if (
|
|
489
|
-
const op =
|
|
601
|
+
if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
|
|
602
|
+
const op = t.type;
|
|
490
603
|
this.eat(op);
|
|
491
604
|
const argument = this.unary();
|
|
492
|
-
return {
|
|
605
|
+
return {
|
|
606
|
+
type: 'UpdateExpression',
|
|
607
|
+
operator: op,
|
|
608
|
+
argument,
|
|
609
|
+
prefix: true,
|
|
610
|
+
line: t.line,
|
|
611
|
+
column: t.column
|
|
612
|
+
};
|
|
493
613
|
}
|
|
494
614
|
|
|
495
615
|
return this.postfix();
|
|
@@ -498,15 +618,21 @@ unary() {
|
|
|
498
618
|
postfix() {
|
|
499
619
|
let node = this.primary();
|
|
500
620
|
while (true) {
|
|
501
|
-
|
|
621
|
+
const t = this.current;
|
|
622
|
+
|
|
623
|
+
if (t.type === 'LBRACKET') {
|
|
624
|
+
const startLine = t.line;
|
|
625
|
+
const startCol = t.column;
|
|
502
626
|
this.eat('LBRACKET');
|
|
503
627
|
const index = this.expression();
|
|
504
628
|
if (this.current.type === 'RBRACKET') this.eat('RBRACKET');
|
|
505
|
-
node = { type: 'IndexExpression', object: node, indexer: index };
|
|
629
|
+
node = { type: 'IndexExpression', object: node, indexer: index, line: startLine, column: startCol };
|
|
506
630
|
continue;
|
|
507
631
|
}
|
|
508
632
|
|
|
509
|
-
if (
|
|
633
|
+
if (t.type === 'LPAREN') {
|
|
634
|
+
const startLine = t.line;
|
|
635
|
+
const startCol = t.column;
|
|
510
636
|
this.eat('LPAREN');
|
|
511
637
|
const args = [];
|
|
512
638
|
while (this.current.type !== 'RPAREN' && this.current.type !== 'EOF') {
|
|
@@ -514,30 +640,30 @@ postfix() {
|
|
|
514
640
|
if (this.current.type === 'COMMA') this.eat('COMMA'); // optional comma
|
|
515
641
|
}
|
|
516
642
|
if (this.current.type === 'RPAREN') this.eat('RPAREN');
|
|
517
|
-
node = { type: 'CallExpression', callee: node, arguments: args };
|
|
643
|
+
node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
|
|
518
644
|
continue;
|
|
519
645
|
}
|
|
520
646
|
|
|
521
|
-
if (
|
|
647
|
+
if (t.type === 'DOT') {
|
|
648
|
+
const startLine = t.line;
|
|
649
|
+
const startCol = t.column;
|
|
522
650
|
this.eat('DOT');
|
|
523
651
|
const property = this.current.value || '';
|
|
524
652
|
if (this.current.type === 'IDENTIFIER') this.eat('IDENTIFIER');
|
|
525
|
-
node = { type: 'MemberExpression', object: node, property };
|
|
653
|
+
node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
|
|
526
654
|
continue;
|
|
527
655
|
}
|
|
528
656
|
|
|
529
|
-
if (
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
node = { type: 'UpdateExpression', operator: op, argument: node, prefix: false };
|
|
657
|
+
if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
|
|
658
|
+
this.eat(t.type);
|
|
659
|
+
node = { type: 'UpdateExpression', operator: t.type, argument: node, prefix: false, line: t.line, column: t.column };
|
|
533
660
|
continue;
|
|
534
661
|
}
|
|
535
662
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const argNode = { type: 'Identifier', name: this.current.value };
|
|
663
|
+
if (node.type === 'Identifier' && t.type === 'IDENTIFIER') {
|
|
664
|
+
const argNode = { type: 'Identifier', name: t.value, line: t.line, column: t.column };
|
|
539
665
|
this.eat('IDENTIFIER');
|
|
540
|
-
node = { type: 'CallExpression', callee: node, arguments: [argNode] };
|
|
666
|
+
node = { type: 'CallExpression', callee: node, arguments: [argNode], line: node.line, column: node.column };
|
|
541
667
|
continue;
|
|
542
668
|
}
|
|
543
669
|
|
|
@@ -547,12 +673,18 @@ postfix() {
|
|
|
547
673
|
}
|
|
548
674
|
|
|
549
675
|
arrowFunction(params) {
|
|
550
|
-
|
|
676
|
+
const t = this.current;
|
|
677
|
+
if (t.type === 'ARROW') this.eat('ARROW');
|
|
551
678
|
const body = this.expression();
|
|
679
|
+
const startLine = params.length > 0 ? params[0].line : t.line;
|
|
680
|
+
const startCol = params.length > 0 ? params[0].column : t.column;
|
|
681
|
+
|
|
552
682
|
return {
|
|
553
683
|
type: 'ArrowFunctionExpression',
|
|
554
684
|
params,
|
|
555
|
-
body
|
|
685
|
+
body,
|
|
686
|
+
line: startLine,
|
|
687
|
+
column: startCol
|
|
556
688
|
};
|
|
557
689
|
}
|
|
558
690
|
|
|
@@ -560,16 +692,35 @@ arrowFunction(params) {
|
|
|
560
692
|
primary() {
|
|
561
693
|
const t = this.current;
|
|
562
694
|
|
|
563
|
-
if (t.type === 'NUMBER') {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
695
|
+
if (t.type === 'NUMBER') {
|
|
696
|
+
this.eat('NUMBER');
|
|
697
|
+
return { type: 'Literal', value: t.value, line: t.line, column: t.column };
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (t.type === 'STRING') {
|
|
701
|
+
this.eat('STRING');
|
|
702
|
+
return { type: 'Literal', value: t.value, line: t.line, column: t.column };
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (t.type === 'TRUE') {
|
|
706
|
+
this.eat('TRUE');
|
|
707
|
+
return { type: 'Literal', value: true, line: t.line, column: t.column };
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (t.type === 'FALSE') {
|
|
711
|
+
this.eat('FALSE');
|
|
712
|
+
return { type: 'Literal', value: false, line: t.line, column: t.column };
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (t.type === 'NULL') {
|
|
716
|
+
this.eat('NULL');
|
|
717
|
+
return { type: 'Literal', value: null, line: t.line, column: t.column };
|
|
718
|
+
}
|
|
568
719
|
|
|
569
720
|
if (t.type === 'AWAIT') {
|
|
570
721
|
this.eat('AWAIT');
|
|
571
722
|
const argument = this.expression();
|
|
572
|
-
return { type: 'AwaitExpression', argument };
|
|
723
|
+
return { type: 'AwaitExpression', argument, line: t.line, column: t.column };
|
|
573
724
|
}
|
|
574
725
|
|
|
575
726
|
if (t.type === 'NEW') {
|
|
@@ -585,7 +736,7 @@ arrowFunction(params) {
|
|
|
585
736
|
}
|
|
586
737
|
}
|
|
587
738
|
this.eat('RPAREN');
|
|
588
|
-
return { type: 'NewExpression', callee, arguments: args };
|
|
739
|
+
return { type: 'NewExpression', callee, arguments: args, line: t.line, column: t.column };
|
|
589
740
|
}
|
|
590
741
|
|
|
591
742
|
if (t.type === 'ASK') {
|
|
@@ -600,7 +751,13 @@ arrowFunction(params) {
|
|
|
600
751
|
}
|
|
601
752
|
}
|
|
602
753
|
this.eat('RPAREN');
|
|
603
|
-
return {
|
|
754
|
+
return {
|
|
755
|
+
type: 'CallExpression',
|
|
756
|
+
callee: { type: 'Identifier', name: 'ask', line: t.line, column: t.column },
|
|
757
|
+
arguments: args,
|
|
758
|
+
line: t.line,
|
|
759
|
+
column: t.column
|
|
760
|
+
};
|
|
604
761
|
}
|
|
605
762
|
|
|
606
763
|
if (t.type === 'IDENTIFIER') {
|
|
@@ -608,13 +765,15 @@ arrowFunction(params) {
|
|
|
608
765
|
this.eat('IDENTIFIER');
|
|
609
766
|
|
|
610
767
|
if (this.current.type === 'ARROW') {
|
|
611
|
-
return this.arrowFunction([name]);
|
|
768
|
+
return this.arrowFunction([{ type: 'Identifier', name, line: t.line, column: t.column }]);
|
|
612
769
|
}
|
|
613
770
|
|
|
614
|
-
return { type: 'Identifier', name };
|
|
771
|
+
return { type: 'Identifier', name, line: t.line, column: t.column };
|
|
615
772
|
}
|
|
616
773
|
|
|
617
774
|
if (t.type === 'LPAREN') {
|
|
775
|
+
const startLine = t.line;
|
|
776
|
+
const startCol = t.column;
|
|
618
777
|
this.eat('LPAREN');
|
|
619
778
|
const elements = [];
|
|
620
779
|
|
|
@@ -630,10 +789,12 @@ arrowFunction(params) {
|
|
|
630
789
|
|
|
631
790
|
if (this.current.type === 'ARROW') return this.arrowFunction(elements);
|
|
632
791
|
|
|
633
|
-
return elements.length === 1 ? elements[0] : { type: 'ArrayExpression', elements };
|
|
792
|
+
return elements.length === 1 ? elements[0] : { type: 'ArrayExpression', elements, line: startLine, column: startCol };
|
|
634
793
|
}
|
|
635
794
|
|
|
636
795
|
if (t.type === 'LBRACKET') {
|
|
796
|
+
const startLine = t.line;
|
|
797
|
+
const startCol = t.column;
|
|
637
798
|
this.eat('LBRACKET');
|
|
638
799
|
const elements = [];
|
|
639
800
|
if (this.current.type !== 'RBRACKET') {
|
|
@@ -644,10 +805,12 @@ arrowFunction(params) {
|
|
|
644
805
|
}
|
|
645
806
|
}
|
|
646
807
|
this.eat('RBRACKET');
|
|
647
|
-
return { type: 'ArrayExpression', elements };
|
|
808
|
+
return { type: 'ArrayExpression', elements, line: startLine, column: startCol };
|
|
648
809
|
}
|
|
649
810
|
|
|
650
811
|
if (t.type === 'LBRACE') {
|
|
812
|
+
const startLine = t.line;
|
|
813
|
+
const startCol = t.column;
|
|
651
814
|
this.eat('LBRACE');
|
|
652
815
|
const props = [];
|
|
653
816
|
while (this.current.type !== 'RBRACE') {
|
|
@@ -658,17 +821,17 @@ arrowFunction(params) {
|
|
|
658
821
|
if (this.current.type === 'COMMA') this.eat('COMMA'); // optional trailing comma
|
|
659
822
|
}
|
|
660
823
|
this.eat('RBRACE');
|
|
661
|
-
return { type: 'ObjectExpression', props };
|
|
824
|
+
return { type: 'ObjectExpression', props, line: startLine, column: startCol };
|
|
662
825
|
}
|
|
663
826
|
|
|
664
827
|
throw new ParseError(
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
);
|
|
669
|
-
|
|
828
|
+
`Unexpected token '${t.type}'`,
|
|
829
|
+
t,
|
|
830
|
+
this.source
|
|
831
|
+
);
|
|
670
832
|
}
|
|
671
833
|
|
|
834
|
+
|
|
672
835
|
}
|
|
673
836
|
|
|
674
837
|
module.exports = Parser;
|