OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library analyzer.src.generated.parser; | 5 library analyzer.src.generated.parser; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 import "dart:math" as math; | 8 import "dart:math" as math; |
9 | 9 |
10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
11 import 'package:analyzer/dart/ast/token.dart'; | 11 import 'package:analyzer/dart/ast/token.dart'; |
12 import 'package:analyzer/error/error.dart'; | 12 import 'package:analyzer/error/error.dart'; |
13 import 'package:analyzer/error/listener.dart'; | 13 import 'package:analyzer/error/listener.dart'; |
14 import 'package:analyzer/src/dart/ast/ast.dart'; | 14 import 'package:analyzer/src/dart/ast/ast.dart'; |
15 import 'package:analyzer/src/dart/ast/token.dart'; | 15 import 'package:analyzer/src/dart/ast/token.dart'; |
16 import 'package:analyzer/src/dart/scanner/reader.dart'; | 16 import 'package:analyzer/src/dart/scanner/reader.dart'; |
17 import 'package:analyzer/src/dart/scanner/scanner.dart'; | 17 import 'package:analyzer/src/dart/scanner/scanner.dart'; |
18 import 'package:analyzer/src/error/codes.dart'; | 18 import 'package:analyzer/src/error/codes.dart'; |
19 import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine; | 19 import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine; |
20 import 'package:analyzer/src/generated/generated/shared_messages.dart' | 20 import 'package:analyzer/src/generated/generated/shared_messages.dart' |
21 as shared_messages; | 21 as shared_messages; |
22 import 'package:analyzer/src/generated/java_core.dart'; | 22 import 'package:analyzer/src/generated/java_core.dart'; |
23 import 'package:analyzer/src/generated/java_engine.dart'; | 23 import 'package:analyzer/src/generated/java_engine.dart'; |
24 import 'package:analyzer/src/generated/source.dart'; | 24 import 'package:analyzer/src/generated/source.dart'; |
25 import 'package:analyzer/src/generated/utilities_dart.dart'; | 25 import 'package:analyzer/src/generated/utilities_dart.dart'; |
26 | 26 |
27 export 'package:analyzer/src/dart/ast/utilities.dart' show ResolutionCopier; | 27 export 'package:analyzer/src/dart/ast/utilities.dart' show ResolutionCopier; |
28 | 28 |
29 Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{ | |
30 'parseCompilationUnit_1': new MethodTrampoline( | |
31 1, (Parser target, arg0) => target.parseCompilationUnit(arg0)), | |
32 'parseDirectives_1': new MethodTrampoline( | |
33 1, (Parser target, arg0) => target.parseDirectives(arg0)), | |
34 'parseExpression_1': new MethodTrampoline( | |
35 1, (Parser target, arg0) => target.parseExpression(arg0)), | |
36 'parseStatement_1': new MethodTrampoline( | |
37 1, (Parser target, arg0) => target.parseStatement(arg0)), | |
38 'parseStatements_1': new MethodTrampoline( | |
39 1, (Parser target, arg0) => target.parseStatements(arg0)), | |
40 'parseAnnotation_0': | |
41 new MethodTrampoline(0, (Parser target) => target.parseAnnotation()), | |
42 'parseArgument_0': | |
43 new MethodTrampoline(0, (Parser target) => target.parseArgument()), | |
44 'parseArgumentList_0': | |
45 new MethodTrampoline(0, (Parser target) => target.parseArgumentList()), | |
46 'parseBitwiseOrExpression_0': new MethodTrampoline( | |
47 0, (Parser target) => target.parseBitwiseOrExpression()), | |
48 'parseBlock_0': | |
49 new MethodTrampoline(0, (Parser target) => target.parseBlock()), | |
50 'parseClassMember_1': new MethodTrampoline( | |
51 1, (Parser target, arg0) => target.parseClassMember(arg0)), | |
52 'parseCompilationUnit_0': new MethodTrampoline( | |
53 0, (Parser target) => target.parseCompilationUnit2()), | |
54 'parseConditionalExpression_0': new MethodTrampoline( | |
55 0, (Parser target) => target.parseConditionalExpression()), | |
56 'parseConstructorName_0': | |
57 new MethodTrampoline(0, (Parser target) => target.parseConstructorName()), | |
58 'parseExpression_0': | |
59 new MethodTrampoline(0, (Parser target) => target.parseExpression2()), | |
60 'parseExpressionWithoutCascade_0': new MethodTrampoline( | |
61 0, (Parser target) => target.parseExpressionWithoutCascade()), | |
62 'parseExtendsClause_0': | |
63 new MethodTrampoline(0, (Parser target) => target.parseExtendsClause()), | |
64 'parseFormalParameterList_0': new MethodTrampoline( | |
65 0, (Parser target) => target.parseFormalParameterList()), | |
66 'parseFunctionExpression_0': new MethodTrampoline( | |
67 0, (Parser target) => target.parseFunctionExpression()), | |
68 'parseImplementsClause_0': new MethodTrampoline( | |
69 0, (Parser target) => target.parseImplementsClause()), | |
70 'parseLabel_0': | |
71 new MethodTrampoline(0, (Parser target) => target.parseLabel()), | |
72 'parseLibraryIdentifier_0': new MethodTrampoline( | |
73 0, (Parser target) => target.parseLibraryIdentifier()), | |
74 'parseLogicalOrExpression_0': new MethodTrampoline( | |
75 0, (Parser target) => target.parseLogicalOrExpression()), | |
76 'parseMapLiteralEntry_0': | |
77 new MethodTrampoline(0, (Parser target) => target.parseMapLiteralEntry()), | |
78 'parseNormalFormalParameter_0': new MethodTrampoline( | |
79 0, (Parser target) => target.parseNormalFormalParameter()), | |
80 'parsePrefixedIdentifier_0': new MethodTrampoline( | |
81 0, (Parser target) => target.parsePrefixedIdentifier()), | |
82 'parseReturnType_0': | |
83 new MethodTrampoline(0, (Parser target) => target.parseReturnType()), | |
84 'parseSimpleIdentifier_0': new MethodTrampoline( | |
85 0, (Parser target) => target.parseSimpleIdentifier()), | |
86 'parseStatement_0': | |
87 new MethodTrampoline(0, (Parser target) => target.parseStatement2()), | |
88 'parseStringLiteral_0': | |
89 new MethodTrampoline(0, (Parser target) => target.parseStringLiteral()), | |
90 'parseTypeArgumentList_0': new MethodTrampoline( | |
91 0, (Parser target) => target.parseTypeArgumentList()), | |
92 'parseTypeName_0': | |
93 new MethodTrampoline(0, (Parser target) => target.parseTypeName(false)), | |
94 'parseTypeParameter_0': | |
95 new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()), | |
96 'parseTypeParameterList_0': new MethodTrampoline( | |
97 0, (Parser target) => target.parseTypeParameterList()), | |
98 'parseWithClause_0': | |
99 new MethodTrampoline(0, (Parser target) => target.parseWithClause()), | |
100 'advance_0': new MethodTrampoline(0, (Parser target) => target._advance()), | |
101 'appendScalarValue_5': new MethodTrampoline( | |
102 5, | |
103 (Parser target, arg0, arg1, arg2, arg3, arg4) => | |
104 target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)), | |
105 'convertToFunctionDeclaration_1': new MethodTrampoline( | |
106 1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)), | |
107 'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline( | |
108 0, (Parser target) => target._couldBeStartOfCompilationUnitMember()), | |
109 'createSyntheticIdentifier_0': new MethodTrampoline( | |
110 0, (Parser target) => target.createSyntheticIdentifier()), | |
111 'createSyntheticKeyword_1': new MethodTrampoline( | |
112 1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)), | |
113 'createSyntheticStringLiteral_0': new MethodTrampoline( | |
114 0, (Parser target) => target.createSyntheticStringLiteral()), | |
115 'createSyntheticToken_1': new MethodTrampoline( | |
116 1, (Parser target, arg0) => target._createSyntheticToken(arg0)), | |
117 'ensureAssignable_1': new MethodTrampoline( | |
118 1, (Parser target, arg0) => target._ensureAssignable(arg0)), | |
119 'expect_1': | |
120 new MethodTrampoline(1, (Parser target, arg0) => target._expect(arg0)), | |
121 'expectGt_0': new MethodTrampoline(0, (Parser target) => target._expectGt()), | |
122 'expectKeyword_1': new MethodTrampoline( | |
123 1, (Parser target, arg0) => target._expectKeyword(arg0)), | |
124 'findRange_2': new MethodTrampoline( | |
125 2, | |
126 (Parser target, List<List<int>> arg0, int arg1) => | |
127 target._findRange(arg0, arg1)), | |
128 'getCodeBlockRanges_1': new MethodTrampoline( | |
129 1, (Parser target, arg0) => target._getCodeBlockRanges(arg0)), | |
130 'getEndToken_1': new MethodTrampoline( | |
131 1, (Parser target, arg0) => target._getEndToken(arg0)), | |
132 'injectToken_1': new MethodTrampoline( | |
133 1, (Parser target, arg0) => target._injectToken(arg0)), | |
134 'isFunctionDeclaration_0': new MethodTrampoline( | |
135 0, (Parser target) => target.isFunctionDeclaration()), | |
136 'isFunctionExpression_1': new MethodTrampoline( | |
137 1, (Parser target, arg0) => target.isFunctionExpression(arg0)), | |
138 'isHexDigit_1': new MethodTrampoline( | |
139 1, (Parser target, arg0) => target._isHexDigit(arg0)), | |
140 'isInitializedVariableDeclaration_0': new MethodTrampoline( | |
141 0, (Parser target) => target.isInitializedVariableDeclaration()), | |
142 'isLinkText_2': new MethodTrampoline( | |
143 2, (Parser target, arg0, arg1) => target._isLinkText(arg0, arg1)), | |
144 'isOperator_1': new MethodTrampoline( | |
145 1, (Parser target, arg0) => target._isOperator(arg0)), | |
146 'isSwitchMember_0': | |
147 new MethodTrampoline(0, (Parser target) => target.isSwitchMember()), | |
148 'isTypedIdentifier_1': new MethodTrampoline( | |
149 1, (Parser target, arg0) => target._isTypedIdentifier(arg0)), | |
150 'lockErrorListener_0': | |
151 new MethodTrampoline(0, (Parser target) => target._lockErrorListener()), | |
152 'matches_1': | |
153 new MethodTrampoline(1, (Parser target, arg0) => target._matches(arg0)), | |
154 'matchesGt_0': | |
155 new MethodTrampoline(0, (Parser target) => target._matchesGt()), | |
156 'matchesIdentifier_0': | |
157 new MethodTrampoline(0, (Parser target) => target._matchesIdentifier()), | |
158 'matchesKeyword_1': new MethodTrampoline( | |
159 1, (Parser target, arg0) => target._matchesKeyword(arg0)), | |
160 'matchesString_1': new MethodTrampoline( | |
161 1, (Parser target, arg0) => target._matchesString(arg0)), | |
162 'optional_1': | |
163 new MethodTrampoline(1, (Parser target, arg0) => target._optional(arg0)), | |
164 'parseAdditiveExpression_0': new MethodTrampoline( | |
165 0, (Parser target) => target.parseAdditiveExpression()), | |
166 'parseAssertStatement_0': | |
167 new MethodTrampoline(0, (Parser target) => target.parseAssertStatement()), | |
168 'parseAssignableExpression_1': new MethodTrampoline( | |
169 1, (Parser target, arg0) => target.parseAssignableExpression(arg0)), | |
170 'parseAssignableSelector_2': new MethodTrampoline( | |
171 2, | |
172 (Parser target, arg0, arg1) => | |
173 target._parseAssignableSelector(arg0, arg1)), | |
174 'parseAwaitExpression_0': | |
175 new MethodTrampoline(0, (Parser target) => target.parseAwaitExpression()), | |
176 'parseBitwiseAndExpression_0': new MethodTrampoline( | |
177 0, (Parser target) => target.parseBitwiseAndExpression()), | |
178 'parseBitwiseXorExpression_0': new MethodTrampoline( | |
179 0, (Parser target) => target.parseBitwiseXorExpression()), | |
180 'parseBreakStatement_0': | |
181 new MethodTrampoline(0, (Parser target) => target.parseBreakStatement()), | |
182 'parseCascadeSection_0': | |
183 new MethodTrampoline(0, (Parser target) => target.parseCascadeSection()), | |
184 'parseClassDeclaration_2': new MethodTrampoline(2, | |
185 (Parser target, arg0, arg1) => target.parseClassDeclaration(arg0, arg1)), | |
186 'parseClassMembers_2': new MethodTrampoline( | |
187 2, (Parser target, arg0, arg1) => target._parseClassMembers(arg0, arg1)), | |
188 'parseClassTypeAlias_3': new MethodTrampoline( | |
189 3, | |
190 (Parser target, arg0, arg1, arg2) => | |
191 target.parseClassTypeAlias(arg0, arg1, arg2)), | |
192 'parseCombinator_0': | |
193 new MethodTrampoline(0, (Parser target) => target.parseCombinator()), | |
194 'parseCombinators_0': | |
195 new MethodTrampoline(0, (Parser target) => target.parseCombinators()), | |
196 'parseCommentAndMetadata_0': new MethodTrampoline( | |
197 0, (Parser target) => target.parseCommentAndMetadata()), | |
198 'parseCommentReference_2': new MethodTrampoline(2, | |
199 (Parser target, arg0, arg1) => target.parseCommentReference(arg0, arg1)), | |
200 'parseCommentReferences_1': new MethodTrampoline( | |
201 1, | |
202 (Parser target, List<DocumentationCommentToken> arg0) => | |
203 target._parseCommentReferences(arg0)), | |
204 'parseCompilationUnitMember_1': new MethodTrampoline( | |
205 1, (Parser target, arg0) => target.parseCompilationUnitMember(arg0)), | |
206 'parseConfiguration_0': | |
207 new MethodTrampoline(0, (Parser target) => target.parseConfiguration()), | |
208 'parseConstExpression_0': | |
209 new MethodTrampoline(0, (Parser target) => target.parseConstExpression()), | |
210 'parseConstructor_8': new MethodTrampoline( | |
211 8, | |
212 (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target | |
213 ._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)), | |
214 'parseConstructorFieldInitializer_1': new MethodTrampoline(1, | |
215 (Parser target, arg0) => target._parseConstructorFieldInitializer(arg0)), | |
216 'parseContinueStatement_0': new MethodTrampoline( | |
217 0, (Parser target) => target.parseContinueStatement()), | |
218 'parseDirective_1': new MethodTrampoline( | |
219 1, (Parser target, arg0) => target._parseDirective(arg0)), | |
220 'parseDirectives_0': | |
221 new MethodTrampoline(0, (Parser target) => target.parseDirectives2()), | |
222 'parseDocumentationComment_0': new MethodTrampoline(0, (Parser target) { | |
223 List<DocumentationCommentToken> tokens = | |
224 target.parseDocumentationCommentTokens(); | |
225 return target.parseDocumentationComment(tokens); | |
226 }), | |
227 'parseDoStatement_0': | |
228 new MethodTrampoline(0, (Parser target) => target.parseDoStatement()), | |
229 'parseDottedName_0': | |
230 new MethodTrampoline(0, (Parser target) => target.parseDottedName()), | |
231 'parseEmptyStatement_0': | |
232 new MethodTrampoline(0, (Parser target) => target.parseEmptyStatement()), | |
233 'parseEnumConstantDeclaration_0': new MethodTrampoline( | |
234 0, (Parser target) => target._parseEnumConstantDeclaration()), | |
235 'parseEnumDeclaration_1': new MethodTrampoline( | |
236 1, (Parser target, arg0) => target.parseEnumDeclaration(arg0)), | |
237 'parseEqualityExpression_0': new MethodTrampoline( | |
238 0, (Parser target) => target._parseEqualityExpression()), | |
239 'parseExportDirective_1': new MethodTrampoline( | |
240 1, (Parser target, arg0) => target._parseExportDirective(arg0)), | |
241 'parseExpressionList_0': | |
242 new MethodTrampoline(0, (Parser target) => target.parseExpressionList()), | |
243 'parseFinalConstVarOrType_1': new MethodTrampoline( | |
244 1, (Parser target, arg0) => target.parseFinalConstVarOrType(arg0)), | |
245 'parseFormalParameter_1': new MethodTrampoline( | |
246 1, (Parser target, arg0) => target._parseFormalParameter(arg0)), | |
247 'parseForStatement_0': | |
248 new MethodTrampoline(0, (Parser target) => target.parseForStatement()), | |
249 'parseFunctionBody_3': new MethodTrampoline( | |
250 3, | |
251 (Parser target, arg0, arg1, arg2) => | |
252 target.parseFunctionBody(arg0, arg1, arg2)), | |
253 'parseFunctionDeclaration_3': new MethodTrampoline( | |
254 3, | |
255 (Parser target, arg0, arg1, arg2) => | |
256 target._parseFunctionDeclaration(arg0, arg1, arg2)), | |
257 'parseFunctionDeclarationStatement_0': new MethodTrampoline( | |
258 0, (Parser target) => target.parseFunctionDeclarationStatement()), | |
259 'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline( | |
260 2, | |
261 (Parser target, arg0, arg1) => | |
262 target._parseFunctionDeclarationStatementAfterReturnType(arg0, arg1)), | |
263 'parseFunctionTypeAlias_2': new MethodTrampoline( | |
264 2, | |
265 (Parser target, arg0, arg1) => | |
266 target._parseFunctionTypeAlias(arg0, arg1)), | |
267 'parseGetter_4': new MethodTrampoline( | |
268 4, | |
269 (Parser target, arg0, arg1, arg2, arg3) => | |
270 target._parseGetter(arg0, arg1, arg2, arg3)), | |
271 'parseIdentifierList_0': | |
272 new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()), | |
273 'parseIfStatement_0': | |
274 new MethodTrampoline(0, (Parser target) => target.parseIfStatement()), | |
275 'parseImportDirective_1': new MethodTrampoline( | |
276 1, (Parser target, arg0) => target._parseImportDirective(arg0)), | |
277 'parseInitializedIdentifierList_4': new MethodTrampoline( | |
278 4, | |
279 (Parser target, arg0, arg1, arg2, arg3) => | |
280 target._parseInitializedIdentifierList(arg0, arg1, arg2, arg3)), | |
281 'parseInstanceCreationExpression_1': new MethodTrampoline(1, | |
282 (Parser target, arg0) => target._parseInstanceCreationExpression(arg0)), | |
283 'parseLibraryDirective_1': new MethodTrampoline( | |
284 1, (Parser target, arg0) => target._parseLibraryDirective(arg0)), | |
285 'parseLibraryName_2': new MethodTrampoline( | |
286 2, (Parser target, arg0, arg1) => target._parseLibraryName(arg0, arg1)), | |
287 'parseListLiteral_2': new MethodTrampoline( | |
288 2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)), | |
289 'parseListOrMapLiteral_1': new MethodTrampoline( | |
290 1, (Parser target, arg0) => target.parseListOrMapLiteral(arg0)), | |
291 'parseLogicalAndExpression_0': new MethodTrampoline( | |
292 0, (Parser target) => target._parseLogicalAndExpression()), | |
293 'parseMapLiteral_2': new MethodTrampoline( | |
294 2, (Parser target, arg0, arg1) => target._parseMapLiteral(arg0, arg1)), | |
295 'parseMethodDeclarationAfterParameters_7': new MethodTrampoline( | |
296 7, | |
297 (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6) => | |
298 target._parseMethodDeclarationAfterParameters( | |
299 arg0, arg1, arg2, arg3, arg4, arg5, arg6)), | |
300 'parseMethodDeclarationAfterReturnType_4': new MethodTrampoline( | |
301 4, | |
302 (Parser target, arg0, arg1, arg2, arg3) => target | |
303 ._parseMethodDeclarationAfterReturnType(arg0, arg1, arg2, arg3)), | |
304 'parseModifiers_0': | |
305 new MethodTrampoline(0, (Parser target) => target._parseModifiers()), | |
306 'parseMultiplicativeExpression_0': new MethodTrampoline( | |
307 0, (Parser target) => target.parseMultiplicativeExpression()), | |
308 'parseNativeClause_0': | |
309 new MethodTrampoline(0, (Parser target) => target._parseNativeClause()), | |
310 'parseNewExpression_0': | |
311 new MethodTrampoline(0, (Parser target) => target._parseNewExpression()), | |
312 'parseNonLabeledStatement_0': new MethodTrampoline( | |
313 0, (Parser target) => target._parseNonLabeledStatement()), | |
314 'parseOperator_3': new MethodTrampoline( | |
315 3, | |
316 (Parser target, arg0, arg1, arg2) => | |
317 target.parseOperator(arg0, arg1, arg2)), | |
318 'parseOptionalReturnType_0': new MethodTrampoline( | |
319 0, (Parser target) => target._parseOptionalReturnType()), | |
320 'parsePartDirective_1': new MethodTrampoline( | |
321 1, (Parser target, arg0) => target._parsePartOrPartOfDirective(arg0)), | |
322 'parsePostfixExpression_0': new MethodTrampoline( | |
323 0, (Parser target) => target._parsePostfixExpression()), | |
324 'parsePrimaryExpression_0': new MethodTrampoline( | |
325 0, (Parser target) => target.parsePrimaryExpression()), | |
326 'parseRedirectingConstructorInvocation_1': new MethodTrampoline( | |
327 1, | |
328 (Parser target, arg0) => | |
329 target._parseRedirectingConstructorInvocation(arg0)), | |
330 'parseRelationalExpression_0': new MethodTrampoline( | |
331 0, (Parser target) => target.parseRelationalExpression()), | |
332 'parseRethrowExpression_0': new MethodTrampoline( | |
333 0, (Parser target) => target.parseRethrowExpression()), | |
334 'parseReturnStatement_0': | |
335 new MethodTrampoline(0, (Parser target) => target.parseReturnStatement()), | |
336 'parseSetter_4': new MethodTrampoline( | |
337 4, | |
338 (Parser target, arg0, arg1, arg2, arg3) => | |
339 target._parseSetter(arg0, arg1, arg2, arg3)), | |
340 'parseShiftExpression_0': | |
341 new MethodTrampoline(0, (Parser target) => target.parseShiftExpression()), | |
342 'parseStatementList_0': | |
343 new MethodTrampoline(0, (Parser target) => target._parseStatementList()), | |
344 'parseStringInterpolation_1': new MethodTrampoline( | |
345 1, (Parser target, arg0) => target._parseStringInterpolation(arg0)), | |
346 'parseSuperConstructorInvocation_0': new MethodTrampoline( | |
347 0, (Parser target) => target.parseSuperConstructorInvocation()), | |
348 'parseSwitchStatement_0': | |
349 new MethodTrampoline(0, (Parser target) => target.parseSwitchStatement()), | |
350 'parseSymbolLiteral_0': | |
351 new MethodTrampoline(0, (Parser target) => target.parseSymbolLiteral()), | |
352 'parseThrowExpression_0': | |
353 new MethodTrampoline(0, (Parser target) => target.parseThrowExpression()), | |
354 'parseThrowExpressionWithoutCascade_0': new MethodTrampoline( | |
355 0, (Parser target) => target.parseThrowExpressionWithoutCascade()), | |
356 'parseTryStatement_0': | |
357 new MethodTrampoline(0, (Parser target) => target.parseTryStatement()), | |
358 'parseTypeAlias_1': new MethodTrampoline( | |
359 1, (Parser target, arg0) => target._parseTypeAlias(arg0)), | |
360 'parseUnaryExpression_0': | |
361 new MethodTrampoline(0, (Parser target) => target.parseUnaryExpression()), | |
362 'parseVariableDeclaration_0': new MethodTrampoline( | |
363 0, (Parser target) => target.parseVariableDeclaration()), | |
364 'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline( | |
365 1, | |
366 (Parser target, arg0) => | |
367 target.parseVariableDeclarationListAfterMetadata(arg0)), | |
368 'parseVariableDeclarationListAfterType_3': new MethodTrampoline( | |
369 3, | |
370 (Parser target, arg0, arg1, arg2) => | |
371 target.parseVariableDeclarationListAfterType(arg0, arg1, arg2)), | |
372 'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline( | |
373 1, | |
374 (Parser target, arg0) => | |
375 target.parseVariableDeclarationStatementAfterMetadata(arg0)), | |
376 'parseVariableDeclarationStatementAfterType_3': new MethodTrampoline( | |
377 3, | |
378 (Parser target, arg0, arg1, arg2) => | |
379 target._parseVariableDeclarationStatementAfterType(arg0, arg1, arg2)), | |
380 'parseWhileStatement_0': | |
381 new MethodTrampoline(0, (Parser target) => target.parseWhileStatement()), | |
382 'parseYieldStatement_0': | |
383 new MethodTrampoline(0, (Parser target) => target.parseYieldStatement()), | |
384 'peek_0': new MethodTrampoline(0, (Parser target) => target._peek()), | |
385 'peekAt_1': | |
386 new MethodTrampoline(1, (Parser target, arg0) => target._peekAt(arg0)), | |
387 'reportError_1': new MethodTrampoline( | |
388 1, (Parser target, arg0) => target._reportError(arg0)), | |
389 'reportErrorForCurrentToken_2': new MethodTrampoline( | |
390 2, | |
391 (Parser target, arg0, arg1) => | |
392 target._reportErrorForCurrentToken(arg0, arg1)), | |
393 'reportErrorForNode_3': new MethodTrampoline( | |
394 3, | |
395 (Parser target, arg0, arg1, arg2) => | |
396 target._reportErrorForNode(arg0, arg1, arg2)), | |
397 'reportErrorForToken_3': new MethodTrampoline( | |
398 3, | |
399 (Parser target, arg0, arg1, arg2) => | |
400 target._reportErrorForToken(arg0, arg1, arg2)), | |
401 'skipBlock_0': | |
402 new MethodTrampoline(0, (Parser target) => target._skipBlock()), | |
403 'skipFinalConstVarOrType_1': new MethodTrampoline( | |
404 1, (Parser target, arg0) => target._skipFinalConstVarOrType(arg0)), | |
405 'skipFormalParameterList_1': new MethodTrampoline( | |
406 1, (Parser target, arg0) => target._skipFormalParameterList(arg0)), | |
407 'skipPastMatchingToken_1': new MethodTrampoline( | |
408 1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)), | |
409 'skipPrefixedIdentifier_1': new MethodTrampoline( | |
410 1, (Parser target, arg0) => target.skipPrefixedIdentifier(arg0)), | |
411 'skipReturnType_1': new MethodTrampoline( | |
412 1, (Parser target, arg0) => target.skipReturnType(arg0)), | |
413 'skipSimpleIdentifier_1': new MethodTrampoline( | |
414 1, (Parser target, arg0) => target.skipSimpleIdentifier(arg0)), | |
415 'skipStringInterpolation_1': new MethodTrampoline( | |
416 1, (Parser target, arg0) => target._skipStringInterpolation(arg0)), | |
417 'skipStringLiteral_1': new MethodTrampoline( | |
418 1, (Parser target, arg0) => target.skipStringLiteral(arg0)), | |
419 'skipTypeArgumentList_1': new MethodTrampoline( | |
420 1, (Parser target, arg0) => target.skipTypeArgumentList(arg0)), | |
421 'skipTypeName_1': new MethodTrampoline( | |
422 1, (Parser target, arg0) => target.skipTypeName(arg0)), | |
423 'skipTypeParameterList_1': new MethodTrampoline( | |
424 1, (Parser target, arg0) => target._skipTypeParameterList(arg0)), | |
425 'tokenMatches_2': new MethodTrampoline( | |
426 2, (Parser target, arg0, arg1) => target._tokenMatches(arg0, arg1)), | |
427 'tokenMatchesIdentifier_1': new MethodTrampoline( | |
428 1, (Parser target, arg0) => target._tokenMatchesIdentifier(arg0)), | |
429 'tokenMatchesKeyword_2': new MethodTrampoline(2, | |
430 (Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)), | |
431 'tokenMatchesString_2': new MethodTrampoline( | |
432 2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)), | |
433 'unlockErrorListener_0': | |
434 new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()), | |
435 'validateFormalParameterList_1': new MethodTrampoline( | |
436 1, (Parser target, arg0) => target._validateFormalParameterList(arg0)), | |
437 'validateModifiersForClass_1': new MethodTrampoline( | |
438 1, (Parser target, arg0) => target._validateModifiersForClass(arg0)), | |
439 'validateModifiersForConstructor_1': new MethodTrampoline(1, | |
440 (Parser target, arg0) => target._validateModifiersForConstructor(arg0)), | |
441 'validateModifiersForEnum_1': new MethodTrampoline( | |
442 1, (Parser target, arg0) => target._validateModifiersForEnum(arg0)), | |
443 'validateModifiersForField_1': new MethodTrampoline( | |
444 1, (Parser target, arg0) => target._validateModifiersForField(arg0)), | |
445 'validateModifiersForFunctionDeclarationStatement_1': new MethodTrampoline( | |
446 1, | |
447 (Parser target, arg0) => | |
448 target._validateModifiersForFunctionDeclarationStatement(arg0)), | |
449 'validateModifiersForGetterOrSetterOrMethod_1': new MethodTrampoline( | |
450 1, | |
451 (Parser target, arg0) => | |
452 target._validateModifiersForGetterOrSetterOrMethod(arg0)), | |
453 'validateModifiersForOperator_1': new MethodTrampoline( | |
454 1, (Parser target, arg0) => target._validateModifiersForOperator(arg0)), | |
455 'validateModifiersForTopLevelDeclaration_1': new MethodTrampoline( | |
456 1, | |
457 (Parser target, arg0) => | |
458 target._validateModifiersForTopLevelDeclaration(arg0)), | |
459 'validateModifiersForTopLevelFunction_1': new MethodTrampoline( | |
460 1, | |
461 (Parser target, arg0) => | |
462 target._validateModifiersForTopLevelFunction(arg0)), | |
463 'validateModifiersForTopLevelVariable_1': new MethodTrampoline( | |
464 1, | |
465 (Parser target, arg0) => | |
466 target._validateModifiersForTopLevelVariable(arg0)), | |
467 'validateModifiersForTypedef_1': new MethodTrampoline( | |
468 1, (Parser target, arg0) => target._validateModifiersForTypedef(arg0)), | |
469 }; | |
470 | |
471 Object invokeParserMethodImpl( | |
472 Parser parser, String methodName, List<Object> objects) { | |
473 MethodTrampoline method = | |
474 methodTable_Parser['${methodName}_${objects.length}']; | |
475 if (method == null) { | |
476 throw new ArgumentError('There is no method named $methodName'); | |
477 } | |
478 return method.invoke(parser, objects); | |
479 } | |
480 | |
481 /** | 29 /** |
482 * A simple data-holder for a method that needs to return multiple values. | 30 * A simple data-holder for a method that needs to return multiple values. |
483 */ | 31 */ |
484 class CommentAndMetadata { | 32 class CommentAndMetadata { |
485 /** | 33 /** |
486 * The documentation comment that was parsed, or `null` if none was given. | 34 * The documentation comment that was parsed, or `null` if none was given. |
487 */ | 35 */ |
488 final Comment comment; | 36 final Comment comment; |
489 | 37 |
490 /** | 38 /** |
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
776 * to match the given [enable] flag. | 324 * to match the given [enable] flag. |
777 */ | 325 */ |
778 void set enableNnbd(bool enable) { | 326 void set enableNnbd(bool enable) { |
779 _enableNnbd = enable; | 327 _enableNnbd = enable; |
780 } | 328 } |
781 | 329 |
782 /** | 330 /** |
783 * Return `true` if the current token is the first token of a return type that | 331 * Return `true` if the current token is the first token of a return type that |
784 * is followed by an identifier, possibly followed by a list of type | 332 * is followed by an identifier, possibly followed by a list of type |
785 * parameters, followed by a left-parenthesis. This is used by | 333 * parameters, followed by a left-parenthesis. This is used by |
786 * [_parseTypeAlias] to determine whether or not to parse a return type. | 334 * [parseTypeAlias] to determine whether or not to parse a return type. |
787 */ | 335 */ |
788 @deprecated | 336 @deprecated |
789 bool get hasReturnTypeInTypeAlias { | 337 bool get hasReturnTypeInTypeAlias { |
790 Token next = skipReturnType(_currentToken); | 338 Token next = skipReturnType(_currentToken); |
791 if (next == null) { | 339 if (next == null) { |
792 return false; | 340 return false; |
793 } | 341 } |
794 return _tokenMatchesIdentifier(next); | 342 return _tokenMatchesIdentifier(next); |
795 } | 343 } |
796 | 344 |
(...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1212 * expression is allowed to be a primary without any assignable selector. | 760 * expression is allowed to be a primary without any assignable selector. |
1213 * Return the assignable expression that was parsed. | 761 * Return the assignable expression that was parsed. |
1214 * | 762 * |
1215 * assignableExpression ::= | 763 * assignableExpression ::= |
1216 * primary (arguments* assignableSelector)+ | 764 * primary (arguments* assignableSelector)+ |
1217 * | 'super' unconditionalAssignableSelector | 765 * | 'super' unconditionalAssignableSelector |
1218 * | identifier | 766 * | identifier |
1219 */ | 767 */ |
1220 Expression parseAssignableExpression(bool primaryAllowed) { | 768 Expression parseAssignableExpression(bool primaryAllowed) { |
1221 if (_matchesKeyword(Keyword.SUPER)) { | 769 if (_matchesKeyword(Keyword.SUPER)) { |
1222 return _parseAssignableSelector( | 770 return parseAssignableSelector( |
1223 new SuperExpression(getAndAdvance()), false, | 771 new SuperExpression(getAndAdvance()), false, |
1224 allowConditional: false); | 772 allowConditional: false); |
1225 } | 773 } |
1226 return _parseAssignableExpressionNotStartingWithSuper(primaryAllowed); | 774 return _parseAssignableExpressionNotStartingWithSuper(primaryAllowed); |
1227 } | 775 } |
1228 | 776 |
1229 /** | 777 /** |
| 778 * Parse an assignable selector. The [prefix] is the expression preceding the |
| 779 * selector. The [optional] is `true` if the selector is optional. Return the |
| 780 * assignable selector that was parsed, or the original prefix if there was no |
| 781 * assignable selector. If [allowConditional] is false, then the '?.' |
| 782 * operator will still be parsed, but a parse error will be generated. |
| 783 * |
| 784 * unconditionalAssignableSelector ::= |
| 785 * '[' expression ']' |
| 786 * | '.' identifier |
| 787 * |
| 788 * assignableSelector ::= |
| 789 * unconditionalAssignableSelector |
| 790 * | '?.' identifier |
| 791 */ |
| 792 Expression parseAssignableSelector(Expression prefix, bool optional, |
| 793 {bool allowConditional: true}) { |
| 794 TokenType type = _currentToken.type; |
| 795 if (type == TokenType.OPEN_SQUARE_BRACKET) { |
| 796 Token leftBracket = getAndAdvance(); |
| 797 bool wasInInitializer = _inInitializer; |
| 798 _inInitializer = false; |
| 799 try { |
| 800 Expression index = parseExpression2(); |
| 801 Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET); |
| 802 return new IndexExpression.forTarget( |
| 803 prefix, leftBracket, index, rightBracket); |
| 804 } finally { |
| 805 _inInitializer = wasInInitializer; |
| 806 } |
| 807 } else { |
| 808 bool isQuestionPeriod = type == TokenType.QUESTION_PERIOD; |
| 809 if (type == TokenType.PERIOD || isQuestionPeriod) { |
| 810 if (isQuestionPeriod && !allowConditional) { |
| 811 _reportErrorForCurrentToken( |
| 812 ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, |
| 813 [_currentToken.lexeme]); |
| 814 } |
| 815 Token operator = getAndAdvance(); |
| 816 return new PropertyAccess(prefix, operator, parseSimpleIdentifier()); |
| 817 } else { |
| 818 if (!optional) { |
| 819 // Report the missing selector. |
| 820 _reportErrorForCurrentToken( |
| 821 ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR); |
| 822 } |
| 823 return prefix; |
| 824 } |
| 825 } |
| 826 } |
| 827 |
| 828 /** |
1230 * Parse a await expression. Return the await expression that was parsed. | 829 * Parse a await expression. Return the await expression that was parsed. |
1231 * | 830 * |
1232 * This method assumes that the current token matches `_AWAIT`. | 831 * This method assumes that the current token matches `_AWAIT`. |
1233 * | 832 * |
1234 * awaitExpression ::= | 833 * awaitExpression ::= |
1235 * 'await' unaryExpression | 834 * 'await' unaryExpression |
1236 */ | 835 */ |
1237 AwaitExpression parseAwaitExpression() { | 836 AwaitExpression parseAwaitExpression() { |
1238 Token awaitToken = getAndAdvance(); | 837 Token awaitToken = getAndAdvance(); |
1239 Expression expression = parseUnaryExpression(); | 838 Expression expression = parseUnaryExpression(); |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1430 } | 1029 } |
1431 } while (_isLikelyArgumentList()); | 1030 } while (_isLikelyArgumentList()); |
1432 } else if (functionName != null) { | 1031 } else if (functionName != null) { |
1433 expression = new PropertyAccess(expression, period, functionName); | 1032 expression = new PropertyAccess(expression, period, functionName); |
1434 period = null; | 1033 period = null; |
1435 } | 1034 } |
1436 assert(expression != null); | 1035 assert(expression != null); |
1437 bool progress = true; | 1036 bool progress = true; |
1438 while (progress) { | 1037 while (progress) { |
1439 progress = false; | 1038 progress = false; |
1440 Expression selector = _parseAssignableSelector(expression, true); | 1039 Expression selector = parseAssignableSelector(expression, true); |
1441 if (!identical(selector, expression)) { | 1040 if (!identical(selector, expression)) { |
1442 expression = selector; | 1041 expression = selector; |
1443 progress = true; | 1042 progress = true; |
1444 while (_isLikelyArgumentList()) { | 1043 while (_isLikelyArgumentList()) { |
1445 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); | 1044 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); |
1446 Expression currentExpression = expression; | 1045 Expression currentExpression = expression; |
1447 if (currentExpression is PropertyAccess) { | 1046 if (currentExpression is PropertyAccess) { |
1448 expression = new MethodInvocation( | 1047 expression = new MethodInvocation( |
1449 currentExpression.target, | 1048 currentExpression.target, |
1450 currentExpression.operator, | 1049 currentExpression.operator, |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1604 * Parse a class member. The [className] is the name of the class containing | 1203 * Parse a class member. The [className] is the name of the class containing |
1605 * the member being parsed. Return the class member that was parsed, or `null` | 1204 * the member being parsed. Return the class member that was parsed, or `null` |
1606 * if what was found was not a valid class member. | 1205 * if what was found was not a valid class member. |
1607 * | 1206 * |
1608 * classMemberDefinition ::= | 1207 * classMemberDefinition ::= |
1609 * declaration ';' | 1208 * declaration ';' |
1610 * | methodSignature functionBody | 1209 * | methodSignature functionBody |
1611 */ | 1210 */ |
1612 ClassMember parseClassMember(String className) { | 1211 ClassMember parseClassMember(String className) { |
1613 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); | 1212 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); |
1614 Modifiers modifiers = _parseModifiers(); | 1213 Modifiers modifiers = parseModifiers(); |
1615 Keyword keyword = _currentToken.keyword; | 1214 Keyword keyword = _currentToken.keyword; |
1616 if (keyword == Keyword.VOID) { | 1215 if (keyword == Keyword.VOID) { |
1617 TypeName returnType = | 1216 TypeName returnType = |
1618 new TypeName(new SimpleIdentifier(getAndAdvance()), null); | 1217 new TypeName(new SimpleIdentifier(getAndAdvance()), null); |
1619 keyword = _currentToken.keyword; | 1218 keyword = _currentToken.keyword; |
1620 Token next = _peek(); | 1219 Token next = _peek(); |
1621 bool isFollowedByIdentifier = _tokenMatchesIdentifier(next); | 1220 bool isFollowedByIdentifier = _tokenMatchesIdentifier(next); |
1622 if (keyword == Keyword.GET && isFollowedByIdentifier) { | 1221 if (keyword == Keyword.GET && isFollowedByIdentifier) { |
1623 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1222 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1624 return _parseGetter(commentAndMetadata, modifiers.externalKeyword, | 1223 return parseGetter(commentAndMetadata, modifiers.externalKeyword, |
1625 modifiers.staticKeyword, returnType); | 1224 modifiers.staticKeyword, returnType); |
1626 } else if (keyword == Keyword.SET && isFollowedByIdentifier) { | 1225 } else if (keyword == Keyword.SET && isFollowedByIdentifier) { |
1627 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1226 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1628 return _parseSetter(commentAndMetadata, modifiers.externalKeyword, | 1227 return parseSetter(commentAndMetadata, modifiers.externalKeyword, |
1629 modifiers.staticKeyword, returnType); | 1228 modifiers.staticKeyword, returnType); |
1630 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { | 1229 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { |
1631 _validateModifiersForOperator(modifiers); | 1230 _validateModifiersForOperator(modifiers); |
1632 return _parseOperatorAfterKeyword(commentAndMetadata, | 1231 return _parseOperatorAfterKeyword(commentAndMetadata, |
1633 modifiers.externalKeyword, returnType, getAndAdvance()); | 1232 modifiers.externalKeyword, returnType, getAndAdvance()); |
1634 } else if (_matchesIdentifier() && | 1233 } else if (_matchesIdentifier() && |
1635 _peek().matchesAny(const <TokenType>[ | 1234 _peek().matchesAny(const <TokenType>[ |
1636 TokenType.OPEN_PAREN, | 1235 TokenType.OPEN_PAREN, |
1637 TokenType.OPEN_CURLY_BRACKET, | 1236 TokenType.OPEN_CURLY_BRACKET, |
1638 TokenType.FUNCTION, | 1237 TokenType.FUNCTION, |
1639 TokenType.LT | 1238 TokenType.LT |
1640 ])) { | 1239 ])) { |
1641 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1240 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1642 return _parseMethodDeclarationAfterReturnType(commentAndMetadata, | 1241 return _parseMethodDeclarationAfterReturnType(commentAndMetadata, |
1643 modifiers.externalKeyword, modifiers.staticKeyword, returnType); | 1242 modifiers.externalKeyword, modifiers.staticKeyword, returnType); |
1644 } else { | 1243 } else { |
1645 // | 1244 // |
1646 // We have found an error of some kind. Try to recover. | 1245 // We have found an error of some kind. Try to recover. |
1647 // | 1246 // |
1648 if (_matchesIdentifier()) { | 1247 if (_matchesIdentifier()) { |
1649 if (_peek().matchesAny(const <TokenType>[ | 1248 if (_peek().matchesAny(const <TokenType>[ |
1650 TokenType.EQ, | 1249 TokenType.EQ, |
1651 TokenType.COMMA, | 1250 TokenType.COMMA, |
1652 TokenType.SEMICOLON | 1251 TokenType.SEMICOLON |
1653 ])) { | 1252 ])) { |
1654 // | 1253 // |
1655 // We appear to have a variable declaration with a type of "void". | 1254 // We appear to have a variable declaration with a type of "void". |
1656 // | 1255 // |
1657 _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType); | 1256 _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType); |
1658 return _parseInitializedIdentifierList( | 1257 return parseInitializedIdentifierList( |
1659 commentAndMetadata, | 1258 commentAndMetadata, |
1660 modifiers.staticKeyword, | 1259 modifiers.staticKeyword, |
1661 _validateModifiersForField(modifiers), | 1260 _validateModifiersForField(modifiers), |
1662 returnType); | 1261 returnType); |
1663 } | 1262 } |
1664 } | 1263 } |
1665 if (_isOperator(_currentToken)) { | 1264 if (_isOperator(_currentToken)) { |
1666 // | 1265 // |
1667 // We appear to have found an operator declaration without the | 1266 // We appear to have found an operator declaration without the |
1668 // 'operator' keyword. | 1267 // 'operator' keyword. |
1669 // | 1268 // |
1670 _validateModifiersForOperator(modifiers); | 1269 _validateModifiersForOperator(modifiers); |
1671 return parseOperator( | 1270 return parseOperator( |
1672 commentAndMetadata, modifiers.externalKeyword, returnType); | 1271 commentAndMetadata, modifiers.externalKeyword, returnType); |
1673 } | 1272 } |
1674 _reportErrorForToken( | 1273 _reportErrorForToken( |
1675 ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); | 1274 ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); |
1676 return null; | 1275 return null; |
1677 } | 1276 } |
1678 } | 1277 } |
1679 Token next = _peek(); | 1278 Token next = _peek(); |
1680 bool isFollowedByIdentifier = _tokenMatchesIdentifier(next); | 1279 bool isFollowedByIdentifier = _tokenMatchesIdentifier(next); |
1681 if (keyword == Keyword.GET && isFollowedByIdentifier) { | 1280 if (keyword == Keyword.GET && isFollowedByIdentifier) { |
1682 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1281 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1683 return _parseGetter(commentAndMetadata, modifiers.externalKeyword, | 1282 return parseGetter(commentAndMetadata, modifiers.externalKeyword, |
1684 modifiers.staticKeyword, null); | 1283 modifiers.staticKeyword, null); |
1685 } else if (keyword == Keyword.SET && isFollowedByIdentifier) { | 1284 } else if (keyword == Keyword.SET && isFollowedByIdentifier) { |
1686 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1285 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1687 return _parseSetter(commentAndMetadata, modifiers.externalKeyword, | 1286 return parseSetter(commentAndMetadata, modifiers.externalKeyword, |
1688 modifiers.staticKeyword, null); | 1287 modifiers.staticKeyword, null); |
1689 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { | 1288 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { |
1690 _validateModifiersForOperator(modifiers); | 1289 _validateModifiersForOperator(modifiers); |
1691 return _parseOperatorAfterKeyword( | 1290 return _parseOperatorAfterKeyword( |
1692 commentAndMetadata, modifiers.externalKeyword, null, getAndAdvance()); | 1291 commentAndMetadata, modifiers.externalKeyword, null, getAndAdvance()); |
1693 } else if (!_matchesIdentifier()) { | 1292 } else if (!_matchesIdentifier()) { |
1694 // | 1293 // |
1695 // Recover from an error. | 1294 // Recover from an error. |
1696 // | 1295 // |
1697 if (_matchesKeyword(Keyword.CLASS)) { | 1296 if (_matchesKeyword(Keyword.CLASS)) { |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1813 TokenType.EQ, | 1412 TokenType.EQ, |
1814 TokenType.COMMA, | 1413 TokenType.COMMA, |
1815 TokenType.SEMICOLON | 1414 TokenType.SEMICOLON |
1816 ])) { | 1415 ])) { |
1817 if (modifiers.constKeyword == null && | 1416 if (modifiers.constKeyword == null && |
1818 modifiers.finalKeyword == null && | 1417 modifiers.finalKeyword == null && |
1819 modifiers.varKeyword == null) { | 1418 modifiers.varKeyword == null) { |
1820 _reportErrorForCurrentToken( | 1419 _reportErrorForCurrentToken( |
1821 ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); | 1420 ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); |
1822 } | 1421 } |
1823 return _parseInitializedIdentifierList(commentAndMetadata, | 1422 return parseInitializedIdentifierList(commentAndMetadata, |
1824 modifiers.staticKeyword, _validateModifiersForField(modifiers), null); | 1423 modifiers.staticKeyword, _validateModifiersForField(modifiers), null); |
1825 } else if (keyword == Keyword.TYPEDEF) { | 1424 } else if (keyword == Keyword.TYPEDEF) { |
1826 _reportErrorForCurrentToken(ParserErrorCode.TYPEDEF_IN_CLASS); | 1425 _reportErrorForCurrentToken(ParserErrorCode.TYPEDEF_IN_CLASS); |
1827 // TODO(brianwilkerson) We don't currently have any way to capture the | 1426 // TODO(brianwilkerson) We don't currently have any way to capture the |
1828 // function type alias that was parsed. | 1427 // function type alias that was parsed. |
1829 _parseFunctionTypeAlias(commentAndMetadata, getAndAdvance()); | 1428 _parseFunctionTypeAlias(commentAndMetadata, getAndAdvance()); |
1830 return null; | 1429 return null; |
1831 } else if (parseGenericMethods) { | 1430 } else if (parseGenericMethods) { |
1832 Token token = _skipTypeParameterList(_peek()); | 1431 Token token = _skipTypeParameterList(_peek()); |
1833 if (token != null && _tokenMatches(token, TokenType.OPEN_PAREN)) { | 1432 if (token != null && _tokenMatches(token, TokenType.OPEN_PAREN)) { |
1834 return _parseMethodDeclarationAfterReturnType(commentAndMetadata, | 1433 return _parseMethodDeclarationAfterReturnType(commentAndMetadata, |
1835 modifiers.externalKeyword, modifiers.staticKeyword, null); | 1434 modifiers.externalKeyword, modifiers.staticKeyword, null); |
1836 } | 1435 } |
1837 } | 1436 } |
1838 TypeName type = _parseTypeNameAfterIdentifier(); | 1437 TypeName type = _parseTypeNameAfterIdentifier(); |
1839 keyword = _currentToken.keyword; | 1438 keyword = _currentToken.keyword; |
1840 next = _peek(); | 1439 next = _peek(); |
1841 isFollowedByIdentifier = _tokenMatchesIdentifier(next); | 1440 isFollowedByIdentifier = _tokenMatchesIdentifier(next); |
1842 if (keyword == Keyword.GET && isFollowedByIdentifier) { | 1441 if (keyword == Keyword.GET && isFollowedByIdentifier) { |
1843 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1442 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1844 return _parseGetter(commentAndMetadata, modifiers.externalKeyword, | 1443 return parseGetter(commentAndMetadata, modifiers.externalKeyword, |
1845 modifiers.staticKeyword, type); | 1444 modifiers.staticKeyword, type); |
1846 } else if (keyword == Keyword.SET && isFollowedByIdentifier) { | 1445 } else if (keyword == Keyword.SET && isFollowedByIdentifier) { |
1847 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1446 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1848 return _parseSetter(commentAndMetadata, modifiers.externalKeyword, | 1447 return parseSetter(commentAndMetadata, modifiers.externalKeyword, |
1849 modifiers.staticKeyword, type); | 1448 modifiers.staticKeyword, type); |
1850 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { | 1449 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { |
1851 _validateModifiersForOperator(modifiers); | 1450 _validateModifiersForOperator(modifiers); |
1852 return _parseOperatorAfterKeyword( | 1451 return _parseOperatorAfterKeyword( |
1853 commentAndMetadata, modifiers.externalKeyword, type, getAndAdvance()); | 1452 commentAndMetadata, modifiers.externalKeyword, type, getAndAdvance()); |
1854 } else if (!_matchesIdentifier()) { | 1453 } else if (!_matchesIdentifier()) { |
1855 if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { | 1454 if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { |
1856 // | 1455 // |
1857 // We appear to have found an incomplete declaration at the end of the | 1456 // We appear to have found an incomplete declaration at the end of the |
1858 // class. At this point it consists of a type name, so we'll treat it as | 1457 // class. At this point it consists of a type name, so we'll treat it as |
1859 // a field declaration with a missing field name and semicolon. | 1458 // a field declaration with a missing field name and semicolon. |
1860 // | 1459 // |
1861 return _parseInitializedIdentifierList( | 1460 return parseInitializedIdentifierList( |
1862 commentAndMetadata, | 1461 commentAndMetadata, |
1863 modifiers.staticKeyword, | 1462 modifiers.staticKeyword, |
1864 _validateModifiersForField(modifiers), | 1463 _validateModifiersForField(modifiers), |
1865 type); | 1464 type); |
1866 } | 1465 } |
1867 if (_isOperator(_currentToken)) { | 1466 if (_isOperator(_currentToken)) { |
1868 // | 1467 // |
1869 // We appear to have found an operator declaration without the | 1468 // We appear to have found an operator declaration without the |
1870 // 'operator' keyword. | 1469 // 'operator' keyword. |
1871 // | 1470 // |
1872 _validateModifiersForOperator(modifiers); | 1471 _validateModifiersForOperator(modifiers); |
1873 return parseOperator( | 1472 return parseOperator( |
1874 commentAndMetadata, modifiers.externalKeyword, type); | 1473 commentAndMetadata, modifiers.externalKeyword, type); |
1875 } | 1474 } |
1876 // | 1475 // |
1877 // We appear to have found an incomplete declaration before another | 1476 // We appear to have found an incomplete declaration before another |
1878 // declaration. At this point it consists of a type name, so we'll treat | 1477 // declaration. At this point it consists of a type name, so we'll treat |
1879 // it as a field declaration with a missing field name and semicolon. | 1478 // it as a field declaration with a missing field name and semicolon. |
1880 // | 1479 // |
1881 _reportErrorForToken( | 1480 _reportErrorForToken( |
1882 ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken); | 1481 ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken); |
1883 try { | 1482 try { |
1884 _lockErrorListener(); | 1483 _lockErrorListener(); |
1885 return _parseInitializedIdentifierList( | 1484 return parseInitializedIdentifierList( |
1886 commentAndMetadata, | 1485 commentAndMetadata, |
1887 modifiers.staticKeyword, | 1486 modifiers.staticKeyword, |
1888 _validateModifiersForField(modifiers), | 1487 _validateModifiersForField(modifiers), |
1889 type); | 1488 type); |
1890 } finally { | 1489 } finally { |
1891 _unlockErrorListener(); | 1490 _unlockErrorListener(); |
1892 } | 1491 } |
1893 } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) { | 1492 } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) { |
1894 SimpleIdentifier methodName = | 1493 SimpleIdentifier methodName = |
1895 _parseSimpleIdentifierUnchecked(isDeclaration: true); | 1494 _parseSimpleIdentifierUnchecked(isDeclaration: true); |
(...skipping 24 matching lines...) Expand all Loading... |
1920 } else if (parseGenericMethods && _tokenMatches(next, TokenType.LT)) { | 1519 } else if (parseGenericMethods && _tokenMatches(next, TokenType.LT)) { |
1921 return _parseMethodDeclarationAfterReturnType(commentAndMetadata, | 1520 return _parseMethodDeclarationAfterReturnType(commentAndMetadata, |
1922 modifiers.externalKeyword, modifiers.staticKeyword, type); | 1521 modifiers.externalKeyword, modifiers.staticKeyword, type); |
1923 } else if (_tokenMatches(next, TokenType.OPEN_CURLY_BRACKET)) { | 1522 } else if (_tokenMatches(next, TokenType.OPEN_CURLY_BRACKET)) { |
1924 // We have found "TypeName identifier {", and are guessing that this is a | 1523 // We have found "TypeName identifier {", and are guessing that this is a |
1925 // getter without the keyword 'get'. | 1524 // getter without the keyword 'get'. |
1926 _validateModifiersForGetterOrSetterOrMethod(modifiers); | 1525 _validateModifiersForGetterOrSetterOrMethod(modifiers); |
1927 _reportErrorForCurrentToken(ParserErrorCode.MISSING_GET); | 1526 _reportErrorForCurrentToken(ParserErrorCode.MISSING_GET); |
1928 _currentToken = _injectToken( | 1527 _currentToken = _injectToken( |
1929 new Parser_SyntheticKeywordToken(Keyword.GET, _currentToken.offset)); | 1528 new Parser_SyntheticKeywordToken(Keyword.GET, _currentToken.offset)); |
1930 return _parseGetter(commentAndMetadata, modifiers.externalKeyword, | 1529 return parseGetter(commentAndMetadata, modifiers.externalKeyword, |
1931 modifiers.staticKeyword, type); | 1530 modifiers.staticKeyword, type); |
1932 } | 1531 } |
1933 return _parseInitializedIdentifierList(commentAndMetadata, | 1532 return parseInitializedIdentifierList(commentAndMetadata, |
1934 modifiers.staticKeyword, _validateModifiersForField(modifiers), type); | 1533 modifiers.staticKeyword, _validateModifiersForField(modifiers), type); |
1935 } | 1534 } |
1936 | 1535 |
1937 /** | 1536 /** |
1938 * Parse a class type alias. The [commentAndMetadata] is the metadata to be | 1537 * Parse a class type alias. The [commentAndMetadata] is the metadata to be |
1939 * associated with the member. The [abstractKeyword] is the token representing | 1538 * associated with the member. The [abstractKeyword] is the token representing |
1940 * the 'abstract' keyword. The [classKeyword] is the token representing the | 1539 * the 'abstract' keyword. The [classKeyword] is the token representing the |
1941 * 'class' keyword. Return the class type alias that was parsed. | 1540 * 'class' keyword. Return the class type alias that was parsed. |
1942 * | 1541 * |
1943 * This method assumes that the current token matches an identifier. | 1542 * This method assumes that the current token matches an identifier. |
(...skipping 19 matching lines...) Expand all Loading... |
1963 /** | 1562 /** |
1964 * Parse a single combinator. Return the combinator that was parsed, or `null` | 1563 * Parse a single combinator. Return the combinator that was parsed, or `null` |
1965 * if no combinator is found. | 1564 * if no combinator is found. |
1966 * | 1565 * |
1967 * combinator ::= | 1566 * combinator ::= |
1968 * 'show' identifier (',' identifier)* | 1567 * 'show' identifier (',' identifier)* |
1969 * | 'hide' identifier (',' identifier)* | 1568 * | 'hide' identifier (',' identifier)* |
1970 */ | 1569 */ |
1971 Combinator parseCombinator() { | 1570 Combinator parseCombinator() { |
1972 if (_matchesString(_SHOW)) { | 1571 if (_matchesString(_SHOW)) { |
1973 return new ShowCombinator(getAndAdvance(), _parseIdentifierList()); | 1572 return new ShowCombinator(getAndAdvance(), parseIdentifierList()); |
1974 } else if (_matchesString(_HIDE)) { | 1573 } else if (_matchesString(_HIDE)) { |
1975 return new HideCombinator(getAndAdvance(), _parseIdentifierList()); | 1574 return new HideCombinator(getAndAdvance(), parseIdentifierList()); |
1976 } | 1575 } |
1977 return null; | 1576 return null; |
1978 } | 1577 } |
1979 | 1578 |
1980 /** | 1579 /** |
1981 * Parse a list of combinators in a directive. Return the combinators that | 1580 * Parse a list of combinators in a directive. Return the combinators that |
1982 * were parsed, or `null` if there are no combinators. | 1581 * were parsed, or `null` if there are no combinators. |
1983 * | 1582 * |
1984 * combinator ::= | 1583 * combinator ::= |
1985 * 'show' identifier (',' identifier)* | 1584 * 'show' identifier (',' identifier)* |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2127 return null; | 1726 return null; |
2128 } | 1727 } |
2129 } | 1728 } |
2130 } catch (exception) { | 1729 } catch (exception) { |
2131 // Ignored because we assume that it wasn't a real comment reference. | 1730 // Ignored because we assume that it wasn't a real comment reference. |
2132 } | 1731 } |
2133 return null; | 1732 return null; |
2134 } | 1733 } |
2135 | 1734 |
2136 /** | 1735 /** |
| 1736 * Parse all of the comment references occurring in the given array of |
| 1737 * documentation comments. The [tokens] are the comment tokens representing |
| 1738 * the documentation comments to be parsed. Return the comment references that |
| 1739 * were parsed. |
| 1740 * |
| 1741 * commentReference ::= |
| 1742 * '[' 'new'? qualified ']' libraryReference? |
| 1743 * |
| 1744 * libraryReference ::= |
| 1745 * '(' stringLiteral ')' |
| 1746 */ |
| 1747 List<CommentReference> parseCommentReferences( |
| 1748 List<DocumentationCommentToken> tokens) { |
| 1749 List<CommentReference> references = <CommentReference>[]; |
| 1750 bool isInGitHubCodeBlock = false; |
| 1751 for (DocumentationCommentToken token in tokens) { |
| 1752 String comment = token.lexeme; |
| 1753 // Skip GitHub code blocks. |
| 1754 // https://help.github.com/articles/creating-and-highlighting-code-blocks/ |
| 1755 if (tokens.length != 1) { |
| 1756 if (comment.indexOf('```') != -1) { |
| 1757 isInGitHubCodeBlock = !isInGitHubCodeBlock; |
| 1758 } |
| 1759 if (isInGitHubCodeBlock) { |
| 1760 continue; |
| 1761 } |
| 1762 } |
| 1763 // Remove GitHub include code. |
| 1764 comment = _removeGitHubInlineCode(comment); |
| 1765 // Find references. |
| 1766 int length = comment.length; |
| 1767 List<List<int>> codeBlockRanges = _getCodeBlockRanges(comment); |
| 1768 int leftIndex = comment.indexOf('['); |
| 1769 while (leftIndex >= 0 && leftIndex + 1 < length) { |
| 1770 List<int> range = _findRange(codeBlockRanges, leftIndex); |
| 1771 if (range == null) { |
| 1772 int nameOffset = token.offset + leftIndex + 1; |
| 1773 int rightIndex = comment.indexOf(']', leftIndex); |
| 1774 if (rightIndex >= 0) { |
| 1775 int firstChar = comment.codeUnitAt(leftIndex + 1); |
| 1776 if (firstChar != 0x27 && firstChar != 0x22) { |
| 1777 if (_isLinkText(comment, rightIndex)) { |
| 1778 // TODO(brianwilkerson) Handle the case where there's a library |
| 1779 // URI in the link text. |
| 1780 } else { |
| 1781 CommentReference reference = parseCommentReference( |
| 1782 comment.substring(leftIndex + 1, rightIndex), nameOffset); |
| 1783 if (reference != null) { |
| 1784 references.add(reference); |
| 1785 token.references.add(reference.beginToken); |
| 1786 } |
| 1787 } |
| 1788 } |
| 1789 } else { |
| 1790 // terminating ']' is not typed yet |
| 1791 int charAfterLeft = comment.codeUnitAt(leftIndex + 1); |
| 1792 Token nameToken; |
| 1793 if (Character.isLetterOrDigit(charAfterLeft)) { |
| 1794 int nameEnd = StringUtilities.indexOfFirstNotLetterDigit( |
| 1795 comment, leftIndex + 1); |
| 1796 String name = comment.substring(leftIndex + 1, nameEnd); |
| 1797 nameToken = |
| 1798 new StringToken(TokenType.IDENTIFIER, name, nameOffset); |
| 1799 } else { |
| 1800 nameToken = new SyntheticStringToken( |
| 1801 TokenType.IDENTIFIER, '', nameOffset); |
| 1802 } |
| 1803 nameToken.setNext(new SimpleToken(TokenType.EOF, nameToken.end)); |
| 1804 references.add( |
| 1805 new CommentReference(null, new SimpleIdentifier(nameToken))); |
| 1806 token.references.add(nameToken); |
| 1807 // next character |
| 1808 rightIndex = leftIndex + 1; |
| 1809 } |
| 1810 leftIndex = comment.indexOf('[', rightIndex); |
| 1811 } else { |
| 1812 leftIndex = comment.indexOf('[', range[1]); |
| 1813 } |
| 1814 } |
| 1815 } |
| 1816 return references; |
| 1817 } |
| 1818 |
| 1819 /** |
2137 * Parse a compilation unit, starting with the given [token]. Return the | 1820 * Parse a compilation unit, starting with the given [token]. Return the |
2138 * compilation unit that was parsed. | 1821 * compilation unit that was parsed. |
2139 */ | 1822 */ |
2140 CompilationUnit parseCompilationUnit(Token token) { | 1823 CompilationUnit parseCompilationUnit(Token token) { |
2141 _currentToken = token; | 1824 _currentToken = token; |
2142 return parseCompilationUnit2(); | 1825 return parseCompilationUnit2(); |
2143 } | 1826 } |
2144 | 1827 |
2145 /** | 1828 /** |
2146 * Parse a compilation unit. Return the compilation unit that was parsed. | 1829 * Parse a compilation unit. Return the compilation unit that was parsed. |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2188 keyword == Keyword.PART) && | 1871 keyword == Keyword.PART) && |
2189 nextType != TokenType.PERIOD && | 1872 nextType != TokenType.PERIOD && |
2190 nextType != TokenType.LT && | 1873 nextType != TokenType.LT && |
2191 nextType != TokenType.OPEN_PAREN) { | 1874 nextType != TokenType.OPEN_PAREN) { |
2192 Directive parseDirective() { | 1875 Directive parseDirective() { |
2193 if (keyword == Keyword.IMPORT) { | 1876 if (keyword == Keyword.IMPORT) { |
2194 if (partDirectiveFound) { | 1877 if (partDirectiveFound) { |
2195 _reportErrorForCurrentToken( | 1878 _reportErrorForCurrentToken( |
2196 ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE); | 1879 ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE); |
2197 } | 1880 } |
2198 return _parseImportDirective(commentAndMetadata); | 1881 return parseImportDirective(commentAndMetadata); |
2199 } else if (keyword == Keyword.EXPORT) { | 1882 } else if (keyword == Keyword.EXPORT) { |
2200 if (partDirectiveFound) { | 1883 if (partDirectiveFound) { |
2201 _reportErrorForCurrentToken( | 1884 _reportErrorForCurrentToken( |
2202 ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE); | 1885 ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE); |
2203 } | 1886 } |
2204 return _parseExportDirective(commentAndMetadata); | 1887 return parseExportDirective(commentAndMetadata); |
2205 } else if (keyword == Keyword.LIBRARY) { | 1888 } else if (keyword == Keyword.LIBRARY) { |
2206 if (libraryDirectiveFound) { | 1889 if (libraryDirectiveFound) { |
2207 _reportErrorForCurrentToken( | 1890 _reportErrorForCurrentToken( |
2208 ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES); | 1891 ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES); |
2209 } else { | 1892 } else { |
2210 if (directives.length > 0) { | 1893 if (directives.length > 0) { |
2211 _reportErrorForCurrentToken( | 1894 _reportErrorForCurrentToken( |
2212 ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST); | 1895 ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST); |
2213 } | 1896 } |
2214 libraryDirectiveFound = true; | 1897 libraryDirectiveFound = true; |
2215 } | 1898 } |
2216 return _parseLibraryDirective(commentAndMetadata); | 1899 return parseLibraryDirective(commentAndMetadata); |
2217 } else if (keyword == Keyword.PART) { | 1900 } else if (keyword == Keyword.PART) { |
2218 if (_tokenMatchesString(_peek(), _OF)) { | 1901 if (_tokenMatchesString(_peek(), _OF)) { |
2219 partOfDirectiveFound = true; | 1902 partOfDirectiveFound = true; |
2220 return _parsePartOfDirective(commentAndMetadata); | 1903 return _parsePartOfDirective(commentAndMetadata); |
2221 } else { | 1904 } else { |
2222 partDirectiveFound = true; | 1905 partDirectiveFound = true; |
2223 return _parsePartDirective(commentAndMetadata); | 1906 return _parsePartDirective(commentAndMetadata); |
2224 } | 1907 } |
2225 } else { | 1908 } else { |
2226 // Internal error: this method should not have been invoked if the | 1909 // Internal error: this method should not have been invoked if the |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2310 * | external functionSignature | 1993 * | external functionSignature |
2311 * | external getterSignature | 1994 * | external getterSignature |
2312 * | external setterSignature | 1995 * | external setterSignature |
2313 * | functionSignature functionBody | 1996 * | functionSignature functionBody |
2314 * | returnType? getOrSet identifier formalParameterList functionBody | 1997 * | returnType? getOrSet identifier formalParameterList functionBody |
2315 * | (final | const) type? staticFinalDeclarationList ';' | 1998 * | (final | const) type? staticFinalDeclarationList ';' |
2316 * | variableDeclaration ';' | 1999 * | variableDeclaration ';' |
2317 */ | 2000 */ |
2318 CompilationUnitMember parseCompilationUnitMember( | 2001 CompilationUnitMember parseCompilationUnitMember( |
2319 CommentAndMetadata commentAndMetadata) { | 2002 CommentAndMetadata commentAndMetadata) { |
2320 Modifiers modifiers = _parseModifiers(); | 2003 Modifiers modifiers = parseModifiers(); |
2321 Keyword keyword = _currentToken.keyword; | 2004 Keyword keyword = _currentToken.keyword; |
2322 if (keyword == Keyword.CLASS) { | 2005 if (keyword == Keyword.CLASS) { |
2323 return parseClassDeclaration( | 2006 return parseClassDeclaration( |
2324 commentAndMetadata, _validateModifiersForClass(modifiers)); | 2007 commentAndMetadata, _validateModifiersForClass(modifiers)); |
2325 } | 2008 } |
2326 Token next = _peek(); | 2009 Token next = _peek(); |
2327 TokenType nextType = next.type; | 2010 TokenType nextType = next.type; |
2328 if (keyword == Keyword.TYPEDEF && | 2011 if (keyword == Keyword.TYPEDEF && |
2329 nextType != TokenType.PERIOD && | 2012 nextType != TokenType.PERIOD && |
2330 nextType != TokenType.LT && | 2013 nextType != TokenType.LT && |
2331 nextType != TokenType.OPEN_PAREN) { | 2014 nextType != TokenType.OPEN_PAREN) { |
2332 _validateModifiersForTypedef(modifiers); | 2015 _validateModifiersForTypedef(modifiers); |
2333 return _parseTypeAlias(commentAndMetadata); | 2016 return parseTypeAlias(commentAndMetadata); |
2334 } else if (keyword == Keyword.ENUM) { | 2017 } else if (keyword == Keyword.ENUM) { |
2335 _validateModifiersForEnum(modifiers); | 2018 _validateModifiersForEnum(modifiers); |
2336 return parseEnumDeclaration(commentAndMetadata); | 2019 return parseEnumDeclaration(commentAndMetadata); |
2337 } else if (keyword == Keyword.VOID) { | 2020 } else if (keyword == Keyword.VOID) { |
2338 TypeName returnType = | 2021 TypeName returnType = |
2339 new TypeName(new SimpleIdentifier(getAndAdvance()), null); | 2022 new TypeName(new SimpleIdentifier(getAndAdvance()), null); |
2340 keyword = _currentToken.keyword; | 2023 keyword = _currentToken.keyword; |
2341 next = _peek(); | 2024 next = _peek(); |
2342 if ((keyword == Keyword.GET || keyword == Keyword.SET) && | 2025 if ((keyword == Keyword.GET || keyword == Keyword.SET) && |
2343 _tokenMatchesIdentifier(next)) { | 2026 _tokenMatchesIdentifier(next)) { |
2344 _validateModifiersForTopLevelFunction(modifiers); | 2027 _validateModifiersForTopLevelFunction(modifiers); |
2345 return _parseFunctionDeclaration( | 2028 return parseFunctionDeclaration( |
2346 commentAndMetadata, modifiers.externalKeyword, returnType); | 2029 commentAndMetadata, modifiers.externalKeyword, returnType); |
2347 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { | 2030 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { |
2348 _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); | 2031 _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); |
2349 return _convertToFunctionDeclaration(_parseOperatorAfterKeyword( | 2032 return _convertToFunctionDeclaration(_parseOperatorAfterKeyword( |
2350 commentAndMetadata, | 2033 commentAndMetadata, |
2351 modifiers.externalKeyword, | 2034 modifiers.externalKeyword, |
2352 returnType, | 2035 returnType, |
2353 getAndAdvance())); | 2036 getAndAdvance())); |
2354 } else if (_matchesIdentifier() && | 2037 } else if (_matchesIdentifier() && |
2355 next.matchesAny(const <TokenType>[ | 2038 next.matchesAny(const <TokenType>[ |
2356 TokenType.OPEN_PAREN, | 2039 TokenType.OPEN_PAREN, |
2357 TokenType.OPEN_CURLY_BRACKET, | 2040 TokenType.OPEN_CURLY_BRACKET, |
2358 TokenType.FUNCTION, | 2041 TokenType.FUNCTION, |
2359 TokenType.LT | 2042 TokenType.LT |
2360 ])) { | 2043 ])) { |
2361 _validateModifiersForTopLevelFunction(modifiers); | 2044 _validateModifiersForTopLevelFunction(modifiers); |
2362 return _parseFunctionDeclaration( | 2045 return parseFunctionDeclaration( |
2363 commentAndMetadata, modifiers.externalKeyword, returnType); | 2046 commentAndMetadata, modifiers.externalKeyword, returnType); |
2364 } else { | 2047 } else { |
2365 // | 2048 // |
2366 // We have found an error of some kind. Try to recover. | 2049 // We have found an error of some kind. Try to recover. |
2367 // | 2050 // |
2368 if (_matchesIdentifier()) { | 2051 if (_matchesIdentifier()) { |
2369 if (next.matchesAny(const <TokenType>[ | 2052 if (next.matchesAny(const <TokenType>[ |
2370 TokenType.EQ, | 2053 TokenType.EQ, |
2371 TokenType.COMMA, | 2054 TokenType.COMMA, |
2372 TokenType.SEMICOLON | 2055 TokenType.SEMICOLON |
(...skipping 10 matching lines...) Expand all Loading... |
2383 _expect(TokenType.SEMICOLON)); | 2066 _expect(TokenType.SEMICOLON)); |
2384 } | 2067 } |
2385 } | 2068 } |
2386 _reportErrorForToken( | 2069 _reportErrorForToken( |
2387 ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); | 2070 ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); |
2388 return null; | 2071 return null; |
2389 } | 2072 } |
2390 } else if ((keyword == Keyword.GET || keyword == Keyword.SET) && | 2073 } else if ((keyword == Keyword.GET || keyword == Keyword.SET) && |
2391 _tokenMatchesIdentifier(next)) { | 2074 _tokenMatchesIdentifier(next)) { |
2392 _validateModifiersForTopLevelFunction(modifiers); | 2075 _validateModifiersForTopLevelFunction(modifiers); |
2393 return _parseFunctionDeclaration( | 2076 return parseFunctionDeclaration( |
2394 commentAndMetadata, modifiers.externalKeyword, null); | 2077 commentAndMetadata, modifiers.externalKeyword, null); |
2395 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { | 2078 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { |
2396 _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); | 2079 _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); |
2397 return _convertToFunctionDeclaration(_parseOperatorAfterKeyword( | 2080 return _convertToFunctionDeclaration(_parseOperatorAfterKeyword( |
2398 commentAndMetadata, | 2081 commentAndMetadata, |
2399 modifiers.externalKeyword, | 2082 modifiers.externalKeyword, |
2400 null, | 2083 null, |
2401 getAndAdvance())); | 2084 getAndAdvance())); |
2402 } else if (!_matchesIdentifier()) { | 2085 } else if (!_matchesIdentifier()) { |
2403 Token keyword = modifiers.varKeyword; | 2086 Token keyword = modifiers.varKeyword; |
(...skipping 13 matching lines...) Expand all Loading... |
2417 List<VariableDeclaration> variables = <VariableDeclaration>[variable]; | 2100 List<VariableDeclaration> variables = <VariableDeclaration>[variable]; |
2418 return new TopLevelVariableDeclaration( | 2101 return new TopLevelVariableDeclaration( |
2419 commentAndMetadata.comment, | 2102 commentAndMetadata.comment, |
2420 commentAndMetadata.metadata, | 2103 commentAndMetadata.metadata, |
2421 new VariableDeclarationList(null, null, keyword, null, variables), | 2104 new VariableDeclarationList(null, null, keyword, null, variables), |
2422 _expect(TokenType.SEMICOLON)); | 2105 _expect(TokenType.SEMICOLON)); |
2423 } | 2106 } |
2424 _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); | 2107 _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); |
2425 return null; | 2108 return null; |
2426 } else if (_isPeekGenericTypeParametersAndOpenParen()) { | 2109 } else if (_isPeekGenericTypeParametersAndOpenParen()) { |
2427 return _parseFunctionDeclaration( | 2110 return parseFunctionDeclaration( |
2428 commentAndMetadata, modifiers.externalKeyword, null); | 2111 commentAndMetadata, modifiers.externalKeyword, null); |
2429 } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) { | 2112 } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) { |
2430 TypeName returnType = _parseOptionalTypeNameComment(); | 2113 TypeName returnType = _parseOptionalTypeNameComment(); |
2431 _validateModifiersForTopLevelFunction(modifiers); | 2114 _validateModifiersForTopLevelFunction(modifiers); |
2432 return _parseFunctionDeclaration( | 2115 return parseFunctionDeclaration( |
2433 commentAndMetadata, modifiers.externalKeyword, returnType); | 2116 commentAndMetadata, modifiers.externalKeyword, returnType); |
2434 } else if (next.matchesAny(const <TokenType>[ | 2117 } else if (next.matchesAny(const <TokenType>[ |
2435 TokenType.EQ, | 2118 TokenType.EQ, |
2436 TokenType.COMMA, | 2119 TokenType.COMMA, |
2437 TokenType.SEMICOLON | 2120 TokenType.SEMICOLON |
2438 ])) { | 2121 ])) { |
2439 if (modifiers.constKeyword == null && | 2122 if (modifiers.constKeyword == null && |
2440 modifiers.finalKeyword == null && | 2123 modifiers.finalKeyword == null && |
2441 modifiers.varKeyword == null) { | 2124 modifiers.varKeyword == null) { |
2442 _reportErrorForCurrentToken( | 2125 _reportErrorForCurrentToken( |
2443 ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); | 2126 ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); |
2444 } | 2127 } |
2445 return new TopLevelVariableDeclaration( | 2128 return new TopLevelVariableDeclaration( |
2446 commentAndMetadata.comment, | 2129 commentAndMetadata.comment, |
2447 commentAndMetadata.metadata, | 2130 commentAndMetadata.metadata, |
2448 parseVariableDeclarationListAfterType( | 2131 parseVariableDeclarationListAfterType( |
2449 null, _validateModifiersForTopLevelVariable(modifiers), null), | 2132 null, _validateModifiersForTopLevelVariable(modifiers), null), |
2450 _expect(TokenType.SEMICOLON)); | 2133 _expect(TokenType.SEMICOLON)); |
2451 } | 2134 } |
2452 TypeName returnType = parseReturnType(); | 2135 TypeName returnType = parseReturnType(); |
2453 keyword = _currentToken.keyword; | 2136 keyword = _currentToken.keyword; |
2454 next = _peek(); | 2137 next = _peek(); |
2455 if ((keyword == Keyword.GET || keyword == Keyword.SET) && | 2138 if ((keyword == Keyword.GET || keyword == Keyword.SET) && |
2456 _tokenMatchesIdentifier(next)) { | 2139 _tokenMatchesIdentifier(next)) { |
2457 _validateModifiersForTopLevelFunction(modifiers); | 2140 _validateModifiersForTopLevelFunction(modifiers); |
2458 return _parseFunctionDeclaration( | 2141 return parseFunctionDeclaration( |
2459 commentAndMetadata, modifiers.externalKeyword, returnType); | 2142 commentAndMetadata, modifiers.externalKeyword, returnType); |
2460 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { | 2143 } else if (keyword == Keyword.OPERATOR && _isOperator(next)) { |
2461 _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); | 2144 _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); |
2462 return _convertToFunctionDeclaration(_parseOperatorAfterKeyword( | 2145 return _convertToFunctionDeclaration(_parseOperatorAfterKeyword( |
2463 commentAndMetadata, | 2146 commentAndMetadata, |
2464 modifiers.externalKeyword, | 2147 modifiers.externalKeyword, |
2465 returnType, | 2148 returnType, |
2466 getAndAdvance())); | 2149 getAndAdvance())); |
2467 } else if (_matches(TokenType.AT)) { | 2150 } else if (_matches(TokenType.AT)) { |
2468 return new TopLevelVariableDeclaration( | 2151 return new TopLevelVariableDeclaration( |
(...skipping 20 matching lines...) Expand all Loading... |
2489 commentAndMetadata.metadata, | 2172 commentAndMetadata.metadata, |
2490 new VariableDeclarationList(null, null, null, returnType, variables), | 2173 new VariableDeclarationList(null, null, null, returnType, variables), |
2491 semicolon); | 2174 semicolon); |
2492 } else if (next.matchesAny(const <TokenType>[ | 2175 } else if (next.matchesAny(const <TokenType>[ |
2493 TokenType.OPEN_PAREN, | 2176 TokenType.OPEN_PAREN, |
2494 TokenType.FUNCTION, | 2177 TokenType.FUNCTION, |
2495 TokenType.OPEN_CURLY_BRACKET, | 2178 TokenType.OPEN_CURLY_BRACKET, |
2496 TokenType.LT | 2179 TokenType.LT |
2497 ])) { | 2180 ])) { |
2498 _validateModifiersForTopLevelFunction(modifiers); | 2181 _validateModifiersForTopLevelFunction(modifiers); |
2499 return _parseFunctionDeclaration( | 2182 return parseFunctionDeclaration( |
2500 commentAndMetadata, modifiers.externalKeyword, returnType); | 2183 commentAndMetadata, modifiers.externalKeyword, returnType); |
2501 } | 2184 } |
2502 return new TopLevelVariableDeclaration( | 2185 return new TopLevelVariableDeclaration( |
2503 commentAndMetadata.comment, | 2186 commentAndMetadata.comment, |
2504 commentAndMetadata.metadata, | 2187 commentAndMetadata.metadata, |
2505 parseVariableDeclarationListAfterType( | 2188 parseVariableDeclarationListAfterType( |
2506 null, _validateModifiersForTopLevelVariable(modifiers), returnType), | 2189 null, _validateModifiersForTopLevelVariable(modifiers), returnType), |
2507 _expect(TokenType.SEMICOLON)); | 2190 _expect(TokenType.SEMICOLON)); |
2508 } | 2191 } |
2509 | 2192 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2571 * | listLiteral | 2254 * | listLiteral |
2572 * | mapLiteral | 2255 * | mapLiteral |
2573 */ | 2256 */ |
2574 Expression parseConstExpression() { | 2257 Expression parseConstExpression() { |
2575 Token keyword = getAndAdvance(); | 2258 Token keyword = getAndAdvance(); |
2576 TokenType type = _currentToken.type; | 2259 TokenType type = _currentToken.type; |
2577 if (type == TokenType.LT || _injectGenericCommentTypeList()) { | 2260 if (type == TokenType.LT || _injectGenericCommentTypeList()) { |
2578 return parseListOrMapLiteral(keyword); | 2261 return parseListOrMapLiteral(keyword); |
2579 } else if (type == TokenType.OPEN_SQUARE_BRACKET || | 2262 } else if (type == TokenType.OPEN_SQUARE_BRACKET || |
2580 type == TokenType.INDEX) { | 2263 type == TokenType.INDEX) { |
2581 return _parseListLiteral(keyword, null); | 2264 return parseListLiteral(keyword, null); |
2582 } else if (type == TokenType.OPEN_CURLY_BRACKET) { | 2265 } else if (type == TokenType.OPEN_CURLY_BRACKET) { |
2583 return _parseMapLiteral(keyword, null); | 2266 return parseMapLiteral(keyword, null); |
2584 } | 2267 } |
2585 return _parseInstanceCreationExpression(keyword); | 2268 return parseInstanceCreationExpression(keyword); |
2586 } | 2269 } |
2587 | 2270 |
2588 /** | 2271 /** |
| 2272 * Parse a field initializer within a constructor. The flag [hasThis] should |
| 2273 * be true if the current token is `this`. Return the field initializer that |
| 2274 * was parsed. |
| 2275 * |
| 2276 * fieldInitializer: |
| 2277 * ('this' '.')? identifier '=' conditionalExpression cascadeSection* |
| 2278 */ |
| 2279 ConstructorFieldInitializer parseConstructorFieldInitializer(bool hasThis) { |
| 2280 Token keywordToken = null; |
| 2281 Token period = null; |
| 2282 if (hasThis) { |
| 2283 keywordToken = getAndAdvance(); |
| 2284 period = _expect(TokenType.PERIOD); |
| 2285 } |
| 2286 SimpleIdentifier fieldName = parseSimpleIdentifier(); |
| 2287 Token equals = null; |
| 2288 TokenType type = _currentToken.type; |
| 2289 if (type == TokenType.EQ) { |
| 2290 equals = getAndAdvance(); |
| 2291 } else { |
| 2292 _reportErrorForCurrentToken( |
| 2293 ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER); |
| 2294 Keyword keyword = _currentToken.keyword; |
| 2295 if (keyword != Keyword.THIS && |
| 2296 keyword != Keyword.SUPER && |
| 2297 type != TokenType.OPEN_CURLY_BRACKET && |
| 2298 type != TokenType.FUNCTION) { |
| 2299 equals = _createSyntheticToken(TokenType.EQ); |
| 2300 } else { |
| 2301 return new ConstructorFieldInitializer(keywordToken, period, fieldName, |
| 2302 _createSyntheticToken(TokenType.EQ), createSyntheticIdentifier()); |
| 2303 } |
| 2304 } |
| 2305 bool wasInInitializer = _inInitializer; |
| 2306 _inInitializer = true; |
| 2307 try { |
| 2308 Expression expression = parseConditionalExpression(); |
| 2309 if (_matches(TokenType.PERIOD_PERIOD)) { |
| 2310 List<Expression> cascadeSections = <Expression>[]; |
| 2311 do { |
| 2312 Expression section = parseCascadeSection(); |
| 2313 if (section != null) { |
| 2314 cascadeSections.add(section); |
| 2315 } |
| 2316 } while (_matches(TokenType.PERIOD_PERIOD)); |
| 2317 expression = new CascadeExpression(expression, cascadeSections); |
| 2318 } |
| 2319 return new ConstructorFieldInitializer( |
| 2320 keywordToken, period, fieldName, equals, expression); |
| 2321 } finally { |
| 2322 _inInitializer = wasInInitializer; |
| 2323 } |
| 2324 } |
| 2325 |
| 2326 /** |
2589 * Parse the name of a constructor. Return the constructor name that was | 2327 * Parse the name of a constructor. Return the constructor name that was |
2590 * parsed. | 2328 * parsed. |
2591 * | 2329 * |
2592 * constructorName: | 2330 * constructorName: |
2593 * type ('.' identifier)? | 2331 * type ('.' identifier)? |
2594 */ | 2332 */ |
2595 ConstructorName parseConstructorName() { | 2333 ConstructorName parseConstructorName() { |
2596 TypeName type = parseTypeName(false); | 2334 TypeName type = parseTypeName(false); |
2597 Token period = null; | 2335 Token period = null; |
2598 SimpleIdentifier name = null; | 2336 SimpleIdentifier name = null; |
(...skipping 24 matching lines...) Expand all Loading... |
2623 } | 2361 } |
2624 if (_inSwitch && !_inLoop && label == null) { | 2362 if (_inSwitch && !_inLoop && label == null) { |
2625 _reportErrorForToken( | 2363 _reportErrorForToken( |
2626 ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword); | 2364 ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword); |
2627 } | 2365 } |
2628 Token semicolon = _expect(TokenType.SEMICOLON); | 2366 Token semicolon = _expect(TokenType.SEMICOLON); |
2629 return new ContinueStatement(continueKeyword, label, semicolon); | 2367 return new ContinueStatement(continueKeyword, label, semicolon); |
2630 } | 2368 } |
2631 | 2369 |
2632 /** | 2370 /** |
| 2371 * Parse a directive. The [commentAndMetadata] is the metadata to be |
| 2372 * associated with the directive. Return the directive that was parsed. |
| 2373 * |
| 2374 * directive ::= |
| 2375 * exportDirective |
| 2376 * | libraryDirective |
| 2377 * | importDirective |
| 2378 * | partDirective |
| 2379 */ |
| 2380 Directive parseDirective(CommentAndMetadata commentAndMetadata) { |
| 2381 if (_matchesKeyword(Keyword.IMPORT)) { |
| 2382 return parseImportDirective(commentAndMetadata); |
| 2383 } else if (_matchesKeyword(Keyword.EXPORT)) { |
| 2384 return parseExportDirective(commentAndMetadata); |
| 2385 } else if (_matchesKeyword(Keyword.LIBRARY)) { |
| 2386 return parseLibraryDirective(commentAndMetadata); |
| 2387 } else if (_matchesKeyword(Keyword.PART)) { |
| 2388 return parsePartOrPartOfDirective(commentAndMetadata); |
| 2389 } else { |
| 2390 // Internal error: this method should not have been invoked if the current |
| 2391 // token was something other than one of the above. |
| 2392 throw new StateError( |
| 2393 "parseDirective invoked in an invalid state; currentToken = $_currentT
oken"); |
| 2394 } |
| 2395 } |
| 2396 |
| 2397 /** |
2633 * Parse the script tag and directives in a compilation unit, starting with | 2398 * Parse the script tag and directives in a compilation unit, starting with |
2634 * the given [token], until the first non-directive is encountered. The | 2399 * the given [token], until the first non-directive is encountered. The |
2635 * remainder of the compilation unit will not be parsed. Specifically, if | 2400 * remainder of the compilation unit will not be parsed. Specifically, if |
2636 * there are directives later in the file, they will not be parsed. Return the | 2401 * there are directives later in the file, they will not be parsed. Return the |
2637 * compilation unit that was parsed. | 2402 * compilation unit that was parsed. |
2638 */ | 2403 */ |
2639 CompilationUnit parseDirectives(Token token) { | 2404 CompilationUnit parseDirectives(Token token) { |
2640 _currentToken = token; | 2405 _currentToken = token; |
2641 return parseDirectives2(); | 2406 return parseDirectives2(); |
2642 } | 2407 } |
(...skipping 16 matching lines...) Expand all Loading... |
2659 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); | 2424 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); |
2660 Keyword keyword = _currentToken.keyword; | 2425 Keyword keyword = _currentToken.keyword; |
2661 TokenType type = _peek().type; | 2426 TokenType type = _peek().type; |
2662 if ((keyword == Keyword.IMPORT || | 2427 if ((keyword == Keyword.IMPORT || |
2663 keyword == Keyword.EXPORT || | 2428 keyword == Keyword.EXPORT || |
2664 keyword == Keyword.LIBRARY || | 2429 keyword == Keyword.LIBRARY || |
2665 keyword == Keyword.PART) && | 2430 keyword == Keyword.PART) && |
2666 type != TokenType.PERIOD && | 2431 type != TokenType.PERIOD && |
2667 type != TokenType.LT && | 2432 type != TokenType.LT && |
2668 type != TokenType.OPEN_PAREN) { | 2433 type != TokenType.OPEN_PAREN) { |
2669 directives.add(_parseDirective(commentAndMetadata)); | 2434 directives.add(parseDirective(commentAndMetadata)); |
2670 } else if (_matches(TokenType.SEMICOLON)) { | 2435 } else if (_matches(TokenType.SEMICOLON)) { |
2671 _advance(); | 2436 _advance(); |
2672 } else { | 2437 } else { |
2673 while (!_matches(TokenType.EOF)) { | 2438 while (!_matches(TokenType.EOF)) { |
2674 _advance(); | 2439 _advance(); |
2675 } | 2440 } |
2676 return new CompilationUnit( | 2441 return new CompilationUnit( |
2677 firstToken, scriptTag, directives, null, _currentToken); | 2442 firstToken, scriptTag, directives, null, _currentToken); |
2678 } | 2443 } |
2679 } | 2444 } |
2680 return new CompilationUnit( | 2445 return new CompilationUnit( |
2681 firstToken, scriptTag, directives, null, _currentToken); | 2446 firstToken, scriptTag, directives, null, _currentToken); |
2682 } | 2447 } |
2683 | 2448 |
2684 /** | 2449 /** |
2685 * Parse a documentation comment based on the given list of documentation | 2450 * Parse a documentation comment based on the given list of documentation |
2686 * comment tokens. Return the documentation comment that was parsed, or `null` | 2451 * comment tokens. Return the documentation comment that was parsed, or `null` |
2687 * if there was no comment. | 2452 * if there was no comment. |
2688 * | 2453 * |
2689 * documentationComment ::= | 2454 * documentationComment ::= |
2690 * multiLineComment? | 2455 * multiLineComment? |
2691 * | singleLineComment* | 2456 * | singleLineComment* |
2692 */ | 2457 */ |
2693 Comment parseDocumentationComment(List<DocumentationCommentToken> tokens) { | 2458 Comment parseDocumentationComment(List<DocumentationCommentToken> tokens) { |
2694 if (tokens == null) { | 2459 if (tokens == null) { |
2695 return null; | 2460 return null; |
2696 } | 2461 } |
2697 List<CommentReference> references = _parseCommentReferences(tokens); | 2462 List<CommentReference> references = parseCommentReferences(tokens); |
2698 return Comment.createDocumentationCommentWithReferences(tokens, references); | 2463 return Comment.createDocumentationCommentWithReferences(tokens, references); |
2699 } | 2464 } |
2700 | 2465 |
2701 /** | 2466 /** |
2702 * Parse a documentation comment. Return the documentation comment that was | 2467 * Parse a documentation comment. Return the documentation comment that was |
2703 * parsed, or `null` if there was no comment. | 2468 * parsed, or `null` if there was no comment. |
2704 * | 2469 * |
2705 * documentationComment ::= | 2470 * documentationComment ::= |
2706 * multiLineComment? | 2471 * multiLineComment? |
2707 * | singleLineComment* | 2472 * | singleLineComment* |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2822 commentAndMetadata.comment, | 2587 commentAndMetadata.comment, |
2823 commentAndMetadata.metadata, | 2588 commentAndMetadata.metadata, |
2824 keyword, | 2589 keyword, |
2825 name, | 2590 name, |
2826 leftBracket, | 2591 leftBracket, |
2827 constants, | 2592 constants, |
2828 rightBracket); | 2593 rightBracket); |
2829 } | 2594 } |
2830 | 2595 |
2831 /** | 2596 /** |
| 2597 * Parse an equality expression. Return the equality expression that was |
| 2598 * parsed. |
| 2599 * |
| 2600 * equalityExpression ::= |
| 2601 * relationalExpression (equalityOperator relationalExpression)? |
| 2602 * | 'super' equalityOperator relationalExpression |
| 2603 */ |
| 2604 Expression parseEqualityExpression() { |
| 2605 Expression expression; |
| 2606 if (_currentToken.keyword == Keyword.SUPER && |
| 2607 _currentToken.next.type.isEqualityOperator) { |
| 2608 expression = new SuperExpression(getAndAdvance()); |
| 2609 } else { |
| 2610 expression = parseRelationalExpression(); |
| 2611 } |
| 2612 bool leftEqualityExpression = false; |
| 2613 while (_currentToken.type.isEqualityOperator) { |
| 2614 if (leftEqualityExpression) { |
| 2615 _reportErrorForNode( |
| 2616 ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression); |
| 2617 } |
| 2618 expression = new BinaryExpression( |
| 2619 expression, getAndAdvance(), parseRelationalExpression()); |
| 2620 leftEqualityExpression = true; |
| 2621 } |
| 2622 return expression; |
| 2623 } |
| 2624 |
| 2625 /** |
| 2626 * Parse an export directive. The [commentAndMetadata] is the metadata to be |
| 2627 * associated with the directive. Return the export directive that was parsed. |
| 2628 * |
| 2629 * This method assumes that the current token matches `Keyword.EXPORT`. |
| 2630 * |
| 2631 * exportDirective ::= |
| 2632 * metadata 'export' stringLiteral configuration* combinator*';' |
| 2633 */ |
| 2634 ExportDirective parseExportDirective(CommentAndMetadata commentAndMetadata) { |
| 2635 Token exportKeyword = getAndAdvance(); |
| 2636 StringLiteral libraryUri = _parseUri(); |
| 2637 List<Configuration> configurations = _parseConfigurations(); |
| 2638 List<Combinator> combinators = parseCombinators(); |
| 2639 Token semicolon = _expect(TokenType.SEMICOLON); |
| 2640 return new ExportDirective( |
| 2641 commentAndMetadata.comment, |
| 2642 commentAndMetadata.metadata, |
| 2643 exportKeyword, |
| 2644 libraryUri, |
| 2645 configurations, |
| 2646 combinators, |
| 2647 semicolon); |
| 2648 } |
| 2649 |
| 2650 /** |
2832 * Parse an expression, starting with the given [token]. Return the expression | 2651 * Parse an expression, starting with the given [token]. Return the expression |
2833 * that was parsed, or `null` if the tokens do not represent a recognizable | 2652 * that was parsed, or `null` if the tokens do not represent a recognizable |
2834 * expression. | 2653 * expression. |
2835 */ | 2654 */ |
2836 Expression parseExpression(Token token) { | 2655 Expression parseExpression(Token token) { |
2837 _currentToken = token; | 2656 _currentToken = token; |
2838 return parseExpression2(); | 2657 return parseExpression2(); |
2839 } | 2658 } |
2840 | 2659 |
2841 /** | 2660 /** |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2978 ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); | 2797 ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); |
2979 } else { | 2798 } else { |
2980 // Support parameters such as `(/*=K*/ key, /*=V*/ value)` | 2799 // Support parameters such as `(/*=K*/ key, /*=V*/ value)` |
2981 // This is not supported if the type is required. | 2800 // This is not supported if the type is required. |
2982 type = _parseOptionalTypeNameComment(); | 2801 type = _parseOptionalTypeNameComment(); |
2983 } | 2802 } |
2984 return new FinalConstVarOrType(keywordToken, type); | 2803 return new FinalConstVarOrType(keywordToken, type); |
2985 } | 2804 } |
2986 | 2805 |
2987 /** | 2806 /** |
| 2807 * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be |
| 2808 * `true`. The [kind] is the kind of parameter being expected based on the |
| 2809 * presence or absence of group delimiters. Return the formal parameter that |
| 2810 * was parsed. |
| 2811 * |
| 2812 * defaultFormalParameter ::= |
| 2813 * normalFormalParameter ('=' expression)? |
| 2814 * |
| 2815 * defaultNamedParameter ::= |
| 2816 * normalFormalParameter (':' expression)? |
| 2817 */ |
| 2818 FormalParameter parseFormalParameter(ParameterKind kind) { |
| 2819 NormalFormalParameter parameter = parseNormalFormalParameter(); |
| 2820 TokenType type = _currentToken.type; |
| 2821 if (type == TokenType.EQ) { |
| 2822 Token separator = getAndAdvance(); |
| 2823 Expression defaultValue = parseExpression2(); |
| 2824 if (kind == ParameterKind.NAMED) { |
| 2825 _reportErrorForToken( |
| 2826 ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, separator); |
| 2827 } else if (kind == ParameterKind.REQUIRED) { |
| 2828 _reportErrorForNode( |
| 2829 ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter); |
| 2830 } |
| 2831 return new DefaultFormalParameter( |
| 2832 parameter, kind, separator, defaultValue); |
| 2833 } else if (type == TokenType.COLON) { |
| 2834 Token separator = getAndAdvance(); |
| 2835 Expression defaultValue = parseExpression2(); |
| 2836 if (kind == ParameterKind.POSITIONAL) { |
| 2837 _reportErrorForToken( |
| 2838 ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER, |
| 2839 separator); |
| 2840 } else if (kind == ParameterKind.REQUIRED) { |
| 2841 _reportErrorForNode( |
| 2842 ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter); |
| 2843 } |
| 2844 return new DefaultFormalParameter( |
| 2845 parameter, kind, separator, defaultValue); |
| 2846 } else if (kind != ParameterKind.REQUIRED) { |
| 2847 return new DefaultFormalParameter(parameter, kind, null, null); |
| 2848 } |
| 2849 return parameter; |
| 2850 } |
| 2851 |
| 2852 /** |
2988 * Parse a list of formal parameters. Return the formal parameters that were | 2853 * Parse a list of formal parameters. Return the formal parameters that were |
2989 * parsed. | 2854 * parsed. |
2990 * | 2855 * |
2991 * formalParameterList ::= | 2856 * formalParameterList ::= |
2992 * '(' ')' | 2857 * '(' ')' |
2993 * | '(' normalFormalParameters (',' optionalFormalParameters)? ')' | 2858 * | '(' normalFormalParameters (',' optionalFormalParameters)? ')' |
2994 * | '(' optionalFormalParameters ')' | 2859 * | '(' optionalFormalParameters ')' |
2995 * | 2860 * |
2996 * normalFormalParameters ::= | 2861 * normalFormalParameters ::= |
2997 * normalFormalParameter (',' normalFormalParameter)* | 2862 * normalFormalParameter (',' normalFormalParameter)* |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3282 } | 3147 } |
3283 } finally { | 3148 } finally { |
3284 _inAsync = wasInAsync; | 3149 _inAsync = wasInAsync; |
3285 _inGenerator = wasInGenerator; | 3150 _inGenerator = wasInGenerator; |
3286 _inLoop = wasInLoop; | 3151 _inLoop = wasInLoop; |
3287 _inSwitch = wasInSwitch; | 3152 _inSwitch = wasInSwitch; |
3288 } | 3153 } |
3289 } | 3154 } |
3290 | 3155 |
3291 /** | 3156 /** |
| 3157 * Parse a function declaration. The [commentAndMetadata] is the documentation |
| 3158 * comment and metadata to be associated with the declaration. The |
| 3159 * [externalKeyword] is the 'external' keyword, or `null` if the function is |
| 3160 * not external. The [returnType] is the return type, or `null` if there is no |
| 3161 * return type. The [isStatement] is `true` if the function declaration is |
| 3162 * being parsed as a statement. Return the function declaration that was |
| 3163 * parsed. |
| 3164 * |
| 3165 * functionDeclaration ::= |
| 3166 * functionSignature functionBody |
| 3167 * | returnType? getOrSet identifier formalParameterList functionBody |
| 3168 */ |
| 3169 FunctionDeclaration parseFunctionDeclaration( |
| 3170 CommentAndMetadata commentAndMetadata, |
| 3171 Token externalKeyword, |
| 3172 TypeName returnType) { |
| 3173 Token keywordToken = null; |
| 3174 bool isGetter = false; |
| 3175 Keyword keyword = _currentToken.keyword; |
| 3176 SimpleIdentifier name = null; |
| 3177 if (keyword == Keyword.GET) { |
| 3178 keywordToken = getAndAdvance(); |
| 3179 isGetter = true; |
| 3180 } else if (keyword == Keyword.SET) { |
| 3181 keywordToken = getAndAdvance(); |
| 3182 } |
| 3183 if (keywordToken != null && _matches(TokenType.OPEN_PAREN)) { |
| 3184 name = new SimpleIdentifier(keywordToken, isDeclaration: true); |
| 3185 keywordToken = null; |
| 3186 isGetter = false; |
| 3187 } else { |
| 3188 name = parseSimpleIdentifier(isDeclaration: true); |
| 3189 } |
| 3190 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); |
| 3191 FormalParameterList parameters = null; |
| 3192 if (!isGetter) { |
| 3193 if (_matches(TokenType.OPEN_PAREN)) { |
| 3194 parameters = _parseFormalParameterListUnchecked(); |
| 3195 _validateFormalParameterList(parameters); |
| 3196 } else { |
| 3197 _reportErrorForCurrentToken( |
| 3198 ParserErrorCode.MISSING_FUNCTION_PARAMETERS); |
| 3199 parameters = new FormalParameterList( |
| 3200 _createSyntheticToken(TokenType.OPEN_PAREN), |
| 3201 null, |
| 3202 null, |
| 3203 null, |
| 3204 _createSyntheticToken(TokenType.CLOSE_PAREN)); |
| 3205 } |
| 3206 } else if (_matches(TokenType.OPEN_PAREN)) { |
| 3207 _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS); |
| 3208 _parseFormalParameterListUnchecked(); |
| 3209 } |
| 3210 FunctionBody body; |
| 3211 if (externalKeyword == null) { |
| 3212 body = parseFunctionBody( |
| 3213 false, ParserErrorCode.MISSING_FUNCTION_BODY, false); |
| 3214 } else { |
| 3215 body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON)); |
| 3216 } |
| 3217 // if (!isStatement && matches(TokenType.SEMICOLON)) { |
| 3218 // // TODO(brianwilkerson) Improve this error message. |
| 3219 // reportError(ParserErrorCode.UNEXPECTED_TOKEN, currentToken.getLexeme
()); |
| 3220 // advance(); |
| 3221 // } |
| 3222 return new FunctionDeclaration( |
| 3223 commentAndMetadata.comment, |
| 3224 commentAndMetadata.metadata, |
| 3225 externalKeyword, |
| 3226 returnType, |
| 3227 keywordToken, |
| 3228 name, |
| 3229 new FunctionExpression(typeParameters, parameters, body)); |
| 3230 } |
| 3231 |
| 3232 /** |
3292 * Parse a function declaration statement. Return the function declaration | 3233 * Parse a function declaration statement. Return the function declaration |
3293 * statement that was parsed. | 3234 * statement that was parsed. |
3294 * | 3235 * |
3295 * functionDeclarationStatement ::= | 3236 * functionDeclarationStatement ::= |
3296 * functionSignature functionBody | 3237 * functionSignature functionBody |
3297 */ | 3238 */ |
3298 Statement parseFunctionDeclarationStatement() { | 3239 Statement parseFunctionDeclarationStatement() { |
3299 Modifiers modifiers = _parseModifiers(); | 3240 Modifiers modifiers = parseModifiers(); |
3300 _validateModifiersForFunctionDeclarationStatement(modifiers); | 3241 _validateModifiersForFunctionDeclarationStatement(modifiers); |
3301 return _parseFunctionDeclarationStatementAfterReturnType( | 3242 return _parseFunctionDeclarationStatementAfterReturnType( |
3302 parseCommentAndMetadata(), _parseOptionalReturnType()); | 3243 parseCommentAndMetadata(), _parseOptionalReturnType()); |
3303 } | 3244 } |
3304 | 3245 |
3305 /** | 3246 /** |
3306 * Parse a function expression. Return the function expression that was | 3247 * Parse a function expression. Return the function expression that was |
3307 * parsed. | 3248 * parsed. |
3308 * | 3249 * |
3309 * functionExpression ::= | 3250 * functionExpression ::= |
3310 * typeParameters? formalParameterList functionExpressionBody | 3251 * typeParameters? formalParameterList functionExpressionBody |
3311 */ | 3252 */ |
3312 FunctionExpression parseFunctionExpression() { | 3253 FunctionExpression parseFunctionExpression() { |
3313 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); | 3254 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); |
3314 FormalParameterList parameters = parseFormalParameterList(); | 3255 FormalParameterList parameters = parseFormalParameterList(); |
3315 _validateFormalParameterList(parameters); | 3256 _validateFormalParameterList(parameters); |
3316 FunctionBody body = | 3257 FunctionBody body = |
3317 parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true); | 3258 parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true); |
3318 return new FunctionExpression(typeParameters, parameters, body); | 3259 return new FunctionExpression(typeParameters, parameters, body); |
3319 } | 3260 } |
3320 | 3261 |
3321 /** | 3262 /** |
| 3263 * Parse a getter. The [commentAndMetadata] is the documentation comment and |
| 3264 * metadata to be associated with the declaration. The externalKeyword] is the |
| 3265 * 'external' token. The staticKeyword] is the static keyword, or `null` if |
| 3266 * the getter is not static. The [returnType] the return type that has already |
| 3267 * been parsed, or `null` if there was no return type. Return the getter that |
| 3268 * was parsed. |
| 3269 * |
| 3270 * This method assumes that the current token matches `Keyword.GET`. |
| 3271 * |
| 3272 * getter ::= |
| 3273 * getterSignature functionBody? |
| 3274 * |
| 3275 * getterSignature ::= |
| 3276 * 'external'? 'static'? returnType? 'get' identifier |
| 3277 */ |
| 3278 MethodDeclaration parseGetter(CommentAndMetadata commentAndMetadata, |
| 3279 Token externalKeyword, Token staticKeyword, TypeName returnType) { |
| 3280 Token propertyKeyword = getAndAdvance(); |
| 3281 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); |
| 3282 if (_matches(TokenType.OPEN_PAREN) && |
| 3283 _tokenMatches(_peek(), TokenType.CLOSE_PAREN)) { |
| 3284 _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS); |
| 3285 _advance(); |
| 3286 _advance(); |
| 3287 } |
| 3288 FunctionBody body = parseFunctionBody( |
| 3289 externalKeyword != null || staticKeyword == null, |
| 3290 ParserErrorCode.STATIC_GETTER_WITHOUT_BODY, |
| 3291 false); |
| 3292 if (externalKeyword != null && body is! EmptyFunctionBody) { |
| 3293 _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_GETTER_WITH_BODY); |
| 3294 } |
| 3295 return new MethodDeclaration( |
| 3296 commentAndMetadata.comment, |
| 3297 commentAndMetadata.metadata, |
| 3298 externalKeyword, |
| 3299 staticKeyword, |
| 3300 returnType, |
| 3301 propertyKeyword, |
| 3302 null, |
| 3303 name, |
| 3304 null, |
| 3305 null, |
| 3306 body); |
| 3307 } |
| 3308 |
| 3309 /** |
| 3310 * Parse a list of identifiers. Return the list of identifiers that were |
| 3311 * parsed. |
| 3312 * |
| 3313 * identifierList ::= |
| 3314 * identifier (',' identifier)* |
| 3315 */ |
| 3316 List<SimpleIdentifier> parseIdentifierList() { |
| 3317 List<SimpleIdentifier> identifiers = <SimpleIdentifier>[ |
| 3318 parseSimpleIdentifier() |
| 3319 ]; |
| 3320 while (_optional(TokenType.COMMA)) { |
| 3321 identifiers.add(parseSimpleIdentifier()); |
| 3322 } |
| 3323 return identifiers; |
| 3324 } |
| 3325 |
| 3326 /** |
3322 * Parse an if-null expression. Return the if-null expression that was | 3327 * Parse an if-null expression. Return the if-null expression that was |
3323 * parsed. | 3328 * parsed. |
3324 * | 3329 * |
3325 * ifNullExpression ::= logicalOrExpression ('??' logicalOrExpression)* | 3330 * ifNullExpression ::= logicalOrExpression ('??' logicalOrExpression)* |
3326 */ | 3331 */ |
3327 Expression parseIfNullExpression() { | 3332 Expression parseIfNullExpression() { |
3328 Expression expression = parseLogicalOrExpression(); | 3333 Expression expression = parseLogicalOrExpression(); |
3329 while (_currentToken.type == TokenType.QUESTION_QUESTION) { | 3334 while (_currentToken.type == TokenType.QUESTION_QUESTION) { |
3330 expression = new BinaryExpression( | 3335 expression = new BinaryExpression( |
3331 expression, getAndAdvance(), parseLogicalOrExpression()); | 3336 expression, getAndAdvance(), parseLogicalOrExpression()); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3369 Token keyword = getAndAdvance(); | 3374 Token keyword = getAndAdvance(); |
3370 List<TypeName> interfaces = <TypeName>[]; | 3375 List<TypeName> interfaces = <TypeName>[]; |
3371 interfaces.add(parseTypeName(false)); | 3376 interfaces.add(parseTypeName(false)); |
3372 while (_optional(TokenType.COMMA)) { | 3377 while (_optional(TokenType.COMMA)) { |
3373 interfaces.add(parseTypeName(false)); | 3378 interfaces.add(parseTypeName(false)); |
3374 } | 3379 } |
3375 return new ImplementsClause(keyword, interfaces); | 3380 return new ImplementsClause(keyword, interfaces); |
3376 } | 3381 } |
3377 | 3382 |
3378 /** | 3383 /** |
| 3384 * Parse an import directive. The [commentAndMetadata] is the metadata to be |
| 3385 * associated with the directive. Return the import directive that was parsed. |
| 3386 * |
| 3387 * This method assumes that the current token matches `Keyword.IMPORT`. |
| 3388 * |
| 3389 * importDirective ::= |
| 3390 * metadata 'import' stringLiteral configuration* (deferred)? ('as' id
entifier)? combinator*';' |
| 3391 */ |
| 3392 ImportDirective parseImportDirective(CommentAndMetadata commentAndMetadata) { |
| 3393 Token importKeyword = getAndAdvance(); |
| 3394 StringLiteral libraryUri = _parseUri(); |
| 3395 List<Configuration> configurations = _parseConfigurations(); |
| 3396 Token deferredToken = null; |
| 3397 Token asToken = null; |
| 3398 SimpleIdentifier prefix = null; |
| 3399 if (_matchesKeyword(Keyword.DEFERRED)) { |
| 3400 deferredToken = getAndAdvance(); |
| 3401 } |
| 3402 if (_matchesKeyword(Keyword.AS)) { |
| 3403 asToken = getAndAdvance(); |
| 3404 prefix = parseSimpleIdentifier(isDeclaration: true); |
| 3405 } else if (deferredToken != null) { |
| 3406 _reportErrorForCurrentToken( |
| 3407 ParserErrorCode.MISSING_PREFIX_IN_DEFERRED_IMPORT); |
| 3408 } else if (!_matches(TokenType.SEMICOLON) && |
| 3409 !_matchesString(_SHOW) && |
| 3410 !_matchesString(_HIDE)) { |
| 3411 Token nextToken = _peek(); |
| 3412 if (_tokenMatchesKeyword(nextToken, Keyword.AS) || |
| 3413 _tokenMatchesString(nextToken, _SHOW) || |
| 3414 _tokenMatchesString(nextToken, _HIDE)) { |
| 3415 _reportErrorForCurrentToken( |
| 3416 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken]); |
| 3417 _advance(); |
| 3418 if (_matchesKeyword(Keyword.AS)) { |
| 3419 asToken = getAndAdvance(); |
| 3420 prefix = parseSimpleIdentifier(isDeclaration: true); |
| 3421 } |
| 3422 } |
| 3423 } |
| 3424 List<Combinator> combinators = parseCombinators(); |
| 3425 Token semicolon = _expect(TokenType.SEMICOLON); |
| 3426 return new ImportDirective( |
| 3427 commentAndMetadata.comment, |
| 3428 commentAndMetadata.metadata, |
| 3429 importKeyword, |
| 3430 libraryUri, |
| 3431 configurations, |
| 3432 deferredToken, |
| 3433 asToken, |
| 3434 prefix, |
| 3435 combinators, |
| 3436 semicolon); |
| 3437 } |
| 3438 |
| 3439 /** |
| 3440 * Parse a list of initialized identifiers. The [commentAndMetadata] is the |
| 3441 * documentation comment and metadata to be associated with the declaration. |
| 3442 * The [staticKeyword] is the static keyword, or `null` if the getter is not |
| 3443 * static. The [keyword] is the token representing the 'final', 'const' or |
| 3444 * 'var' keyword, or `null` if there is no keyword. The [type] is the type |
| 3445 * that has already been parsed, or `null` if 'var' was provided. Return the |
| 3446 * getter that was parsed. |
| 3447 * |
| 3448 * ?? ::= |
| 3449 * 'static'? ('var' | type) initializedIdentifierList ';' |
| 3450 * | 'final' type? initializedIdentifierList ';' |
| 3451 * |
| 3452 * initializedIdentifierList ::= |
| 3453 * initializedIdentifier (',' initializedIdentifier)* |
| 3454 * |
| 3455 * initializedIdentifier ::= |
| 3456 * identifier ('=' expression)? |
| 3457 */ |
| 3458 FieldDeclaration parseInitializedIdentifierList( |
| 3459 CommentAndMetadata commentAndMetadata, |
| 3460 Token staticKeyword, |
| 3461 Token keyword, |
| 3462 TypeName type) { |
| 3463 VariableDeclarationList fieldList = |
| 3464 parseVariableDeclarationListAfterType(null, keyword, type); |
| 3465 return new FieldDeclaration( |
| 3466 commentAndMetadata.comment, |
| 3467 commentAndMetadata.metadata, |
| 3468 staticKeyword, |
| 3469 fieldList, |
| 3470 _expect(TokenType.SEMICOLON)); |
| 3471 } |
| 3472 |
| 3473 /** |
| 3474 * Parse an instance creation expression. The [keyword] is the 'new' or |
| 3475 * 'const' keyword that introduces the expression. Return the instance |
| 3476 * creation expression that was parsed. |
| 3477 * |
| 3478 * instanceCreationExpression ::= |
| 3479 * ('new' | 'const') type ('.' identifier)? argumentList |
| 3480 */ |
| 3481 InstanceCreationExpression parseInstanceCreationExpression(Token keyword) { |
| 3482 ConstructorName constructorName = parseConstructorName(); |
| 3483 ArgumentList argumentList = _parseArgumentListChecked(); |
| 3484 return new InstanceCreationExpression( |
| 3485 keyword, constructorName, argumentList); |
| 3486 } |
| 3487 |
| 3488 /** |
3379 * Parse a label. Return the label that was parsed. | 3489 * Parse a label. Return the label that was parsed. |
3380 * | 3490 * |
3381 * This method assumes that the current token matches an identifier and that | 3491 * This method assumes that the current token matches an identifier and that |
3382 * the following token matches `TokenType.COLON`. | 3492 * the following token matches `TokenType.COLON`. |
3383 * | 3493 * |
3384 * label ::= | 3494 * label ::= |
3385 * identifier ':' | 3495 * identifier ':' |
3386 */ | 3496 */ |
3387 Label parseLabel({bool isDeclaration: false}) { | 3497 Label parseLabel({bool isDeclaration: false}) { |
3388 SimpleIdentifier label = | 3498 SimpleIdentifier label = |
3389 _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration); | 3499 _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration); |
3390 Token colon = getAndAdvance(); | 3500 Token colon = getAndAdvance(); |
3391 return new Label(label, colon); | 3501 return new Label(label, colon); |
3392 } | 3502 } |
3393 | 3503 |
3394 /** | 3504 /** |
| 3505 * Parse a library directive. The [commentAndMetadata] is the metadata to be |
| 3506 * associated with the directive. Return the library directive that was |
| 3507 * parsed. |
| 3508 * |
| 3509 * This method assumes that the current token matches `Keyword.LIBRARY`. |
| 3510 * |
| 3511 * libraryDirective ::= |
| 3512 * metadata 'library' identifier ';' |
| 3513 */ |
| 3514 LibraryDirective parseLibraryDirective( |
| 3515 CommentAndMetadata commentAndMetadata) { |
| 3516 Token keyword = getAndAdvance(); |
| 3517 LibraryIdentifier libraryName = _parseLibraryName( |
| 3518 ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword); |
| 3519 Token semicolon = _expect(TokenType.SEMICOLON); |
| 3520 return new LibraryDirective(commentAndMetadata.comment, |
| 3521 commentAndMetadata.metadata, keyword, libraryName, semicolon); |
| 3522 } |
| 3523 |
| 3524 /** |
3395 * Parse a library identifier. Return the library identifier that was parsed. | 3525 * Parse a library identifier. Return the library identifier that was parsed. |
3396 * | 3526 * |
3397 * libraryIdentifier ::= | 3527 * libraryIdentifier ::= |
3398 * identifier ('.' identifier)* | 3528 * identifier ('.' identifier)* |
3399 */ | 3529 */ |
3400 LibraryIdentifier parseLibraryIdentifier() { | 3530 LibraryIdentifier parseLibraryIdentifier() { |
3401 List<SimpleIdentifier> components = <SimpleIdentifier>[]; | 3531 List<SimpleIdentifier> components = <SimpleIdentifier>[]; |
3402 components.add(parseSimpleIdentifier()); | 3532 components.add(parseSimpleIdentifier()); |
3403 while (_optional(TokenType.PERIOD)) { | 3533 while (_optional(TokenType.PERIOD)) { |
3404 components.add(parseSimpleIdentifier()); | 3534 components.add(parseSimpleIdentifier()); |
3405 } | 3535 } |
3406 return new LibraryIdentifier(components); | 3536 return new LibraryIdentifier(components); |
3407 } | 3537 } |
3408 | 3538 |
3409 /** | 3539 /** |
| 3540 * Parse a list literal. The [modifier] is the 'const' modifier appearing |
| 3541 * before the literal, or `null` if there is no modifier. The [typeArguments] |
| 3542 * is the type arguments appearing before the literal, or `null` if there are |
| 3543 * no type arguments. Return the list literal that was parsed. |
| 3544 * |
| 3545 * This method assumes that the current token matches either |
| 3546 * `TokenType.OPEN_SQUARE_BRACKET` or `TokenType.INDEX`. |
| 3547 * |
| 3548 * listLiteral ::= |
| 3549 * 'const'? typeArguments? '[' (expressionList ','?)? ']' |
| 3550 */ |
| 3551 ListLiteral parseListLiteral(Token modifier, TypeArgumentList typeArguments) { |
| 3552 if (_matches(TokenType.INDEX)) { |
| 3553 // Split the token into two separate tokens. |
| 3554 BeginToken leftBracket = _createToken( |
| 3555 _currentToken, TokenType.OPEN_SQUARE_BRACKET, |
| 3556 isBegin: true); |
| 3557 Token rightBracket = |
| 3558 new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1); |
| 3559 leftBracket.endToken = rightBracket; |
| 3560 rightBracket.setNext(_currentToken.next); |
| 3561 leftBracket.setNext(rightBracket); |
| 3562 _currentToken.previous.setNext(leftBracket); |
| 3563 _currentToken = _currentToken.next; |
| 3564 return new ListLiteral( |
| 3565 modifier, typeArguments, leftBracket, null, rightBracket); |
| 3566 } |
| 3567 Token leftBracket = getAndAdvance(); |
| 3568 if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) { |
| 3569 return new ListLiteral( |
| 3570 modifier, typeArguments, leftBracket, null, getAndAdvance()); |
| 3571 } |
| 3572 bool wasInInitializer = _inInitializer; |
| 3573 _inInitializer = false; |
| 3574 try { |
| 3575 List<Expression> elements = <Expression>[parseExpression2()]; |
| 3576 while (_optional(TokenType.COMMA)) { |
| 3577 if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) { |
| 3578 return new ListLiteral( |
| 3579 modifier, typeArguments, leftBracket, elements, getAndAdvance()); |
| 3580 } |
| 3581 elements.add(parseExpression2()); |
| 3582 } |
| 3583 Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET); |
| 3584 return new ListLiteral( |
| 3585 modifier, typeArguments, leftBracket, elements, rightBracket); |
| 3586 } finally { |
| 3587 _inInitializer = wasInInitializer; |
| 3588 } |
| 3589 } |
| 3590 |
| 3591 /** |
3410 * Parse a list or map literal. The [modifier] is the 'const' modifier | 3592 * Parse a list or map literal. The [modifier] is the 'const' modifier |
3411 * appearing before the literal, or `null` if there is no modifier. Return the | 3593 * appearing before the literal, or `null` if there is no modifier. Return the |
3412 * list or map literal that was parsed. | 3594 * list or map literal that was parsed. |
3413 * | 3595 * |
3414 * listOrMapLiteral ::= | 3596 * listOrMapLiteral ::= |
3415 * listLiteral | 3597 * listLiteral |
3416 * | mapLiteral | 3598 * | mapLiteral |
3417 */ | 3599 */ |
3418 TypedLiteral parseListOrMapLiteral(Token modifier) { | 3600 TypedLiteral parseListOrMapLiteral(Token modifier) { |
3419 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); | 3601 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); |
3420 if (_matches(TokenType.OPEN_CURLY_BRACKET)) { | 3602 if (_matches(TokenType.OPEN_CURLY_BRACKET)) { |
3421 return _parseMapLiteral(modifier, typeArguments); | 3603 return parseMapLiteral(modifier, typeArguments); |
3422 } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) || | 3604 } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) || |
3423 _matches(TokenType.INDEX)) { | 3605 _matches(TokenType.INDEX)) { |
3424 return _parseListLiteral(modifier, typeArguments); | 3606 return parseListLiteral(modifier, typeArguments); |
3425 } | 3607 } |
3426 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL); | 3608 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL); |
3427 return new ListLiteral( | 3609 return new ListLiteral( |
3428 modifier, | 3610 modifier, |
3429 typeArguments, | 3611 typeArguments, |
3430 _createSyntheticToken(TokenType.OPEN_SQUARE_BRACKET), | 3612 _createSyntheticToken(TokenType.OPEN_SQUARE_BRACKET), |
3431 null, | 3613 null, |
3432 _createSyntheticToken(TokenType.CLOSE_SQUARE_BRACKET)); | 3614 _createSyntheticToken(TokenType.CLOSE_SQUARE_BRACKET)); |
3433 } | 3615 } |
3434 | 3616 |
3435 /** | 3617 /** |
| 3618 * Parse a logical and expression. Return the logical and expression that was |
| 3619 * parsed. |
| 3620 * |
| 3621 * logicalAndExpression ::= |
| 3622 * equalityExpression ('&&' equalityExpression)* |
| 3623 */ |
| 3624 Expression parseLogicalAndExpression() { |
| 3625 Expression expression = parseEqualityExpression(); |
| 3626 while (_currentToken.type == TokenType.AMPERSAND_AMPERSAND) { |
| 3627 expression = new BinaryExpression( |
| 3628 expression, getAndAdvance(), parseEqualityExpression()); |
| 3629 } |
| 3630 return expression; |
| 3631 } |
| 3632 |
| 3633 /** |
3436 * Parse a logical or expression. Return the logical or expression that was | 3634 * Parse a logical or expression. Return the logical or expression that was |
3437 * parsed. | 3635 * parsed. |
3438 * | 3636 * |
3439 * logicalOrExpression ::= | 3637 * logicalOrExpression ::= |
3440 * logicalAndExpression ('||' logicalAndExpression)* | 3638 * logicalAndExpression ('||' logicalAndExpression)* |
3441 */ | 3639 */ |
3442 Expression parseLogicalOrExpression() { | 3640 Expression parseLogicalOrExpression() { |
3443 Expression expression = _parseLogicalAndExpression(); | 3641 Expression expression = parseLogicalAndExpression(); |
3444 while (_currentToken.type == TokenType.BAR_BAR) { | 3642 while (_currentToken.type == TokenType.BAR_BAR) { |
3445 expression = new BinaryExpression( | 3643 expression = new BinaryExpression( |
3446 expression, getAndAdvance(), _parseLogicalAndExpression()); | 3644 expression, getAndAdvance(), parseLogicalAndExpression()); |
3447 } | 3645 } |
3448 return expression; | 3646 return expression; |
3449 } | 3647 } |
3450 | 3648 |
3451 /** | 3649 /** |
| 3650 * Parse a map literal. The [modifier] is the 'const' modifier appearing |
| 3651 * before the literal, or `null` if there is no modifier. The [typeArguments] |
| 3652 * is the type arguments that were declared, or `null` if there are no type |
| 3653 * arguments. Return the map literal that was parsed. |
| 3654 * |
| 3655 * This method assumes that the current token matches |
| 3656 * `TokenType.OPEN_CURLY_BRACKET`. |
| 3657 * |
| 3658 * mapLiteral ::= |
| 3659 * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)*
','?)? '}' |
| 3660 */ |
| 3661 MapLiteral parseMapLiteral(Token modifier, TypeArgumentList typeArguments) { |
| 3662 Token leftBracket = getAndAdvance(); |
| 3663 if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { |
| 3664 return new MapLiteral( |
| 3665 modifier, typeArguments, leftBracket, null, getAndAdvance()); |
| 3666 } |
| 3667 bool wasInInitializer = _inInitializer; |
| 3668 _inInitializer = false; |
| 3669 try { |
| 3670 List<MapLiteralEntry> entries = <MapLiteralEntry>[parseMapLiteralEntry()]; |
| 3671 while (_optional(TokenType.COMMA)) { |
| 3672 if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { |
| 3673 return new MapLiteral( |
| 3674 modifier, typeArguments, leftBracket, entries, getAndAdvance()); |
| 3675 } |
| 3676 entries.add(parseMapLiteralEntry()); |
| 3677 } |
| 3678 Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); |
| 3679 return new MapLiteral( |
| 3680 modifier, typeArguments, leftBracket, entries, rightBracket); |
| 3681 } finally { |
| 3682 _inInitializer = wasInInitializer; |
| 3683 } |
| 3684 } |
| 3685 |
| 3686 /** |
3452 * Parse a map literal entry. Return the map literal entry that was parsed. | 3687 * Parse a map literal entry. Return the map literal entry that was parsed. |
3453 * | 3688 * |
3454 * mapLiteralEntry ::= | 3689 * mapLiteralEntry ::= |
3455 * expression ':' expression | 3690 * expression ':' expression |
3456 */ | 3691 */ |
3457 MapLiteralEntry parseMapLiteralEntry() { | 3692 MapLiteralEntry parseMapLiteralEntry() { |
3458 Expression key = parseExpression2(); | 3693 Expression key = parseExpression2(); |
3459 Token separator = _expect(TokenType.COLON); | 3694 Token separator = _expect(TokenType.COLON); |
3460 Expression value = parseExpression2(); | 3695 Expression value = parseExpression2(); |
3461 return new MapLiteralEntry(key, separator, value); | 3696 return new MapLiteralEntry(key, separator, value); |
3462 } | 3697 } |
3463 | 3698 |
3464 /** | 3699 /** |
3465 * Parse a multiplicative expression. Return the multiplicative expression | |
3466 * that was parsed. | |
3467 * | |
3468 * multiplicativeExpression ::= | |
3469 * unaryExpression (multiplicativeOperator unaryExpression)* | |
3470 * | 'super' (multiplicativeOperator unaryExpression)+ | |
3471 */ | |
3472 Expression parseMultiplicativeExpression() { | |
3473 Expression expression; | |
3474 if (_currentToken.keyword == Keyword.SUPER && | |
3475 _currentToken.next.type.isMultiplicativeOperator) { | |
3476 expression = new SuperExpression(getAndAdvance()); | |
3477 } else { | |
3478 expression = parseUnaryExpression(); | |
3479 } | |
3480 while (_currentToken.type.isMultiplicativeOperator) { | |
3481 expression = new BinaryExpression( | |
3482 expression, getAndAdvance(), parseUnaryExpression()); | |
3483 } | |
3484 return expression; | |
3485 } | |
3486 | |
3487 /** | |
3488 * Parse a normal formal parameter. Return the normal formal parameter that | |
3489 * was parsed. | |
3490 * | |
3491 * normalFormalParameter ::= | |
3492 * functionSignature | |
3493 * | fieldFormalParameter | |
3494 * | simpleFormalParameter | |
3495 * | |
3496 * functionSignature: | |
3497 * metadata returnType? identifier typeParameters? formalParameterList | |
3498 * | |
3499 * fieldFormalParameter ::= | |
3500 * metadata finalConstVarOrType? 'this' '.' identifier | |
3501 * | |
3502 * simpleFormalParameter ::= | |
3503 * declaredIdentifier | |
3504 * | metadata identifier | |
3505 */ | |
3506 NormalFormalParameter parseNormalFormalParameter() { | |
3507 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); | |
3508 FinalConstVarOrType holder = parseFinalConstVarOrType(true); | |
3509 Token thisKeyword = null; | |
3510 Token period = null; | |
3511 if (_matchesKeyword(Keyword.THIS)) { | |
3512 thisKeyword = getAndAdvance(); | |
3513 period = _expect(TokenType.PERIOD); | |
3514 } | |
3515 SimpleIdentifier identifier = parseSimpleIdentifier(); | |
3516 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); | |
3517 if (_matches(TokenType.OPEN_PAREN)) { | |
3518 FormalParameterList parameters = _parseFormalParameterListUnchecked(); | |
3519 if (thisKeyword == null) { | |
3520 if (holder.keyword != null) { | |
3521 _reportErrorForToken( | |
3522 ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword); | |
3523 } | |
3524 Token question = null; | |
3525 if (enableNnbd && _matches(TokenType.QUESTION)) { | |
3526 question = getAndAdvance(); | |
3527 } | |
3528 return new FunctionTypedFormalParameter( | |
3529 commentAndMetadata.comment, | |
3530 commentAndMetadata.metadata, | |
3531 holder.type, | |
3532 new SimpleIdentifier(identifier.token, isDeclaration: true), | |
3533 typeParameters, | |
3534 parameters, | |
3535 question: question); | |
3536 } else { | |
3537 return new FieldFormalParameter( | |
3538 commentAndMetadata.comment, | |
3539 commentAndMetadata.metadata, | |
3540 holder.keyword, | |
3541 holder.type, | |
3542 thisKeyword, | |
3543 period, | |
3544 identifier, | |
3545 typeParameters, | |
3546 parameters); | |
3547 } | |
3548 } else if (typeParameters != null) { | |
3549 // TODO(brianwilkerson) Report an error. It looks like a function-typed | |
3550 // parameter with no parameter list. | |
3551 //_reportErrorForToken(ParserErrorCode.MISSING_PARAMETERS, typeParameters.
endToken); | |
3552 } | |
3553 TypeName type = holder.type; | |
3554 if (type != null) { | |
3555 if (_tokenMatchesKeyword(type.name.beginToken, Keyword.VOID)) { | |
3556 _reportErrorForToken( | |
3557 ParserErrorCode.VOID_PARAMETER, type.name.beginToken); | |
3558 } else if (holder.keyword != null && | |
3559 _tokenMatchesKeyword(holder.keyword, Keyword.VAR)) { | |
3560 _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, holder.keyword); | |
3561 } | |
3562 } | |
3563 if (thisKeyword != null) { | |
3564 // TODO(brianwilkerson) If there are type parameters but no parameters, | |
3565 // should we create a synthetic empty parameter list here so we can | |
3566 // capture the type parameters? | |
3567 return new FieldFormalParameter( | |
3568 commentAndMetadata.comment, | |
3569 commentAndMetadata.metadata, | |
3570 holder.keyword, | |
3571 holder.type, | |
3572 thisKeyword, | |
3573 period, | |
3574 identifier, | |
3575 null, | |
3576 null); | |
3577 } | |
3578 return new SimpleFormalParameter( | |
3579 commentAndMetadata.comment, | |
3580 commentAndMetadata.metadata, | |
3581 holder.keyword, | |
3582 holder.type, | |
3583 new SimpleIdentifier(identifier.token, isDeclaration: true)); | |
3584 } | |
3585 | |
3586 /** | |
3587 * Parse an operator declaration. The [commentAndMetadata] is the | |
3588 * documentation comment and metadata to be associated with the declaration. | |
3589 * The [externalKeyword] is the 'external' token. The [returnType] is the | |
3590 * return type that has already been parsed, or `null` if there was no return | |
3591 * type. Return the operator declaration that was parsed. | |
3592 * | |
3593 * operatorDeclaration ::= | |
3594 * operatorSignature (';' | functionBody) | |
3595 * | |
3596 * operatorSignature ::= | |
3597 * 'external'? returnType? 'operator' operator formalParameterList | |
3598 */ | |
3599 MethodDeclaration parseOperator(CommentAndMetadata commentAndMetadata, | |
3600 Token externalKeyword, TypeName returnType) { | |
3601 Token operatorKeyword; | |
3602 if (_matchesKeyword(Keyword.OPERATOR)) { | |
3603 operatorKeyword = getAndAdvance(); | |
3604 } else { | |
3605 _reportErrorForToken( | |
3606 ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken); | |
3607 operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR); | |
3608 } | |
3609 return _parseOperatorAfterKeyword( | |
3610 commentAndMetadata, externalKeyword, returnType, operatorKeyword); | |
3611 } | |
3612 | |
3613 /** | |
3614 * Parse a prefixed identifier. Return the prefixed identifier that was | |
3615 * parsed. | |
3616 * | |
3617 * prefixedIdentifier ::= | |
3618 * identifier ('.' identifier)? | |
3619 */ | |
3620 Identifier parsePrefixedIdentifier() { | |
3621 return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier()); | |
3622 } | |
3623 | |
3624 /** | |
3625 * Parse a primary expression. Return the primary expression that was parsed. | |
3626 * | |
3627 * primary ::= | |
3628 * thisExpression | |
3629 * | 'super' unconditionalAssignableSelector | |
3630 * | functionExpression | |
3631 * | literal | |
3632 * | identifier | |
3633 * | newExpression | |
3634 * | constObjectExpression | |
3635 * | '(' expression ')' | |
3636 * | argumentDefinitionTest | |
3637 * | |
3638 * literal ::= | |
3639 * nullLiteral | |
3640 * | booleanLiteral | |
3641 * | numericLiteral | |
3642 * | stringLiteral | |
3643 * | symbolLiteral | |
3644 * | mapLiteral | |
3645 * | listLiteral | |
3646 */ | |
3647 Expression parsePrimaryExpression() { | |
3648 if (_matchesIdentifier()) { | |
3649 // TODO(brianwilkerson) The code below was an attempt to recover from an | |
3650 // error case, but it needs to be applied as a recovery only after we | |
3651 // know that parsing it as an identifier doesn't work. Leaving the code as | |
3652 // a reminder of how to recover. | |
3653 // if (isFunctionExpression(_peek())) { | |
3654 // // | |
3655 // // Function expressions were allowed to have names at one point, but t
his is now illegal. | |
3656 // // | |
3657 // reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance()
); | |
3658 // return parseFunctionExpression(); | |
3659 // } | |
3660 return _parsePrefixedIdentifierUnchecked(); | |
3661 } | |
3662 TokenType type = _currentToken.type; | |
3663 if (type == TokenType.STRING) { | |
3664 return parseStringLiteral(); | |
3665 } else if (type == TokenType.INT) { | |
3666 Token token = getAndAdvance(); | |
3667 int value = null; | |
3668 try { | |
3669 value = int.parse(token.lexeme); | |
3670 } on FormatException { | |
3671 // The invalid format should have been reported by the scanner. | |
3672 } | |
3673 return new IntegerLiteral(token, value); | |
3674 } | |
3675 Keyword keyword = _currentToken.keyword; | |
3676 if (keyword == Keyword.NULL) { | |
3677 return new NullLiteral(getAndAdvance()); | |
3678 } else if (keyword == Keyword.NEW) { | |
3679 return _parseNewExpression(); | |
3680 } else if (keyword == Keyword.THIS) { | |
3681 return new ThisExpression(getAndAdvance()); | |
3682 } else if (keyword == Keyword.SUPER) { | |
3683 // TODO(paulberry): verify with Gilad that "super" must be followed by | |
3684 // unconditionalAssignableSelector in this case. | |
3685 return _parseAssignableSelector( | |
3686 new SuperExpression(getAndAdvance()), false, | |
3687 allowConditional: false); | |
3688 } else if (keyword == Keyword.FALSE) { | |
3689 return new BooleanLiteral(getAndAdvance(), false); | |
3690 } else if (keyword == Keyword.TRUE) { | |
3691 return new BooleanLiteral(getAndAdvance(), true); | |
3692 } | |
3693 if (type == TokenType.DOUBLE) { | |
3694 Token token = getAndAdvance(); | |
3695 double value = 0.0; | |
3696 try { | |
3697 value = double.parse(token.lexeme); | |
3698 } on FormatException { | |
3699 // The invalid format should have been reported by the scanner. | |
3700 } | |
3701 return new DoubleLiteral(token, value); | |
3702 } else if (type == TokenType.HEXADECIMAL) { | |
3703 Token token = getAndAdvance(); | |
3704 int value = null; | |
3705 try { | |
3706 value = int.parse(token.lexeme.substring(2), radix: 16); | |
3707 } on FormatException { | |
3708 // The invalid format should have been reported by the scanner. | |
3709 } | |
3710 return new IntegerLiteral(token, value); | |
3711 } else if (keyword == Keyword.CONST) { | |
3712 return parseConstExpression(); | |
3713 } else if (type == TokenType.OPEN_PAREN) { | |
3714 if (isFunctionExpression(_currentToken)) { | |
3715 return parseFunctionExpression(); | |
3716 } | |
3717 Token leftParenthesis = getAndAdvance(); | |
3718 bool wasInInitializer = _inInitializer; | |
3719 _inInitializer = false; | |
3720 try { | |
3721 Expression expression = parseExpression2(); | |
3722 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); | |
3723 return new ParenthesizedExpression( | |
3724 leftParenthesis, expression, rightParenthesis); | |
3725 } finally { | |
3726 _inInitializer = wasInInitializer; | |
3727 } | |
3728 } else if (type == TokenType.LT || _injectGenericCommentTypeList()) { | |
3729 return parseListOrMapLiteral(null); | |
3730 } else if (type == TokenType.OPEN_CURLY_BRACKET) { | |
3731 return _parseMapLiteral(null, null); | |
3732 } else if (type == TokenType.OPEN_SQUARE_BRACKET || | |
3733 type == TokenType.INDEX) { | |
3734 return _parseListLiteral(null, null); | |
3735 } else if (type == TokenType.QUESTION && | |
3736 _tokenMatches(_peek(), TokenType.IDENTIFIER)) { | |
3737 _reportErrorForCurrentToken( | |
3738 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); | |
3739 _advance(); | |
3740 return parsePrimaryExpression(); | |
3741 } else if (keyword == Keyword.VOID) { | |
3742 // | |
3743 // Recover from having a return type of "void" where a return type is not | |
3744 // expected. | |
3745 // | |
3746 // TODO(brianwilkerson) Improve this error message. | |
3747 _reportErrorForCurrentToken( | |
3748 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); | |
3749 _advance(); | |
3750 return parsePrimaryExpression(); | |
3751 } else if (type == TokenType.HASH) { | |
3752 return parseSymbolLiteral(); | |
3753 } else { | |
3754 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); | |
3755 return createSyntheticIdentifier(); | |
3756 } | |
3757 } | |
3758 | |
3759 /** | |
3760 * Parse a relational expression. Return the relational expression that was | |
3761 * parsed. | |
3762 * | |
3763 * relationalExpression ::= | |
3764 * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperato
r bitwiseOrExpression)? | |
3765 * | 'super' relationalOperator bitwiseOrExpression | |
3766 */ | |
3767 Expression parseRelationalExpression() { | |
3768 if (_currentToken.keyword == Keyword.SUPER && | |
3769 _currentToken.next.type.isRelationalOperator) { | |
3770 Expression expression = new SuperExpression(getAndAdvance()); | |
3771 Token operator = getAndAdvance(); | |
3772 return new BinaryExpression( | |
3773 expression, operator, parseBitwiseOrExpression()); | |
3774 } | |
3775 Expression expression = parseBitwiseOrExpression(); | |
3776 Keyword keyword = _currentToken.keyword; | |
3777 if (keyword == Keyword.AS) { | |
3778 Token asOperator = getAndAdvance(); | |
3779 return new AsExpression(expression, asOperator, parseTypeName(true)); | |
3780 } else if (keyword == Keyword.IS) { | |
3781 Token isOperator = getAndAdvance(); | |
3782 Token notOperator = null; | |
3783 if (_matches(TokenType.BANG)) { | |
3784 notOperator = getAndAdvance(); | |
3785 } | |
3786 return new IsExpression( | |
3787 expression, isOperator, notOperator, parseTypeName(true)); | |
3788 } else if (_currentToken.type.isRelationalOperator) { | |
3789 Token operator = getAndAdvance(); | |
3790 return new BinaryExpression( | |
3791 expression, operator, parseBitwiseOrExpression()); | |
3792 } | |
3793 return expression; | |
3794 } | |
3795 | |
3796 /** | |
3797 * Parse a rethrow expression. Return the rethrow expression that was parsed. | |
3798 * | |
3799 * This method assumes that the current token matches `Keyword.RETHROW`. | |
3800 * | |
3801 * rethrowExpression ::= | |
3802 * 'rethrow' | |
3803 */ | |
3804 Expression parseRethrowExpression() => new RethrowExpression(getAndAdvance()); | |
3805 | |
3806 /** | |
3807 * Parse a return statement. Return the return statement that was parsed. | |
3808 * | |
3809 * This method assumes that the current token matches `Keyword.RETURN`. | |
3810 * | |
3811 * returnStatement ::= | |
3812 * 'return' expression? ';' | |
3813 */ | |
3814 Statement parseReturnStatement() { | |
3815 Token returnKeyword = getAndAdvance(); | |
3816 if (_matches(TokenType.SEMICOLON)) { | |
3817 return new ReturnStatement(returnKeyword, null, getAndAdvance()); | |
3818 } | |
3819 Expression expression = parseExpression2(); | |
3820 Token semicolon = _expect(TokenType.SEMICOLON); | |
3821 return new ReturnStatement(returnKeyword, expression, semicolon); | |
3822 } | |
3823 | |
3824 /** | |
3825 * Parse a return type. Return the return type that was parsed. | |
3826 * | |
3827 * returnType ::= | |
3828 * 'void' | |
3829 * | type | |
3830 */ | |
3831 TypeName parseReturnType() { | |
3832 if (_currentToken.keyword == Keyword.VOID) { | |
3833 return new TypeName(new SimpleIdentifier(getAndAdvance()), null); | |
3834 } else { | |
3835 return parseTypeName(false); | |
3836 } | |
3837 } | |
3838 | |
3839 /** | |
3840 * Parse a shift expression. Return the shift expression that was parsed. | |
3841 * | |
3842 * shiftExpression ::= | |
3843 * additiveExpression (shiftOperator additiveExpression)* | |
3844 * | 'super' (shiftOperator additiveExpression)+ | |
3845 */ | |
3846 Expression parseShiftExpression() { | |
3847 Expression expression; | |
3848 if (_currentToken.keyword == Keyword.SUPER && | |
3849 _currentToken.next.type.isShiftOperator) { | |
3850 expression = new SuperExpression(getAndAdvance()); | |
3851 } else { | |
3852 expression = parseAdditiveExpression(); | |
3853 } | |
3854 while (_currentToken.type.isShiftOperator) { | |
3855 expression = new BinaryExpression( | |
3856 expression, getAndAdvance(), parseAdditiveExpression()); | |
3857 } | |
3858 return expression; | |
3859 } | |
3860 | |
3861 /** | |
3862 * Parse a simple identifier. Return the simple identifier that was parsed. | |
3863 * | |
3864 * identifier ::= | |
3865 * IDENTIFIER | |
3866 */ | |
3867 SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) { | |
3868 if (_matchesIdentifier()) { | |
3869 return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration); | |
3870 } | |
3871 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); | |
3872 return createSyntheticIdentifier(isDeclaration: isDeclaration); | |
3873 } | |
3874 | |
3875 /** | |
3876 * Parse a statement, starting with the given [token]. Return the statement | |
3877 * that was parsed, or `null` if the tokens do not represent a recognizable | |
3878 * statement. | |
3879 */ | |
3880 Statement parseStatement(Token token) { | |
3881 _currentToken = token; | |
3882 return parseStatement2(); | |
3883 } | |
3884 | |
3885 /** | |
3886 * Parse a statement. Return the statement that was parsed. | |
3887 * | |
3888 * statement ::= | |
3889 * label* nonLabeledStatement | |
3890 */ | |
3891 Statement parseStatement2() { | |
3892 List<Label> labels = null; | |
3893 while (_matchesIdentifier() && _currentToken.next.type == TokenType.COLON) { | |
3894 Label label = parseLabel(isDeclaration: true); | |
3895 if (labels == null) { | |
3896 labels = <Label>[label]; | |
3897 } else { | |
3898 labels.add(label); | |
3899 } | |
3900 } | |
3901 Statement statement = _parseNonLabeledStatement(); | |
3902 if (labels == null) { | |
3903 return statement; | |
3904 } | |
3905 return new LabeledStatement(labels, statement); | |
3906 } | |
3907 | |
3908 /** | |
3909 * Parse a sequence of statements, starting with the given [token]. Return the | |
3910 * statements that were parsed, or `null` if the tokens do not represent a | |
3911 * recognizable sequence of statements. | |
3912 */ | |
3913 List<Statement> parseStatements(Token token) { | |
3914 _currentToken = token; | |
3915 return _parseStatementList(); | |
3916 } | |
3917 | |
3918 /** | |
3919 * Parse a string literal. Return the string literal that was parsed. | |
3920 * | |
3921 * stringLiteral ::= | |
3922 * MULTI_LINE_STRING+ | |
3923 * | SINGLE_LINE_STRING+ | |
3924 */ | |
3925 StringLiteral parseStringLiteral() { | |
3926 if (_matches(TokenType.STRING)) { | |
3927 return _parseStringLiteralUnchecked(); | |
3928 } | |
3929 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL); | |
3930 return createSyntheticStringLiteral(); | |
3931 } | |
3932 | |
3933 /** | |
3934 * Parse a super constructor invocation. Return the super constructor | |
3935 * invocation that was parsed. | |
3936 * | |
3937 * This method assumes that the current token matches [Keyword.SUPER]. | |
3938 * | |
3939 * superConstructorInvocation ::= | |
3940 * 'super' ('.' identifier)? arguments | |
3941 */ | |
3942 SuperConstructorInvocation parseSuperConstructorInvocation() { | |
3943 Token keyword = getAndAdvance(); | |
3944 Token period = null; | |
3945 SimpleIdentifier constructorName = null; | |
3946 if (_matches(TokenType.PERIOD)) { | |
3947 period = getAndAdvance(); | |
3948 constructorName = parseSimpleIdentifier(); | |
3949 } | |
3950 ArgumentList argumentList = _parseArgumentListChecked(); | |
3951 return new SuperConstructorInvocation( | |
3952 keyword, period, constructorName, argumentList); | |
3953 } | |
3954 | |
3955 /** | |
3956 * Parse a switch statement. Return the switch statement that was parsed. | |
3957 * | |
3958 * switchStatement ::= | |
3959 * 'switch' '(' expression ')' '{' switchCase* defaultCase? '}' | |
3960 * | |
3961 * switchCase ::= | |
3962 * label* ('case' expression ':') statements | |
3963 * | |
3964 * defaultCase ::= | |
3965 * label* 'default' ':' statements | |
3966 */ | |
3967 SwitchStatement parseSwitchStatement() { | |
3968 bool wasInSwitch = _inSwitch; | |
3969 _inSwitch = true; | |
3970 try { | |
3971 HashSet<String> definedLabels = new HashSet<String>(); | |
3972 Token keyword = _expectKeyword(Keyword.SWITCH); | |
3973 Token leftParenthesis = _expect(TokenType.OPEN_PAREN); | |
3974 Expression expression = parseExpression2(); | |
3975 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); | |
3976 Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET); | |
3977 Token defaultKeyword = null; | |
3978 List<SwitchMember> members = <SwitchMember>[]; | |
3979 TokenType type = _currentToken.type; | |
3980 while (type != TokenType.EOF && type != TokenType.CLOSE_CURLY_BRACKET) { | |
3981 List<Label> labels = <Label>[]; | |
3982 while ( | |
3983 _matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) { | |
3984 SimpleIdentifier identifier = | |
3985 _parseSimpleIdentifierUnchecked(isDeclaration: true); | |
3986 String label = identifier.token.lexeme; | |
3987 if (definedLabels.contains(label)) { | |
3988 _reportErrorForToken( | |
3989 ParserErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT, | |
3990 identifier.token, | |
3991 [label]); | |
3992 } else { | |
3993 definedLabels.add(label); | |
3994 } | |
3995 Token colon = getAndAdvance(); | |
3996 labels.add(new Label(identifier, colon)); | |
3997 } | |
3998 Keyword keyword = _currentToken.keyword; | |
3999 if (keyword == Keyword.CASE) { | |
4000 Token caseKeyword = getAndAdvance(); | |
4001 Expression caseExpression = parseExpression2(); | |
4002 Token colon = _expect(TokenType.COLON); | |
4003 members.add(new SwitchCase(labels, caseKeyword, caseExpression, colon, | |
4004 _parseStatementList())); | |
4005 if (defaultKeyword != null) { | |
4006 _reportErrorForToken( | |
4007 ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE, | |
4008 caseKeyword); | |
4009 } | |
4010 } else if (keyword == Keyword.DEFAULT) { | |
4011 if (defaultKeyword != null) { | |
4012 _reportErrorForToken( | |
4013 ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, _peek()); | |
4014 } | |
4015 defaultKeyword = getAndAdvance(); | |
4016 Token colon = _expect(TokenType.COLON); | |
4017 members.add(new SwitchDefault( | |
4018 labels, defaultKeyword, colon, _parseStatementList())); | |
4019 } else { | |
4020 // We need to advance, otherwise we could end up in an infinite loop, | |
4021 // but this could be a lot smarter about recovering from the error. | |
4022 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT); | |
4023 bool atEndOrNextMember() { | |
4024 TokenType type = _currentToken.type; | |
4025 if (type == TokenType.EOF || | |
4026 type == TokenType.CLOSE_CURLY_BRACKET) { | |
4027 return true; | |
4028 } | |
4029 Keyword keyword = _currentToken.keyword; | |
4030 return keyword == Keyword.CASE || keyword == Keyword.DEFAULT; | |
4031 } | |
4032 | |
4033 while (!atEndOrNextMember()) { | |
4034 _advance(); | |
4035 } | |
4036 } | |
4037 type = _currentToken.type; | |
4038 } | |
4039 Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); | |
4040 return new SwitchStatement(keyword, leftParenthesis, expression, | |
4041 rightParenthesis, leftBracket, members, rightBracket); | |
4042 } finally { | |
4043 _inSwitch = wasInSwitch; | |
4044 } | |
4045 } | |
4046 | |
4047 /** | |
4048 * Parse a symbol literal. Return the symbol literal that was parsed. | |
4049 * | |
4050 * This method assumes that the current token matches [TokenType.HASH]. | |
4051 * | |
4052 * symbolLiteral ::= | |
4053 * '#' identifier ('.' identifier)* | |
4054 */ | |
4055 SymbolLiteral parseSymbolLiteral() { | |
4056 Token poundSign = getAndAdvance(); | |
4057 List<Token> components = <Token>[]; | |
4058 if (_matchesIdentifier()) { | |
4059 components.add(getAndAdvance()); | |
4060 while (_optional(TokenType.PERIOD)) { | |
4061 if (_matchesIdentifier()) { | |
4062 components.add(getAndAdvance()); | |
4063 } else { | |
4064 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); | |
4065 components.add(_createSyntheticToken(TokenType.IDENTIFIER)); | |
4066 break; | |
4067 } | |
4068 } | |
4069 } else if (_currentToken.isOperator) { | |
4070 components.add(getAndAdvance()); | |
4071 } else if (_matchesKeyword(Keyword.VOID)) { | |
4072 components.add(getAndAdvance()); | |
4073 } else { | |
4074 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); | |
4075 components.add(_createSyntheticToken(TokenType.IDENTIFIER)); | |
4076 } | |
4077 return new SymbolLiteral(poundSign, components); | |
4078 } | |
4079 | |
4080 /** | |
4081 * Parse a throw expression. Return the throw expression that was parsed. | |
4082 * | |
4083 * This method assumes that the current token matches [Keyword.THROW]. | |
4084 * | |
4085 * throwExpression ::= | |
4086 * 'throw' expression | |
4087 */ | |
4088 Expression parseThrowExpression() { | |
4089 Token keyword = getAndAdvance(); | |
4090 TokenType type = _currentToken.type; | |
4091 if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) { | |
4092 _reportErrorForToken( | |
4093 ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken); | |
4094 return new ThrowExpression(keyword, createSyntheticIdentifier()); | |
4095 } | |
4096 Expression expression = parseExpression2(); | |
4097 return new ThrowExpression(keyword, expression); | |
4098 } | |
4099 | |
4100 /** | |
4101 * Parse a throw expression. Return the throw expression that was parsed. | |
4102 * | |
4103 * This method assumes that the current token matches [Keyword.THROW]. | |
4104 * | |
4105 * throwExpressionWithoutCascade ::= | |
4106 * 'throw' expressionWithoutCascade | |
4107 */ | |
4108 Expression parseThrowExpressionWithoutCascade() { | |
4109 Token keyword = getAndAdvance(); | |
4110 TokenType type = _currentToken.type; | |
4111 if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) { | |
4112 _reportErrorForToken( | |
4113 ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken); | |
4114 return new ThrowExpression(keyword, createSyntheticIdentifier()); | |
4115 } | |
4116 Expression expression = parseExpressionWithoutCascade(); | |
4117 return new ThrowExpression(keyword, expression); | |
4118 } | |
4119 | |
4120 /** | |
4121 * Parse a try statement. Return the try statement that was parsed. | |
4122 * | |
4123 * This method assumes that the current token matches [Keyword.TRY]. | |
4124 * | |
4125 * tryStatement ::= | |
4126 * 'try' block (onPart+ finallyPart? | finallyPart) | |
4127 * | |
4128 * onPart ::= | |
4129 * catchPart block | |
4130 * | 'on' type catchPart? block | |
4131 * | |
4132 * catchPart ::= | |
4133 * 'catch' '(' identifier (',' identifier)? ')' | |
4134 * | |
4135 * finallyPart ::= | |
4136 * 'finally' block | |
4137 */ | |
4138 Statement parseTryStatement() { | |
4139 Token tryKeyword = getAndAdvance(); | |
4140 Block body = _parseBlockChecked(); | |
4141 List<CatchClause> catchClauses = <CatchClause>[]; | |
4142 Block finallyClause = null; | |
4143 while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) { | |
4144 Token onKeyword = null; | |
4145 TypeName exceptionType = null; | |
4146 if (_matchesString(_ON)) { | |
4147 onKeyword = getAndAdvance(); | |
4148 exceptionType = parseTypeName(false); | |
4149 } | |
4150 Token catchKeyword = null; | |
4151 Token leftParenthesis = null; | |
4152 SimpleIdentifier exceptionParameter = null; | |
4153 Token comma = null; | |
4154 SimpleIdentifier stackTraceParameter = null; | |
4155 Token rightParenthesis = null; | |
4156 if (_matchesKeyword(Keyword.CATCH)) { | |
4157 catchKeyword = getAndAdvance(); | |
4158 leftParenthesis = _expect(TokenType.OPEN_PAREN); | |
4159 exceptionParameter = parseSimpleIdentifier(isDeclaration: true); | |
4160 if (_matches(TokenType.COMMA)) { | |
4161 comma = getAndAdvance(); | |
4162 stackTraceParameter = parseSimpleIdentifier(isDeclaration: true); | |
4163 } | |
4164 rightParenthesis = _expect(TokenType.CLOSE_PAREN); | |
4165 } | |
4166 Block catchBody = _parseBlockChecked(); | |
4167 catchClauses.add(new CatchClause( | |
4168 onKeyword, | |
4169 exceptionType, | |
4170 catchKeyword, | |
4171 leftParenthesis, | |
4172 exceptionParameter, | |
4173 comma, | |
4174 stackTraceParameter, | |
4175 rightParenthesis, | |
4176 catchBody)); | |
4177 } | |
4178 Token finallyKeyword = null; | |
4179 if (_matchesKeyword(Keyword.FINALLY)) { | |
4180 finallyKeyword = getAndAdvance(); | |
4181 finallyClause = _parseBlockChecked(); | |
4182 } else if (catchClauses.isEmpty) { | |
4183 _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY); | |
4184 } | |
4185 return new TryStatement( | |
4186 tryKeyword, body, catchClauses, finallyKeyword, finallyClause); | |
4187 } | |
4188 | |
4189 /** | |
4190 * Parse a list of type arguments. Return the type argument list that was | |
4191 * parsed. | |
4192 * | |
4193 * This method assumes that the current token matches `TokenType.LT`. | |
4194 * | |
4195 * typeArguments ::= | |
4196 * '<' typeList '>' | |
4197 * | |
4198 * typeList ::= | |
4199 * type (',' type)* | |
4200 */ | |
4201 TypeArgumentList parseTypeArgumentList() { | |
4202 Token leftBracket = getAndAdvance(); | |
4203 List<TypeName> arguments = <TypeName>[parseTypeName(false)]; | |
4204 while (_optional(TokenType.COMMA)) { | |
4205 arguments.add(parseTypeName(false)); | |
4206 } | |
4207 Token rightBracket = _expectGt(); | |
4208 return new TypeArgumentList(leftBracket, arguments, rightBracket); | |
4209 } | |
4210 | |
4211 /** | |
4212 * Parse a type name. Return the type name that was parsed. | |
4213 * | |
4214 * type ::= | |
4215 * qualified typeArguments? | |
4216 */ | |
4217 TypeName parseTypeName(bool inExpression) { | |
4218 TypeName realType = _parseTypeName(inExpression); | |
4219 // If this is followed by a generic method type comment, allow the comment | |
4220 // type to replace the real type name. | |
4221 // TODO(jmesserly): this feels like a big hammer. Can we restrict it to | |
4222 // only work inside generic methods? | |
4223 TypeName typeFromComment = _parseOptionalTypeNameComment(); | |
4224 return typeFromComment ?? realType; | |
4225 } | |
4226 | |
4227 /** | |
4228 * Parse a type parameter. Return the type parameter that was parsed. | |
4229 * | |
4230 * typeParameter ::= | |
4231 * metadata name ('extends' bound)? | |
4232 */ | |
4233 TypeParameter parseTypeParameter() { | |
4234 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); | |
4235 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); | |
4236 if (_matchesKeyword(Keyword.EXTENDS)) { | |
4237 Token keyword = getAndAdvance(); | |
4238 TypeName bound = parseTypeName(false); | |
4239 return new TypeParameter(commentAndMetadata.comment, | |
4240 commentAndMetadata.metadata, name, keyword, bound); | |
4241 } | |
4242 return new TypeParameter(commentAndMetadata.comment, | |
4243 commentAndMetadata.metadata, name, null, null); | |
4244 } | |
4245 | |
4246 /** | |
4247 * Parse a list of type parameters. Return the list of type parameters that | |
4248 * were parsed. | |
4249 * | |
4250 * This method assumes that the current token matches `TokenType.LT`. | |
4251 * | |
4252 * typeParameterList ::= | |
4253 * '<' typeParameter (',' typeParameter)* '>' | |
4254 */ | |
4255 TypeParameterList parseTypeParameterList() { | |
4256 Token leftBracket = getAndAdvance(); | |
4257 List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()]; | |
4258 while (_optional(TokenType.COMMA)) { | |
4259 typeParameters.add(parseTypeParameter()); | |
4260 } | |
4261 Token rightBracket = _expectGt(); | |
4262 return new TypeParameterList(leftBracket, typeParameters, rightBracket); | |
4263 } | |
4264 | |
4265 /** | |
4266 * Parse a unary expression. Return the unary expression that was parsed. | |
4267 * | |
4268 * unaryExpression ::= | |
4269 * prefixOperator unaryExpression | |
4270 * | awaitExpression | |
4271 * | postfixExpression | |
4272 * | unaryOperator 'super' | |
4273 * | '-' 'super' | |
4274 * | incrementOperator assignableExpression | |
4275 */ | |
4276 Expression parseUnaryExpression() { | |
4277 TokenType type = _currentToken.type; | |
4278 if (type == TokenType.MINUS || | |
4279 type == TokenType.BANG || | |
4280 type == TokenType.TILDE) { | |
4281 Token operator = getAndAdvance(); | |
4282 if (_matchesKeyword(Keyword.SUPER)) { | |
4283 TokenType nextType = _peek().type; | |
4284 if (nextType == TokenType.OPEN_SQUARE_BRACKET || | |
4285 nextType == TokenType.PERIOD) { | |
4286 // "prefixOperator unaryExpression" | |
4287 // --> "prefixOperator postfixExpression" | |
4288 // --> "prefixOperator primary selector*" | |
4289 // --> "prefixOperator 'super' assignableSelector selector*" | |
4290 return new PrefixExpression(operator, parseUnaryExpression()); | |
4291 } | |
4292 return new PrefixExpression( | |
4293 operator, new SuperExpression(getAndAdvance())); | |
4294 } | |
4295 return new PrefixExpression(operator, parseUnaryExpression()); | |
4296 } else if (_currentToken.type.isIncrementOperator) { | |
4297 Token operator = getAndAdvance(); | |
4298 if (_matchesKeyword(Keyword.SUPER)) { | |
4299 TokenType nextType = _peek().type; | |
4300 if (nextType == TokenType.OPEN_SQUARE_BRACKET || | |
4301 nextType == TokenType.PERIOD) { | |
4302 // --> "prefixOperator 'super' assignableSelector selector*" | |
4303 return new PrefixExpression(operator, parseUnaryExpression()); | |
4304 } | |
4305 // | |
4306 // Even though it is not valid to use an incrementing operator | |
4307 // ('++' or '--') before 'super', we can (and therefore must) interpret | |
4308 // "--super" as semantically equivalent to "-(-super)". Unfortunately, | |
4309 // we cannot do the same for "++super" because "+super" is also not | |
4310 // valid. | |
4311 // | |
4312 if (type == TokenType.MINUS_MINUS) { | |
4313 Token firstOperator = _createToken(operator, TokenType.MINUS); | |
4314 Token secondOperator = | |
4315 new Token(TokenType.MINUS, operator.offset + 1); | |
4316 secondOperator.setNext(_currentToken); | |
4317 firstOperator.setNext(secondOperator); | |
4318 operator.previous.setNext(firstOperator); | |
4319 return new PrefixExpression( | |
4320 firstOperator, | |
4321 new PrefixExpression( | |
4322 secondOperator, new SuperExpression(getAndAdvance()))); | |
4323 } | |
4324 // Invalid operator before 'super' | |
4325 _reportErrorForCurrentToken( | |
4326 ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]); | |
4327 return new PrefixExpression( | |
4328 operator, new SuperExpression(getAndAdvance())); | |
4329 } | |
4330 return new PrefixExpression( | |
4331 operator, _parseAssignableExpressionNotStartingWithSuper(false)); | |
4332 } else if (type == TokenType.PLUS) { | |
4333 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); | |
4334 return createSyntheticIdentifier(); | |
4335 } else if (_inAsync && _matchesString(_AWAIT)) { | |
4336 return parseAwaitExpression(); | |
4337 } | |
4338 return _parsePostfixExpression(); | |
4339 } | |
4340 | |
4341 /** | |
4342 * Parse a variable declaration. Return the variable declaration that was | |
4343 * parsed. | |
4344 * | |
4345 * variableDeclaration ::= | |
4346 * identifier ('=' expression)? | |
4347 */ | |
4348 VariableDeclaration parseVariableDeclaration() { | |
4349 // TODO(paulberry): prior to the fix for bug 23204, we permitted | |
4350 // annotations before variable declarations (e.g. "String @deprecated s;"). | |
4351 // Although such constructions are prohibited by the spec, we may want to | |
4352 // consider handling them anyway to allow for better parser recovery in the | |
4353 // event that the user erroneously tries to use them. However, as a | |
4354 // counterargument, this would likely degrade parser recovery in the event | |
4355 // of a construct like "class C { int @deprecated foo() {} }" (i.e. the | |
4356 // user is in the middle of inserting "int bar;" prior to | |
4357 // "@deprecated foo() {}"). | |
4358 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); | |
4359 Token equals = null; | |
4360 Expression initializer = null; | |
4361 if (_matches(TokenType.EQ)) { | |
4362 equals = getAndAdvance(); | |
4363 initializer = parseExpression2(); | |
4364 } | |
4365 return new VariableDeclaration(name, equals, initializer); | |
4366 } | |
4367 | |
4368 /** | |
4369 * Parse a variable declaration list. The [commentAndMetadata] is the metadata | |
4370 * to be associated with the variable declaration list. Return the variable | |
4371 * declaration list that was parsed. | |
4372 * | |
4373 * variableDeclarationList ::= | |
4374 * finalConstVarOrType variableDeclaration (',' variableDeclaration)* | |
4375 */ | |
4376 VariableDeclarationList parseVariableDeclarationListAfterMetadata( | |
4377 CommentAndMetadata commentAndMetadata) { | |
4378 FinalConstVarOrType holder = parseFinalConstVarOrType(false); | |
4379 return parseVariableDeclarationListAfterType( | |
4380 commentAndMetadata, holder.keyword, holder.type); | |
4381 } | |
4382 | |
4383 /** | |
4384 * Parse a variable declaration list. The [commentAndMetadata] is the metadata | |
4385 * to be associated with the variable declaration list, or `null` if there is | |
4386 * no attempt at parsing the comment and metadata. The [keyword] is the token | |
4387 * representing the 'final', 'const' or 'var' keyword, or `null` if there is | |
4388 * no keyword. The [type] is the type of the variables in the list. Return the | |
4389 * variable declaration list that was parsed. | |
4390 * | |
4391 * variableDeclarationList ::= | |
4392 * finalConstVarOrType variableDeclaration (',' variableDeclaration)* | |
4393 */ | |
4394 VariableDeclarationList parseVariableDeclarationListAfterType( | |
4395 CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) { | |
4396 if (type != null && | |
4397 keyword != null && | |
4398 _tokenMatchesKeyword(keyword, Keyword.VAR)) { | |
4399 _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, keyword); | |
4400 } | |
4401 List<VariableDeclaration> variables = <VariableDeclaration>[ | |
4402 parseVariableDeclaration() | |
4403 ]; | |
4404 while (_optional(TokenType.COMMA)) { | |
4405 variables.add(parseVariableDeclaration()); | |
4406 } | |
4407 return new VariableDeclarationList(commentAndMetadata?.comment, | |
4408 commentAndMetadata?.metadata, keyword, type, variables); | |
4409 } | |
4410 | |
4411 /** | |
4412 * Parse a variable declaration statement. The [commentAndMetadata] is the | |
4413 * metadata to be associated with the variable declaration statement, or | |
4414 * `null` if there is no attempt at parsing the comment and metadata. Return | |
4415 * the variable declaration statement that was parsed. | |
4416 * | |
4417 * variableDeclarationStatement ::= | |
4418 * variableDeclarationList ';' | |
4419 */ | |
4420 VariableDeclarationStatement parseVariableDeclarationStatementAfterMetadata( | |
4421 CommentAndMetadata commentAndMetadata) { | |
4422 // Token startToken = currentToken; | |
4423 VariableDeclarationList variableList = | |
4424 parseVariableDeclarationListAfterMetadata(commentAndMetadata); | |
4425 // if (!matches(TokenType.SEMICOLON)) { | |
4426 // if (matches(startToken, Keyword.VAR) && isTypedIdentifier(startToken
.getNext())) { | |
4427 // // TODO(brianwilkerson) This appears to be of the form "var type v
ariable". We should do | |
4428 // // a better job of recovering in this case. | |
4429 // } | |
4430 // } | |
4431 Token semicolon = _expect(TokenType.SEMICOLON); | |
4432 return new VariableDeclarationStatement(variableList, semicolon); | |
4433 } | |
4434 | |
4435 /** | |
4436 * Parse a while statement. Return the while statement that was parsed. | |
4437 * | |
4438 * This method assumes that the current token matches [Keyword.WHILE]. | |
4439 * | |
4440 * whileStatement ::= | |
4441 * 'while' '(' expression ')' statement | |
4442 */ | |
4443 Statement parseWhileStatement() { | |
4444 bool wasInLoop = _inLoop; | |
4445 _inLoop = true; | |
4446 try { | |
4447 Token keyword = getAndAdvance(); | |
4448 Token leftParenthesis = _expect(TokenType.OPEN_PAREN); | |
4449 Expression condition = parseExpression2(); | |
4450 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); | |
4451 Statement body = parseStatement2(); | |
4452 return new WhileStatement( | |
4453 keyword, leftParenthesis, condition, rightParenthesis, body); | |
4454 } finally { | |
4455 _inLoop = wasInLoop; | |
4456 } | |
4457 } | |
4458 | |
4459 /** | |
4460 * Parse a with clause. Return the with clause that was parsed. | |
4461 * | |
4462 * This method assumes that the current token matches `Keyword.WITH`. | |
4463 * | |
4464 * withClause ::= | |
4465 * 'with' typeName (',' typeName)* | |
4466 */ | |
4467 WithClause parseWithClause() { | |
4468 Token withKeyword = getAndAdvance(); | |
4469 List<TypeName> types = <TypeName>[parseTypeName(false)]; | |
4470 while (_optional(TokenType.COMMA)) { | |
4471 types.add(parseTypeName(false)); | |
4472 } | |
4473 return new WithClause(withKeyword, types); | |
4474 } | |
4475 | |
4476 /** | |
4477 * Parse a yield statement. Return the yield statement that was parsed. | |
4478 * | |
4479 * This method assumes that the current token matches [Keyword.YIELD]. | |
4480 * | |
4481 * yieldStatement ::= | |
4482 * 'yield' '*'? expression ';' | |
4483 */ | |
4484 YieldStatement parseYieldStatement() { | |
4485 Token yieldToken = getAndAdvance(); | |
4486 Token star = null; | |
4487 if (_matches(TokenType.STAR)) { | |
4488 star = getAndAdvance(); | |
4489 } | |
4490 Expression expression = parseExpression2(); | |
4491 Token semicolon = _expect(TokenType.SEMICOLON); | |
4492 return new YieldStatement(yieldToken, star, expression, semicolon); | |
4493 } | |
4494 | |
4495 /** | |
4496 * Parse a prefixed identifier, starting at the [startToken], without actually | |
4497 * creating a prefixed identifier or changing the current token. Return the | |
4498 * token following the prefixed identifier that was parsed, or `null` if the | |
4499 * given token is not the first token in a valid prefixed identifier. | |
4500 * | |
4501 * This method must be kept in sync with [parsePrefixedIdentifier]. | |
4502 * | |
4503 * prefixedIdentifier ::= | |
4504 * identifier ('.' identifier)? | |
4505 */ | |
4506 Token skipPrefixedIdentifier(Token startToken) { | |
4507 Token token = skipSimpleIdentifier(startToken); | |
4508 if (token == null) { | |
4509 return null; | |
4510 } else if (!_tokenMatches(token, TokenType.PERIOD)) { | |
4511 return token; | |
4512 } | |
4513 token = token.next; | |
4514 Token nextToken = skipSimpleIdentifier(token); | |
4515 if (nextToken != null) { | |
4516 return nextToken; | |
4517 } else if (_tokenMatches(token, TokenType.CLOSE_PAREN) || | |
4518 _tokenMatches(token, TokenType.COMMA)) { | |
4519 // If the `id.` is followed by something that cannot produce a valid | |
4520 // structure then assume this is a prefixed identifier but missing the | |
4521 // trailing identifier | |
4522 return token; | |
4523 } | |
4524 return null; | |
4525 } | |
4526 | |
4527 /** | |
4528 * Parse a return type, starting at the [startToken], without actually | |
4529 * creating a return type or changing the current token. Return the token | |
4530 * following the return type that was parsed, or `null` if the given token is | |
4531 * not the first token in a valid return type. | |
4532 * | |
4533 * This method must be kept in sync with [parseReturnType]. | |
4534 * | |
4535 * returnType ::= | |
4536 * 'void' | |
4537 * | type | |
4538 */ | |
4539 Token skipReturnType(Token startToken) { | |
4540 if (_tokenMatchesKeyword(startToken, Keyword.VOID)) { | |
4541 return startToken.next; | |
4542 } else { | |
4543 return skipTypeName(startToken); | |
4544 } | |
4545 } | |
4546 | |
4547 /** | |
4548 * Parse a simple identifier, starting at the [startToken], without actually | |
4549 * creating a simple identifier or changing the current token. Return the | |
4550 * token following the simple identifier that was parsed, or `null` if the | |
4551 * given token is not the first token in a valid simple identifier. | |
4552 * | |
4553 * This method must be kept in sync with [parseSimpleIdentifier]. | |
4554 * | |
4555 * identifier ::= | |
4556 * IDENTIFIER | |
4557 */ | |
4558 Token skipSimpleIdentifier(Token startToken) { | |
4559 if (_tokenMatches(startToken, TokenType.IDENTIFIER) || | |
4560 _tokenMatchesPseudoKeyword(startToken)) { | |
4561 return startToken.next; | |
4562 } | |
4563 return null; | |
4564 } | |
4565 | |
4566 /** | |
4567 * Parse a string literal, starting at the [startToken], without actually | |
4568 * creating a string literal or changing the current token. Return the token | |
4569 * following the string literal that was parsed, or `null` if the given token | |
4570 * is not the first token in a valid string literal. | |
4571 * | |
4572 * This method must be kept in sync with [parseStringLiteral]. | |
4573 * | |
4574 * stringLiteral ::= | |
4575 * MULTI_LINE_STRING+ | |
4576 * | SINGLE_LINE_STRING+ | |
4577 */ | |
4578 Token skipStringLiteral(Token startToken) { | |
4579 Token token = startToken; | |
4580 while (token != null && _tokenMatches(token, TokenType.STRING)) { | |
4581 token = token.next; | |
4582 TokenType type = token.type; | |
4583 if (type == TokenType.STRING_INTERPOLATION_EXPRESSION || | |
4584 type == TokenType.STRING_INTERPOLATION_IDENTIFIER) { | |
4585 token = _skipStringInterpolation(token); | |
4586 } | |
4587 } | |
4588 if (identical(token, startToken)) { | |
4589 return null; | |
4590 } | |
4591 return token; | |
4592 } | |
4593 | |
4594 /** | |
4595 * Parse a list of type arguments, starting at the [startToken], without | |
4596 * actually creating a type argument list or changing the current token. | |
4597 * Return the token following the type argument list that was parsed, or | |
4598 * `null` if the given token is not the first token in a valid type argument | |
4599 * list. | |
4600 * | |
4601 * This method must be kept in sync with [parseTypeArgumentList]. | |
4602 * | |
4603 * typeArguments ::= | |
4604 * '<' typeList '>' | |
4605 * | |
4606 * typeList ::= | |
4607 * type (',' type)* | |
4608 */ | |
4609 Token skipTypeArgumentList(Token startToken) { | |
4610 Token token = startToken; | |
4611 if (!_tokenMatches(token, TokenType.LT) && | |
4612 !_injectGenericCommentTypeList()) { | |
4613 return null; | |
4614 } | |
4615 token = skipTypeName(token.next); | |
4616 if (token == null) { | |
4617 // If the start token '<' is followed by '>' | |
4618 // then assume this should be type argument list but is missing a type | |
4619 token = startToken.next; | |
4620 if (_tokenMatches(token, TokenType.GT)) { | |
4621 return token.next; | |
4622 } | |
4623 return null; | |
4624 } | |
4625 while (_tokenMatches(token, TokenType.COMMA)) { | |
4626 token = skipTypeName(token.next); | |
4627 if (token == null) { | |
4628 return null; | |
4629 } | |
4630 } | |
4631 if (token.type == TokenType.GT) { | |
4632 return token.next; | |
4633 } else if (token.type == TokenType.GT_GT) { | |
4634 Token second = new Token(TokenType.GT, token.offset + 1); | |
4635 second.setNextWithoutSettingPrevious(token.next); | |
4636 return second; | |
4637 } | |
4638 return null; | |
4639 } | |
4640 | |
4641 /** | |
4642 * Parse a type name, starting at the [startToken], without actually creating | |
4643 * a type name or changing the current token. Return the token following the | |
4644 * type name that was parsed, or `null` if the given token is not the first | |
4645 * token in a valid type name. | |
4646 * | |
4647 * This method must be kept in sync with [parseTypeName]. | |
4648 * | |
4649 * type ::= | |
4650 * qualified typeArguments? | |
4651 */ | |
4652 Token skipTypeName(Token startToken) { | |
4653 Token token = skipPrefixedIdentifier(startToken); | |
4654 if (token == null) { | |
4655 return null; | |
4656 } | |
4657 if (_tokenMatches(token, TokenType.LT)) { | |
4658 token = skipTypeArgumentList(token); | |
4659 } | |
4660 return token; | |
4661 } | |
4662 | |
4663 /** | |
4664 * Advance to the next token in the token stream. | |
4665 */ | |
4666 void _advance() { | |
4667 _currentToken = _currentToken.next; | |
4668 } | |
4669 | |
4670 /** | |
4671 * Append the character equivalent of the given [scalarValue] to the given | |
4672 * [builder]. Use the [startIndex] and [endIndex] to report an error, and | |
4673 * don't append anything to the builder, if the scalar value is invalid. The | |
4674 * [escapeSequence] is the escape sequence that was parsed to produce the | |
4675 * scalar value (used for error reporting). | |
4676 */ | |
4677 void _appendScalarValue(StringBuffer buffer, String escapeSequence, | |
4678 int scalarValue, int startIndex, int endIndex) { | |
4679 if (scalarValue < 0 || | |
4680 scalarValue > Character.MAX_CODE_POINT || | |
4681 (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) { | |
4682 _reportErrorForCurrentToken( | |
4683 ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]); | |
4684 return; | |
4685 } | |
4686 if (scalarValue < Character.MAX_VALUE) { | |
4687 buffer.writeCharCode(scalarValue); | |
4688 } else { | |
4689 buffer.write(Character.toChars(scalarValue)); | |
4690 } | |
4691 } | |
4692 | |
4693 /** | |
4694 * Clone all token starting from the given [token] up to the end of the token | |
4695 * stream, and return the first token in the new token stream. | |
4696 */ | |
4697 Token _cloneTokens(Token token) { | |
4698 if (token == null) { | |
4699 return null; | |
4700 } | |
4701 token = token is CommentToken ? token.parent : token; | |
4702 Token head = new Token(TokenType.EOF, -1); | |
4703 head.setNext(head); | |
4704 Token current = head; | |
4705 while (token.type != TokenType.EOF) { | |
4706 Token clone = token.copy(); | |
4707 current.setNext(clone); | |
4708 current = clone; | |
4709 token = token.next; | |
4710 } | |
4711 Token tail = new Token(TokenType.EOF, 0); | |
4712 tail.setNext(tail); | |
4713 current.setNext(tail); | |
4714 return head.next; | |
4715 } | |
4716 | |
4717 /** | |
4718 * Convert the given [method] declaration into the nearest valid top-level | |
4719 * function declaration (that is, the function declaration that most closely | |
4720 * captures the components of the given method declaration). | |
4721 */ | |
4722 FunctionDeclaration _convertToFunctionDeclaration(MethodDeclaration method) => | |
4723 new FunctionDeclaration( | |
4724 method.documentationComment, | |
4725 method.metadata, | |
4726 method.externalKeyword, | |
4727 method.returnType, | |
4728 method.propertyKeyword, | |
4729 method.name, | |
4730 new FunctionExpression( | |
4731 method.typeParameters, method.parameters, method.body)); | |
4732 | |
4733 /** | |
4734 * Return `true` if the current token could be the start of a compilation unit | |
4735 * member. This method is used for recovery purposes to decide when to stop | |
4736 * skipping tokens after finding an error while parsing a compilation unit | |
4737 * member. | |
4738 */ | |
4739 bool _couldBeStartOfCompilationUnitMember() { | |
4740 Keyword keyword = _currentToken.keyword; | |
4741 Token next = _currentToken.next; | |
4742 TokenType nextType = next.type; | |
4743 if ((keyword == Keyword.IMPORT || | |
4744 keyword == Keyword.EXPORT || | |
4745 keyword == Keyword.LIBRARY || | |
4746 keyword == Keyword.PART) && | |
4747 nextType != TokenType.PERIOD && | |
4748 nextType != TokenType.LT) { | |
4749 // This looks like the start of a directive | |
4750 return true; | |
4751 } else if (keyword == Keyword.CLASS) { | |
4752 // This looks like the start of a class definition | |
4753 return true; | |
4754 } else if (keyword == Keyword.TYPEDEF && | |
4755 nextType != TokenType.PERIOD && | |
4756 nextType != TokenType.LT) { | |
4757 // This looks like the start of a typedef | |
4758 return true; | |
4759 } else if (keyword == Keyword.VOID || | |
4760 ((keyword == Keyword.GET || keyword == Keyword.SET) && | |
4761 _tokenMatchesIdentifier(next)) || | |
4762 (keyword == Keyword.OPERATOR && _isOperator(next))) { | |
4763 // This looks like the start of a function | |
4764 return true; | |
4765 } else if (_matchesIdentifier()) { | |
4766 if (nextType == TokenType.OPEN_PAREN) { | |
4767 // This looks like the start of a function | |
4768 return true; | |
4769 } | |
4770 Token token = skipReturnType(_currentToken); | |
4771 if (token == null) { | |
4772 return false; | |
4773 } | |
4774 // TODO(brianwilkerson) This looks wrong; should we be checking 'token'? | |
4775 if (keyword == Keyword.GET || | |
4776 keyword == Keyword.SET || | |
4777 (keyword == Keyword.OPERATOR && _isOperator(next)) || | |
4778 _matchesIdentifier()) { | |
4779 return true; | |
4780 } | |
4781 } | |
4782 return false; | |
4783 } | |
4784 | |
4785 /** | |
4786 * Return a synthetic token representing the given [keyword]. | |
4787 */ | |
4788 Token _createSyntheticKeyword(Keyword keyword) => _injectToken( | |
4789 new Parser_SyntheticKeywordToken(keyword, _currentToken.offset)); | |
4790 | |
4791 /** | |
4792 * Return a synthetic token with the given [type]. | |
4793 */ | |
4794 Token _createSyntheticToken(TokenType type) => | |
4795 _injectToken(new StringToken(type, "", _currentToken.offset)); | |
4796 | |
4797 /** | |
4798 * Create and return a new token with the given [type]. The token will replace | |
4799 * the first portion of the given [token], so it will have the same offset and | |
4800 * will have any comments that might have preceeded the token. | |
4801 */ | |
4802 Token _createToken(Token token, TokenType type, {bool isBegin: false}) { | |
4803 CommentToken comments = token.precedingComments; | |
4804 if (comments == null) { | |
4805 if (isBegin) { | |
4806 return new BeginToken(type, token.offset); | |
4807 } | |
4808 return new Token(type, token.offset); | |
4809 } else if (isBegin) { | |
4810 return new BeginTokenWithComment(type, token.offset, comments); | |
4811 } | |
4812 return new TokenWithComment(type, token.offset, comments); | |
4813 } | |
4814 | |
4815 /** | |
4816 * Check that the given [expression] is assignable and report an error if it | |
4817 * isn't. | |
4818 * | |
4819 * assignableExpression ::= | |
4820 * primary (arguments* assignableSelector)+ | |
4821 * | 'super' unconditionalAssignableSelector | |
4822 * | identifier | |
4823 * | |
4824 * unconditionalAssignableSelector ::= | |
4825 * '[' expression ']' | |
4826 * | '.' identifier | |
4827 * | |
4828 * assignableSelector ::= | |
4829 * unconditionalAssignableSelector | |
4830 * | '?.' identifier | |
4831 */ | |
4832 void _ensureAssignable(Expression expression) { | |
4833 if (expression != null && !expression.isAssignable) { | |
4834 _reportErrorForCurrentToken( | |
4835 ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE); | |
4836 } | |
4837 } | |
4838 | |
4839 /** | |
4840 * If the current token has the expected type, return it after advancing to | |
4841 * the next token. Otherwise report an error and return the current token | |
4842 * without advancing. | |
4843 * | |
4844 * Note that the method [_expectGt] should be used if the argument to this | |
4845 * method would be [TokenType.GT]. | |
4846 * | |
4847 * The [type] is the type of token that is expected. | |
4848 */ | |
4849 Token _expect(TokenType type) { | |
4850 if (_matches(type)) { | |
4851 return getAndAdvance(); | |
4852 } | |
4853 // Remove uses of this method in favor of matches? | |
4854 // Pass in the error code to use to report the error? | |
4855 if (type == TokenType.SEMICOLON) { | |
4856 if (_tokenMatches(_currentToken.next, TokenType.SEMICOLON)) { | |
4857 _reportErrorForCurrentToken( | |
4858 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); | |
4859 _advance(); | |
4860 return getAndAdvance(); | |
4861 } | |
4862 _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN, | |
4863 _currentToken.previous, [type.lexeme]); | |
4864 return _createSyntheticToken(TokenType.SEMICOLON); | |
4865 } | |
4866 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]); | |
4867 return _createSyntheticToken(type); | |
4868 } | |
4869 | |
4870 /** | |
4871 * If the current token has the type [TokenType.GT], return it after advancing | |
4872 * to the next token. Otherwise report an error and create a synthetic token. | |
4873 */ | |
4874 Token _expectGt() { | |
4875 if (_matchesGt()) { | |
4876 return getAndAdvance(); | |
4877 } | |
4878 _reportErrorForCurrentToken( | |
4879 ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]); | |
4880 return _createSyntheticToken(TokenType.GT); | |
4881 } | |
4882 | |
4883 /** | |
4884 * If the current token is a keyword matching the given [keyword], return it | |
4885 * after advancing to the next token. Otherwise report an error and return the | |
4886 * current token without advancing. | |
4887 */ | |
4888 Token _expectKeyword(Keyword keyword) { | |
4889 if (_matchesKeyword(keyword)) { | |
4890 return getAndAdvance(); | |
4891 } | |
4892 // Remove uses of this method in favor of matches? | |
4893 // Pass in the error code to use to report the error? | |
4894 _reportErrorForCurrentToken( | |
4895 ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]); | |
4896 return _currentToken; | |
4897 } | |
4898 | |
4899 /** | |
4900 * Search the given list of [ranges] for a range that contains the given | |
4901 * [index]. Return the range that was found, or `null` if none of the ranges | |
4902 * contain the index. | |
4903 */ | |
4904 List<int> _findRange(List<List<int>> ranges, int index) { | |
4905 int rangeCount = ranges.length; | |
4906 for (int i = 0; i < rangeCount; i++) { | |
4907 List<int> range = ranges[i]; | |
4908 if (range[0] <= index && index <= range[1]) { | |
4909 return range; | |
4910 } else if (index < range[0]) { | |
4911 return null; | |
4912 } | |
4913 } | |
4914 return null; | |
4915 } | |
4916 | |
4917 /** | |
4918 * Return a list of the ranges of characters in the given [comment] that | |
4919 * should be treated as code blocks. | |
4920 */ | |
4921 List<List<int>> _getCodeBlockRanges(String comment) { | |
4922 List<List<int>> ranges = <List<int>>[]; | |
4923 int length = comment.length; | |
4924 if (length < 3) { | |
4925 return ranges; | |
4926 } | |
4927 int index = 0; | |
4928 int firstChar = comment.codeUnitAt(0); | |
4929 if (firstChar == 0x2F) { | |
4930 int secondChar = comment.codeUnitAt(1); | |
4931 int thirdChar = comment.codeUnitAt(2); | |
4932 if ((secondChar == 0x2A && thirdChar == 0x2A) || | |
4933 (secondChar == 0x2F && thirdChar == 0x2F)) { | |
4934 index = 3; | |
4935 } | |
4936 } | |
4937 if (StringUtilities.startsWith4(comment, index, 0x20, 0x20, 0x20, 0x20)) { | |
4938 int end = index + 4; | |
4939 while (end < length && | |
4940 comment.codeUnitAt(end) != 0xD && | |
4941 comment.codeUnitAt(end) != 0xA) { | |
4942 end = end + 1; | |
4943 } | |
4944 ranges.add(<int>[index, end]); | |
4945 index = end; | |
4946 } | |
4947 while (index < length) { | |
4948 int currentChar = comment.codeUnitAt(index); | |
4949 if (currentChar == 0xD || currentChar == 0xA) { | |
4950 index = index + 1; | |
4951 while (index < length && | |
4952 Character.isWhitespace(comment.codeUnitAt(index))) { | |
4953 index = index + 1; | |
4954 } | |
4955 if (StringUtilities.startsWith6( | |
4956 comment, index, 0x2A, 0x20, 0x20, 0x20, 0x20, 0x20)) { | |
4957 int end = index + 6; | |
4958 while (end < length && | |
4959 comment.codeUnitAt(end) != 0xD && | |
4960 comment.codeUnitAt(end) != 0xA) { | |
4961 end = end + 1; | |
4962 } | |
4963 ranges.add(<int>[index, end]); | |
4964 index = end; | |
4965 } | |
4966 } else if (index + 1 < length && | |
4967 currentChar == 0x5B && | |
4968 comment.codeUnitAt(index + 1) == 0x3A) { | |
4969 int end = StringUtilities.indexOf2(comment, index + 2, 0x3A, 0x5D); | |
4970 if (end < 0) { | |
4971 end = length; | |
4972 } | |
4973 ranges.add(<int>[index, end]); | |
4974 index = end + 1; | |
4975 } else { | |
4976 index = index + 1; | |
4977 } | |
4978 } | |
4979 return ranges; | |
4980 } | |
4981 | |
4982 /** | |
4983 * Return the end token associated with the given [beginToken], or `null` if | |
4984 * either the given token is not a begin token or it does not have an end | |
4985 * token associated with it. | |
4986 */ | |
4987 Token _getEndToken(Token beginToken) { | |
4988 if (beginToken is BeginToken) { | |
4989 return beginToken.endToken; | |
4990 } | |
4991 return null; | |
4992 } | |
4993 | |
4994 bool _injectGenericComment(TokenType type, int prefixLen) { | |
4995 if (parseGenericMethodComments) { | |
4996 CommentToken t = _currentToken.precedingComments; | |
4997 for (; t != null; t = t.next) { | |
4998 if (t.type == type) { | |
4999 String comment = t.lexeme.substring(prefixLen, t.lexeme.length - 2); | |
5000 Token list = _scanGenericMethodComment(comment, t.offset + prefixLen); | |
5001 if (list != null) { | |
5002 // Remove the token from the comment stream. | |
5003 t.remove(); | |
5004 // Insert the tokens into the stream. | |
5005 _injectTokenList(list); | |
5006 return true; | |
5007 } | |
5008 } | |
5009 } | |
5010 } | |
5011 return false; | |
5012 } | |
5013 | |
5014 /** | |
5015 * Matches a generic comment type substitution and injects it into the token | |
5016 * stream. Returns true if a match was injected, otherwise false. | |
5017 * | |
5018 * These comments are of the form `/*=T*/`, in other words, a [TypeName] | |
5019 * inside a slash-star comment, preceded by equals sign. | |
5020 */ | |
5021 bool _injectGenericCommentTypeAssign() { | |
5022 return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_ASSIGN, 3); | |
5023 } | |
5024 | |
5025 /** | |
5026 * Matches a generic comment type parameters and injects them into the token | |
5027 * stream. Returns true if a match was injected, otherwise false. | |
5028 * | |
5029 * These comments are of the form `/*<K, V>*/`, in other words, a | |
5030 * [TypeParameterList] or [TypeArgumentList] inside a slash-star comment. | |
5031 */ | |
5032 bool _injectGenericCommentTypeList() { | |
5033 return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_LIST, 2); | |
5034 } | |
5035 | |
5036 /** | |
5037 * Inject the given [token] into the token stream immediately before the | |
5038 * current token. | |
5039 */ | |
5040 Token _injectToken(Token token) { | |
5041 Token previous = _currentToken.previous; | |
5042 token.setNext(_currentToken); | |
5043 previous.setNext(token); | |
5044 return token; | |
5045 } | |
5046 | |
5047 void _injectTokenList(Token firstToken) { | |
5048 // Scanner creates a cyclic EOF token. | |
5049 Token lastToken = firstToken; | |
5050 while (lastToken.next.type != TokenType.EOF) { | |
5051 lastToken = lastToken.next; | |
5052 } | |
5053 // Inject these new tokens into the stream. | |
5054 Token previous = _currentToken.previous; | |
5055 lastToken.setNext(_currentToken); | |
5056 previous.setNext(firstToken); | |
5057 _currentToken = firstToken; | |
5058 } | |
5059 | |
5060 /** | |
5061 * Return `true` if the current token could be the question mark in a | |
5062 * condition expression. The current token is assumed to be a question mark. | |
5063 */ | |
5064 bool _isConditionalOperator() { | |
5065 void parseOperation(Parser parser) { | |
5066 parser.parseExpressionWithoutCascade(); | |
5067 } | |
5068 | |
5069 Token token = _skip(_currentToken.next, parseOperation); | |
5070 if (token == null || !_tokenMatches(token, TokenType.COLON)) { | |
5071 return false; | |
5072 } | |
5073 token = _skip(token.next, parseOperation); | |
5074 return token != null; | |
5075 } | |
5076 | |
5077 /** | |
5078 * Return `true` if the given [character] is a valid hexadecimal digit. | |
5079 */ | |
5080 bool _isHexDigit(int character) => | |
5081 (0x30 <= character && character <= 0x39) || | |
5082 (0x41 <= character && character <= 0x46) || | |
5083 (0x61 <= character && character <= 0x66); | |
5084 | |
5085 bool _isLikelyArgumentList() { | |
5086 // Try to reduce the amount of lookahead required here before enabling | |
5087 // generic methods. | |
5088 if (_matches(TokenType.OPEN_PAREN)) { | |
5089 return true; | |
5090 } | |
5091 if (!parseGenericMethods) { | |
5092 return false; | |
5093 } | |
5094 Token token = skipTypeArgumentList(_currentToken); | |
5095 return token != null && _tokenMatches(token, TokenType.OPEN_PAREN); | |
5096 } | |
5097 | |
5098 /** | |
5099 * Given that we have just found bracketed text within the given [comment], | |
5100 * look to see whether that text is (a) followed by a parenthesized link | |
5101 * address, (b) followed by a colon, or (c) followed by optional whitespace | |
5102 * and another square bracket. The [rightIndex] is the index of the right | |
5103 * bracket. Return `true` if the bracketed text is followed by a link address. | |
5104 * | |
5105 * This method uses the syntax described by the | |
5106 * <a href="http://daringfireball.net/projects/markdown/syntax">markdown</a> | |
5107 * project. | |
5108 */ | |
5109 bool _isLinkText(String comment, int rightIndex) { | |
5110 int length = comment.length; | |
5111 int index = rightIndex + 1; | |
5112 if (index >= length) { | |
5113 return false; | |
5114 } | |
5115 int nextChar = comment.codeUnitAt(index); | |
5116 if (nextChar == 0x28 || nextChar == 0x3A) { | |
5117 return true; | |
5118 } | |
5119 while (Character.isWhitespace(nextChar)) { | |
5120 index = index + 1; | |
5121 if (index >= length) { | |
5122 return false; | |
5123 } | |
5124 nextChar = comment.codeUnitAt(index); | |
5125 } | |
5126 return nextChar == 0x5B; | |
5127 } | |
5128 | |
5129 /** | |
5130 * Return `true` if the given [startToken] appears to be the beginning of an | |
5131 * operator declaration. | |
5132 */ | |
5133 bool _isOperator(Token startToken) { | |
5134 // Accept any operator here, even if it is not user definable. | |
5135 if (!startToken.isOperator) { | |
5136 return false; | |
5137 } | |
5138 // Token "=" means that it is actually a field initializer. | |
5139 if (startToken.type == TokenType.EQ) { | |
5140 return false; | |
5141 } | |
5142 // Consume all operator tokens. | |
5143 Token token = startToken.next; | |
5144 while (token.isOperator) { | |
5145 token = token.next; | |
5146 } | |
5147 // Formal parameter list is expect now. | |
5148 return _tokenMatches(token, TokenType.OPEN_PAREN); | |
5149 } | |
5150 | |
5151 bool _isPeekGenericTypeParametersAndOpenParen() { | |
5152 if (!parseGenericMethods) { | |
5153 return false; | |
5154 } | |
5155 Token token = _skipTypeParameterList(_peek()); | |
5156 return token != null && _tokenMatches(token, TokenType.OPEN_PAREN); | |
5157 } | |
5158 | |
5159 /** | |
5160 * Return `true` if the [startToken] appears to be the first token of a type | |
5161 * name that is followed by a variable or field formal parameter. | |
5162 */ | |
5163 bool _isTypedIdentifier(Token startToken) { | |
5164 Token token = skipReturnType(startToken); | |
5165 if (token == null) { | |
5166 return false; | |
5167 } else if (_tokenMatchesIdentifier(token)) { | |
5168 return true; | |
5169 } else if (_tokenMatchesKeyword(token, Keyword.THIS) && | |
5170 _tokenMatches(token.next, TokenType.PERIOD) && | |
5171 _tokenMatchesIdentifier(token.next.next)) { | |
5172 return true; | |
5173 } else if (_tokenMatchesKeyword(startToken, Keyword.VOID)) { | |
5174 // The keyword 'void' isn't a valid identifier, so it should be assumed to | |
5175 // be a type name. | |
5176 return true; | |
5177 } else if (startToken.next != token && | |
5178 !_tokenMatches(token, TokenType.OPEN_PAREN)) { | |
5179 // The type is more than a simple identifier, so it should be assumed to | |
5180 // be a type name. | |
5181 return true; | |
5182 } | |
5183 return false; | |
5184 } | |
5185 | |
5186 /** | |
5187 * Increments the error reporting lock level. If level is more than `0`, then | |
5188 * [reportError] wont report any error. | |
5189 */ | |
5190 void _lockErrorListener() { | |
5191 _errorListenerLock++; | |
5192 } | |
5193 | |
5194 /** | |
5195 * Return `true` if the current token has the given [type]. Note that the | |
5196 * method [_matchesGt] should be used if the argument to this method would be | |
5197 * [TokenType.GT]. | |
5198 */ | |
5199 bool _matches(TokenType type) => _currentToken.type == type; | |
5200 | |
5201 /** | |
5202 * Return `true` if the current token has a type of [TokenType.GT]. Note that | |
5203 * this method, unlike other variants, will modify the token stream if | |
5204 * possible to match desired type. In particular, if the next token is either | |
5205 * a '>>' or '>>>', the token stream will be re-written and `true` will be | |
5206 * returned. | |
5207 */ | |
5208 bool _matchesGt() { | |
5209 TokenType currentType = _currentToken.type; | |
5210 if (currentType == TokenType.GT) { | |
5211 return true; | |
5212 } else if (currentType == TokenType.GT_GT) { | |
5213 Token first = _createToken(_currentToken, TokenType.GT); | |
5214 Token second = new Token(TokenType.GT, _currentToken.offset + 1); | |
5215 second.setNext(_currentToken.next); | |
5216 first.setNext(second); | |
5217 _currentToken.previous.setNext(first); | |
5218 _currentToken = first; | |
5219 return true; | |
5220 } else if (currentType == TokenType.GT_EQ) { | |
5221 Token first = _createToken(_currentToken, TokenType.GT); | |
5222 Token second = new Token(TokenType.EQ, _currentToken.offset + 1); | |
5223 second.setNext(_currentToken.next); | |
5224 first.setNext(second); | |
5225 _currentToken.previous.setNext(first); | |
5226 _currentToken = first; | |
5227 return true; | |
5228 } else if (currentType == TokenType.GT_GT_EQ) { | |
5229 int offset = _currentToken.offset; | |
5230 Token first = _createToken(_currentToken, TokenType.GT); | |
5231 Token second = new Token(TokenType.GT, offset + 1); | |
5232 Token third = new Token(TokenType.EQ, offset + 2); | |
5233 third.setNext(_currentToken.next); | |
5234 second.setNext(third); | |
5235 first.setNext(second); | |
5236 _currentToken.previous.setNext(first); | |
5237 _currentToken = first; | |
5238 return true; | |
5239 } | |
5240 return false; | |
5241 } | |
5242 | |
5243 /** | |
5244 * Return `true` if the current token is a valid identifier. Valid identifiers | |
5245 * include built-in identifiers (pseudo-keywords). | |
5246 */ | |
5247 bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken); | |
5248 | |
5249 /** | |
5250 * Return `true` if the current token matches the given [keyword]. | |
5251 */ | |
5252 bool _matchesKeyword(Keyword keyword) => | |
5253 _tokenMatchesKeyword(_currentToken, keyword); | |
5254 | |
5255 /** | |
5256 * Return `true` if the current token matches the given [identifier]. | |
5257 */ | |
5258 bool _matchesString(String identifier) => | |
5259 _currentToken.type == TokenType.IDENTIFIER && | |
5260 _currentToken.lexeme == identifier; | |
5261 | |
5262 /** | |
5263 * If the current token has the given [type], then advance to the next token | |
5264 * and return `true`. Otherwise, return `false` without advancing. This method | |
5265 * should not be invoked with an argument value of [TokenType.GT]. | |
5266 */ | |
5267 bool _optional(TokenType type) { | |
5268 if (_currentToken.type == type) { | |
5269 _advance(); | |
5270 return true; | |
5271 } | |
5272 return false; | |
5273 } | |
5274 | |
5275 /** | |
5276 * Parse an argument list when we need to check for an open paren and recover | |
5277 * when there isn't one. Return the argument list that was parsed. | |
5278 */ | |
5279 ArgumentList _parseArgumentListChecked() { | |
5280 if (_matches(TokenType.OPEN_PAREN)) { | |
5281 return parseArgumentList(); | |
5282 } | |
5283 _reportErrorForCurrentToken( | |
5284 ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_PAREN.lexeme]); | |
5285 // Recovery: Look to see whether there is a close paren that isn't matched | |
5286 // to an open paren and if so parse the list of arguments as normal. | |
5287 return new ArgumentList(_createSyntheticToken(TokenType.OPEN_PAREN), null, | |
5288 _createSyntheticToken(TokenType.CLOSE_PAREN)); | |
5289 } | |
5290 | |
5291 /** | |
5292 * Parse an assert within a constructor's initializer list. Return the assert. | |
5293 * | |
5294 * This method assumes that the current token matches `Keyword.ASSERT`. | |
5295 * | |
5296 * assertInitializer ::= | |
5297 * 'assert' '(' expression [',' expression] ')' | |
5298 */ | |
5299 void _parseAssertInitializer() { | |
5300 // TODO(brianwilkerson) Capture the syntax in the AST using a new class, | |
5301 // such as AssertInitializer | |
5302 Token keyword = getAndAdvance(); | |
5303 Token leftParen = _expect(TokenType.OPEN_PAREN); | |
5304 Expression expression = parseExpression2(); | |
5305 Token comma; | |
5306 Expression message; | |
5307 if (_matches(TokenType.COMMA)) { | |
5308 comma = getAndAdvance(); | |
5309 message = parseExpression2(); | |
5310 } | |
5311 Token rightParen = _expect(TokenType.CLOSE_PAREN); | |
5312 // return new AssertInitializer( | |
5313 // keyword, leftParen, expression, comma, message, rightParen); | |
5314 } | |
5315 | |
5316 /** | |
5317 * Parse an assignable expression given that the current token is not 'super'. | |
5318 * The [primaryAllowed] is `true` if the expression is allowed to be a primary | |
5319 * without any assignable selector. Return the assignable expression that was | |
5320 * parsed. | |
5321 */ | |
5322 Expression _parseAssignableExpressionNotStartingWithSuper( | |
5323 bool primaryAllowed) { | |
5324 // | |
5325 // A primary expression can start with an identifier. We resolve the | |
5326 // ambiguity by determining whether the primary consists of anything other | |
5327 // than an identifier and/or is followed by an assignableSelector. | |
5328 // | |
5329 Expression expression = parsePrimaryExpression(); | |
5330 bool isOptional = primaryAllowed || expression is SimpleIdentifier; | |
5331 while (true) { | |
5332 while (_isLikelyArgumentList()) { | |
5333 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); | |
5334 ArgumentList argumentList = parseArgumentList(); | |
5335 Expression currentExpression = expression; | |
5336 if (currentExpression is SimpleIdentifier) { | |
5337 expression = new MethodInvocation( | |
5338 null, null, currentExpression, typeArguments, argumentList); | |
5339 } else if (currentExpression is PrefixedIdentifier) { | |
5340 expression = new MethodInvocation( | |
5341 currentExpression.prefix, | |
5342 currentExpression.period, | |
5343 currentExpression.identifier, | |
5344 typeArguments, | |
5345 argumentList); | |
5346 } else if (currentExpression is PropertyAccess) { | |
5347 expression = new MethodInvocation( | |
5348 currentExpression.target, | |
5349 currentExpression.operator, | |
5350 currentExpression.propertyName, | |
5351 typeArguments, | |
5352 argumentList); | |
5353 } else { | |
5354 expression = new FunctionExpressionInvocation( | |
5355 expression, typeArguments, argumentList); | |
5356 } | |
5357 if (!primaryAllowed) { | |
5358 isOptional = false; | |
5359 } | |
5360 } | |
5361 Expression selectorExpression = _parseAssignableSelector( | |
5362 expression, isOptional || (expression is PrefixedIdentifier)); | |
5363 if (identical(selectorExpression, expression)) { | |
5364 if (!isOptional && (expression is PrefixedIdentifier)) { | |
5365 PrefixedIdentifier identifier = expression as PrefixedIdentifier; | |
5366 expression = new PropertyAccess( | |
5367 identifier.prefix, identifier.period, identifier.identifier); | |
5368 } | |
5369 return expression; | |
5370 } | |
5371 expression = selectorExpression; | |
5372 isOptional = true; | |
5373 } | |
5374 } | |
5375 | |
5376 /** | |
5377 * Parse an assignable selector. The [prefix] is the expression preceding the | |
5378 * selector. The [optional] is `true` if the selector is optional. Return the | |
5379 * assignable selector that was parsed, or the original prefix if there was no | |
5380 * assignable selector. If [allowConditional] is false, then the '?.' | |
5381 * operator will still be parsed, but a parse error will be generated. | |
5382 * | |
5383 * unconditionalAssignableSelector ::= | |
5384 * '[' expression ']' | |
5385 * | '.' identifier | |
5386 * | |
5387 * assignableSelector ::= | |
5388 * unconditionalAssignableSelector | |
5389 * | '?.' identifier | |
5390 */ | |
5391 Expression _parseAssignableSelector(Expression prefix, bool optional, | |
5392 {bool allowConditional: true}) { | |
5393 TokenType type = _currentToken.type; | |
5394 if (type == TokenType.OPEN_SQUARE_BRACKET) { | |
5395 Token leftBracket = getAndAdvance(); | |
5396 bool wasInInitializer = _inInitializer; | |
5397 _inInitializer = false; | |
5398 try { | |
5399 Expression index = parseExpression2(); | |
5400 Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET); | |
5401 return new IndexExpression.forTarget( | |
5402 prefix, leftBracket, index, rightBracket); | |
5403 } finally { | |
5404 _inInitializer = wasInInitializer; | |
5405 } | |
5406 } else { | |
5407 bool isQuestionPeriod = type == TokenType.QUESTION_PERIOD; | |
5408 if (type == TokenType.PERIOD || isQuestionPeriod) { | |
5409 if (isQuestionPeriod && !allowConditional) { | |
5410 _reportErrorForCurrentToken( | |
5411 ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, | |
5412 [_currentToken.lexeme]); | |
5413 } | |
5414 Token operator = getAndAdvance(); | |
5415 return new PropertyAccess(prefix, operator, parseSimpleIdentifier()); | |
5416 } else { | |
5417 if (!optional) { | |
5418 // Report the missing selector. | |
5419 _reportErrorForCurrentToken( | |
5420 ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR); | |
5421 } | |
5422 return prefix; | |
5423 } | |
5424 } | |
5425 } | |
5426 | |
5427 /** | |
5428 * Parse a block when we need to check for an open curly brace and recover | |
5429 * when there isn't one. Return the block that was parsed. | |
5430 * | |
5431 * block ::= | |
5432 * '{' statements '}' | |
5433 */ | |
5434 Block _parseBlockChecked() { | |
5435 if (_matches(TokenType.OPEN_CURLY_BRACKET)) { | |
5436 return parseBlock(); | |
5437 } | |
5438 // TODO(brianwilkerson) Improve the error message. | |
5439 _reportErrorForCurrentToken( | |
5440 ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_CURLY_BRACKET.lexeme]); | |
5441 // Recovery: Check for an unmatched closing curly bracket and parse | |
5442 // statements until it is reached. | |
5443 return new Block(_createSyntheticToken(TokenType.OPEN_CURLY_BRACKET), null, | |
5444 _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET)); | |
5445 } | |
5446 | |
5447 /** | |
5448 * Parse a list of class members. The [className] is the name of the class | |
5449 * whose members are being parsed. The [closingBracket] is the closing bracket | |
5450 * for the class, or `null` if the closing bracket is missing. Return the list | |
5451 * of class members that were parsed. | |
5452 * | |
5453 * classMembers ::= | |
5454 * (metadata memberDefinition)* | |
5455 */ | |
5456 List<ClassMember> _parseClassMembers(String className, Token closingBracket) { | |
5457 List<ClassMember> members = <ClassMember>[]; | |
5458 Token memberStart = _currentToken; | |
5459 TokenType type = _currentToken.type; | |
5460 Keyword keyword = _currentToken.keyword; | |
5461 while (type != TokenType.EOF && | |
5462 type != TokenType.CLOSE_CURLY_BRACKET && | |
5463 (closingBracket != null || | |
5464 (keyword != Keyword.CLASS && keyword != Keyword.TYPEDEF))) { | |
5465 if (type == TokenType.SEMICOLON) { | |
5466 _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, | |
5467 [_currentToken.lexeme]); | |
5468 _advance(); | |
5469 } else { | |
5470 ClassMember member = parseClassMember(className); | |
5471 if (member != null) { | |
5472 members.add(member); | |
5473 } | |
5474 } | |
5475 if (identical(_currentToken, memberStart)) { | |
5476 _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, | |
5477 [_currentToken.lexeme]); | |
5478 _advance(); | |
5479 } | |
5480 memberStart = _currentToken; | |
5481 type = _currentToken.type; | |
5482 keyword = _currentToken.keyword; | |
5483 } | |
5484 return members; | |
5485 } | |
5486 | |
5487 /** | |
5488 * Parse a class type alias. The [commentAndMetadata] is the metadata to be | |
5489 * associated with the member. The [abstractKeyword] is the token representing | |
5490 * the 'abstract' keyword. The [classKeyword] is the token representing the | |
5491 * 'class' keyword. The [className] is the name of the alias, and the | |
5492 * [typeParameters] are the type parameters following the name. Return the | |
5493 * class type alias that was parsed. | |
5494 * | |
5495 * classTypeAlias ::= | |
5496 * identifier typeParameters? '=' 'abstract'? mixinApplication | |
5497 * | |
5498 * mixinApplication ::= | |
5499 * type withClause implementsClause? ';' | |
5500 */ | |
5501 ClassTypeAlias _parseClassTypeAliasAfterName( | |
5502 CommentAndMetadata commentAndMetadata, | |
5503 Token abstractKeyword, | |
5504 Token classKeyword, | |
5505 SimpleIdentifier className, | |
5506 TypeParameterList typeParameters) { | |
5507 Token equals = _expect(TokenType.EQ); | |
5508 TypeName superclass = parseTypeName(false); | |
5509 WithClause withClause = null; | |
5510 if (_matchesKeyword(Keyword.WITH)) { | |
5511 withClause = parseWithClause(); | |
5512 } else { | |
5513 _reportErrorForCurrentToken( | |
5514 ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]); | |
5515 } | |
5516 ImplementsClause implementsClause = null; | |
5517 if (_matchesKeyword(Keyword.IMPLEMENTS)) { | |
5518 implementsClause = parseImplementsClause(); | |
5519 } | |
5520 Token semicolon; | |
5521 if (_matches(TokenType.SEMICOLON)) { | |
5522 semicolon = getAndAdvance(); | |
5523 } else { | |
5524 if (_matches(TokenType.OPEN_CURLY_BRACKET)) { | |
5525 _reportErrorForCurrentToken( | |
5526 ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]); | |
5527 Token leftBracket = getAndAdvance(); | |
5528 _parseClassMembers(className.name, _getEndToken(leftBracket)); | |
5529 _expect(TokenType.CLOSE_CURLY_BRACKET); | |
5530 } else { | |
5531 _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN, | |
5532 _currentToken.previous, [TokenType.SEMICOLON.lexeme]); | |
5533 } | |
5534 semicolon = _createSyntheticToken(TokenType.SEMICOLON); | |
5535 } | |
5536 return new ClassTypeAlias( | |
5537 commentAndMetadata.comment, | |
5538 commentAndMetadata.metadata, | |
5539 classKeyword, | |
5540 className, | |
5541 typeParameters, | |
5542 equals, | |
5543 abstractKeyword, | |
5544 superclass, | |
5545 withClause, | |
5546 implementsClause, | |
5547 semicolon); | |
5548 } | |
5549 | |
5550 /** | |
5551 * Parse all of the comment references occurring in the given array of | |
5552 * documentation comments. The [tokens] are the comment tokens representing | |
5553 * the documentation comments to be parsed. Return the comment references that | |
5554 * were parsed. | |
5555 * | |
5556 * commentReference ::= | |
5557 * '[' 'new'? qualified ']' libraryReference? | |
5558 * | |
5559 * libraryReference ::= | |
5560 * '(' stringLiteral ')' | |
5561 */ | |
5562 List<CommentReference> _parseCommentReferences( | |
5563 List<DocumentationCommentToken> tokens) { | |
5564 List<CommentReference> references = <CommentReference>[]; | |
5565 bool isInGitHubCodeBlock = false; | |
5566 for (DocumentationCommentToken token in tokens) { | |
5567 String comment = token.lexeme; | |
5568 // Skip GitHub code blocks. | |
5569 // https://help.github.com/articles/creating-and-highlighting-code-blocks/ | |
5570 if (tokens.length != 1) { | |
5571 if (comment.indexOf('```') != -1) { | |
5572 isInGitHubCodeBlock = !isInGitHubCodeBlock; | |
5573 } | |
5574 if (isInGitHubCodeBlock) { | |
5575 continue; | |
5576 } | |
5577 } | |
5578 // Remove GitHub include code. | |
5579 comment = _removeGitHubInlineCode(comment); | |
5580 // Find references. | |
5581 int length = comment.length; | |
5582 List<List<int>> codeBlockRanges = _getCodeBlockRanges(comment); | |
5583 int leftIndex = comment.indexOf('['); | |
5584 while (leftIndex >= 0 && leftIndex + 1 < length) { | |
5585 List<int> range = _findRange(codeBlockRanges, leftIndex); | |
5586 if (range == null) { | |
5587 int nameOffset = token.offset + leftIndex + 1; | |
5588 int rightIndex = comment.indexOf(']', leftIndex); | |
5589 if (rightIndex >= 0) { | |
5590 int firstChar = comment.codeUnitAt(leftIndex + 1); | |
5591 if (firstChar != 0x27 && firstChar != 0x22) { | |
5592 if (_isLinkText(comment, rightIndex)) { | |
5593 // TODO(brianwilkerson) Handle the case where there's a library | |
5594 // URI in the link text. | |
5595 } else { | |
5596 CommentReference reference = parseCommentReference( | |
5597 comment.substring(leftIndex + 1, rightIndex), nameOffset); | |
5598 if (reference != null) { | |
5599 references.add(reference); | |
5600 token.references.add(reference.beginToken); | |
5601 } | |
5602 } | |
5603 } | |
5604 } else { | |
5605 // terminating ']' is not typed yet | |
5606 int charAfterLeft = comment.codeUnitAt(leftIndex + 1); | |
5607 Token nameToken; | |
5608 if (Character.isLetterOrDigit(charAfterLeft)) { | |
5609 int nameEnd = StringUtilities.indexOfFirstNotLetterDigit( | |
5610 comment, leftIndex + 1); | |
5611 String name = comment.substring(leftIndex + 1, nameEnd); | |
5612 nameToken = | |
5613 new StringToken(TokenType.IDENTIFIER, name, nameOffset); | |
5614 } else { | |
5615 nameToken = new SyntheticStringToken( | |
5616 TokenType.IDENTIFIER, '', nameOffset); | |
5617 } | |
5618 nameToken.setNext(new SimpleToken(TokenType.EOF, nameToken.end)); | |
5619 references.add( | |
5620 new CommentReference(null, new SimpleIdentifier(nameToken))); | |
5621 token.references.add(nameToken); | |
5622 // next character | |
5623 rightIndex = leftIndex + 1; | |
5624 } | |
5625 leftIndex = comment.indexOf('[', rightIndex); | |
5626 } else { | |
5627 leftIndex = comment.indexOf('[', range[1]); | |
5628 } | |
5629 } | |
5630 } | |
5631 return references; | |
5632 } | |
5633 | |
5634 /** | |
5635 * Parse a list of configurations. Return the configurations that were parsed, | |
5636 * or `null` if there are no configurations. | |
5637 */ | |
5638 List<Configuration> _parseConfigurations() { | |
5639 List<Configuration> configurations = null; | |
5640 while (_matchesKeyword(Keyword.IF)) { | |
5641 configurations ??= <Configuration>[]; | |
5642 configurations.add(parseConfiguration()); | |
5643 } | |
5644 return configurations; | |
5645 } | |
5646 | |
5647 ConstructorDeclaration _parseConstructor( | |
5648 CommentAndMetadata commentAndMetadata, | |
5649 Token externalKeyword, | |
5650 Token constKeyword, | |
5651 Token factoryKeyword, | |
5652 SimpleIdentifier returnType, | |
5653 Token period, | |
5654 SimpleIdentifier name, | |
5655 FormalParameterList parameters) { | |
5656 bool bodyAllowed = externalKeyword == null; | |
5657 Token separator = null; | |
5658 List<ConstructorInitializer> initializers = null; | |
5659 if (_matches(TokenType.COLON)) { | |
5660 separator = getAndAdvance(); | |
5661 initializers = <ConstructorInitializer>[]; | |
5662 do { | |
5663 Keyword keyword = _currentToken.keyword; | |
5664 if (keyword == Keyword.THIS) { | |
5665 TokenType nextType = _peek().type; | |
5666 if (nextType == TokenType.OPEN_PAREN) { | |
5667 bodyAllowed = false; | |
5668 initializers.add(_parseRedirectingConstructorInvocation(false)); | |
5669 } else if (nextType == TokenType.PERIOD && | |
5670 _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) { | |
5671 bodyAllowed = false; | |
5672 initializers.add(_parseRedirectingConstructorInvocation(true)); | |
5673 } else { | |
5674 initializers.add(_parseConstructorFieldInitializer(true)); | |
5675 } | |
5676 } else if (keyword == Keyword.SUPER) { | |
5677 initializers.add(parseSuperConstructorInvocation()); | |
5678 } else if (_matches(TokenType.OPEN_CURLY_BRACKET) || | |
5679 _matches(TokenType.FUNCTION)) { | |
5680 _reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER); | |
5681 } else if (_enableAssertInitializer && | |
5682 _matchesKeyword(Keyword.ASSERT)) { | |
5683 _parseAssertInitializer(); | |
5684 } else { | |
5685 initializers.add(_parseConstructorFieldInitializer(false)); | |
5686 } | |
5687 } while (_optional(TokenType.COMMA)); | |
5688 if (factoryKeyword != null) { | |
5689 _reportErrorForToken( | |
5690 ParserErrorCode.FACTORY_WITH_INITIALIZERS, factoryKeyword); | |
5691 } | |
5692 } | |
5693 ConstructorName redirectedConstructor = null; | |
5694 FunctionBody body; | |
5695 if (_matches(TokenType.EQ)) { | |
5696 separator = getAndAdvance(); | |
5697 redirectedConstructor = parseConstructorName(); | |
5698 body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON)); | |
5699 if (factoryKeyword == null) { | |
5700 _reportErrorForNode( | |
5701 ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR, | |
5702 redirectedConstructor); | |
5703 } | |
5704 } else { | |
5705 body = | |
5706 parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false); | |
5707 if (constKeyword != null && | |
5708 factoryKeyword != null && | |
5709 externalKeyword == null) { | |
5710 _reportErrorForToken(ParserErrorCode.CONST_FACTORY, factoryKeyword); | |
5711 } else if (body is EmptyFunctionBody) { | |
5712 if (factoryKeyword != null && | |
5713 externalKeyword == null && | |
5714 _parseFunctionBodies) { | |
5715 _reportErrorForToken( | |
5716 ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword); | |
5717 } | |
5718 } else { | |
5719 if (constKeyword != null) { | |
5720 _reportErrorForNode( | |
5721 ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body); | |
5722 } else if (externalKeyword != null) { | |
5723 _reportErrorForNode( | |
5724 ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body); | |
5725 } else if (!bodyAllowed) { | |
5726 _reportErrorForNode( | |
5727 ParserErrorCode.REDIRECTING_CONSTRUCTOR_WITH_BODY, body); | |
5728 } | |
5729 } | |
5730 } | |
5731 return new ConstructorDeclaration( | |
5732 commentAndMetadata.comment, | |
5733 commentAndMetadata.metadata, | |
5734 externalKeyword, | |
5735 constKeyword, | |
5736 factoryKeyword, | |
5737 returnType, | |
5738 period, | |
5739 name, | |
5740 parameters, | |
5741 separator, | |
5742 initializers, | |
5743 redirectedConstructor, | |
5744 body); | |
5745 } | |
5746 | |
5747 /** | |
5748 * Parse a field initializer within a constructor. The flag [hasThis] should | |
5749 * be true if the current token is `this`. Return the field initializer that | |
5750 * was parsed. | |
5751 * | |
5752 * fieldInitializer: | |
5753 * ('this' '.')? identifier '=' conditionalExpression cascadeSection* | |
5754 */ | |
5755 ConstructorFieldInitializer _parseConstructorFieldInitializer(bool hasThis) { | |
5756 Token keywordToken = null; | |
5757 Token period = null; | |
5758 if (hasThis) { | |
5759 keywordToken = getAndAdvance(); | |
5760 period = _expect(TokenType.PERIOD); | |
5761 } | |
5762 SimpleIdentifier fieldName = parseSimpleIdentifier(); | |
5763 Token equals = null; | |
5764 TokenType type = _currentToken.type; | |
5765 if (type == TokenType.EQ) { | |
5766 equals = getAndAdvance(); | |
5767 } else { | |
5768 _reportErrorForCurrentToken( | |
5769 ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER); | |
5770 Keyword keyword = _currentToken.keyword; | |
5771 if (keyword != Keyword.THIS && | |
5772 keyword != Keyword.SUPER && | |
5773 type != TokenType.OPEN_CURLY_BRACKET && | |
5774 type != TokenType.FUNCTION) { | |
5775 equals = _createSyntheticToken(TokenType.EQ); | |
5776 } else { | |
5777 return new ConstructorFieldInitializer(keywordToken, period, fieldName, | |
5778 _createSyntheticToken(TokenType.EQ), createSyntheticIdentifier()); | |
5779 } | |
5780 } | |
5781 bool wasInInitializer = _inInitializer; | |
5782 _inInitializer = true; | |
5783 try { | |
5784 Expression expression = parseConditionalExpression(); | |
5785 if (_matches(TokenType.PERIOD_PERIOD)) { | |
5786 List<Expression> cascadeSections = <Expression>[]; | |
5787 do { | |
5788 Expression section = parseCascadeSection(); | |
5789 if (section != null) { | |
5790 cascadeSections.add(section); | |
5791 } | |
5792 } while (_matches(TokenType.PERIOD_PERIOD)); | |
5793 expression = new CascadeExpression(expression, cascadeSections); | |
5794 } | |
5795 return new ConstructorFieldInitializer( | |
5796 keywordToken, period, fieldName, equals, expression); | |
5797 } finally { | |
5798 _inInitializer = wasInInitializer; | |
5799 } | |
5800 } | |
5801 | |
5802 /** | |
5803 * Parse a directive. The [commentAndMetadata] is the metadata to be | |
5804 * associated with the directive. Return the directive that was parsed. | |
5805 * | |
5806 * directive ::= | |
5807 * exportDirective | |
5808 * | libraryDirective | |
5809 * | importDirective | |
5810 * | partDirective | |
5811 */ | |
5812 Directive _parseDirective(CommentAndMetadata commentAndMetadata) { | |
5813 if (_matchesKeyword(Keyword.IMPORT)) { | |
5814 return _parseImportDirective(commentAndMetadata); | |
5815 } else if (_matchesKeyword(Keyword.EXPORT)) { | |
5816 return _parseExportDirective(commentAndMetadata); | |
5817 } else if (_matchesKeyword(Keyword.LIBRARY)) { | |
5818 return _parseLibraryDirective(commentAndMetadata); | |
5819 } else if (_matchesKeyword(Keyword.PART)) { | |
5820 return _parsePartOrPartOfDirective(commentAndMetadata); | |
5821 } else { | |
5822 // Internal error: this method should not have been invoked if the current | |
5823 // token was something other than one of the above. | |
5824 throw new StateError( | |
5825 "parseDirective invoked in an invalid state; currentToken = $_currentT
oken"); | |
5826 } | |
5827 } | |
5828 | |
5829 /** | |
5830 * Parse an enum constant declaration. Return the enum constant declaration | |
5831 * that was parsed. | |
5832 * | |
5833 * Specified: | |
5834 * | |
5835 * enumConstant ::= | |
5836 * id | |
5837 * | |
5838 * Actual: | |
5839 * | |
5840 * enumConstant ::= | |
5841 * metadata id | |
5842 */ | |
5843 EnumConstantDeclaration _parseEnumConstantDeclaration() { | |
5844 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); | |
5845 SimpleIdentifier name; | |
5846 if (_matchesIdentifier()) { | |
5847 name = _parseSimpleIdentifierUnchecked(isDeclaration: true); | |
5848 } else { | |
5849 name = createSyntheticIdentifier(); | |
5850 } | |
5851 if (commentAndMetadata.hasMetadata) { | |
5852 _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT, | |
5853 commentAndMetadata.metadata[0]); | |
5854 } | |
5855 return new EnumConstantDeclaration( | |
5856 commentAndMetadata.comment, commentAndMetadata.metadata, name); | |
5857 } | |
5858 | |
5859 /** | |
5860 * Parse an equality expression. Return the equality expression that was | |
5861 * parsed. | |
5862 * | |
5863 * equalityExpression ::= | |
5864 * relationalExpression (equalityOperator relationalExpression)? | |
5865 * | 'super' equalityOperator relationalExpression | |
5866 */ | |
5867 Expression _parseEqualityExpression() { | |
5868 Expression expression; | |
5869 if (_currentToken.keyword == Keyword.SUPER && | |
5870 _currentToken.next.type.isEqualityOperator) { | |
5871 expression = new SuperExpression(getAndAdvance()); | |
5872 } else { | |
5873 expression = parseRelationalExpression(); | |
5874 } | |
5875 bool leftEqualityExpression = false; | |
5876 while (_currentToken.type.isEqualityOperator) { | |
5877 if (leftEqualityExpression) { | |
5878 _reportErrorForNode( | |
5879 ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression); | |
5880 } | |
5881 expression = new BinaryExpression( | |
5882 expression, getAndAdvance(), parseRelationalExpression()); | |
5883 leftEqualityExpression = true; | |
5884 } | |
5885 return expression; | |
5886 } | |
5887 | |
5888 /** | |
5889 * Parse an export directive. The [commentAndMetadata] is the metadata to be | |
5890 * associated with the directive. Return the export directive that was parsed. | |
5891 * | |
5892 * This method assumes that the current token matches `Keyword.EXPORT`. | |
5893 * | |
5894 * exportDirective ::= | |
5895 * metadata 'export' stringLiteral configuration* combinator*';' | |
5896 */ | |
5897 ExportDirective _parseExportDirective(CommentAndMetadata commentAndMetadata) { | |
5898 Token exportKeyword = getAndAdvance(); | |
5899 StringLiteral libraryUri = _parseUri(); | |
5900 List<Configuration> configurations = _parseConfigurations(); | |
5901 List<Combinator> combinators = parseCombinators(); | |
5902 Token semicolon = _expect(TokenType.SEMICOLON); | |
5903 return new ExportDirective( | |
5904 commentAndMetadata.comment, | |
5905 commentAndMetadata.metadata, | |
5906 exportKeyword, | |
5907 libraryUri, | |
5908 configurations, | |
5909 combinators, | |
5910 semicolon); | |
5911 } | |
5912 | |
5913 /** | |
5914 * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be | |
5915 * `true`. The [kind] is the kind of parameter being expected based on the | |
5916 * presence or absence of group delimiters. Return the formal parameter that | |
5917 * was parsed. | |
5918 * | |
5919 * defaultFormalParameter ::= | |
5920 * normalFormalParameter ('=' expression)? | |
5921 * | |
5922 * defaultNamedParameter ::= | |
5923 * normalFormalParameter (':' expression)? | |
5924 */ | |
5925 FormalParameter _parseFormalParameter(ParameterKind kind) { | |
5926 NormalFormalParameter parameter = parseNormalFormalParameter(); | |
5927 TokenType type = _currentToken.type; | |
5928 if (type == TokenType.EQ) { | |
5929 Token separator = getAndAdvance(); | |
5930 Expression defaultValue = parseExpression2(); | |
5931 if (kind == ParameterKind.NAMED) { | |
5932 _reportErrorForToken( | |
5933 ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, separator); | |
5934 } else if (kind == ParameterKind.REQUIRED) { | |
5935 _reportErrorForNode( | |
5936 ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter); | |
5937 } | |
5938 return new DefaultFormalParameter( | |
5939 parameter, kind, separator, defaultValue); | |
5940 } else if (type == TokenType.COLON) { | |
5941 Token separator = getAndAdvance(); | |
5942 Expression defaultValue = parseExpression2(); | |
5943 if (kind == ParameterKind.POSITIONAL) { | |
5944 _reportErrorForToken( | |
5945 ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER, | |
5946 separator); | |
5947 } else if (kind == ParameterKind.REQUIRED) { | |
5948 _reportErrorForNode( | |
5949 ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter); | |
5950 } | |
5951 return new DefaultFormalParameter( | |
5952 parameter, kind, separator, defaultValue); | |
5953 } else if (kind != ParameterKind.REQUIRED) { | |
5954 return new DefaultFormalParameter(parameter, kind, null, null); | |
5955 } | |
5956 return parameter; | |
5957 } | |
5958 | |
5959 /** | |
5960 * Parse a list of formal parameters given that the list starts with the given | |
5961 * [leftParenthesis]. Return the formal parameters that were parsed. | |
5962 */ | |
5963 FormalParameterList _parseFormalParameterListAfterParen( | |
5964 Token leftParenthesis) { | |
5965 if (_matches(TokenType.CLOSE_PAREN)) { | |
5966 return new FormalParameterList( | |
5967 leftParenthesis, null, null, null, getAndAdvance()); | |
5968 } | |
5969 // | |
5970 // Even though it is invalid to have default parameters outside of brackets, | |
5971 // required parameters inside of brackets, or multiple groups of default and | |
5972 // named parameters, we allow all of these cases so that we can recover | |
5973 // better. | |
5974 // | |
5975 List<FormalParameter> parameters = <FormalParameter>[]; | |
5976 Token leftSquareBracket = null; | |
5977 Token rightSquareBracket = null; | |
5978 Token leftCurlyBracket = null; | |
5979 Token rightCurlyBracket = null; | |
5980 ParameterKind kind = ParameterKind.REQUIRED; | |
5981 bool firstParameter = true; | |
5982 bool reportedMultiplePositionalGroups = false; | |
5983 bool reportedMultipleNamedGroups = false; | |
5984 bool reportedMixedGroups = false; | |
5985 bool wasOptionalParameter = false; | |
5986 Token initialToken = null; | |
5987 do { | |
5988 if (firstParameter) { | |
5989 firstParameter = false; | |
5990 } else if (!_optional(TokenType.COMMA)) { | |
5991 // TODO(brianwilkerson) The token is wrong, we need to recover from this | |
5992 // case. | |
5993 if (_getEndToken(leftParenthesis) != null) { | |
5994 _reportErrorForCurrentToken( | |
5995 ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]); | |
5996 } else { | |
5997 _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS, | |
5998 _currentToken.previous); | |
5999 break; | |
6000 } | |
6001 } | |
6002 initialToken = _currentToken; | |
6003 // | |
6004 // Handle the beginning of parameter groups. | |
6005 // | |
6006 TokenType type = _currentToken.type; | |
6007 if (type == TokenType.OPEN_SQUARE_BRACKET) { | |
6008 wasOptionalParameter = true; | |
6009 if (leftSquareBracket != null && !reportedMultiplePositionalGroups) { | |
6010 _reportErrorForCurrentToken( | |
6011 ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS); | |
6012 reportedMultiplePositionalGroups = true; | |
6013 } | |
6014 if (leftCurlyBracket != null && !reportedMixedGroups) { | |
6015 _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS); | |
6016 reportedMixedGroups = true; | |
6017 } | |
6018 leftSquareBracket = getAndAdvance(); | |
6019 kind = ParameterKind.POSITIONAL; | |
6020 } else if (type == TokenType.OPEN_CURLY_BRACKET) { | |
6021 wasOptionalParameter = true; | |
6022 if (leftCurlyBracket != null && !reportedMultipleNamedGroups) { | |
6023 _reportErrorForCurrentToken( | |
6024 ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS); | |
6025 reportedMultipleNamedGroups = true; | |
6026 } | |
6027 if (leftSquareBracket != null && !reportedMixedGroups) { | |
6028 _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS); | |
6029 reportedMixedGroups = true; | |
6030 } | |
6031 leftCurlyBracket = getAndAdvance(); | |
6032 kind = ParameterKind.NAMED; | |
6033 } | |
6034 // | |
6035 // Parse and record the parameter. | |
6036 // | |
6037 FormalParameter parameter = _parseFormalParameter(kind); | |
6038 parameters.add(parameter); | |
6039 if (kind == ParameterKind.REQUIRED && wasOptionalParameter) { | |
6040 _reportErrorForNode( | |
6041 ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter); | |
6042 } | |
6043 // | |
6044 // Handle the end of parameter groups. | |
6045 // | |
6046 // TODO(brianwilkerson) Improve the detection and reporting of missing and | |
6047 // mismatched delimiters. | |
6048 type = _currentToken.type; | |
6049 | |
6050 // Advance past trailing commas as appropriate. | |
6051 if (type == TokenType.COMMA) { | |
6052 // Only parse commas trailing normal (non-positional/named) params. | |
6053 if (rightSquareBracket == null && rightCurlyBracket == null) { | |
6054 Token next = _peek(); | |
6055 if (next.type == TokenType.CLOSE_PAREN || | |
6056 next.type == TokenType.CLOSE_CURLY_BRACKET || | |
6057 next.type == TokenType.CLOSE_SQUARE_BRACKET) { | |
6058 _advance(); | |
6059 type = _currentToken.type; | |
6060 } | |
6061 } | |
6062 } | |
6063 | |
6064 if (type == TokenType.CLOSE_SQUARE_BRACKET) { | |
6065 rightSquareBracket = getAndAdvance(); | |
6066 if (leftSquareBracket == null) { | |
6067 if (leftCurlyBracket != null) { | |
6068 _reportErrorForCurrentToken( | |
6069 ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]); | |
6070 rightCurlyBracket = rightSquareBracket; | |
6071 rightSquareBracket = null; | |
6072 } else { | |
6073 _reportErrorForCurrentToken( | |
6074 ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, | |
6075 ["["]); | |
6076 } | |
6077 } | |
6078 kind = ParameterKind.REQUIRED; | |
6079 } else if (type == TokenType.CLOSE_CURLY_BRACKET) { | |
6080 rightCurlyBracket = getAndAdvance(); | |
6081 if (leftCurlyBracket == null) { | |
6082 if (leftSquareBracket != null) { | |
6083 _reportErrorForCurrentToken( | |
6084 ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]); | |
6085 rightSquareBracket = rightCurlyBracket; | |
6086 rightCurlyBracket = null; | |
6087 } else { | |
6088 _reportErrorForCurrentToken( | |
6089 ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, | |
6090 ["{"]); | |
6091 } | |
6092 } | |
6093 kind = ParameterKind.REQUIRED; | |
6094 } | |
6095 } while (!_matches(TokenType.CLOSE_PAREN) && | |
6096 !identical(initialToken, _currentToken)); | |
6097 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); | |
6098 // | |
6099 // Check that the groups were closed correctly. | |
6100 // | |
6101 if (leftSquareBracket != null && rightSquareBracket == null) { | |
6102 _reportErrorForCurrentToken( | |
6103 ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]); | |
6104 } | |
6105 if (leftCurlyBracket != null && rightCurlyBracket == null) { | |
6106 _reportErrorForCurrentToken( | |
6107 ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]); | |
6108 } | |
6109 // | |
6110 // Build the parameter list. | |
6111 // | |
6112 leftSquareBracket ??= leftCurlyBracket; | |
6113 rightSquareBracket ??= rightCurlyBracket; | |
6114 return new FormalParameterList(leftParenthesis, parameters, | |
6115 leftSquareBracket, rightSquareBracket, rightParenthesis); | |
6116 } | |
6117 | |
6118 /** | |
6119 * Parse a list of formal parameters. Return the formal parameters that were | |
6120 * parsed. | |
6121 * | |
6122 * This method assumes that the current token matches `TokenType.OPEN_PAREN`. | |
6123 */ | |
6124 FormalParameterList _parseFormalParameterListUnchecked() { | |
6125 return _parseFormalParameterListAfterParen(getAndAdvance()); | |
6126 } | |
6127 | |
6128 /** | |
6129 * Parse a function declaration. The [commentAndMetadata] is the documentation | |
6130 * comment and metadata to be associated with the declaration. The | |
6131 * [externalKeyword] is the 'external' keyword, or `null` if the function is | |
6132 * not external. The [returnType] is the return type, or `null` if there is no | |
6133 * return type. The [isStatement] is `true` if the function declaration is | |
6134 * being parsed as a statement. Return the function declaration that was | |
6135 * parsed. | |
6136 * | |
6137 * functionDeclaration ::= | |
6138 * functionSignature functionBody | |
6139 * | returnType? getOrSet identifier formalParameterList functionBody | |
6140 */ | |
6141 FunctionDeclaration _parseFunctionDeclaration( | |
6142 CommentAndMetadata commentAndMetadata, | |
6143 Token externalKeyword, | |
6144 TypeName returnType) { | |
6145 Token keywordToken = null; | |
6146 bool isGetter = false; | |
6147 Keyword keyword = _currentToken.keyword; | |
6148 SimpleIdentifier name = null; | |
6149 if (keyword == Keyword.GET) { | |
6150 keywordToken = getAndAdvance(); | |
6151 isGetter = true; | |
6152 } else if (keyword == Keyword.SET) { | |
6153 keywordToken = getAndAdvance(); | |
6154 } | |
6155 if (keywordToken != null && _matches(TokenType.OPEN_PAREN)) { | |
6156 name = new SimpleIdentifier(keywordToken, isDeclaration: true); | |
6157 keywordToken = null; | |
6158 isGetter = false; | |
6159 } else { | |
6160 name = parseSimpleIdentifier(isDeclaration: true); | |
6161 } | |
6162 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); | |
6163 FormalParameterList parameters = null; | |
6164 if (!isGetter) { | |
6165 if (_matches(TokenType.OPEN_PAREN)) { | |
6166 parameters = _parseFormalParameterListUnchecked(); | |
6167 _validateFormalParameterList(parameters); | |
6168 } else { | |
6169 _reportErrorForCurrentToken( | |
6170 ParserErrorCode.MISSING_FUNCTION_PARAMETERS); | |
6171 parameters = new FormalParameterList( | |
6172 _createSyntheticToken(TokenType.OPEN_PAREN), | |
6173 null, | |
6174 null, | |
6175 null, | |
6176 _createSyntheticToken(TokenType.CLOSE_PAREN)); | |
6177 } | |
6178 } else if (_matches(TokenType.OPEN_PAREN)) { | |
6179 _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS); | |
6180 _parseFormalParameterListUnchecked(); | |
6181 } | |
6182 FunctionBody body; | |
6183 if (externalKeyword == null) { | |
6184 body = parseFunctionBody( | |
6185 false, ParserErrorCode.MISSING_FUNCTION_BODY, false); | |
6186 } else { | |
6187 body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON)); | |
6188 } | |
6189 // if (!isStatement && matches(TokenType.SEMICOLON)) { | |
6190 // // TODO(brianwilkerson) Improve this error message. | |
6191 // reportError(ParserErrorCode.UNEXPECTED_TOKEN, currentToken.getLexeme
()); | |
6192 // advance(); | |
6193 // } | |
6194 return new FunctionDeclaration( | |
6195 commentAndMetadata.comment, | |
6196 commentAndMetadata.metadata, | |
6197 externalKeyword, | |
6198 returnType, | |
6199 keywordToken, | |
6200 name, | |
6201 new FunctionExpression(typeParameters, parameters, body)); | |
6202 } | |
6203 | |
6204 /** | |
6205 * Parse a function declaration statement. The [commentAndMetadata] is the | |
6206 * documentation comment and metadata to be associated with the declaration. | |
6207 * The [returnType] is the return type, or `null` if there is no return type. | |
6208 * Return the function declaration statement that was parsed. | |
6209 * | |
6210 * functionDeclarationStatement ::= | |
6211 * functionSignature functionBody | |
6212 */ | |
6213 Statement _parseFunctionDeclarationStatementAfterReturnType( | |
6214 CommentAndMetadata commentAndMetadata, TypeName returnType) { | |
6215 FunctionDeclaration declaration = | |
6216 _parseFunctionDeclaration(commentAndMetadata, null, returnType); | |
6217 Token propertyKeyword = declaration.propertyKeyword; | |
6218 if (propertyKeyword != null) { | |
6219 if (propertyKeyword.keyword == Keyword.GET) { | |
6220 _reportErrorForToken( | |
6221 ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword); | |
6222 } else { | |
6223 _reportErrorForToken( | |
6224 ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword); | |
6225 } | |
6226 } | |
6227 return new FunctionDeclarationStatement(declaration); | |
6228 } | |
6229 | |
6230 /** | |
6231 * Parse a function type alias. The [commentAndMetadata] is the metadata to be | |
6232 * associated with the member. The [keyword] is the token representing the | |
6233 * 'typedef' keyword. Return the function type alias that was parsed. | |
6234 * | |
6235 * functionTypeAlias ::= | |
6236 * functionPrefix typeParameterList? formalParameterList ';' | |
6237 * | |
6238 * functionPrefix ::= | |
6239 * returnType? name | |
6240 */ | |
6241 FunctionTypeAlias _parseFunctionTypeAlias( | |
6242 CommentAndMetadata commentAndMetadata, Token keyword) { | |
6243 TypeName returnType = null; | |
6244 if (hasReturnTypeInTypeAlias) { | |
6245 returnType = parseReturnType(); | |
6246 } | |
6247 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); | |
6248 TypeParameterList typeParameters = null; | |
6249 if (_matches(TokenType.LT)) { | |
6250 typeParameters = parseTypeParameterList(); | |
6251 } | |
6252 TokenType type = _currentToken.type; | |
6253 if (type == TokenType.SEMICOLON || type == TokenType.EOF) { | |
6254 _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS); | |
6255 FormalParameterList parameters = new FormalParameterList( | |
6256 _createSyntheticToken(TokenType.OPEN_PAREN), | |
6257 null, | |
6258 null, | |
6259 null, | |
6260 _createSyntheticToken(TokenType.CLOSE_PAREN)); | |
6261 Token semicolon = _expect(TokenType.SEMICOLON); | |
6262 return new FunctionTypeAlias( | |
6263 commentAndMetadata.comment, | |
6264 commentAndMetadata.metadata, | |
6265 keyword, | |
6266 returnType, | |
6267 name, | |
6268 typeParameters, | |
6269 parameters, | |
6270 semicolon); | |
6271 } else if (type == TokenType.OPEN_PAREN) { | |
6272 FormalParameterList parameters = _parseFormalParameterListUnchecked(); | |
6273 _validateFormalParameterList(parameters); | |
6274 Token semicolon = _expect(TokenType.SEMICOLON); | |
6275 return new FunctionTypeAlias( | |
6276 commentAndMetadata.comment, | |
6277 commentAndMetadata.metadata, | |
6278 keyword, | |
6279 returnType, | |
6280 name, | |
6281 typeParameters, | |
6282 parameters, | |
6283 semicolon); | |
6284 } else { | |
6285 _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS); | |
6286 // Recovery: At the very least we should skip to the start of the next | |
6287 // valid compilation unit member, allowing for the possibility of finding | |
6288 // the typedef parameters before that point. | |
6289 return new FunctionTypeAlias( | |
6290 commentAndMetadata.comment, | |
6291 commentAndMetadata.metadata, | |
6292 keyword, | |
6293 returnType, | |
6294 name, | |
6295 typeParameters, | |
6296 new FormalParameterList(_createSyntheticToken(TokenType.OPEN_PAREN), | |
6297 null, null, null, _createSyntheticToken(TokenType.CLOSE_PAREN)), | |
6298 _createSyntheticToken(TokenType.SEMICOLON)); | |
6299 } | |
6300 } | |
6301 | |
6302 /** | |
6303 * Parses generic type parameters from a comment. | |
6304 * | |
6305 * Normally this is handled by [_parseGenericMethodTypeParameters], but if the | |
6306 * code already handles the normal generic type parameters, the comment | |
6307 * matcher can be called directly. For example, we may have already tried | |
6308 * matching `<` (less than sign) in a method declaration, and be currently | |
6309 * on the `(` (open paren) because we didn't find it. In that case, this | |
6310 * function will parse the preceding comment such as `/*<T, R>*/`. | |
6311 */ | |
6312 TypeParameterList _parseGenericCommentTypeParameters() { | |
6313 if (_injectGenericCommentTypeList()) { | |
6314 return parseTypeParameterList(); | |
6315 } | |
6316 return null; | |
6317 } | |
6318 | |
6319 /** | |
6320 * Parse the generic method or function's type parameters. | |
6321 * | |
6322 * For backwards compatibility this can optionally use comments. | |
6323 * See [parseGenericMethodComments]. | |
6324 */ | |
6325 TypeParameterList _parseGenericMethodTypeParameters() { | |
6326 if (parseGenericMethods && _matches(TokenType.LT) || | |
6327 _injectGenericCommentTypeList()) { | |
6328 return parseTypeParameterList(); | |
6329 } | |
6330 return null; | |
6331 } | |
6332 | |
6333 /** | |
6334 * Parse a getter. The [commentAndMetadata] is the documentation comment and | |
6335 * metadata to be associated with the declaration. The externalKeyword] is the | |
6336 * 'external' token. The staticKeyword] is the static keyword, or `null` if | |
6337 * the getter is not static. The [returnType] the return type that has already | |
6338 * been parsed, or `null` if there was no return type. Return the getter that | |
6339 * was parsed. | |
6340 * | |
6341 * This method assumes that the current token matches `Keyword.GET`. | |
6342 * | |
6343 * getter ::= | |
6344 * getterSignature functionBody? | |
6345 * | |
6346 * getterSignature ::= | |
6347 * 'external'? 'static'? returnType? 'get' identifier | |
6348 */ | |
6349 MethodDeclaration _parseGetter(CommentAndMetadata commentAndMetadata, | |
6350 Token externalKeyword, Token staticKeyword, TypeName returnType) { | |
6351 Token propertyKeyword = getAndAdvance(); | |
6352 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); | |
6353 if (_matches(TokenType.OPEN_PAREN) && | |
6354 _tokenMatches(_peek(), TokenType.CLOSE_PAREN)) { | |
6355 _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS); | |
6356 _advance(); | |
6357 _advance(); | |
6358 } | |
6359 FunctionBody body = parseFunctionBody( | |
6360 externalKeyword != null || staticKeyword == null, | |
6361 ParserErrorCode.STATIC_GETTER_WITHOUT_BODY, | |
6362 false); | |
6363 if (externalKeyword != null && body is! EmptyFunctionBody) { | |
6364 _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_GETTER_WITH_BODY); | |
6365 } | |
6366 return new MethodDeclaration( | |
6367 commentAndMetadata.comment, | |
6368 commentAndMetadata.metadata, | |
6369 externalKeyword, | |
6370 staticKeyword, | |
6371 returnType, | |
6372 propertyKeyword, | |
6373 null, | |
6374 name, | |
6375 null, | |
6376 null, | |
6377 body); | |
6378 } | |
6379 | |
6380 /** | |
6381 * Parse a list of identifiers. Return the list of identifiers that were | |
6382 * parsed. | |
6383 * | |
6384 * identifierList ::= | |
6385 * identifier (',' identifier)* | |
6386 */ | |
6387 List<SimpleIdentifier> _parseIdentifierList() { | |
6388 List<SimpleIdentifier> identifiers = <SimpleIdentifier>[ | |
6389 parseSimpleIdentifier() | |
6390 ]; | |
6391 while (_optional(TokenType.COMMA)) { | |
6392 identifiers.add(parseSimpleIdentifier()); | |
6393 } | |
6394 return identifiers; | |
6395 } | |
6396 | |
6397 /** | |
6398 * Parse an import directive. The [commentAndMetadata] is the metadata to be | |
6399 * associated with the directive. Return the import directive that was parsed. | |
6400 * | |
6401 * This method assumes that the current token matches `Keyword.IMPORT`. | |
6402 * | |
6403 * importDirective ::= | |
6404 * metadata 'import' stringLiteral configuration* (deferred)? ('as' id
entifier)? combinator*';' | |
6405 */ | |
6406 ImportDirective _parseImportDirective(CommentAndMetadata commentAndMetadata) { | |
6407 Token importKeyword = getAndAdvance(); | |
6408 StringLiteral libraryUri = _parseUri(); | |
6409 List<Configuration> configurations = _parseConfigurations(); | |
6410 Token deferredToken = null; | |
6411 Token asToken = null; | |
6412 SimpleIdentifier prefix = null; | |
6413 if (_matchesKeyword(Keyword.DEFERRED)) { | |
6414 deferredToken = getAndAdvance(); | |
6415 } | |
6416 if (_matchesKeyword(Keyword.AS)) { | |
6417 asToken = getAndAdvance(); | |
6418 prefix = parseSimpleIdentifier(isDeclaration: true); | |
6419 } else if (deferredToken != null) { | |
6420 _reportErrorForCurrentToken( | |
6421 ParserErrorCode.MISSING_PREFIX_IN_DEFERRED_IMPORT); | |
6422 } else if (!_matches(TokenType.SEMICOLON) && | |
6423 !_matchesString(_SHOW) && | |
6424 !_matchesString(_HIDE)) { | |
6425 Token nextToken = _peek(); | |
6426 if (_tokenMatchesKeyword(nextToken, Keyword.AS) || | |
6427 _tokenMatchesString(nextToken, _SHOW) || | |
6428 _tokenMatchesString(nextToken, _HIDE)) { | |
6429 _reportErrorForCurrentToken( | |
6430 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken]); | |
6431 _advance(); | |
6432 if (_matchesKeyword(Keyword.AS)) { | |
6433 asToken = getAndAdvance(); | |
6434 prefix = parseSimpleIdentifier(isDeclaration: true); | |
6435 } | |
6436 } | |
6437 } | |
6438 List<Combinator> combinators = parseCombinators(); | |
6439 Token semicolon = _expect(TokenType.SEMICOLON); | |
6440 return new ImportDirective( | |
6441 commentAndMetadata.comment, | |
6442 commentAndMetadata.metadata, | |
6443 importKeyword, | |
6444 libraryUri, | |
6445 configurations, | |
6446 deferredToken, | |
6447 asToken, | |
6448 prefix, | |
6449 combinators, | |
6450 semicolon); | |
6451 } | |
6452 | |
6453 /** | |
6454 * Parse a list of initialized identifiers. The [commentAndMetadata] is the | |
6455 * documentation comment and metadata to be associated with the declaration. | |
6456 * The [staticKeyword] is the static keyword, or `null` if the getter is not | |
6457 * static. The [keyword] is the token representing the 'final', 'const' or | |
6458 * 'var' keyword, or `null` if there is no keyword. The [type] is the type | |
6459 * that has already been parsed, or `null` if 'var' was provided. Return the | |
6460 * getter that was parsed. | |
6461 * | |
6462 * ?? ::= | |
6463 * 'static'? ('var' | type) initializedIdentifierList ';' | |
6464 * | 'final' type? initializedIdentifierList ';' | |
6465 * | |
6466 * initializedIdentifierList ::= | |
6467 * initializedIdentifier (',' initializedIdentifier)* | |
6468 * | |
6469 * initializedIdentifier ::= | |
6470 * identifier ('=' expression)? | |
6471 */ | |
6472 FieldDeclaration _parseInitializedIdentifierList( | |
6473 CommentAndMetadata commentAndMetadata, | |
6474 Token staticKeyword, | |
6475 Token keyword, | |
6476 TypeName type) { | |
6477 VariableDeclarationList fieldList = | |
6478 parseVariableDeclarationListAfterType(null, keyword, type); | |
6479 return new FieldDeclaration( | |
6480 commentAndMetadata.comment, | |
6481 commentAndMetadata.metadata, | |
6482 staticKeyword, | |
6483 fieldList, | |
6484 _expect(TokenType.SEMICOLON)); | |
6485 } | |
6486 | |
6487 /** | |
6488 * Parse an instance creation expression. The [keyword] is the 'new' or | |
6489 * 'const' keyword that introduces the expression. Return the instance | |
6490 * creation expression that was parsed. | |
6491 * | |
6492 * instanceCreationExpression ::= | |
6493 * ('new' | 'const') type ('.' identifier)? argumentList | |
6494 */ | |
6495 InstanceCreationExpression _parseInstanceCreationExpression(Token keyword) { | |
6496 ConstructorName constructorName = parseConstructorName(); | |
6497 ArgumentList argumentList = _parseArgumentListChecked(); | |
6498 return new InstanceCreationExpression( | |
6499 keyword, constructorName, argumentList); | |
6500 } | |
6501 | |
6502 /** | |
6503 * Parse a library directive. The [commentAndMetadata] is the metadata to be | |
6504 * associated with the directive. Return the library directive that was | |
6505 * parsed. | |
6506 * | |
6507 * This method assumes that the current token matches `Keyword.LIBRARY`. | |
6508 * | |
6509 * libraryDirective ::= | |
6510 * metadata 'library' identifier ';' | |
6511 */ | |
6512 LibraryDirective _parseLibraryDirective( | |
6513 CommentAndMetadata commentAndMetadata) { | |
6514 Token keyword = getAndAdvance(); | |
6515 LibraryIdentifier libraryName = _parseLibraryName( | |
6516 ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword); | |
6517 Token semicolon = _expect(TokenType.SEMICOLON); | |
6518 return new LibraryDirective(commentAndMetadata.comment, | |
6519 commentAndMetadata.metadata, keyword, libraryName, semicolon); | |
6520 } | |
6521 | |
6522 /** | |
6523 * Parse a library name. The [missingNameError] is the error code to be used | |
6524 * if the library name is missing. The [missingNameToken] is the token | |
6525 * associated with the error produced if the library name is missing. Return | |
6526 * the library name that was parsed. | |
6527 * | |
6528 * libraryName ::= | |
6529 * libraryIdentifier | |
6530 */ | |
6531 LibraryIdentifier _parseLibraryName( | |
6532 ParserErrorCode missingNameError, Token missingNameToken) { | |
6533 if (_matchesIdentifier()) { | |
6534 return parseLibraryIdentifier(); | |
6535 } else if (_matches(TokenType.STRING)) { | |
6536 // Recovery: This should be extended to handle arbitrary tokens until we | |
6537 // can find a token that can start a compilation unit member. | |
6538 StringLiteral string = parseStringLiteral(); | |
6539 _reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string); | |
6540 } else { | |
6541 _reportErrorForToken(missingNameError, missingNameToken); | |
6542 } | |
6543 return new LibraryIdentifier( | |
6544 <SimpleIdentifier>[createSyntheticIdentifier()]); | |
6545 } | |
6546 | |
6547 /** | |
6548 * Parse a list literal. The [modifier] is the 'const' modifier appearing | |
6549 * before the literal, or `null` if there is no modifier. The [typeArguments] | |
6550 * is the type arguments appearing before the literal, or `null` if there are | |
6551 * no type arguments. Return the list literal that was parsed. | |
6552 * | |
6553 * This method assumes that the current token matches either | |
6554 * `TokenType.OPEN_SQUARE_BRACKET` or `TokenType.INDEX`. | |
6555 * | |
6556 * listLiteral ::= | |
6557 * 'const'? typeArguments? '[' (expressionList ','?)? ']' | |
6558 */ | |
6559 ListLiteral _parseListLiteral( | |
6560 Token modifier, TypeArgumentList typeArguments) { | |
6561 if (_matches(TokenType.INDEX)) { | |
6562 // Split the token into two separate tokens. | |
6563 BeginToken leftBracket = _createToken( | |
6564 _currentToken, TokenType.OPEN_SQUARE_BRACKET, | |
6565 isBegin: true); | |
6566 Token rightBracket = | |
6567 new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1); | |
6568 leftBracket.endToken = rightBracket; | |
6569 rightBracket.setNext(_currentToken.next); | |
6570 leftBracket.setNext(rightBracket); | |
6571 _currentToken.previous.setNext(leftBracket); | |
6572 _currentToken = _currentToken.next; | |
6573 return new ListLiteral( | |
6574 modifier, typeArguments, leftBracket, null, rightBracket); | |
6575 } | |
6576 Token leftBracket = getAndAdvance(); | |
6577 if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) { | |
6578 return new ListLiteral( | |
6579 modifier, typeArguments, leftBracket, null, getAndAdvance()); | |
6580 } | |
6581 bool wasInInitializer = _inInitializer; | |
6582 _inInitializer = false; | |
6583 try { | |
6584 List<Expression> elements = <Expression>[parseExpression2()]; | |
6585 while (_optional(TokenType.COMMA)) { | |
6586 if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) { | |
6587 return new ListLiteral( | |
6588 modifier, typeArguments, leftBracket, elements, getAndAdvance()); | |
6589 } | |
6590 elements.add(parseExpression2()); | |
6591 } | |
6592 Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET); | |
6593 return new ListLiteral( | |
6594 modifier, typeArguments, leftBracket, elements, rightBracket); | |
6595 } finally { | |
6596 _inInitializer = wasInInitializer; | |
6597 } | |
6598 } | |
6599 | |
6600 /** | |
6601 * Parse a logical and expression. Return the logical and expression that was | |
6602 * parsed. | |
6603 * | |
6604 * logicalAndExpression ::= | |
6605 * equalityExpression ('&&' equalityExpression)* | |
6606 */ | |
6607 Expression _parseLogicalAndExpression() { | |
6608 Expression expression = _parseEqualityExpression(); | |
6609 while (_currentToken.type == TokenType.AMPERSAND_AMPERSAND) { | |
6610 expression = new BinaryExpression( | |
6611 expression, getAndAdvance(), _parseEqualityExpression()); | |
6612 } | |
6613 return expression; | |
6614 } | |
6615 | |
6616 /** | |
6617 * Parse a map literal. The [modifier] is the 'const' modifier appearing | |
6618 * before the literal, or `null` if there is no modifier. The [typeArguments] | |
6619 * is the type arguments that were declared, or `null` if there are no type | |
6620 * arguments. Return the map literal that was parsed. | |
6621 * | |
6622 * This method assumes that the current token matches | |
6623 * `TokenType.OPEN_CURLY_BRACKET`. | |
6624 * | |
6625 * mapLiteral ::= | |
6626 * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)*
','?)? '}' | |
6627 */ | |
6628 MapLiteral _parseMapLiteral(Token modifier, TypeArgumentList typeArguments) { | |
6629 Token leftBracket = getAndAdvance(); | |
6630 if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { | |
6631 return new MapLiteral( | |
6632 modifier, typeArguments, leftBracket, null, getAndAdvance()); | |
6633 } | |
6634 bool wasInInitializer = _inInitializer; | |
6635 _inInitializer = false; | |
6636 try { | |
6637 List<MapLiteralEntry> entries = <MapLiteralEntry>[parseMapLiteralEntry()]; | |
6638 while (_optional(TokenType.COMMA)) { | |
6639 if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { | |
6640 return new MapLiteral( | |
6641 modifier, typeArguments, leftBracket, entries, getAndAdvance()); | |
6642 } | |
6643 entries.add(parseMapLiteralEntry()); | |
6644 } | |
6645 Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); | |
6646 return new MapLiteral( | |
6647 modifier, typeArguments, leftBracket, entries, rightBracket); | |
6648 } finally { | |
6649 _inInitializer = wasInInitializer; | |
6650 } | |
6651 } | |
6652 | |
6653 /** | |
6654 * Parse a method declaration. The [commentAndMetadata] is the documentation | |
6655 * comment and metadata to be associated with the declaration. The | |
6656 * [externalKeyword] is the 'external' token. The [staticKeyword] is the | |
6657 * static keyword, or `null` if the getter is not static. The [returnType] is | |
6658 * the return type of the method. The [name] is the name of the method. The | |
6659 * [parameters] is the parameters to the method. Return the method declaration | |
6660 * that was parsed. | |
6661 * | |
6662 * functionDeclaration ::= | |
6663 * ('external' 'static'?)? functionSignature functionBody | |
6664 * | 'external'? functionSignature ';' | |
6665 */ | |
6666 MethodDeclaration _parseMethodDeclarationAfterParameters( | |
6667 CommentAndMetadata commentAndMetadata, | |
6668 Token externalKeyword, | |
6669 Token staticKeyword, | |
6670 TypeName returnType, | |
6671 SimpleIdentifier name, | |
6672 TypeParameterList typeParameters, | |
6673 FormalParameterList parameters) { | |
6674 FunctionBody body = parseFunctionBody( | |
6675 externalKeyword != null || staticKeyword == null, | |
6676 ParserErrorCode.MISSING_FUNCTION_BODY, | |
6677 false); | |
6678 if (externalKeyword != null) { | |
6679 if (body is! EmptyFunctionBody) { | |
6680 _reportErrorForNode(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body); | |
6681 } | |
6682 } else if (staticKeyword != null) { | |
6683 if (body is EmptyFunctionBody && _parseFunctionBodies) { | |
6684 _reportErrorForNode(ParserErrorCode.ABSTRACT_STATIC_METHOD, body); | |
6685 } | |
6686 } | |
6687 return new MethodDeclaration( | |
6688 commentAndMetadata.comment, | |
6689 commentAndMetadata.metadata, | |
6690 externalKeyword, | |
6691 staticKeyword, | |
6692 returnType, | |
6693 null, | |
6694 null, | |
6695 name, | |
6696 typeParameters, | |
6697 parameters, | |
6698 body); | |
6699 } | |
6700 | |
6701 /** | |
6702 * Parse a method declaration. The [commentAndMetadata] is the documentation | |
6703 * comment and metadata to be associated with the declaration. The | |
6704 * [externalKeyword] is the 'external' token. The [staticKeyword] is the | |
6705 * static keyword, or `null` if the getter is not static. The [returnType] is | |
6706 * the return type of the method. Return the method declaration that was | |
6707 * parsed. | |
6708 * | |
6709 * functionDeclaration ::= | |
6710 * 'external'? 'static'? functionSignature functionBody | |
6711 * | 'external'? functionSignature ';' | |
6712 */ | |
6713 MethodDeclaration _parseMethodDeclarationAfterReturnType( | |
6714 CommentAndMetadata commentAndMetadata, | |
6715 Token externalKeyword, | |
6716 Token staticKeyword, | |
6717 TypeName returnType) { | |
6718 SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true); | |
6719 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); | |
6720 FormalParameterList parameters; | |
6721 TokenType type = _currentToken.type; | |
6722 // TODO(brianwilkerson) Figure out why we care what the current token is if | |
6723 // it isn't a paren. | |
6724 if (type != TokenType.OPEN_PAREN && | |
6725 (type == TokenType.OPEN_CURLY_BRACKET || type == TokenType.FUNCTION)) { | |
6726 _reportErrorForToken( | |
6727 ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous); | |
6728 parameters = new FormalParameterList( | |
6729 _createSyntheticToken(TokenType.OPEN_PAREN), | |
6730 null, | |
6731 null, | |
6732 null, | |
6733 _createSyntheticToken(TokenType.CLOSE_PAREN)); | |
6734 } else { | |
6735 parameters = parseFormalParameterList(); | |
6736 } | |
6737 _validateFormalParameterList(parameters); | |
6738 return _parseMethodDeclarationAfterParameters( | |
6739 commentAndMetadata, | |
6740 externalKeyword, | |
6741 staticKeyword, | |
6742 returnType, | |
6743 methodName, | |
6744 typeParameters, | |
6745 parameters); | |
6746 } | |
6747 | |
6748 /** | |
6749 * Parse the modifiers preceding a declaration. This method allows the | 3700 * Parse the modifiers preceding a declaration. This method allows the |
6750 * modifiers to appear in any order but does generate errors for duplicated | 3701 * modifiers to appear in any order but does generate errors for duplicated |
6751 * modifiers. Checks for other problems, such as having the modifiers appear | 3702 * modifiers. Checks for other problems, such as having the modifiers appear |
6752 * in the wrong order or specifying both 'const' and 'final', are reported in | 3703 * in the wrong order or specifying both 'const' and 'final', are reported in |
6753 * one of the methods whose name is prefixed with `validateModifiersFor`. | 3704 * one of the methods whose name is prefixed with `validateModifiersFor`. |
6754 * Return the modifiers that were parsed. | 3705 * Return the modifiers that were parsed. |
6755 * | 3706 * |
6756 * modifiers ::= | 3707 * modifiers ::= |
6757 * ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static'
| 'var')* | 3708 * ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static'
| 'var')* |
6758 */ | 3709 */ |
6759 Modifiers _parseModifiers() { | 3710 Modifiers parseModifiers() { |
6760 Modifiers modifiers = new Modifiers(); | 3711 Modifiers modifiers = new Modifiers(); |
6761 bool progress = true; | 3712 bool progress = true; |
6762 while (progress) { | 3713 while (progress) { |
6763 TokenType nextType = _peek().type; | 3714 TokenType nextType = _peek().type; |
6764 if (nextType == TokenType.PERIOD || | 3715 if (nextType == TokenType.PERIOD || |
6765 nextType == TokenType.LT || | 3716 nextType == TokenType.LT || |
6766 nextType == TokenType.OPEN_PAREN) { | 3717 nextType == TokenType.OPEN_PAREN) { |
6767 return modifiers; | 3718 return modifiers; |
6768 } | 3719 } |
6769 Keyword keyword = _currentToken.keyword; | 3720 Keyword keyword = _currentToken.keyword; |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6824 modifiers.varKeyword = getAndAdvance(); | 3775 modifiers.varKeyword = getAndAdvance(); |
6825 } | 3776 } |
6826 } else { | 3777 } else { |
6827 progress = false; | 3778 progress = false; |
6828 } | 3779 } |
6829 } | 3780 } |
6830 return modifiers; | 3781 return modifiers; |
6831 } | 3782 } |
6832 | 3783 |
6833 /** | 3784 /** |
6834 * Parse a class native clause. Return the native clause that was parsed. | 3785 * Parse a multiplicative expression. Return the multiplicative expression |
6835 * | 3786 * that was parsed. |
6836 * This method assumes that the current token matches `_NATIVE`. | 3787 * |
6837 * | 3788 * multiplicativeExpression ::= |
6838 * classNativeClause ::= | 3789 * unaryExpression (multiplicativeOperator unaryExpression)* |
6839 * 'native' name | 3790 * | 'super' (multiplicativeOperator unaryExpression)+ |
6840 */ | 3791 */ |
6841 NativeClause _parseNativeClause() { | 3792 Expression parseMultiplicativeExpression() { |
6842 Token keyword = getAndAdvance(); | 3793 Expression expression; |
6843 StringLiteral name = parseStringLiteral(); | 3794 if (_currentToken.keyword == Keyword.SUPER && |
6844 return new NativeClause(keyword, name); | 3795 _currentToken.next.type.isMultiplicativeOperator) { |
| 3796 expression = new SuperExpression(getAndAdvance()); |
| 3797 } else { |
| 3798 expression = parseUnaryExpression(); |
| 3799 } |
| 3800 while (_currentToken.type.isMultiplicativeOperator) { |
| 3801 expression = new BinaryExpression( |
| 3802 expression, getAndAdvance(), parseUnaryExpression()); |
| 3803 } |
| 3804 return expression; |
6845 } | 3805 } |
6846 | 3806 |
6847 /** | 3807 /** |
6848 * Parse a new expression. Return the new expression that was parsed. | 3808 * Parse a new expression. Return the new expression that was parsed. |
6849 * | 3809 * |
6850 * This method assumes that the current token matches `Keyword.NEW`. | 3810 * This method assumes that the current token matches `Keyword.NEW`. |
6851 * | 3811 * |
6852 * newExpression ::= | 3812 * newExpression ::= |
6853 * instanceCreationExpression | 3813 * instanceCreationExpression |
6854 */ | 3814 */ |
6855 InstanceCreationExpression _parseNewExpression() => | 3815 InstanceCreationExpression parseNewExpression() => |
6856 _parseInstanceCreationExpression(getAndAdvance()); | 3816 parseInstanceCreationExpression(getAndAdvance()); |
6857 | 3817 |
6858 /** | 3818 /** |
6859 * Parse a non-labeled statement. Return the non-labeled statement that was | 3819 * Parse a non-labeled statement. Return the non-labeled statement that was |
6860 * parsed. | 3820 * parsed. |
6861 * | 3821 * |
6862 * nonLabeledStatement ::= | 3822 * nonLabeledStatement ::= |
6863 * block | 3823 * block |
6864 * | assertStatement | 3824 * | assertStatement |
6865 * | breakStatement | 3825 * | breakStatement |
6866 * | continueStatement | 3826 * | continueStatement |
6867 * | doStatement | 3827 * | doStatement |
6868 * | forStatement | 3828 * | forStatement |
6869 * | ifStatement | 3829 * | ifStatement |
6870 * | returnStatement | 3830 * | returnStatement |
6871 * | switchStatement | 3831 * | switchStatement |
6872 * | tryStatement | 3832 * | tryStatement |
6873 * | whileStatement | 3833 * | whileStatement |
6874 * | variableDeclarationList ';' | 3834 * | variableDeclarationList ';' |
6875 * | expressionStatement | 3835 * | expressionStatement |
6876 * | functionSignature functionBody | 3836 * | functionSignature functionBody |
6877 */ | 3837 */ |
6878 Statement _parseNonLabeledStatement() { | 3838 Statement parseNonLabeledStatement() { |
6879 // TODO(brianwilkerson) Pass the comment and metadata on where appropriate. | 3839 // TODO(brianwilkerson) Pass the comment and metadata on where appropriate. |
6880 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); | 3840 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); |
6881 TokenType type = _currentToken.type; | 3841 TokenType type = _currentToken.type; |
6882 if (type == TokenType.OPEN_CURLY_BRACKET) { | 3842 if (type == TokenType.OPEN_CURLY_BRACKET) { |
6883 if (_tokenMatches(_peek(), TokenType.STRING)) { | 3843 if (_tokenMatches(_peek(), TokenType.STRING)) { |
6884 Token afterString = skipStringLiteral(_currentToken.next); | 3844 Token afterString = skipStringLiteral(_currentToken.next); |
6885 if (afterString != null && afterString.type == TokenType.COLON) { | 3845 if (afterString != null && afterString.type == TokenType.COLON) { |
6886 return new ExpressionStatement( | 3846 return new ExpressionStatement( |
6887 parseExpression2(), _expect(TokenType.SEMICOLON)); | 3847 parseExpression2(), _expect(TokenType.SEMICOLON)); |
6888 } | 3848 } |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7029 } else if (type == TokenType.CLOSE_CURLY_BRACKET) { | 3989 } else if (type == TokenType.CLOSE_CURLY_BRACKET) { |
7030 _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT); | 3990 _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT); |
7031 return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON)); | 3991 return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON)); |
7032 } else { | 3992 } else { |
7033 return new ExpressionStatement( | 3993 return new ExpressionStatement( |
7034 parseExpression2(), _expect(TokenType.SEMICOLON)); | 3994 parseExpression2(), _expect(TokenType.SEMICOLON)); |
7035 } | 3995 } |
7036 } | 3996 } |
7037 | 3997 |
7038 /** | 3998 /** |
| 3999 * Parse a normal formal parameter. Return the normal formal parameter that |
| 4000 * was parsed. |
| 4001 * |
| 4002 * normalFormalParameter ::= |
| 4003 * functionSignature |
| 4004 * | fieldFormalParameter |
| 4005 * | simpleFormalParameter |
| 4006 * |
| 4007 * functionSignature: |
| 4008 * metadata returnType? identifier typeParameters? formalParameterList |
| 4009 * |
| 4010 * fieldFormalParameter ::= |
| 4011 * metadata finalConstVarOrType? 'this' '.' identifier |
| 4012 * |
| 4013 * simpleFormalParameter ::= |
| 4014 * declaredIdentifier |
| 4015 * | metadata identifier |
| 4016 */ |
| 4017 NormalFormalParameter parseNormalFormalParameter() { |
| 4018 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); |
| 4019 FinalConstVarOrType holder = parseFinalConstVarOrType(true); |
| 4020 Token thisKeyword = null; |
| 4021 Token period = null; |
| 4022 if (_matchesKeyword(Keyword.THIS)) { |
| 4023 thisKeyword = getAndAdvance(); |
| 4024 period = _expect(TokenType.PERIOD); |
| 4025 } |
| 4026 SimpleIdentifier identifier = parseSimpleIdentifier(); |
| 4027 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); |
| 4028 if (_matches(TokenType.OPEN_PAREN)) { |
| 4029 FormalParameterList parameters = _parseFormalParameterListUnchecked(); |
| 4030 if (thisKeyword == null) { |
| 4031 if (holder.keyword != null) { |
| 4032 _reportErrorForToken( |
| 4033 ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword); |
| 4034 } |
| 4035 Token question = null; |
| 4036 if (enableNnbd && _matches(TokenType.QUESTION)) { |
| 4037 question = getAndAdvance(); |
| 4038 } |
| 4039 return new FunctionTypedFormalParameter( |
| 4040 commentAndMetadata.comment, |
| 4041 commentAndMetadata.metadata, |
| 4042 holder.type, |
| 4043 new SimpleIdentifier(identifier.token, isDeclaration: true), |
| 4044 typeParameters, |
| 4045 parameters, |
| 4046 question: question); |
| 4047 } else { |
| 4048 return new FieldFormalParameter( |
| 4049 commentAndMetadata.comment, |
| 4050 commentAndMetadata.metadata, |
| 4051 holder.keyword, |
| 4052 holder.type, |
| 4053 thisKeyword, |
| 4054 period, |
| 4055 identifier, |
| 4056 typeParameters, |
| 4057 parameters); |
| 4058 } |
| 4059 } else if (typeParameters != null) { |
| 4060 // TODO(brianwilkerson) Report an error. It looks like a function-typed |
| 4061 // parameter with no parameter list. |
| 4062 //_reportErrorForToken(ParserErrorCode.MISSING_PARAMETERS, typeParameters.
endToken); |
| 4063 } |
| 4064 TypeName type = holder.type; |
| 4065 if (type != null) { |
| 4066 if (_tokenMatchesKeyword(type.name.beginToken, Keyword.VOID)) { |
| 4067 _reportErrorForToken( |
| 4068 ParserErrorCode.VOID_PARAMETER, type.name.beginToken); |
| 4069 } else if (holder.keyword != null && |
| 4070 _tokenMatchesKeyword(holder.keyword, Keyword.VAR)) { |
| 4071 _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, holder.keyword); |
| 4072 } |
| 4073 } |
| 4074 if (thisKeyword != null) { |
| 4075 // TODO(brianwilkerson) If there are type parameters but no parameters, |
| 4076 // should we create a synthetic empty parameter list here so we can |
| 4077 // capture the type parameters? |
| 4078 return new FieldFormalParameter( |
| 4079 commentAndMetadata.comment, |
| 4080 commentAndMetadata.metadata, |
| 4081 holder.keyword, |
| 4082 holder.type, |
| 4083 thisKeyword, |
| 4084 period, |
| 4085 identifier, |
| 4086 null, |
| 4087 null); |
| 4088 } |
| 4089 return new SimpleFormalParameter( |
| 4090 commentAndMetadata.comment, |
| 4091 commentAndMetadata.metadata, |
| 4092 holder.keyword, |
| 4093 holder.type, |
| 4094 new SimpleIdentifier(identifier.token, isDeclaration: true)); |
| 4095 } |
| 4096 |
| 4097 /** |
| 4098 * Parse an operator declaration. The [commentAndMetadata] is the |
| 4099 * documentation comment and metadata to be associated with the declaration. |
| 4100 * The [externalKeyword] is the 'external' token. The [returnType] is the |
| 4101 * return type that has already been parsed, or `null` if there was no return |
| 4102 * type. Return the operator declaration that was parsed. |
| 4103 * |
| 4104 * operatorDeclaration ::= |
| 4105 * operatorSignature (';' | functionBody) |
| 4106 * |
| 4107 * operatorSignature ::= |
| 4108 * 'external'? returnType? 'operator' operator formalParameterList |
| 4109 */ |
| 4110 MethodDeclaration parseOperator(CommentAndMetadata commentAndMetadata, |
| 4111 Token externalKeyword, TypeName returnType) { |
| 4112 Token operatorKeyword; |
| 4113 if (_matchesKeyword(Keyword.OPERATOR)) { |
| 4114 operatorKeyword = getAndAdvance(); |
| 4115 } else { |
| 4116 _reportErrorForToken( |
| 4117 ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken); |
| 4118 operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR); |
| 4119 } |
| 4120 return _parseOperatorAfterKeyword( |
| 4121 commentAndMetadata, externalKeyword, returnType, operatorKeyword); |
| 4122 } |
| 4123 |
| 4124 /** |
| 4125 * Parse a part or part-of directive. The [commentAndMetadata] is the metadata |
| 4126 * to be associated with the directive. Return the part or part-of directive |
| 4127 * that was parsed. |
| 4128 * |
| 4129 * This method assumes that the current token matches `Keyword.PART`. |
| 4130 * |
| 4131 * partDirective ::= |
| 4132 * metadata 'part' stringLiteral ';' |
| 4133 * |
| 4134 * partOfDirective ::= |
| 4135 * metadata 'part' 'of' identifier ';' |
| 4136 */ |
| 4137 Directive parsePartOrPartOfDirective(CommentAndMetadata commentAndMetadata) { |
| 4138 if (_tokenMatchesString(_peek(), _OF)) { |
| 4139 return _parsePartOfDirective(commentAndMetadata); |
| 4140 } |
| 4141 return _parsePartDirective(commentAndMetadata); |
| 4142 } |
| 4143 |
| 4144 /** |
| 4145 * Parse a postfix expression. Return the postfix expression that was parsed. |
| 4146 * |
| 4147 * postfixExpression ::= |
| 4148 * assignableExpression postfixOperator |
| 4149 * | primary selector* |
| 4150 * |
| 4151 * selector ::= |
| 4152 * assignableSelector |
| 4153 * | argumentList |
| 4154 */ |
| 4155 Expression parsePostfixExpression() { |
| 4156 Expression operand = parseAssignableExpression(true); |
| 4157 TokenType type = _currentToken.type; |
| 4158 if (type == TokenType.OPEN_SQUARE_BRACKET || |
| 4159 type == TokenType.PERIOD || |
| 4160 type == TokenType.QUESTION_PERIOD || |
| 4161 type == TokenType.OPEN_PAREN || |
| 4162 (parseGenericMethods && type == TokenType.LT)) { |
| 4163 do { |
| 4164 if (_isLikelyArgumentList()) { |
| 4165 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); |
| 4166 ArgumentList argumentList = parseArgumentList(); |
| 4167 Expression currentOperand = operand; |
| 4168 if (currentOperand is PropertyAccess) { |
| 4169 operand = new MethodInvocation( |
| 4170 currentOperand.target, |
| 4171 currentOperand.operator, |
| 4172 currentOperand.propertyName, |
| 4173 typeArguments, |
| 4174 argumentList); |
| 4175 } else { |
| 4176 operand = new FunctionExpressionInvocation( |
| 4177 operand, typeArguments, argumentList); |
| 4178 } |
| 4179 } else { |
| 4180 operand = parseAssignableSelector(operand, true); |
| 4181 } |
| 4182 type = _currentToken.type; |
| 4183 } while (type == TokenType.OPEN_SQUARE_BRACKET || |
| 4184 type == TokenType.PERIOD || |
| 4185 type == TokenType.QUESTION_PERIOD || |
| 4186 type == TokenType.OPEN_PAREN); |
| 4187 return operand; |
| 4188 } |
| 4189 if (!_currentToken.type.isIncrementOperator) { |
| 4190 return operand; |
| 4191 } |
| 4192 _ensureAssignable(operand); |
| 4193 Token operator = getAndAdvance(); |
| 4194 return new PostfixExpression(operand, operator); |
| 4195 } |
| 4196 |
| 4197 /** |
| 4198 * Parse a prefixed identifier. Return the prefixed identifier that was |
| 4199 * parsed. |
| 4200 * |
| 4201 * prefixedIdentifier ::= |
| 4202 * identifier ('.' identifier)? |
| 4203 */ |
| 4204 Identifier parsePrefixedIdentifier() { |
| 4205 return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier()); |
| 4206 } |
| 4207 |
| 4208 /** |
| 4209 * Parse a primary expression. Return the primary expression that was parsed. |
| 4210 * |
| 4211 * primary ::= |
| 4212 * thisExpression |
| 4213 * | 'super' unconditionalAssignableSelector |
| 4214 * | functionExpression |
| 4215 * | literal |
| 4216 * | identifier |
| 4217 * | newExpression |
| 4218 * | constObjectExpression |
| 4219 * | '(' expression ')' |
| 4220 * | argumentDefinitionTest |
| 4221 * |
| 4222 * literal ::= |
| 4223 * nullLiteral |
| 4224 * | booleanLiteral |
| 4225 * | numericLiteral |
| 4226 * | stringLiteral |
| 4227 * | symbolLiteral |
| 4228 * | mapLiteral |
| 4229 * | listLiteral |
| 4230 */ |
| 4231 Expression parsePrimaryExpression() { |
| 4232 if (_matchesIdentifier()) { |
| 4233 // TODO(brianwilkerson) The code below was an attempt to recover from an |
| 4234 // error case, but it needs to be applied as a recovery only after we |
| 4235 // know that parsing it as an identifier doesn't work. Leaving the code as |
| 4236 // a reminder of how to recover. |
| 4237 // if (isFunctionExpression(_peek())) { |
| 4238 // // |
| 4239 // // Function expressions were allowed to have names at one point, but t
his is now illegal. |
| 4240 // // |
| 4241 // reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance()
); |
| 4242 // return parseFunctionExpression(); |
| 4243 // } |
| 4244 return _parsePrefixedIdentifierUnchecked(); |
| 4245 } |
| 4246 TokenType type = _currentToken.type; |
| 4247 if (type == TokenType.STRING) { |
| 4248 return parseStringLiteral(); |
| 4249 } else if (type == TokenType.INT) { |
| 4250 Token token = getAndAdvance(); |
| 4251 int value = null; |
| 4252 try { |
| 4253 value = int.parse(token.lexeme); |
| 4254 } on FormatException { |
| 4255 // The invalid format should have been reported by the scanner. |
| 4256 } |
| 4257 return new IntegerLiteral(token, value); |
| 4258 } |
| 4259 Keyword keyword = _currentToken.keyword; |
| 4260 if (keyword == Keyword.NULL) { |
| 4261 return new NullLiteral(getAndAdvance()); |
| 4262 } else if (keyword == Keyword.NEW) { |
| 4263 return parseNewExpression(); |
| 4264 } else if (keyword == Keyword.THIS) { |
| 4265 return new ThisExpression(getAndAdvance()); |
| 4266 } else if (keyword == Keyword.SUPER) { |
| 4267 // TODO(paulberry): verify with Gilad that "super" must be followed by |
| 4268 // unconditionalAssignableSelector in this case. |
| 4269 return parseAssignableSelector( |
| 4270 new SuperExpression(getAndAdvance()), false, |
| 4271 allowConditional: false); |
| 4272 } else if (keyword == Keyword.FALSE) { |
| 4273 return new BooleanLiteral(getAndAdvance(), false); |
| 4274 } else if (keyword == Keyword.TRUE) { |
| 4275 return new BooleanLiteral(getAndAdvance(), true); |
| 4276 } |
| 4277 if (type == TokenType.DOUBLE) { |
| 4278 Token token = getAndAdvance(); |
| 4279 double value = 0.0; |
| 4280 try { |
| 4281 value = double.parse(token.lexeme); |
| 4282 } on FormatException { |
| 4283 // The invalid format should have been reported by the scanner. |
| 4284 } |
| 4285 return new DoubleLiteral(token, value); |
| 4286 } else if (type == TokenType.HEXADECIMAL) { |
| 4287 Token token = getAndAdvance(); |
| 4288 int value = null; |
| 4289 try { |
| 4290 value = int.parse(token.lexeme.substring(2), radix: 16); |
| 4291 } on FormatException { |
| 4292 // The invalid format should have been reported by the scanner. |
| 4293 } |
| 4294 return new IntegerLiteral(token, value); |
| 4295 } else if (keyword == Keyword.CONST) { |
| 4296 return parseConstExpression(); |
| 4297 } else if (type == TokenType.OPEN_PAREN) { |
| 4298 if (isFunctionExpression(_currentToken)) { |
| 4299 return parseFunctionExpression(); |
| 4300 } |
| 4301 Token leftParenthesis = getAndAdvance(); |
| 4302 bool wasInInitializer = _inInitializer; |
| 4303 _inInitializer = false; |
| 4304 try { |
| 4305 Expression expression = parseExpression2(); |
| 4306 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); |
| 4307 return new ParenthesizedExpression( |
| 4308 leftParenthesis, expression, rightParenthesis); |
| 4309 } finally { |
| 4310 _inInitializer = wasInInitializer; |
| 4311 } |
| 4312 } else if (type == TokenType.LT || _injectGenericCommentTypeList()) { |
| 4313 return parseListOrMapLiteral(null); |
| 4314 } else if (type == TokenType.OPEN_CURLY_BRACKET) { |
| 4315 return parseMapLiteral(null, null); |
| 4316 } else if (type == TokenType.OPEN_SQUARE_BRACKET || |
| 4317 type == TokenType.INDEX) { |
| 4318 return parseListLiteral(null, null); |
| 4319 } else if (type == TokenType.QUESTION && |
| 4320 _tokenMatches(_peek(), TokenType.IDENTIFIER)) { |
| 4321 _reportErrorForCurrentToken( |
| 4322 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); |
| 4323 _advance(); |
| 4324 return parsePrimaryExpression(); |
| 4325 } else if (keyword == Keyword.VOID) { |
| 4326 // |
| 4327 // Recover from having a return type of "void" where a return type is not |
| 4328 // expected. |
| 4329 // |
| 4330 // TODO(brianwilkerson) Improve this error message. |
| 4331 _reportErrorForCurrentToken( |
| 4332 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); |
| 4333 _advance(); |
| 4334 return parsePrimaryExpression(); |
| 4335 } else if (type == TokenType.HASH) { |
| 4336 return parseSymbolLiteral(); |
| 4337 } else { |
| 4338 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); |
| 4339 return createSyntheticIdentifier(); |
| 4340 } |
| 4341 } |
| 4342 |
| 4343 /** |
| 4344 * Parse a redirecting constructor invocation. The flag [hasPeriod] should be |
| 4345 * `true` if the `this` is followed by a period. Return the redirecting |
| 4346 * constructor invocation that was parsed. |
| 4347 * |
| 4348 * This method assumes that the current token matches `Keyword.THIS`. |
| 4349 * |
| 4350 * redirectingConstructorInvocation ::= |
| 4351 * 'this' ('.' identifier)? arguments |
| 4352 */ |
| 4353 RedirectingConstructorInvocation parseRedirectingConstructorInvocation( |
| 4354 bool hasPeriod) { |
| 4355 Token keyword = getAndAdvance(); |
| 4356 Token period = null; |
| 4357 SimpleIdentifier constructorName = null; |
| 4358 if (hasPeriod) { |
| 4359 period = getAndAdvance(); |
| 4360 if (_matchesIdentifier()) { |
| 4361 constructorName = _parseSimpleIdentifierUnchecked(isDeclaration: false); |
| 4362 } else { |
| 4363 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); |
| 4364 constructorName = createSyntheticIdentifier(isDeclaration: false); |
| 4365 _advance(); |
| 4366 } |
| 4367 } |
| 4368 ArgumentList argumentList = _parseArgumentListChecked(); |
| 4369 return new RedirectingConstructorInvocation( |
| 4370 keyword, period, constructorName, argumentList); |
| 4371 } |
| 4372 |
| 4373 /** |
| 4374 * Parse a relational expression. Return the relational expression that was |
| 4375 * parsed. |
| 4376 * |
| 4377 * relationalExpression ::= |
| 4378 * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperato
r bitwiseOrExpression)? |
| 4379 * | 'super' relationalOperator bitwiseOrExpression |
| 4380 */ |
| 4381 Expression parseRelationalExpression() { |
| 4382 if (_currentToken.keyword == Keyword.SUPER && |
| 4383 _currentToken.next.type.isRelationalOperator) { |
| 4384 Expression expression = new SuperExpression(getAndAdvance()); |
| 4385 Token operator = getAndAdvance(); |
| 4386 return new BinaryExpression( |
| 4387 expression, operator, parseBitwiseOrExpression()); |
| 4388 } |
| 4389 Expression expression = parseBitwiseOrExpression(); |
| 4390 Keyword keyword = _currentToken.keyword; |
| 4391 if (keyword == Keyword.AS) { |
| 4392 Token asOperator = getAndAdvance(); |
| 4393 return new AsExpression(expression, asOperator, parseTypeName(true)); |
| 4394 } else if (keyword == Keyword.IS) { |
| 4395 Token isOperator = getAndAdvance(); |
| 4396 Token notOperator = null; |
| 4397 if (_matches(TokenType.BANG)) { |
| 4398 notOperator = getAndAdvance(); |
| 4399 } |
| 4400 return new IsExpression( |
| 4401 expression, isOperator, notOperator, parseTypeName(true)); |
| 4402 } else if (_currentToken.type.isRelationalOperator) { |
| 4403 Token operator = getAndAdvance(); |
| 4404 return new BinaryExpression( |
| 4405 expression, operator, parseBitwiseOrExpression()); |
| 4406 } |
| 4407 return expression; |
| 4408 } |
| 4409 |
| 4410 /** |
| 4411 * Parse a rethrow expression. Return the rethrow expression that was parsed. |
| 4412 * |
| 4413 * This method assumes that the current token matches `Keyword.RETHROW`. |
| 4414 * |
| 4415 * rethrowExpression ::= |
| 4416 * 'rethrow' |
| 4417 */ |
| 4418 Expression parseRethrowExpression() => new RethrowExpression(getAndAdvance()); |
| 4419 |
| 4420 /** |
| 4421 * Parse a return statement. Return the return statement that was parsed. |
| 4422 * |
| 4423 * This method assumes that the current token matches `Keyword.RETURN`. |
| 4424 * |
| 4425 * returnStatement ::= |
| 4426 * 'return' expression? ';' |
| 4427 */ |
| 4428 Statement parseReturnStatement() { |
| 4429 Token returnKeyword = getAndAdvance(); |
| 4430 if (_matches(TokenType.SEMICOLON)) { |
| 4431 return new ReturnStatement(returnKeyword, null, getAndAdvance()); |
| 4432 } |
| 4433 Expression expression = parseExpression2(); |
| 4434 Token semicolon = _expect(TokenType.SEMICOLON); |
| 4435 return new ReturnStatement(returnKeyword, expression, semicolon); |
| 4436 } |
| 4437 |
| 4438 /** |
| 4439 * Parse a return type. Return the return type that was parsed. |
| 4440 * |
| 4441 * returnType ::= |
| 4442 * 'void' |
| 4443 * | type |
| 4444 */ |
| 4445 TypeName parseReturnType() { |
| 4446 if (_currentToken.keyword == Keyword.VOID) { |
| 4447 return new TypeName(new SimpleIdentifier(getAndAdvance()), null); |
| 4448 } else { |
| 4449 return parseTypeName(false); |
| 4450 } |
| 4451 } |
| 4452 |
| 4453 /** |
| 4454 * Parse a setter. The [commentAndMetadata] is the documentation comment and |
| 4455 * metadata to be associated with the declaration. The [externalKeyword] is |
| 4456 * the 'external' token. The [staticKeyword] is the static keyword, or `null` |
| 4457 * if the setter is not static. The [returnType] is the return type that has |
| 4458 * already been parsed, or `null` if there was no return type. Return the |
| 4459 * setter that was parsed. |
| 4460 * |
| 4461 * This method assumes that the current token matches `Keyword.SET`. |
| 4462 * |
| 4463 * setter ::= |
| 4464 * setterSignature functionBody? |
| 4465 * |
| 4466 * setterSignature ::= |
| 4467 * 'external'? 'static'? returnType? 'set' identifier formalParameterL
ist |
| 4468 */ |
| 4469 MethodDeclaration parseSetter(CommentAndMetadata commentAndMetadata, |
| 4470 Token externalKeyword, Token staticKeyword, TypeName returnType) { |
| 4471 Token propertyKeyword = getAndAdvance(); |
| 4472 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); |
| 4473 FormalParameterList parameters = parseFormalParameterList(); |
| 4474 _validateFormalParameterList(parameters); |
| 4475 FunctionBody body = parseFunctionBody( |
| 4476 externalKeyword != null || staticKeyword == null, |
| 4477 ParserErrorCode.STATIC_SETTER_WITHOUT_BODY, |
| 4478 false); |
| 4479 if (externalKeyword != null && body is! EmptyFunctionBody) { |
| 4480 _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY); |
| 4481 } |
| 4482 return new MethodDeclaration( |
| 4483 commentAndMetadata.comment, |
| 4484 commentAndMetadata.metadata, |
| 4485 externalKeyword, |
| 4486 staticKeyword, |
| 4487 returnType, |
| 4488 propertyKeyword, |
| 4489 null, |
| 4490 name, |
| 4491 null, |
| 4492 parameters, |
| 4493 body); |
| 4494 } |
| 4495 |
| 4496 /** |
| 4497 * Parse a shift expression. Return the shift expression that was parsed. |
| 4498 * |
| 4499 * shiftExpression ::= |
| 4500 * additiveExpression (shiftOperator additiveExpression)* |
| 4501 * | 'super' (shiftOperator additiveExpression)+ |
| 4502 */ |
| 4503 Expression parseShiftExpression() { |
| 4504 Expression expression; |
| 4505 if (_currentToken.keyword == Keyword.SUPER && |
| 4506 _currentToken.next.type.isShiftOperator) { |
| 4507 expression = new SuperExpression(getAndAdvance()); |
| 4508 } else { |
| 4509 expression = parseAdditiveExpression(); |
| 4510 } |
| 4511 while (_currentToken.type.isShiftOperator) { |
| 4512 expression = new BinaryExpression( |
| 4513 expression, getAndAdvance(), parseAdditiveExpression()); |
| 4514 } |
| 4515 return expression; |
| 4516 } |
| 4517 |
| 4518 /** |
| 4519 * Parse a simple identifier. Return the simple identifier that was parsed. |
| 4520 * |
| 4521 * identifier ::= |
| 4522 * IDENTIFIER |
| 4523 */ |
| 4524 SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) { |
| 4525 if (_matchesIdentifier()) { |
| 4526 return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration); |
| 4527 } |
| 4528 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); |
| 4529 return createSyntheticIdentifier(isDeclaration: isDeclaration); |
| 4530 } |
| 4531 |
| 4532 /** |
| 4533 * Parse a statement, starting with the given [token]. Return the statement |
| 4534 * that was parsed, or `null` if the tokens do not represent a recognizable |
| 4535 * statement. |
| 4536 */ |
| 4537 Statement parseStatement(Token token) { |
| 4538 _currentToken = token; |
| 4539 return parseStatement2(); |
| 4540 } |
| 4541 |
| 4542 /** |
| 4543 * Parse a statement. Return the statement that was parsed. |
| 4544 * |
| 4545 * statement ::= |
| 4546 * label* nonLabeledStatement |
| 4547 */ |
| 4548 Statement parseStatement2() { |
| 4549 List<Label> labels = null; |
| 4550 while (_matchesIdentifier() && _currentToken.next.type == TokenType.COLON) { |
| 4551 Label label = parseLabel(isDeclaration: true); |
| 4552 if (labels == null) { |
| 4553 labels = <Label>[label]; |
| 4554 } else { |
| 4555 labels.add(label); |
| 4556 } |
| 4557 } |
| 4558 Statement statement = parseNonLabeledStatement(); |
| 4559 if (labels == null) { |
| 4560 return statement; |
| 4561 } |
| 4562 return new LabeledStatement(labels, statement); |
| 4563 } |
| 4564 |
| 4565 /** |
| 4566 * Parse a sequence of statements, starting with the given [token]. Return the |
| 4567 * statements that were parsed, or `null` if the tokens do not represent a |
| 4568 * recognizable sequence of statements. |
| 4569 */ |
| 4570 List<Statement> parseStatements(Token token) { |
| 4571 _currentToken = token; |
| 4572 return _parseStatementList(); |
| 4573 } |
| 4574 |
| 4575 /** |
| 4576 * Parse a string literal. Return the string literal that was parsed. |
| 4577 * |
| 4578 * stringLiteral ::= |
| 4579 * MULTI_LINE_STRING+ |
| 4580 * | SINGLE_LINE_STRING+ |
| 4581 */ |
| 4582 StringLiteral parseStringLiteral() { |
| 4583 if (_matches(TokenType.STRING)) { |
| 4584 return _parseStringLiteralUnchecked(); |
| 4585 } |
| 4586 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL); |
| 4587 return createSyntheticStringLiteral(); |
| 4588 } |
| 4589 |
| 4590 /** |
| 4591 * Parse a super constructor invocation. Return the super constructor |
| 4592 * invocation that was parsed. |
| 4593 * |
| 4594 * This method assumes that the current token matches [Keyword.SUPER]. |
| 4595 * |
| 4596 * superConstructorInvocation ::= |
| 4597 * 'super' ('.' identifier)? arguments |
| 4598 */ |
| 4599 SuperConstructorInvocation parseSuperConstructorInvocation() { |
| 4600 Token keyword = getAndAdvance(); |
| 4601 Token period = null; |
| 4602 SimpleIdentifier constructorName = null; |
| 4603 if (_matches(TokenType.PERIOD)) { |
| 4604 period = getAndAdvance(); |
| 4605 constructorName = parseSimpleIdentifier(); |
| 4606 } |
| 4607 ArgumentList argumentList = _parseArgumentListChecked(); |
| 4608 return new SuperConstructorInvocation( |
| 4609 keyword, period, constructorName, argumentList); |
| 4610 } |
| 4611 |
| 4612 /** |
| 4613 * Parse a switch statement. Return the switch statement that was parsed. |
| 4614 * |
| 4615 * switchStatement ::= |
| 4616 * 'switch' '(' expression ')' '{' switchCase* defaultCase? '}' |
| 4617 * |
| 4618 * switchCase ::= |
| 4619 * label* ('case' expression ':') statements |
| 4620 * |
| 4621 * defaultCase ::= |
| 4622 * label* 'default' ':' statements |
| 4623 */ |
| 4624 SwitchStatement parseSwitchStatement() { |
| 4625 bool wasInSwitch = _inSwitch; |
| 4626 _inSwitch = true; |
| 4627 try { |
| 4628 HashSet<String> definedLabels = new HashSet<String>(); |
| 4629 Token keyword = _expectKeyword(Keyword.SWITCH); |
| 4630 Token leftParenthesis = _expect(TokenType.OPEN_PAREN); |
| 4631 Expression expression = parseExpression2(); |
| 4632 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); |
| 4633 Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET); |
| 4634 Token defaultKeyword = null; |
| 4635 List<SwitchMember> members = <SwitchMember>[]; |
| 4636 TokenType type = _currentToken.type; |
| 4637 while (type != TokenType.EOF && type != TokenType.CLOSE_CURLY_BRACKET) { |
| 4638 List<Label> labels = <Label>[]; |
| 4639 while ( |
| 4640 _matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) { |
| 4641 SimpleIdentifier identifier = |
| 4642 _parseSimpleIdentifierUnchecked(isDeclaration: true); |
| 4643 String label = identifier.token.lexeme; |
| 4644 if (definedLabels.contains(label)) { |
| 4645 _reportErrorForToken( |
| 4646 ParserErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT, |
| 4647 identifier.token, |
| 4648 [label]); |
| 4649 } else { |
| 4650 definedLabels.add(label); |
| 4651 } |
| 4652 Token colon = getAndAdvance(); |
| 4653 labels.add(new Label(identifier, colon)); |
| 4654 } |
| 4655 Keyword keyword = _currentToken.keyword; |
| 4656 if (keyword == Keyword.CASE) { |
| 4657 Token caseKeyword = getAndAdvance(); |
| 4658 Expression caseExpression = parseExpression2(); |
| 4659 Token colon = _expect(TokenType.COLON); |
| 4660 members.add(new SwitchCase(labels, caseKeyword, caseExpression, colon, |
| 4661 _parseStatementList())); |
| 4662 if (defaultKeyword != null) { |
| 4663 _reportErrorForToken( |
| 4664 ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE, |
| 4665 caseKeyword); |
| 4666 } |
| 4667 } else if (keyword == Keyword.DEFAULT) { |
| 4668 if (defaultKeyword != null) { |
| 4669 _reportErrorForToken( |
| 4670 ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, _peek()); |
| 4671 } |
| 4672 defaultKeyword = getAndAdvance(); |
| 4673 Token colon = _expect(TokenType.COLON); |
| 4674 members.add(new SwitchDefault( |
| 4675 labels, defaultKeyword, colon, _parseStatementList())); |
| 4676 } else { |
| 4677 // We need to advance, otherwise we could end up in an infinite loop, |
| 4678 // but this could be a lot smarter about recovering from the error. |
| 4679 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT); |
| 4680 bool atEndOrNextMember() { |
| 4681 TokenType type = _currentToken.type; |
| 4682 if (type == TokenType.EOF || |
| 4683 type == TokenType.CLOSE_CURLY_BRACKET) { |
| 4684 return true; |
| 4685 } |
| 4686 Keyword keyword = _currentToken.keyword; |
| 4687 return keyword == Keyword.CASE || keyword == Keyword.DEFAULT; |
| 4688 } |
| 4689 |
| 4690 while (!atEndOrNextMember()) { |
| 4691 _advance(); |
| 4692 } |
| 4693 } |
| 4694 type = _currentToken.type; |
| 4695 } |
| 4696 Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); |
| 4697 return new SwitchStatement(keyword, leftParenthesis, expression, |
| 4698 rightParenthesis, leftBracket, members, rightBracket); |
| 4699 } finally { |
| 4700 _inSwitch = wasInSwitch; |
| 4701 } |
| 4702 } |
| 4703 |
| 4704 /** |
| 4705 * Parse a symbol literal. Return the symbol literal that was parsed. |
| 4706 * |
| 4707 * This method assumes that the current token matches [TokenType.HASH]. |
| 4708 * |
| 4709 * symbolLiteral ::= |
| 4710 * '#' identifier ('.' identifier)* |
| 4711 */ |
| 4712 SymbolLiteral parseSymbolLiteral() { |
| 4713 Token poundSign = getAndAdvance(); |
| 4714 List<Token> components = <Token>[]; |
| 4715 if (_matchesIdentifier()) { |
| 4716 components.add(getAndAdvance()); |
| 4717 while (_optional(TokenType.PERIOD)) { |
| 4718 if (_matchesIdentifier()) { |
| 4719 components.add(getAndAdvance()); |
| 4720 } else { |
| 4721 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); |
| 4722 components.add(_createSyntheticToken(TokenType.IDENTIFIER)); |
| 4723 break; |
| 4724 } |
| 4725 } |
| 4726 } else if (_currentToken.isOperator) { |
| 4727 components.add(getAndAdvance()); |
| 4728 } else if (_matchesKeyword(Keyword.VOID)) { |
| 4729 components.add(getAndAdvance()); |
| 4730 } else { |
| 4731 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); |
| 4732 components.add(_createSyntheticToken(TokenType.IDENTIFIER)); |
| 4733 } |
| 4734 return new SymbolLiteral(poundSign, components); |
| 4735 } |
| 4736 |
| 4737 /** |
| 4738 * Parse a throw expression. Return the throw expression that was parsed. |
| 4739 * |
| 4740 * This method assumes that the current token matches [Keyword.THROW]. |
| 4741 * |
| 4742 * throwExpression ::= |
| 4743 * 'throw' expression |
| 4744 */ |
| 4745 Expression parseThrowExpression() { |
| 4746 Token keyword = getAndAdvance(); |
| 4747 TokenType type = _currentToken.type; |
| 4748 if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) { |
| 4749 _reportErrorForToken( |
| 4750 ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken); |
| 4751 return new ThrowExpression(keyword, createSyntheticIdentifier()); |
| 4752 } |
| 4753 Expression expression = parseExpression2(); |
| 4754 return new ThrowExpression(keyword, expression); |
| 4755 } |
| 4756 |
| 4757 /** |
| 4758 * Parse a throw expression. Return the throw expression that was parsed. |
| 4759 * |
| 4760 * This method assumes that the current token matches [Keyword.THROW]. |
| 4761 * |
| 4762 * throwExpressionWithoutCascade ::= |
| 4763 * 'throw' expressionWithoutCascade |
| 4764 */ |
| 4765 Expression parseThrowExpressionWithoutCascade() { |
| 4766 Token keyword = getAndAdvance(); |
| 4767 TokenType type = _currentToken.type; |
| 4768 if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) { |
| 4769 _reportErrorForToken( |
| 4770 ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken); |
| 4771 return new ThrowExpression(keyword, createSyntheticIdentifier()); |
| 4772 } |
| 4773 Expression expression = parseExpressionWithoutCascade(); |
| 4774 return new ThrowExpression(keyword, expression); |
| 4775 } |
| 4776 |
| 4777 /** |
| 4778 * Parse a try statement. Return the try statement that was parsed. |
| 4779 * |
| 4780 * This method assumes that the current token matches [Keyword.TRY]. |
| 4781 * |
| 4782 * tryStatement ::= |
| 4783 * 'try' block (onPart+ finallyPart? | finallyPart) |
| 4784 * |
| 4785 * onPart ::= |
| 4786 * catchPart block |
| 4787 * | 'on' type catchPart? block |
| 4788 * |
| 4789 * catchPart ::= |
| 4790 * 'catch' '(' identifier (',' identifier)? ')' |
| 4791 * |
| 4792 * finallyPart ::= |
| 4793 * 'finally' block |
| 4794 */ |
| 4795 Statement parseTryStatement() { |
| 4796 Token tryKeyword = getAndAdvance(); |
| 4797 Block body = _parseBlockChecked(); |
| 4798 List<CatchClause> catchClauses = <CatchClause>[]; |
| 4799 Block finallyClause = null; |
| 4800 while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) { |
| 4801 Token onKeyword = null; |
| 4802 TypeName exceptionType = null; |
| 4803 if (_matchesString(_ON)) { |
| 4804 onKeyword = getAndAdvance(); |
| 4805 exceptionType = parseTypeName(false); |
| 4806 } |
| 4807 Token catchKeyword = null; |
| 4808 Token leftParenthesis = null; |
| 4809 SimpleIdentifier exceptionParameter = null; |
| 4810 Token comma = null; |
| 4811 SimpleIdentifier stackTraceParameter = null; |
| 4812 Token rightParenthesis = null; |
| 4813 if (_matchesKeyword(Keyword.CATCH)) { |
| 4814 catchKeyword = getAndAdvance(); |
| 4815 leftParenthesis = _expect(TokenType.OPEN_PAREN); |
| 4816 exceptionParameter = parseSimpleIdentifier(isDeclaration: true); |
| 4817 if (_matches(TokenType.COMMA)) { |
| 4818 comma = getAndAdvance(); |
| 4819 stackTraceParameter = parseSimpleIdentifier(isDeclaration: true); |
| 4820 } |
| 4821 rightParenthesis = _expect(TokenType.CLOSE_PAREN); |
| 4822 } |
| 4823 Block catchBody = _parseBlockChecked(); |
| 4824 catchClauses.add(new CatchClause( |
| 4825 onKeyword, |
| 4826 exceptionType, |
| 4827 catchKeyword, |
| 4828 leftParenthesis, |
| 4829 exceptionParameter, |
| 4830 comma, |
| 4831 stackTraceParameter, |
| 4832 rightParenthesis, |
| 4833 catchBody)); |
| 4834 } |
| 4835 Token finallyKeyword = null; |
| 4836 if (_matchesKeyword(Keyword.FINALLY)) { |
| 4837 finallyKeyword = getAndAdvance(); |
| 4838 finallyClause = _parseBlockChecked(); |
| 4839 } else if (catchClauses.isEmpty) { |
| 4840 _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY); |
| 4841 } |
| 4842 return new TryStatement( |
| 4843 tryKeyword, body, catchClauses, finallyKeyword, finallyClause); |
| 4844 } |
| 4845 |
| 4846 /** |
| 4847 * Parse a type alias. The [commentAndMetadata] is the metadata to be |
| 4848 * associated with the member. Return the type alias that was parsed. |
| 4849 * |
| 4850 * This method assumes that the current token matches [Keyword.TYPEDEF]. |
| 4851 * |
| 4852 * typeAlias ::= |
| 4853 * 'typedef' typeAliasBody |
| 4854 * |
| 4855 * typeAliasBody ::= |
| 4856 * classTypeAlias |
| 4857 * | functionTypeAlias |
| 4858 * |
| 4859 * classTypeAlias ::= |
| 4860 * identifier typeParameters? '=' 'abstract'? mixinApplication |
| 4861 * |
| 4862 * mixinApplication ::= |
| 4863 * qualified withClause implementsClause? ';' |
| 4864 * |
| 4865 * functionTypeAlias ::= |
| 4866 * functionPrefix typeParameterList? formalParameterList ';' |
| 4867 * |
| 4868 * functionPrefix ::= |
| 4869 * returnType? name |
| 4870 */ |
| 4871 TypeAlias parseTypeAlias(CommentAndMetadata commentAndMetadata) { |
| 4872 Token keyword = getAndAdvance(); |
| 4873 if (_matchesIdentifier()) { |
| 4874 Token next = _peek(); |
| 4875 if (_tokenMatches(next, TokenType.LT)) { |
| 4876 next = _skipTypeParameterList(next); |
| 4877 if (next != null && _tokenMatches(next, TokenType.EQ)) { |
| 4878 TypeAlias typeAlias = |
| 4879 parseClassTypeAlias(commentAndMetadata, null, keyword); |
| 4880 _reportErrorForToken( |
| 4881 ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword); |
| 4882 return typeAlias; |
| 4883 } |
| 4884 } else if (_tokenMatches(next, TokenType.EQ)) { |
| 4885 TypeAlias typeAlias = |
| 4886 parseClassTypeAlias(commentAndMetadata, null, keyword); |
| 4887 _reportErrorForToken( |
| 4888 ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword); |
| 4889 return typeAlias; |
| 4890 } |
| 4891 } |
| 4892 return _parseFunctionTypeAlias(commentAndMetadata, keyword); |
| 4893 } |
| 4894 |
| 4895 /** |
| 4896 * Parse a list of type arguments. Return the type argument list that was |
| 4897 * parsed. |
| 4898 * |
| 4899 * This method assumes that the current token matches `TokenType.LT`. |
| 4900 * |
| 4901 * typeArguments ::= |
| 4902 * '<' typeList '>' |
| 4903 * |
| 4904 * typeList ::= |
| 4905 * type (',' type)* |
| 4906 */ |
| 4907 TypeArgumentList parseTypeArgumentList() { |
| 4908 Token leftBracket = getAndAdvance(); |
| 4909 List<TypeName> arguments = <TypeName>[parseTypeName(false)]; |
| 4910 while (_optional(TokenType.COMMA)) { |
| 4911 arguments.add(parseTypeName(false)); |
| 4912 } |
| 4913 Token rightBracket = _expectGt(); |
| 4914 return new TypeArgumentList(leftBracket, arguments, rightBracket); |
| 4915 } |
| 4916 |
| 4917 /** |
| 4918 * Parse a type name. Return the type name that was parsed. |
| 4919 * |
| 4920 * type ::= |
| 4921 * qualified typeArguments? |
| 4922 */ |
| 4923 TypeName parseTypeName(bool inExpression) { |
| 4924 TypeName realType = _parseTypeName(inExpression); |
| 4925 // If this is followed by a generic method type comment, allow the comment |
| 4926 // type to replace the real type name. |
| 4927 // TODO(jmesserly): this feels like a big hammer. Can we restrict it to |
| 4928 // only work inside generic methods? |
| 4929 TypeName typeFromComment = _parseOptionalTypeNameComment(); |
| 4930 return typeFromComment ?? realType; |
| 4931 } |
| 4932 |
| 4933 /** |
| 4934 * Parse a type parameter. Return the type parameter that was parsed. |
| 4935 * |
| 4936 * typeParameter ::= |
| 4937 * metadata name ('extends' bound)? |
| 4938 */ |
| 4939 TypeParameter parseTypeParameter() { |
| 4940 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); |
| 4941 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); |
| 4942 if (_matchesKeyword(Keyword.EXTENDS)) { |
| 4943 Token keyword = getAndAdvance(); |
| 4944 TypeName bound = parseTypeName(false); |
| 4945 return new TypeParameter(commentAndMetadata.comment, |
| 4946 commentAndMetadata.metadata, name, keyword, bound); |
| 4947 } |
| 4948 return new TypeParameter(commentAndMetadata.comment, |
| 4949 commentAndMetadata.metadata, name, null, null); |
| 4950 } |
| 4951 |
| 4952 /** |
| 4953 * Parse a list of type parameters. Return the list of type parameters that |
| 4954 * were parsed. |
| 4955 * |
| 4956 * This method assumes that the current token matches `TokenType.LT`. |
| 4957 * |
| 4958 * typeParameterList ::= |
| 4959 * '<' typeParameter (',' typeParameter)* '>' |
| 4960 */ |
| 4961 TypeParameterList parseTypeParameterList() { |
| 4962 Token leftBracket = getAndAdvance(); |
| 4963 List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()]; |
| 4964 while (_optional(TokenType.COMMA)) { |
| 4965 typeParameters.add(parseTypeParameter()); |
| 4966 } |
| 4967 Token rightBracket = _expectGt(); |
| 4968 return new TypeParameterList(leftBracket, typeParameters, rightBracket); |
| 4969 } |
| 4970 |
| 4971 /** |
| 4972 * Parse a unary expression. Return the unary expression that was parsed. |
| 4973 * |
| 4974 * unaryExpression ::= |
| 4975 * prefixOperator unaryExpression |
| 4976 * | awaitExpression |
| 4977 * | postfixExpression |
| 4978 * | unaryOperator 'super' |
| 4979 * | '-' 'super' |
| 4980 * | incrementOperator assignableExpression |
| 4981 */ |
| 4982 Expression parseUnaryExpression() { |
| 4983 TokenType type = _currentToken.type; |
| 4984 if (type == TokenType.MINUS || |
| 4985 type == TokenType.BANG || |
| 4986 type == TokenType.TILDE) { |
| 4987 Token operator = getAndAdvance(); |
| 4988 if (_matchesKeyword(Keyword.SUPER)) { |
| 4989 TokenType nextType = _peek().type; |
| 4990 if (nextType == TokenType.OPEN_SQUARE_BRACKET || |
| 4991 nextType == TokenType.PERIOD) { |
| 4992 // "prefixOperator unaryExpression" |
| 4993 // --> "prefixOperator postfixExpression" |
| 4994 // --> "prefixOperator primary selector*" |
| 4995 // --> "prefixOperator 'super' assignableSelector selector*" |
| 4996 return new PrefixExpression(operator, parseUnaryExpression()); |
| 4997 } |
| 4998 return new PrefixExpression( |
| 4999 operator, new SuperExpression(getAndAdvance())); |
| 5000 } |
| 5001 return new PrefixExpression(operator, parseUnaryExpression()); |
| 5002 } else if (_currentToken.type.isIncrementOperator) { |
| 5003 Token operator = getAndAdvance(); |
| 5004 if (_matchesKeyword(Keyword.SUPER)) { |
| 5005 TokenType nextType = _peek().type; |
| 5006 if (nextType == TokenType.OPEN_SQUARE_BRACKET || |
| 5007 nextType == TokenType.PERIOD) { |
| 5008 // --> "prefixOperator 'super' assignableSelector selector*" |
| 5009 return new PrefixExpression(operator, parseUnaryExpression()); |
| 5010 } |
| 5011 // |
| 5012 // Even though it is not valid to use an incrementing operator |
| 5013 // ('++' or '--') before 'super', we can (and therefore must) interpret |
| 5014 // "--super" as semantically equivalent to "-(-super)". Unfortunately, |
| 5015 // we cannot do the same for "++super" because "+super" is also not |
| 5016 // valid. |
| 5017 // |
| 5018 if (type == TokenType.MINUS_MINUS) { |
| 5019 Token firstOperator = _createToken(operator, TokenType.MINUS); |
| 5020 Token secondOperator = |
| 5021 new Token(TokenType.MINUS, operator.offset + 1); |
| 5022 secondOperator.setNext(_currentToken); |
| 5023 firstOperator.setNext(secondOperator); |
| 5024 operator.previous.setNext(firstOperator); |
| 5025 return new PrefixExpression( |
| 5026 firstOperator, |
| 5027 new PrefixExpression( |
| 5028 secondOperator, new SuperExpression(getAndAdvance()))); |
| 5029 } |
| 5030 // Invalid operator before 'super' |
| 5031 _reportErrorForCurrentToken( |
| 5032 ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]); |
| 5033 return new PrefixExpression( |
| 5034 operator, new SuperExpression(getAndAdvance())); |
| 5035 } |
| 5036 return new PrefixExpression( |
| 5037 operator, _parseAssignableExpressionNotStartingWithSuper(false)); |
| 5038 } else if (type == TokenType.PLUS) { |
| 5039 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); |
| 5040 return createSyntheticIdentifier(); |
| 5041 } else if (_inAsync && _matchesString(_AWAIT)) { |
| 5042 return parseAwaitExpression(); |
| 5043 } |
| 5044 return parsePostfixExpression(); |
| 5045 } |
| 5046 |
| 5047 /** |
| 5048 * Parse a variable declaration. Return the variable declaration that was |
| 5049 * parsed. |
| 5050 * |
| 5051 * variableDeclaration ::= |
| 5052 * identifier ('=' expression)? |
| 5053 */ |
| 5054 VariableDeclaration parseVariableDeclaration() { |
| 5055 // TODO(paulberry): prior to the fix for bug 23204, we permitted |
| 5056 // annotations before variable declarations (e.g. "String @deprecated s;"). |
| 5057 // Although such constructions are prohibited by the spec, we may want to |
| 5058 // consider handling them anyway to allow for better parser recovery in the |
| 5059 // event that the user erroneously tries to use them. However, as a |
| 5060 // counterargument, this would likely degrade parser recovery in the event |
| 5061 // of a construct like "class C { int @deprecated foo() {} }" (i.e. the |
| 5062 // user is in the middle of inserting "int bar;" prior to |
| 5063 // "@deprecated foo() {}"). |
| 5064 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); |
| 5065 Token equals = null; |
| 5066 Expression initializer = null; |
| 5067 if (_matches(TokenType.EQ)) { |
| 5068 equals = getAndAdvance(); |
| 5069 initializer = parseExpression2(); |
| 5070 } |
| 5071 return new VariableDeclaration(name, equals, initializer); |
| 5072 } |
| 5073 |
| 5074 /** |
| 5075 * Parse a variable declaration list. The [commentAndMetadata] is the metadata |
| 5076 * to be associated with the variable declaration list. Return the variable |
| 5077 * declaration list that was parsed. |
| 5078 * |
| 5079 * variableDeclarationList ::= |
| 5080 * finalConstVarOrType variableDeclaration (',' variableDeclaration)* |
| 5081 */ |
| 5082 VariableDeclarationList parseVariableDeclarationListAfterMetadata( |
| 5083 CommentAndMetadata commentAndMetadata) { |
| 5084 FinalConstVarOrType holder = parseFinalConstVarOrType(false); |
| 5085 return parseVariableDeclarationListAfterType( |
| 5086 commentAndMetadata, holder.keyword, holder.type); |
| 5087 } |
| 5088 |
| 5089 /** |
| 5090 * Parse a variable declaration list. The [commentAndMetadata] is the metadata |
| 5091 * to be associated with the variable declaration list, or `null` if there is |
| 5092 * no attempt at parsing the comment and metadata. The [keyword] is the token |
| 5093 * representing the 'final', 'const' or 'var' keyword, or `null` if there is |
| 5094 * no keyword. The [type] is the type of the variables in the list. Return the |
| 5095 * variable declaration list that was parsed. |
| 5096 * |
| 5097 * variableDeclarationList ::= |
| 5098 * finalConstVarOrType variableDeclaration (',' variableDeclaration)* |
| 5099 */ |
| 5100 VariableDeclarationList parseVariableDeclarationListAfterType( |
| 5101 CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) { |
| 5102 if (type != null && |
| 5103 keyword != null && |
| 5104 _tokenMatchesKeyword(keyword, Keyword.VAR)) { |
| 5105 _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, keyword); |
| 5106 } |
| 5107 List<VariableDeclaration> variables = <VariableDeclaration>[ |
| 5108 parseVariableDeclaration() |
| 5109 ]; |
| 5110 while (_optional(TokenType.COMMA)) { |
| 5111 variables.add(parseVariableDeclaration()); |
| 5112 } |
| 5113 return new VariableDeclarationList(commentAndMetadata?.comment, |
| 5114 commentAndMetadata?.metadata, keyword, type, variables); |
| 5115 } |
| 5116 |
| 5117 /** |
| 5118 * Parse a variable declaration statement. The [commentAndMetadata] is the |
| 5119 * metadata to be associated with the variable declaration statement, or |
| 5120 * `null` if there is no attempt at parsing the comment and metadata. Return |
| 5121 * the variable declaration statement that was parsed. |
| 5122 * |
| 5123 * variableDeclarationStatement ::= |
| 5124 * variableDeclarationList ';' |
| 5125 */ |
| 5126 VariableDeclarationStatement parseVariableDeclarationStatementAfterMetadata( |
| 5127 CommentAndMetadata commentAndMetadata) { |
| 5128 // Token startToken = currentToken; |
| 5129 VariableDeclarationList variableList = |
| 5130 parseVariableDeclarationListAfterMetadata(commentAndMetadata); |
| 5131 // if (!matches(TokenType.SEMICOLON)) { |
| 5132 // if (matches(startToken, Keyword.VAR) && isTypedIdentifier(startToken
.getNext())) { |
| 5133 // // TODO(brianwilkerson) This appears to be of the form "var type v
ariable". We should do |
| 5134 // // a better job of recovering in this case. |
| 5135 // } |
| 5136 // } |
| 5137 Token semicolon = _expect(TokenType.SEMICOLON); |
| 5138 return new VariableDeclarationStatement(variableList, semicolon); |
| 5139 } |
| 5140 |
| 5141 /** |
| 5142 * Parse a while statement. Return the while statement that was parsed. |
| 5143 * |
| 5144 * This method assumes that the current token matches [Keyword.WHILE]. |
| 5145 * |
| 5146 * whileStatement ::= |
| 5147 * 'while' '(' expression ')' statement |
| 5148 */ |
| 5149 Statement parseWhileStatement() { |
| 5150 bool wasInLoop = _inLoop; |
| 5151 _inLoop = true; |
| 5152 try { |
| 5153 Token keyword = getAndAdvance(); |
| 5154 Token leftParenthesis = _expect(TokenType.OPEN_PAREN); |
| 5155 Expression condition = parseExpression2(); |
| 5156 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); |
| 5157 Statement body = parseStatement2(); |
| 5158 return new WhileStatement( |
| 5159 keyword, leftParenthesis, condition, rightParenthesis, body); |
| 5160 } finally { |
| 5161 _inLoop = wasInLoop; |
| 5162 } |
| 5163 } |
| 5164 |
| 5165 /** |
| 5166 * Parse a with clause. Return the with clause that was parsed. |
| 5167 * |
| 5168 * This method assumes that the current token matches `Keyword.WITH`. |
| 5169 * |
| 5170 * withClause ::= |
| 5171 * 'with' typeName (',' typeName)* |
| 5172 */ |
| 5173 WithClause parseWithClause() { |
| 5174 Token withKeyword = getAndAdvance(); |
| 5175 List<TypeName> types = <TypeName>[parseTypeName(false)]; |
| 5176 while (_optional(TokenType.COMMA)) { |
| 5177 types.add(parseTypeName(false)); |
| 5178 } |
| 5179 return new WithClause(withKeyword, types); |
| 5180 } |
| 5181 |
| 5182 /** |
| 5183 * Parse a yield statement. Return the yield statement that was parsed. |
| 5184 * |
| 5185 * This method assumes that the current token matches [Keyword.YIELD]. |
| 5186 * |
| 5187 * yieldStatement ::= |
| 5188 * 'yield' '*'? expression ';' |
| 5189 */ |
| 5190 YieldStatement parseYieldStatement() { |
| 5191 Token yieldToken = getAndAdvance(); |
| 5192 Token star = null; |
| 5193 if (_matches(TokenType.STAR)) { |
| 5194 star = getAndAdvance(); |
| 5195 } |
| 5196 Expression expression = parseExpression2(); |
| 5197 Token semicolon = _expect(TokenType.SEMICOLON); |
| 5198 return new YieldStatement(yieldToken, star, expression, semicolon); |
| 5199 } |
| 5200 |
| 5201 /** |
| 5202 * Parse a prefixed identifier, starting at the [startToken], without actually |
| 5203 * creating a prefixed identifier or changing the current token. Return the |
| 5204 * token following the prefixed identifier that was parsed, or `null` if the |
| 5205 * given token is not the first token in a valid prefixed identifier. |
| 5206 * |
| 5207 * This method must be kept in sync with [parsePrefixedIdentifier]. |
| 5208 * |
| 5209 * prefixedIdentifier ::= |
| 5210 * identifier ('.' identifier)? |
| 5211 */ |
| 5212 Token skipPrefixedIdentifier(Token startToken) { |
| 5213 Token token = skipSimpleIdentifier(startToken); |
| 5214 if (token == null) { |
| 5215 return null; |
| 5216 } else if (!_tokenMatches(token, TokenType.PERIOD)) { |
| 5217 return token; |
| 5218 } |
| 5219 token = token.next; |
| 5220 Token nextToken = skipSimpleIdentifier(token); |
| 5221 if (nextToken != null) { |
| 5222 return nextToken; |
| 5223 } else if (_tokenMatches(token, TokenType.CLOSE_PAREN) || |
| 5224 _tokenMatches(token, TokenType.COMMA)) { |
| 5225 // If the `id.` is followed by something that cannot produce a valid |
| 5226 // structure then assume this is a prefixed identifier but missing the |
| 5227 // trailing identifier |
| 5228 return token; |
| 5229 } |
| 5230 return null; |
| 5231 } |
| 5232 |
| 5233 /** |
| 5234 * Parse a return type, starting at the [startToken], without actually |
| 5235 * creating a return type or changing the current token. Return the token |
| 5236 * following the return type that was parsed, or `null` if the given token is |
| 5237 * not the first token in a valid return type. |
| 5238 * |
| 5239 * This method must be kept in sync with [parseReturnType]. |
| 5240 * |
| 5241 * returnType ::= |
| 5242 * 'void' |
| 5243 * | type |
| 5244 */ |
| 5245 Token skipReturnType(Token startToken) { |
| 5246 if (_tokenMatchesKeyword(startToken, Keyword.VOID)) { |
| 5247 return startToken.next; |
| 5248 } else { |
| 5249 return skipTypeName(startToken); |
| 5250 } |
| 5251 } |
| 5252 |
| 5253 /** |
| 5254 * Parse a simple identifier, starting at the [startToken], without actually |
| 5255 * creating a simple identifier or changing the current token. Return the |
| 5256 * token following the simple identifier that was parsed, or `null` if the |
| 5257 * given token is not the first token in a valid simple identifier. |
| 5258 * |
| 5259 * This method must be kept in sync with [parseSimpleIdentifier]. |
| 5260 * |
| 5261 * identifier ::= |
| 5262 * IDENTIFIER |
| 5263 */ |
| 5264 Token skipSimpleIdentifier(Token startToken) { |
| 5265 if (_tokenMatches(startToken, TokenType.IDENTIFIER) || |
| 5266 _tokenMatchesPseudoKeyword(startToken)) { |
| 5267 return startToken.next; |
| 5268 } |
| 5269 return null; |
| 5270 } |
| 5271 |
| 5272 /** |
| 5273 * Parse a string literal, starting at the [startToken], without actually |
| 5274 * creating a string literal or changing the current token. Return the token |
| 5275 * following the string literal that was parsed, or `null` if the given token |
| 5276 * is not the first token in a valid string literal. |
| 5277 * |
| 5278 * This method must be kept in sync with [parseStringLiteral]. |
| 5279 * |
| 5280 * stringLiteral ::= |
| 5281 * MULTI_LINE_STRING+ |
| 5282 * | SINGLE_LINE_STRING+ |
| 5283 */ |
| 5284 Token skipStringLiteral(Token startToken) { |
| 5285 Token token = startToken; |
| 5286 while (token != null && _tokenMatches(token, TokenType.STRING)) { |
| 5287 token = token.next; |
| 5288 TokenType type = token.type; |
| 5289 if (type == TokenType.STRING_INTERPOLATION_EXPRESSION || |
| 5290 type == TokenType.STRING_INTERPOLATION_IDENTIFIER) { |
| 5291 token = _skipStringInterpolation(token); |
| 5292 } |
| 5293 } |
| 5294 if (identical(token, startToken)) { |
| 5295 return null; |
| 5296 } |
| 5297 return token; |
| 5298 } |
| 5299 |
| 5300 /** |
| 5301 * Parse a list of type arguments, starting at the [startToken], without |
| 5302 * actually creating a type argument list or changing the current token. |
| 5303 * Return the token following the type argument list that was parsed, or |
| 5304 * `null` if the given token is not the first token in a valid type argument |
| 5305 * list. |
| 5306 * |
| 5307 * This method must be kept in sync with [parseTypeArgumentList]. |
| 5308 * |
| 5309 * typeArguments ::= |
| 5310 * '<' typeList '>' |
| 5311 * |
| 5312 * typeList ::= |
| 5313 * type (',' type)* |
| 5314 */ |
| 5315 Token skipTypeArgumentList(Token startToken) { |
| 5316 Token token = startToken; |
| 5317 if (!_tokenMatches(token, TokenType.LT) && |
| 5318 !_injectGenericCommentTypeList()) { |
| 5319 return null; |
| 5320 } |
| 5321 token = skipTypeName(token.next); |
| 5322 if (token == null) { |
| 5323 // If the start token '<' is followed by '>' |
| 5324 // then assume this should be type argument list but is missing a type |
| 5325 token = startToken.next; |
| 5326 if (_tokenMatches(token, TokenType.GT)) { |
| 5327 return token.next; |
| 5328 } |
| 5329 return null; |
| 5330 } |
| 5331 while (_tokenMatches(token, TokenType.COMMA)) { |
| 5332 token = skipTypeName(token.next); |
| 5333 if (token == null) { |
| 5334 return null; |
| 5335 } |
| 5336 } |
| 5337 if (token.type == TokenType.GT) { |
| 5338 return token.next; |
| 5339 } else if (token.type == TokenType.GT_GT) { |
| 5340 Token second = new Token(TokenType.GT, token.offset + 1); |
| 5341 second.setNextWithoutSettingPrevious(token.next); |
| 5342 return second; |
| 5343 } |
| 5344 return null; |
| 5345 } |
| 5346 |
| 5347 /** |
| 5348 * Parse a type name, starting at the [startToken], without actually creating |
| 5349 * a type name or changing the current token. Return the token following the |
| 5350 * type name that was parsed, or `null` if the given token is not the first |
| 5351 * token in a valid type name. |
| 5352 * |
| 5353 * This method must be kept in sync with [parseTypeName]. |
| 5354 * |
| 5355 * type ::= |
| 5356 * qualified typeArguments? |
| 5357 */ |
| 5358 Token skipTypeName(Token startToken) { |
| 5359 Token token = skipPrefixedIdentifier(startToken); |
| 5360 if (token == null) { |
| 5361 return null; |
| 5362 } |
| 5363 if (_tokenMatches(token, TokenType.LT)) { |
| 5364 token = skipTypeArgumentList(token); |
| 5365 } |
| 5366 return token; |
| 5367 } |
| 5368 |
| 5369 /** |
| 5370 * Advance to the next token in the token stream. |
| 5371 */ |
| 5372 void _advance() { |
| 5373 _currentToken = _currentToken.next; |
| 5374 } |
| 5375 |
| 5376 /** |
| 5377 * Append the character equivalent of the given [scalarValue] to the given |
| 5378 * [builder]. Use the [startIndex] and [endIndex] to report an error, and |
| 5379 * don't append anything to the builder, if the scalar value is invalid. The |
| 5380 * [escapeSequence] is the escape sequence that was parsed to produce the |
| 5381 * scalar value (used for error reporting). |
| 5382 */ |
| 5383 void _appendScalarValue(StringBuffer buffer, String escapeSequence, |
| 5384 int scalarValue, int startIndex, int endIndex) { |
| 5385 if (scalarValue < 0 || |
| 5386 scalarValue > Character.MAX_CODE_POINT || |
| 5387 (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) { |
| 5388 _reportErrorForCurrentToken( |
| 5389 ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]); |
| 5390 return; |
| 5391 } |
| 5392 if (scalarValue < Character.MAX_VALUE) { |
| 5393 buffer.writeCharCode(scalarValue); |
| 5394 } else { |
| 5395 buffer.write(Character.toChars(scalarValue)); |
| 5396 } |
| 5397 } |
| 5398 |
| 5399 /** |
| 5400 * Clone all token starting from the given [token] up to the end of the token |
| 5401 * stream, and return the first token in the new token stream. |
| 5402 */ |
| 5403 Token _cloneTokens(Token token) { |
| 5404 if (token == null) { |
| 5405 return null; |
| 5406 } |
| 5407 token = token is CommentToken ? token.parent : token; |
| 5408 Token head = new Token(TokenType.EOF, -1); |
| 5409 head.setNext(head); |
| 5410 Token current = head; |
| 5411 while (token.type != TokenType.EOF) { |
| 5412 Token clone = token.copy(); |
| 5413 current.setNext(clone); |
| 5414 current = clone; |
| 5415 token = token.next; |
| 5416 } |
| 5417 Token tail = new Token(TokenType.EOF, 0); |
| 5418 tail.setNext(tail); |
| 5419 current.setNext(tail); |
| 5420 return head.next; |
| 5421 } |
| 5422 |
| 5423 /** |
| 5424 * Convert the given [method] declaration into the nearest valid top-level |
| 5425 * function declaration (that is, the function declaration that most closely |
| 5426 * captures the components of the given method declaration). |
| 5427 */ |
| 5428 FunctionDeclaration _convertToFunctionDeclaration(MethodDeclaration method) => |
| 5429 new FunctionDeclaration( |
| 5430 method.documentationComment, |
| 5431 method.metadata, |
| 5432 method.externalKeyword, |
| 5433 method.returnType, |
| 5434 method.propertyKeyword, |
| 5435 method.name, |
| 5436 new FunctionExpression( |
| 5437 method.typeParameters, method.parameters, method.body)); |
| 5438 |
| 5439 /** |
| 5440 * Return `true` if the current token could be the start of a compilation unit |
| 5441 * member. This method is used for recovery purposes to decide when to stop |
| 5442 * skipping tokens after finding an error while parsing a compilation unit |
| 5443 * member. |
| 5444 */ |
| 5445 bool _couldBeStartOfCompilationUnitMember() { |
| 5446 Keyword keyword = _currentToken.keyword; |
| 5447 Token next = _currentToken.next; |
| 5448 TokenType nextType = next.type; |
| 5449 if ((keyword == Keyword.IMPORT || |
| 5450 keyword == Keyword.EXPORT || |
| 5451 keyword == Keyword.LIBRARY || |
| 5452 keyword == Keyword.PART) && |
| 5453 nextType != TokenType.PERIOD && |
| 5454 nextType != TokenType.LT) { |
| 5455 // This looks like the start of a directive |
| 5456 return true; |
| 5457 } else if (keyword == Keyword.CLASS) { |
| 5458 // This looks like the start of a class definition |
| 5459 return true; |
| 5460 } else if (keyword == Keyword.TYPEDEF && |
| 5461 nextType != TokenType.PERIOD && |
| 5462 nextType != TokenType.LT) { |
| 5463 // This looks like the start of a typedef |
| 5464 return true; |
| 5465 } else if (keyword == Keyword.VOID || |
| 5466 ((keyword == Keyword.GET || keyword == Keyword.SET) && |
| 5467 _tokenMatchesIdentifier(next)) || |
| 5468 (keyword == Keyword.OPERATOR && _isOperator(next))) { |
| 5469 // This looks like the start of a function |
| 5470 return true; |
| 5471 } else if (_matchesIdentifier()) { |
| 5472 if (nextType == TokenType.OPEN_PAREN) { |
| 5473 // This looks like the start of a function |
| 5474 return true; |
| 5475 } |
| 5476 Token token = skipReturnType(_currentToken); |
| 5477 if (token == null) { |
| 5478 return false; |
| 5479 } |
| 5480 // TODO(brianwilkerson) This looks wrong; should we be checking 'token'? |
| 5481 if (keyword == Keyword.GET || |
| 5482 keyword == Keyword.SET || |
| 5483 (keyword == Keyword.OPERATOR && _isOperator(next)) || |
| 5484 _matchesIdentifier()) { |
| 5485 return true; |
| 5486 } |
| 5487 } |
| 5488 return false; |
| 5489 } |
| 5490 |
| 5491 /** |
| 5492 * Return a synthetic token representing the given [keyword]. |
| 5493 */ |
| 5494 Token _createSyntheticKeyword(Keyword keyword) => _injectToken( |
| 5495 new Parser_SyntheticKeywordToken(keyword, _currentToken.offset)); |
| 5496 |
| 5497 /** |
| 5498 * Return a synthetic token with the given [type]. |
| 5499 */ |
| 5500 Token _createSyntheticToken(TokenType type) => |
| 5501 _injectToken(new StringToken(type, "", _currentToken.offset)); |
| 5502 |
| 5503 /** |
| 5504 * Create and return a new token with the given [type]. The token will replace |
| 5505 * the first portion of the given [token], so it will have the same offset and |
| 5506 * will have any comments that might have preceeded the token. |
| 5507 */ |
| 5508 Token _createToken(Token token, TokenType type, {bool isBegin: false}) { |
| 5509 CommentToken comments = token.precedingComments; |
| 5510 if (comments == null) { |
| 5511 if (isBegin) { |
| 5512 return new BeginToken(type, token.offset); |
| 5513 } |
| 5514 return new Token(type, token.offset); |
| 5515 } else if (isBegin) { |
| 5516 return new BeginTokenWithComment(type, token.offset, comments); |
| 5517 } |
| 5518 return new TokenWithComment(type, token.offset, comments); |
| 5519 } |
| 5520 |
| 5521 /** |
| 5522 * Check that the given [expression] is assignable and report an error if it |
| 5523 * isn't. |
| 5524 * |
| 5525 * assignableExpression ::= |
| 5526 * primary (arguments* assignableSelector)+ |
| 5527 * | 'super' unconditionalAssignableSelector |
| 5528 * | identifier |
| 5529 * |
| 5530 * unconditionalAssignableSelector ::= |
| 5531 * '[' expression ']' |
| 5532 * | '.' identifier |
| 5533 * |
| 5534 * assignableSelector ::= |
| 5535 * unconditionalAssignableSelector |
| 5536 * | '?.' identifier |
| 5537 */ |
| 5538 void _ensureAssignable(Expression expression) { |
| 5539 if (expression != null && !expression.isAssignable) { |
| 5540 _reportErrorForCurrentToken( |
| 5541 ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE); |
| 5542 } |
| 5543 } |
| 5544 |
| 5545 /** |
| 5546 * If the current token has the expected type, return it after advancing to |
| 5547 * the next token. Otherwise report an error and return the current token |
| 5548 * without advancing. |
| 5549 * |
| 5550 * Note that the method [_expectGt] should be used if the argument to this |
| 5551 * method would be [TokenType.GT]. |
| 5552 * |
| 5553 * The [type] is the type of token that is expected. |
| 5554 */ |
| 5555 Token _expect(TokenType type) { |
| 5556 if (_matches(type)) { |
| 5557 return getAndAdvance(); |
| 5558 } |
| 5559 // Remove uses of this method in favor of matches? |
| 5560 // Pass in the error code to use to report the error? |
| 5561 if (type == TokenType.SEMICOLON) { |
| 5562 if (_tokenMatches(_currentToken.next, TokenType.SEMICOLON)) { |
| 5563 _reportErrorForCurrentToken( |
| 5564 ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); |
| 5565 _advance(); |
| 5566 return getAndAdvance(); |
| 5567 } |
| 5568 _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN, |
| 5569 _currentToken.previous, [type.lexeme]); |
| 5570 return _createSyntheticToken(TokenType.SEMICOLON); |
| 5571 } |
| 5572 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]); |
| 5573 return _createSyntheticToken(type); |
| 5574 } |
| 5575 |
| 5576 /** |
| 5577 * If the current token has the type [TokenType.GT], return it after advancing |
| 5578 * to the next token. Otherwise report an error and create a synthetic token. |
| 5579 */ |
| 5580 Token _expectGt() { |
| 5581 if (_matchesGt()) { |
| 5582 return getAndAdvance(); |
| 5583 } |
| 5584 _reportErrorForCurrentToken( |
| 5585 ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]); |
| 5586 return _createSyntheticToken(TokenType.GT); |
| 5587 } |
| 5588 |
| 5589 /** |
| 5590 * If the current token is a keyword matching the given [keyword], return it |
| 5591 * after advancing to the next token. Otherwise report an error and return the |
| 5592 * current token without advancing. |
| 5593 */ |
| 5594 Token _expectKeyword(Keyword keyword) { |
| 5595 if (_matchesKeyword(keyword)) { |
| 5596 return getAndAdvance(); |
| 5597 } |
| 5598 // Remove uses of this method in favor of matches? |
| 5599 // Pass in the error code to use to report the error? |
| 5600 _reportErrorForCurrentToken( |
| 5601 ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]); |
| 5602 return _currentToken; |
| 5603 } |
| 5604 |
| 5605 /** |
| 5606 * Search the given list of [ranges] for a range that contains the given |
| 5607 * [index]. Return the range that was found, or `null` if none of the ranges |
| 5608 * contain the index. |
| 5609 */ |
| 5610 List<int> _findRange(List<List<int>> ranges, int index) { |
| 5611 int rangeCount = ranges.length; |
| 5612 for (int i = 0; i < rangeCount; i++) { |
| 5613 List<int> range = ranges[i]; |
| 5614 if (range[0] <= index && index <= range[1]) { |
| 5615 return range; |
| 5616 } else if (index < range[0]) { |
| 5617 return null; |
| 5618 } |
| 5619 } |
| 5620 return null; |
| 5621 } |
| 5622 |
| 5623 /** |
| 5624 * Return a list of the ranges of characters in the given [comment] that |
| 5625 * should be treated as code blocks. |
| 5626 */ |
| 5627 List<List<int>> _getCodeBlockRanges(String comment) { |
| 5628 List<List<int>> ranges = <List<int>>[]; |
| 5629 int length = comment.length; |
| 5630 if (length < 3) { |
| 5631 return ranges; |
| 5632 } |
| 5633 int index = 0; |
| 5634 int firstChar = comment.codeUnitAt(0); |
| 5635 if (firstChar == 0x2F) { |
| 5636 int secondChar = comment.codeUnitAt(1); |
| 5637 int thirdChar = comment.codeUnitAt(2); |
| 5638 if ((secondChar == 0x2A && thirdChar == 0x2A) || |
| 5639 (secondChar == 0x2F && thirdChar == 0x2F)) { |
| 5640 index = 3; |
| 5641 } |
| 5642 } |
| 5643 if (StringUtilities.startsWith4(comment, index, 0x20, 0x20, 0x20, 0x20)) { |
| 5644 int end = index + 4; |
| 5645 while (end < length && |
| 5646 comment.codeUnitAt(end) != 0xD && |
| 5647 comment.codeUnitAt(end) != 0xA) { |
| 5648 end = end + 1; |
| 5649 } |
| 5650 ranges.add(<int>[index, end]); |
| 5651 index = end; |
| 5652 } |
| 5653 while (index < length) { |
| 5654 int currentChar = comment.codeUnitAt(index); |
| 5655 if (currentChar == 0xD || currentChar == 0xA) { |
| 5656 index = index + 1; |
| 5657 while (index < length && |
| 5658 Character.isWhitespace(comment.codeUnitAt(index))) { |
| 5659 index = index + 1; |
| 5660 } |
| 5661 if (StringUtilities.startsWith6( |
| 5662 comment, index, 0x2A, 0x20, 0x20, 0x20, 0x20, 0x20)) { |
| 5663 int end = index + 6; |
| 5664 while (end < length && |
| 5665 comment.codeUnitAt(end) != 0xD && |
| 5666 comment.codeUnitAt(end) != 0xA) { |
| 5667 end = end + 1; |
| 5668 } |
| 5669 ranges.add(<int>[index, end]); |
| 5670 index = end; |
| 5671 } |
| 5672 } else if (index + 1 < length && |
| 5673 currentChar == 0x5B && |
| 5674 comment.codeUnitAt(index + 1) == 0x3A) { |
| 5675 int end = StringUtilities.indexOf2(comment, index + 2, 0x3A, 0x5D); |
| 5676 if (end < 0) { |
| 5677 end = length; |
| 5678 } |
| 5679 ranges.add(<int>[index, end]); |
| 5680 index = end + 1; |
| 5681 } else { |
| 5682 index = index + 1; |
| 5683 } |
| 5684 } |
| 5685 return ranges; |
| 5686 } |
| 5687 |
| 5688 /** |
| 5689 * Return the end token associated with the given [beginToken], or `null` if |
| 5690 * either the given token is not a begin token or it does not have an end |
| 5691 * token associated with it. |
| 5692 */ |
| 5693 Token _getEndToken(Token beginToken) { |
| 5694 if (beginToken is BeginToken) { |
| 5695 return beginToken.endToken; |
| 5696 } |
| 5697 return null; |
| 5698 } |
| 5699 |
| 5700 bool _injectGenericComment(TokenType type, int prefixLen) { |
| 5701 if (parseGenericMethodComments) { |
| 5702 CommentToken t = _currentToken.precedingComments; |
| 5703 for (; t != null; t = t.next) { |
| 5704 if (t.type == type) { |
| 5705 String comment = t.lexeme.substring(prefixLen, t.lexeme.length - 2); |
| 5706 Token list = _scanGenericMethodComment(comment, t.offset + prefixLen); |
| 5707 if (list != null) { |
| 5708 // Remove the token from the comment stream. |
| 5709 t.remove(); |
| 5710 // Insert the tokens into the stream. |
| 5711 _injectTokenList(list); |
| 5712 return true; |
| 5713 } |
| 5714 } |
| 5715 } |
| 5716 } |
| 5717 return false; |
| 5718 } |
| 5719 |
| 5720 /** |
| 5721 * Matches a generic comment type substitution and injects it into the token |
| 5722 * stream. Returns true if a match was injected, otherwise false. |
| 5723 * |
| 5724 * These comments are of the form `/*=T*/`, in other words, a [TypeName] |
| 5725 * inside a slash-star comment, preceded by equals sign. |
| 5726 */ |
| 5727 bool _injectGenericCommentTypeAssign() { |
| 5728 return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_ASSIGN, 3); |
| 5729 } |
| 5730 |
| 5731 /** |
| 5732 * Matches a generic comment type parameters and injects them into the token |
| 5733 * stream. Returns true if a match was injected, otherwise false. |
| 5734 * |
| 5735 * These comments are of the form `/*<K, V>*/`, in other words, a |
| 5736 * [TypeParameterList] or [TypeArgumentList] inside a slash-star comment. |
| 5737 */ |
| 5738 bool _injectGenericCommentTypeList() { |
| 5739 return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_LIST, 2); |
| 5740 } |
| 5741 |
| 5742 /** |
| 5743 * Inject the given [token] into the token stream immediately before the |
| 5744 * current token. |
| 5745 */ |
| 5746 Token _injectToken(Token token) { |
| 5747 Token previous = _currentToken.previous; |
| 5748 token.setNext(_currentToken); |
| 5749 previous.setNext(token); |
| 5750 return token; |
| 5751 } |
| 5752 |
| 5753 void _injectTokenList(Token firstToken) { |
| 5754 // Scanner creates a cyclic EOF token. |
| 5755 Token lastToken = firstToken; |
| 5756 while (lastToken.next.type != TokenType.EOF) { |
| 5757 lastToken = lastToken.next; |
| 5758 } |
| 5759 // Inject these new tokens into the stream. |
| 5760 Token previous = _currentToken.previous; |
| 5761 lastToken.setNext(_currentToken); |
| 5762 previous.setNext(firstToken); |
| 5763 _currentToken = firstToken; |
| 5764 } |
| 5765 |
| 5766 /** |
| 5767 * Return `true` if the current token could be the question mark in a |
| 5768 * condition expression. The current token is assumed to be a question mark. |
| 5769 */ |
| 5770 bool _isConditionalOperator() { |
| 5771 void parseOperation(Parser parser) { |
| 5772 parser.parseExpressionWithoutCascade(); |
| 5773 } |
| 5774 |
| 5775 Token token = _skip(_currentToken.next, parseOperation); |
| 5776 if (token == null || !_tokenMatches(token, TokenType.COLON)) { |
| 5777 return false; |
| 5778 } |
| 5779 token = _skip(token.next, parseOperation); |
| 5780 return token != null; |
| 5781 } |
| 5782 |
| 5783 /** |
| 5784 * Return `true` if the given [character] is a valid hexadecimal digit. |
| 5785 */ |
| 5786 bool _isHexDigit(int character) => |
| 5787 (0x30 <= character && character <= 0x39) || |
| 5788 (0x41 <= character && character <= 0x46) || |
| 5789 (0x61 <= character && character <= 0x66); |
| 5790 |
| 5791 bool _isLikelyArgumentList() { |
| 5792 // Try to reduce the amount of lookahead required here before enabling |
| 5793 // generic methods. |
| 5794 if (_matches(TokenType.OPEN_PAREN)) { |
| 5795 return true; |
| 5796 } |
| 5797 if (!parseGenericMethods) { |
| 5798 return false; |
| 5799 } |
| 5800 Token token = skipTypeArgumentList(_currentToken); |
| 5801 return token != null && _tokenMatches(token, TokenType.OPEN_PAREN); |
| 5802 } |
| 5803 |
| 5804 /** |
| 5805 * Given that we have just found bracketed text within the given [comment], |
| 5806 * look to see whether that text is (a) followed by a parenthesized link |
| 5807 * address, (b) followed by a colon, or (c) followed by optional whitespace |
| 5808 * and another square bracket. The [rightIndex] is the index of the right |
| 5809 * bracket. Return `true` if the bracketed text is followed by a link address. |
| 5810 * |
| 5811 * This method uses the syntax described by the |
| 5812 * <a href="http://daringfireball.net/projects/markdown/syntax">markdown</a> |
| 5813 * project. |
| 5814 */ |
| 5815 bool _isLinkText(String comment, int rightIndex) { |
| 5816 int length = comment.length; |
| 5817 int index = rightIndex + 1; |
| 5818 if (index >= length) { |
| 5819 return false; |
| 5820 } |
| 5821 int nextChar = comment.codeUnitAt(index); |
| 5822 if (nextChar == 0x28 || nextChar == 0x3A) { |
| 5823 return true; |
| 5824 } |
| 5825 while (Character.isWhitespace(nextChar)) { |
| 5826 index = index + 1; |
| 5827 if (index >= length) { |
| 5828 return false; |
| 5829 } |
| 5830 nextChar = comment.codeUnitAt(index); |
| 5831 } |
| 5832 return nextChar == 0x5B; |
| 5833 } |
| 5834 |
| 5835 /** |
| 5836 * Return `true` if the given [startToken] appears to be the beginning of an |
| 5837 * operator declaration. |
| 5838 */ |
| 5839 bool _isOperator(Token startToken) { |
| 5840 // Accept any operator here, even if it is not user definable. |
| 5841 if (!startToken.isOperator) { |
| 5842 return false; |
| 5843 } |
| 5844 // Token "=" means that it is actually a field initializer. |
| 5845 if (startToken.type == TokenType.EQ) { |
| 5846 return false; |
| 5847 } |
| 5848 // Consume all operator tokens. |
| 5849 Token token = startToken.next; |
| 5850 while (token.isOperator) { |
| 5851 token = token.next; |
| 5852 } |
| 5853 // Formal parameter list is expect now. |
| 5854 return _tokenMatches(token, TokenType.OPEN_PAREN); |
| 5855 } |
| 5856 |
| 5857 bool _isPeekGenericTypeParametersAndOpenParen() { |
| 5858 if (!parseGenericMethods) { |
| 5859 return false; |
| 5860 } |
| 5861 Token token = _skipTypeParameterList(_peek()); |
| 5862 return token != null && _tokenMatches(token, TokenType.OPEN_PAREN); |
| 5863 } |
| 5864 |
| 5865 /** |
| 5866 * Return `true` if the [startToken] appears to be the first token of a type |
| 5867 * name that is followed by a variable or field formal parameter. |
| 5868 */ |
| 5869 bool _isTypedIdentifier(Token startToken) { |
| 5870 Token token = skipReturnType(startToken); |
| 5871 if (token == null) { |
| 5872 return false; |
| 5873 } else if (_tokenMatchesIdentifier(token)) { |
| 5874 return true; |
| 5875 } else if (_tokenMatchesKeyword(token, Keyword.THIS) && |
| 5876 _tokenMatches(token.next, TokenType.PERIOD) && |
| 5877 _tokenMatchesIdentifier(token.next.next)) { |
| 5878 return true; |
| 5879 } else if (_tokenMatchesKeyword(startToken, Keyword.VOID)) { |
| 5880 // The keyword 'void' isn't a valid identifier, so it should be assumed to |
| 5881 // be a type name. |
| 5882 return true; |
| 5883 } else if (startToken.next != token && |
| 5884 !_tokenMatches(token, TokenType.OPEN_PAREN)) { |
| 5885 // The type is more than a simple identifier, so it should be assumed to |
| 5886 // be a type name. |
| 5887 return true; |
| 5888 } |
| 5889 return false; |
| 5890 } |
| 5891 |
| 5892 /** |
| 5893 * Increments the error reporting lock level. If level is more than `0`, then |
| 5894 * [reportError] wont report any error. |
| 5895 */ |
| 5896 void _lockErrorListener() { |
| 5897 _errorListenerLock++; |
| 5898 } |
| 5899 |
| 5900 /** |
| 5901 * Return `true` if the current token has the given [type]. Note that the |
| 5902 * method [_matchesGt] should be used if the argument to this method would be |
| 5903 * [TokenType.GT]. |
| 5904 */ |
| 5905 bool _matches(TokenType type) => _currentToken.type == type; |
| 5906 |
| 5907 /** |
| 5908 * Return `true` if the current token has a type of [TokenType.GT]. Note that |
| 5909 * this method, unlike other variants, will modify the token stream if |
| 5910 * possible to match desired type. In particular, if the next token is either |
| 5911 * a '>>' or '>>>', the token stream will be re-written and `true` will be |
| 5912 * returned. |
| 5913 */ |
| 5914 bool _matchesGt() { |
| 5915 TokenType currentType = _currentToken.type; |
| 5916 if (currentType == TokenType.GT) { |
| 5917 return true; |
| 5918 } else if (currentType == TokenType.GT_GT) { |
| 5919 Token first = _createToken(_currentToken, TokenType.GT); |
| 5920 Token second = new Token(TokenType.GT, _currentToken.offset + 1); |
| 5921 second.setNext(_currentToken.next); |
| 5922 first.setNext(second); |
| 5923 _currentToken.previous.setNext(first); |
| 5924 _currentToken = first; |
| 5925 return true; |
| 5926 } else if (currentType == TokenType.GT_EQ) { |
| 5927 Token first = _createToken(_currentToken, TokenType.GT); |
| 5928 Token second = new Token(TokenType.EQ, _currentToken.offset + 1); |
| 5929 second.setNext(_currentToken.next); |
| 5930 first.setNext(second); |
| 5931 _currentToken.previous.setNext(first); |
| 5932 _currentToken = first; |
| 5933 return true; |
| 5934 } else if (currentType == TokenType.GT_GT_EQ) { |
| 5935 int offset = _currentToken.offset; |
| 5936 Token first = _createToken(_currentToken, TokenType.GT); |
| 5937 Token second = new Token(TokenType.GT, offset + 1); |
| 5938 Token third = new Token(TokenType.EQ, offset + 2); |
| 5939 third.setNext(_currentToken.next); |
| 5940 second.setNext(third); |
| 5941 first.setNext(second); |
| 5942 _currentToken.previous.setNext(first); |
| 5943 _currentToken = first; |
| 5944 return true; |
| 5945 } |
| 5946 return false; |
| 5947 } |
| 5948 |
| 5949 /** |
| 5950 * Return `true` if the current token is a valid identifier. Valid identifiers |
| 5951 * include built-in identifiers (pseudo-keywords). |
| 5952 */ |
| 5953 bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken); |
| 5954 |
| 5955 /** |
| 5956 * Return `true` if the current token matches the given [keyword]. |
| 5957 */ |
| 5958 bool _matchesKeyword(Keyword keyword) => |
| 5959 _tokenMatchesKeyword(_currentToken, keyword); |
| 5960 |
| 5961 /** |
| 5962 * Return `true` if the current token matches the given [identifier]. |
| 5963 */ |
| 5964 bool _matchesString(String identifier) => |
| 5965 _currentToken.type == TokenType.IDENTIFIER && |
| 5966 _currentToken.lexeme == identifier; |
| 5967 |
| 5968 /** |
| 5969 * If the current token has the given [type], then advance to the next token |
| 5970 * and return `true`. Otherwise, return `false` without advancing. This method |
| 5971 * should not be invoked with an argument value of [TokenType.GT]. |
| 5972 */ |
| 5973 bool _optional(TokenType type) { |
| 5974 if (_currentToken.type == type) { |
| 5975 _advance(); |
| 5976 return true; |
| 5977 } |
| 5978 return false; |
| 5979 } |
| 5980 |
| 5981 /** |
| 5982 * Parse an argument list when we need to check for an open paren and recover |
| 5983 * when there isn't one. Return the argument list that was parsed. |
| 5984 */ |
| 5985 ArgumentList _parseArgumentListChecked() { |
| 5986 if (_matches(TokenType.OPEN_PAREN)) { |
| 5987 return parseArgumentList(); |
| 5988 } |
| 5989 _reportErrorForCurrentToken( |
| 5990 ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_PAREN.lexeme]); |
| 5991 // Recovery: Look to see whether there is a close paren that isn't matched |
| 5992 // to an open paren and if so parse the list of arguments as normal. |
| 5993 return new ArgumentList(_createSyntheticToken(TokenType.OPEN_PAREN), null, |
| 5994 _createSyntheticToken(TokenType.CLOSE_PAREN)); |
| 5995 } |
| 5996 |
| 5997 /** |
| 5998 * Parse an assert within a constructor's initializer list. Return the assert. |
| 5999 * |
| 6000 * This method assumes that the current token matches `Keyword.ASSERT`. |
| 6001 * |
| 6002 * assertInitializer ::= |
| 6003 * 'assert' '(' expression [',' expression] ')' |
| 6004 */ |
| 6005 void _parseAssertInitializer() { |
| 6006 // TODO(brianwilkerson) Capture the syntax in the AST using a new class, |
| 6007 // such as AssertInitializer |
| 6008 Token keyword = getAndAdvance(); |
| 6009 Token leftParen = _expect(TokenType.OPEN_PAREN); |
| 6010 Expression expression = parseExpression2(); |
| 6011 Token comma; |
| 6012 Expression message; |
| 6013 if (_matches(TokenType.COMMA)) { |
| 6014 comma = getAndAdvance(); |
| 6015 message = parseExpression2(); |
| 6016 } |
| 6017 Token rightParen = _expect(TokenType.CLOSE_PAREN); |
| 6018 // return new AssertInitializer( |
| 6019 // keyword, leftParen, expression, comma, message, rightParen); |
| 6020 } |
| 6021 |
| 6022 /** |
| 6023 * Parse an assignable expression given that the current token is not 'super'. |
| 6024 * The [primaryAllowed] is `true` if the expression is allowed to be a primary |
| 6025 * without any assignable selector. Return the assignable expression that was |
| 6026 * parsed. |
| 6027 */ |
| 6028 Expression _parseAssignableExpressionNotStartingWithSuper( |
| 6029 bool primaryAllowed) { |
| 6030 // |
| 6031 // A primary expression can start with an identifier. We resolve the |
| 6032 // ambiguity by determining whether the primary consists of anything other |
| 6033 // than an identifier and/or is followed by an assignableSelector. |
| 6034 // |
| 6035 Expression expression = parsePrimaryExpression(); |
| 6036 bool isOptional = primaryAllowed || expression is SimpleIdentifier; |
| 6037 while (true) { |
| 6038 while (_isLikelyArgumentList()) { |
| 6039 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); |
| 6040 ArgumentList argumentList = parseArgumentList(); |
| 6041 Expression currentExpression = expression; |
| 6042 if (currentExpression is SimpleIdentifier) { |
| 6043 expression = new MethodInvocation( |
| 6044 null, null, currentExpression, typeArguments, argumentList); |
| 6045 } else if (currentExpression is PrefixedIdentifier) { |
| 6046 expression = new MethodInvocation( |
| 6047 currentExpression.prefix, |
| 6048 currentExpression.period, |
| 6049 currentExpression.identifier, |
| 6050 typeArguments, |
| 6051 argumentList); |
| 6052 } else if (currentExpression is PropertyAccess) { |
| 6053 expression = new MethodInvocation( |
| 6054 currentExpression.target, |
| 6055 currentExpression.operator, |
| 6056 currentExpression.propertyName, |
| 6057 typeArguments, |
| 6058 argumentList); |
| 6059 } else { |
| 6060 expression = new FunctionExpressionInvocation( |
| 6061 expression, typeArguments, argumentList); |
| 6062 } |
| 6063 if (!primaryAllowed) { |
| 6064 isOptional = false; |
| 6065 } |
| 6066 } |
| 6067 Expression selectorExpression = parseAssignableSelector( |
| 6068 expression, isOptional || (expression is PrefixedIdentifier)); |
| 6069 if (identical(selectorExpression, expression)) { |
| 6070 if (!isOptional && (expression is PrefixedIdentifier)) { |
| 6071 PrefixedIdentifier identifier = expression as PrefixedIdentifier; |
| 6072 expression = new PropertyAccess( |
| 6073 identifier.prefix, identifier.period, identifier.identifier); |
| 6074 } |
| 6075 return expression; |
| 6076 } |
| 6077 expression = selectorExpression; |
| 6078 isOptional = true; |
| 6079 } |
| 6080 } |
| 6081 |
| 6082 /** |
| 6083 * Parse a block when we need to check for an open curly brace and recover |
| 6084 * when there isn't one. Return the block that was parsed. |
| 6085 * |
| 6086 * block ::= |
| 6087 * '{' statements '}' |
| 6088 */ |
| 6089 Block _parseBlockChecked() { |
| 6090 if (_matches(TokenType.OPEN_CURLY_BRACKET)) { |
| 6091 return parseBlock(); |
| 6092 } |
| 6093 // TODO(brianwilkerson) Improve the error message. |
| 6094 _reportErrorForCurrentToken( |
| 6095 ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_CURLY_BRACKET.lexeme]); |
| 6096 // Recovery: Check for an unmatched closing curly bracket and parse |
| 6097 // statements until it is reached. |
| 6098 return new Block(_createSyntheticToken(TokenType.OPEN_CURLY_BRACKET), null, |
| 6099 _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET)); |
| 6100 } |
| 6101 |
| 6102 /** |
| 6103 * Parse a list of class members. The [className] is the name of the class |
| 6104 * whose members are being parsed. The [closingBracket] is the closing bracket |
| 6105 * for the class, or `null` if the closing bracket is missing. Return the list |
| 6106 * of class members that were parsed. |
| 6107 * |
| 6108 * classMembers ::= |
| 6109 * (metadata memberDefinition)* |
| 6110 */ |
| 6111 List<ClassMember> _parseClassMembers(String className, Token closingBracket) { |
| 6112 List<ClassMember> members = <ClassMember>[]; |
| 6113 Token memberStart = _currentToken; |
| 6114 TokenType type = _currentToken.type; |
| 6115 Keyword keyword = _currentToken.keyword; |
| 6116 while (type != TokenType.EOF && |
| 6117 type != TokenType.CLOSE_CURLY_BRACKET && |
| 6118 (closingBracket != null || |
| 6119 (keyword != Keyword.CLASS && keyword != Keyword.TYPEDEF))) { |
| 6120 if (type == TokenType.SEMICOLON) { |
| 6121 _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, |
| 6122 [_currentToken.lexeme]); |
| 6123 _advance(); |
| 6124 } else { |
| 6125 ClassMember member = parseClassMember(className); |
| 6126 if (member != null) { |
| 6127 members.add(member); |
| 6128 } |
| 6129 } |
| 6130 if (identical(_currentToken, memberStart)) { |
| 6131 _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, |
| 6132 [_currentToken.lexeme]); |
| 6133 _advance(); |
| 6134 } |
| 6135 memberStart = _currentToken; |
| 6136 type = _currentToken.type; |
| 6137 keyword = _currentToken.keyword; |
| 6138 } |
| 6139 return members; |
| 6140 } |
| 6141 |
| 6142 /** |
| 6143 * Parse a class type alias. The [commentAndMetadata] is the metadata to be |
| 6144 * associated with the member. The [abstractKeyword] is the token representing |
| 6145 * the 'abstract' keyword. The [classKeyword] is the token representing the |
| 6146 * 'class' keyword. The [className] is the name of the alias, and the |
| 6147 * [typeParameters] are the type parameters following the name. Return the |
| 6148 * class type alias that was parsed. |
| 6149 * |
| 6150 * classTypeAlias ::= |
| 6151 * identifier typeParameters? '=' 'abstract'? mixinApplication |
| 6152 * |
| 6153 * mixinApplication ::= |
| 6154 * type withClause implementsClause? ';' |
| 6155 */ |
| 6156 ClassTypeAlias _parseClassTypeAliasAfterName( |
| 6157 CommentAndMetadata commentAndMetadata, |
| 6158 Token abstractKeyword, |
| 6159 Token classKeyword, |
| 6160 SimpleIdentifier className, |
| 6161 TypeParameterList typeParameters) { |
| 6162 Token equals = _expect(TokenType.EQ); |
| 6163 TypeName superclass = parseTypeName(false); |
| 6164 WithClause withClause = null; |
| 6165 if (_matchesKeyword(Keyword.WITH)) { |
| 6166 withClause = parseWithClause(); |
| 6167 } else { |
| 6168 _reportErrorForCurrentToken( |
| 6169 ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]); |
| 6170 } |
| 6171 ImplementsClause implementsClause = null; |
| 6172 if (_matchesKeyword(Keyword.IMPLEMENTS)) { |
| 6173 implementsClause = parseImplementsClause(); |
| 6174 } |
| 6175 Token semicolon; |
| 6176 if (_matches(TokenType.SEMICOLON)) { |
| 6177 semicolon = getAndAdvance(); |
| 6178 } else { |
| 6179 if (_matches(TokenType.OPEN_CURLY_BRACKET)) { |
| 6180 _reportErrorForCurrentToken( |
| 6181 ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]); |
| 6182 Token leftBracket = getAndAdvance(); |
| 6183 _parseClassMembers(className.name, _getEndToken(leftBracket)); |
| 6184 _expect(TokenType.CLOSE_CURLY_BRACKET); |
| 6185 } else { |
| 6186 _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN, |
| 6187 _currentToken.previous, [TokenType.SEMICOLON.lexeme]); |
| 6188 } |
| 6189 semicolon = _createSyntheticToken(TokenType.SEMICOLON); |
| 6190 } |
| 6191 return new ClassTypeAlias( |
| 6192 commentAndMetadata.comment, |
| 6193 commentAndMetadata.metadata, |
| 6194 classKeyword, |
| 6195 className, |
| 6196 typeParameters, |
| 6197 equals, |
| 6198 abstractKeyword, |
| 6199 superclass, |
| 6200 withClause, |
| 6201 implementsClause, |
| 6202 semicolon); |
| 6203 } |
| 6204 |
| 6205 /** |
| 6206 * Parse a list of configurations. Return the configurations that were parsed, |
| 6207 * or `null` if there are no configurations. |
| 6208 */ |
| 6209 List<Configuration> _parseConfigurations() { |
| 6210 List<Configuration> configurations = null; |
| 6211 while (_matchesKeyword(Keyword.IF)) { |
| 6212 configurations ??= <Configuration>[]; |
| 6213 configurations.add(parseConfiguration()); |
| 6214 } |
| 6215 return configurations; |
| 6216 } |
| 6217 |
| 6218 ConstructorDeclaration _parseConstructor( |
| 6219 CommentAndMetadata commentAndMetadata, |
| 6220 Token externalKeyword, |
| 6221 Token constKeyword, |
| 6222 Token factoryKeyword, |
| 6223 SimpleIdentifier returnType, |
| 6224 Token period, |
| 6225 SimpleIdentifier name, |
| 6226 FormalParameterList parameters) { |
| 6227 bool bodyAllowed = externalKeyword == null; |
| 6228 Token separator = null; |
| 6229 List<ConstructorInitializer> initializers = null; |
| 6230 if (_matches(TokenType.COLON)) { |
| 6231 separator = getAndAdvance(); |
| 6232 initializers = <ConstructorInitializer>[]; |
| 6233 do { |
| 6234 Keyword keyword = _currentToken.keyword; |
| 6235 if (keyword == Keyword.THIS) { |
| 6236 TokenType nextType = _peek().type; |
| 6237 if (nextType == TokenType.OPEN_PAREN) { |
| 6238 bodyAllowed = false; |
| 6239 initializers.add(parseRedirectingConstructorInvocation(false)); |
| 6240 } else if (nextType == TokenType.PERIOD && |
| 6241 _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) { |
| 6242 bodyAllowed = false; |
| 6243 initializers.add(parseRedirectingConstructorInvocation(true)); |
| 6244 } else { |
| 6245 initializers.add(parseConstructorFieldInitializer(true)); |
| 6246 } |
| 6247 } else if (keyword == Keyword.SUPER) { |
| 6248 initializers.add(parseSuperConstructorInvocation()); |
| 6249 } else if (_matches(TokenType.OPEN_CURLY_BRACKET) || |
| 6250 _matches(TokenType.FUNCTION)) { |
| 6251 _reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER); |
| 6252 } else if (_enableAssertInitializer && |
| 6253 _matchesKeyword(Keyword.ASSERT)) { |
| 6254 _parseAssertInitializer(); |
| 6255 } else { |
| 6256 initializers.add(parseConstructorFieldInitializer(false)); |
| 6257 } |
| 6258 } while (_optional(TokenType.COMMA)); |
| 6259 if (factoryKeyword != null) { |
| 6260 _reportErrorForToken( |
| 6261 ParserErrorCode.FACTORY_WITH_INITIALIZERS, factoryKeyword); |
| 6262 } |
| 6263 } |
| 6264 ConstructorName redirectedConstructor = null; |
| 6265 FunctionBody body; |
| 6266 if (_matches(TokenType.EQ)) { |
| 6267 separator = getAndAdvance(); |
| 6268 redirectedConstructor = parseConstructorName(); |
| 6269 body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON)); |
| 6270 if (factoryKeyword == null) { |
| 6271 _reportErrorForNode( |
| 6272 ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR, |
| 6273 redirectedConstructor); |
| 6274 } |
| 6275 } else { |
| 6276 body = |
| 6277 parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false); |
| 6278 if (constKeyword != null && |
| 6279 factoryKeyword != null && |
| 6280 externalKeyword == null) { |
| 6281 _reportErrorForToken(ParserErrorCode.CONST_FACTORY, factoryKeyword); |
| 6282 } else if (body is EmptyFunctionBody) { |
| 6283 if (factoryKeyword != null && |
| 6284 externalKeyword == null && |
| 6285 _parseFunctionBodies) { |
| 6286 _reportErrorForToken( |
| 6287 ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword); |
| 6288 } |
| 6289 } else { |
| 6290 if (constKeyword != null) { |
| 6291 _reportErrorForNode( |
| 6292 ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body); |
| 6293 } else if (externalKeyword != null) { |
| 6294 _reportErrorForNode( |
| 6295 ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body); |
| 6296 } else if (!bodyAllowed) { |
| 6297 _reportErrorForNode( |
| 6298 ParserErrorCode.REDIRECTING_CONSTRUCTOR_WITH_BODY, body); |
| 6299 } |
| 6300 } |
| 6301 } |
| 6302 return new ConstructorDeclaration( |
| 6303 commentAndMetadata.comment, |
| 6304 commentAndMetadata.metadata, |
| 6305 externalKeyword, |
| 6306 constKeyword, |
| 6307 factoryKeyword, |
| 6308 returnType, |
| 6309 period, |
| 6310 name, |
| 6311 parameters, |
| 6312 separator, |
| 6313 initializers, |
| 6314 redirectedConstructor, |
| 6315 body); |
| 6316 } |
| 6317 |
| 6318 /** |
| 6319 * Parse an enum constant declaration. Return the enum constant declaration |
| 6320 * that was parsed. |
| 6321 * |
| 6322 * Specified: |
| 6323 * |
| 6324 * enumConstant ::= |
| 6325 * id |
| 6326 * |
| 6327 * Actual: |
| 6328 * |
| 6329 * enumConstant ::= |
| 6330 * metadata id |
| 6331 */ |
| 6332 EnumConstantDeclaration _parseEnumConstantDeclaration() { |
| 6333 CommentAndMetadata commentAndMetadata = parseCommentAndMetadata(); |
| 6334 SimpleIdentifier name; |
| 6335 if (_matchesIdentifier()) { |
| 6336 name = _parseSimpleIdentifierUnchecked(isDeclaration: true); |
| 6337 } else { |
| 6338 name = createSyntheticIdentifier(); |
| 6339 } |
| 6340 if (commentAndMetadata.hasMetadata) { |
| 6341 _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT, |
| 6342 commentAndMetadata.metadata[0]); |
| 6343 } |
| 6344 return new EnumConstantDeclaration( |
| 6345 commentAndMetadata.comment, commentAndMetadata.metadata, name); |
| 6346 } |
| 6347 |
| 6348 /** |
| 6349 * Parse a list of formal parameters given that the list starts with the given |
| 6350 * [leftParenthesis]. Return the formal parameters that were parsed. |
| 6351 */ |
| 6352 FormalParameterList _parseFormalParameterListAfterParen( |
| 6353 Token leftParenthesis) { |
| 6354 if (_matches(TokenType.CLOSE_PAREN)) { |
| 6355 return new FormalParameterList( |
| 6356 leftParenthesis, null, null, null, getAndAdvance()); |
| 6357 } |
| 6358 // |
| 6359 // Even though it is invalid to have default parameters outside of brackets, |
| 6360 // required parameters inside of brackets, or multiple groups of default and |
| 6361 // named parameters, we allow all of these cases so that we can recover |
| 6362 // better. |
| 6363 // |
| 6364 List<FormalParameter> parameters = <FormalParameter>[]; |
| 6365 Token leftSquareBracket = null; |
| 6366 Token rightSquareBracket = null; |
| 6367 Token leftCurlyBracket = null; |
| 6368 Token rightCurlyBracket = null; |
| 6369 ParameterKind kind = ParameterKind.REQUIRED; |
| 6370 bool firstParameter = true; |
| 6371 bool reportedMultiplePositionalGroups = false; |
| 6372 bool reportedMultipleNamedGroups = false; |
| 6373 bool reportedMixedGroups = false; |
| 6374 bool wasOptionalParameter = false; |
| 6375 Token initialToken = null; |
| 6376 do { |
| 6377 if (firstParameter) { |
| 6378 firstParameter = false; |
| 6379 } else if (!_optional(TokenType.COMMA)) { |
| 6380 // TODO(brianwilkerson) The token is wrong, we need to recover from this |
| 6381 // case. |
| 6382 if (_getEndToken(leftParenthesis) != null) { |
| 6383 _reportErrorForCurrentToken( |
| 6384 ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]); |
| 6385 } else { |
| 6386 _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS, |
| 6387 _currentToken.previous); |
| 6388 break; |
| 6389 } |
| 6390 } |
| 6391 initialToken = _currentToken; |
| 6392 // |
| 6393 // Handle the beginning of parameter groups. |
| 6394 // |
| 6395 TokenType type = _currentToken.type; |
| 6396 if (type == TokenType.OPEN_SQUARE_BRACKET) { |
| 6397 wasOptionalParameter = true; |
| 6398 if (leftSquareBracket != null && !reportedMultiplePositionalGroups) { |
| 6399 _reportErrorForCurrentToken( |
| 6400 ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS); |
| 6401 reportedMultiplePositionalGroups = true; |
| 6402 } |
| 6403 if (leftCurlyBracket != null && !reportedMixedGroups) { |
| 6404 _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS); |
| 6405 reportedMixedGroups = true; |
| 6406 } |
| 6407 leftSquareBracket = getAndAdvance(); |
| 6408 kind = ParameterKind.POSITIONAL; |
| 6409 } else if (type == TokenType.OPEN_CURLY_BRACKET) { |
| 6410 wasOptionalParameter = true; |
| 6411 if (leftCurlyBracket != null && !reportedMultipleNamedGroups) { |
| 6412 _reportErrorForCurrentToken( |
| 6413 ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS); |
| 6414 reportedMultipleNamedGroups = true; |
| 6415 } |
| 6416 if (leftSquareBracket != null && !reportedMixedGroups) { |
| 6417 _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS); |
| 6418 reportedMixedGroups = true; |
| 6419 } |
| 6420 leftCurlyBracket = getAndAdvance(); |
| 6421 kind = ParameterKind.NAMED; |
| 6422 } |
| 6423 // |
| 6424 // Parse and record the parameter. |
| 6425 // |
| 6426 FormalParameter parameter = parseFormalParameter(kind); |
| 6427 parameters.add(parameter); |
| 6428 if (kind == ParameterKind.REQUIRED && wasOptionalParameter) { |
| 6429 _reportErrorForNode( |
| 6430 ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter); |
| 6431 } |
| 6432 // |
| 6433 // Handle the end of parameter groups. |
| 6434 // |
| 6435 // TODO(brianwilkerson) Improve the detection and reporting of missing and |
| 6436 // mismatched delimiters. |
| 6437 type = _currentToken.type; |
| 6438 |
| 6439 // Advance past trailing commas as appropriate. |
| 6440 if (type == TokenType.COMMA) { |
| 6441 // Only parse commas trailing normal (non-positional/named) params. |
| 6442 if (rightSquareBracket == null && rightCurlyBracket == null) { |
| 6443 Token next = _peek(); |
| 6444 if (next.type == TokenType.CLOSE_PAREN || |
| 6445 next.type == TokenType.CLOSE_CURLY_BRACKET || |
| 6446 next.type == TokenType.CLOSE_SQUARE_BRACKET) { |
| 6447 _advance(); |
| 6448 type = _currentToken.type; |
| 6449 } |
| 6450 } |
| 6451 } |
| 6452 |
| 6453 if (type == TokenType.CLOSE_SQUARE_BRACKET) { |
| 6454 rightSquareBracket = getAndAdvance(); |
| 6455 if (leftSquareBracket == null) { |
| 6456 if (leftCurlyBracket != null) { |
| 6457 _reportErrorForCurrentToken( |
| 6458 ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]); |
| 6459 rightCurlyBracket = rightSquareBracket; |
| 6460 rightSquareBracket = null; |
| 6461 } else { |
| 6462 _reportErrorForCurrentToken( |
| 6463 ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, |
| 6464 ["["]); |
| 6465 } |
| 6466 } |
| 6467 kind = ParameterKind.REQUIRED; |
| 6468 } else if (type == TokenType.CLOSE_CURLY_BRACKET) { |
| 6469 rightCurlyBracket = getAndAdvance(); |
| 6470 if (leftCurlyBracket == null) { |
| 6471 if (leftSquareBracket != null) { |
| 6472 _reportErrorForCurrentToken( |
| 6473 ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]); |
| 6474 rightSquareBracket = rightCurlyBracket; |
| 6475 rightCurlyBracket = null; |
| 6476 } else { |
| 6477 _reportErrorForCurrentToken( |
| 6478 ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, |
| 6479 ["{"]); |
| 6480 } |
| 6481 } |
| 6482 kind = ParameterKind.REQUIRED; |
| 6483 } |
| 6484 } while (!_matches(TokenType.CLOSE_PAREN) && |
| 6485 !identical(initialToken, _currentToken)); |
| 6486 Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); |
| 6487 // |
| 6488 // Check that the groups were closed correctly. |
| 6489 // |
| 6490 if (leftSquareBracket != null && rightSquareBracket == null) { |
| 6491 _reportErrorForCurrentToken( |
| 6492 ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]); |
| 6493 } |
| 6494 if (leftCurlyBracket != null && rightCurlyBracket == null) { |
| 6495 _reportErrorForCurrentToken( |
| 6496 ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]); |
| 6497 } |
| 6498 // |
| 6499 // Build the parameter list. |
| 6500 // |
| 6501 leftSquareBracket ??= leftCurlyBracket; |
| 6502 rightSquareBracket ??= rightCurlyBracket; |
| 6503 return new FormalParameterList(leftParenthesis, parameters, |
| 6504 leftSquareBracket, rightSquareBracket, rightParenthesis); |
| 6505 } |
| 6506 |
| 6507 /** |
| 6508 * Parse a list of formal parameters. Return the formal parameters that were |
| 6509 * parsed. |
| 6510 * |
| 6511 * This method assumes that the current token matches `TokenType.OPEN_PAREN`. |
| 6512 */ |
| 6513 FormalParameterList _parseFormalParameterListUnchecked() { |
| 6514 return _parseFormalParameterListAfterParen(getAndAdvance()); |
| 6515 } |
| 6516 |
| 6517 /** |
| 6518 * Parse a function declaration statement. The [commentAndMetadata] is the |
| 6519 * documentation comment and metadata to be associated with the declaration. |
| 6520 * The [returnType] is the return type, or `null` if there is no return type. |
| 6521 * Return the function declaration statement that was parsed. |
| 6522 * |
| 6523 * functionDeclarationStatement ::= |
| 6524 * functionSignature functionBody |
| 6525 */ |
| 6526 Statement _parseFunctionDeclarationStatementAfterReturnType( |
| 6527 CommentAndMetadata commentAndMetadata, TypeName returnType) { |
| 6528 FunctionDeclaration declaration = |
| 6529 parseFunctionDeclaration(commentAndMetadata, null, returnType); |
| 6530 Token propertyKeyword = declaration.propertyKeyword; |
| 6531 if (propertyKeyword != null) { |
| 6532 if (propertyKeyword.keyword == Keyword.GET) { |
| 6533 _reportErrorForToken( |
| 6534 ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword); |
| 6535 } else { |
| 6536 _reportErrorForToken( |
| 6537 ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword); |
| 6538 } |
| 6539 } |
| 6540 return new FunctionDeclarationStatement(declaration); |
| 6541 } |
| 6542 |
| 6543 /** |
| 6544 * Parse a function type alias. The [commentAndMetadata] is the metadata to be |
| 6545 * associated with the member. The [keyword] is the token representing the |
| 6546 * 'typedef' keyword. Return the function type alias that was parsed. |
| 6547 * |
| 6548 * functionTypeAlias ::= |
| 6549 * functionPrefix typeParameterList? formalParameterList ';' |
| 6550 * |
| 6551 * functionPrefix ::= |
| 6552 * returnType? name |
| 6553 */ |
| 6554 FunctionTypeAlias _parseFunctionTypeAlias( |
| 6555 CommentAndMetadata commentAndMetadata, Token keyword) { |
| 6556 TypeName returnType = null; |
| 6557 if (hasReturnTypeInTypeAlias) { |
| 6558 returnType = parseReturnType(); |
| 6559 } |
| 6560 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); |
| 6561 TypeParameterList typeParameters = null; |
| 6562 if (_matches(TokenType.LT)) { |
| 6563 typeParameters = parseTypeParameterList(); |
| 6564 } |
| 6565 TokenType type = _currentToken.type; |
| 6566 if (type == TokenType.SEMICOLON || type == TokenType.EOF) { |
| 6567 _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS); |
| 6568 FormalParameterList parameters = new FormalParameterList( |
| 6569 _createSyntheticToken(TokenType.OPEN_PAREN), |
| 6570 null, |
| 6571 null, |
| 6572 null, |
| 6573 _createSyntheticToken(TokenType.CLOSE_PAREN)); |
| 6574 Token semicolon = _expect(TokenType.SEMICOLON); |
| 6575 return new FunctionTypeAlias( |
| 6576 commentAndMetadata.comment, |
| 6577 commentAndMetadata.metadata, |
| 6578 keyword, |
| 6579 returnType, |
| 6580 name, |
| 6581 typeParameters, |
| 6582 parameters, |
| 6583 semicolon); |
| 6584 } else if (type == TokenType.OPEN_PAREN) { |
| 6585 FormalParameterList parameters = _parseFormalParameterListUnchecked(); |
| 6586 _validateFormalParameterList(parameters); |
| 6587 Token semicolon = _expect(TokenType.SEMICOLON); |
| 6588 return new FunctionTypeAlias( |
| 6589 commentAndMetadata.comment, |
| 6590 commentAndMetadata.metadata, |
| 6591 keyword, |
| 6592 returnType, |
| 6593 name, |
| 6594 typeParameters, |
| 6595 parameters, |
| 6596 semicolon); |
| 6597 } else { |
| 6598 _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS); |
| 6599 // Recovery: At the very least we should skip to the start of the next |
| 6600 // valid compilation unit member, allowing for the possibility of finding |
| 6601 // the typedef parameters before that point. |
| 6602 return new FunctionTypeAlias( |
| 6603 commentAndMetadata.comment, |
| 6604 commentAndMetadata.metadata, |
| 6605 keyword, |
| 6606 returnType, |
| 6607 name, |
| 6608 typeParameters, |
| 6609 new FormalParameterList(_createSyntheticToken(TokenType.OPEN_PAREN), |
| 6610 null, null, null, _createSyntheticToken(TokenType.CLOSE_PAREN)), |
| 6611 _createSyntheticToken(TokenType.SEMICOLON)); |
| 6612 } |
| 6613 } |
| 6614 |
| 6615 /** |
| 6616 * Parses generic type parameters from a comment. |
| 6617 * |
| 6618 * Normally this is handled by [_parseGenericMethodTypeParameters], but if the |
| 6619 * code already handles the normal generic type parameters, the comment |
| 6620 * matcher can be called directly. For example, we may have already tried |
| 6621 * matching `<` (less than sign) in a method declaration, and be currently |
| 6622 * on the `(` (open paren) because we didn't find it. In that case, this |
| 6623 * function will parse the preceding comment such as `/*<T, R>*/`. |
| 6624 */ |
| 6625 TypeParameterList _parseGenericCommentTypeParameters() { |
| 6626 if (_injectGenericCommentTypeList()) { |
| 6627 return parseTypeParameterList(); |
| 6628 } |
| 6629 return null; |
| 6630 } |
| 6631 |
| 6632 /** |
| 6633 * Parse the generic method or function's type parameters. |
| 6634 * |
| 6635 * For backwards compatibility this can optionally use comments. |
| 6636 * See [parseGenericMethodComments]. |
| 6637 */ |
| 6638 TypeParameterList _parseGenericMethodTypeParameters() { |
| 6639 if (parseGenericMethods && _matches(TokenType.LT) || |
| 6640 _injectGenericCommentTypeList()) { |
| 6641 return parseTypeParameterList(); |
| 6642 } |
| 6643 return null; |
| 6644 } |
| 6645 |
| 6646 /** |
| 6647 * Parse a library name. The [missingNameError] is the error code to be used |
| 6648 * if the library name is missing. The [missingNameToken] is the token |
| 6649 * associated with the error produced if the library name is missing. Return |
| 6650 * the library name that was parsed. |
| 6651 * |
| 6652 * libraryName ::= |
| 6653 * libraryIdentifier |
| 6654 */ |
| 6655 LibraryIdentifier _parseLibraryName( |
| 6656 ParserErrorCode missingNameError, Token missingNameToken) { |
| 6657 if (_matchesIdentifier()) { |
| 6658 return parseLibraryIdentifier(); |
| 6659 } else if (_matches(TokenType.STRING)) { |
| 6660 // Recovery: This should be extended to handle arbitrary tokens until we |
| 6661 // can find a token that can start a compilation unit member. |
| 6662 StringLiteral string = parseStringLiteral(); |
| 6663 _reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string); |
| 6664 } else { |
| 6665 _reportErrorForToken(missingNameError, missingNameToken); |
| 6666 } |
| 6667 return new LibraryIdentifier( |
| 6668 <SimpleIdentifier>[createSyntheticIdentifier()]); |
| 6669 } |
| 6670 |
| 6671 /** |
| 6672 * Parse a method declaration. The [commentAndMetadata] is the documentation |
| 6673 * comment and metadata to be associated with the declaration. The |
| 6674 * [externalKeyword] is the 'external' token. The [staticKeyword] is the |
| 6675 * static keyword, or `null` if the getter is not static. The [returnType] is |
| 6676 * the return type of the method. The [name] is the name of the method. The |
| 6677 * [parameters] is the parameters to the method. Return the method declaration |
| 6678 * that was parsed. |
| 6679 * |
| 6680 * functionDeclaration ::= |
| 6681 * ('external' 'static'?)? functionSignature functionBody |
| 6682 * | 'external'? functionSignature ';' |
| 6683 */ |
| 6684 MethodDeclaration _parseMethodDeclarationAfterParameters( |
| 6685 CommentAndMetadata commentAndMetadata, |
| 6686 Token externalKeyword, |
| 6687 Token staticKeyword, |
| 6688 TypeName returnType, |
| 6689 SimpleIdentifier name, |
| 6690 TypeParameterList typeParameters, |
| 6691 FormalParameterList parameters) { |
| 6692 FunctionBody body = parseFunctionBody( |
| 6693 externalKeyword != null || staticKeyword == null, |
| 6694 ParserErrorCode.MISSING_FUNCTION_BODY, |
| 6695 false); |
| 6696 if (externalKeyword != null) { |
| 6697 if (body is! EmptyFunctionBody) { |
| 6698 _reportErrorForNode(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body); |
| 6699 } |
| 6700 } else if (staticKeyword != null) { |
| 6701 if (body is EmptyFunctionBody && _parseFunctionBodies) { |
| 6702 _reportErrorForNode(ParserErrorCode.ABSTRACT_STATIC_METHOD, body); |
| 6703 } |
| 6704 } |
| 6705 return new MethodDeclaration( |
| 6706 commentAndMetadata.comment, |
| 6707 commentAndMetadata.metadata, |
| 6708 externalKeyword, |
| 6709 staticKeyword, |
| 6710 returnType, |
| 6711 null, |
| 6712 null, |
| 6713 name, |
| 6714 typeParameters, |
| 6715 parameters, |
| 6716 body); |
| 6717 } |
| 6718 |
| 6719 /** |
| 6720 * Parse a method declaration. The [commentAndMetadata] is the documentation |
| 6721 * comment and metadata to be associated with the declaration. The |
| 6722 * [externalKeyword] is the 'external' token. The [staticKeyword] is the |
| 6723 * static keyword, or `null` if the getter is not static. The [returnType] is |
| 6724 * the return type of the method. Return the method declaration that was |
| 6725 * parsed. |
| 6726 * |
| 6727 * functionDeclaration ::= |
| 6728 * 'external'? 'static'? functionSignature functionBody |
| 6729 * | 'external'? functionSignature ';' |
| 6730 */ |
| 6731 MethodDeclaration _parseMethodDeclarationAfterReturnType( |
| 6732 CommentAndMetadata commentAndMetadata, |
| 6733 Token externalKeyword, |
| 6734 Token staticKeyword, |
| 6735 TypeName returnType) { |
| 6736 SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true); |
| 6737 TypeParameterList typeParameters = _parseGenericMethodTypeParameters(); |
| 6738 FormalParameterList parameters; |
| 6739 TokenType type = _currentToken.type; |
| 6740 // TODO(brianwilkerson) Figure out why we care what the current token is if |
| 6741 // it isn't a paren. |
| 6742 if (type != TokenType.OPEN_PAREN && |
| 6743 (type == TokenType.OPEN_CURLY_BRACKET || type == TokenType.FUNCTION)) { |
| 6744 _reportErrorForToken( |
| 6745 ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous); |
| 6746 parameters = new FormalParameterList( |
| 6747 _createSyntheticToken(TokenType.OPEN_PAREN), |
| 6748 null, |
| 6749 null, |
| 6750 null, |
| 6751 _createSyntheticToken(TokenType.CLOSE_PAREN)); |
| 6752 } else { |
| 6753 parameters = parseFormalParameterList(); |
| 6754 } |
| 6755 _validateFormalParameterList(parameters); |
| 6756 return _parseMethodDeclarationAfterParameters( |
| 6757 commentAndMetadata, |
| 6758 externalKeyword, |
| 6759 staticKeyword, |
| 6760 returnType, |
| 6761 methodName, |
| 6762 typeParameters, |
| 6763 parameters); |
| 6764 } |
| 6765 |
| 6766 /** |
| 6767 * Parse a class native clause. Return the native clause that was parsed. |
| 6768 * |
| 6769 * This method assumes that the current token matches `_NATIVE`. |
| 6770 * |
| 6771 * classNativeClause ::= |
| 6772 * 'native' name |
| 6773 */ |
| 6774 NativeClause _parseNativeClause() { |
| 6775 Token keyword = getAndAdvance(); |
| 6776 StringLiteral name = parseStringLiteral(); |
| 6777 return new NativeClause(keyword, name); |
| 6778 } |
| 6779 |
| 6780 /** |
7039 * Parse an operator declaration starting after the 'operator' keyword. The | 6781 * Parse an operator declaration starting after the 'operator' keyword. The |
7040 * [commentAndMetadata] is the documentation comment and metadata to be | 6782 * [commentAndMetadata] is the documentation comment and metadata to be |
7041 * associated with the declaration. The [externalKeyword] is the 'external' | 6783 * associated with the declaration. The [externalKeyword] is the 'external' |
7042 * token. The [returnType] is the return type that has already been parsed, or | 6784 * token. The [returnType] is the return type that has already been parsed, or |
7043 * `null` if there was no return type. The [operatorKeyword] is the 'operator' | 6785 * `null` if there was no return type. The [operatorKeyword] is the 'operator' |
7044 * keyword. Return the operator declaration that was parsed. | 6786 * keyword. Return the operator declaration that was parsed. |
7045 * | 6787 * |
7046 * operatorDeclaration ::= | 6788 * operatorDeclaration ::= |
7047 * operatorSignature (';' | functionBody) | 6789 * operatorSignature (';' | functionBody) |
7048 * | 6790 * |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7180 return new PartOfDirective( | 6922 return new PartOfDirective( |
7181 commentAndMetadata.comment, | 6923 commentAndMetadata.comment, |
7182 commentAndMetadata.metadata, | 6924 commentAndMetadata.metadata, |
7183 partKeyword, | 6925 partKeyword, |
7184 ofKeyword, | 6926 ofKeyword, |
7185 libraryName, | 6927 libraryName, |
7186 semicolon); | 6928 semicolon); |
7187 } | 6929 } |
7188 | 6930 |
7189 /** | 6931 /** |
7190 * Parse a part or part-of directive. The [commentAndMetadata] is the metadata | |
7191 * to be associated with the directive. Return the part or part-of directive | |
7192 * that was parsed. | |
7193 * | |
7194 * This method assumes that the current token matches `Keyword.PART`. | |
7195 * | |
7196 * partDirective ::= | |
7197 * metadata 'part' stringLiteral ';' | |
7198 * | |
7199 * partOfDirective ::= | |
7200 * metadata 'part' 'of' identifier ';' | |
7201 */ | |
7202 Directive _parsePartOrPartOfDirective(CommentAndMetadata commentAndMetadata) { | |
7203 if (_tokenMatchesString(_peek(), _OF)) { | |
7204 return _parsePartOfDirective(commentAndMetadata); | |
7205 } | |
7206 return _parsePartDirective(commentAndMetadata); | |
7207 } | |
7208 | |
7209 /** | |
7210 * Parse a postfix expression. Return the postfix expression that was parsed. | |
7211 * | |
7212 * postfixExpression ::= | |
7213 * assignableExpression postfixOperator | |
7214 * | primary selector* | |
7215 * | |
7216 * selector ::= | |
7217 * assignableSelector | |
7218 * | argumentList | |
7219 */ | |
7220 Expression _parsePostfixExpression() { | |
7221 Expression operand = parseAssignableExpression(true); | |
7222 TokenType type = _currentToken.type; | |
7223 if (type == TokenType.OPEN_SQUARE_BRACKET || | |
7224 type == TokenType.PERIOD || | |
7225 type == TokenType.QUESTION_PERIOD || | |
7226 type == TokenType.OPEN_PAREN || | |
7227 (parseGenericMethods && type == TokenType.LT)) { | |
7228 do { | |
7229 if (_isLikelyArgumentList()) { | |
7230 TypeArgumentList typeArguments = _parseOptionalTypeArguments(); | |
7231 ArgumentList argumentList = parseArgumentList(); | |
7232 Expression currentOperand = operand; | |
7233 if (currentOperand is PropertyAccess) { | |
7234 operand = new MethodInvocation( | |
7235 currentOperand.target, | |
7236 currentOperand.operator, | |
7237 currentOperand.propertyName, | |
7238 typeArguments, | |
7239 argumentList); | |
7240 } else { | |
7241 operand = new FunctionExpressionInvocation( | |
7242 operand, typeArguments, argumentList); | |
7243 } | |
7244 } else { | |
7245 operand = _parseAssignableSelector(operand, true); | |
7246 } | |
7247 type = _currentToken.type; | |
7248 } while (type == TokenType.OPEN_SQUARE_BRACKET || | |
7249 type == TokenType.PERIOD || | |
7250 type == TokenType.QUESTION_PERIOD || | |
7251 type == TokenType.OPEN_PAREN); | |
7252 return operand; | |
7253 } | |
7254 if (!_currentToken.type.isIncrementOperator) { | |
7255 return operand; | |
7256 } | |
7257 _ensureAssignable(operand); | |
7258 Token operator = getAndAdvance(); | |
7259 return new PostfixExpression(operand, operator); | |
7260 } | |
7261 | |
7262 /** | |
7263 * Parse a prefixed identifier given that the given [qualifier] was already | 6932 * Parse a prefixed identifier given that the given [qualifier] was already |
7264 * parsed. Return the prefixed identifier that was parsed. | 6933 * parsed. Return the prefixed identifier that was parsed. |
7265 * | 6934 * |
7266 * prefixedIdentifier ::= | 6935 * prefixedIdentifier ::= |
7267 * identifier ('.' identifier)? | 6936 * identifier ('.' identifier)? |
7268 */ | 6937 */ |
7269 Identifier _parsePrefixedIdentifierAfterIdentifier( | 6938 Identifier _parsePrefixedIdentifierAfterIdentifier( |
7270 SimpleIdentifier qualifier) { | 6939 SimpleIdentifier qualifier) { |
7271 if (!_matches(TokenType.PERIOD) || _injectGenericCommentTypeList()) { | 6940 if (!_matches(TokenType.PERIOD) || _injectGenericCommentTypeList()) { |
7272 return qualifier; | 6941 return qualifier; |
(...skipping 11 matching lines...) Expand all Loading... |
7284 * | 6953 * |
7285 * prefixedIdentifier ::= | 6954 * prefixedIdentifier ::= |
7286 * identifier ('.' identifier)? | 6955 * identifier ('.' identifier)? |
7287 */ | 6956 */ |
7288 Identifier _parsePrefixedIdentifierUnchecked() { | 6957 Identifier _parsePrefixedIdentifierUnchecked() { |
7289 return _parsePrefixedIdentifierAfterIdentifier( | 6958 return _parsePrefixedIdentifierAfterIdentifier( |
7290 _parseSimpleIdentifierUnchecked()); | 6959 _parseSimpleIdentifierUnchecked()); |
7291 } | 6960 } |
7292 | 6961 |
7293 /** | 6962 /** |
7294 * Parse a redirecting constructor invocation. The flag [hasPeriod] should be | |
7295 * `true` if the `this` is followed by a period. Return the redirecting | |
7296 * constructor invocation that was parsed. | |
7297 * | |
7298 * This method assumes that the current token matches `Keyword.THIS`. | |
7299 * | |
7300 * redirectingConstructorInvocation ::= | |
7301 * 'this' ('.' identifier)? arguments | |
7302 */ | |
7303 RedirectingConstructorInvocation _parseRedirectingConstructorInvocation( | |
7304 bool hasPeriod) { | |
7305 Token keyword = getAndAdvance(); | |
7306 Token period = null; | |
7307 SimpleIdentifier constructorName = null; | |
7308 if (hasPeriod) { | |
7309 period = getAndAdvance(); | |
7310 if (_matchesIdentifier()) { | |
7311 constructorName = _parseSimpleIdentifierUnchecked(isDeclaration: false); | |
7312 } else { | |
7313 _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); | |
7314 constructorName = createSyntheticIdentifier(isDeclaration: false); | |
7315 _advance(); | |
7316 } | |
7317 } | |
7318 ArgumentList argumentList = _parseArgumentListChecked(); | |
7319 return new RedirectingConstructorInvocation( | |
7320 keyword, period, constructorName, argumentList); | |
7321 } | |
7322 | |
7323 /** | |
7324 * Parse a setter. The [commentAndMetadata] is the documentation comment and | |
7325 * metadata to be associated with the declaration. The [externalKeyword] is | |
7326 * the 'external' token. The [staticKeyword] is the static keyword, or `null` | |
7327 * if the setter is not static. The [returnType] is the return type that has | |
7328 * already been parsed, or `null` if there was no return type. Return the | |
7329 * setter that was parsed. | |
7330 * | |
7331 * This method assumes that the current token matches `Keyword.SET`. | |
7332 * | |
7333 * setter ::= | |
7334 * setterSignature functionBody? | |
7335 * | |
7336 * setterSignature ::= | |
7337 * 'external'? 'static'? returnType? 'set' identifier formalParameterL
ist | |
7338 */ | |
7339 MethodDeclaration _parseSetter(CommentAndMetadata commentAndMetadata, | |
7340 Token externalKeyword, Token staticKeyword, TypeName returnType) { | |
7341 Token propertyKeyword = getAndAdvance(); | |
7342 SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true); | |
7343 FormalParameterList parameters = parseFormalParameterList(); | |
7344 _validateFormalParameterList(parameters); | |
7345 FunctionBody body = parseFunctionBody( | |
7346 externalKeyword != null || staticKeyword == null, | |
7347 ParserErrorCode.STATIC_SETTER_WITHOUT_BODY, | |
7348 false); | |
7349 if (externalKeyword != null && body is! EmptyFunctionBody) { | |
7350 _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY); | |
7351 } | |
7352 return new MethodDeclaration( | |
7353 commentAndMetadata.comment, | |
7354 commentAndMetadata.metadata, | |
7355 externalKeyword, | |
7356 staticKeyword, | |
7357 returnType, | |
7358 propertyKeyword, | |
7359 null, | |
7360 name, | |
7361 null, | |
7362 parameters, | |
7363 body); | |
7364 } | |
7365 | |
7366 /** | |
7367 * Parse a simple identifier. Return the simple identifier that was parsed. | 6963 * Parse a simple identifier. Return the simple identifier that was parsed. |
7368 * | 6964 * |
7369 * This method assumes that the current token matches an identifier. | 6965 * This method assumes that the current token matches an identifier. |
7370 * | 6966 * |
7371 * identifier ::= | 6967 * identifier ::= |
7372 * IDENTIFIER | 6968 * IDENTIFIER |
7373 */ | 6969 */ |
7374 SimpleIdentifier _parseSimpleIdentifierUnchecked( | 6970 SimpleIdentifier _parseSimpleIdentifierUnchecked( |
7375 {bool isDeclaration: false}) { | 6971 {bool isDeclaration: false}) { |
7376 String lexeme = _currentToken.lexeme; | 6972 String lexeme = _currentToken.lexeme; |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7477 _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) { | 7073 _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) { |
7478 strings.add(_parseStringInterpolation(string)); | 7074 strings.add(_parseStringInterpolation(string)); |
7479 } else { | 7075 } else { |
7480 strings.add(new SimpleStringLiteral( | 7076 strings.add(new SimpleStringLiteral( |
7481 string, computeStringValue(string.lexeme, true, true))); | 7077 string, computeStringValue(string.lexeme, true, true))); |
7482 } | 7078 } |
7483 } while (_matches(TokenType.STRING)); | 7079 } while (_matches(TokenType.STRING)); |
7484 return strings.length == 1 ? strings[0] : new AdjacentStrings(strings); | 7080 return strings.length == 1 ? strings[0] : new AdjacentStrings(strings); |
7485 } | 7081 } |
7486 | 7082 |
7487 /** | |
7488 * Parse a type alias. The [commentAndMetadata] is the metadata to be | |
7489 * associated with the member. Return the type alias that was parsed. | |
7490 * | |
7491 * This method assumes that the current token matches [Keyword.TYPEDEF]. | |
7492 * | |
7493 * typeAlias ::= | |
7494 * 'typedef' typeAliasBody | |
7495 * | |
7496 * typeAliasBody ::= | |
7497 * classTypeAlias | |
7498 * | functionTypeAlias | |
7499 * | |
7500 * classTypeAlias ::= | |
7501 * identifier typeParameters? '=' 'abstract'? mixinApplication | |
7502 * | |
7503 * mixinApplication ::= | |
7504 * qualified withClause implementsClause? ';' | |
7505 * | |
7506 * functionTypeAlias ::= | |
7507 * functionPrefix typeParameterList? formalParameterList ';' | |
7508 * | |
7509 * functionPrefix ::= | |
7510 * returnType? name | |
7511 */ | |
7512 TypeAlias _parseTypeAlias(CommentAndMetadata commentAndMetadata) { | |
7513 Token keyword = getAndAdvance(); | |
7514 if (_matchesIdentifier()) { | |
7515 Token next = _peek(); | |
7516 if (_tokenMatches(next, TokenType.LT)) { | |
7517 next = _skipTypeParameterList(next); | |
7518 if (next != null && _tokenMatches(next, TokenType.EQ)) { | |
7519 TypeAlias typeAlias = | |
7520 parseClassTypeAlias(commentAndMetadata, null, keyword); | |
7521 _reportErrorForToken( | |
7522 ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword); | |
7523 return typeAlias; | |
7524 } | |
7525 } else if (_tokenMatches(next, TokenType.EQ)) { | |
7526 TypeAlias typeAlias = | |
7527 parseClassTypeAlias(commentAndMetadata, null, keyword); | |
7528 _reportErrorForToken( | |
7529 ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword); | |
7530 return typeAlias; | |
7531 } | |
7532 } | |
7533 return _parseFunctionTypeAlias(commentAndMetadata, keyword); | |
7534 } | |
7535 | |
7536 TypeName _parseTypeName(bool inExpression) { | 7083 TypeName _parseTypeName(bool inExpression) { |
7537 Identifier typeName; | 7084 Identifier typeName; |
7538 if (_matchesIdentifier()) { | 7085 if (_matchesIdentifier()) { |
7539 typeName = _parsePrefixedIdentifierUnchecked(); | 7086 typeName = _parsePrefixedIdentifierUnchecked(); |
7540 } else if (_matchesKeyword(Keyword.VAR)) { | 7087 } else if (_matchesKeyword(Keyword.VAR)) { |
7541 _reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME); | 7088 _reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME); |
7542 typeName = new SimpleIdentifier(getAndAdvance()); | 7089 typeName = new SimpleIdentifier(getAndAdvance()); |
7543 } else { | 7090 } else { |
7544 typeName = createSyntheticIdentifier(); | 7091 typeName = createSyntheticIdentifier(); |
7545 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME); | 7092 _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME); |
(...skipping 1638 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
9184 */ | 8731 */ |
9185 const ParserErrorCode(String name, String message, [String correction]) | 8732 const ParserErrorCode(String name, String message, [String correction]) |
9186 : super(name, message, correction); | 8733 : super(name, message, correction); |
9187 | 8734 |
9188 @override | 8735 @override |
9189 ErrorSeverity get errorSeverity => ErrorSeverity.ERROR; | 8736 ErrorSeverity get errorSeverity => ErrorSeverity.ERROR; |
9190 | 8737 |
9191 @override | 8738 @override |
9192 ErrorType get type => ErrorType.SYNTACTIC_ERROR; | 8739 ErrorType get type => ErrorType.SYNTACTIC_ERROR; |
9193 } | 8740 } |
OLD | NEW |