| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "fidl/parser.h" |
| |
| #include <errno.h> |
| #include <lib/fit/function.h> |
| |
| #include "fidl/attributes.h" |
| #include "fidl/diagnostics.h" |
| #include "fidl/experimental_flags.h" |
| #include "fidl/types.h" |
| #include "fidl/utils.h" |
| |
| namespace fidl { |
| |
| // The "case" keyword is not folded into CASE_TOKEN and CASE_IDENTIFIER because |
| // doing so confuses clang-format. |
| #define CASE_TOKEN(K) Token::KindAndSubkind(K, Token::Subkind::kNone).combined() |
| |
| #define CASE_IDENTIFIER(K) Token::KindAndSubkind(Token::Kind::kIdentifier, K).combined() |
| |
| #define TOKEN_TYPE_CASES \ |
| case CASE_IDENTIFIER(Token::Subkind::kNone): \ |
| case CASE_IDENTIFIER(Token::Subkind::kArray): \ |
| case CASE_IDENTIFIER(Token::Subkind::kVector): \ |
| case CASE_IDENTIFIER(Token::Subkind::kString): \ |
| case CASE_IDENTIFIER(Token::Subkind::kRequest) |
| |
| #define TOKEN_ATTR_CASES \ |
| case Token::Kind::kDocComment: \ |
| case Token::Kind::kLeftSquare |
| |
| #define TOKEN_LITERAL_CASES \ |
| case CASE_IDENTIFIER(Token::Subkind::kTrue): \ |
| case CASE_IDENTIFIER(Token::Subkind::kFalse): \ |
| case CASE_TOKEN(Token::Kind::kNumericLiteral): \ |
| case CASE_TOKEN(Token::Kind::kStringLiteral) |
| |
| namespace { |
| |
| enum { |
| More, |
| Done, |
| }; |
| |
| template <typename T, typename Fn> |
| void add(std::vector<std::unique_ptr<T>>* elements, Fn producer_fn) { |
| fit::function<std::unique_ptr<T>()> producer(producer_fn); |
| auto element = producer(); |
| if (element) |
| elements->emplace_back(std::move(element)); |
| } |
| |
| } // namespace |
| |
| Parser::Parser(Lexer* lexer, Reporter* reporter, ExperimentalFlags experimental_flags) |
| : lexer_(lexer), |
| reporter_(reporter), |
| experimental_flags_(experimental_flags), |
| state_(State::kNormal) { |
| last_token_ = Lex(); |
| } |
| |
| std::nullptr_t Parser::Fail() { return Fail(ErrUnexpectedToken); } |
| |
| std::nullptr_t Parser::Fail(std::unique_ptr<Diagnostic> err) { |
| assert(err && "should not report nullptr error"); |
| if (Ok()) { |
| err->span = last_token_.span(); |
| reporter_->Report(std::move(err)); |
| } |
| return nullptr; |
| } |
| |
| template <typename... Args> |
| std::nullptr_t Parser::Fail(const ErrorDef<Args...>& err, const Args&... args) { |
| return Fail(err, last_token_, args...); |
| } |
| |
| template <typename... Args> |
| std::nullptr_t Parser::Fail(const ErrorDef<Args...>& err, Token token, const Args&... args) { |
| if (Ok()) { |
| reporter_->Report(err, token, args...); |
| } |
| return nullptr; |
| } |
| |
| template <typename... Args> |
| std::nullptr_t Parser::Fail(const ErrorDef<Args...>& err, const std::optional<SourceSpan>& span, |
| const Args&... args) { |
| if (Ok()) { |
| reporter_->Report(err, span, args...); |
| } |
| return nullptr; |
| } |
| |
| Parser::Modifiers Parser::ParseModifiers() { |
| Modifiers modifiers; |
| Token token; |
| |
| // Consume tokens until we get one that isn't a modifier, treating duplicates |
| // and conflicts as immediately recovered errors. For conflicts (e.g. "strict |
| // flexible" or "flexible strict"), we use the earliest one. |
| for (;;) { |
| switch (Peek().combined()) { |
| case CASE_IDENTIFIER(Token::Subkind::kStrict): |
| case CASE_IDENTIFIER(Token::Subkind::kFlexible): |
| token = ConsumeToken(OfKind(Token::Kind::kIdentifier)).value(); |
| if (modifiers.strictness) { |
| if (token.subkind() == modifiers.strictness_token->subkind()) { |
| Fail(ErrDuplicateModifier, token, token.kind_and_subkind()); |
| RecoverOneError(); |
| } else { |
| Fail(ErrConflictingModifier, token, token.kind_and_subkind(), |
| modifiers.strictness_token->kind_and_subkind()); |
| RecoverOneError(); |
| } |
| } else { |
| const auto value = token.subkind() == Token::Subkind::kStrict |
| ? types::Strictness::kStrict |
| : types::Strictness::kFlexible; |
| modifiers.strictness = value; |
| modifiers.strictness_token = token; |
| } |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kResource): |
| token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kResource)).value(); |
| if (modifiers.resourceness) { |
| Fail(ErrDuplicateModifier, token, token.kind_and_subkind()); |
| RecoverOneError(); |
| } else { |
| modifiers.resourceness = types::Resourceness::kResource; |
| modifiers.resourceness_token = token; |
| } |
| break; |
| default: |
| return modifiers; |
| } |
| } |
| } |
| |
| std::unique_ptr<raw::Identifier> Parser::ParseIdentifier(bool is_discarded) { |
| ASTScope scope(this, is_discarded); |
| std::optional<Token> token = ConsumeToken(OfKind(Token::Kind::kIdentifier)); |
| if (!Ok() || !token) |
| return Fail(); |
| std::string identifier(token->data()); |
| if (!utils::IsValidIdentifierComponent(identifier)) |
| return Fail(ErrInvalidIdentifier, identifier); |
| |
| return std::make_unique<raw::Identifier>(scope.GetSourceElement()); |
| } |
| |
| std::unique_ptr<raw::CompoundIdentifier> Parser::ParseCompoundIdentifier() { |
| ASTScope scope(this); |
| std::vector<std::unique_ptr<raw::Identifier>> components; |
| |
| components.emplace_back(ParseIdentifier()); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_component = [&components, this]() { |
| switch (Peek().combined()) { |
| default: |
| return Done; |
| |
| case CASE_TOKEN(Token::Kind::kDot): |
| ConsumeToken(OfKind(Token::Kind::kDot)); |
| if (Ok()) { |
| components.emplace_back(ParseIdentifier()); |
| } |
| return More; |
| } |
| }; |
| |
| while (parse_component() == More) { |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::CompoundIdentifier>(scope.GetSourceElement(), std::move(components)); |
| } |
| |
| std::unique_ptr<raw::CompoundIdentifier> Parser::ParseLibraryName() { |
| auto library_name = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| for (const auto& component : library_name->components) { |
| std::string component_data(component->start_.data()); |
| if (!utils::IsValidLibraryComponent(component_data)) { |
| return Fail(ErrInvalidLibraryNameComponent, component->start_, component_data); |
| } |
| } |
| |
| return library_name; |
| } |
| |
| std::unique_ptr<raw::StringLiteral> Parser::ParseStringLiteral() { |
| ASTScope scope(this); |
| ConsumeToken(OfKind(Token::Kind::kStringLiteral)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::StringLiteral>(scope.GetSourceElement()); |
| } |
| |
| std::unique_ptr<raw::NumericLiteral> Parser::ParseNumericLiteral() { |
| ASTScope scope(this); |
| ConsumeToken(OfKind(Token::Kind::kNumericLiteral)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::NumericLiteral>(scope.GetSourceElement()); |
| } |
| |
| std::unique_ptr<raw::Ordinal64> Parser::ParseOrdinal64() { |
| ASTScope scope(this); |
| |
| if (!MaybeConsumeToken(OfKind(Token::Kind::kNumericLiteral))) |
| return Fail(ErrMissingOrdinalBeforeType); |
| if (!Ok()) |
| return Fail(); |
| auto data = scope.GetSourceElement().span().data(); |
| std::string string_data(data.data(), data.data() + data.size()); |
| errno = 0; |
| unsigned long long value = strtoull(string_data.data(), nullptr, 0); |
| assert(errno == 0 && "unparsable number should not be lexed."); |
| if (value > std::numeric_limits<uint32_t>::max()) |
| return Fail(ErrOrdinalOutOfBound); |
| uint32_t ordinal = static_cast<uint32_t>(value); |
| if (ordinal == 0u) |
| return Fail(ErrOrdinalsMustStartAtOne); |
| |
| ConsumeToken(OfKind(Token::Kind::kColon)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::Ordinal64>(scope.GetSourceElement(), ordinal); |
| } |
| |
| std::unique_ptr<raw::TrueLiteral> Parser::ParseTrueLiteral() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kTrue)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::TrueLiteral>(scope.GetSourceElement()); |
| } |
| |
| std::unique_ptr<raw::FalseLiteral> Parser::ParseFalseLiteral() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kFalse)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::FalseLiteral>(scope.GetSourceElement()); |
| } |
| |
| std::unique_ptr<raw::Literal> Parser::ParseLiteral() { |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kStringLiteral): |
| return ParseStringLiteral(); |
| |
| case CASE_TOKEN(Token::Kind::kNumericLiteral): |
| return ParseNumericLiteral(); |
| |
| case CASE_IDENTIFIER(Token::Subkind::kTrue): |
| return ParseTrueLiteral(); |
| |
| case CASE_IDENTIFIER(Token::Subkind::kFalse): |
| return ParseFalseLiteral(); |
| |
| default: |
| return Fail(); |
| } |
| } |
| |
| std::unique_ptr<raw::Attribute> Parser::ParseAttribute() { |
| ASTScope scope(this); |
| auto name = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| std::unique_ptr<raw::StringLiteral> value; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kEqual))) { |
| value = ParseStringLiteral(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| std::string str_name(""); |
| std::string str_value(""); |
| if (name) |
| str_name = std::string(name->span().data().data(), name->span().data().size()); |
| if (value) { |
| auto data = value->span().data(); |
| if (data.size() >= 2 && data[0] == '"' && data[data.size() - 1] == '"') { |
| str_value = std::string(value->span().data().data() + 1, value->span().data().size() - 2); |
| } |
| } |
| return std::make_unique<raw::Attribute>(scope.GetSourceElement(), str_name, str_value); |
| } |
| |
| std::unique_ptr<raw::AttributeList> Parser::ParseAttributeList( |
| std::unique_ptr<raw::Attribute> doc_comment, ASTScope& scope) { |
| AttributesBuilder attributes_builder(reporter_); |
| if (doc_comment) { |
| if (!attributes_builder.Insert(std::move(*doc_comment.get()))) |
| return Fail(); |
| } |
| ConsumeToken(OfKind(Token::Kind::kLeftSquare)); |
| if (!Ok()) |
| return Fail(); |
| for (;;) { |
| auto attribute = ParseAttribute(); |
| if (!Ok()) |
| return Fail(); |
| if (!attributes_builder.Insert(std::move(*attribute.get()))) |
| return Fail(); |
| if (!MaybeConsumeToken(OfKind(Token::Kind::kComma))) |
| break; |
| } |
| ConsumeToken(OfKind(Token::Kind::kRightSquare)); |
| if (!Ok()) |
| return Fail(); |
| auto attribute_list = |
| std::make_unique<raw::AttributeList>(scope.GetSourceElement(), attributes_builder.Done()); |
| return attribute_list; |
| } |
| |
| std::unique_ptr<raw::Attribute> Parser::ParseDocComment() { |
| ASTScope scope(this); |
| std::string str_value(""); |
| |
| std::optional<Token> doc_line; |
| bool is_first_doc_comment = true; |
| while (Peek().kind() == Token::Kind::kDocComment) { |
| if (is_first_doc_comment) { |
| is_first_doc_comment = false; |
| } else { |
| // disallow any blank lines between this doc comment and the previous one |
| std::string_view trailing_whitespace = last_token_.previous_end().data(); |
| if (std::count(trailing_whitespace.cbegin(), trailing_whitespace.cend(), '\n') > 1) |
| reporter_->Report(WarnBlankLinesWithinDocCommentBlock, previous_token_); |
| } |
| |
| doc_line = ConsumeToken(OfKind(Token::Kind::kDocComment)); |
| if (!Ok() || !doc_line) |
| return Fail(); |
| // NOTE: we currently explicitly only support UNIX line endings |
| str_value += |
| std::string(doc_line->span().data().data() + 3, doc_line->span().data().size() - 2); |
| } |
| |
| if (Peek().kind() == Token::Kind::kEndOfFile) |
| reporter_->Report(WarnDocCommentMustBeFollowedByDeclaration, previous_token_); |
| |
| return std::make_unique<raw::Attribute>(scope.GetSourceElement(), "Doc", str_value); |
| } |
| |
| std::unique_ptr<raw::AttributeList> Parser::MaybeParseAttributeList(bool for_parameter) { |
| ASTScope scope(this); |
| std::unique_ptr<raw::Attribute> doc_comment; |
| // Doc comments must appear above attributes |
| if (Peek().kind() == Token::Kind::kDocComment) { |
| doc_comment = ParseDocComment(); |
| } |
| if (for_parameter && doc_comment) { |
| reporter_->Report(ErrDocCommentOnParameters, previous_token_); |
| return Fail(); |
| } |
| if (Peek().kind() == Token::Kind::kLeftSquare) { |
| return ParseAttributeList(std::move(doc_comment), scope); |
| } |
| // no generic attributes, start the attribute list |
| if (doc_comment) { |
| AttributesBuilder attributes_builder(reporter_); |
| if (!attributes_builder.Insert(std::move(*doc_comment.get()))) |
| return Fail(); |
| return std::make_unique<raw::AttributeList>(scope.GetSourceElement(), |
| attributes_builder.Done()); |
| } |
| return nullptr; |
| } |
| |
| std::unique_ptr<raw::Constant> Parser::ParseConstant() { |
| std::unique_ptr<raw::Constant> constant; |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kIdentifier): { |
| auto identifier = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| constant = std::make_unique<raw::IdentifierConstant>(std::move(identifier)); |
| break; |
| } |
| |
| TOKEN_LITERAL_CASES : { |
| auto literal = ParseLiteral(); |
| if (!Ok()) |
| return Fail(); |
| constant = std::make_unique<raw::LiteralConstant>(std::move(literal)); |
| break; |
| } |
| |
| case CASE_TOKEN(Token::Kind::kLeftParen): { |
| if (!experimental_flags_.IsFlagEnabled(ExperimentalFlags::Flag::kEnableHandleRights)) |
| return Fail(); |
| |
| ASTScope scope(this); |
| ConsumeToken(OfKind(Token::Kind::kLeftParen)); |
| constant = ParseConstant(); |
| ConsumeToken(OfKind(Token::Kind::kRightParen)); |
| if (!Ok()) |
| return Fail(); |
| constant->update_span(scope.GetSourceElement()); |
| break; |
| } |
| |
| default: |
| return Fail(); |
| } |
| |
| if (Peek().combined() == Token::Kind::kPipe) { |
| ConsumeToken(OfKind(Token::Kind::kPipe)); |
| std::unique_ptr right_operand = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| return std::make_unique<raw::BinaryOperatorConstant>( |
| std::move(constant), std::move(right_operand), raw::BinaryOperatorConstant::Operator::kOr); |
| } |
| return constant; |
| } |
| |
| std::unique_ptr<raw::AliasDeclaration> Parser::ParseAliasDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kAlias)); |
| if (!Ok()) |
| return Fail(); |
| |
| ValidateModifiers</* none */>(modifiers, decl_token.value()); |
| |
| auto alias = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| ConsumeToken(OfKind(Token::Kind::kEqual)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::AliasDeclaration>(scope.GetSourceElement(), std::move(attributes), |
| std::move(alias), std::move(type_ctor)); |
| } |
| |
| std::unique_ptr<raw::Using> Parser::ParseUsing(std::unique_ptr<raw::AttributeList> attributes, |
| ASTScope& scope, const Modifiers& modifiers) { |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kUsing)); |
| if (!Ok()) |
| return Fail(); |
| auto decl_start_token = decl_token.value(); |
| |
| ValidateModifiers</* none */>(modifiers, decl_start_token); |
| |
| auto using_path = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::Identifier> maybe_alias; |
| std::unique_ptr<raw::TypeConstructorOld> maybe_type_ctor; |
| |
| if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kAs))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_alias = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| } else if (MaybeConsumeToken(OfKind(Token::Kind::kEqual))) { |
| if (syntax_ == utils::Syntax::kNew || |
| experimental_flags_.IsFlagEnabled(ExperimentalFlags::Flag::kDisallowOldUsingSyntax)) |
| return Fail(ErrOldUsingSyntaxDeprecated, using_path->span()); |
| if (!Ok() || using_path->components.size() != 1u) |
| return Fail(ErrCompoundAliasIdentifier, using_path->span()); |
| maybe_type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::Using>( |
| scope.GetSourceElement(), std::make_unique<Token>(decl_start_token), std::move(attributes), |
| std::move(using_path), std::move(maybe_alias), std::move(maybe_type_ctor)); |
| } |
| |
| std::unique_ptr<raw::TypeConstructorOld> Parser::ParseTypeConstructorOld() { |
| ASTScope scope(this); |
| auto identifier = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::TypeConstructorOld> maybe_arg_type_ctor; |
| std::unique_ptr<raw::Constant> handle_rights; |
| std::unique_ptr<raw::Constant> maybe_size; |
| std::unique_ptr<raw::Identifier> handle_subtype_identifier; |
| auto nullability = types::Nullability::kNonnullable; |
| |
| if (MaybeConsumeToken(OfKind(Token::Kind::kLeftAngle))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_arg_type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kRightAngle)); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| if (!Ok()) |
| return Fail(); |
| // TODO(fxbug.dev/64629): To properly generalize handle, while supporting |
| // all the features which currently exist, we will need to parse a much more |
| // liberal grammar at this stage (a 'type constructor'), and defer the |
| // interpretation of this data to the compilation step. |
| if (identifier->components.back()->span().data() == "handle") { |
| if (MaybeConsumeToken(OfKind(Token::Kind::kLeftAngle))) { |
| handle_subtype_identifier = ParseIdentifier(); |
| if (experimental_flags_.IsFlagEnabled(ExperimentalFlags::Flag::kEnableHandleRights)) { |
| if (MaybeConsumeToken(OfKind(Token::Kind::kComma))) { |
| handle_rights = ParseConstant(); |
| } |
| } |
| ConsumeToken(OfKind(Token::Kind::kRightAngle)); |
| if (!Ok()) |
| return Fail(); |
| } else { |
| handle_subtype_identifier = ParseIdentifier(); |
| } |
| } else { |
| maybe_size = ParseConstant(); |
| } |
| if (!Ok()) |
| return Fail(); |
| } |
| if (MaybeConsumeToken(OfKind(Token::Kind::kQuestion))) { |
| if (!Ok()) |
| return Fail(); |
| nullability = types::Nullability::kNullable; |
| } |
| |
| return std::make_unique<raw::TypeConstructorOld>( |
| scope.GetSourceElement(), std::move(identifier), std::move(maybe_arg_type_ctor), |
| std::move(handle_subtype_identifier), std::move(handle_rights), std::move(maybe_size), |
| nullability); |
| } |
| |
| std::unique_ptr<raw::BitsMember> Parser::ParseBitsMember() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| ConsumeToken(OfKind(Token::Kind::kEqual)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto member_value = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::BitsMember>(scope.GetSourceElement(), std::move(identifier), |
| std::move(member_value), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::BitsDeclaration> Parser::ParseBitsDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::BitsMember>> members; |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kBits)); |
| if (!Ok()) |
| return Fail(); |
| auto decl_start_token = decl_token.value(); |
| |
| ValidateModifiers<types::Strictness>(modifiers, decl_start_token); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::TypeConstructorOld> maybe_type_ctor; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&members, this]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } else { |
| add(&members, [&] { return ParseBitsMember(); }); |
| return More; |
| } |
| }; |
| |
| auto checkpoint = reporter_->Checkpoint(); |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| if (!checkpoint.NoNewErrors()) |
| return nullptr; |
| |
| if (members.empty()) |
| return Fail(ErrMustHaveOneMember); |
| |
| if (modifiers.strictness != std::nullopt) { |
| decl_start_token = modifiers.strictness_token.value(); |
| } |
| |
| return std::make_unique<raw::BitsDeclaration>( |
| scope.GetSourceElement(), std::make_unique<Token>(decl_start_token), std::move(attributes), |
| std::move(identifier), std::move(maybe_type_ctor), std::move(members), |
| modifiers.strictness.value_or(types::Strictness::kStrict)); |
| } |
| |
| std::unique_ptr<raw::ConstDeclaration> Parser::ParseConstDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kConst)); |
| if (!Ok()) |
| return Fail(); |
| |
| ValidateModifiers</* none */>(modifiers, decl_token.value()); |
| |
| // TODO(fxbug.dev/70247): remove branching |
| raw::TypeConstructor type_ctor; |
| std::unique_ptr<raw::Identifier> identifier; |
| if (syntax_ == utils::Syntax::kNew) { |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| } else { |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| ConsumeToken(OfKind(Token::Kind::kEqual)); |
| if (!Ok()) |
| return Fail(); |
| auto constant = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::ConstDeclaration>(scope.GetSourceElement(), std::move(attributes), |
| std::move(type_ctor), std::move(identifier), |
| std::move(constant)); |
| } |
| |
| std::unique_ptr<raw::EnumMember> Parser::ParseEnumMember() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| ConsumeToken(OfKind(Token::Kind::kEqual)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto member_value = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::EnumMember>(scope.GetSourceElement(), std::move(identifier), |
| std::move(member_value), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::EnumDeclaration> Parser::ParseEnumDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::EnumMember>> members; |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kEnum)); |
| if (!Ok()) |
| return Fail(); |
| auto decl_start_token = decl_token.value(); |
| |
| ValidateModifiers<types::Strictness>(modifiers, decl_start_token); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::TypeConstructorOld> maybe_type_ctor; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&members, this]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } else { |
| add(&members, [&] { return ParseEnumMember(); }); |
| return More; |
| } |
| }; |
| |
| auto checkpoint = reporter_->Checkpoint(); |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| if (!checkpoint.NoNewErrors()) |
| return nullptr; |
| |
| if (members.empty()) |
| return Fail(ErrMustHaveOneMember); |
| |
| if (modifiers.strictness != std::nullopt) { |
| decl_start_token = modifiers.strictness_token.value(); |
| } |
| |
| return std::make_unique<raw::EnumDeclaration>( |
| scope.GetSourceElement(), std::make_unique<Token>(decl_start_token), std::move(attributes), |
| std::move(identifier), std::move(maybe_type_ctor), std::move(members), |
| modifiers.strictness.value_or(types::Strictness::kStrict)); |
| } |
| |
| std::unique_ptr<raw::Parameter> Parser::ParseParameter() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(/*for_parameter=*/true); |
| if (!Ok()) |
| return Fail(); |
| |
| // TODO(fxbug.dev/70247): remove branching |
| raw::TypeConstructor type_ctor; |
| std::unique_ptr<raw::Identifier> identifier; |
| if (syntax_ == utils::Syntax::kNew) { |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| } else { |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::Parameter>(scope.GetSourceElement(), std::move(type_ctor), |
| std::move(identifier), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::ParameterList> Parser::ParseParameterList() { |
| ASTScope scope(this); |
| std::vector<std::unique_ptr<raw::Parameter>> parameter_list; |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftParen)); |
| if (!Ok()) |
| return Fail(); |
| |
| if (Peek().kind() != Token::Kind::kRightParen) { |
| auto parameter = ParseParameter(); |
| parameter_list.emplace_back(std::move(parameter)); |
| if (!Ok()) { |
| const auto result = RecoverToEndOfParam(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } |
| } |
| while (Peek().kind() == Token::Kind::kComma) { |
| ConsumeToken(OfKind(Token::Kind::kComma)); |
| if (!Ok()) |
| return Fail(); |
| parameter_list.emplace_back(ParseParameter()); |
| if (!Ok()) { |
| const auto result = RecoverToEndOfParam(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } |
| } |
| } |
| } |
| |
| ConsumeToken(OfKind(Token::Kind::kRightParen)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::ParameterList>(scope.GetSourceElement(), std::move(parameter_list)); |
| } |
| |
| std::unique_ptr<raw::ProtocolMethod> Parser::ParseProtocolEvent( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope) { |
| ConsumeToken(OfKind(Token::Kind::kArrow)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto method_name = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_params = [this](std::unique_ptr<raw::ParameterList>* params_out) { |
| if (!Ok()) |
| return false; |
| *params_out = ParseParameterList(); |
| if (!Ok()) |
| return false; |
| |
| return true; |
| }; |
| |
| std::unique_ptr<raw::ParameterList> response; |
| if (!parse_params(&response)) |
| return Fail(); |
| |
| std::unique_ptr<raw::TypeConstructorOld> maybe_error; |
| if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kError))) { |
| maybe_error = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| assert(method_name); |
| assert(response); |
| |
| return std::make_unique<raw::ProtocolMethod>(scope.GetSourceElement(), std::move(attributes), |
| std::move(method_name), nullptr /* maybe_request */, |
| std::move(response), std::move(maybe_error)); |
| } |
| |
| std::unique_ptr<raw::ProtocolMethod> Parser::ParseProtocolMethod( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, |
| std::unique_ptr<raw::Identifier> method_name) { |
| auto parse_params = [this](std::unique_ptr<raw::ParameterList>* params_out) { |
| *params_out = ParseParameterList(); |
| if (!Ok()) |
| return false; |
| return true; |
| }; |
| |
| std::unique_ptr<raw::ParameterList> request; |
| if (!parse_params(&request)) |
| return Fail(); |
| |
| std::unique_ptr<raw::ParameterList> maybe_response; |
| std::unique_ptr<raw::TypeConstructorOld> maybe_error; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kArrow))) { |
| if (!Ok()) |
| return Fail(); |
| if (!parse_params(&maybe_response)) |
| return Fail(); |
| if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kError))) { |
| maybe_error = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| } |
| } |
| |
| assert(method_name); |
| assert(request); |
| |
| return std::make_unique<raw::ProtocolMethod>(scope.GetSourceElement(), std::move(attributes), |
| std::move(method_name), std::move(request), |
| std::move(maybe_response), std::move(maybe_error)); |
| } |
| |
| void Parser::ParseProtocolMember( |
| std::vector<std::unique_ptr<raw::ComposeProtocol>>* composed_protocols, |
| std::vector<std::unique_ptr<raw::ProtocolMethod>>* methods) { |
| ASTScope scope(this); |
| std::unique_ptr<raw::AttributeList> attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| Fail(); |
| |
| switch (Peek().kind()) { |
| case Token::Kind::kArrow: { |
| add(methods, [&] { return ParseProtocolEvent(std::move(attributes), scope); }); |
| break; |
| } |
| case Token::Kind::kIdentifier: { |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| break; |
| if (Peek().kind() == Token::Kind::kLeftParen) { |
| add(methods, [&] { |
| return ParseProtocolMethod(std::move(attributes), scope, std::move(identifier)); |
| }); |
| break; |
| } else if (identifier->span().data() == "compose") { |
| if (attributes) { |
| Fail(ErrCannotAttachAttributesToCompose); |
| break; |
| } |
| auto protocol_name = ParseCompoundIdentifier(); |
| if (!Ok()) |
| break; |
| composed_protocols->push_back(std::make_unique<raw::ComposeProtocol>( |
| raw::SourceElement(identifier->start_, protocol_name->end_), std::move(protocol_name))); |
| break; |
| } else { |
| Fail(ErrUnrecognizedProtocolMember); |
| break; |
| } |
| } |
| default: |
| Fail(ErrExpectedProtocolMember); |
| break; |
| } |
| } |
| |
| std::unique_ptr<raw::ProtocolDeclaration> Parser::ParseProtocolDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::ComposeProtocol>> composed_protocols; |
| std::vector<std::unique_ptr<raw::ProtocolMethod>> methods; |
| |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kProtocol)); |
| if (!Ok()) |
| return Fail(); |
| |
| ValidateModifiers</* none */>(modifiers, decl_token.value()); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&composed_protocols, &methods, this]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } else { |
| ParseProtocolMember(&composed_protocols, &methods); |
| return More; |
| } |
| }; |
| |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| return std::make_unique<raw::ProtocolDeclaration>( |
| scope.GetSourceElement(), std::move(attributes), std::move(identifier), |
| std::move(composed_protocols), std::move(methods)); |
| } |
| |
| std::unique_ptr<raw::ResourceProperty> Parser::ParseResourcePropertyDeclaration() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| |
| // TODO(fxbug.dev/70247): remove branching |
| raw::TypeConstructor type_ctor; |
| std::unique_ptr<raw::Identifier> identifier; |
| if (syntax_ == utils::Syntax::kNew) { |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| } else { |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::ResourceProperty>(scope.GetSourceElement(), std::move(type_ctor), |
| std::move(identifier), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::ResourceDeclaration> Parser::ParseResourceDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::ResourceProperty>> properties; |
| |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kResourceDefinition)); |
| if (!Ok()) |
| return Fail(); |
| |
| ValidateModifiers</* none */>(modifiers, decl_token.value()); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| raw::TypeConstructor maybe_type_ctor; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| // TODO(fxbug.dev/70247): remove branching |
| if (syntax_ == utils::Syntax::kNew) { |
| auto resource_type_identifier = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| maybe_type_ctor = std::make_unique<raw::TypeConstructorNew>( |
| scope.GetSourceElement(), |
| std::make_unique<raw::NamedLayoutReference>(scope.GetSourceElement(), |
| std::move(resource_type_identifier)), |
| /*parameters=*/nullptr, |
| /*constraints=*/nullptr); |
| } else { |
| if (!Ok()) { |
| return Fail(); |
| } |
| maybe_type_ctor = ParseTypeConstructor(); |
| } |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| // Just the scaffolding of the resource here, only properties is currently accepted. |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kProperties)); |
| if (!Ok()) |
| return Fail(); |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_prop = [&properties, this]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } else { |
| add(&properties, [&] { return ParseResourcePropertyDeclaration(); }); |
| return More; |
| } |
| }; |
| |
| auto checkpoint = reporter_->Checkpoint(); |
| while (parse_prop() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| if (!checkpoint.NoNewErrors()) |
| return nullptr; |
| |
| if (properties.empty()) |
| return Fail(ErrMustHaveOneProperty); |
| |
| // End of properties block. |
| ConsumeToken(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| |
| // End of resource. |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::ResourceDeclaration>( |
| scope.GetSourceElement(), std::move(attributes), std::move(identifier), |
| std::move(maybe_type_ctor), std::move(properties)); |
| } |
| |
| std::unique_ptr<raw::ServiceMember> Parser::ParseServiceMember() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| |
| // TODO(fxbug.dev/70247): remove branching |
| raw::TypeConstructor type_ctor; |
| std::unique_ptr<raw::Identifier> identifier; |
| if (syntax_ == utils::Syntax::kNew) { |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| } else { |
| type_ctor = ParseTypeConstructor(); |
| if (!Ok()) |
| return Fail(); |
| identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::ServiceMember>(scope.GetSourceElement(), std::move(type_ctor), |
| std::move(identifier), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::ServiceDeclaration> Parser::ParseServiceDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::ServiceMember>> members; |
| |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kService)); |
| if (!Ok()) |
| return Fail(); |
| |
| ValidateModifiers</* none */>(modifiers, decl_token.value()); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } else { |
| add(&members, [&] { return ParseServiceMember(); }); |
| return More; |
| } |
| }; |
| |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| return std::make_unique<raw::ServiceDeclaration>(scope.GetSourceElement(), std::move(attributes), |
| std::move(identifier), std::move(members)); |
| } |
| |
| std::unique_ptr<raw::StructMember> Parser::ParseStructMember() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| auto type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::Constant> maybe_default_value; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kEqual))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_default_value = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::StructMember>(scope.GetSourceElement(), std::move(type_ctor), |
| std::move(identifier), std::move(maybe_default_value), |
| std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::StructDeclaration> Parser::ParseStructDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::StructMember>> members; |
| |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kStruct)); |
| if (!Ok()) |
| return Fail(); |
| auto decl_start_token = decl_token.value(); |
| |
| ValidateModifiers<types::Resourceness>(modifiers, decl_start_token); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&members, this]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } else { |
| add(&members, [&] { return ParseStructMember(); }); |
| return More; |
| } |
| }; |
| |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| return Fail(); |
| |
| const auto resourceness = modifiers.resourceness.value_or(types::Resourceness::kValue); |
| if (resourceness == types::Resourceness::kResource) { |
| decl_start_token = modifiers.resourceness_token.value(); |
| } |
| |
| return std::make_unique<raw::StructDeclaration>( |
| scope.GetSourceElement(), std::make_unique<Token>(decl_start_token), std::move(attributes), |
| std::move(identifier), std::move(members), resourceness); |
| } |
| |
| std::unique_ptr<raw::TableMember> Parser::ParseTableMember() { |
| ASTScope scope(this); |
| std::unique_ptr<raw::AttributeList> attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| |
| auto ordinal = ParseOrdinal64(); |
| if (!Ok()) |
| return Fail(); |
| |
| if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kReserved))) { |
| if (!Ok()) |
| return Fail(); |
| if (attributes != nullptr) |
| return Fail(ErrCannotAttachAttributesToReservedOrdinals); |
| return std::make_unique<raw::TableMember>(scope.GetSourceElement(), std::move(ordinal)); |
| } |
| |
| auto type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::Constant> maybe_default_value; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kEqual))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_default_value = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::TableMember>(scope.GetSourceElement(), std::move(ordinal), |
| std::move(type_ctor), std::move(identifier), |
| std::move(maybe_default_value), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::TableDeclaration> Parser::ParseTableDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::TableMember>> members; |
| |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kTable)); |
| if (!Ok()) |
| return Fail(); |
| auto decl_start_token = decl_token.value(); |
| |
| ValidateModifiers<types::Resourceness>(modifiers, decl_start_token); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&members, this]() { |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kRightCurly): |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| |
| case CASE_TOKEN(Token::Kind::kNumericLiteral): |
| TOKEN_ATTR_CASES : { |
| add(&members, [&] { return ParseTableMember(); }); |
| return More; |
| } |
| |
| default: |
| Fail(ErrExpectedOrdinalOrCloseBrace, Peek()); |
| return Done; |
| } |
| }; |
| |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| const auto resourceness = modifiers.resourceness.value_or(types::Resourceness::kValue); |
| if (resourceness == types::Resourceness::kResource) { |
| decl_start_token = modifiers.resourceness_token.value(); |
| } |
| |
| return std::make_unique<raw::TableDeclaration>( |
| scope.GetSourceElement(), std::make_unique<Token>(decl_start_token), std::move(attributes), |
| std::move(identifier), std::move(members), types::Strictness::kFlexible, resourceness); |
| } |
| |
| std::unique_ptr<raw::UnionMember> Parser::ParseUnionMember() { |
| ASTScope scope(this); |
| |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| auto ordinal = ParseOrdinal64(); |
| if (!Ok()) |
| return Fail(); |
| |
| if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kReserved))) { |
| if (!Ok()) |
| return Fail(); |
| if (attributes) |
| return Fail(ErrCannotAttachAttributesToReservedOrdinals); |
| return std::make_unique<raw::UnionMember>(scope.GetSourceElement(), std::move(ordinal)); |
| } |
| |
| auto type_ctor = ParseTypeConstructorOld(); |
| if (!Ok()) |
| return Fail(); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::Constant> maybe_default_value; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kEqual))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_default_value = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::UnionMember>(scope.GetSourceElement(), std::move(ordinal), |
| std::move(type_ctor), std::move(identifier), |
| std::move(maybe_default_value), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::UnionDeclaration> Parser::ParseUnionDeclaration( |
| std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope, const Modifiers& modifiers) { |
| std::vector<std::unique_ptr<raw::UnionMember>> members; |
| |
| const auto decl_token = ConsumeToken(IdentifierOfSubkind(Token::Subkind::kUnion)); |
| if (!Ok()) |
| return Fail(); |
| auto decl_start_token = decl_token.value(); |
| |
| ValidateModifiers<types::Strictness, types::Resourceness>(modifiers, decl_start_token); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| bool contains_non_reserved_member = false; |
| auto parse_member = [&]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } else { |
| auto member = ParseUnionMember(); |
| if (member) { |
| members.emplace_back(std::move(member)); |
| if (members.back() && members.back()->maybe_used) |
| contains_non_reserved_member = true; |
| } |
| return More; |
| } |
| }; |
| |
| auto checkpoint = reporter_->Checkpoint(); |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| return Fail(); |
| |
| if (!checkpoint.NoNewErrors()) |
| return nullptr; |
| |
| if (!contains_non_reserved_member) |
| return Fail(ErrMustHaveNonReservedMember); |
| |
| const auto resourceness = modifiers.resourceness.value_or(types::Resourceness::kValue); |
| if (resourceness == types::Resourceness::kResource) { |
| decl_start_token = modifiers.resourceness_token.value(); |
| } else if (modifiers.strictness != std::nullopt) { |
| decl_start_token = modifiers.strictness_token.value(); |
| } |
| |
| return std::make_unique<raw::UnionDeclaration>( |
| scope.GetSourceElement(), std::make_unique<Token>(decl_start_token), std::move(attributes), |
| std::move(identifier), std::move(members), |
| modifiers.strictness.value_or(types::Strictness::kStrict), |
| modifiers.strictness != std::nullopt, resourceness); |
| } |
| |
| std::unique_ptr<raw::File> Parser::ParseFile() { |
| ASTScope scope(this); |
| |
| syntax_ = utils::Syntax::kOld; |
| if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kDeprecatedSyntax))) { |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| if (!experimental_flags_.IsFlagEnabled(ExperimentalFlags::Flag::kAllowNewSyntax)) { |
| Fail(ErrRemoveSyntaxVersion); |
| } |
| } else if (experimental_flags_.IsFlagEnabled(ExperimentalFlags::Flag::kAllowNewSyntax)) { |
| syntax_ = utils::Syntax::kNew; |
| } |
| |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kLibrary)); |
| if (!Ok()) |
| return Fail(); |
| auto library_name = ParseLibraryName(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| |
| if (syntax_ == utils::Syntax::kNew) |
| return ParseFileNewSyntax(scope, std::move(attributes), std::move(library_name)); |
| |
| bool done_with_library_imports = false; |
| std::vector<std::unique_ptr<raw::AliasDeclaration>> alias_list; |
| std::vector<std::unique_ptr<raw::Using>> using_list; |
| std::vector<std::unique_ptr<raw::BitsDeclaration>> bits_declaration_list; |
| std::vector<std::unique_ptr<raw::ConstDeclaration>> const_declaration_list; |
| std::vector<std::unique_ptr<raw::EnumDeclaration>> enum_declaration_list; |
| std::vector<std::unique_ptr<raw::ProtocolDeclaration>> protocol_declaration_list; |
| std::vector<std::unique_ptr<raw::ResourceDeclaration>> resource_declaration_list; |
| std::vector<std::unique_ptr<raw::ServiceDeclaration>> service_declaration_list; |
| std::vector<std::unique_ptr<raw::StructDeclaration>> struct_declaration_list; |
| std::vector<std::unique_ptr<raw::TableDeclaration>> table_declaration_list; |
| std::vector<std::unique_ptr<raw::UnionDeclaration>> union_declaration_list; |
| std::vector<std::unique_ptr<raw::TypeDecl>> type_decls; |
| auto parse_declaration = [&alias_list, &bits_declaration_list, &const_declaration_list, |
| &enum_declaration_list, &protocol_declaration_list, |
| &resource_declaration_list, &service_declaration_list, |
| &struct_declaration_list, &done_with_library_imports, &using_list, |
| &table_declaration_list, &union_declaration_list, this]() { |
| ASTScope scope(this); |
| std::unique_ptr<raw::AttributeList> attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return More; |
| |
| const auto modifiers = ParseModifiers(); |
| |
| switch (Peek().combined()) { |
| default: |
| Fail(ErrExpectedDeclaration, last_token_.data()); |
| return More; |
| |
| case CASE_TOKEN(Token::Kind::kEndOfFile): |
| return Done; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kDeprecatedSyntax): { |
| if (experimental_flags_.IsFlagEnabled(ExperimentalFlags::Flag::kAllowNewSyntax)) { |
| Fail(ErrMisplacedSyntaxVersion); |
| } else { |
| Fail(ErrRemoveSyntaxVersion); |
| } |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kAlias): { |
| done_with_library_imports = true; |
| add(&alias_list, |
| [&] { return ParseAliasDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kBits): { |
| done_with_library_imports = true; |
| add(&bits_declaration_list, |
| [&] { return ParseBitsDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kConst): { |
| done_with_library_imports = true; |
| add(&const_declaration_list, |
| [&] { return ParseConstDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kEnum): { |
| done_with_library_imports = true; |
| add(&enum_declaration_list, |
| [&] { return ParseEnumDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kProtocol): { |
| done_with_library_imports = true; |
| add(&protocol_declaration_list, |
| [&] { return ParseProtocolDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kResourceDefinition): { |
| done_with_library_imports = true; |
| add(&resource_declaration_list, |
| [&] { return ParseResourceDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kService): { |
| done_with_library_imports = true; |
| add(&service_declaration_list, |
| [&] { return ParseServiceDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kStruct): { |
| done_with_library_imports = true; |
| add(&struct_declaration_list, |
| [&] { return ParseStructDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kTable): { |
| done_with_library_imports = true; |
| add(&table_declaration_list, |
| [&] { return ParseTableDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kUsing): { |
| auto using_decl = ParseUsing(std::move(attributes), scope, modifiers); |
| if (using_decl == nullptr) { |
| // Failed to parse using declaration. |
| return Done; |
| } |
| if (using_decl->maybe_type_ctor) { |
| done_with_library_imports = true; |
| } else if (done_with_library_imports) { |
| reporter_->Report(ErrLibraryImportsMustBeGroupedAtTopOfFile, using_decl->span()); |
| } |
| using_list.emplace_back(std::move(using_decl)); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kUnion): { |
| done_with_library_imports = true; |
| add(&union_declaration_list, |
| [&] { return ParseUnionDeclaration(std::move(attributes), scope, modifiers); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kXUnion): |
| switch (modifiers.strictness.value_or(types::Strictness::kFlexible)) { |
| case types::Strictness::kFlexible: |
| Fail(ErrXunionDeprecated); |
| return More; |
| case types::Strictness::kStrict: |
| Fail(ErrStrictXunionDeprecated); |
| return More; |
| } |
| } |
| }; |
| |
| while (parse_declaration() == More) { |
| if (!Ok()) { |
| // If this returns RecoverResult::Continue, we have consumed up to a '}' |
| // and expect a ';' to follow. |
| auto result = RecoverToEndOfDecl(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| break; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| |
| std::optional<Token> end = ConsumeToken(OfKind(Token::Kind::kEndOfFile)); |
| if (!Ok() || !end) |
| return Fail(); |
| |
| return std::make_unique<raw::File>( |
| scope.GetSourceElement(), end.value(), std::move(attributes), std::move(library_name), |
| std::move(alias_list), std::move(using_list), std::move(bits_declaration_list), |
| std::move(const_declaration_list), std::move(enum_declaration_list), |
| std::move(protocol_declaration_list), std::move(resource_declaration_list), |
| std::move(service_declaration_list), std::move(struct_declaration_list), |
| std::move(table_declaration_list), std::move(union_declaration_list), std::move(type_decls), |
| std::move(comment_tokens_), fidl::utils::Syntax::kOld); |
| } |
| |
| std::unique_ptr<raw::LayoutParameter> Parser::ParseLayoutParameter() { |
| ASTScope scope(this); |
| |
| switch (Peek().combined()) { |
| TOKEN_LITERAL_CASES : { |
| auto literal = ParseLiteral(); |
| if (!Ok()) |
| return Fail(); |
| auto constant = std::make_unique<raw::LiteralConstant>(std::move(literal)); |
| return std::make_unique<raw::LiteralLayoutParameter>(scope.GetSourceElement(), |
| std::move(constant)); |
| } |
| default: { |
| auto type_ctor = ParseTypeConstructorNew(); |
| if (!Ok()) |
| return Fail(); |
| |
| // For non-anonymous type constructors like "foo<T>" or "foo:optional," the presence of type |
| // parameters and constraints, respectively, confirms that "foo" refers to a type reference. |
| // In cases with no type parameters or constraints present (ie, just "foo"), it is impossible |
| // to deduce whether "foo" refers to a type or a value. In such cases, we must discard the |
| // recently built type constructor, and convert it to a compound identifier instead. |
| if (type_ctor->layout_ref->kind == raw::LayoutReference::Kind::kNamed && |
| type_ctor->parameters == nullptr && type_ctor->constraints == nullptr) { |
| auto named_ref = static_cast<raw::NamedLayoutReference*>(type_ctor->layout_ref.get()); |
| return std::make_unique<raw::AmbiguousLayoutParameter>(scope.GetSourceElement(), |
| std::move(named_ref->identifier)); |
| } |
| return std::make_unique<raw::TypeLayoutParameter>(scope.GetSourceElement(), |
| std::move(type_ctor)); |
| } |
| } |
| } |
| |
| std::unique_ptr<raw::LayoutParameterList> Parser::MaybeParseLayoutParameterList() { |
| if (!MaybeConsumeToken(OfKind(Token::Kind::kLeftAngle))) { |
| return nullptr; |
| } |
| |
| ASTScope scope(this); |
| std::vector<std::unique_ptr<raw::LayoutParameter>> params; |
| for (;;) { |
| params.emplace_back(ParseLayoutParameter()); |
| if (!Ok()) |
| return Fail(); |
| if (!MaybeConsumeToken(OfKind(Token::Kind::kComma))) |
| break; |
| } |
| |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kRightAngle)); |
| return std::make_unique<raw::LayoutParameterList>(scope.GetSourceElement(), std::move(params)); |
| } |
| |
| std::unique_ptr<raw::TypeConstraints> Parser::ParseConstraints() { |
| ASTScope scope(this); |
| bool bracketed = false; |
| std::vector<std::unique_ptr<raw::Constant>> constraints; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kLeftAngle))) { |
| bracketed = true; |
| } |
| |
| for (;;) { |
| constraints.emplace_back(ParseConstant()); |
| if (!Ok()) |
| return Fail(); |
| if (!MaybeConsumeToken(OfKind(Token::Kind::kComma))) |
| break; |
| } |
| |
| if (bracketed) { |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kRightAngle)); |
| if (constraints.size() == 1) { |
| Fail(ErrUnnecessaryConstraintBrackets); |
| } |
| } else if (constraints.size() > 1) { |
| Fail(ErrMissingConstraintBrackets); |
| } |
| return std::make_unique<raw::TypeConstraints>(scope.GetSourceElement(), std::move(constraints)); |
| } |
| |
| std::unique_ptr<raw::LayoutMember> Parser::ParseLayoutMember(raw::LayoutMember::Kind kind) { |
| ASTScope scope(this); |
| |
| // TODO(fxbug.dev/65978): Parse attributes. |
| |
| std::unique_ptr<raw::Ordinal64> ordinal = nullptr; |
| if (kind == raw::LayoutMember::Kind::kOrdinaled) { |
| ordinal = ParseOrdinal64(); |
| if (!Ok()) |
| return Fail(); |
| |
| if (MaybeConsumeToken(IdentifierOfSubkind(Token::Subkind::kReserved))) { |
| if (!Ok()) |
| return Fail(); |
| return std::make_unique<raw::OrdinaledLayoutMember>(scope.GetSourceElement(), |
| std::move(ordinal)); |
| } |
| } |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::TypeConstructorNew> layout = nullptr; |
| if (kind != raw::LayoutMember::Kind::kValue) { |
| layout = ParseTypeConstructorNew(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| // An equal sign followed by a constant (aka, a default value) is optional for |
| // a struct member, but required for a value member. |
| std::unique_ptr<raw::Constant> value = nullptr; |
| if (kind == raw::LayoutMember::Kind::kStruct && MaybeConsumeToken(OfKind(Token::Kind::kEqual))) { |
| if (!Ok()) |
| return Fail(); |
| value = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| } else if (kind == raw::LayoutMember::Kind::kValue) { |
| ConsumeToken(OfKind(Token::Kind::kEqual)); |
| if (!Ok()) |
| return Fail(); |
| |
| value = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| switch (kind) { |
| case raw::LayoutMember::kOrdinaled: { |
| return std::make_unique<raw::OrdinaledLayoutMember>( |
| scope.GetSourceElement(), std::move(ordinal), std::move(identifier), std::move(layout)); |
| } |
| case raw::LayoutMember::kStruct: { |
| return std::make_unique<raw::StructLayoutMember>( |
| scope.GetSourceElement(), std::move(identifier), std::move(layout), std::move(value)); |
| } |
| case raw::LayoutMember::kValue: { |
| return std::make_unique<raw::ValueLayoutMember>(scope.GetSourceElement(), |
| std::move(identifier), std::move(value)); |
| } |
| } |
| } |
| |
| std::unique_ptr<raw::Layout> Parser::ParseLayout( |
| ASTScope& scope, const Modifiers& modifiers, |
| std::unique_ptr<raw::CompoundIdentifier> identifier, |
| std::unique_ptr<raw::TypeConstructorNew> subtype_ctor) { |
| raw::Layout::Kind kind; |
| raw::LayoutMember::Kind member_kind; |
| |
| if (identifier->components.size() != 1) { |
| return Fail(ErrInvalidLayoutClass); |
| } |
| |
| // TODO(fxbug.dev/65978): Once fully transitioned, we will be able to |
| // remove token subkinds for struct, union, table, bits, and enum. Or |
| // maybe we want to have a 'recognize token subkind' on an identifier |
| // instead of doing string comparison directly. |
| if (identifier->components[0]->span().data() == "bits") { |
| ValidateModifiers<types::Strictness>(modifiers, identifier->components[0]->start_); |
| kind = raw::Layout::Kind::kBits; |
| member_kind = raw::LayoutMember::Kind::kValue; |
| } else if (identifier->components[0]->span().data() == "enum") { |
| ValidateModifiers<types::Strictness>(modifiers, identifier->components[0]->start_); |
| kind = raw::Layout::Kind::kEnum; |
| member_kind = raw::LayoutMember::Kind::kValue; |
| } else if (identifier->components[0]->span().data() == "struct") { |
| ValidateModifiers<types::Resourceness>(modifiers, identifier->components[0]->start_); |
| kind = raw::Layout::Kind::kStruct; |
| member_kind = raw::LayoutMember::Kind::kStruct; |
| } else if (identifier->components[0]->span().data() == "table") { |
| ValidateModifiers<types::Resourceness>(modifiers, identifier->components[0]->start_); |
| kind = raw::Layout::Kind::kTable; |
| member_kind = raw::LayoutMember::Kind::kOrdinaled; |
| } else if (identifier->components[0]->span().data() == "union") { |
| ValidateModifiers<types::Strictness, types::Resourceness>(modifiers, |
| identifier->components[0]->start_); |
| kind = raw::Layout::Kind::kUnion; |
| member_kind = raw::LayoutMember::Kind::kOrdinaled; |
| } else { |
| return Fail(ErrInvalidLayoutClass); |
| } |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| std::vector<std::unique_ptr<raw::LayoutMember>> members; |
| auto parse_member = [&]() { |
| if (Peek().kind() == Token::Kind::kRightCurly) { |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| } |
| add(&members, [&] { return ParseLayoutMember(member_kind); }); |
| return More; |
| }; |
| |
| auto checkpoint = reporter_->Checkpoint(); |
| while (parse_member() == More) { |
| if (!Ok()) { |
| const auto result = RecoverToEndOfMember(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } |
| if (result == RecoverResult::EndOfScope) { |
| continue; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| if (!Ok()) |
| return Fail(); |
| |
| if (member_kind == raw::LayoutMember::Kind::kValue && members.empty()) |
| return Fail(ErrMustHaveOneMember); |
| |
| // avoid returning a "must have non reserved member" error if there was en |
| // error while parsing the members |
| if (!checkpoint.NoNewErrors()) |
| return nullptr; |
| |
| if (kind == raw::Layout::Kind::kUnion) { |
| bool contains_non_reserved_member = false; |
| for (const std::unique_ptr<raw::LayoutMember>& member : members) { |
| assert(member->kind == raw::LayoutMember::Kind::kOrdinaled && |
| "unions should only have ordinaled members"); |
| const auto& union_member = static_cast<raw::OrdinaledLayoutMember*>(member.get()); |
| if (!union_member->reserved) |
| contains_non_reserved_member = true; |
| } |
| if (!contains_non_reserved_member) |
| return Fail(ErrMustHaveNonReservedMember); |
| } |
| |
| return std::make_unique<raw::Layout>( |
| scope.GetSourceElement(), kind, std::move(members), modifiers.strictness, |
| modifiers.resourceness.value_or(types::Resourceness::kValue), std::move(subtype_ctor)); |
| } |
| |
| // [ name | { ... } ][ < ... > ][ : ... ] |
| std::unique_ptr<raw::TypeConstructorNew> Parser::ParseTypeConstructorNew() { |
| ASTScope scope(this); |
| const auto modifiers = ParseModifiers(); |
| auto identifier = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::LayoutReference> layout_ref; |
| switch (Peek().kind()) { |
| case Token::Kind::kLeftCurly: { |
| auto layout = ParseLayout(scope, modifiers, std::move(identifier), /*subtype_ctor=*/nullptr); |
| layout_ref = |
| std::make_unique<raw::InlineLayoutReference>(scope.GetSourceElement(), std::move(layout)); |
| break; |
| } |
| case Token::Kind::kColon: { |
| // The colon case is ambiguous. Consider the following two examples: |
| // |
| // type A = enum : foo { BAR = 1; }; |
| // type B = enum : foo; |
| // |
| // When the parser encounters the colon in each case, it has no idea |
| // whether the value immediately after it should be interpreted as the |
| // wrapped type in an inline layout of kind enum, or otherwise as the only |
| // constraint on a named layout called "enum." |
| // |
| // To resolve this confusion, we parse the token after the colon as a |
| // constant, then check to see if the token after that is a left curly |
| // brace. If it is, we assume that this is in fact the inline layout case |
| // ("type A"). If it is not, we assume that it is a named layout with |
| // constraints ("type B"). |
| ASTScope after_colon_scope(this); |
| ConsumeToken(OfKind(Token::Kind::kColon)); |
| if (!Ok()) |
| return Fail(); |
| |
| // If the token after the colon is the opener to a constraints list, we |
| // know for sure that the identifier before the colon must be a |
| // NamedLayoutReference, so none of the other checks in this case are |
| // required. |
| if (Peek().kind() == Token::Kind::kLeftAngle) { |
| layout_ref = std::make_unique<raw::NamedLayoutReference>(scope.GetSourceElement(), |
| std::move(identifier)); |
| break; |
| } |
| |
| std::unique_ptr<raw::Constant> constraint_or_subtype = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| |
| // If the token after the constant is not an open brace, this was actually |
| // a one-entry constraints block the whole time, so it should be parsed as |
| // such. |
| if (Peek().kind() != Token::Kind::kLeftCurly) { |
| layout_ref = std::make_unique<raw::NamedLayoutReference>(scope.GetSourceElement(), |
| std::move(identifier)); |
| std::vector<std::unique_ptr<raw::Constant>> components; |
| components.emplace_back(std::move(constraint_or_subtype)); |
| auto constraints = std::make_unique<raw::TypeConstraints>( |
| after_colon_scope.GetSourceElement(), std::move(components)); |
| return std::make_unique<raw::TypeConstructorNew>( |
| scope.GetSourceElement(), std::move(layout_ref), nullptr, std::move(constraints)); |
| } |
| |
| // The token we just parsed as a constant is in fact a layout subtype. |
| // Coerce it into that class, then build the layout_ref. |
| if (constraint_or_subtype->kind != raw::Constant::Kind::kIdentifier) { |
| return Fail(ErrInvalidWrappedType); |
| } |
| |
| auto subtype_element = |
| raw::SourceElement(constraint_or_subtype->start_, constraint_or_subtype->end_); |
| auto subtype_constant = static_cast<raw::IdentifierConstant*>(constraint_or_subtype.get()); |
| auto subtype_ref = std::make_unique<raw::NamedLayoutReference>( |
| subtype_element, std::move(subtype_constant->identifier)); |
| auto subtype_ctor = std::make_unique<raw::TypeConstructorNew>( |
| subtype_element, std::move(subtype_ref), /*parameters=*/nullptr, /*constraints=*/nullptr); |
| auto layout = ParseLayout(scope, modifiers, std::move(identifier), std::move(subtype_ctor)); |
| layout_ref = |
| std::make_unique<raw::InlineLayoutReference>(scope.GetSourceElement(), std::move(layout)); |
| break; |
| } |
| default: { |
| ValidateModifiers</* none */>(modifiers, identifier->start_); |
| layout_ref = std::make_unique<raw::NamedLayoutReference>(scope.GetSourceElement(), |
| std::move(identifier)); |
| } |
| } |
| |
| std::unique_ptr<raw::LayoutParameterList> parameters; |
| if (previous_token_.kind() != Token::Kind::kColon) { |
| parameters = MaybeParseLayoutParameterList(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| std::unique_ptr<raw::TypeConstraints> constraints; |
| MaybeConsumeToken(OfKind(Token::Kind::kColon)); |
| if (previous_token_.kind() == Token::Kind::kColon) { |
| constraints = ParseConstraints(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::TypeConstructorNew>(scope.GetSourceElement(), std::move(layout_ref), |
| std::move(parameters), std::move(constraints)); |
| } |
| |
| raw::TypeConstructor Parser::ParseTypeConstructor() { |
| if (syntax_ == fidl::utils::Syntax::kNew) |
| return ParseTypeConstructorNew(); |
| return ParseTypeConstructorOld(); |
| } |
| |
| std::unique_ptr<raw::TypeDecl> Parser::ParseTypeDecl(ASTScope& scope) { |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kType)); |
| assert(Ok() && "caller should check first token"); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| ConsumeToken(OfKind(Token::Kind::kEqual)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto layout = ParseTypeConstructorNew(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::TypeDecl>(scope.GetSourceElement(), std::move(identifier), |
| std::move(layout)); |
| } |
| |
| std::unique_ptr<raw::File> Parser::ParseFileNewSyntax( |
| ASTScope& scope, std::unique_ptr<raw::AttributeList> library_attributes, |
| std::unique_ptr<raw::CompoundIdentifier> library_name) { |
| std::vector<std::unique_ptr<raw::AliasDeclaration>> alias_list; |
| std::vector<std::unique_ptr<raw::Using>> using_list; |
| std::vector<std::unique_ptr<raw::BitsDeclaration>> bits_declaration_list; |
| std::vector<std::unique_ptr<raw::ConstDeclaration>> const_declaration_list; |
| std::vector<std::unique_ptr<raw::EnumDeclaration>> enum_declaration_list; |
| std::vector<std::unique_ptr<raw::ProtocolDeclaration>> protocol_declaration_list; |
| std::vector<std::unique_ptr<raw::ResourceDeclaration>> resource_declaration_list; |
| std::vector<std::unique_ptr<raw::ServiceDeclaration>> service_declaration_list; |
| std::vector<std::unique_ptr<raw::StructDeclaration>> struct_declaration_list; |
| std::vector<std::unique_ptr<raw::TableDeclaration>> table_declaration_list; |
| std::vector<std::unique_ptr<raw::UnionDeclaration>> union_declaration_list; |
| std::vector<std::unique_ptr<raw::TypeDecl>> type_decls; |
| |
| bool done_with_library_imports = false; |
| auto parse_declaration = [&]() { |
| // TODO(fxbug.dev/70247): Once we're fully on the new syntax, we should refactor all of the |
| // top-level "Parse..." methods to omit their externally defined ASTScope parameter. This was |
| // necessary when top-level definitions could begin with modifiers (ex: "strict struct S {...") |
| // which is no longer possible in the new syntax. |
| ASTScope scope(this); |
| std::unique_ptr<raw::AttributeList> attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return More; |
| |
| switch (Peek().combined()) { |
| default: |
| Fail(ErrExpectedDeclaration, last_token_.data()); |
| return More; |
| |
| case CASE_TOKEN(Token::Kind::kEndOfFile): |
| return Done; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kDeprecatedSyntax): { |
| Fail(ErrMisplacedSyntaxVersion); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kAlias): { |
| done_with_library_imports = true; |
| add(&alias_list, |
| [&] { return ParseAliasDeclaration(std::move(attributes), scope, Modifiers()); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kConst): { |
| done_with_library_imports = true; |
| add(&const_declaration_list, |
| [&] { return ParseConstDeclaration(std::move(attributes), scope, Modifiers()); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kType): { |
| done_with_library_imports = true; |
| add(&type_decls, [&] { return ParseTypeDecl(scope); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kProtocol): { |
| done_with_library_imports = true; |
| add(&protocol_declaration_list, |
| [&] { return ParseProtocolDeclaration(std::move(attributes), scope, Modifiers()); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kResourceDefinition): { |
| done_with_library_imports = true; |
| add(&resource_declaration_list, |
| [&] { return ParseResourceDeclaration(std::move(attributes), scope, Modifiers()); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kService): { |
| done_with_library_imports = true; |
| add(&service_declaration_list, |
| [&] { return ParseServiceDeclaration(std::move(attributes), scope, Modifiers()); }); |
| return More; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kUsing): { |
| add(&using_list, [&] { return ParseUsing(std::move(attributes), scope, Modifiers()); }); |
| if (Ok() && done_with_library_imports) { |
| reporter_->Report(diagnostics::ErrLibraryImportsMustBeGroupedAtTopOfFile, |
| using_list.back()->span()); |
| } |
| return More; |
| } |
| } |
| }; |
| |
| while (parse_declaration() == More) { |
| if (!Ok()) { |
| // If this returns RecoverResult::Continue, we have consumed up to a '}' |
| // and expect a ';' to follow. |
| auto result = RecoverToEndOfDecl(); |
| if (result == RecoverResult::Failure) { |
| return Fail(); |
| } else if (result == RecoverResult::EndOfScope) { |
| break; |
| } |
| } |
| ConsumeTokenOrRecover(OfKind(Token::Kind::kSemicolon)); |
| } |
| |
| std::optional<Token> end = ConsumeToken(OfKind(Token::Kind::kEndOfFile)); |
| if (!Ok() || !end) |
| return Fail(); |
| |
| return std::make_unique<raw::File>( |
| scope.GetSourceElement(), end.value(), std::move(library_attributes), std::move(library_name), |
| std::move(alias_list), std::move(using_list), std::move(bits_declaration_list), |
| std::move(const_declaration_list), std::move(enum_declaration_list), |
| std::move(protocol_declaration_list), std::move(resource_declaration_list), |
| std::move(service_declaration_list), std::move(struct_declaration_list), |
| std::move(table_declaration_list), std::move(union_declaration_list), std::move(type_decls), |
| std::move(comment_tokens_), fidl::utils::Syntax::kNew); |
| } |
| |
| bool Parser::ConsumeTokensUntil(std::set<Token::Kind> exit_tokens) { |
| auto p = [&](Token::KindAndSubkind token) -> std::unique_ptr<Diagnostic> { |
| for (const auto& exit_token : exit_tokens) { |
| if (token.kind() == exit_token) |
| // signal to ReadToken to stop by returning an error |
| return Reporter::MakeError(ErrUnexpectedToken); |
| } |
| // nullptr return value indicates -> yes, consume to ReadToken |
| return nullptr; |
| }; |
| |
| // Consume tokens until we find a synchronization point |
| while (ReadToken(p, OnNoMatch::kIgnore) != std::nullopt) { |
| if (!Ok()) |
| return false; |
| } |
| return true; |
| } |
| |
| Parser::RecoverResult Parser::RecoverToEndOfDecl() { |
| if (ConsumedEOF()) { |
| return RecoverResult::Failure; |
| } |
| |
| RecoverAllErrors(); |
| |
| static const auto exit_tokens = std::set<Token::Kind>{ |
| Token::Kind::kRightCurly, |
| Token::Kind::kEndOfFile, |
| }; |
| if (!ConsumeTokensUntil(exit_tokens)) { |
| return RecoverResult::Failure; |
| } |
| |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kRightCurly): |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| if (!Ok()) |
| return RecoverResult::Failure; |
| return RecoverResult::Continue; |
| case CASE_TOKEN(Token::Kind::kEndOfFile): |
| return RecoverResult::EndOfScope; |
| default: |
| return RecoverResult::Failure; |
| } |
| } |
| |
| Parser::RecoverResult Parser::RecoverToEndOfMember() { |
| if (ConsumedEOF()) { |
| return RecoverResult::Failure; |
| } |
| |
| RecoverAllErrors(); |
| |
| static const auto exit_tokens = std::set<Token::Kind>{ |
| Token::Kind::kSemicolon, |
| Token::Kind::kRightCurly, |
| Token::Kind::kEndOfFile, |
| }; |
| if (!ConsumeTokensUntil(exit_tokens)) { |
| return RecoverResult::Failure; |
| } |
| |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kSemicolon): |
| return RecoverResult::Continue; |
| case CASE_TOKEN(Token::Kind::kRightCurly): |
| return RecoverResult::EndOfScope; |
| default: |
| return RecoverResult::Failure; |
| } |
| } |
| |
| template <Token::Kind ClosingToken> |
| Parser::RecoverResult Parser::RecoverToEndOfListItem() { |
| if (ConsumedEOF()) { |
| return RecoverResult::Failure; |
| } |
| |
| RecoverAllErrors(); |
| |
| static const auto exit_tokens = std::set<Token::Kind>{ |
| Token::Kind::kComma, |
| Token::Kind::kSemicolon, |
| Token::Kind::kRightCurly, |
| Token::Kind::kEndOfFile, |
| ClosingToken, |
| }; |
| if (!ConsumeTokensUntil(exit_tokens)) { |
| return RecoverResult::Failure; |
| } |
| |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kComma): |
| return RecoverResult::Continue; |
| case CASE_TOKEN(ClosingToken): |
| return RecoverResult::EndOfScope; |
| default: |
| return RecoverResult::Failure; |
| } |
| } |
| |
| Parser::RecoverResult Parser::RecoverToEndOfParam() { |
| return RecoverToEndOfListItem<Token::Kind::kRightParen>(); |
| } |
| |
| } // namespace fidl |