mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:50:14 +01:00
This patch tries to make everything adhere to the Google declaration order. This is tricky as there doesn't appear to be a `clang-tidy` check that helps us here at all. One of the complex cases are the `enum`-wrapping classes we use. These need to have a *public* conversion operator to the nested `enum` type to support `switch` statements and the like. However, this makes it impossible to fully respect the Google declaration order. We need to define the enum type before the public API in order to use it in contexts like the conversion operator. Even defining out-of-line won't help avoid this. It is weird to have a type in the public API that is private, but again this is only intended to be used for implicit conversions within a `switch` statement or a `case` label. In one case, we had the non-conforming order of declaration. I've added a comment to explain why there. In the other case, the conversion operator is actually *private* rather than public, which doesn't actually work in practice. I've made this public and moved the declaration order to match with a matching comment.
357 lines
13 KiB
C++
357 lines
13 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 PARSER_PARSE_TREE_H_
|
|
#define PARSER_PARSE_TREE_H_
|
|
|
|
#include <iterator>
|
|
|
|
#include "diagnostics/diagnostic_emitter.h"
|
|
#include "lexer/tokenized_buffer.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/iterator.h"
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "parser/parse_node_kind.h"
|
|
|
|
namespace Carbon {
|
|
|
|
// A tree of parsed tokens based on the language grammar.
|
|
//
|
|
// This is a purely syntactic parse tree without any semantics yet attached. It
|
|
// is based on the token stream and the grammar of the language without even
|
|
// name lookup.
|
|
//
|
|
// The tree is designed to make depth-first traversal especially efficient, with
|
|
// postorder and reverse postorder (RPO, a topological order) not even requiring
|
|
// extra state.
|
|
//
|
|
// The nodes of the tree follow a flyweight pattern and are handles into the
|
|
// tree. The tree itself must be available to query for information about those
|
|
// nodes.
|
|
//
|
|
// Nodes also have a precise one-to-one correspondence to tokens from the parsed
|
|
// token stream. Each node can be thought of as the tree-position of a
|
|
// particular token from the stream.
|
|
//
|
|
// The tree is immutable once built, but is designed to support reasonably
|
|
// efficient patterns that build a new tree with a specific transformation
|
|
// applied.
|
|
class ParseTree {
|
|
public:
|
|
class Node;
|
|
class PostorderIterator;
|
|
class SiblingIterator;
|
|
|
|
// Parses the token buffer into a `ParseTree`.
|
|
//
|
|
// This is the factory function which is used to build parse trees.
|
|
static auto Parse(TokenizedBuffer& tokens, DiagnosticEmitter& emitter)
|
|
-> ParseTree;
|
|
|
|
// Tests whether there are any errors in the parse tree.
|
|
[[nodiscard]] auto HasErrors() const -> bool { return has_errors; }
|
|
|
|
// Returns the number of nodes in this parse tree.
|
|
[[nodiscard]] auto Size() const -> int { return node_impls.size(); }
|
|
|
|
// Returns an iterable range over the parse tree nodes in depth-first
|
|
// postorder.
|
|
[[nodiscard]] auto Postorder() const
|
|
-> llvm::iterator_range<PostorderIterator>;
|
|
|
|
// Returns an iterable range over the parse tree node and all of its
|
|
// descendants in depth-first postorder.
|
|
[[nodiscard]] auto Postorder(Node n) const
|
|
-> llvm::iterator_range<PostorderIterator>;
|
|
|
|
// Returns an iterable range over the direct children of a node in the parse
|
|
// tree. This is a forward range, but is constant time to increment. The order
|
|
// of children is the same as would be found in a reverse postorder traversal.
|
|
[[nodiscard]] auto Children(Node n) const
|
|
-> llvm::iterator_range<SiblingIterator>;
|
|
|
|
// Returns an iterable range over the roots of the parse tree. This is a
|
|
// forward range, but is constant time to increment. The order of roots is the
|
|
// same as would be found in a reverse postorder traversal.
|
|
[[nodiscard]] auto Roots() const -> llvm::iterator_range<SiblingIterator>;
|
|
|
|
// Tests whether a particular node contains an error and may not match the
|
|
// full expected structure of the grammar.
|
|
[[nodiscard]] auto HasErrorInNode(Node n) const -> bool;
|
|
|
|
// Returns the kind of the given parse tree node.
|
|
[[nodiscard]] auto GetNodeKind(Node n) const -> ParseNodeKind;
|
|
|
|
// Returns the token the given parse tree node models.
|
|
[[nodiscard]] auto GetNodeToken(Node n) const -> TokenizedBuffer::Token;
|
|
|
|
// Returns the text backing the token for the given node.
|
|
//
|
|
// This is a convenience method for chaining from a node through its token to
|
|
// the underlying source text.
|
|
[[nodiscard]] auto GetNodeText(Node n) const -> llvm::StringRef;
|
|
|
|
// Prints a description of the parse tree to the provided `raw_ostream`.
|
|
//
|
|
// While the parse tree is represented as a postorder sequence, we print it in
|
|
// preorder to make it easier to visualize and read. The node indices are the
|
|
// postorder indices. The print out represents each node as a YAML record,
|
|
// with children nested within it.
|
|
//
|
|
// A single node without children is formatted as:
|
|
// ```
|
|
// {node_index: 0, kind: 'foo', text: '...'}
|
|
// ```
|
|
// A node with two children, one of them with an error:
|
|
// ```
|
|
// {node_index: 2, kind: 'foo', text: '...', children: [
|
|
// {node_index: 0, kind: 'bar', text: '...', has_error: yes},
|
|
// {node_index: 1, kind: 'baz', text: '...'}]}
|
|
// ```
|
|
// The top level is formatted as an array of these nodes.
|
|
// ```
|
|
// [
|
|
// {node_index: 1, kind: 'foo', text: '...'},
|
|
// {node_index: 0, kind: 'foo', text: '...'},
|
|
// ...
|
|
// ]
|
|
// ```
|
|
//
|
|
// This can be parsed as YAML using tools like `python-yq` combined with `jq`
|
|
// on the command line. The format is also reasonably amenable to other
|
|
// line-oriented shell tools from `grep` to `awk`.
|
|
auto Print(llvm::raw_ostream& output) const -> void;
|
|
|
|
// Verifies the parse tree structure.
|
|
//
|
|
// This tries to check any invariants of the parse tree structure and write
|
|
// out information about it to stderr. Returns false if anything fails to
|
|
// verify. This is primarily intended to be used as a debugging aid. A typical
|
|
// usage is to `assert` on the result. This routine doesn't directly assert so
|
|
// that it can be used even when asserts are disabled or within a debugger.
|
|
[[nodiscard]] auto Verify() const -> bool;
|
|
|
|
private:
|
|
class Parser;
|
|
friend Parser;
|
|
|
|
// The in-memory representation of data used for a particular node in the
|
|
// tree.
|
|
struct NodeImpl {
|
|
explicit NodeImpl(ParseNodeKind k, TokenizedBuffer::Token t,
|
|
int subtree_size_arg)
|
|
: kind(k), token(t), subtree_size(subtree_size_arg) {}
|
|
|
|
// The kind of this node. Note that this is only a single byte.
|
|
ParseNodeKind kind;
|
|
|
|
// We have 3 bytes of padding here that we can pack flags or other compact
|
|
// data into.
|
|
|
|
// Whether this node is or contains a parse error.
|
|
//
|
|
// When this is true, this node and its children may not have the expected
|
|
// grammatical production structure. Prior to reasoning about any specific
|
|
// subtree structure, this flag must be checked.
|
|
//
|
|
// Not every node in the path from the root to an error will have this field
|
|
// set to true. However, any node structure that fails to conform to the
|
|
// expected grammatical production will be contained within a subtree with
|
|
// this flag set. Whether parents of that subtree also have it set is
|
|
// optional (and will depend on the particular parse implementation
|
|
// strategy). The goal is that you can rely on grammar-based structural
|
|
// invariants *until* you encounter a node with this set.
|
|
bool has_error = false;
|
|
|
|
// The token root of this node.
|
|
TokenizedBuffer::Token token;
|
|
|
|
// The size of this node's subtree of the parse tree. This is the number of
|
|
// nodes (and thus tokens) that are covered by this node (and its
|
|
// descendents) in the parse tree.
|
|
//
|
|
// During a *reverse* postorder (RPO) traversal of the parse tree, this can
|
|
// also be thought of as the offset to the next non-descendant node. When
|
|
// this node is not the first child of its parent (which is the last child
|
|
// visited in RPO), that is the offset to the next sibling. When this node
|
|
// *is* the first child of its parent, this will be an offset to the node's
|
|
// parent's next sibling, or if it the parent is also a first child, the
|
|
// grandparent's next sibling, and so on.
|
|
//
|
|
// This field should always be a positive integer as at least this node is
|
|
// part of its subtree.
|
|
int32_t subtree_size;
|
|
};
|
|
|
|
static_assert(sizeof(NodeImpl) == 12,
|
|
"Unexpected size of node implementation!");
|
|
|
|
// Wires up the reference to the tokenized buffer. The global `parse` routine
|
|
// should be used to actually parse the tokens into a tree.
|
|
explicit ParseTree(TokenizedBuffer& tokens_arg) : tokens(&tokens_arg) {}
|
|
|
|
// Depth-first postorder sequence of node implementation data.
|
|
llvm::SmallVector<NodeImpl, 0> node_impls;
|
|
|
|
TokenizedBuffer* tokens;
|
|
|
|
// Indicates if any errors were encountered while parsing.
|
|
//
|
|
// This doesn't indicate how much of the tree is structurally accurate with
|
|
// respect to the grammar. That can be identified by looking at the `HasError`
|
|
// flag for a given node (see above for details). This simply indicates that
|
|
// some errors were encountered somewhere. A key implication is that when this
|
|
// is true we do *not* have the expected 1:1 mapping between tokens and parsed
|
|
// nodes as some tokens may have been skipped.
|
|
bool has_errors = false;
|
|
};
|
|
|
|
// A lightweight handle representing a node in the tree.
|
|
//
|
|
// Objects of this type are small and cheap to copy and store. They don't
|
|
// contain any of the information about the node, and serve as a handle that
|
|
// can be used with the underlying tree to query for detailed information.
|
|
//
|
|
// That said, nodes can be compared and are part of a depth-first pre-order
|
|
// sequence across all nodes in the parse tree.
|
|
class ParseTree::Node {
|
|
public:
|
|
// Node handles are default constructable, but such a node cannot be used
|
|
// for anything. It just allows it to be initialized later through
|
|
// assignment. Any other operation on a default constructed node is an
|
|
// error.
|
|
Node() = default;
|
|
|
|
friend auto operator==(Node lhs, Node rhs) -> bool {
|
|
return lhs.index == rhs.index;
|
|
}
|
|
friend auto operator!=(Node lhs, Node rhs) -> bool {
|
|
return lhs.index != rhs.index;
|
|
}
|
|
friend auto operator<(Node lhs, Node rhs) -> bool {
|
|
return lhs.index < rhs.index;
|
|
}
|
|
friend auto operator<=(Node lhs, Node rhs) -> bool {
|
|
return lhs.index <= rhs.index;
|
|
}
|
|
friend auto operator>(Node lhs, Node rhs) -> bool {
|
|
return lhs.index > rhs.index;
|
|
}
|
|
friend auto operator>=(Node lhs, Node rhs) -> bool {
|
|
return lhs.index >= rhs.index;
|
|
}
|
|
|
|
// Returns an opaque integer identifier of the node in the tree. Clients
|
|
// should not expect any particular semantics from this value.
|
|
//
|
|
// FIXME: Maybe we can switch to stream operator overloads?
|
|
[[nodiscard]] auto GetIndex() const -> int { return index; }
|
|
|
|
private:
|
|
friend ParseTree;
|
|
friend Parser;
|
|
friend PostorderIterator;
|
|
friend SiblingIterator;
|
|
|
|
// Constructs a node with a specific index into the parse tree's postorder
|
|
// sequence of node implementations.
|
|
explicit Node(int index_arg) : index(index_arg) {}
|
|
|
|
// The index of this node's implementation in the postorder sequence.
|
|
int32_t index;
|
|
};
|
|
|
|
// A random-access iterator to the depth-first postorder sequence of parse nodes
|
|
// in the parse tree. It produces `ParseTree::Node` objects which are opaque
|
|
// handles and must be used in conjunction with the `ParseTree` itself.
|
|
class ParseTree::PostorderIterator
|
|
: public llvm::iterator_facade_base<PostorderIterator,
|
|
std::random_access_iterator_tag, Node,
|
|
int, Node*, Node> {
|
|
public:
|
|
// Default construction is only provided to satisfy iterator requirements. It
|
|
// produces an unusable iterator, and you must assign a valid iterator to it
|
|
// before performing any operations.
|
|
PostorderIterator() = default;
|
|
|
|
auto operator==(const PostorderIterator& rhs) const -> bool {
|
|
return node == rhs.node;
|
|
}
|
|
auto operator<(const PostorderIterator& rhs) const -> bool {
|
|
return node < rhs.node;
|
|
}
|
|
|
|
auto operator*() const -> Node { return node; }
|
|
|
|
auto operator-(const PostorderIterator& rhs) const -> int {
|
|
return node.index - rhs.node.index;
|
|
}
|
|
|
|
auto operator+=(int offset) -> PostorderIterator& {
|
|
node.index += offset;
|
|
return *this;
|
|
}
|
|
auto operator-=(int offset) -> PostorderIterator& {
|
|
node.index -= offset;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
friend class ParseTree;
|
|
|
|
explicit PostorderIterator(Node n) : node(n) {}
|
|
|
|
Node node;
|
|
};
|
|
|
|
// A forward iterator across the silbings at a particular level in the parse
|
|
// tree. It produces `ParseTree::Node` objects which are opaque handles and must
|
|
// be used in conjunction with the `ParseTree` itself.
|
|
//
|
|
// While this is a forward iterator and may not have good locality within the
|
|
// `ParseTree` data structure, it is still constant time to increment and
|
|
// suitable for algorithms relying on that property.
|
|
//
|
|
// The siblings are discovered through a reverse postorder (RPO) tree traversal
|
|
// (which is made constant time through cached distance information), and so the
|
|
// relative order of siblings matches their RPO order.
|
|
class ParseTree::SiblingIterator
|
|
: public llvm::iterator_facade_base<
|
|
SiblingIterator, std::forward_iterator_tag, Node, int, Node*, Node> {
|
|
public:
|
|
SiblingIterator() = default;
|
|
|
|
auto operator==(const SiblingIterator& rhs) const -> bool {
|
|
return node == rhs.node;
|
|
}
|
|
auto operator<(const SiblingIterator& rhs) const -> bool {
|
|
// Note that child iterators walk in reverse compared to the postorder
|
|
// index.
|
|
return node > rhs.node;
|
|
}
|
|
|
|
auto operator*() const -> Node { return node; }
|
|
|
|
using iterator_facade_base::operator++;
|
|
auto operator++() -> SiblingIterator& {
|
|
node.index -= std::abs(tree->node_impls[node.index].subtree_size);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
friend class ParseTree;
|
|
|
|
explicit SiblingIterator(const ParseTree& tree_arg, Node n)
|
|
: tree(&tree_arg), node(n) {}
|
|
|
|
const ParseTree* tree;
|
|
|
|
Node node;
|
|
};
|
|
|
|
} // namespace Carbon
|
|
|
|
#endif // PARSER_PARSE_TREE_H_
|