Chromium Code Reviews| Index: runtime/vm/parser.cc |
| diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
| index e9e6e93b215b35c03ca941902529e795dc244fa2..6ec2886643f90657b49167f26d6ffce2168e3d5e 100644 |
| --- a/runtime/vm/parser.cc |
| +++ b/runtime/vm/parser.cc |
| @@ -48,6 +48,7 @@ DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef."); |
| // committed to the current version. |
| DEFINE_FLAG(bool, conditional_directives, true, |
| "Enable conditional directives"); |
| +DEFINE_FLAG(bool, generic_method_syntax, false, "Enbable generic functions."); |
| DEFINE_FLAG(bool, initializing_formal_access, false, |
| "Make initializing formal parameters visible in initializer list."); |
| DEFINE_FLAG(bool, warn_super, false, |
| @@ -125,6 +126,24 @@ class BoolScope : public ValueObject { |
| }; |
| +// Helper class to save and restore token position. |
| +class Parser::TokenPosScope : public ValueObject { |
| + public: |
| + explicit TokenPosScope(Parser *p) : _p(p) { |
| + _saved_pos = p->TokenPos(); |
| + } |
| + TokenPosScope(Parser *p, TokenPosition pos) : _p(p), _saved_pos(pos) { |
| + } |
| + ~TokenPosScope() { |
| + _p->SetPosition(_saved_pos); |
| + } |
| + |
| + private: |
| + Parser* _p; |
| + TokenPosition _saved_pos; |
|
siva
2016/08/19 20:30:58
Normally we have been using _ as a suffix not pref
hausner
2016/08/19 21:40:04
Done. Accedentally mixed dart and c++ convention.
|
| +}; |
| + |
| + |
| class RecursionChecker : public ValueObject { |
| public: |
| explicit RecursionChecker(Parser* p) : parser_(p) { |
| @@ -2065,7 +2084,7 @@ void Parser::ParseFormalParameter(bool allow_explicit_default_value, |
| } |
| } |
| - if (CurrentToken() == Token::kLPAREN) { |
| + if (IsParameterPart()) { |
| // This parameter is probably a closure. If we saw the keyword 'var' |
| // or 'final', a closure is not legal here and we ignore the |
| // opening parens. |
| @@ -2079,6 +2098,18 @@ void Parser::ParseFormalParameter(bool allow_explicit_default_value, |
| AbstractType::Handle(Z, parameter.type->raw()); |
| // Finish parsing the function type parameter. |
| + if (CurrentToken() == Token::kLT) { |
| + // TODO(hausner): handle generic function types. |
| + TokenPosition type_param_pos = TokenPos(); |
| + if (!TryParseTypeParameters()) { |
| + ReportError(type_param_pos, "error in type parameters"); |
|
siva
2016/08/19 20:30:58
This error seems to be printed unconditionally eve
hausner
2016/08/19 21:40:04
The old code would have printed some other syntax
|
| + } |
| + if (!FLAG_generic_method_syntax) { |
| + ReportError(type_param_pos, "generic function types not supported"); |
| + } |
| + } |
| + |
| + ASSERT(CurrentToken() == Token::kLPAREN); |
| ParamList func_params; |
| // Add implicit closure object parameter. |
| @@ -3716,7 +3747,10 @@ RawLibraryPrefix* Parser::ParsePrefix() { |
| void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) { |
| TRACE_PARSER("ParseMethodOrConstructor"); |
| - ASSERT(CurrentToken() == Token::kLPAREN || method->IsGetter()); |
| + // We are at the beginning of the formal parameters list. |
| + ASSERT(CurrentToken() == Token::kLPAREN || |
| + CurrentToken() == Token::kLT || |
| + method->IsGetter()); |
| ASSERT(method->type != NULL); |
| ASSERT(current_member_ == method); |
| @@ -3741,6 +3775,22 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) { |
| current_class().set_is_const(); |
| } |
| + if (CurrentToken() == Token::kLT) { |
| + // Parse type parameters, but ignore them. |
| + // TODO(hausner): handle type parameters. |
| + TokenPosition type_param_pos = TokenPos(); |
| + if (method->IsFactoryOrConstructor()) { |
| + ReportError(method->name_pos, "constructor cannot be generic"); |
| + } |
| + if (method->IsGetter() || method->IsSetter()) { |
| + ReportError(type_param_pos, "%s cannot be generic", |
| + method->IsGetter() ? "getter" : "setter"); |
| + } |
| + if (!TryParseTypeParameters()) { |
| + ReportError(type_param_pos, "error in type parameters"); |
| + } |
|
siva
2016/08/19 20:30:58
Ditto comment about error being unconditional and
hausner
2016/08/19 21:40:04
The error will be printed when the function is act
|
| + } |
| + |
| // Parse the formal parameters. |
| const bool are_implicitly_final = method->has_const; |
| const bool allow_explicit_default_values = true; |
| @@ -4341,6 +4391,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members, |
| member.has_static = true; |
| // The result type depends on the name of the factory method. |
| } |
| + |
| // Optionally parse a type. |
| if (CurrentToken() == Token::kVOID) { |
| if (member.has_var || member.has_factory) { |
| @@ -4349,29 +4400,24 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members, |
| ConsumeToken(); |
| ASSERT(member.type == NULL); |
| member.type = &Object::void_type(); |
| - } else if (CurrentToken() == Token::kIDENT) { |
| - // This is either a type name or the name of a method/constructor/field. |
| - if ((member.type == NULL) && !member.has_factory) { |
| - // We have not seen a member type yet, so we check if the next |
| - // identifier could represent a type before parsing it. |
| - Token::Kind follower = LookaheadToken(1); |
| - // We have an identifier followed by a 'follower' token. |
| - // We either parse a type or assume that no type is specified. |
| - if ((follower == Token::kLT) || // Parameterized type. |
| - (follower == Token::kGET) || // Getter following a type. |
| - (follower == Token::kSET) || // Setter following a type. |
| - (follower == Token::kOPERATOR) || // Operator following a type. |
| - (Token::IsIdentifier(follower)) || // Member name following a type. |
| - ((follower == Token::kPERIOD) && // Qualified class name of type, |
| - (LookaheadToken(3) != Token::kLPAREN))) { // but not a named constr. |
| - ASSERT(is_top_level_); |
| - // The declared type of fields is never ignored, even in unchecked mode, |
| - // because getters and setters could be closurized at some time (not |
| - // supported yet). |
| - member.type = &AbstractType::ZoneHandle(Z, |
| - ParseType(ClassFinalizer::kResolveTypeParameters)); |
| + } else { |
| + bool found_type = false; |
| + { |
| + // Lookahead to determine whether the next tokens are a return type. |
| + TokenPosScope saved_pos(this); |
| + if (TryParseReturnType()) { |
| + if (IsIdentifier() || |
| + (CurrentToken() == Token::kGET) || |
| + (CurrentToken() == Token::kSET) || |
| + (CurrentToken() == Token::kOPERATOR)) { |
| + found_type = true; |
| + } |
| } |
| } |
| + if (found_type) { |
| + member.type = &AbstractType::ZoneHandle(Z, |
| + ParseType(ClassFinalizer::kResolveTypeParameters)); |
| + } |
| } |
| // Optionally parse a (possibly named) constructor name or factory. |
| @@ -4422,6 +4468,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members, |
| CheckToken(Token::kLPAREN); |
| } else if ((CurrentToken() == Token::kGET) && !member.has_var && |
| (LookaheadToken(1) != Token::kLPAREN) && |
| + (LookaheadToken(1) != Token::kLT) && |
| (LookaheadToken(1) != Token::kASSIGN) && |
| (LookaheadToken(1) != Token::kCOMMA) && |
| (LookaheadToken(1) != Token::kSEMICOLON)) { |
| @@ -4432,6 +4479,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members, |
| // If the result type was not specified, it will be set to DynamicType. |
| } else if ((CurrentToken() == Token::kSET) && !member.has_var && |
| (LookaheadToken(1) != Token::kLPAREN) && |
| + (LookaheadToken(1) != Token::kLT) && |
| (LookaheadToken(1) != Token::kASSIGN) && |
| (LookaheadToken(1) != Token::kCOMMA) && |
| (LookaheadToken(1) != Token::kSEMICOLON)) { |
| @@ -4450,6 +4498,8 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members, |
| (LookaheadToken(1) != Token::kASSIGN) && |
| (LookaheadToken(1) != Token::kCOMMA) && |
| (LookaheadToken(1) != Token::kSEMICOLON)) { |
| + // TODO(hausner): handle the case of a generic function named 'operator': |
| + // eg: T operator<T>(a, b) => ... |
| ConsumeToken(); |
| if (!Token::CanBeOverloaded(CurrentToken())) { |
| ReportError("invalid operator overloading"); |
| @@ -4473,7 +4523,7 @@ void Parser::ParseClassMemberDefinition(ClassDesc* members, |
| } |
| ASSERT(member.name != NULL); |
| - if (CurrentToken() == Token::kLPAREN || member.IsGetter()) { |
| + if (IsParameterPart() || member.IsGetter()) { |
| // Constructor or method. |
| if (member.type == NULL) { |
| member.type = &Object::dynamic_type(); |
| @@ -5128,16 +5178,14 @@ bool Parser::IsFunctionTypeAliasName() { |
| if (IsIdentifier() && (LookaheadToken(1) == Token::kLPAREN)) { |
| return true; |
| } |
| - const TokenPosition saved_pos = TokenPos(); |
| - bool is_alias_name = false; |
| + const TokenPosScope saved_pos(this); |
| if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) { |
| ConsumeToken(); |
| if (TryParseTypeParameters() && (CurrentToken() == Token::kLPAREN)) { |
| - is_alias_name = true; |
| + return true; |
| } |
| } |
| - SetPosition(saved_pos); |
| - return is_alias_name; |
| + return false; |
| } |
| @@ -5147,16 +5195,14 @@ bool Parser::IsMixinAppAlias() { |
| if (IsIdentifier() && (LookaheadToken(1) == Token::kASSIGN)) { |
| return true; |
| } |
| - const TokenPosition saved_pos = TokenPos(); |
| - bool is_mixin_def = false; |
| + const TokenPosScope saved_pos(this); |
| if (IsIdentifier() && (LookaheadToken(1) == Token::kLT)) { |
| ConsumeToken(); |
| if (TryParseTypeParameters() && (CurrentToken() == Token::kASSIGN)) { |
| - is_mixin_def = true; |
| + return true; |
| } |
| } |
| - SetPosition(saved_pos); |
| - return is_mixin_def; |
| + return false; |
| } |
| @@ -5263,9 +5309,9 @@ void Parser::ParseTypedef(const GrowableObjectArray& pending_classes, |
| } |
| -// Consumes exactly one right angle bracket. If the current token is a single |
| -// bracket token, it is consumed normally. However, if it is a double or triple |
| -// bracket, it is replaced by a single or double bracket token without |
| +// Consumes exactly one right angle bracket. If the current token is |
| +// a single bracket token, it is consumed normally. However, if it is |
| +// a double bracket, it is replaced by a single bracket token without |
| // incrementing the token index. |
| void Parser::ConsumeRightAngleBracket() { |
| if (token_kind_ == Token::kGT) { |
| @@ -5282,12 +5328,10 @@ bool Parser::IsPatchAnnotation(TokenPosition pos) { |
| if (pos == TokenPosition::kNoSource) { |
| return false; |
| } |
| - TokenPosition saved_pos = TokenPos(); |
| + TokenPosScope saved_pos(this); |
| SetPosition(pos); |
| ExpectToken(Token::kAT); |
| - bool is_patch = IsSymbol(Symbols::Patch()); |
| - SetPosition(saved_pos); |
| - return is_patch; |
| + return IsSymbol(Symbols::Patch()); |
| } |
| @@ -5645,8 +5689,7 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level, |
| result_type = Type::VoidType(); |
| } else { |
| // Parse optional type. |
| - if ((CurrentToken() == Token::kIDENT) && |
| - (LookaheadToken(1) != Token::kLPAREN)) { |
| + if (IsFunctionReturnType()) { |
| result_type = ParseType(ClassFinalizer::kResolveTypeParameters); |
| } |
| } |
| @@ -5668,6 +5711,18 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level, |
| // A setter named x= may co-exist with a function named x, thus we do |
| // not need to check setters. |
| + if (CurrentToken() == Token::kLT) { |
| + // Type parameters of generic function. |
| + // TODO(hausner): handle type parameters. |
| + TokenPosition type_arg_pos = TokenPos(); |
| + if (!TryParseTypeParameters()) { |
| + ReportError(type_arg_pos, "error in type parameters"); |
| + } |
|
siva
2016/08/19 20:30:58
Ditto comment about the error being unconditional
hausner
2016/08/19 21:40:04
Done.
|
| + if (!FLAG_generic_method_syntax) { |
| + ReportError(type_arg_pos, "generic functions not supported"); |
| + } |
| + } |
| + |
| CheckToken(Token::kLPAREN); |
| const TokenPosition function_pos = TokenPos(); |
| ParamList params; |
| @@ -7760,15 +7815,14 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
| const TokenPosition function_pos = TokenPos(); |
| TokenPosition metadata_pos = TokenPosition::kNoSource; |
| if (is_literal) { |
| - ASSERT(CurrentToken() == Token::kLPAREN); |
| + ASSERT(CurrentToken() == Token::kLPAREN || CurrentToken() == Token::kLT); |
| function_name = &Symbols::AnonymousClosure(); |
| } else { |
| metadata_pos = SkipMetadata(); |
| if (CurrentToken() == Token::kVOID) { |
| ConsumeToken(); |
| result_type = Type::VoidType(); |
| - } else if ((CurrentToken() == Token::kIDENT) && |
| - (LookaheadToken(1) != Token::kLPAREN)) { |
| + } else if (IsFunctionReturnType()) { |
| result_type = ParseType(ClassFinalizer::kCanonicalize); |
| } |
| const TokenPosition name_pos = TokenPos(); |
| @@ -7790,6 +7844,18 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
| line_number); |
| } |
| } |
| + |
| + if (CurrentToken() == Token::kLT) { |
| + TokenPosition type_arg_pos = TokenPos(); |
| + // TODO(hausner): handle type parameters of generic function. |
| + if (!TryParseTypeParameters()) { |
| + ReportError(type_arg_pos, "error in type parameters"); |
| + } |
|
siva
2016/08/19 20:30:58
Ditto comment about the error being unconditional
hausner
2016/08/19 21:40:04
Done.
|
| + if (!FLAG_generic_method_syntax) { |
| + ReportError(type_arg_pos, "generic functions not supported"); |
| + } |
| + } |
| + |
| CheckToken(Token::kLPAREN); |
| // Check whether we have parsed this closure function before, in a previous |
| @@ -7967,20 +8033,22 @@ bool Parser::TryParseTypeParameters() { |
| // We are possibly looking at type parameters. Find closing ">". |
| int nesting_level = 0; |
| do { |
| - if (CurrentToken() == Token::kLT) { |
| + Token::Kind ct = CurrentToken(); |
| + if (ct == Token::kLT) { |
| nesting_level++; |
| - } else if (CurrentToken() == Token::kGT) { |
| + } else if (ct == Token::kGT) { |
| nesting_level--; |
| - } else if (CurrentToken() == Token::kSHR) { |
| + } else if (ct == Token::kSHR) { |
| nesting_level -= 2; |
| - } else if (CurrentToken() == Token::kIDENT) { |
| + } else if (ct == Token::kIDENT) { |
| // Check to see if it is a qualified identifier. |
| if (LookaheadToken(1) == Token::kPERIOD) { |
| // Consume the identifier, the period will be consumed below. |
| ConsumeToken(); |
| } |
| - } else if (CurrentToken() != Token::kCOMMA && |
| - CurrentToken() != Token::kEXTENDS) { |
| + } else if ((ct != Token::kCOMMA) && |
| + (ct != Token::kEXTENDS) && |
| + (!FLAG_generic_method_syntax || (ct != Token::kSUPER))) { |
| // We are looking at something other than type parameters. |
| return false; |
| } |
| @@ -7994,6 +8062,84 @@ bool Parser::TryParseTypeParameters() { |
| } |
| +// Returns true if the next tokens can be parsed as type parameters. |
| +bool Parser::IsTypeParameters() { |
| + if (CurrentToken() == Token::kLT) { |
| + TokenPosScope param_pos(this); |
| + if (!TryParseTypeParameters()) { |
| + return false; |
| + } |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| + |
| +// Returns true if the next tokens are [ typeParameters ] '('. |
| +bool Parser::IsParameterPart() { |
| + if (CurrentToken() == Token::kLPAREN) { |
| + return true; |
| + } |
| + if (CurrentToken() == Token::kLT) { |
| + TokenPosScope type_arg_pos(this); |
| + if (!TryParseTypeParameters()) { |
| + return false; |
| + } |
| + return CurrentToken() == Token::kLPAREN; |
| + } |
| + return false; |
| +} |
| + |
| + |
| +// Returns true if the current and next tokens can be parsed as type |
| +// arguments. Current token position is not saved and restored. |
| +bool Parser::TryParseTypeArguments() { |
| + if (CurrentToken() == Token::kLT) { |
|
siva
2016/08/19 20:30:58
Ditto comment about turning this 'if' into an ASSE
hausner
2016/08/19 21:40:04
Done.
|
| + // We are possibly looking at type arguments. Find closing ">". |
| + int nesting_level = 0; |
| + do { |
| + Token::Kind ct = CurrentToken(); |
| + if (ct == Token::kLT) { |
| + nesting_level++; |
| + } else if (ct == Token::kGT) { |
| + nesting_level--; |
| + } else if (ct == Token::kSHR) { |
| + nesting_level -= 2; |
| + } else if (ct == Token::kIDENT) { |
| + // Check to see if it is a qualified identifier. |
| + if (ct == Token::kPERIOD) { |
|
siva
2016/08/19 20:30:58
ct was kIDENT based on the check above, how will (
hausner
2016/08/19 21:40:04
Nice catch, thank you.
|
| + // Consume the identifier, the period will be consumed below. |
| + ConsumeToken(); |
| + } |
| + } else if (ct != Token::kCOMMA) { |
| + return false; |
| + } |
| + ConsumeToken(); |
| + } while (nesting_level > 0); |
| + if (nesting_level < 0) { |
| + return false; |
| + } |
| + } |
| + return true; |
|
siva
2016/08/19 20:30:58
This will return true for something like
<<>,<>> o
hausner
2016/08/19 21:40:04
Yes, for the same reason as explained above. Parse
|
| +} |
| + |
| + |
| +// Returns true if the next tokens are [ typeArguments ] '('. |
| +bool Parser::IsArgumentPart() { |
| + if (CurrentToken() == Token::kLPAREN) { |
| + return true; |
| + } |
| + if (CurrentToken() == Token::kLT) { |
| + TokenPosScope type_arg_pos(this); |
| + if (!TryParseTypeArguments()) { |
| + return false; |
| + } |
| + return CurrentToken() == Token::kLPAREN; |
| + } |
| + return false; |
| +} |
| + |
| + |
| bool Parser::IsSimpleLiteral(const AbstractType& type, Instance* value) { |
| // Assigning null never causes a type error. |
| if (CurrentToken() == Token::kNULL) { |
| @@ -8157,84 +8303,114 @@ bool Parser::IsVariableDeclaration() { |
| } |
| +// Look ahead to see if the following tokens are a return type followed |
| +// by an identifier. |
| +bool Parser::IsFunctionReturnType() { |
| + TokenPosScope decl_pos(this); |
| + if (TryParseReturnType()) { |
| + if (IsIdentifier()) { |
| + // Return type followed by function name. |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| + |
| // Look ahead to detect whether the next tokens should be parsed as |
| // a function declaration. Token position remains unchanged. |
| bool Parser::IsFunctionDeclaration() { |
| - const TokenPosition saved_pos = TokenPos(); |
| bool is_external = false; |
| + TokenPosScope decl_pos(this); |
| SkipMetadata(); |
| - if (is_top_level_ && (CurrentToken() == Token::kEXTERNAL)) { |
| - // Skip over 'external' for top-level function declarations. |
| - is_external = true; |
| - ConsumeToken(); |
| + if (is_top_level_) { |
| + if (is_patch_source() && |
| + (CurrentToken() == Token::kIDENT) && |
| + CurrentLiteral()->Equals("patch") && |
| + (LookaheadToken(1) != Token::kLPAREN)) { |
| + // Skip over 'patch' for top-level function declarations in patch sources. |
|
siva
2016/08/19 20:30:58
I thought we removed support for 'patch' going for
hausner
2016/08/19 21:40:04
Yes. This probably reappeared because I copied thi
|
| + ConsumeToken(); |
| + } else if (CurrentToken() == Token::kEXTERNAL) { |
| + // Skip over 'external' for top-level function declarations. |
| + is_external = true; |
| + ConsumeToken(); |
| + } |
| } |
| - if (IsIdentifier() && (LookaheadToken(1) == Token::kLPAREN)) { |
| - // Possibly a function without explicit return type. |
| - ConsumeToken(); // Consume function identifier. |
| - } else if (TryParseReturnType()) { |
| + const TokenPosition type_or_name_pos = TokenPos(); |
| + if (TryParseReturnType()) { |
| if (!IsIdentifier()) { |
| - SetPosition(saved_pos); |
| - return false; |
| + SetPosition(type_or_name_pos); |
| } |
| - ConsumeToken(); // Consume function identifier. |
| } else { |
| - SetPosition(saved_pos); |
| + SetPosition(type_or_name_pos); |
| + } |
| + // Check for function name followed by optional type parameters. |
| + if (!IsIdentifier()) { |
| return false; |
| } |
| + ConsumeToken(); |
| + if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) { |
| + return false; |
| + } |
| + |
| + // Optional type, function name and optinal type parameters are parsed. |
| + if (CurrentToken() != Token::kLPAREN) { |
| + return false; |
| + } |
| + |
| // Check parameter list and the following token. |
| - if (CurrentToken() == Token::kLPAREN) { |
| - SkipToMatchingParenthesis(); |
| - if ((CurrentToken() == Token::kLBRACE) || |
| - (CurrentToken() == Token::kARROW) || |
| - (is_top_level_ && IsSymbol(Symbols::Native())) || |
| - is_external || |
| - IsSymbol(Symbols::Async()) || |
| - IsSymbol(Symbols::Sync())) { |
| - SetPosition(saved_pos); |
| - return true; |
| - } |
| + SkipToMatchingParenthesis(); |
| + if ((CurrentToken() == Token::kLBRACE) || |
| + (CurrentToken() == Token::kARROW) || |
| + (is_top_level_ && IsSymbol(Symbols::Native())) || |
| + is_external || |
| + IsSymbol(Symbols::Async()) || |
| + IsSymbol(Symbols::Sync())) { |
| + return true; |
| } |
| - SetPosition(saved_pos); |
| return false; |
| } |
| bool Parser::IsTopLevelAccessor() { |
| - const TokenPosition saved_pos = TokenPos(); |
| + const TokenPosScope saved_pos(this); |
| if (CurrentToken() == Token::kEXTERNAL) { |
| ConsumeToken(); |
| } |
| if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) { |
| - SetPosition(saved_pos); |
| return true; |
| } |
| if (TryParseReturnType()) { |
| if ((CurrentToken() == Token::kGET) || (CurrentToken() == Token::kSET)) { |
| if (Token::IsIdentifier(LookaheadToken(1))) { // Accessor name. |
| - SetPosition(saved_pos); |
| return true; |
| } |
| } |
| } |
| - SetPosition(saved_pos); |
| return false; |
| } |
| bool Parser::IsFunctionLiteral() { |
| - if (CurrentToken() != Token::kLPAREN || !allow_function_literals_) { |
| + if (!allow_function_literals_) { |
| return false; |
| } |
| - const TokenPosition saved_pos = TokenPos(); |
| - bool is_function_literal = false; |
| - SkipToMatchingParenthesis(); |
| - ParseFunctionModifier(); |
| - if ((CurrentToken() == Token::kLBRACE) || |
| - (CurrentToken() == Token::kARROW)) { |
| - is_function_literal = true; |
| + if ((CurrentToken() == Token::kLPAREN) || (CurrentToken() == Token::kLT)) { |
| + TokenPosScope saved_pos(this); |
| + if ((CurrentToken() == Token::kLT) && !TryParseTypeParameters()) { |
| + return false; |
| + } |
| + if (CurrentToken() != Token::kLPAREN) { |
| + return false; |
| + } |
| + SkipToMatchingParenthesis(); |
| + ParseFunctionModifier(); |
| + if ((CurrentToken() == Token::kLBRACE) || |
| + (CurrentToken() == Token::kARROW)) { |
| + return true; |
| + } |
| } |
| - SetPosition(saved_pos); |
| - return is_function_literal; |
| + return false; |
| } |
| @@ -8242,8 +8418,7 @@ bool Parser::IsFunctionLiteral() { |
| // statement. Returns true if we recognize a for ( .. in expr) |
| // statement. |
| bool Parser::IsForInStatement() { |
| - const TokenPosition saved_pos = TokenPos(); |
| - bool result = false; |
| + const TokenPosScope saved_pos(this); |
| // Allow const modifier as well when recognizing a for-in statement |
| // pattern. We will get an error later if the loop variable is |
| // declared with const. |
| @@ -8254,16 +8429,15 @@ bool Parser::IsForInStatement() { |
| } |
| if (IsIdentifier()) { |
| if (LookaheadToken(1) == Token::kIN) { |
| - result = true; |
| + return true; |
| } else if (TryParseOptionalType()) { |
| if (IsIdentifier()) { |
| ConsumeToken(); |
| } |
| - result = (CurrentToken() == Token::kIN); |
| + return CurrentToken() == Token::kIN; |
| } |
| } |
| - SetPosition(saved_pos); |
| - return result; |
| + return false; |
| } |
| @@ -11663,8 +11837,17 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { |
| } |
| const TokenPosition ident_pos = TokenPos(); |
| String* ident = ExpectIdentifier("identifier expected"); |
| - if (CurrentToken() == Token::kLPAREN) { |
| - // Identifier followed by a opening paren: method call. |
| + if (IsArgumentPart()) { |
| + // Identifier followed by optional type arguments and opening paren: |
| + // method call. |
| + if (CurrentToken() == Token::kLT) { |
| + // Type arguments. |
| + if (!FLAG_generic_method_syntax) { |
| + ReportError("generic type arguments not supported."); |
| + } |
| + // TODO(hausner): handle type arguments. |
| + ParseTypeArguments(ClassFinalizer::kIgnore); |
| + } |
| if (left->IsPrimaryNode() && |
| left->AsPrimaryNode()->primary().IsClass()) { |
| // Static method call prefixed with class name. |
| @@ -11756,7 +11939,15 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { |
| } |
| selector = new(Z) LoadIndexedNode( |
| bracket_pos, array, index, Class::ZoneHandle(Z)); |
| - } else if (CurrentToken() == Token::kLPAREN) { |
| + } else if (IsArgumentPart()) { |
| + if (CurrentToken() == Token::kLT) { |
| + // Type arguments. |
| + if (!FLAG_generic_method_syntax) { |
| + ReportError("generic type arguments not supported."); |
| + } |
| + // TODO(hausner): handle type arguments. |
| + ParseTypeArguments(ClassFinalizer::kIgnore); |
| + } |
| if (left->IsPrimaryNode()) { |
| PrimaryNode* primary_node = left->AsPrimaryNode(); |
| const TokenPosition primary_pos = primary_node->token_pos(); |
| @@ -14377,6 +14568,9 @@ void Parser::SkipMapLiteral() { |
| void Parser::SkipActualParameters() { |
| + if (CurrentToken() == Token::kLT) { |
| + SkipTypeArguments(); |
| + } |
| ExpectToken(Token::kLPAREN); |
| while (CurrentToken() != Token::kRPAREN) { |
| if (IsIdentifier() && (LookaheadToken(1) == Token::kCOLON)) { |
| @@ -14544,7 +14738,7 @@ void Parser::SkipSelectors() { |
| ConsumeToken(); |
| SkipNestedExpr(); |
| ExpectToken(Token::kRBRACK); |
| - } else if (current_token == Token::kLPAREN) { |
| + } else if (IsArgumentPart()) { |
| SkipActualParameters(); |
| } else { |
| break; |