UNPKG

4.33 kBJavaScriptView Raw
1import {flowParseAssignableListItemTypes} from "../plugins/flow";
2import {tsParseAssignableListItemTypes, tsParseModifiers} from "../plugins/typescript";
3import {
4 eat,
5 IdentifierRole,
6 match,
7 next,
8 popTypeContext,
9 pushTypeContext,
10} from "../tokenizer/index";
11import {ContextualKeyword} from "../tokenizer/keywords";
12import {TokenType, TokenType as tt} from "../tokenizer/types";
13import {isFlowEnabled, isTypeScriptEnabled, state} from "./base";
14import {parseIdentifier, parseMaybeAssign, parseObj} from "./expression";
15import {expect, unexpected} from "./util";
16
17export function parseSpread() {
18 next();
19 parseMaybeAssign(false);
20}
21
22export function parseRest(isBlockScope) {
23 next();
24 parseBindingAtom(isBlockScope);
25}
26
27export function parseBindingIdentifier(isBlockScope) {
28 parseIdentifier();
29 markPriorBindingIdentifier(isBlockScope);
30}
31
32export function parseImportedIdentifier() {
33 parseIdentifier();
34 state.tokens[state.tokens.length - 1].identifierRole = IdentifierRole.ImportDeclaration;
35}
36
37export function markPriorBindingIdentifier(isBlockScope) {
38 let identifierRole;
39 if (state.scopeDepth === 0) {
40 identifierRole = IdentifierRole.TopLevelDeclaration;
41 } else if (isBlockScope) {
42 identifierRole = IdentifierRole.BlockScopedDeclaration;
43 } else {
44 identifierRole = IdentifierRole.FunctionScopedDeclaration;
45 }
46 state.tokens[state.tokens.length - 1].identifierRole = identifierRole;
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 contextId = 0,
88) {
89 let first = true;
90
91 let hasRemovedComma = false;
92 const firstItemTokenIndex = state.tokens.length;
93
94 while (!eat(close) && !state.error) {
95 if (first) {
96 first = false;
97 } else {
98 expect(tt.comma);
99 state.tokens[state.tokens.length - 1].contextId = contextId;
100 // After a "this" type in TypeScript, we need to set the following comma (if any) to also be
101 // a type token so that it will be removed.
102 if (!hasRemovedComma && state.tokens[firstItemTokenIndex].isType) {
103 state.tokens[state.tokens.length - 1].isType = true;
104 hasRemovedComma = true;
105 }
106 }
107 if (allowEmpty && match(tt.comma)) {
108 // Empty item; nothing further to parse for this item.
109 } else if (eat(close)) {
110 break;
111 } else if (match(tt.ellipsis)) {
112 parseRest(isBlockScope);
113 parseAssignableListItemTypes();
114 // Support rest element trailing commas allowed by TypeScript <2.9.
115 eat(TokenType.comma);
116 expect(close);
117 break;
118 } else {
119 parseAssignableListItem(allowModifiers, isBlockScope);
120 }
121 }
122}
123
124function parseAssignableListItem(allowModifiers, isBlockScope) {
125 if (allowModifiers) {
126 tsParseModifiers([
127 ContextualKeyword._public,
128 ContextualKeyword._protected,
129 ContextualKeyword._private,
130 ContextualKeyword._readonly,
131 ContextualKeyword._override,
132 ]);
133 }
134
135 parseMaybeDefault(isBlockScope);
136 parseAssignableListItemTypes();
137 parseMaybeDefault(isBlockScope, true /* leftAlreadyParsed */);
138}
139
140function parseAssignableListItemTypes() {
141 if (isFlowEnabled) {
142 flowParseAssignableListItemTypes();
143 } else if (isTypeScriptEnabled) {
144 tsParseAssignableListItemTypes();
145 }
146}
147
148// Parses assignment pattern around given atom if possible.
149export function parseMaybeDefault(isBlockScope, leftAlreadyParsed = false) {
150 if (!leftAlreadyParsed) {
151 parseBindingAtom(isBlockScope);
152 }
153 if (!eat(tt.eq)) {
154 return;
155 }
156 const eqIndex = state.tokens.length - 1;
157 parseMaybeAssign();
158 state.tokens[eqIndex].rhsEndIndex = state.tokens.length;
159}