blob: f9249022bc2730ff8961faf4bcdbcb03ccff380d [file] [log] [blame]
// 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.
#ifndef ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_PARSER_H_
#define ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_PARSER_H_
#include <memory>
#include <optional>
#include "error_reporter.h"
#include "lexer.h"
#include "raw_ast.h"
namespace fidl {
class Parser {
public:
Parser(Lexer* lexer, ErrorReporter* error_reporter);
std::unique_ptr<raw::File> Parse() { return ParseFile(); }
bool Ok() const { return error_reporter_->errors().size() == 0; }
private:
Token Lex() { return lexer_->LexNoComments(); }
Token::KindAndSubkind Peek() { return last_token_.kind_and_subkind(); }
// ASTScope is a tool to track the start and end source location of each
// node automatically. The parser associates each node with the start and
// end of its source location. It also tracks the "gap" in between the
// start and the previous interesting source element. As we walk the tree,
// we create ASTScope objects that can track the beginning and end of the
// text associated with the Node being built. The ASTScope object then
// colludes with the Parser to figure out where the beginning and end of
// that node are.
//
// ASTScope should only be created on the stack, when starting to parse
// something that will result in a new AST node.
class ASTScope {
public:
explicit ASTScope(Parser* parser)
: parser_(parser) {
suppress_ = parser_->suppress_gap_checks_;
parser_->suppress_gap_checks_ = false;
parser_->active_ast_scopes_.push_back(raw::SourceElement(Token(), Token()));
}
// The suppress mechanism
ASTScope(Parser* parser, bool suppress)
: parser_(parser), suppress_(suppress) {
parser_->active_ast_scopes_.push_back(raw::SourceElement(Token(), Token()));
suppress_ = parser_->suppress_gap_checks_;
parser_->suppress_gap_checks_ = suppress;
}
raw::SourceElement GetSourceElement() {
parser_->active_ast_scopes_.back().end_ = parser_->previous_token_;
if (!parser_->suppress_gap_checks_) {
parser_->last_was_gap_start_ = true;
}
return raw::SourceElement(parser_->active_ast_scopes_.back());
}
~ASTScope() {
parser_->suppress_gap_checks_ = suppress_;
parser_->active_ast_scopes_.pop_back();
}
ASTScope(const ASTScope&) = delete;
ASTScope& operator=(const ASTScope&) = delete;
private:
Parser* parser_;
bool suppress_;
};
void UpdateMarks(Token& token) {
// There should always be at least one of these - the outermost.
if (active_ast_scopes_.size() == 0) {
Fail("Internal compiler error: unbalanced parse tree");
}
if (!suppress_gap_checks_) {
// If the end of the last node was the start of a gap, record that.
if (last_was_gap_start_ && previous_token_.kind() != Token::Kind::kNotAToken) {
gap_start_ = token.previous_end();
last_was_gap_start_ = false;
}
// If this is a start node, then the end of it will be the start of
// a gap.
if (active_ast_scopes_.back().start_.kind() == Token::Kind::kNotAToken) {
last_was_gap_start_ = true;
}
}
// Update the token to record the correct location of the beginning of
// the gap prior to it.
if (gap_start_.valid()) {
token.set_previous_end(gap_start_);
}
for (auto& scope : active_ast_scopes_) {
if (scope.start_.kind() == Token::Kind::kNotAToken) {
scope.start_ = token;
}
}
previous_token_ = token;
}
// ConsumeToken consumes a token, and matches using the predicate |p|.
// See #OfKind, and #IdentifierOfSubkind for the two expected predicates.
//
// If the token is not retained on return, is_discarded should be true.
// That allows the parser to track its source location, in case it should
// become interesting to the AST.
template <class Predicate>
Token ConsumeToken(Predicate p, bool is_discarded = false) {
std::unique_ptr<std::string> failure_message = p(Peek());
if (failure_message) {
Fail(*failure_message);
}
auto token = last_token_;
last_token_ = Lex();
UpdateMarks(token);
return token;
}
// MaybeConsumeToken consumes a token if-and-only-if it matches the given
// predicate |p|.
// See #OfKind, and #IdentifierOfSubkind for the two expected predicates.
template <class Predicate>
bool MaybeConsumeToken(Predicate p) {
std::unique_ptr<std::string> failure_message = p(Peek());
if (failure_message) {
return false;
}
previous_token_ = last_token_;
UpdateMarks(last_token_);
last_token_ = Lex();
return true;
}
static auto OfKind(Token::Kind expected_kind) {
return [expected_kind](Token::KindAndSubkind actual) -> std::unique_ptr<std::string> {
if (actual.kind() != expected_kind) {
auto message = std::make_unique<std::string>("unexpected token ");
message->append(Token::Name(actual));
message->append(", was expecting ");
message->append(Token::Name(Token::KindAndSubkind(expected_kind, Token::Subkind::kNone)));
return message;
}
return nullptr;
};
}
static auto IdentifierOfSubkind(Token::Subkind expected_subkind) {
return [expected_subkind](Token::KindAndSubkind actual) -> std::unique_ptr<std::string> {
auto expected = Token::KindAndSubkind(Token::Kind::kIdentifier, expected_subkind);
if (actual.combined() != expected.combined()) {
auto message = std::make_unique<std::string>("unexpected identifier ");
message->append(Token::Name(actual));
message->append(", was expecting ");
message->append(Token::Name(Token::KindAndSubkind(Token::Kind::kIdentifier, Token::Subkind::kNone)));
return message;
}
return nullptr;
};
}
bool LookupHandleSubtype(const raw::Identifier* identifier,
std::optional<types::HandleSubtype>* out_handle_subtype);
decltype(nullptr) Fail();
decltype(nullptr) Fail(std::string_view message);
std::unique_ptr<raw::Identifier> ParseIdentifier(bool is_discarded = false);
std::unique_ptr<raw::CompoundIdentifier> ParseCompoundIdentifier();
std::unique_ptr<raw::StringLiteral> ParseStringLiteral();
std::unique_ptr<raw::NumericLiteral> ParseNumericLiteral();
std::unique_ptr<raw::TrueLiteral> ParseTrueLiteral();
std::unique_ptr<raw::FalseLiteral> ParseFalseLiteral();
std::unique_ptr<raw::Literal> ParseLiteral();
std::unique_ptr<raw::Ordinal> ParseOrdinal();
std::unique_ptr<raw::Constant> ParseConstant();
std::unique_ptr<raw::Attribute> ParseAttribute();
std::unique_ptr<raw::Attribute> ParseDocComment();
std::unique_ptr<raw::AttributeList> ParseAttributeList(std::unique_ptr<raw::Attribute> doc_comment, ASTScope& scope);
std::unique_ptr<raw::AttributeList> MaybeParseAttributeList();
std::unique_ptr<raw::Using> ParseUsing();
std::unique_ptr<raw::TypeConstructor> ParseTypeConstructor();
std::unique_ptr<raw::BitsMember> ParseBitsMember();
std::unique_ptr<raw::BitsDeclaration>
ParseBitsDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::ConstDeclaration>
ParseConstDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::EnumMember> ParseEnumMember();
std::unique_ptr<raw::EnumDeclaration>
ParseEnumDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::Parameter> ParseParameter();
std::unique_ptr<raw::ParameterList> ParseParameterList();
std::unique_ptr<raw::InterfaceMethod> ParseProtocolEvent(
std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope,
std::unique_ptr<raw::Ordinal> ordinal);
std::unique_ptr<raw::InterfaceMethod> ParseProtocolMethod(
std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope,
std::unique_ptr<raw::Ordinal> ordinal,
std::unique_ptr<raw::Identifier> method_name);
// ParseProtocolMember parses any one protocol member, i.e. an event,
// a method, or a compose stanza.
void ParseProtocolMember(
std::unique_ptr<raw::AttributeList> attributes, ASTScope& scope,
std::vector<std::unique_ptr<raw::ComposeProtocol>>* composed_protocols,
std::vector<std::unique_ptr<raw::InterfaceMethod>>* methods);
std::unique_ptr<raw::InterfaceDeclaration>
ParseProtocolDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::StructMember> ParseStructMember();
std::unique_ptr<raw::StructDeclaration>
ParseStructDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::TableMember>
ParseTableMember();
std::unique_ptr<raw::TableDeclaration>
ParseTableDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::UnionMember> ParseUnionMember();
std::unique_ptr<raw::UnionDeclaration>
ParseUnionDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::XUnionMember> ParseXUnionMember();
std::unique_ptr<raw::XUnionDeclaration>
ParseXUnionDeclaration(std::unique_ptr<raw::AttributeList> attributes, ASTScope&);
std::unique_ptr<raw::File> ParseFile();
std::map<std::string_view, types::HandleSubtype> handle_subtype_table_;
Lexer* lexer_;
ErrorReporter* error_reporter_;
// The stack of information interesting to the currently active ASTScope
// objects.
std::vector<raw::SourceElement> active_ast_scopes_;
// The most recent start of a "gap" - the uninteresting source prior to the
// beginning of a token (usually mostly containing whitespace).
SourceLocation gap_start_;
// Indicates that the last element was the start of a gap, and that the
// scope should be updated accordingly.
bool last_was_gap_start_ = false;
// Suppress updating the gap for the current Scope. Useful when
// you don't know whether a scope is going to be interesting lexically, and
// you have to decide at runtime.
bool suppress_gap_checks_ = false;
// The token before last_token_ (below).
Token previous_token_;
Token last_token_;
};
} // namespace fidl
#endif // ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_PARSER_H_