mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Previously, users of TokenSyntax would always deal with RC<TokenSyntax> which is a subclass of RawSyntax. Instead, provide TokenSyntax as a fully-realized Syntax node, that will always exist as a leaf in the Syntax tree. This hides the implementation detail of RawSyntax and SyntaxData completely from clients of libSyntax, and paves the way for future generation of Syntax nodes.
285 lines
9.8 KiB
C++
285 lines
9.8 KiB
C++
//===--- SyntaxData.h - Swift Syntax Data Interface -------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the SyntaxData interface, the type for the instance
|
|
// data for Syntax nodes.
|
|
//
|
|
// Effectively, these provide two main things to a Syntax node - parental
|
|
// relationships and caching for its children.
|
|
//
|
|
// A SyntaxData contains at least a strong reference to the RawSyntax,
|
|
// from which most information comes, and additionally a weak reference to
|
|
// its parent and the "index" at which it occurs in its parent. These were
|
|
// originally intended to have the important public APIs for structured
|
|
// editing but now contain no significant or public API; for those, see the
|
|
// Syntax type. These are purely to contain data, hence the name.
|
|
//
|
|
// Conceptually, SyntaxData add the characteristic of specific identity in a
|
|
// piece of Swift source code. While the RawSyntax for the integer literal
|
|
// token '1' can be reused anywhere a '1' occurs and has identical formatting,
|
|
// a SyntaxData represents *a* specific '1' at a particular location in
|
|
// Swift source.
|
|
//
|
|
// These are effectively internal implementation. For all public APIs, look
|
|
// for the type without "Data" in its name. For example, a StructDeclSyntaxData
|
|
// expresses its API through the wrapping StructDeclSyntax type.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
#ifndef SWIFT_SYNTAX_SYNTAXDATA_H
|
|
#define SWIFT_SYNTAX_SYNTAXDATA_H
|
|
|
|
#include "swift/Syntax/AtomicCache.h"
|
|
#include "swift/Syntax/RawSyntax.h"
|
|
#include "swift/Syntax/References.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include <atomic>
|
|
|
|
namespace swift {
|
|
namespace syntax {
|
|
|
|
/// The class for holding parented syntax.
|
|
///
|
|
/// This structure should not contain significant public
|
|
/// API or internal modification API.
|
|
///
|
|
/// This is only for holding a strong reference to the RawSyntax, a weak
|
|
/// reference to the parent, and, in subclasses, lazily created strong
|
|
/// references to non-terminal child nodes.
|
|
class SyntaxData final : public llvm::ThreadSafeRefCountedBase<SyntaxData> {
|
|
friend struct SyntaxFactory;
|
|
#define SYNTAX(Id, Parent) friend class Id##Syntax;
|
|
#include "swift/Syntax/SyntaxKinds.def"
|
|
|
|
using RootDataPair = std::pair<RC<SyntaxData>, RC<SyntaxData>>;
|
|
|
|
llvm::SmallVector<AtomicCache<SyntaxData>, 10> Children;
|
|
|
|
public:
|
|
/// The shared raw syntax representing this syntax data node.
|
|
const RC<RawSyntax> Raw;
|
|
|
|
/// The parent of this syntax.
|
|
///
|
|
/// WARNING! Do not access this directly. Use getParent(),
|
|
/// which enforces nullptr checking.
|
|
const SyntaxData *Parent;
|
|
|
|
/// The index into the parent's child layout.
|
|
///
|
|
/// If there is no parent, this is 0.
|
|
const CursorIndex IndexInParent;
|
|
|
|
SyntaxData(RC<RawSyntax> Raw, const SyntaxData *Parent = nullptr,
|
|
CursorIndex IndexInParent = 0)
|
|
: Raw(Raw), Parent(Parent), IndexInParent(IndexInParent) {
|
|
if (Raw) {
|
|
for (size_t I = 0; I < Raw->Layout.size(); ++I) {
|
|
Children.push_back(AtomicCache<SyntaxData>());
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Constructs a SyntaxNode by replacing `self` and recursively building
|
|
/// the parent chain up to the root.
|
|
template <typename SyntaxNode>
|
|
SyntaxNode replaceSelf(const RC<RawSyntax> NewRaw) const {
|
|
auto NewRootAndData = replaceSelf(NewRaw);
|
|
return { NewRootAndData.first, NewRootAndData.second.get() };
|
|
}
|
|
|
|
/// With a new RawSyntax node, create a new node from this one and
|
|
/// recursively rebuild the parental chain up to the root.
|
|
///
|
|
/// DO NOT expose this as public API.
|
|
RootDataPair replaceSelf(const RC<RawSyntax> NewRaw) const {
|
|
if (hasParent()) {
|
|
auto NewRootAndParent =
|
|
getParent().getValue()->replaceChild(NewRaw, IndexInParent);
|
|
auto NewMe = NewRootAndParent.second->getChild(IndexInParent);
|
|
return { NewRootAndParent.first, NewMe.get() };
|
|
} else {
|
|
auto NewMe = make(NewRaw, nullptr, IndexInParent);
|
|
return { NewMe, NewMe.get() };
|
|
}
|
|
}
|
|
|
|
/// Create the data for a child node with the raw syntax in our layout
|
|
/// at the provided index.
|
|
/// DO NOT expose this as public API.
|
|
RC<SyntaxData> realizeSyntaxNode(CursorIndex Index) const {
|
|
auto RawChild = Raw->Layout.at(Index);
|
|
return SyntaxData::make(RawChild, this, Index);
|
|
}
|
|
|
|
/// Replace a child in the raw syntax and recursively rebuild the
|
|
/// parental chain up to the root.
|
|
///
|
|
/// DO NOT expose this as public API.
|
|
template <typename SyntaxNode, typename CursorType>
|
|
SyntaxNode replaceChild(const RC<RawSyntax> RawChild,
|
|
CursorType ChildCursor) const {
|
|
auto NewRootAndParent = replaceChild(RawChild, ChildCursor);
|
|
return SyntaxNode {
|
|
NewRootAndParent.first,
|
|
NewRootAndParent.second.get()
|
|
};
|
|
}
|
|
|
|
/// Replace a child in the raw syntax and recursively rebuild the
|
|
/// parental chain up to the root.
|
|
///
|
|
/// DO NOT expose this as public API.
|
|
template <typename CursorType>
|
|
RootDataPair replaceChild(const RC<RawSyntax> RawChild,
|
|
CursorType ChildCursor) const {
|
|
auto NewRaw = Raw->replaceChild(ChildCursor, RawChild);
|
|
return replaceSelf(NewRaw);
|
|
}
|
|
|
|
public:
|
|
|
|
static RC<SyntaxData> make(RC<RawSyntax> Raw,
|
|
const SyntaxData *Parent = nullptr,
|
|
CursorIndex IndexInParent = 0);
|
|
|
|
/// Returns the raw syntax node for this syntax node.
|
|
const RC<RawSyntax> getRaw() const {
|
|
return Raw;
|
|
}
|
|
|
|
/// Returns the kind of syntax node this is.
|
|
SyntaxKind getKind() const {
|
|
return Raw->Kind;
|
|
}
|
|
|
|
/// Return the parent syntax if there is one.
|
|
llvm::Optional<const SyntaxData *> getParent() const {
|
|
if (Parent != nullptr) {
|
|
return Parent;
|
|
}
|
|
return llvm::None;
|
|
}
|
|
|
|
/// Returns true if this syntax node has a parent.
|
|
bool hasParent() const {
|
|
return Parent != nullptr;
|
|
}
|
|
|
|
/// Returns the child index of this node in its parent, if it has a parent,
|
|
/// otherwise 0.
|
|
size_t getIndexInParent() const {
|
|
return IndexInParent;
|
|
}
|
|
|
|
/// Returns the number of children this SyntaxData represents.
|
|
size_t getNumChildren() const {
|
|
return Raw->Layout.size();
|
|
}
|
|
|
|
/// Gets the child at the index specified by the provided cursor,
|
|
/// lazily creating it if necessary.
|
|
template <typename CursorType>
|
|
RC<SyntaxData> getChild(CursorType Cursor) const {
|
|
return getChild((size_t)cursorIndex(Cursor));
|
|
}
|
|
|
|
/// Gets the child at the specified index in this data's children array.
|
|
/// Why do we need this?
|
|
/// - SyntaxData nodes should have pointer identity.
|
|
/// - We only want to construct parented, realized child nodes as
|
|
/// SyntaxData when asked.
|
|
///
|
|
/// For example, if we have a ReturnStmtSyntax, and ask for its returned
|
|
/// expression for the first time with getExpression(), two nodes can race
|
|
/// to create and set the cached expression.
|
|
///
|
|
/// Looking at an example - say we have a SyntaxData.
|
|
///
|
|
/// SyntaxData = {
|
|
/// RC<RawSyntax> Raw = {
|
|
/// RC<RawTokenSyntax> { SyntaxKind::Token, tok::return_kw, "return" },
|
|
/// RC<RawSyntax> { SyntaxKind::SomeExpression, ... }
|
|
/// }
|
|
/// llvm::SmallVector<AtomicCache<SyntaxData>, 10> Children {
|
|
/// AtomicCache<SyntaxData> { RC<SyntaxData> = nullptr; },
|
|
/// AtomicCache<SyntaxData> { RC<SyntaxData> = nullptr; },
|
|
/// }
|
|
/// }
|
|
///
|
|
/// If we wanted to safely create the 0th child, an instance of TokenSyntax,
|
|
/// then we ask the AtomicCache in that position to realize its value and
|
|
/// cache it. This is safe because AtomicCache only ever mutates its cache
|
|
/// one time -- the first initialization that wins a compare_exchange_strong.
|
|
RC<SyntaxData> getChild(size_t Index) const {
|
|
return Children[Index].getOrCreate([&]() {
|
|
return realizeSyntaxNode(Index);
|
|
});
|
|
}
|
|
|
|
/// Returns true if the data node represents type syntax.
|
|
bool isType() const;
|
|
|
|
/// Returns true if the data node represents statement syntax.
|
|
bool isStmt() const;
|
|
|
|
/// Returns true if the data node represents declaration syntax.
|
|
bool isDecl() const;
|
|
|
|
/// Returns true if the data node represents expression syntax.
|
|
bool isExpr() const;
|
|
|
|
/// Returns true if the data node represents pattern syntax.
|
|
bool isPattern() const;
|
|
|
|
/// Returns true if this syntax is of some "unknown" kind.
|
|
bool isUnknown() const;
|
|
|
|
/// Dump a debug description of the syntax data for debugging to
|
|
/// standard error.
|
|
void dump(llvm::raw_ostream &OS) const;
|
|
};
|
|
|
|
} // end namespace syntax
|
|
} // end namespace swift
|
|
|
|
// DenseMapInfo for RC<SyntaxData>, used for a Syntax Node -> lib/AST mapping.
|
|
namespace llvm {
|
|
using SD = swift::syntax::SyntaxData;
|
|
using RCSD = swift::RC<SD>;
|
|
template <> struct llvm::DenseMapInfo<RCSD> {
|
|
static inline RCSD getEmptyKey() {
|
|
return SD::make(nullptr, nullptr, 0);
|
|
}
|
|
static inline RCSD getTombstoneKey() {
|
|
return SD::make(nullptr, nullptr, 0);
|
|
}
|
|
static unsigned getHashValue(const RCSD Value) {
|
|
unsigned H = 0;
|
|
H ^= DenseMapInfo<uintptr_t>::getHashValue(reinterpret_cast<const uintptr_t>(Value->Raw.get()));
|
|
H ^= DenseMapInfo<uintptr_t>::getHashValue(reinterpret_cast<const uintptr_t>(Value->Parent));
|
|
H ^= DenseMapInfo<swift::syntax::CursorIndex>::getHashValue(Value->getIndexInParent());
|
|
return H;
|
|
}
|
|
static bool isEqual(const RCSD LHS, const RCSD RHS) {
|
|
return LHS->getRaw().get() == RHS->getRaw().get() &&
|
|
LHS->getParent() == RHS->getParent() &&
|
|
LHS->getIndexInParent() == RHS->getIndexInParent();
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif // SWIFT_SYNTAX_SYNTAXDATA_H
|
|
|