Files
carbon-lang/toolchain/parse/node_kind.h
T
2e97f27b8d Typed wrappers around parse tree nodes (#3534)
These are intended to allow the structure of a parse tree node to be
described more precisely in code, to support these use cases:

- Automated checking that the parse tree conforms to the expected
structure. (Added to `Tree::Verify`.)
- Easier reading and understanding of the structure of the parse tree by
toolchain developers. (See `parse/typed_nodes.h`.)
- Easier navigation of the parse tree, for example for tooling uses and
for use when forming diagnostics.

On this last point, an object representing the file may be inspecting
using `Tree::ExtractFile`, as in:
```
auto file = tree->ExtractFile();
for (AnyDeclId decl_id : file.decls) {
  // `decl_id` is convertible to a `NodeId`.
  if (std::optional<FunctionDecl> fn_decl =
      tree->ExtractAs<FunctionDecl>(decl_id)) {
    // fn_decl->params is a `TuplePatternId` (which extends `NodeId`)
    // that is guaranteed to reference a `TuplePattern`.
    std::optional<TuplePattern> params = tree->Extract(fn_decl->params);
    // `params` has a value unless there was an error in that node.
  } else if (auto class_def = tree->ExtractAs<ClassDefinition>(decl_id)) {
    // ...
  }
}
```

The `Extract...` functions collect the child nodes into the typed parse
node's fields (internally using a `Tree::SiblingIterator`) for easy
access. However, this is not as fast as directly observing the tree
structure using the postorder strategy being used by the check stage.

These functions rely on using struct reflection on the typed parse node
definitions from `parse/typed_nodes.h` to get the expected structure of
child nodes and then populate them.

Note that validating these in `Tree::Verify` adds significant cost to
it, and is currently included in the parsing stage. Without this change,
a 10 mloc test case of lex & parse takes 4.129 s ± 0.041 s. With this
change, it takes 5.768 s ± 0.036 s.

This builds upon and completes #3393.

Co-authored-by: Richard Smith <richard@metafoo.co.uk>

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
2023-12-22 22:14:11 +00:00

127 lines
4.0 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CARBON_TOOLCHAIN_PARSE_NODE_KIND_H_
#define CARBON_TOOLCHAIN_PARSE_NODE_KIND_H_
#include <cstdint>
#include "common/enum_base.h"
#include "llvm/ADT/BitmaskEnum.h"
#include "toolchain/lex/token_kind.h"
namespace Carbon::Parse {
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
// Represents a set of keyword modifiers, using a separate bit per modifier.
//
// We expect this to grow, so are using a bigger size than needed.
// NOLINTNEXTLINE(performance-enum-size)
enum class NodeCategory : uint32_t {
Decl = 1 << 0,
Expr = 1 << 1,
Modifier = 1 << 2,
NameComponent = 1 << 3,
Pattern = 1 << 4,
Statement = 1 << 5,
None = 0,
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Statement)
};
inline auto operator!(NodeCategory k) -> bool {
return !static_cast<uint32_t>(k);
}
CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint8_t) {
#define CARBON_PARSE_NODE_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
#include "toolchain/parse/node_kind.def"
};
// A class wrapping an enumeration of the different kinds of nodes in the parse
// tree.
class NodeKind : public CARBON_ENUM_BASE(NodeKind) {
public:
#define CARBON_PARSE_NODE_KIND(Name) CARBON_ENUM_CONSTANT_DECL(Name)
#include "toolchain/parse/node_kind.def"
// Validates that a `parse_node_kind` parser node can be generated for a
// `lex_token_kind` lexer token.
auto CheckMatchesTokenKind(Lex::TokenKind lex_token_kind, bool has_error)
-> void;
// Returns true if the node is bracketed; otherwise, child_count is used.
auto has_bracket() const -> bool;
// Returns the bracketing node kind for the current node kind. Requires that
// has_bracket is true.
auto bracket() const -> NodeKind;
// Returns the number of children that the node must have, often 0. Requires
// that has_bracket is false.
auto child_count() const -> int32_t;
// Returns which categories this node kind is in.
auto category() const -> NodeCategory;
using EnumBase::Create;
class Definition;
// Provides a definition for this parse node kind. Should only be called
// once, to construct the kind as part of defining it in `typed_nodes.h`.
constexpr auto Define(NodeCategory category = NodeCategory::None) const
-> Definition;
private:
// Looks up the definition for this instruction kind.
auto definition() const -> const Definition&;
};
#define CARBON_PARSE_NODE_KIND(Name) \
CARBON_ENUM_CONSTANT_DEFINITION(NodeKind, Name)
#include "toolchain/parse/node_kind.def"
// We expect the parse node kind to fit compactly into 8 bits.
static_assert(sizeof(NodeKind) == 1, "Kind objects include padding!");
// A definition of a parse node kind. This is a NodeKind value, plus
// ancillary data such as the name to use for the node kind in LLVM IR. These
// are not copyable, and only one instance of this type is expected to exist per
// parse node kind, specifically `TypedNode::Kind`. Use `NodeKind` instead as a
// thin wrapper around a parse node kind index.
class NodeKind::Definition : public NodeKind {
public:
// Not copyable.
Definition(const Definition&) = delete;
auto operator=(const Definition&) -> Definition& = delete;
// Returns which categories this node kind is in.
constexpr auto category() const -> NodeCategory { return category_; }
private:
friend class NodeKind;
constexpr Definition(NodeKind kind, NodeCategory category)
: NodeKind(kind), category_(category) {}
NodeCategory category_;
};
constexpr auto NodeKind::Define(NodeCategory category) const -> Definition {
return Definition(*this, category);
}
// HasKindMember<T> is true if T has a `static const NodeKind::Definition Kind`
// member.
template <typename T, typename KindType = const NodeKind::Definition*>
inline constexpr bool HasKindMember = false;
template <typename T>
inline constexpr bool HasKindMember<T, decltype(&T::Kind)> = true;
} // namespace Carbon::Parse
#endif // CARBON_TOOLCHAIN_PARSE_NODE_KIND_H_