starlight-cli 1.0.21 → 1.0.23
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 +291 -253
- package/package.json +1 -1
- package/src/evaluator.js +141 -86
- package/src/lexer.js +114 -109
- package/src/parser.js +35 -55
- package/src/starlight.js +1 -1
package/src/parser.js
CHANGED
|
@@ -12,7 +12,10 @@ class Parser {
|
|
|
12
12
|
|
|
13
13
|
eat(type) {
|
|
14
14
|
if (this.current.type === type) this.advance();
|
|
15
|
-
else throw new Error(
|
|
15
|
+
else throw new Error(
|
|
16
|
+
`Expected ${type}, got ${this.current.type} at line ${this.current.line}, column ${this.current.column}`
|
|
17
|
+
);
|
|
18
|
+
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
peekType(offset = 1) {
|
|
@@ -99,52 +102,52 @@ class Parser {
|
|
|
99
102
|
const body = this.block();
|
|
100
103
|
return { type: 'WhileStatement', test, body };
|
|
101
104
|
}
|
|
102
|
-
|
|
103
105
|
importStatement() {
|
|
104
106
|
this.eat('IMPORT');
|
|
105
107
|
|
|
106
108
|
let specifiers = [];
|
|
107
109
|
|
|
108
110
|
if (this.current.type === 'STAR') {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
this.eat('STAR');
|
|
112
|
+
this.eat('AS');
|
|
113
|
+
const name = this.current.value;
|
|
114
|
+
this.eat('IDENTIFIER');
|
|
115
|
+
specifiers.push({ type: 'NamespaceImport', local: name });
|
|
114
116
|
}
|
|
115
117
|
else if (this.current.type === 'LBRACE') {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
this.eat('LBRACE');
|
|
119
|
+
while (this.current.type !== 'RBRACE') {
|
|
120
|
+
const importedName = this.current.value;
|
|
121
|
+
this.eat('IDENTIFIER');
|
|
122
|
+
let localName = importedName;
|
|
123
|
+
if (this.current.type === 'AS') {
|
|
124
|
+
this.eat('AS');
|
|
125
|
+
localName = this.current.value;
|
|
126
|
+
this.eat('IDENTIFIER');
|
|
127
|
+
}
|
|
128
|
+
specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
|
|
129
|
+
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
125
130
|
}
|
|
126
|
-
|
|
127
|
-
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
128
|
-
}
|
|
129
|
-
this.eat('RBRACE');
|
|
131
|
+
this.eat('RBRACE');
|
|
130
132
|
}
|
|
131
133
|
else if (this.current.type === 'IDENTIFIER') {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
const localName = this.current.value;
|
|
135
|
+
this.eat('IDENTIFIER');
|
|
136
|
+
specifiers.push({ type: 'DefaultImport', local: localName });
|
|
135
137
|
} else {
|
|
136
|
-
|
|
138
|
+
throw new Error(`Unexpected token in import statement at line ${this.current.line}, column ${this.current.column}`);
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
this.eat('FROM');
|
|
140
142
|
const pathToken = this.current;
|
|
141
|
-
if (pathToken.type !== 'STRING') throw new Error(
|
|
143
|
+
if (pathToken.type !== 'STRING') throw new Error(`Expected string literal after from in import at line ${this.current.line}, column ${this.current.column}`);
|
|
142
144
|
this.eat('STRING');
|
|
143
145
|
|
|
144
146
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
145
147
|
|
|
146
148
|
return { type: 'ImportStatement', path: pathToken.value, specifiers };
|
|
147
|
-
|
|
149
|
+
}
|
|
150
|
+
|
|
148
151
|
|
|
149
152
|
forStatement() {
|
|
150
153
|
this.eat('FOR');
|
|
@@ -224,23 +227,6 @@ class Parser {
|
|
|
224
227
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
225
228
|
return { type: 'ExpressionStatement', expression: expr };
|
|
226
229
|
}
|
|
227
|
-
parseTemplateLiteral() {
|
|
228
|
-
const parts = [];
|
|
229
|
-
while (true) {
|
|
230
|
-
if (this.current.type === 'TEMPLATE_STRING') {
|
|
231
|
-
parts.push({ type: 'Literal', value: this.current.value });
|
|
232
|
-
this.eat('TEMPLATE_STRING');
|
|
233
|
-
} else if (this.current.type === 'DOLLAR_LBRACE') {
|
|
234
|
-
this.eat('DOLLAR_LBRACE');
|
|
235
|
-
const expr = this.expression();
|
|
236
|
-
parts.push(expr);
|
|
237
|
-
this.eat('RBRACE');
|
|
238
|
-
} else {
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return { type: 'TemplateLiteral', parts };
|
|
243
|
-
}
|
|
244
230
|
|
|
245
231
|
expression() {
|
|
246
232
|
return this.assignment();
|
|
@@ -287,7 +273,7 @@ class Parser {
|
|
|
287
273
|
|
|
288
274
|
equality() {
|
|
289
275
|
let node = this.comparison();
|
|
290
|
-
while (['EQEQ', 'NOTEQ'
|
|
276
|
+
while (['EQEQ', 'NOTEQ'].includes(this.current.type)) {
|
|
291
277
|
const op = this.current.type;
|
|
292
278
|
this.eat(op);
|
|
293
279
|
node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison() };
|
|
@@ -297,7 +283,7 @@ class Parser {
|
|
|
297
283
|
|
|
298
284
|
comparison() {
|
|
299
285
|
let node = this.term();
|
|
300
|
-
while (['LT', 'LTE', 'GT', 'GTE'
|
|
286
|
+
while (['LT', 'LTE', 'GT', 'GTE'].includes(this.current.type)) {
|
|
301
287
|
const op = this.current.type;
|
|
302
288
|
this.eat(op);
|
|
303
289
|
node = { type: 'BinaryExpression', operator: op, left: node, right: this.term() };
|
|
@@ -366,7 +352,7 @@ class Parser {
|
|
|
366
352
|
}
|
|
367
353
|
if (this.current.type === 'DOT') {
|
|
368
354
|
this.eat('DOT');
|
|
369
|
-
if (this.current.type !== 'IDENTIFIER') throw new Error(
|
|
355
|
+
if (this.current.type !== 'IDENTIFIER') throw new Error(`Expected property name after dot at line ${this.current.line}, column ${this.current.column}`);
|
|
370
356
|
const property = this.current.value;
|
|
371
357
|
this.eat('IDENTIFIER');
|
|
372
358
|
node = { type: 'MemberExpression', object: node, property };
|
|
@@ -388,15 +374,9 @@ class Parser {
|
|
|
388
374
|
|
|
389
375
|
if (t.type === 'NUMBER') { this.eat('NUMBER'); return { type: 'Literal', value: t.value }; }
|
|
390
376
|
if (t.type === 'STRING') { this.eat('STRING'); return { type: 'Literal', value: t.value }; }
|
|
391
|
-
if (t.type === 'TEMPLATE_STRING') {
|
|
392
|
-
return this.parseTemplateLiteral();
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
396
377
|
if (t.type === 'TRUE') { this.eat('TRUE'); return { type: 'Literal', value: true }; }
|
|
397
378
|
if (t.type === 'FALSE') { this.eat('FALSE'); return { type: 'Literal', value: false }; }
|
|
398
379
|
if (t.type === 'NULL') { this.eat('NULL'); return { type: 'Literal', value: null }; }
|
|
399
|
-
if (t.type === 'UNDEFINED') { this.eat('UNDEFINED'); return { type: 'Literal', value: undefined }; }
|
|
400
380
|
|
|
401
381
|
if (t.type === 'ASK') {
|
|
402
382
|
this.eat('ASK');
|
|
@@ -447,7 +427,7 @@ class Parser {
|
|
|
447
427
|
let key;
|
|
448
428
|
if (this.current.type === 'STRING') { key = this.current.value; this.eat('STRING'); }
|
|
449
429
|
else if (this.current.type === 'IDENTIFIER') { key = this.current.value; this.eat('IDENTIFIER'); }
|
|
450
|
-
else throw new Error(
|
|
430
|
+
else throw new Error(`Invalid object key at line ${this.current.line}, column ${this.current.column}`);
|
|
451
431
|
this.eat('COLON');
|
|
452
432
|
const value = this.expression();
|
|
453
433
|
props.push({ key, value });
|
|
@@ -457,8 +437,8 @@ class Parser {
|
|
|
457
437
|
return { type: 'ObjectExpression', props };
|
|
458
438
|
}
|
|
459
439
|
|
|
460
|
-
throw new Error(`Unexpected token in primary: ${t.type}`);
|
|
440
|
+
throw new Error(`Unexpected token in primary: ${t.type} at line ${this.current.line}, column ${this.current.column}`);
|
|
461
441
|
}
|
|
462
442
|
}
|
|
463
443
|
|
|
464
|
-
module.exports = Parser;
|
|
444
|
+
module.exports = Parser;
|