UNPKG

3.99 kBJavaScriptView Raw
1import {flowParseAssignableListItemTypes} from "../plugins/flow";
2import {
3 tsParseAccessModifier,
4 tsParseAssignableListItemTypes,
5 tsParseModifier,
6} from "../plugins/typescript";
7import {
8 eat,
9 IdentifierRole,
10 match,
11 next,
12 popTypeContext,
13 pushTypeContext,
14} from "../tokenizer/index";
15import {ContextualKeyword} from "../tokenizer/keywords";
16import {TokenType, TokenType as tt} from "../tokenizer/types";
17import {isFlowEnabled, isTypeScriptEnabled, state} from "./base";
18import {parseIdentifier, parseMaybeAssign, parseObj} from "./expression";
19import {expect, unexpected} from "./util";
20
21export function parseSpread() {
22 next();
23 parseMaybeAssign(false);
24}
25
26export function parseRest(isBlockScope) {
27 next();
28 parseBindingAtom(isBlockScope);
29}
30
31export function parseBindingIdentifier(isBlockScope) {
32 parseIdentifier();
33 markPriorBindingIdentifier(isBlockScope);
34}
35
36export function markPriorBindingIdentifier(isBlockScope) {
37 if (state.isType) {
38 return;
39 }
40 if (state.scopeDepth === 0) {
41 state.tokens[state.tokens.length - 1].identifierRole = IdentifierRole.TopLevelDeclaration;
42 } else {
43 state.tokens[state.tokens.length - 1].identifierRole = isBlockScope
44 ? IdentifierRole.BlockScopedDeclaration
45 : IdentifierRole.FunctionScopedDeclaration;
46 }
47}
48
49// Parses lvalue (assignable) atom.
50export function parseBindingAtom(isBlockScope) {
51 switch (state.type) {
52 case tt._this: {
53 // In TypeScript, "this" may be the name of a parameter, so allow it.
54 const oldIsType = pushTypeContext(0);
55 next();
56 popTypeContext(oldIsType);
57 return;
58 }
59
60 case tt._yield:
61 case tt.name: {
62 state.type = tt.name;
63 parseBindingIdentifier(isBlockScope);
64 return;
65 }
66
67 case tt.bracketL: {
68 next();
69 parseBindingList(tt.bracketR, isBlockScope, true /* allowEmpty */);
70 return;
71 }
72
73 case tt.braceL:
74 parseObj(true, isBlockScope);
75 return;
76
77 default:
78 unexpected();
79 }
80}
81
82export function parseBindingList(
83 close,
84 isBlockScope,
85 allowEmpty = false,
86 allowModifiers = false,
87) {
88 let first = true;
89
90 let hasRemovedComma = false;
91 const firstItemTokenIndex = state.tokens.length;
92
93 while (!eat(close) && !state.error) {
94 if (first) {
95 first = false;
96 } else {
97 expect(tt.comma);
98 // After a "this" type in TypeScript, we need to set the following comma (if any) to also be
99 // a type token so that it will be removed.
100 if (!hasRemovedComma && state.tokens[firstItemTokenIndex].isType) {
101 state.tokens[state.tokens.length - 1].isType = true;
102 hasRemovedComma = true;
103 }
104 }
105 if (allowEmpty && match(tt.comma)) {
106 // Empty item; nothing further to parse for this item.
107 } else if (eat(close)) {
108 break;
109 } else if (match(tt.ellipsis)) {
110 parseRest(isBlockScope);
111 parseAssignableListItemTypes();
112 // Support rest element trailing commas allowed by TypeScript <2.9.
113 eat(TokenType.comma);
114 expect(close);
115 break;
116 } else {
117 parseAssignableListItem(allowModifiers, isBlockScope);
118 }
119 }
120}
121
122function parseAssignableListItem(allowModifiers, isBlockScope) {
123 if (allowModifiers) {
124 tsParseAccessModifier();
125 tsParseModifier([ContextualKeyword._readonly]);
126 }
127
128 parseMaybeDefault(isBlockScope);
129 parseAssignableListItemTypes();
130 parseMaybeDefault(isBlockScope, true /* leftAlreadyParsed */);
131}
132
133function parseAssignableListItemTypes() {
134 if (isFlowEnabled) {
135 flowParseAssignableListItemTypes();
136 } else if (isTypeScriptEnabled) {
137 tsParseAssignableListItemTypes();
138 }
139}
140
141// Parses assignment pattern around given atom if possible.
142export function parseMaybeDefault(isBlockScope, leftAlreadyParsed = false) {
143 if (!leftAlreadyParsed) {
144 parseBindingAtom(isBlockScope);
145 }
146 if (!eat(tt.eq)) {
147 return;
148 }
149 const eqIndex = state.tokens.length - 1;
150 parseMaybeAssign();
151 state.tokens[eqIndex].rhsEndIndex = state.tokens.length;
152}