// 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" namespace fidl { #define TOKEN_PRIMITIVE_TYPE_CASES \ case Token::Kind::kBool: \ case Token::Kind::kInt8: \ case Token::Kind::kInt16: \ case Token::Kind::kInt32: \ case Token::Kind::kInt64: \ case Token::Kind::kUint8: \ case Token::Kind::kUint16: \ case Token::Kind::kUint32: \ case Token::Kind::kUint64: \ case Token::Kind::kFloat32: \ case Token::Kind::kFloat64 #define TOKEN_TYPE_CASES \ TOKEN_PRIMITIVE_TYPE_CASES: \ case Token::Kind::kIdentifier: \ case Token::Kind::kArray: \ case Token::Kind::kVector: \ case Token::Kind::kString: \ case Token::Kind::kHandle: \ case Token::Kind::kRequest #define TOKEN_ATTR_CASES \ case Token::Kind::kDocComment: \ case Token::Kind::kLeftSquare #define TOKEN_LITERAL_CASES \ case Token::Kind::kTrue: \ case Token::Kind::kFalse: \ case Token::Kind::kNumericLiteral: \ case Token::Kind::kStringLiteral namespace { enum { More, Done, }; } // namespace Parser::Parser(Lexer* lexer, ErrorReporter* error_reporter) : lexer_(lexer), error_reporter_(error_reporter), latest_discarded_end_() { handle_subtype_table_ = { {"process", types::HandleSubtype::kProcess}, {"thread", types::HandleSubtype::kThread}, {"vmo", types::HandleSubtype::kVmo}, {"channel", types::HandleSubtype::kChannel}, {"event", types::HandleSubtype::kEvent}, {"port", types::HandleSubtype::kPort}, {"interrupt", types::HandleSubtype::kInterrupt}, {"log", types::HandleSubtype::kLog}, {"socket", types::HandleSubtype::kSocket}, {"resource", types::HandleSubtype::kResource}, {"eventpair", types::HandleSubtype::kEventpair}, {"job", types::HandleSubtype::kJob}, {"vmar", types::HandleSubtype::kVmar}, {"fifo", types::HandleSubtype::kFifo}, {"guest", types::HandleSubtype::kGuest}, {"timer", types::HandleSubtype::kTimer}, }; last_token_ = Lex(); } bool Parser::LookupHandleSubtype(const raw::Identifier* identifier, types::HandleSubtype* subtype_out) { auto lookup = handle_subtype_table_.find(identifier->location().data()); if (lookup == handle_subtype_table_.end()) { return false; } *subtype_out = lookup->second; return true; } decltype(nullptr) Parser::Fail() { return Fail("found unexpected token"); } decltype(nullptr) Parser::Fail(StringView message) { if (ok_) { error_reporter_->ReportError(last_token_, std::move(message)); ok_ = false; } return nullptr; } std::unique_ptr Parser::ParseIdentifier(bool is_discarded) { Token identifier = ConsumeToken(Token::Kind::kIdentifier, is_discarded); if (!Ok()) return Fail(); return std::make_unique(identifier, identifier); } std::unique_ptr Parser::ParseCompoundIdentifier() { std::vector> components; components.emplace_back(ParseIdentifier()); if (!Ok()) return Fail(); Token first_token = components[0]->start_; auto parse_component = [&components, this]() { switch (Peek()) { default: return Done; case Token::Kind::kDot: ConsumeToken(Token::Kind::kDot, true); if (Ok()) { components.emplace_back(ParseIdentifier()); } return More; } }; while (parse_component() == More) { if (!Ok()) return Fail(); } return std::make_unique(first_token, MarkLastUseful(), std::move(components)); } std::unique_ptr Parser::ParseStringLiteral() { Token string_literal = ConsumeToken(Token::Kind::kStringLiteral); if (!Ok()) return Fail(); return std::make_unique(string_literal); } std::unique_ptr Parser::ParseNumericLiteral() { auto numeric_literal = ConsumeToken(Token::Kind::kNumericLiteral); if (!Ok()) return Fail(); return std::make_unique(numeric_literal); } std::unique_ptr Parser::ParseOrdinal() { auto numeric_literal = ConsumeToken(Token::Kind::kNumericLiteral); if (!Ok()) return Fail(); auto colon = ConsumeToken(Token::Kind::kColon); if (!Ok()) return Fail(); return std::make_unique(numeric_literal, colon); } std::unique_ptr Parser::ParseTrueLiteral() { Token token = ConsumeToken(Token::Kind::kTrue); if (!Ok()) return Fail(); return std::make_unique(token); } std::unique_ptr Parser::ParseFalseLiteral() { Token token = ConsumeToken(Token::Kind::kFalse); if (!Ok()) return Fail(); return std::make_unique(token); } std::unique_ptr Parser::ParseLiteral() { switch (Peek()) { case Token::Kind::kStringLiteral: return ParseStringLiteral(); case Token::Kind::kNumericLiteral: return ParseNumericLiteral(); case Token::Kind::kTrue: return ParseTrueLiteral(); case Token::Kind::kFalse: return ParseFalseLiteral(); default: return Fail(); } } std::unique_ptr Parser::ParseAttribute() { auto name = ParseIdentifier(); if (!Ok()) return Fail(); std::unique_ptr value; if (MaybeConsumeToken(Token::Kind::kEqual)) { value = ParseStringLiteral(); if (!Ok()) return Fail(); } std::string str_name(""); std::string str_value(""); if (name) str_name = std::string(name->location().data().data(), name->location().data().size()); if (value) { auto data = value->location().data(); if (data.size() >= 2 && data[0] == '"' && data[data.size() - 1] == '"') { str_value = std::string(value->location().data().data() + 1, value->location().data().size() - 2); } } return std::make_unique(name->start_, MarkLastUseful(), str_name, str_value); } std::unique_ptr Parser::ParseAttributeList(std::unique_ptr&& doc_comment) { Token start; auto attributes = std::make_unique(); if (doc_comment) { start = doc_comment->start_; attributes->Insert(std::move(doc_comment)); ConsumeToken(Token::Kind::kLeftSquare, true); } else { start = ConsumeToken(Token::Kind::kLeftSquare); } if (!Ok()) return Fail(); for (;;) { auto attribute = ParseAttribute(); if (!Ok()) return Fail(); auto attribute_name = attribute->name; if (!attributes->Insert(std::move(attribute))) { std::string message("Duplicate attribute with name '"); message += attribute_name; message += "'"; return Fail(message); } if (!MaybeConsumeToken(Token::Kind::kComma)) break; } ConsumeToken(Token::Kind::kRightSquare, true); if (!Ok()) return Fail(); auto attribute_list = std::make_unique(start, MarkLastUseful(), std::move(attributes)); return attribute_list; } std::unique_ptr Parser::ParseDocComment() { std::string str_value(""); Token start; Token end; Token doc_line; while (Peek() == Token::Kind::kDocComment) { // Most of the tokens are discarded, except the first and last, which we // retroactively mark useful. doc_line = ConsumeToken(Token::Kind::kDocComment, true); if (start.kind() == Token::Kind::kNotAToken) { start = MarkLastUseful(); } str_value += std::string(doc_line.location().data().data() + 3, doc_line.location().data().size() - 2); assert(Ok()); } end = MarkLastUseful(); return std::make_unique(start, end, "Doc", str_value); } std::unique_ptr Parser::MaybeParseAttributeList() { std::unique_ptr doc_comment; // Doc comments must appear above attributes if (Peek() == Token::Kind::kDocComment) { doc_comment = ParseDocComment(); } if (Peek() == Token::Kind::kLeftSquare) { return ParseAttributeList(std::move(doc_comment)); } // no generic attributes, start the attribute list if (doc_comment) { auto attributes = std::make_unique(); Token start = doc_comment->start_; Token end = doc_comment->end_; attributes->Insert(std::move(doc_comment)); return std::make_unique(start, end, std::move(attributes)); } return nullptr; } std::unique_ptr Parser::ParseConstant() { switch (Peek()) { case Token::Kind::kIdentifier: { auto identifier = ParseCompoundIdentifier(); if (!Ok()) return Fail(); return std::make_unique(std::move(identifier)); } TOKEN_LITERAL_CASES : { auto literal = ParseLiteral(); if (!Ok()) return Fail(); return std::make_unique(std::move(literal)); } default: return Fail(); } } std::unique_ptr Parser::ParseUsing() { Token start = ConsumeToken(Token::Kind::kUsing); if (!Ok()) return Fail(); auto using_path = ParseCompoundIdentifier(); if (!Ok()) return Fail(); std::unique_ptr maybe_alias; std::unique_ptr maybe_primitive; if (MaybeConsumeToken(Token::Kind::kAs)) { if (!Ok()) return Fail(); maybe_alias = ParseIdentifier(); if (!Ok()) return Fail(); } else if (MaybeConsumeToken(Token::Kind::kEqual)) { if (!Ok() || using_path->components.size() != 1u) return Fail(); maybe_primitive = ParsePrimitiveType(); if (!Ok()) return Fail(); } return std::make_unique(start, MarkLastUseful(), std::move(using_path), std::move(maybe_alias), std::move(maybe_primitive)); } std::unique_ptr Parser::ParseArrayType() { Token start = ConsumeToken(Token::Kind::kArray); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kLeftAngle, true); if (!Ok()) return Fail(); auto element_type = ParseType(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kRightAngle, true); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kColon, true); if (!Ok()) return Fail(); auto element_count = ParseConstant(); if (!Ok()) return Fail(); return std::make_unique(start, MarkLastUseful(), std::move(element_type), std::move(element_count)); } std::unique_ptr Parser::ParseVectorType() { Token start = ConsumeToken(Token::Kind::kVector); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kLeftAngle, true); if (!Ok()) return Fail(); auto element_type = ParseType(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kRightAngle, true); if (!Ok()) return Fail(); std::unique_ptr maybe_element_count; if (MaybeConsumeToken(Token::Kind::kColon)) { if (!Ok()) return Fail(); maybe_element_count = ParseConstant(); if (!Ok()) return Fail(); } auto nullability = types::Nullability::kNonnullable; if (MaybeConsumeToken(Token::Kind::kQuestion)) { nullability = types::Nullability::kNullable; } return std::make_unique(start, MarkLastUseful(), std::move(element_type), std::move(maybe_element_count), nullability); } std::unique_ptr Parser::ParseStringType() { Token start = ConsumeToken(Token::Kind::kString); if (!Ok()) return Fail(); std::unique_ptr maybe_element_count; if (MaybeConsumeToken(Token::Kind::kColon)) { if (!Ok()) return Fail(); maybe_element_count = ParseConstant(); if (!Ok()) return Fail(); } auto nullability = types::Nullability::kNonnullable; if (MaybeConsumeToken(Token::Kind::kQuestion)) { nullability = types::Nullability::kNullable; } return std::make_unique(start, MarkLastUseful(), std::move(maybe_element_count), nullability); } std::unique_ptr Parser::ParseHandleType() { Token start = ConsumeToken(Token::Kind::kHandle); if (!Ok()) return Fail(); auto subtype = types::HandleSubtype::kHandle; if (MaybeConsumeToken(Token::Kind::kLeftAngle)) { if (!Ok()) return Fail(); auto identifier = ParseIdentifier(true); if (!Ok()) return Fail(); if (!LookupHandleSubtype(identifier.get(), &subtype)) return Fail(); ConsumeToken(Token::Kind::kRightAngle, true); if (!Ok()) return Fail(); } auto nullability = types::Nullability::kNonnullable; if (MaybeConsumeToken(Token::Kind::kQuestion)) { nullability = types::Nullability::kNullable; } return std::make_unique(start, MarkLastUseful(), subtype, nullability); } std::unique_ptr Parser::ParsePrimitiveType() { types::PrimitiveSubtype subtype; switch (Peek()) { case Token::Kind::kBool: subtype = types::PrimitiveSubtype::kBool; break; case Token::Kind::kInt8: subtype = types::PrimitiveSubtype::kInt8; break; case Token::Kind::kInt16: subtype = types::PrimitiveSubtype::kInt16; break; case Token::Kind::kInt32: subtype = types::PrimitiveSubtype::kInt32; break; case Token::Kind::kInt64: subtype = types::PrimitiveSubtype::kInt64; break; case Token::Kind::kUint8: subtype = types::PrimitiveSubtype::kUint8; break; case Token::Kind::kUint16: subtype = types::PrimitiveSubtype::kUint16; break; case Token::Kind::kUint32: subtype = types::PrimitiveSubtype::kUint32; break; case Token::Kind::kUint64: subtype = types::PrimitiveSubtype::kUint64; break; case Token::Kind::kFloat32: subtype = types::PrimitiveSubtype::kFloat32; break; case Token::Kind::kFloat64: subtype = types::PrimitiveSubtype::kFloat64; break; default: return Fail(); } Token start = ConsumeToken(Peek()); if (!Ok()) return Fail(); return std::make_unique(start, MarkLastUseful(), subtype); } std::unique_ptr Parser::ParseRequestHandleType() { Token start = ConsumeToken(Token::Kind::kRequest); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kLeftAngle, true); if (!Ok()) return Fail(); auto identifier = ParseCompoundIdentifier(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kRightAngle, true); if (!Ok()) return Fail(); auto nullability = types::Nullability::kNonnullable; if (MaybeConsumeToken(Token::Kind::kQuestion)) { nullability = types::Nullability::kNullable; } return std::make_unique(start, MarkLastUseful(), std::move(identifier), nullability); } std::unique_ptr Parser::ParseType() { switch (Peek()) { case Token::Kind::kIdentifier: { auto identifier = ParseCompoundIdentifier(); if (!Ok()) return Fail(); auto nullability = types::Nullability::kNonnullable; if (MaybeConsumeToken(Token::Kind::kQuestion)) { if (!Ok()) return Fail(); nullability = types::Nullability::kNullable; } return std::make_unique(identifier->start_, MarkLastUseful(), std::move(identifier), nullability); } case Token::Kind::kArray: { auto type = ParseArrayType(); if (!Ok()) return Fail(); return type; } case Token::Kind::kVector: { auto type = ParseVectorType(); if (!Ok()) return Fail(); return type; } case Token::Kind::kString: { auto type = ParseStringType(); if (!Ok()) return Fail(); return type; } case Token::Kind::kHandle: { auto type = ParseHandleType(); if (!Ok()) return Fail(); return type; } case Token::Kind::kRequest: { auto type = ParseRequestHandleType(); if (!Ok()) return Fail(); return type; } TOKEN_PRIMITIVE_TYPE_CASES : { auto type = ParsePrimitiveType(); if (!Ok()) return Fail(); return type; } default: return Fail(); } } std::unique_ptr Parser::ParseConstDeclaration(std::unique_ptr attributes) { Token start = ConsumeTokenReturnEarliest(Token::Kind::kConst, attributes); if (!Ok()) return Fail(); auto type = ParseType(); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kEqual, true); if (!Ok()) return Fail(); auto constant = ParseConstant(); if (!Ok()) return Fail(); return std::make_unique(start, MarkLastUseful(), std::move(attributes), std::move(type), std::move(identifier), std::move(constant)); } std::unique_ptr Parser::ParseEnumMember() { auto attributes = MaybeParseAttributeList(); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kEqual, true); if (!Ok()) return Fail(); auto member_value = ParseConstant(); if (!Ok()) return Fail(); Token start; if (attributes != nullptr) { start = attributes->start_; } else { start = identifier->start_; } return std::make_unique(start, MarkLastUseful(), std::move(identifier), std::move(member_value), std::move(attributes)); } std::unique_ptr Parser::ParseEnumDeclaration(std::unique_ptr attributes) { std::vector> members; Token start = ConsumeTokenReturnEarliest(Token::Kind::kEnum, attributes); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); std::unique_ptr subtype; if (MaybeConsumeToken(Token::Kind::kColon)) { if (!Ok()) return Fail(); subtype = ParsePrimitiveType(); if (!Ok()) return Fail(); } ConsumeToken(Token::Kind::kLeftCurly, true); if (!Ok()) return Fail(); auto parse_member = [&members, this]() { switch (Peek()) { default: ConsumeToken(Token::Kind::kRightCurly, true); return Done; TOKEN_ATTR_CASES: // intentional fallthrough for attribute parsing TOKEN_TYPE_CASES: members.emplace_back(ParseEnumMember()); return More; } }; while (parse_member() == More) { if (!Ok()) Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); } if (!Ok()) Fail(); if (members.empty()) return Fail(); return std::make_unique(start, MarkLastUseful(), std::move(attributes), std::move(identifier), std::move(subtype), std::move(members)); } std::unique_ptr Parser::ParseParameter() { auto type = ParseType(); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); return std::make_unique(type->start_, MarkLastUseful(), std::move(type), std::move(identifier)); } std::unique_ptr Parser::ParseParameterList() { std::vector> parameter_list; Token start; switch (Peek()) { default: break; TOKEN_TYPE_CASES: auto parameter = ParseParameter(); if (start.kind() != Token::Kind::kNotAToken) { start = parameter->start_; } parameter_list.emplace_back(std::move(parameter)); if (!Ok()) return Fail(); while (Peek() == Token::Kind::kComma) { ConsumeToken(Token::Kind::kComma, true); if (!Ok()) return Fail(); switch (Peek()) { TOKEN_TYPE_CASES: parameter_list.emplace_back(ParseParameter()); if (!Ok()) return Fail(); break; default: return Fail(); } } } return std::make_unique(start, MarkLastUseful(), std::move(parameter_list)); } std::unique_ptr Parser::ParseInterfaceMethod(std::unique_ptr attributes) { Token start; auto ordinal = ParseOrdinal(); if (!Ok()) return Fail(); if (attributes != nullptr && attributes->attributes_->attributes_.size() != 0) { start = attributes->start_; } else { start = ordinal->start_; } std::unique_ptr method_name; std::unique_ptr maybe_request; std::unique_ptr maybe_response; auto parse_params = [this](std::unique_ptr* params_out) { ConsumeToken(Token::Kind::kLeftParen, true); if (!Ok()) return false; *params_out = ParseParameterList(); if (!Ok()) return false; ConsumeToken(Token::Kind::kRightParen, true); if (!Ok()) return false; return true; }; if (MaybeConsumeToken(Token::Kind::kArrow)) { method_name = ParseIdentifier(); if (!Ok()) return Fail(); if (!parse_params(&maybe_response)) return Fail(); } else { method_name = ParseIdentifier(); if (!Ok()) return Fail(); if (!parse_params(&maybe_request)) return Fail(); if (MaybeConsumeToken(Token::Kind::kArrow)) { if (!Ok()) return Fail(); if (!parse_params(&maybe_response)) return Fail(); } } assert(method_name); assert(maybe_request || maybe_response); return std::make_unique(start, MarkLastUseful(), std::move(attributes), std::move(ordinal), std::move(method_name), std::move(maybe_request), std::move(maybe_response)); } std::unique_ptr Parser::ParseInterfaceDeclaration(std::unique_ptr attributes) { std::vector> superinterfaces; std::vector> methods; // The first token may be the word "interface", or it may be the beginning // of the attribute list. Token start = ConsumeTokenReturnEarliest(Token::Kind::kInterface, attributes); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); if (MaybeConsumeToken(Token::Kind::kColon)) { for (;;) { superinterfaces.emplace_back(ParseCompoundIdentifier()); if (!Ok()) return Fail(); if (!MaybeConsumeToken(Token::Kind::kComma)) break; } } ConsumeToken(Token::Kind::kLeftCurly, true); if (!Ok()) return Fail(); auto parse_member = [&methods, this]() { std::unique_ptr attributes = MaybeParseAttributeList(); if (!Ok()) return More; switch (Peek()) { default: ConsumeToken(Token::Kind::kRightCurly, true); return Done; case Token::Kind::kNumericLiteral: methods.emplace_back(ParseInterfaceMethod(std::move(attributes))); return More; } }; while (parse_member() == More) { if (!Ok()) Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); } if (!Ok()) Fail(); return std::make_unique(start, MarkLastUseful(), std::move(attributes), std::move(identifier), std::move(superinterfaces), std::move(methods)); } std::unique_ptr Parser::ParseStructMember() { auto attributes = MaybeParseAttributeList(); if (!Ok()) return Fail(); auto type = ParseType(); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); std::unique_ptr maybe_default_value; if (MaybeConsumeToken(Token::Kind::kEqual)) { if (!Ok()) return Fail(); maybe_default_value = ParseConstant(); if (!Ok()) return Fail(); } Token start; if (attributes != nullptr) { start = attributes->start_; } else { start = type->start_; } return std::make_unique(start, MarkLastUseful(), std::move(type), std::move(identifier), std::move(maybe_default_value), std::move(attributes)); } std::unique_ptr Parser::ParseStructDeclaration(std::unique_ptr attributes) { std::vector> members; Token start = ConsumeTokenReturnEarliest(Token::Kind::kStruct, attributes); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kLeftCurly, true); if (!Ok()) return Fail(); auto parse_member = [&members, this]() { switch (Peek()) { default: ConsumeToken(Token::Kind::kRightCurly, true); return Done; TOKEN_ATTR_CASES: // intentional fallthrough for attribute parsing TOKEN_TYPE_CASES: members.emplace_back(ParseStructMember()); return More; } }; while (parse_member() == More) { if (!Ok()) Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); } if (!Ok()) Fail(); if (members.empty()) return Fail(); return std::make_unique(start, MarkLastUseful(), std::move(attributes), std::move(identifier), std::move(members)); } std::unique_ptr Parser::ParseTableMember() { std::unique_ptr attributes = MaybeParseAttributeList(); if (!Ok()) return Fail(); auto ordinal = ParseOrdinal(); if (!Ok()) return Fail(); if (MaybeConsumeToken(Token::Kind::kReserved)) { if (!Ok()) return Fail(); if (attributes != nullptr) return Fail("Cannot attach attributes to reserved ordinals"); return std::make_unique(ordinal->start_, MarkLastUseful(), std::move(ordinal)); } auto type = ParseType(); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); std::unique_ptr maybe_default_value; if (MaybeConsumeToken(Token::Kind::kEqual)) { if (!Ok()) return Fail(); maybe_default_value = ParseConstant(); if (!Ok()) return Fail(); } Token start; if (attributes != nullptr) { start = attributes->start_; } else { start = ordinal->start_; } return std::make_unique(start, MarkLastUseful(), std::move(ordinal), std::move(type), std::move(identifier), std::move(maybe_default_value), std::move(attributes)); } std::unique_ptr Parser::ParseTableDeclaration(std::unique_ptr attributes) { std::vector> members; Token start = ConsumeTokenReturnEarliest(Token::Kind::kTable, attributes); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kLeftCurly, true); if (!Ok()) return Fail(); auto parse_member = [&members, this]() { switch (Peek()) { default: ConsumeToken(Token::Kind::kRightCurly, true); return Done; case Token::Kind::kNumericLiteral: TOKEN_ATTR_CASES: members.emplace_back(ParseTableMember()); return More; } }; while (parse_member() == More) { if (!Ok()) Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); } if (!Ok()) Fail(); if (members.empty()) return Fail("Tables must have at least one member"); return std::make_unique(start, MarkLastUseful(), std::move(attributes), std::move(identifier), std::move(members)); } std::unique_ptr Parser::ParseUnionMember() { auto attributes = MaybeParseAttributeList(); if (!Ok()) return Fail(); auto type = ParseType(); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); Token start; if (attributes != nullptr) { start = attributes->start_; } else { start = type->start_; } return std::make_unique(start, MarkLastUseful(), std::move(type), std::move(identifier), std::move(attributes)); } std::unique_ptr Parser::ParseUnionDeclaration(std::unique_ptr attributes) { std::vector> members; Token start = ConsumeTokenReturnEarliest(Token::Kind::kUnion, attributes); if (!Ok()) return Fail(); auto identifier = ParseIdentifier(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kLeftCurly, true); if (!Ok()) return Fail(); auto parse_member = [&members, this]() { switch (Peek()) { default: ConsumeToken(Token::Kind::kRightCurly, true); return Done; TOKEN_ATTR_CASES: // intentional fallthrough for attribute parsing TOKEN_TYPE_CASES: members.emplace_back(ParseUnionMember()); return More; } }; while (parse_member() == More) { if (!Ok()) Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); } if (!Ok()) Fail(); if (members.empty()) Fail(); return std::make_unique(start, MarkLastUseful(), std::move(attributes), std::move(identifier), std::move(members)); } std::unique_ptr Parser::ParseFile() { std::vector> using_list; std::vector> const_declaration_list; std::vector> enum_declaration_list; std::vector> interface_declaration_list; std::vector> struct_declaration_list; std::vector> table_declaration_list; std::vector> union_declaration_list; auto attributes = MaybeParseAttributeList(); if (!Ok()) return Fail(); Token start = ConsumeToken(Token::Kind::kLibrary); if (!Ok()) return Fail(); auto library_name = ParseCompoundIdentifier(); if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); auto parse_using = [&using_list, this]() { switch (Peek()) { default: return Done; case Token::Kind::kUsing: using_list.emplace_back(ParseUsing()); return More; } }; while (parse_using() == More) { if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); } auto parse_declaration = [&const_declaration_list, &enum_declaration_list, &interface_declaration_list, &struct_declaration_list, &table_declaration_list, &union_declaration_list, this]() { std::unique_ptr attributes = MaybeParseAttributeList(); if (!Ok()) return More; switch (Peek()) { default: return Done; case Token::Kind::kConst: const_declaration_list.emplace_back(ParseConstDeclaration(std::move(attributes))); return More; case Token::Kind::kEnum: enum_declaration_list.emplace_back(ParseEnumDeclaration(std::move(attributes))); return More; case Token::Kind::kInterface: interface_declaration_list.emplace_back( ParseInterfaceDeclaration(std::move(attributes))); return More; case Token::Kind::kStruct: struct_declaration_list.emplace_back(ParseStructDeclaration(std::move(attributes))); return More; case Token::Kind::kTable: table_declaration_list.emplace_back(ParseTableDeclaration(std::move(attributes))); return More; case Token::Kind::kUnion: union_declaration_list.emplace_back(ParseUnionDeclaration(std::move(attributes))); return More; } }; while (parse_declaration() == More) { if (!Ok()) return Fail(); ConsumeToken(Token::Kind::kSemicolon, true); if (!Ok()) return Fail(); } Token end = ConsumeToken(Token::Kind::kEndOfFile, false); if (!Ok()) return Fail(); return std::make_unique( start, end, std::move(attributes), std::move(library_name), std::move(using_list), std::move(const_declaration_list), std::move(enum_declaration_list), std::move(interface_declaration_list), std::move(struct_declaration_list), std::move(table_declaration_list), std::move(union_declaration_list)); } } // namespace fidl