amis-formula 1.3.13 → 2.0.0-beta.0

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/src/parser.ts ADDED
@@ -0,0 +1,875 @@
1
+ import {lexer as createLexer, TokenEnum, TokenName} from './lexer';
2
+ import {ParserOptions, ASTNode, Token, ASTNodeOrNull, Position} from './types';
3
+
4
+ const argListStates = {
5
+ START: 0,
6
+ COMMA: 1,
7
+ SET: 2
8
+ };
9
+
10
+ const tempalteStates = {
11
+ START: 0,
12
+ SCRIPTING: 1
13
+ };
14
+
15
+ const objectStates = {
16
+ START: 0,
17
+ KEY: 1,
18
+ COLON: 2,
19
+ VALUE: 3,
20
+ COMMA: 4
21
+ };
22
+
23
+ export function parse(input: string, options?: ParserOptions): ASTNode {
24
+ let token: Token;
25
+ const lexer = createLexer(input, options);
26
+ const tokens: Array<Token> = [];
27
+ const tokenChunk: Array<Token> = [];
28
+
29
+ // 允许的变量名字空间
30
+ let variableNamespaces: Array<string> = options?.variableNamespaces ?? [
31
+ 'window',
32
+ 'cookie',
33
+ 'ls',
34
+ 'ss'
35
+ ];
36
+ if (!Array.isArray(variableNamespaces)) {
37
+ variableNamespaces = [];
38
+ }
39
+
40
+ function next() {
41
+ token = tokenChunk.length ? tokenChunk.shift()! : lexer.next();
42
+
43
+ if (!token) {
44
+ throw new TypeError('next token is undefined');
45
+ }
46
+ tokens.push(token);
47
+ }
48
+
49
+ function back() {
50
+ tokenChunk.unshift(tokens.pop()!);
51
+ token = tokens[tokens.length - 1];
52
+ }
53
+
54
+ function matchPunctuator(operator: string | Array<string>) {
55
+ return (
56
+ token.type === TokenName[TokenEnum.Punctuator] &&
57
+ (Array.isArray(operator)
58
+ ? ~operator.indexOf(token.value!)
59
+ : token.value === operator)
60
+ );
61
+ }
62
+
63
+ function fatal() {
64
+ throw TypeError(
65
+ `Unexpected token ${token!.value} in ${token!.start.line}:${
66
+ token!.start.column
67
+ }`
68
+ );
69
+ }
70
+
71
+ function assert(result: any) {
72
+ if (!result) {
73
+ fatal();
74
+ }
75
+ return result;
76
+ }
77
+
78
+ function expression(): ASTNodeOrNull {
79
+ return assignmentExpression();
80
+ }
81
+
82
+ function skipWhiteSpaceChar() {
83
+ while (
84
+ token.type === TokenName[TokenEnum.Char] &&
85
+ /^\s+$/m.test(token.value)
86
+ ) {
87
+ next();
88
+ }
89
+ }
90
+
91
+ function collectFilterArg() {
92
+ const arg: Array<any> = [];
93
+ while (
94
+ !matchPunctuator(':') &&
95
+ token.type !== TokenName[TokenEnum.OpenFilter] &&
96
+ token.type !== TokenName[TokenEnum.CloseScript]
97
+ ) {
98
+ const item =
99
+ literal() ||
100
+ numberLiteral() ||
101
+ stringLiteral() ||
102
+ template() ||
103
+ arrayLiteral() ||
104
+ rawScript() ||
105
+ objectLiteral();
106
+
107
+ if (item) {
108
+ arg.push(item);
109
+ } else {
110
+ assert(
111
+ ~[
112
+ TokenName[TokenEnum.Identifier],
113
+ TokenName[TokenEnum.Punctuator],
114
+ TokenName[TokenEnum.Char]
115
+ ].indexOf(token.type)
116
+ );
117
+
118
+ // 其他的都当字符处理
119
+ if (arg.length && typeof arg[arg.length - 1] === 'string') {
120
+ arg[arg.length - 1] += token.raw || token.value;
121
+ } else {
122
+ arg.push(token.raw || token.value);
123
+ }
124
+ next();
125
+ }
126
+ }
127
+ if (arg.length && typeof arg[arg.length - 1] === 'string') {
128
+ arg[arg.length - 1] = arg[arg.length - 1].replace(/\s+$/, '');
129
+ if (!arg[arg.length - 1]) {
130
+ arg.pop();
131
+ }
132
+ }
133
+ return arg;
134
+ }
135
+
136
+ function complexExpression(): ASTNodeOrNull {
137
+ let ast = expression();
138
+
139
+ const filters: Array<any> = [];
140
+ while (token.type === TokenName[TokenEnum.OpenFilter]) {
141
+ next();
142
+
143
+ skipWhiteSpaceChar();
144
+ const name = assert(identifier());
145
+ const fnName = name.name;
146
+ const args = [];
147
+
148
+ skipWhiteSpaceChar();
149
+ while (matchPunctuator(':')) {
150
+ next();
151
+ skipWhiteSpaceChar();
152
+
153
+ let argContents: any = collectFilterArg();
154
+ if (argContents.length === 1) {
155
+ argContents = argContents[0];
156
+ } else if (!argContents.length) {
157
+ argContents = '';
158
+ }
159
+
160
+ args.push(
161
+ Array.isArray(argContents)
162
+ ? {
163
+ type: 'mixed',
164
+ body: argContents
165
+ }
166
+ : argContents
167
+ );
168
+ }
169
+ filters.push({
170
+ name: fnName,
171
+ args
172
+ });
173
+ }
174
+
175
+ if (filters.length) {
176
+ ast = {
177
+ type: 'filter',
178
+ input: ast,
179
+ filters,
180
+ start: ast!.start,
181
+ end: filters[filters.length - 1].end
182
+ };
183
+ }
184
+
185
+ return ast;
186
+ }
187
+
188
+ function arrowFunction(): ASTNodeOrNull {
189
+ let ast: any = argList() || variable();
190
+ let args: Array<any> = [];
191
+ let start: Position;
192
+
193
+ if (ast?.type === 'variable') {
194
+ args = [ast];
195
+ start = ast.start;
196
+ } else if (ast?.type === 'arg-list') {
197
+ start = ast.start;
198
+ args = ast.body;
199
+ }
200
+
201
+ if (Array.isArray(args) && matchPunctuator('=')) {
202
+ next();
203
+ if (matchPunctuator('>')) {
204
+ next();
205
+ const body = assert(expression());
206
+ return {
207
+ type: 'anonymous_function',
208
+ args: args,
209
+ return: body,
210
+ start: start!,
211
+ end: body.end
212
+ };
213
+ } else {
214
+ back();
215
+ }
216
+ }
217
+
218
+ return ast;
219
+ }
220
+
221
+ function conditionalExpression(): ASTNodeOrNull {
222
+ const ast = logicalOrExpression();
223
+
224
+ if (!ast) {
225
+ return null;
226
+ }
227
+
228
+ if (matchPunctuator('?')) {
229
+ next();
230
+ let consequent = assignmentExpression();
231
+ assert(consequent);
232
+ assert(matchPunctuator(':'));
233
+
234
+ next();
235
+ let alternate = assignmentExpression();
236
+ assert(alternate);
237
+
238
+ return {
239
+ type: 'conditional',
240
+ test: ast,
241
+ consequent: consequent,
242
+ alternate: alternate,
243
+ start: ast.start,
244
+ end: alternate!.end
245
+ };
246
+ }
247
+
248
+ return ast;
249
+ }
250
+
251
+ function binaryExpressionParser(
252
+ type: string,
253
+ operator: string,
254
+ parseFunction: () => any,
255
+ rightParseFunction = parseFunction,
256
+ leftKey = 'left',
257
+ rightKey = 'right'
258
+ ) {
259
+ let ast = parseFunction();
260
+ if (!ast) {
261
+ return null;
262
+ }
263
+
264
+ if (matchPunctuator(operator)) {
265
+ while (matchPunctuator(operator)) {
266
+ next();
267
+ const right = assert(rightParseFunction());
268
+
269
+ ast = {
270
+ type: type,
271
+ op: operator,
272
+ [leftKey]: ast,
273
+ [rightKey]: right,
274
+ start: ast.start,
275
+ end: right.end
276
+ };
277
+ }
278
+ }
279
+
280
+ return ast;
281
+ }
282
+
283
+ function logicalOrExpression(): ASTNodeOrNull {
284
+ return binaryExpressionParser('or', '||', logicalAndExpression);
285
+ }
286
+
287
+ function logicalAndExpression(): ASTNodeOrNull {
288
+ return binaryExpressionParser('and', '&&', bitwiseOrExpression);
289
+ }
290
+
291
+ function bitwiseOrExpression(): ASTNodeOrNull {
292
+ return binaryExpressionParser('binary', '|', bitwiseXOrExpression);
293
+ }
294
+
295
+ function bitwiseXOrExpression(): ASTNodeOrNull {
296
+ return binaryExpressionParser('binary', '^', bitwiseAndExpression);
297
+ }
298
+
299
+ function bitwiseAndExpression(): ASTNodeOrNull {
300
+ return binaryExpressionParser('binary', '&', equalityExpression);
301
+ }
302
+
303
+ function equalityExpression(): ASTNodeOrNull {
304
+ return binaryExpressionParser('eq', '==', () =>
305
+ binaryExpressionParser('ne', '!=', () =>
306
+ binaryExpressionParser('streq', '===', () =>
307
+ binaryExpressionParser('strneq', '!==', relationalExpression)
308
+ )
309
+ )
310
+ );
311
+ }
312
+
313
+ function relationalExpression(): ASTNodeOrNull {
314
+ return binaryExpressionParser('lt', '<', () =>
315
+ binaryExpressionParser('gt', '>', () =>
316
+ binaryExpressionParser('le', '<=', () =>
317
+ binaryExpressionParser('ge', '>=', shiftExpression)
318
+ )
319
+ )
320
+ );
321
+ }
322
+
323
+ function shiftExpression(): ASTNodeOrNull {
324
+ return binaryExpressionParser('shift', '<<', () =>
325
+ binaryExpressionParser('shift', '>>', () =>
326
+ binaryExpressionParser('shift', '>>>', additiveExpression)
327
+ )
328
+ );
329
+ }
330
+
331
+ function additiveExpression(): ASTNodeOrNull {
332
+ return binaryExpressionParser('add', '+', () =>
333
+ binaryExpressionParser('minus', '-', multiplicativeExpression)
334
+ );
335
+ }
336
+
337
+ function multiplicativeExpression(): ASTNodeOrNull {
338
+ return binaryExpressionParser('multiply', '*', () =>
339
+ binaryExpressionParser('divide', '/', () =>
340
+ binaryExpressionParser('remainder', '%', powerExpression)
341
+ )
342
+ );
343
+ }
344
+
345
+ function powerExpression(): ASTNodeOrNull {
346
+ return binaryExpressionParser('power', '**', unaryExpression);
347
+ }
348
+
349
+ function unaryExpression(): ASTNodeOrNull {
350
+ const unaryOperators = ['+', '-', '~', '!'];
351
+ const stack: Array<any> = [];
352
+ while (matchPunctuator(unaryOperators)) {
353
+ stack.push(token);
354
+ next();
355
+ }
356
+ let ast: any = postfixExpression();
357
+ assert(!stack.length || ast);
358
+ while (stack.length) {
359
+ const op = stack.pop();
360
+
361
+ ast = {
362
+ type: 'unary',
363
+ op: op.value,
364
+ value: ast,
365
+ start: op.start,
366
+ end: op.end
367
+ };
368
+ }
369
+ return ast;
370
+ }
371
+
372
+ function postfixExpression(
373
+ parseFunction: () => any = leftHandSideExpression
374
+ ): ASTNodeOrNull {
375
+ let ast = parseFunction();
376
+ if (!ast) {
377
+ return null;
378
+ }
379
+
380
+ while (matchPunctuator('[') || matchPunctuator('.')) {
381
+ const isDot = matchPunctuator('.');
382
+ next();
383
+ const right = assert(
384
+ isDot ? identifier() || numberLiteral() || rawScript() : expression()
385
+ );
386
+
387
+ if (!isDot) {
388
+ assert(matchPunctuator(']'));
389
+ next();
390
+ }
391
+
392
+ ast = {
393
+ type: 'getter',
394
+ host: ast,
395
+ key: right,
396
+ start: ast.start,
397
+ end: right.end
398
+ };
399
+ }
400
+
401
+ return ast;
402
+ }
403
+
404
+ function leftHandSideExpression(): ASTNodeOrNull {
405
+ return functionCall() || arrowFunction() || primaryExpression();
406
+ }
407
+
408
+ function varibleKey(allowVariable = false, inObject = false): ASTNodeOrNull {
409
+ return (
410
+ (allowVariable ? variable() : identifier()) ||
411
+ stringLiteral() ||
412
+ numberLiteral() ||
413
+ (inObject ? objectTemplateKey() : template())
414
+ );
415
+ }
416
+
417
+ function objectTemplateKey(): ASTNodeOrNull {
418
+ if (matchPunctuator('[')) {
419
+ next();
420
+ const key = assert(template());
421
+ assert(matchPunctuator(']'));
422
+ next();
423
+ return key;
424
+ }
425
+ return null;
426
+ }
427
+
428
+ function stringLiteral(): ASTNodeOrNull {
429
+ if (token.type === TokenName[TokenEnum.StringLiteral]) {
430
+ const cToken = token;
431
+ next();
432
+ return {
433
+ type: 'string',
434
+ value: cToken.value,
435
+ start: cToken.start,
436
+ end: cToken.end
437
+ };
438
+ }
439
+ return null;
440
+ }
441
+
442
+ function numberLiteral(): ASTNodeOrNull {
443
+ if (token.type === TokenName[TokenEnum.NumericLiteral]) {
444
+ const value = token.value;
445
+ const cToken = token;
446
+ next();
447
+ return {
448
+ type: 'literal',
449
+ value: value,
450
+ start: cToken.start,
451
+ end: cToken.end
452
+ };
453
+ }
454
+
455
+ return null;
456
+ }
457
+
458
+ function template(): ASTNodeOrNull {
459
+ if (matchPunctuator('`')) {
460
+ const start = token;
461
+ let end = start;
462
+ next();
463
+ let state = tempalteStates.START;
464
+ const ast: ASTNode = {
465
+ type: 'template',
466
+ body: [],
467
+ start: start.start,
468
+ end: start.end
469
+ };
470
+ while (true) {
471
+ if (state === tempalteStates.SCRIPTING) {
472
+ const exp = assert(expression());
473
+ ast.body.push(exp);
474
+ assert(token.type === TokenName[TokenEnum.TemplateRightBrace]);
475
+ next();
476
+ state = tempalteStates.START;
477
+ } else {
478
+ if (matchPunctuator('`')) {
479
+ end = token;
480
+ next();
481
+ break;
482
+ } else if (token.type === TokenName[TokenEnum.TemplateLeftBrace]) {
483
+ next();
484
+ state = tempalteStates.SCRIPTING;
485
+ } else if (token.type === TokenName[TokenEnum.TemplateRaw]) {
486
+ ast.body.push({
487
+ type: 'template_raw',
488
+ value: token.value,
489
+ start: token.start,
490
+ end: token.end
491
+ });
492
+ next();
493
+ } else {
494
+ fatal();
495
+ }
496
+ }
497
+ }
498
+
499
+ ast.end = end.end;
500
+ return ast;
501
+ }
502
+ return null;
503
+ }
504
+
505
+ function identifier(): ASTNodeOrNull {
506
+ if (token.type === TokenName[TokenEnum.Identifier]) {
507
+ const cToken = token;
508
+ next();
509
+ return {
510
+ type: 'identifier',
511
+ name: cToken.value,
512
+ start: cToken.start,
513
+ end: cToken.end
514
+ };
515
+ }
516
+ return null;
517
+ }
518
+
519
+ function primaryExpression(): ASTNodeOrNull {
520
+ return (
521
+ variable() ||
522
+ literal() ||
523
+ numberLiteral() ||
524
+ stringLiteral() ||
525
+ template() ||
526
+ arrayLiteral() ||
527
+ objectLiteral() ||
528
+ (() => {
529
+ const ast = expressionList();
530
+
531
+ if (ast?.body.length === 1) {
532
+ return ast.body[0];
533
+ }
534
+
535
+ return ast;
536
+ })() ||
537
+ rawScript()
538
+ );
539
+ }
540
+
541
+ function literal(): ASTNodeOrNull {
542
+ if (
543
+ token.type === TokenName[TokenEnum.Literal] ||
544
+ token.type === TokenName[TokenEnum.BooleanLiteral]
545
+ ) {
546
+ const value = token.value;
547
+ const cToken = token;
548
+ next();
549
+ return {
550
+ type: 'literal',
551
+ value: value,
552
+ start: cToken.start,
553
+ end: cToken.end
554
+ };
555
+ }
556
+
557
+ return null;
558
+ }
559
+
560
+ function functionCall(): ASTNodeOrNull {
561
+ if (token.type === TokenName[TokenEnum.Identifier]) {
562
+ const id = token;
563
+ next();
564
+ if (matchPunctuator('(')) {
565
+ const argList = expressionList();
566
+ assert(argList);
567
+ return {
568
+ type: 'func_call',
569
+ identifier: id.value,
570
+ args: argList?.body,
571
+ start: id.start,
572
+ end: argList!.end
573
+ };
574
+ } else {
575
+ back();
576
+ }
577
+ }
578
+ return null;
579
+ }
580
+
581
+ function arrayLiteral(): ASTNodeOrNull {
582
+ if (matchPunctuator('[')) {
583
+ const argList = expressionList('[', ']');
584
+ assert(argList);
585
+ return {
586
+ type: 'array',
587
+ members: argList?.body,
588
+ start: argList!.start,
589
+ end: argList!.end
590
+ };
591
+ }
592
+ return null;
593
+ }
594
+
595
+ function expressionList(startOP = '(', endOp = ')'): ASTNodeOrNull {
596
+ if (matchPunctuator(startOP)) {
597
+ const start = token;
598
+ let end: Token;
599
+ next();
600
+ const args: Array<any> = [];
601
+ let state = argListStates.START;
602
+
603
+ while (true) {
604
+ if (state === argListStates.COMMA || !matchPunctuator(endOp)) {
605
+ const arg = assert(expression());
606
+ args.push(arg);
607
+ state = argListStates.START;
608
+
609
+ if (matchPunctuator(',')) {
610
+ next();
611
+ state = argListStates.COMMA;
612
+ }
613
+ } else if (matchPunctuator(endOp)) {
614
+ end = token;
615
+ next();
616
+ break;
617
+ }
618
+ }
619
+ return {
620
+ type: 'expression-list',
621
+ body: args,
622
+ start: start.start,
623
+ end: end!.end
624
+ };
625
+ }
626
+ return null;
627
+ }
628
+
629
+ function argList(startOP = '(', endOp = ')'): ASTNodeOrNull {
630
+ let count = 0;
631
+ let rollback = () => {
632
+ while (count-- > 0) {
633
+ back();
634
+ }
635
+ return null;
636
+ };
637
+ if (matchPunctuator(startOP)) {
638
+ const start = token;
639
+ let end: Token = start;
640
+ next();
641
+ count++;
642
+ const args: Array<any> = [];
643
+ let state = argListStates.START;
644
+
645
+ while (!matchPunctuator(endOp)) {
646
+ if (state === argListStates.COMMA || state === argListStates.START) {
647
+ const arg = variable(false);
648
+
649
+ if (!arg) {
650
+ return rollback();
651
+ }
652
+
653
+ count++;
654
+ args.push(arg);
655
+ state = argListStates.SET;
656
+ } else if (state === argListStates.SET && matchPunctuator(',')) {
657
+ next();
658
+ count++;
659
+ state = argListStates.COMMA;
660
+ } else {
661
+ return rollback();
662
+ }
663
+ }
664
+
665
+ if (matchPunctuator(endOp)) {
666
+ end = token;
667
+ next();
668
+ return {
669
+ type: 'arg-list',
670
+ body: args,
671
+ start: start.start,
672
+ end: end.end
673
+ };
674
+ } else {
675
+ return rollback();
676
+ }
677
+ }
678
+ return null;
679
+ }
680
+
681
+ function objectLiteral(): ASTNodeOrNull {
682
+ if (matchPunctuator('{')) {
683
+ const start = token;
684
+ let end = start;
685
+ next();
686
+ let ast: ASTNode = {
687
+ type: 'object',
688
+ members: [],
689
+ start: start.start,
690
+ end: start.end
691
+ };
692
+ let state = objectStates.START;
693
+ let key: any, value: any;
694
+ while (true) {
695
+ if (state === objectStates.KEY) {
696
+ assert(matchPunctuator(':'));
697
+ next();
698
+ state = objectStates.COLON;
699
+ } else if (state === objectStates.COLON) {
700
+ value = assert(expression());
701
+ ast.members.push({
702
+ key,
703
+ value
704
+ });
705
+ state = objectStates.VALUE;
706
+ } else if (state === objectStates.VALUE) {
707
+ if (matchPunctuator(',')) {
708
+ next();
709
+ state = objectStates.COMMA;
710
+ } else if (matchPunctuator('}')) {
711
+ end = token;
712
+ next();
713
+ break;
714
+ } else {
715
+ fatal();
716
+ }
717
+ } else {
718
+ if (state != objectStates.COMMA && matchPunctuator('}')) {
719
+ end = token;
720
+ next();
721
+ break;
722
+ }
723
+
724
+ key = assert(varibleKey(false, true));
725
+ state = objectStates.KEY;
726
+ }
727
+ }
728
+
729
+ ast.end = end.end;
730
+ return ast;
731
+ }
732
+ return null;
733
+ }
734
+
735
+ function assignmentExpression(): ASTNodeOrNull {
736
+ return conditionalExpression();
737
+ }
738
+
739
+ function contents(): ASTNodeOrNull {
740
+ const node: ASTNode = {
741
+ type: 'document',
742
+ body: [],
743
+ start: token.start,
744
+ end: token.end
745
+ };
746
+ while (token.type !== TokenName[TokenEnum.EOF]) {
747
+ const ast = raw() || rawScript() || oldVariable();
748
+
749
+ if (!ast) {
750
+ break;
751
+ }
752
+ node.body.push(ast);
753
+ }
754
+ if (node.body.length) {
755
+ node.end = node.body[node.body.length - 1].end;
756
+ }
757
+ return node;
758
+ }
759
+
760
+ function raw(): ASTNodeOrNull {
761
+ if (token.type !== TokenName[TokenEnum.RAW]) {
762
+ return null;
763
+ }
764
+
765
+ const cToken = token;
766
+ next();
767
+ return {
768
+ type: 'raw',
769
+ value: cToken.value,
770
+ start: cToken.start,
771
+ end: cToken.end
772
+ };
773
+ }
774
+
775
+ function rawScript(): ASTNodeOrNull {
776
+ if (token.type !== TokenName[TokenEnum.OpenScript]) {
777
+ return null;
778
+ }
779
+
780
+ const start = token;
781
+ let end = start;
782
+ next();
783
+ const exp = assert(complexExpression());
784
+ assert(token.type === TokenName[TokenEnum.CloseScript]);
785
+ end = token;
786
+ next();
787
+
788
+ return {
789
+ type: 'script',
790
+ body: exp,
791
+ start: start.start,
792
+ end: end.end
793
+ };
794
+ }
795
+
796
+ function variable(allowNameSpace = true): ASTNodeOrNull {
797
+ if (token.type === TokenName[TokenEnum.Identifier]) {
798
+ const cToken = token;
799
+ next();
800
+
801
+ if (
802
+ allowNameSpace &&
803
+ matchPunctuator(':') &&
804
+ ~variableNamespaces.indexOf(cToken.value)
805
+ ) {
806
+ next();
807
+ const body = assert(postfixExpression());
808
+ return {
809
+ type: 'ns-variable',
810
+ namespace: cToken.value,
811
+ body,
812
+ start: cToken.start,
813
+ end: body.end
814
+ };
815
+ }
816
+
817
+ return {
818
+ type: 'variable',
819
+ name: cToken.value,
820
+ start: cToken.start,
821
+ end: cToken.end
822
+ };
823
+ } else if (matchPunctuator('&')) {
824
+ const v = token;
825
+ next();
826
+ return {
827
+ type: 'variable',
828
+ name: '&',
829
+ start: v.start,
830
+ end: v.end
831
+ };
832
+ }
833
+ return null;
834
+ }
835
+
836
+ function oldVariable(): ASTNodeOrNull {
837
+ if (token.type !== TokenName[TokenEnum.Variable]) {
838
+ return null;
839
+ }
840
+ const prevToken = token;
841
+ next();
842
+ return {
843
+ type: 'script',
844
+ body: prevToken.value.split('.').reduce((prev: any, key: string) => {
845
+ return prev
846
+ ? {
847
+ type: 'getter',
848
+ host: prev,
849
+ key,
850
+ start: prevToken.start,
851
+ end: prevToken.end
852
+ }
853
+ : {
854
+ type: 'variable',
855
+ name: key,
856
+ start: prevToken.start,
857
+ end: prevToken.end
858
+ };
859
+ }, null),
860
+ start: prevToken.start,
861
+ end: prevToken.end
862
+ };
863
+ }
864
+
865
+ next();
866
+ const ast = options?.variableMode
867
+ ? postfixExpression(variable)
868
+ : options?.evalMode
869
+ ? expression()
870
+ : contents();
871
+
872
+ assert(token!?.type === TokenName[TokenEnum.EOF]);
873
+
874
+ return ast!;
875
+ }