Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 fasta.parser.parser; | 5 library fasta.parser.parser; |
| 6 | 6 |
| 7 import '../fasta_codes.dart' | 7 import '../fasta_codes.dart' |
| 8 show | 8 show |
| 9 FastaCode, | 9 FastaCode, |
| 10 FastaMessage, | 10 FastaMessage, |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 194 /// - `'void'` | 194 /// - `'void'` |
| 195 /// - `'Function' ( '(' | '<' )` | 195 /// - `'Function' ( '(' | '<' )` |
| 196 /// - `identifier ('.' identifier)? ('<' ... '>')? identifer` | 196 /// - `identifier ('.' identifier)? ('<' ... '>')? identifer` |
| 197 /// | 197 /// |
| 198 /// Otherwise, do nothing. | 198 /// Otherwise, do nothing. |
| 199 Optional, | 199 Optional, |
| 200 | 200 |
| 201 /// Indicates that the keyword `typedef` has just been seen, and the parser | 201 /// Indicates that the keyword `typedef` has just been seen, and the parser |
| 202 /// should parse the following as a type unless it is followed by `=`. | 202 /// should parse the following as a type unless it is followed by `=`. |
| 203 Typedef, | 203 Typedef, |
| 204 | |
| 205 /// Indicates that what follows is either a local declaration or an | |
| 206 /// expression. | |
| 207 ExpressionStatementOrDeclaration, | |
| 208 | |
| 209 /// Indicates that the keyword `const` has just been seen, and what follows | |
| 210 /// may be a local variable declaration or an expression. | |
| 211 ExpressionStatementOrConstDeclaration, | |
| 212 | |
| 213 /// Indicates that the parser is parsing an expression and has just seen an | |
| 214 /// identifier. | |
| 215 SendOrFunctionLiteral, | |
|
danrubel
2017/06/28 03:22:00
What is 'Send' ? An invocation or call site?
ahe
2017/06/28 08:06:58
"Send" comes from message send. All method invocat
| |
| 216 | |
| 217 /// Indicates that the parser has just parsed `for '('` and is looking to | |
| 218 /// parse a variable declaration or expression. | |
| 219 VariablesDeclarationOrExpression, | |
| 204 } | 220 } |
| 205 | 221 |
| 206 /// An event generating parser of Dart programs. This parser expects all tokens | 222 /// An event generating parser of Dart programs. This parser expects all tokens |
| 207 /// in a linked list (aka a token stream). | 223 /// in a linked list (aka a token stream). |
| 208 /// | 224 /// |
| 209 /// The class [Scanner] is used to generate a token stream. See the file | 225 /// The class [Scanner] is used to generate a token stream. See the file |
| 210 /// [scanner.dart](../scanner.dart). | 226 /// [scanner.dart](../scanner.dart). |
| 211 /// | 227 /// |
| 212 /// Subclasses of the class [Listener] are used to listen to events. | 228 /// Subclasses of the class [Listener] are used to listen to events. |
| 213 /// | 229 /// |
| (...skipping 936 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1150 bool isGeneralizedFunctionType(Token token) { | 1166 bool isGeneralizedFunctionType(Token token) { |
| 1151 return optional('Function', token) && | 1167 return optional('Function', token) && |
| 1152 (optional('<', token.next) || optional('(', token.next)); | 1168 (optional('<', token.next) || optional('(', token.next)); |
| 1153 } | 1169 } |
| 1154 | 1170 |
| 1155 /// Parse a type, if it is appropriate to do so. | 1171 /// Parse a type, if it is appropriate to do so. |
| 1156 /// | 1172 /// |
| 1157 /// If this method can parse a type, it will return the next (non-null) token | 1173 /// If this method can parse a type, it will return the next (non-null) token |
| 1158 /// after the type. Otherwise, it returns null. | 1174 /// after the type. Otherwise, it returns null. |
| 1159 Token parseType(Token token, | 1175 Token parseType(Token token, |
| 1160 [TypeContinuation continuation = TypeContinuation.Required]) { | 1176 [TypeContinuation continuation = TypeContinuation.Required, |
| 1161 Token begin = token; | 1177 IdentifierContext continuationContext]) { |
| 1178 /// Returns the close brace, bracket, or parenthesis of [left]. For '<', it | |
| 1179 /// may return null. | |
| 1180 Token getClose(BeginToken left) => left.endToken; | |
| 1181 | |
| 1182 /// Where the type begins. | |
| 1183 Token begin; | |
| 1184 | |
| 1185 /// Non-null if 'void' is the first token. | |
| 1186 Token voidToken; | |
| 1187 | |
| 1188 /// True if the tokens at [begin] looks like a type. | |
| 1189 bool looksLikeType = false; | |
| 1190 | |
| 1191 /// True if a type that could be a return type for a generalized function | |
| 1192 /// type was seen during analysis. | |
| 1193 bool hasReturnType = false; | |
| 1194 | |
| 1195 /// The identifier context to use for parsing the type. | |
| 1196 IdentifierContext context = IdentifierContext.typeReference; | |
| 1197 | |
| 1198 /// Non-null if type arguments were seen during analysis. | |
| 1199 BeginToken typeArguments; | |
| 1200 | |
| 1201 /// The number of function types seen during analysis. | |
| 1202 int functionTypes = 0; | |
| 1203 | |
| 1204 /// The start of type variables of function types seen during | |
| 1205 /// analysis. Notice that the tokens in this list might be either `'<'` or | |
| 1206 /// `'('` as not all function types have type parameters. Also, it is safe | |
| 1207 /// to assume that [getClose] will return non-null for all these tokens. | |
| 1208 Link<Token> typeVariableStarters = const Link<Token>(); | |
| 1209 | |
| 1210 { | |
| 1211 // Analyse the next tokens to see if they could be a type. | |
| 1212 | |
| 1213 if (continuation == | |
| 1214 TypeContinuation.ExpressionStatementOrConstDeclaration) { | |
| 1215 // This is a special case. The first token is `const` and we need to | |
| 1216 // analyze the tokens following the const keyword. | |
| 1217 assert(optional("const", token)); | |
| 1218 begin = token; | |
| 1219 token = token.next; | |
| 1220 token = listener.injectGenericCommentTypeAssign(token); | |
| 1221 assert(begin.next == token); | |
| 1222 } else { | |
| 1223 // Modify [begin] in case generic type are injected from a comment. | |
| 1224 begin = token = listener.injectGenericCommentTypeAssign(token); | |
| 1225 } | |
| 1226 | |
| 1227 if (optional("void", token)) { | |
| 1228 // `void` is a type. | |
| 1229 looksLikeType = true; | |
| 1230 voidToken = token; | |
| 1231 token = token.next; | |
| 1232 } else if (isValidTypeReference(token) && | |
| 1233 !isGeneralizedFunctionType(token)) { | |
| 1234 // We're looking at an identifier that could be a type (or `dynamic`). | |
| 1235 looksLikeType = true; | |
| 1236 token = token.next; | |
| 1237 if (optional(".", token) && isValidTypeReference(token.next)) { | |
| 1238 // We're looking at `prefix '.' identifier`. | |
| 1239 context = IdentifierContext.prefixedTypeReference; | |
| 1240 token = token.next.next; | |
| 1241 } | |
| 1242 if (optional("<", token)) { | |
| 1243 Token close = getClose(token); | |
| 1244 if (close != null && | |
| 1245 (optional(">", close) || optional(">>", close))) { | |
| 1246 // We found some type arguments. | |
| 1247 typeArguments = token; | |
| 1248 token = close.next; | |
| 1249 } | |
| 1250 } | |
| 1251 } | |
| 1252 | |
| 1253 // If what we have seen so far looks like a type, that could be a return | |
| 1254 // type for a generalized function type. | |
| 1255 hasReturnType = looksLikeType; | |
| 1256 | |
| 1257 while (optional("Function", token)) { | |
| 1258 Token typeVariableStart = token.next; | |
| 1259 if (optional("<", token.next)) { | |
| 1260 Token close = getClose(token.next); | |
| 1261 if (close != null && optional(">", close)) { | |
| 1262 token = close; | |
| 1263 } else { | |
| 1264 break; // Not a function type. | |
| 1265 } | |
| 1266 } | |
| 1267 if (optional("(", token.next)) { | |
| 1268 // This is a function type. | |
| 1269 Token close = getClose(token.next); | |
| 1270 assert(optional(")", close)); | |
| 1271 looksLikeType = true; | |
| 1272 functionTypes++; | |
| 1273 typeVariableStarters = | |
| 1274 typeVariableStarters.prepend(typeVariableStart); | |
| 1275 token = close.next; | |
| 1276 } else { | |
| 1277 break; // Not a funtion type. | |
| 1278 } | |
| 1279 } | |
| 1280 } | |
| 1162 | 1281 |
| 1163 /// Call this function when it's known that [begin] is a type. This | 1282 /// Call this function when it's known that [begin] is a type. This |
| 1164 /// function will call the appropriate event methods on [listener] to | 1283 /// function will call the appropriate event methods on [listener] to |
| 1165 /// handle the type. | 1284 /// handle the type. |
| 1166 Token commitType() { | 1285 Token commitType() { |
| 1167 if (isGeneralizedFunctionType(token)) { | 1286 assert(typeVariableStarters.length == functionTypes); |
| 1287 | |
| 1288 if (functionTypes > 0 && !hasReturnType) { | |
| 1168 // A function type without return type. | 1289 // A function type without return type. |
| 1169 // Push the non-existing return type first. The loop below will | 1290 // Push the non-existing return type first. The loop below will |
| 1170 // generate the full type. | 1291 // generate the full type. |
| 1171 listener.handleNoType(token); | 1292 listener.handleNoType(begin); |
| 1172 } else if (optional("void", token) && | 1293 token = begin; |
| 1173 isGeneralizedFunctionType(token.next)) { | 1294 } else if (functionTypes > 0 && voidToken != null) { |
| 1174 listener.handleVoidKeyword(token); | 1295 listener.handleVoidKeyword(voidToken); |
| 1175 token = token.next; | 1296 token = voidToken.next; |
| 1176 } else { | 1297 } else { |
| 1177 IdentifierContext context = IdentifierContext.typeReference; | 1298 token = parseIdentifier(begin, context); |
| 1178 if (token.isIdentifier && optional(".", token.next)) { | |
| 1179 context = IdentifierContext.prefixedTypeReference; | |
| 1180 } | |
| 1181 token = parseIdentifier(token, context); | |
| 1182 token = parseQualifiedRestOpt( | 1299 token = parseQualifiedRestOpt( |
| 1183 token, IdentifierContext.typeReferenceContinuation); | 1300 token, IdentifierContext.typeReferenceContinuation); |
| 1301 assert(typeArguments == null || typeArguments == token); | |
| 1184 token = parseTypeArgumentsOpt(token); | 1302 token = parseTypeArgumentsOpt(token); |
| 1185 listener.handleType(begin, token); | 1303 listener.handleType(begin, token); |
| 1186 } | 1304 } |
| 1187 | 1305 |
| 1188 { | 1306 { |
| 1189 Token newBegin = | 1307 Token newBegin = |
| 1190 listener.replaceTokenWithGenericCommentTypeAssign(begin, token); | 1308 listener.replaceTokenWithGenericCommentTypeAssign(begin, token); |
| 1191 if (!identical(newBegin, begin)) { | 1309 if (!identical(newBegin, begin)) { |
| 1192 listener.discardTypeReplacedWithCommentTypeAssign(); | 1310 listener.discardTypeReplacedWithCommentTypeAssign(); |
| 1193 return parseType(newBegin); | 1311 return parseType(newBegin); |
| 1194 } | 1312 } |
| 1195 } | 1313 } |
| 1196 | 1314 |
| 1197 // While we see a `Function(` treat the pushed type as return type. | 1315 // While we see a `Function(` treat the pushed type as return type. |
| 1198 // For example: `int Function() Function(int) Function(String x)`. | 1316 // For example: `int Function() Function(int) Function(String x)`. |
| 1199 while (isGeneralizedFunctionType(token)) { | 1317 for (int i = 0; i < functionTypes; i++) { |
| 1318 assert(isGeneralizedFunctionType(token)); | |
| 1200 token = parseFunctionType(token); | 1319 token = parseFunctionType(token); |
| 1201 } | 1320 } |
| 1321 | |
| 1202 return token; | 1322 return token; |
| 1203 } | 1323 } |
| 1204 | 1324 |
| 1325 /// Returns true if [kind] is '=', ';', or ',', that is, if [kind] could be | |
| 1326 /// the end of a variable declaration. | |
| 1327 bool looksLikeVariableDeclarationEnd(int kind) { | |
| 1328 return EQ_TOKEN == kind || SEMICOLON_TOKEN == kind || COMMA_TOKEN == kind; | |
| 1329 } | |
| 1330 | |
| 1331 /// Returns true if [token] could be the start of a function body. | |
| 1332 bool looksLikeFunctionBody(Token token) { | |
| 1333 return optional('{', token) || | |
| 1334 optional('=>', token) || | |
| 1335 optional('async', token) || | |
| 1336 optional('sync', token); | |
| 1337 } | |
| 1338 | |
| 1205 switch (continuation) { | 1339 switch (continuation) { |
| 1206 case TypeContinuation.Required: | 1340 case TypeContinuation.Required: |
| 1207 return commitType(); | 1341 return commitType(); |
| 1208 | 1342 |
| 1209 optional: | 1343 optional: |
| 1210 case TypeContinuation.Optional: | 1344 case TypeContinuation.Optional: |
| 1211 if (optional("void", token)) { | 1345 if (looksLikeType) { |
| 1212 if (isGeneralizedFunctionType(token.next)) { | 1346 if (functionTypes > 0) { |
| 1213 return commitType(); // This is a type, parse it. | 1347 return commitType(); // Parse function type. |
| 1214 } else { | |
| 1215 listener.handleVoidKeyword(token); | |
| 1216 return token.next; | |
| 1217 } | 1348 } |
| 1218 } else { | 1349 if (voidToken != null) { |
| 1219 if (isGeneralizedFunctionType(token)) { | 1350 listener.handleVoidKeyword(voidToken); |
| 1220 return commitType(); // Function type without return type, parse it. | 1351 return voidToken.next; |
| 1221 } | 1352 } |
| 1222 token = listener.injectGenericCommentTypeAssign(token); | 1353 if (token.isIdentifier || optional('this', token)) { |
| 1223 Token peek = peekAfterIfType(token); | 1354 return commitType(); // Parse type. |
| 1224 if (peek != null && (peek.isIdentifier || optional('this', peek))) { | |
| 1225 // This is a type followed by an identifier, parse it. | |
| 1226 return commitType(); | |
| 1227 } | 1355 } |
| 1228 listener.handleNoType(token); | |
| 1229 return token; | |
| 1230 } | 1356 } |
| 1231 break; | 1357 listener.handleNoType(begin); |
| 1358 return begin; | |
| 1232 | 1359 |
| 1233 case TypeContinuation.Typedef: | 1360 case TypeContinuation.Typedef: |
| 1234 if (optional('=', peekAfterNominalType(token))) { | 1361 if (optional('=', token)) { |
| 1235 return null; // This isn't a type, it's a new-style typedef. | 1362 return null; // This isn't a type, it's a new-style typedef. |
| 1236 } | 1363 } |
| 1237 continue optional; | 1364 continue optional; |
| 1365 | |
| 1366 case TypeContinuation.ExpressionStatementOrDeclaration: | |
| 1367 assert(begin.isIdentifier || identical(begin.stringValue, 'void')); | |
| 1368 if (!inPlainSync && optional("await", begin)) { | |
| 1369 return parseExpressionStatement(begin); | |
| 1370 } | |
| 1371 | |
| 1372 if (looksLikeType && token.isIdentifier) { | |
| 1373 // If the identifier token has a type substitution comment /*=T*/, | |
| 1374 // then the set of tokens type tokens should be replaced with the | |
| 1375 // tokens parsed from the comment. | |
| 1376 Token afterId = token.next; | |
| 1377 | |
| 1378 begin = | |
| 1379 listener.replaceTokenWithGenericCommentTypeAssign(begin, token); | |
| 1380 | |
| 1381 int afterIdKind = afterId.kind; | |
| 1382 if (looksLikeVariableDeclarationEnd(afterIdKind)) { | |
| 1383 // We are looking at `type identifier` followed by | |
| 1384 // `(',' | '=' | ';')`. | |
| 1385 | |
| 1386 // TODO(ahe): Generate type events and call | |
| 1387 // parseVariablesDeclarationRest instead. | |
| 1388 return parseVariablesDeclaration(begin); | |
| 1389 } else if (OPEN_PAREN_TOKEN == afterIdKind) { | |
| 1390 // We are looking at `type identifier '('`. | |
| 1391 if (looksLikeFunctionBody(getClose(afterId).next)) { | |
| 1392 // We are looking at `type identifier '(' ... ')'` followed | |
| 1393 // `( '{' | '=>' | 'async' | 'sync' )`. | |
| 1394 return parseFunctionDeclaration(begin); | |
| 1395 } | |
| 1396 } else if (identical(afterIdKind, LT_TOKEN)) { | |
| 1397 // We are looking at `type identifier '<'`. | |
| 1398 Token afterTypeVariables = getClose(afterId)?.next; | |
| 1399 if (afterTypeVariables != null && | |
| 1400 optional("(", afterTypeVariables)) { | |
| 1401 if (looksLikeFunctionBody(getClose(afterTypeVariables).next)) { | |
| 1402 // We are looking at "type identifier '<' ... '>' '(' ... ')'" | |
| 1403 // followed by '{', '=>', 'async', or 'sync'. | |
| 1404 return parseFunctionDeclaration(begin); | |
| 1405 } | |
| 1406 } | |
| 1407 } | |
| 1408 // Fall-through to expression statement. | |
| 1409 } else { | |
| 1410 token = begin; | |
| 1411 if (optional(':', token.next)) { | |
| 1412 return parseLabeledStatement(token); | |
| 1413 } else if (optional('(', token.next)) { | |
| 1414 if (looksLikeFunctionBody(getClose(token.next).next)) { | |
| 1415 return parseFunctionDeclaration(token); | |
| 1416 } | |
| 1417 } else if (optional('<', token.next)) { | |
| 1418 Token afterTypeVariables = getClose(token.next)?.next; | |
| 1419 if (afterTypeVariables != null && | |
| 1420 optional("(", afterTypeVariables)) { | |
| 1421 if (looksLikeFunctionBody(getClose(afterTypeVariables).next)) { | |
| 1422 return parseFunctionDeclaration(token); | |
| 1423 } | |
| 1424 } | |
| 1425 // Fall through to expression statement. | |
| 1426 } | |
| 1427 } | |
| 1428 return parseExpressionStatement(begin); | |
| 1429 | |
| 1430 case TypeContinuation.ExpressionStatementOrConstDeclaration: | |
| 1431 Token identifier; | |
| 1432 if (looksLikeType && token.isIdentifier) { | |
| 1433 identifier = token; | |
| 1434 } else if (begin.next.isIdentifier) { | |
| 1435 identifier = begin.next; | |
| 1436 } | |
| 1437 if (identifier != null) { | |
| 1438 if (looksLikeVariableDeclarationEnd(identifier.next.kind)) { | |
| 1439 // We are looking at "const type identifier" followed by '=', ';', | |
| 1440 // or ','. | |
| 1441 | |
| 1442 // TODO(ahe): Generate type events and call | |
| 1443 // parseVariablesDeclarationRest instead. | |
| 1444 return parseVariablesDeclaration(begin); | |
| 1445 } | |
| 1446 // Fall-through to expression statement. | |
| 1447 } | |
| 1448 | |
| 1449 return parseExpressionStatement(begin); | |
| 1450 | |
| 1451 case TypeContinuation.SendOrFunctionLiteral: | |
| 1452 if (looksLikeType && | |
| 1453 token.isIdentifier && | |
| 1454 isFunctionDeclaration(token.next)) { | |
| 1455 return parseFunctionExpression(begin); | |
| 1456 } else if (isFunctionDeclaration(begin.next)) { | |
| 1457 return parseFunctionExpression(begin); | |
| 1458 } | |
| 1459 return parseSend(begin, continuationContext); | |
| 1460 | |
| 1461 case TypeContinuation.VariablesDeclarationOrExpression: | |
| 1462 if (looksLikeType && | |
| 1463 token.isIdentifier && | |
| 1464 isOneOf4(token.next, '=', ';', ',', 'in')) { | |
| 1465 // TODO(ahe): Generate type events and call | |
| 1466 // parseVariablesDeclarationNoSemicolonRest instead. | |
| 1467 return parseVariablesDeclarationNoSemicolon(begin); | |
| 1468 } | |
| 1469 return parseExpression(begin); | |
| 1238 } | 1470 } |
| 1239 | 1471 |
| 1240 throw "Internal error: Unhandled continuation '$continuation'."; | 1472 throw "Internal error: Unhandled continuation '$continuation'."; |
| 1241 } | 1473 } |
| 1242 | 1474 |
| 1243 /// Parses a generalized function type. | 1475 /// Parses a generalized function type. |
| 1244 /// | 1476 /// |
| 1245 /// The return type must already be pushed. | 1477 /// The return type must already be pushed. |
| 1246 Token parseFunctionType(Token token) { | 1478 Token parseFunctionType(Token token) { |
| 1247 assert(optional('Function', token)); | 1479 assert(optional('Function', token)); |
| (...skipping 593 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1841 : TypeContinuation.Required); | 2073 : TypeContinuation.Required); |
| 1842 if (typeRequired && beforeType == token) { | 2074 if (typeRequired && beforeType == token) { |
| 1843 reportRecoverableErrorCode(token, codeTypeRequired); | 2075 reportRecoverableErrorCode(token, codeTypeRequired); |
| 1844 } | 2076 } |
| 1845 if (hasVar && beforeType != token) { | 2077 if (hasVar && beforeType != token) { |
| 1846 reportRecoverableErrorCode(beforeType, codeTypeAfterVar); | 2078 reportRecoverableErrorCode(beforeType, codeTypeAfterVar); |
| 1847 } | 2079 } |
| 1848 return token; | 2080 return token; |
| 1849 } | 2081 } |
| 1850 | 2082 |
| 1851 /// Returns the first token after the type starting at [token]. | |
| 1852 /// | |
| 1853 /// This method assumes that [token] is an identifier (or void). Use | |
| 1854 /// [peekAfterIfType] if [token] isn't known to be an identifier. | |
| 1855 Token peekAfterType(Token token) { | |
| 1856 // We are looking at "identifier ...". | |
| 1857 Token peek = token; | |
| 1858 if (!isGeneralizedFunctionType(token)) { | |
| 1859 peek = peekAfterNominalType(token); | |
| 1860 } | |
| 1861 | |
| 1862 // We might have just skipped over the return value of the function type. | |
| 1863 // Check again, if we are now at a function type position. | |
| 1864 while (isGeneralizedFunctionType(peek)) { | |
| 1865 peek = peekAfterFunctionType(peek.next); | |
| 1866 } | |
| 1867 return peek; | |
| 1868 } | |
| 1869 | |
| 1870 /// Returns the first token after the nominal type starting at [token]. | |
| 1871 /// | |
| 1872 /// This method assumes that [token] is an identifier (or void). | |
| 1873 Token peekAfterNominalType(Token token) { | |
| 1874 Token peek = token.next; | |
| 1875 if (identical(peek.kind, PERIOD_TOKEN)) { | |
| 1876 if (peek.next.isIdentifier) { | |
| 1877 // Look past a library prefix. | |
| 1878 peek = peek.next.next; | |
| 1879 } | |
| 1880 } | |
| 1881 // We are looking at "qualified ...". | |
| 1882 if (identical(peek.kind, LT_TOKEN)) { | |
| 1883 // Possibly generic type. | |
| 1884 // We are looking at "qualified '<'". | |
| 1885 BeginToken beginGroupToken = peek; | |
| 1886 Token gtToken = beginGroupToken.endGroup; | |
| 1887 if (gtToken != null) { | |
| 1888 // We are looking at "qualified '<' ... '>' ...". | |
| 1889 peek = gtToken.next; | |
| 1890 } | |
| 1891 } | |
| 1892 return peek; | |
| 1893 } | |
| 1894 | |
| 1895 /// Returns the first token after the function type starting at [token]. | |
| 1896 /// | |
| 1897 /// The token must be at the token *after* the `Function` token | |
| 1898 /// position. That is, the return type and the `Function` token must have | |
| 1899 /// already been skipped. | |
| 1900 /// | |
| 1901 /// This function only skips over one function type syntax. If necessary, | |
| 1902 /// this function must be called multiple times. | |
| 1903 /// | |
| 1904 /// Example: | |
| 1905 /// | |
| 1906 /// int Function() Function<T>(int) | |
| 1907 /// ^ ^ | |
| 1908 /// | |
| 1909 /// A call to this function must be either at `(` or at `<`. If `token` | |
| 1910 /// pointed to the first `(`, then the returned token points to the second | |
| 1911 /// `Function` token. | |
| 1912 Token peekAfterFunctionType(Token token) { | |
| 1913 // Possible inputs are: | |
| 1914 // ( ... ) | |
| 1915 // < ... >( ... ) | |
| 1916 | |
| 1917 Token peek = token; | |
| 1918 // If there is a generic argument to the function, skip over that one first. | |
| 1919 if (identical(peek.kind, LT_TOKEN)) { | |
| 1920 BeginToken beginGroupToken = peek; | |
| 1921 Token closeToken = beginGroupToken.endGroup; | |
| 1922 if (closeToken != null) { | |
| 1923 peek = closeToken.next; | |
| 1924 } | |
| 1925 } | |
| 1926 | |
| 1927 // Now we just need to skip over the formals. | |
| 1928 expect('(', peek); | |
| 1929 | |
| 1930 BeginToken beginGroupToken = peek; | |
| 1931 Token closeToken = beginGroupToken.endGroup; | |
| 1932 if (closeToken != null) { | |
| 1933 peek = closeToken.next; | |
| 1934 } | |
| 1935 | |
| 1936 return peek; | |
| 1937 } | |
| 1938 | |
| 1939 /// If [token] is the start of a type, returns the token after that type. | |
| 1940 /// If [token] is not the start of a type, null is returned. | |
| 1941 Token peekAfterIfType(Token token) { | |
| 1942 if (!optional('void', token) && !token.isIdentifier) { | |
| 1943 return null; | |
| 1944 } | |
| 1945 return peekAfterType(token); | |
| 1946 } | |
| 1947 | |
| 1948 Token skipClassBody(Token token) { | 2083 Token skipClassBody(Token token) { |
| 1949 if (!optional('{', token)) { | 2084 if (!optional('{', token)) { |
| 1950 return reportUnrecoverableErrorCodeWithToken( | 2085 return reportUnrecoverableErrorCodeWithToken( |
| 1951 token, codeExpectedClassBodyToSkip) | 2086 token, codeExpectedClassBodyToSkip) |
| 1952 .next; | 2087 .next; |
| 1953 } | 2088 } |
| 1954 BeginToken beginGroupToken = token; | 2089 BeginToken beginGroupToken = token; |
| 1955 Token endGroup = beginGroupToken.endGroup; | 2090 Token endGroup = beginGroupToken.endGroup; |
| 1956 if (endGroup == null || !identical(endGroup.kind, $CLOSE_CURLY_BRACKET)) { | 2091 if (endGroup == null || !identical(endGroup.kind, $CLOSE_CURLY_BRACKET)) { |
| 1957 return reportUnmatchedToken(beginGroupToken).next; | 2092 return reportUnmatchedToken(beginGroupToken).next; |
| (...skipping 603 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2561 } else { | 2696 } else { |
| 2562 token = parseExpression(token); | 2697 token = parseExpression(token); |
| 2563 if (inGenerator) { | 2698 if (inGenerator) { |
| 2564 reportRecoverableErrorCode(begin.next, codeGeneratorReturnsValue); | 2699 reportRecoverableErrorCode(begin.next, codeGeneratorReturnsValue); |
| 2565 } | 2700 } |
| 2566 listener.endReturnStatement(true, begin, token); | 2701 listener.endReturnStatement(true, begin, token); |
| 2567 } | 2702 } |
| 2568 return expectSemicolon(token); | 2703 return expectSemicolon(token); |
| 2569 } | 2704 } |
| 2570 | 2705 |
| 2571 Token peekIdentifierAfterType(Token token) { | 2706 Token parseExpressionStatementOrDeclaration(Token token) { |
| 2572 Token peek = peekAfterType(token); | 2707 return parseType(token, TypeContinuation.ExpressionStatementOrDeclaration); |
| 2573 if (peek != null && peek.isIdentifier) { | 2708 } |
| 2574 // We are looking at "type identifier". | 2709 |
| 2575 return peek; | 2710 Token parseExpressionStatementOrConstDeclaration(Token token) { |
| 2711 assert(optional('const', token)); | |
| 2712 if (isModifier(token.next)) { | |
| 2713 return parseVariablesDeclaration(token); | |
| 2576 } else { | 2714 } else { |
| 2577 return null; | 2715 return parseType( |
| 2716 token, TypeContinuation.ExpressionStatementOrConstDeclaration); | |
| 2578 } | 2717 } |
| 2579 } | 2718 } |
| 2580 | 2719 |
| 2581 Token peekIdentifierAfterOptionalType(Token token) { | |
| 2582 Token peek = peekAfterIfType(token); | |
| 2583 if (peek != null && peek.isIdentifier) { | |
| 2584 // We are looking at "type identifier". | |
| 2585 return peek; | |
| 2586 } else if (token.isIdentifier) { | |
| 2587 // We are looking at "identifier". | |
| 2588 return token; | |
| 2589 } else { | |
| 2590 return null; | |
| 2591 } | |
| 2592 } | |
| 2593 | |
| 2594 Token parseExpressionStatementOrDeclaration(Token token) { | |
| 2595 if (!inPlainSync && optional("await", token)) { | |
| 2596 return parseExpressionStatement(token); | |
| 2597 } | |
| 2598 assert(token.isIdentifier || identical(token.stringValue, 'void')); | |
| 2599 Token identifier = peekIdentifierAfterType(token); | |
| 2600 if (identifier != null) { | |
| 2601 assert(identifier.isIdentifier); | |
| 2602 | |
| 2603 // If the identifier token has a type substitution comment /*=T*/, | |
| 2604 // then the set of tokens type tokens should be replaced with the | |
| 2605 // tokens parsed from the comment. | |
| 2606 token = | |
| 2607 listener.replaceTokenWithGenericCommentTypeAssign(token, identifier); | |
| 2608 | |
| 2609 Token afterId = identifier.next; | |
| 2610 int afterIdKind = afterId.kind; | |
| 2611 if (identical(afterIdKind, EQ_TOKEN) || | |
| 2612 identical(afterIdKind, SEMICOLON_TOKEN) || | |
| 2613 identical(afterIdKind, COMMA_TOKEN)) { | |
| 2614 // We are looking at "type identifier" followed by '=', ';', ','. | |
| 2615 return parseVariablesDeclaration(token); | |
| 2616 } else if (identical(afterIdKind, OPEN_PAREN_TOKEN)) { | |
| 2617 // We are looking at "type identifier '('". | |
| 2618 BeginToken beginParen = afterId; | |
| 2619 Token endParen = beginParen.endGroup; | |
| 2620 // TODO(eernst): Check for NPE as described in issue 26252. | |
| 2621 Token afterParens = endParen.next; | |
| 2622 if (optional('{', afterParens) || | |
| 2623 optional('=>', afterParens) || | |
| 2624 optional('async', afterParens) || | |
| 2625 optional('sync', afterParens)) { | |
| 2626 // We are looking at "type identifier '(' ... ')'" followed | |
| 2627 // by '{', '=>', 'async', or 'sync'. | |
| 2628 return parseFunctionDeclaration(token); | |
| 2629 } | |
| 2630 } else if (identical(afterIdKind, LT_TOKEN)) { | |
| 2631 // We are looking at "type identifier '<'". | |
| 2632 BeginToken beginAngle = afterId; | |
| 2633 Token endAngle = beginAngle.endGroup; | |
| 2634 if (endAngle != null && | |
| 2635 identical(endAngle.next.kind, OPEN_PAREN_TOKEN)) { | |
| 2636 BeginToken beginParen = endAngle.next; | |
| 2637 Token endParen = beginParen.endGroup; | |
| 2638 if (endParen != null) { | |
| 2639 Token afterParens = endParen.next; | |
| 2640 if (optional('{', afterParens) || | |
| 2641 optional('=>', afterParens) || | |
| 2642 optional('async', afterParens) || | |
| 2643 optional('sync', afterParens)) { | |
| 2644 // We are looking at "type identifier '<' ... '>' '(' ... ')'" | |
| 2645 // followed by '{', '=>', 'async', or 'sync'. | |
| 2646 return parseFunctionDeclaration(token); | |
| 2647 } | |
| 2648 } | |
| 2649 } | |
| 2650 } | |
| 2651 // Fall-through to expression statement. | |
| 2652 } else { | |
| 2653 if (optional(':', token.next)) { | |
| 2654 return parseLabeledStatement(token); | |
| 2655 } else if (optional('(', token.next)) { | |
| 2656 BeginToken begin = token.next; | |
| 2657 // TODO(eernst): Check for NPE as described in issue 26252. | |
| 2658 String afterParens = begin.endGroup.next.stringValue; | |
| 2659 if (identical(afterParens, '{') || | |
| 2660 identical(afterParens, '=>') || | |
| 2661 identical(afterParens, 'async') || | |
| 2662 identical(afterParens, 'sync')) { | |
| 2663 return parseFunctionDeclaration(token); | |
| 2664 } | |
| 2665 } else if (optional('<', token.next)) { | |
| 2666 BeginToken beginAngle = token.next; | |
| 2667 Token endAngle = beginAngle.endGroup; | |
| 2668 if (endAngle != null && | |
| 2669 identical(endAngle.next.kind, OPEN_PAREN_TOKEN)) { | |
| 2670 BeginToken beginParen = endAngle.next; | |
| 2671 Token endParen = beginParen.endGroup; | |
| 2672 if (endParen != null) { | |
| 2673 String afterParens = endParen.next.stringValue; | |
| 2674 if (identical(afterParens, '{') || | |
| 2675 identical(afterParens, '=>') || | |
| 2676 identical(afterParens, 'async') || | |
| 2677 identical(afterParens, 'sync')) { | |
| 2678 return parseFunctionDeclaration(token); | |
| 2679 } | |
| 2680 } | |
| 2681 } | |
| 2682 // Fall through to expression statement. | |
| 2683 } | |
| 2684 } | |
| 2685 return parseExpressionStatement(token); | |
| 2686 } | |
| 2687 | |
| 2688 Token parseExpressionStatementOrConstDeclaration(Token token) { | |
| 2689 assert(identical(token.stringValue, 'const')); | |
| 2690 if (isModifier(token.next)) { | |
| 2691 return parseVariablesDeclaration(token); | |
| 2692 } | |
| 2693 listener.injectGenericCommentTypeAssign(token.next); | |
| 2694 Token identifier = peekIdentifierAfterOptionalType(token.next); | |
| 2695 if (identifier != null) { | |
| 2696 assert(identifier.isIdentifier); | |
| 2697 Token afterId = identifier.next; | |
| 2698 int afterIdKind = afterId.kind; | |
| 2699 if (identical(afterIdKind, EQ_TOKEN) || | |
| 2700 identical(afterIdKind, SEMICOLON_TOKEN) || | |
| 2701 identical(afterIdKind, COMMA_TOKEN)) { | |
| 2702 // We are looking at "const type identifier" followed by '=', ';', or | |
| 2703 // ','. | |
| 2704 return parseVariablesDeclaration(token); | |
| 2705 } | |
| 2706 // Fall-through to expression statement. | |
| 2707 } | |
| 2708 | |
| 2709 return parseExpressionStatement(token); | |
| 2710 } | |
| 2711 | |
| 2712 Token parseLabel(Token token) { | 2720 Token parseLabel(Token token) { |
| 2713 token = parseIdentifier(token, IdentifierContext.labelDeclaration); | 2721 token = parseIdentifier(token, IdentifierContext.labelDeclaration); |
| 2714 Token colon = token; | 2722 Token colon = token; |
| 2715 token = expect(':', token); | 2723 token = expect(':', token); |
| 2716 listener.handleLabel(colon); | 2724 listener.handleLabel(colon); |
| 2717 return token; | 2725 return token; |
| 2718 } | 2726 } |
| 2719 | 2727 |
| 2720 Token parseLabeledStatement(Token token) { | 2728 Token parseLabeledStatement(Token token) { |
| 2721 int labelCount = 0; | 2729 int labelCount = 0; |
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3055 | 3063 |
| 3056 Token expressionExpected(Token token) { | 3064 Token expressionExpected(Token token) { |
| 3057 token = reportUnrecoverableErrorCodeWithToken(token, codeExpectedExpression) | 3065 token = reportUnrecoverableErrorCodeWithToken(token, codeExpectedExpression) |
| 3058 .next; | 3066 .next; |
| 3059 listener.handleInvalidExpression(token); | 3067 listener.handleInvalidExpression(token); |
| 3060 return token; | 3068 return token; |
| 3061 } | 3069 } |
| 3062 | 3070 |
| 3063 Token parseParenthesizedExpressionOrFunctionLiteral(Token token) { | 3071 Token parseParenthesizedExpressionOrFunctionLiteral(Token token) { |
| 3064 BeginToken beginGroup = token; | 3072 BeginToken beginGroup = token; |
| 3065 // TODO(eernst): Check for NPE as described in issue 26252. | |
| 3066 Token nextToken = beginGroup.endGroup.next; | 3073 Token nextToken = beginGroup.endGroup.next; |
| 3067 int kind = nextToken.kind; | 3074 int kind = nextToken.kind; |
| 3068 if (mayParseFunctionExpressions && | 3075 if (mayParseFunctionExpressions && |
| 3069 (identical(kind, FUNCTION_TOKEN) || | 3076 (identical(kind, FUNCTION_TOKEN) || |
| 3070 identical(kind, OPEN_CURLY_BRACKET_TOKEN) || | 3077 identical(kind, OPEN_CURLY_BRACKET_TOKEN) || |
| 3071 (identical(kind, KEYWORD_TOKEN) && | 3078 (identical(kind, KEYWORD_TOKEN) && |
| 3072 (optional('async', nextToken) || | 3079 (optional('async', nextToken) || |
| 3073 optional('sync', nextToken))))) { | 3080 optional('sync', nextToken))))) { |
| 3074 listener.handleNoTypeVariables(token); | 3081 listener.handleNoTypeVariables(token); |
| 3075 return parseUnnamedFunction(token); | 3082 return parseUnnamedFunction(token); |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3234 Token colon = token; | 3241 Token colon = token; |
| 3235 token = expect(':', token); | 3242 token = expect(':', token); |
| 3236 token = parseExpression(token); | 3243 token = parseExpression(token); |
| 3237 listener.endLiteralMapEntry(colon, token); | 3244 listener.endLiteralMapEntry(colon, token); |
| 3238 return token; | 3245 return token; |
| 3239 } | 3246 } |
| 3240 | 3247 |
| 3241 Token parseSendOrFunctionLiteral(Token token, IdentifierContext context) { | 3248 Token parseSendOrFunctionLiteral(Token token, IdentifierContext context) { |
| 3242 if (!mayParseFunctionExpressions) { | 3249 if (!mayParseFunctionExpressions) { |
| 3243 return parseSend(token, context); | 3250 return parseSend(token, context); |
| 3244 } | |
| 3245 Token peek = peekAfterIfType(token); | |
| 3246 if (peek != null && | |
| 3247 identical(peek.kind, IDENTIFIER_TOKEN) && | |
| 3248 isFunctionDeclaration(peek.next)) { | |
| 3249 return parseFunctionExpression(token); | |
| 3250 } else if (isFunctionDeclaration(token.next)) { | |
| 3251 return parseFunctionExpression(token); | |
| 3252 } else { | 3251 } else { |
| 3253 return parseSend(token, context); | 3252 return parseType(token, TypeContinuation.SendOrFunctionLiteral, context); |
| 3254 } | 3253 } |
| 3255 } | 3254 } |
| 3256 | 3255 |
| 3257 bool isFunctionDeclaration(Token token) { | 3256 bool isFunctionDeclaration(Token token) { |
| 3258 if (optional('<', token)) { | 3257 if (optional('<', token)) { |
| 3259 BeginToken begin = token; | 3258 BeginToken begin = token; |
| 3260 if (begin.endGroup == null) return false; | 3259 if (begin.endGroup == null) return false; |
| 3261 token = begin.endGroup.next; | 3260 token = begin.endGroup.next; |
| 3262 } | 3261 } |
| 3263 if (optional('(', token)) { | 3262 if (optional('(', token)) { |
| 3264 BeginToken begin = token; | 3263 BeginToken begin = token; |
| 3265 // TODO(eernst): Check for NPE as described in issue 26252. | |
| 3266 String afterParens = begin.endGroup.next.stringValue; | 3264 String afterParens = begin.endGroup.next.stringValue; |
| 3267 if (identical(afterParens, '{') || | 3265 if (identical(afterParens, '{') || |
| 3268 identical(afterParens, '=>') || | 3266 identical(afterParens, '=>') || |
| 3269 identical(afterParens, 'async') || | 3267 identical(afterParens, 'async') || |
| 3270 identical(afterParens, 'sync')) { | 3268 identical(afterParens, 'sync')) { |
| 3271 return true; | 3269 return true; |
| 3272 } | 3270 } |
| 3273 } | 3271 } |
| 3274 return false; | 3272 return false; |
| 3275 } | 3273 } |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3510 // The is- and as-operators cannot be chained. | 3508 // The is- and as-operators cannot be chained. |
| 3511 reportUnexpectedToken(token); | 3509 reportUnexpectedToken(token); |
| 3512 } | 3510 } |
| 3513 return token; | 3511 return token; |
| 3514 } | 3512 } |
| 3515 | 3513 |
| 3516 Token parseVariablesDeclaration(Token token) { | 3514 Token parseVariablesDeclaration(Token token) { |
| 3517 return parseVariablesDeclarationMaybeSemicolon(token, true); | 3515 return parseVariablesDeclarationMaybeSemicolon(token, true); |
| 3518 } | 3516 } |
| 3519 | 3517 |
| 3518 Token parseVariablesDeclarationRest(Token token) { | |
| 3519 return parseVariablesDeclarationMaybeSemicolonRest(token, true); | |
| 3520 } | |
| 3521 | |
| 3520 Token parseVariablesDeclarationNoSemicolon(Token token) { | 3522 Token parseVariablesDeclarationNoSemicolon(Token token) { |
| 3521 // Only called when parsing a for loop, so this is for parsing locals. | 3523 // Only called when parsing a for loop, so this is for parsing locals. |
| 3522 return parseVariablesDeclarationMaybeSemicolon(token, false); | 3524 return parseVariablesDeclarationMaybeSemicolon(token, false); |
| 3523 } | 3525 } |
| 3524 | 3526 |
| 3527 Token parseVariablesDeclarationNoSemicolonRest(Token token) { | |
| 3528 // Only called when parsing a for loop, so this is for parsing locals. | |
| 3529 return parseVariablesDeclarationMaybeSemicolonRest(token, false); | |
| 3530 } | |
| 3531 | |
| 3525 Token parseVariablesDeclarationMaybeSemicolon( | 3532 Token parseVariablesDeclarationMaybeSemicolon( |
| 3526 Token token, bool endWithSemicolon) { | 3533 Token token, bool endWithSemicolon) { |
| 3527 int count = 1; | |
| 3528 token = parseMetadataStar(token); | 3534 token = parseMetadataStar(token); |
| 3529 | 3535 |
| 3530 // If the next token has a type substitution comment /*=T*/, then | 3536 // If the next token has a type substitution comment /*=T*/, then |
| 3531 // the current 'var' token should be repealed and replaced. | 3537 // the current 'var' token should be repealed and replaced. |
| 3532 if (optional('var', token)) { | 3538 if (optional('var', token)) { |
| 3533 token = | 3539 token = |
| 3534 listener.replaceTokenWithGenericCommentTypeAssign(token, token.next); | 3540 listener.replaceTokenWithGenericCommentTypeAssign(token, token.next); |
| 3535 } | 3541 } |
| 3536 | 3542 |
| 3537 token = parseModifiers(token, MemberKind.Local, isVariable: true); | 3543 token = parseModifiers(token, MemberKind.Local, isVariable: true); |
| 3544 return parseVariablesDeclarationMaybeSemicolonRest(token, endWithSemicolon); | |
| 3545 } | |
| 3546 | |
| 3547 Token parseVariablesDeclarationMaybeSemicolonRest( | |
| 3548 Token token, bool endWithSemicolon) { | |
| 3549 int count = 1; | |
| 3538 listener.beginVariablesDeclaration(token); | 3550 listener.beginVariablesDeclaration(token); |
| 3539 token = parseOptionallyInitializedIdentifier(token); | 3551 token = parseOptionallyInitializedIdentifier(token); |
| 3540 while (optional(',', token)) { | 3552 while (optional(',', token)) { |
| 3541 token = parseOptionallyInitializedIdentifier(token.next); | 3553 token = parseOptionallyInitializedIdentifier(token.next); |
| 3542 ++count; | 3554 ++count; |
| 3543 } | 3555 } |
| 3544 if (endWithSemicolon) { | 3556 if (endWithSemicolon) { |
| 3545 Token semicolon = token; | 3557 Token semicolon = token; |
| 3546 token = expectSemicolon(semicolon); | 3558 token = expectSemicolon(semicolon); |
| 3547 listener.endVariablesDeclaration(count, semicolon); | 3559 listener.endVariablesDeclaration(count, semicolon); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3598 } | 3610 } |
| 3599 | 3611 |
| 3600 Token parseVariablesDeclarationOrExpressionOpt(Token token) { | 3612 Token parseVariablesDeclarationOrExpressionOpt(Token token) { |
| 3601 final String value = token.stringValue; | 3613 final String value = token.stringValue; |
| 3602 if (identical(value, ';')) { | 3614 if (identical(value, ';')) { |
| 3603 listener.handleNoExpression(token); | 3615 listener.handleNoExpression(token); |
| 3604 return token; | 3616 return token; |
| 3605 } else if (isOneOf4(token, '@', 'var', 'final', 'const')) { | 3617 } else if (isOneOf4(token, '@', 'var', 'final', 'const')) { |
| 3606 return parseVariablesDeclarationNoSemicolon(token); | 3618 return parseVariablesDeclarationNoSemicolon(token); |
| 3607 } | 3619 } |
| 3608 Token identifier = peekIdentifierAfterType(token); | 3620 return parseType(token, TypeContinuation.VariablesDeclarationOrExpression); |
| 3609 if (identifier != null) { | |
| 3610 assert(identifier.isIdentifier); | |
| 3611 if (isOneOf4(identifier.next, '=', ';', ',', 'in')) { | |
| 3612 return parseVariablesDeclarationNoSemicolon(token); | |
| 3613 } | |
| 3614 } | |
| 3615 return parseExpression(token); | |
| 3616 } | 3621 } |
| 3617 | 3622 |
| 3618 Token parseForRest(Token forToken, Token leftParenthesis, Token token) { | 3623 Token parseForRest(Token forToken, Token leftParenthesis, Token token) { |
| 3619 Token leftSeparator = token; | 3624 Token leftSeparator = token; |
| 3620 token = expectSemicolon(token); | 3625 token = expectSemicolon(token); |
| 3621 if (optional(';', token)) { | 3626 if (optional(';', token)) { |
| 3622 token = parseEmptyStatement(token); | 3627 token = parseEmptyStatement(token); |
| 3623 } else { | 3628 } else { |
| 3624 token = parseExpressionStatement(token); | 3629 token = parseExpressionStatement(token); |
| 3625 } | 3630 } |
| (...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4064 return reportUnrecoverableError( | 4069 return reportUnrecoverableError( |
| 4065 token, () => code.format(uri, token.charOffset, string)); | 4070 token, () => code.format(uri, token.charOffset, string)); |
| 4066 } | 4071 } |
| 4067 } | 4072 } |
| 4068 | 4073 |
| 4069 typedef FastaMessage NoArgument(Uri uri, int charOffset); | 4074 typedef FastaMessage NoArgument(Uri uri, int charOffset); |
| 4070 | 4075 |
| 4071 typedef FastaMessage TokenArgument(Uri uri, int charOffset, Token token); | 4076 typedef FastaMessage TokenArgument(Uri uri, int charOffset, Token token); |
| 4072 | 4077 |
| 4073 typedef FastaMessage StringArgument(Uri uri, int charOffset, String string); | 4078 typedef FastaMessage StringArgument(Uri uri, int charOffset, String string); |
| OLD | NEW |