mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
It turns out that the bitpacked Commons struct is actually fairly expensive because the CPU needs to apply bitmasks to fetch the IsToken and Presence flag. We've got padding space available, so we might as well properly align these boolean flags. Also on a source level, replace a couple of bit-restricted unsigned fields by their representing type (e.g. SyntaxKind). Finally, we can pull out the common bits to RawSyntax and have the Bits union only contain the token- or layout-specific fields. This also allows us to initialise these fields in the constructor's initialiser list (instead of in the initialiser body). Lastly, change copyToArenaIfNecessary to work on a char *& and length, which allows us to initialise leading/trailing trivia/token text in the initialiser list and adjust if necessary later.
362 lines
12 KiB
C++
362 lines
12 KiB
C++
//===----------- SyntaxParsingContext.h -==============----------*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_PARSE_SYNTAXPARSINGCONTEXT_H
|
|
#define SWIFT_PARSE_SYNTAXPARSINGCONTEXT_H
|
|
|
|
#include "llvm/ADT/PointerUnion.h"
|
|
#include "swift/Basic/Debug.h"
|
|
#include "swift/Basic/SourceLoc.h"
|
|
#include "swift/Parse/ParsedRawSyntaxNode.h"
|
|
#include "swift/Parse/ParsedRawSyntaxRecorder.h"
|
|
|
|
namespace swift {
|
|
|
|
using namespace swift::syntax;
|
|
|
|
class ParsedSyntax;
|
|
class ParsedTokenSyntax;
|
|
struct ParsedTrivia;
|
|
class SourceFile;
|
|
enum class tok : uint8_t;
|
|
class Token;
|
|
class DiagnosticEngine;
|
|
|
|
namespace syntax {
|
|
enum class SyntaxKind : uint16_t;
|
|
}
|
|
|
|
enum class SyntaxContextKind {
|
|
Decl,
|
|
Stmt,
|
|
Expr,
|
|
Type,
|
|
Pattern,
|
|
Syntax,
|
|
};
|
|
|
|
enum class SyntaxNodeCreationKind {
|
|
/// This is for \c SyntaxParsingContext to collect the syntax data and create
|
|
/// a 'recorded' ParsedRawSyntaxNode object, which would be a result of
|
|
/// passing the index data to the \c SyntaxParseActions implementation.
|
|
Recorded,
|
|
/// This is for \c SyntaxParsingContext to collect the syntax data and create
|
|
/// a 'deferred' ParsedRawSyntaxNode object, which captures the data for a
|
|
/// \c SyntaxParseActions invocation to occur later.
|
|
///
|
|
/// This is intended to be used for when it's not clear what will be the final
|
|
/// syntax node in the current parsing context.
|
|
Deferred,
|
|
};
|
|
|
|
constexpr size_t SyntaxAlignInBits = 3;
|
|
|
|
/// RAII object which receive RawSyntax parts. On destruction, this constructs
|
|
/// a specified syntax node from received parts and propagate it to the parent
|
|
/// context.
|
|
///
|
|
/// e.g.
|
|
/// parseExprParen() {
|
|
/// SyntaxParsingContext LocalCtxt(SyntaxContext, SyntaxKind::ParenExpr);
|
|
/// consumeToken(tok::l_paren) // In consumeToken(), a RawTokenSyntax is
|
|
/// // added to the context.
|
|
/// parseExpr(); // On returning from parseExpr(), a Expr Syntax node is
|
|
/// // created and added to the context.
|
|
/// consumeToken(tok::r_paren)
|
|
/// // Now the context holds { '(' Expr ')' }.
|
|
/// // From these parts, it creates ParenExpr node and add it to the parent.
|
|
/// }
|
|
class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext {
|
|
public:
|
|
/// The shared data for all syntax parsing contexts with the same root.
|
|
/// This should be accessible from the root context only.
|
|
struct alignas(1 << SyntaxAlignInBits) RootContextData {
|
|
// The source file under parsing.
|
|
SourceFile &SF;
|
|
|
|
// Where to issue diagnostics.
|
|
DiagnosticEngine &Diags;
|
|
|
|
SourceManager &SourceMgr;
|
|
|
|
unsigned BufferID;
|
|
|
|
// Storage for Collected parts.
|
|
std::vector<ParsedRawSyntaxNode> Storage;
|
|
|
|
ParsedRawSyntaxRecorder Recorder;
|
|
|
|
llvm::BumpPtrAllocator ScratchAlloc;
|
|
|
|
RootContextData(SourceFile &SF, DiagnosticEngine &Diags,
|
|
SourceManager &SourceMgr, unsigned BufferID,
|
|
std::shared_ptr<SyntaxParseActions> spActions)
|
|
: SF(SF), Diags(Diags), SourceMgr(SourceMgr), BufferID(BufferID),
|
|
Recorder(std::move(spActions)) {}
|
|
};
|
|
|
|
private:
|
|
/// Indicates what action should be performed on the destruction of
|
|
/// SyntaxParsingContext
|
|
enum class AccumulationMode {
|
|
// Coerece the result to one of ContextKind.
|
|
// E.g. for ContextKind::Expr, passthroug if the result is CallExpr, whereas
|
|
// <UnknownExpr><SomeDecl /></UnknownDecl> for non Exprs.
|
|
CoerceKind,
|
|
|
|
// Construct a result Syntax with specified SyntaxKind.
|
|
CreateSyntax,
|
|
|
|
/// Construct a deferred raw node, to be recorded later.
|
|
DeferSyntax,
|
|
|
|
// Pass through all parts to the parent context.
|
|
Transparent,
|
|
|
|
// Discard all parts in the context.
|
|
Discard,
|
|
|
|
// The node has been found as incremental update and all parts shall be
|
|
// discarded.
|
|
SkippedForIncrementalUpdate,
|
|
|
|
// Construct SourceFile syntax to the specified SF.
|
|
Root,
|
|
|
|
// Invalid.
|
|
NotSet,
|
|
};
|
|
|
|
// When this context is a root, this points to an instance of RootContextData;
|
|
// When this context isn't a root, this points to the parent context.
|
|
const llvm::PointerUnion<RootContextData *, SyntaxParsingContext *>
|
|
RootDataOrParent;
|
|
|
|
// Reference to the
|
|
SyntaxParsingContext *&CtxtHolder;
|
|
|
|
RootContextData *RootData;
|
|
|
|
// Offet for 'Storage' this context owns from.
|
|
const size_t Offset;
|
|
|
|
// Operation on destruction.
|
|
AccumulationMode Mode = AccumulationMode::NotSet;
|
|
|
|
// Additional info depending on \c Mode.
|
|
union {
|
|
// For AccumulationMode::CreateSyntax; desired syntax node kind.
|
|
SyntaxKind SynKind;
|
|
// For AccumulationMode::CoerceKind; desired syntax node category.
|
|
SyntaxContextKind CtxtKind;
|
|
};
|
|
|
|
/// true if it's in backtracking context.
|
|
bool IsBacktracking = false;
|
|
|
|
/// true if ParsedSyntaxBuilders and ParsedSyntaxRecorder should create
|
|
/// deferred nodes
|
|
bool ShouldDefer = false;
|
|
|
|
// If false, context does nothing.
|
|
bool Enabled;
|
|
|
|
/// Create a syntax node using the tail \c N elements of collected parts and
|
|
/// replace those parts with the single result.
|
|
void createNodeInPlace(SyntaxKind Kind, size_t N,
|
|
SyntaxNodeCreationKind nodeCreateK);
|
|
|
|
ArrayRef<ParsedRawSyntaxNode> getParts() const {
|
|
return llvm::makeArrayRef(getStorage()).drop_front(Offset);
|
|
}
|
|
MutableArrayRef<ParsedRawSyntaxNode> getParts() {
|
|
return llvm::makeMutableArrayRef(getStorage().data(), getStorage().size()).drop_front(Offset);
|
|
}
|
|
|
|
ParsedRawSyntaxNode makeUnknownSyntax(SyntaxKind Kind,
|
|
MutableArrayRef<ParsedRawSyntaxNode> Parts);
|
|
ParsedRawSyntaxNode createSyntaxAs(SyntaxKind Kind,
|
|
MutableArrayRef<ParsedRawSyntaxNode> Parts,
|
|
SyntaxNodeCreationKind nodeCreateK);
|
|
Optional<ParsedRawSyntaxNode> bridgeAs(SyntaxContextKind Kind,
|
|
MutableArrayRef<ParsedRawSyntaxNode> Parts);
|
|
|
|
public:
|
|
/// Construct root context.
|
|
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF,
|
|
unsigned BufferID,
|
|
std::shared_ptr<SyntaxParseActions> SPActions);
|
|
|
|
/// Designated constructor for child context.
|
|
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder)
|
|
: RootDataOrParent(CtxtHolder), CtxtHolder(CtxtHolder),
|
|
RootData(CtxtHolder->RootData), Offset(RootData->Storage.size()),
|
|
IsBacktracking(CtxtHolder->IsBacktracking),
|
|
ShouldDefer(CtxtHolder->ShouldDefer),
|
|
Enabled(CtxtHolder->isEnabled()) {
|
|
assert(CtxtHolder->isTopOfContextStack() &&
|
|
"SyntaxParsingContext cannot have multiple children");
|
|
assert(CtxtHolder->Mode != AccumulationMode::SkippedForIncrementalUpdate &&
|
|
"Cannot create child context for a node loaded from the cache");
|
|
CtxtHolder = this;
|
|
}
|
|
|
|
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SyntaxContextKind Kind)
|
|
: SyntaxParsingContext(CtxtHolder) {
|
|
setCoerceKind(Kind);
|
|
}
|
|
|
|
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SyntaxKind Kind)
|
|
: SyntaxParsingContext(CtxtHolder) {
|
|
setCreateSyntax(Kind);
|
|
}
|
|
|
|
SyntaxParsingContext(const SyntaxParsingContext &other) = delete;
|
|
SyntaxParsingContext &operator=(const SyntaxParsingContext &other) = delete;
|
|
|
|
~SyntaxParsingContext();
|
|
|
|
/// Try looking up if an unmodified node exists at \p LexerOffset of the same
|
|
/// kind. If a node is found, replace the node that is currently being
|
|
/// constructed by the parsing context with the found node and return the
|
|
/// number of bytes the found node took up in the original source. The lexer
|
|
/// should pretend it has read these bytes and continue from the advanced
|
|
/// offset. If nothing is found \c 0 is returned.
|
|
size_t lookupNode(size_t LexerOffset, SourceLoc Loc);
|
|
|
|
void disable() { Enabled = false; }
|
|
bool isEnabled() const { return Enabled; }
|
|
bool isRoot() const { return RootDataOrParent.is<RootContextData*>(); }
|
|
bool isTopOfContextStack() const { return this == CtxtHolder; }
|
|
|
|
SyntaxParsingContext *getParent() const {
|
|
return RootDataOrParent.get<SyntaxParsingContext*>();
|
|
}
|
|
|
|
RootContextData *getRootData() { return RootData; }
|
|
|
|
const RootContextData *getRootData() const { return RootData; }
|
|
|
|
std::vector<ParsedRawSyntaxNode> &getStorage() { return getRootData()->Storage; }
|
|
|
|
const std::vector<ParsedRawSyntaxNode> &getStorage() const {
|
|
return getRootData()->Storage;
|
|
}
|
|
|
|
const SyntaxParsingContext *getRoot() const;
|
|
|
|
ParsedRawSyntaxRecorder &getRecorder() { return getRootData()->Recorder; }
|
|
const ParsedRawSyntaxRecorder &getRecorder() const {
|
|
return getRootData()->Recorder;
|
|
}
|
|
|
|
llvm::BumpPtrAllocator &getScratchAlloc() {
|
|
return getRootData()->ScratchAlloc;
|
|
}
|
|
|
|
/// Add RawSyntax to the parts.
|
|
void addRawSyntax(ParsedRawSyntaxNode &&Raw);
|
|
|
|
/// Add Token with Trivia to the parts.
|
|
void addToken(Token &Tok, StringRef LeadingTrivia, StringRef TrailingTrivia);
|
|
|
|
/// Add Syntax to the parts.
|
|
void addSyntax(ParsedSyntax &&Node);
|
|
|
|
template<typename SyntaxNode>
|
|
llvm::Optional<SyntaxNode> popIf() {
|
|
auto &Storage = getStorage();
|
|
if (Storage.size() <= Offset)
|
|
return llvm::None;
|
|
if (!SyntaxNode::kindof(Storage.back().getKind()))
|
|
return llvm::None;
|
|
auto rawNode = std::move(Storage.back());
|
|
Storage.pop_back();
|
|
return SyntaxNode(std::move(rawNode));
|
|
}
|
|
|
|
ParsedTokenSyntax popToken();
|
|
|
|
/// Create a node using the tail of the collected parts. The number of parts
|
|
/// is automatically determined from \c Kind. Node: limited number of \c Kind
|
|
/// are supported. See the implementation.
|
|
void createNodeInPlace(SyntaxKind Kind,
|
|
SyntaxNodeCreationKind nodeCreateK = SyntaxNodeCreationKind::Recorded);
|
|
|
|
/// Squashing nodes from the back of the pending syntax list to a given syntax
|
|
/// collection kind. If there're no nodes that can fit into the collection kind,
|
|
/// this function does nothing. Otherwise, it creates a collection node in place
|
|
/// to contain all sequential suitable nodes from back.
|
|
void collectNodesInPlace(SyntaxKind ColletionKind,
|
|
SyntaxNodeCreationKind nodeCreateK = SyntaxNodeCreationKind::Recorded);
|
|
|
|
/// On destruction, construct a specified kind of syntax node consuming the
|
|
/// collected parts, then append it to the parent context.
|
|
void setCreateSyntax(SyntaxKind Kind) {
|
|
Mode = AccumulationMode::CreateSyntax;
|
|
SynKind = Kind;
|
|
}
|
|
|
|
/// Same as \c setCreateSyntax but create a deferred node instead of a
|
|
/// recorded one.
|
|
void setDeferSyntax(SyntaxKind Kind) {
|
|
Mode = AccumulationMode::DeferSyntax;
|
|
SynKind = Kind;
|
|
}
|
|
|
|
/// On destruction, if the parts size is 1 and it's kind of \c Kind, just
|
|
/// append it to the parent context. Otherwise, create Unknown{Kind} node from
|
|
/// the collected parts.
|
|
void setCoerceKind(SyntaxContextKind Kind) {
|
|
Mode = AccumulationMode::CoerceKind;
|
|
CtxtKind = Kind;
|
|
}
|
|
|
|
/// Move the collected parts to the tail of parent context.
|
|
void setTransparent() { Mode = AccumulationMode::Transparent; }
|
|
|
|
/// This context is a back tracking context, so we should discard collected
|
|
/// parts on this context.
|
|
void setBackTracking() {
|
|
Mode = AccumulationMode::Discard;
|
|
IsBacktracking = true;
|
|
}
|
|
|
|
/// Cancels backtracking state from the top of the context stack until `this` context.
|
|
void cancelBacktrack();
|
|
|
|
bool isBacktracking() const { return IsBacktracking; }
|
|
|
|
void setShouldDefer(bool Value = true) { ShouldDefer = Value; }
|
|
|
|
bool shouldDefer() const {
|
|
return ShouldDefer || IsBacktracking || Mode == AccumulationMode::Discard;
|
|
}
|
|
|
|
/// Explicitly finalizing syntax tree creation.
|
|
/// This function will be called during the destroying of a root syntax
|
|
/// parsing context. However, we can explicitly call this function to get
|
|
/// the syntax tree before closing the root context.
|
|
OpaqueSyntaxNode finalizeRoot();
|
|
|
|
/// Make a missing node corresponding to the given token kind and
|
|
/// push this node into the context. The synthesized node can help
|
|
/// the creation of valid syntax nodes.
|
|
void synthesize(tok Kind, SourceLoc Loc);
|
|
|
|
/// Dump the nodes that are in the storage stack of the SyntaxParsingContext
|
|
SWIFT_DEBUG_DUMPER(dumpStorage());
|
|
};
|
|
|
|
} // namespace swift
|
|
#endif // SWIFT_SYNTAX_PARSING_CONTEXT_H
|