| // 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 "banjo/parser.h" |
| |
| namespace banjo { |
| |
| // 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_PRIMITIVE_TYPE_CASES \ |
| case CASE_IDENTIFIER(Token::Subkind::kBool): \ |
| case CASE_IDENTIFIER(Token::Subkind::kInt8): \ |
| case CASE_IDENTIFIER(Token::Subkind::kInt16): \ |
| case CASE_IDENTIFIER(Token::Subkind::kInt32): \ |
| case CASE_IDENTIFIER(Token::Subkind::kInt64): \ |
| case CASE_IDENTIFIER(Token::Subkind::kUint8): \ |
| case CASE_IDENTIFIER(Token::Subkind::kUint16): \ |
| case CASE_IDENTIFIER(Token::Subkind::kUint32): \ |
| case CASE_IDENTIFIER(Token::Subkind::kUint64): \ |
| case CASE_IDENTIFIER(Token::Subkind::kFloat32): \ |
| case CASE_IDENTIFIER(Token::Subkind::kFloat64): \ |
| case CASE_IDENTIFIER(Token::Subkind::kUSize): \ |
| case CASE_IDENTIFIER(Token::Subkind::kISize): \ |
| case CASE_IDENTIFIER(Token::Subkind::kVoidPtr) |
| |
| |
| #define TOKEN_TYPE_CASES \ |
| TOKEN_PRIMITIVE_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::kHandle): \ |
| 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, |
| }; |
| } // namespace |
| |
| Parser::Parser(Lexer* lexer, ErrorReporter* error_reporter) |
| : lexer_(lexer), error_reporter_(error_reporter) { |
| 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}, |
| {"debuglog", 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}, |
| {"bti", types::HandleSubtype::kBti}, |
| {"profile", types::HandleSubtype::kProfile}, |
| }; |
| |
| 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<raw::Identifier> Parser::ParseIdentifier(bool is_discarded) { |
| ASTScope scope(this, is_discarded); |
| ConsumeToken(OfKind(Token::Kind::kIdentifier)); |
| if (!Ok()) |
| return Fail(); |
| |
| 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::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::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->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<raw::Attribute>(scope.GetSourceElement(), str_name, str_value); |
| } |
| |
| std::unique_ptr<raw::AttributeList> Parser::ParseAttributeList(std::unique_ptr<raw::Attribute>&& doc_comment, ASTScope& scope) { |
| auto attributes = std::make_unique<raw::Attributes>(); |
| if (doc_comment) { |
| attributes->Insert(std::move(doc_comment)); |
| } |
| ConsumeToken(OfKind(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(OfKind(Token::Kind::kComma))) |
| break; |
| } |
| ConsumeToken(OfKind(Token::Kind::kRightSquare)); |
| if (!Ok()) |
| return Fail(); |
| auto attribute_list = std::make_unique<raw::AttributeList>(scope.GetSourceElement(), std::move(attributes)); |
| return attribute_list; |
| } |
| |
| std::unique_ptr<raw::Attribute> Parser::ParseDocComment() { |
| ASTScope scope(this); |
| std::string str_value(""); |
| |
| Token doc_line; |
| while (Peek().kind() == Token::Kind::kDocComment) { |
| doc_line = ConsumeToken(OfKind(Token::Kind::kDocComment)); |
| str_value += std::string(doc_line.location().data().data() + 3, doc_line.location().data().size() - 2); |
| assert(Ok()); |
| } |
| return std::make_unique<raw::Attribute>(scope.GetSourceElement(), "Doc", std::move(str_value)); |
| } |
| |
| std::unique_ptr<raw::AttributeList> Parser::MaybeParseAttributeList() { |
| 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 (Peek().kind() == Token::Kind::kLeftSquare) { |
| return ParseAttributeList(std::move(doc_comment), scope); |
| } |
| // no generic attributes, start the attribute list |
| if (doc_comment) { |
| auto attributes = std::make_unique<raw::Attributes>(); |
| attributes->Insert(std::move(doc_comment)); |
| return std::make_unique<raw::AttributeList>(scope.GetSourceElement(), std::move(attributes)); |
| } |
| return nullptr; |
| } |
| |
| std::unique_ptr<raw::Constant> Parser::ParseConstant() { |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kIdentifier): { |
| auto identifier = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| return std::make_unique<raw::IdentifierConstant>(std::move(identifier)); |
| } |
| |
| TOKEN_LITERAL_CASES : { |
| auto literal = ParseLiteral(); |
| if (!Ok()) |
| return Fail(); |
| return std::make_unique<raw::LiteralConstant>(std::move(literal)); |
| } |
| |
| default: |
| return Fail(); |
| } |
| } |
| |
| std::unique_ptr<raw::Using> Parser::ParseUsing() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kUsing)); |
| if (!Ok()) |
| return Fail(); |
| auto using_path = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::Identifier> maybe_alias; |
| std::unique_ptr<raw::PrimitiveType> maybe_primitive; |
| |
| 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 (!Ok() || using_path->components.size() != 1u) |
| return Fail(); |
| maybe_primitive = ParsePrimitiveType(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| return std::make_unique<raw::Using>(scope.GetSourceElement(), std::move(using_path), std::move(maybe_alias), std::move(maybe_primitive)); |
| } |
| |
| std::unique_ptr<raw::ArrayType> Parser::ParseArrayType() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kArray)); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftAngle)); |
| if (!Ok()) |
| return Fail(); |
| auto element_type = ParseType(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kRightAngle)); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kColon)); |
| if (!Ok()) |
| return Fail(); |
| auto element_count = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::ArrayType>(scope.GetSourceElement(), std::move(element_type), std::move(element_count)); |
| } |
| |
| std::unique_ptr<raw::VectorType> Parser::ParseVectorType() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kVector)); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftAngle)); |
| if (!Ok()) |
| return Fail(); |
| auto element_type = ParseType(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kRightAngle)); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::Constant> maybe_element_count; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_element_count = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| auto nullability = types::Nullability::kNonnullable; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kQuestion))) { |
| nullability = types::Nullability::kNullable; |
| } |
| |
| return std::make_unique<raw::VectorType>(scope.GetSourceElement(), std::move(element_type), |
| std::move(maybe_element_count), nullability); |
| } |
| |
| std::unique_ptr<raw::StringType> Parser::ParseStringType() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kString)); |
| if (!Ok()) |
| return Fail(); |
| |
| std::unique_ptr<raw::Constant> maybe_element_count; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| if (!Ok()) |
| return Fail(); |
| maybe_element_count = ParseConstant(); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| auto nullability = types::Nullability::kNonnullable; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kQuestion))) { |
| nullability = types::Nullability::kNullable; |
| } |
| |
| return std::make_unique<raw::StringType>(scope.GetSourceElement(), std::move(maybe_element_count), nullability); |
| } |
| |
| std::unique_ptr<raw::HandleType> Parser::ParseHandleType() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kHandle)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto subtype = types::HandleSubtype::kHandle; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kLeftAngle))) { |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(true); |
| if (!Ok()) |
| return Fail(); |
| if (!LookupHandleSubtype(identifier.get(), &subtype)) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kRightAngle)); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| auto nullability = types::Nullability::kNonnullable; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kQuestion))) { |
| nullability = types::Nullability::kNullable; |
| } |
| |
| return std::make_unique<raw::HandleType>(scope.GetSourceElement(), subtype, nullability); |
| } |
| |
| std::unique_ptr<raw::PrimitiveType> Parser::ParsePrimitiveType() { |
| types::PrimitiveSubtype subtype; |
| |
| switch (Peek().combined()) { |
| case CASE_IDENTIFIER(Token::Subkind::kBool): |
| subtype = types::PrimitiveSubtype::kBool; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kInt8): |
| subtype = types::PrimitiveSubtype::kInt8; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kInt16): |
| subtype = types::PrimitiveSubtype::kInt16; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kInt32): |
| subtype = types::PrimitiveSubtype::kInt32; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kInt64): |
| subtype = types::PrimitiveSubtype::kInt64; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kUint8): |
| subtype = types::PrimitiveSubtype::kUint8; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kUint16): |
| subtype = types::PrimitiveSubtype::kUint16; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kUint32): |
| subtype = types::PrimitiveSubtype::kUint32; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kUint64): |
| subtype = types::PrimitiveSubtype::kUint64; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kFloat32): |
| subtype = types::PrimitiveSubtype::kFloat32; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kFloat64): |
| subtype = types::PrimitiveSubtype::kFloat64; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kUSize): |
| subtype = types::PrimitiveSubtype::kUSize; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kISize): |
| subtype = types::PrimitiveSubtype::kISize; |
| break; |
| case CASE_IDENTIFIER(Token::Subkind::kVoidPtr): |
| subtype = types::PrimitiveSubtype::kVoidPtr; |
| break; |
| default: |
| return Fail(); |
| } |
| ASTScope scope(this); |
| ConsumeToken(OfKind(Peek().kind())); |
| if (!Ok()) |
| return Fail(); |
| return std::make_unique<raw::PrimitiveType>(scope.GetSourceElement(), subtype); |
| } |
| |
| std::unique_ptr<raw::RequestHandleType> Parser::ParseRequestHandleType() { |
| ASTScope scope(this); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kRequest)); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftAngle)); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kRightAngle)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto nullability = types::Nullability::kNonnullable; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kQuestion))) { |
| nullability = types::Nullability::kNullable; |
| } |
| |
| return std::make_unique<raw::RequestHandleType>(scope.GetSourceElement(), std::move(identifier), nullability); |
| } |
| |
| std::unique_ptr<raw::Type> Parser::ParseType() { |
| switch (Peek().combined()) { |
| case CASE_TOKEN(Token::Kind::kIdentifier): { |
| ASTScope scope(this); |
| auto identifier = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| auto nullability = types::Nullability::kNonnullable; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kQuestion))) { |
| if (!Ok()) |
| return Fail(); |
| nullability = types::Nullability::kNullable; |
| } |
| return std::make_unique<raw::IdentifierType>(scope.GetSourceElement(), std::move(identifier), nullability); |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kArray): { |
| auto type = ParseArrayType(); |
| if (!Ok()) |
| return Fail(); |
| return type; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kVector): { |
| auto type = ParseVectorType(); |
| if (!Ok()) |
| return Fail(); |
| return type; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kString): { |
| auto type = ParseStringType(); |
| if (!Ok()) |
| return Fail(); |
| return type; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::kHandle): { |
| auto type = ParseHandleType(); |
| if (!Ok()) |
| return Fail(); |
| return type; |
| } |
| |
| case CASE_IDENTIFIER(Token::Subkind::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<raw::ConstDeclaration> |
| Parser::ParseConstDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope) { |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kConst)); |
| |
| if (!Ok()) |
| return Fail(); |
| auto type = ParseType(); |
| if (!Ok()) |
| return Fail(); |
| auto 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), |
| 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) { |
| std::vector<std::unique_ptr<raw::EnumMember>> members; |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kEnum)); |
| |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| std::unique_ptr<raw::PrimitiveType> subtype; |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| if (!Ok()) |
| return Fail(); |
| subtype = ParsePrimitiveType(); |
| if (!Ok()) |
| return Fail(); |
| } |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&members, this]() { |
| switch (Peek().combined()) { |
| default: |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| 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(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| // TODO(surajmalhotra): REMOVE THIS HACK. |
| #if 0 |
| if (members.empty()) |
| return Fail(); |
| #endif |
| |
| return std::make_unique<raw::EnumDeclaration>(scope.GetSourceElement(), |
| std::move(attributes), std::move(identifier), |
| std::move(subtype), std::move(members)); |
| } |
| |
| std::unique_ptr<raw::Parameter> Parser::ParseParameter() { |
| ASTScope scope(this); |
| auto type = ParseType(); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::Parameter>(scope.GetSourceElement(), std::move(type), std::move(identifier)); |
| } |
| |
| std::unique_ptr<raw::ParameterList> Parser::ParseParameterList() { |
| ASTScope scope(this); |
| std::vector<std::unique_ptr<raw::Parameter>> parameter_list; |
| |
| switch (Peek().combined()) { |
| default: |
| break; |
| |
| TOKEN_TYPE_CASES: |
| auto parameter = ParseParameter(); |
| parameter_list.emplace_back(std::move(parameter)); |
| if (!Ok()) |
| return Fail(); |
| while (Peek().kind() == Token::Kind::kComma) { |
| ConsumeToken(OfKind(Token::Kind::kComma)); |
| if (!Ok()) |
| return Fail(); |
| switch (Peek().combined()) { |
| TOKEN_TYPE_CASES: |
| parameter_list.emplace_back(ParseParameter()); |
| if (!Ok()) |
| return Fail(); |
| break; |
| |
| default: |
| return Fail(); |
| } |
| } |
| } |
| |
| return std::make_unique<raw::ParameterList>(scope.GetSourceElement(), std::move(parameter_list)); |
| } |
| |
| std::unique_ptr<raw::InterfaceMethod> Parser::ParseInterfaceMethod(std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope) { |
| std::unique_ptr<raw::Identifier> method_name; |
| std::unique_ptr<raw::ParameterList> maybe_request; |
| std::unique_ptr<raw::ParameterList> maybe_response; |
| |
| auto parse_params = [this](std::unique_ptr<raw::ParameterList>* params_out) { |
| ConsumeToken(OfKind(Token::Kind::kLeftParen)); |
| if (!Ok()) |
| return false; |
| *params_out = ParseParameterList(); |
| if (!Ok()) |
| return false; |
| ConsumeToken(OfKind(Token::Kind::kRightParen)); |
| if (!Ok()) |
| return false; |
| return true; |
| }; |
| |
| if (MaybeConsumeToken(OfKind(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(OfKind(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<raw::InterfaceMethod>(scope.GetSourceElement(), |
| std::move(attributes), |
| std::move(method_name), |
| std::move(maybe_request), |
| std::move(maybe_response)); |
| } |
| |
| std::unique_ptr<raw::InterfaceDeclaration> |
| Parser::ParseInterfaceDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope) { |
| std::vector<std::unique_ptr<raw::CompoundIdentifier>> superinterfaces; |
| std::vector<std::unique_ptr<raw::InterfaceMethod>> methods; |
| |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kInterface)); |
| |
| if (!Ok()) |
| return Fail(); |
| |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| if (MaybeConsumeToken(OfKind(Token::Kind::kColon))) { |
| for (;;) { |
| superinterfaces.emplace_back(ParseCompoundIdentifier()); |
| if (!Ok()) |
| return Fail(); |
| if (!MaybeConsumeToken(OfKind(Token::Kind::kComma))) |
| break; |
| } |
| } |
| |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&methods, this]() { |
| ASTScope scope(this); |
| std::unique_ptr<raw::AttributeList> attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return More; |
| |
| switch (Peek().kind()) { |
| default: |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| return Done; |
| |
| case Token::Kind::kArrow: |
| case Token::Kind::kIdentifier: |
| methods.emplace_back(ParseInterfaceMethod(std::move(attributes), scope)); |
| return More; |
| } |
| }; |
| |
| while (parse_member() == More) { |
| if (!Ok()) |
| Fail(); |
| ConsumeToken(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| return std::make_unique<raw::InterfaceDeclaration>(scope.GetSourceElement(), |
| std::move(attributes), std::move(identifier), |
| std::move(superinterfaces), |
| std::move(methods)); |
| } |
| |
| std::unique_ptr<raw::StructMember> Parser::ParseStructMember() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| auto type = ParseType(); |
| 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), 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) { |
| std::vector<std::unique_ptr<raw::StructMember>> members; |
| |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kStruct)); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&members, this]() { |
| switch (Peek().combined()) { |
| default: |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| 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(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| // TODO(surajmalhotra): REMOVE THIS HACK. |
| #if 0 |
| if (members.empty()) |
| return Fail(); |
| #endif |
| |
| return std::make_unique<raw::StructDeclaration>(scope.GetSourceElement(), |
| std::move(attributes), std::move(identifier), |
| std::move(members)); |
| } |
| |
| std::unique_ptr<raw::UnionMember> Parser::ParseUnionMember() { |
| ASTScope scope(this); |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| auto type = ParseType(); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::UnionMember>(scope.GetSourceElement(), std::move(type), std::move(identifier), std::move(attributes)); |
| } |
| |
| std::unique_ptr<raw::UnionDeclaration> |
| Parser::ParseUnionDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope) { |
| std::vector<std::unique_ptr<raw::UnionMember>> members; |
| |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kUnion)); |
| if (!Ok()) |
| return Fail(); |
| auto identifier = ParseIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kLeftCurly)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_member = [&members, this]() { |
| switch (Peek().combined()) { |
| default: |
| ConsumeToken(OfKind(Token::Kind::kRightCurly)); |
| 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(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| } |
| if (!Ok()) |
| Fail(); |
| |
| if (members.empty()) |
| Fail(); |
| |
| return std::make_unique<raw::UnionDeclaration>(scope.GetSourceElement(), |
| std::move(attributes), std::move(identifier), |
| std::move(members)); |
| } |
| |
| std::unique_ptr<raw::File> Parser::ParseFile() { |
| ASTScope scope(this); |
| std::vector<std::unique_ptr<raw::Using>> using_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::InterfaceDeclaration>> interface_declaration_list; |
| std::vector<std::unique_ptr<raw::StructDeclaration>> struct_declaration_list; |
| std::vector<std::unique_ptr<raw::UnionDeclaration>> union_declaration_list; |
| |
| auto attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(IdentifierOfSubkind(Token::Subkind::kLibrary)); |
| if (!Ok()) |
| return Fail(); |
| auto library_name = ParseCompoundIdentifier(); |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| |
| auto parse_using = [&using_list, this]() { |
| switch (Peek().combined()) { |
| default: |
| return Done; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kUsing): |
| using_list.emplace_back(ParseUsing()); |
| return More; |
| } |
| }; |
| |
| while (parse_using() == More) { |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| auto parse_declaration = [&const_declaration_list, &enum_declaration_list, |
| &interface_declaration_list, &struct_declaration_list, |
| &union_declaration_list, this]() { |
| ASTScope scope(this); |
| std::unique_ptr<raw::AttributeList> attributes = MaybeParseAttributeList(); |
| if (!Ok()) |
| return More; |
| |
| switch (Peek().combined()) { |
| default: |
| return Done; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kConst): |
| const_declaration_list.emplace_back(ParseConstDeclaration(std::move(attributes), scope)); |
| return More; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kEnum): |
| enum_declaration_list.emplace_back(ParseEnumDeclaration(std::move(attributes), scope)); |
| return More; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kInterface): |
| interface_declaration_list.emplace_back( |
| ParseInterfaceDeclaration(std::move(attributes), scope)); |
| return More; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kStruct): |
| struct_declaration_list.emplace_back(ParseStructDeclaration(std::move(attributes), scope)); |
| return More; |
| |
| case CASE_IDENTIFIER(Token::Subkind::kUnion): |
| union_declaration_list.emplace_back(ParseUnionDeclaration(std::move(attributes), scope)); |
| return More; |
| } |
| }; |
| |
| while (parse_declaration() == More) { |
| if (!Ok()) |
| return Fail(); |
| ConsumeToken(OfKind(Token::Kind::kSemicolon)); |
| if (!Ok()) |
| return Fail(); |
| } |
| |
| Token end = ConsumeToken(OfKind(Token::Kind::kEndOfFile)); |
| if (!Ok()) |
| return Fail(); |
| |
| return std::make_unique<raw::File>( |
| scope.GetSourceElement(), 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(union_declaration_list)); |
| } |
| |
| } // namespace banjo |