mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Instead of creating multiple CodeBlockItemList nodes, that need to get merged and discarded later on, do this: * Ensure for libSyntax parsing that we parse the whole file * Create top-level CodeBlockItem nodes that we just directly wrap with a single CodeBlockItemList node at the end The importance of this change will become more obvious later on when we'll decouple syntax parsing from the formation of libSyntax tree nodes.
384 lines
12 KiB
C++
384 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/SourceLoc.h"
|
|
#include "swift/Syntax/Syntax.h"
|
|
#include "swift/Syntax/TokenSyntax.h"
|
|
|
|
namespace swift {
|
|
|
|
using namespace swift::syntax;
|
|
|
|
/// Cache node for RawSyntax.
|
|
class RawSyntaxCacheNode : public llvm::FoldingSetNode {
|
|
|
|
friend llvm::FoldingSetTrait<RawSyntaxCacheNode>;
|
|
|
|
/// Associated RawSyntax.
|
|
RC<RawSyntax> Obj;
|
|
/// FoldingSet node identifier of the associated RawSyntax.
|
|
llvm::FoldingSetNodeIDRef IDRef;
|
|
|
|
public:
|
|
RawSyntaxCacheNode(RC<RawSyntax> Obj, const llvm::FoldingSetNodeIDRef IDRef)
|
|
: Obj(Obj), IDRef(IDRef) {}
|
|
|
|
/// Retrieve assciated RawSyntax.
|
|
RC<RawSyntax> get() { return Obj; }
|
|
|
|
// Only allow allocation of Node using the allocator in SyntaxArena.
|
|
void *operator new(size_t Bytes, RC<SyntaxArena> &Arena,
|
|
unsigned Alignment = alignof(RawSyntaxCacheNode)) {
|
|
return Arena->Allocate(Bytes, Alignment);
|
|
}
|
|
|
|
void *operator new(size_t Bytes) throw() = delete;
|
|
void operator delete(void *Data) throw() = delete;
|
|
};
|
|
|
|
class RawSyntaxTokenCache {
|
|
llvm::FoldingSet<RawSyntaxCacheNode> CachedTokens;
|
|
std::vector<RawSyntaxCacheNode *> CacheNodes;
|
|
|
|
public:
|
|
RC<RawSyntax> getToken(RC<SyntaxArena> &Arena, tok TokKind, OwnedString Text,
|
|
llvm::ArrayRef<TriviaPiece> LeadingTrivia,
|
|
llvm::ArrayRef<TriviaPiece> TrailingTrivia);
|
|
|
|
~RawSyntaxTokenCache();
|
|
};
|
|
|
|
} // namespace swift
|
|
|
|
namespace llvm {
|
|
|
|
using swift::RawSyntaxCacheNode;
|
|
|
|
/// FoldingSet traits for RawSyntax wrapper.
|
|
template <> struct FoldingSetTrait<RawSyntaxCacheNode> {
|
|
|
|
static inline void Profile(RawSyntaxCacheNode &X, FoldingSetNodeID &ID) {
|
|
ID.AddNodeID(X.IDRef);
|
|
}
|
|
|
|
static inline bool Equals(RawSyntaxCacheNode &X, const FoldingSetNodeID &ID,
|
|
unsigned, FoldingSetNodeID &) {
|
|
return ID == X.IDRef;
|
|
}
|
|
static inline unsigned ComputeHash(RawSyntaxCacheNode &X,
|
|
FoldingSetNodeID &) {
|
|
return X.IDRef.ComputeHash();
|
|
}
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
namespace swift {
|
|
class SourceFile;
|
|
class SyntaxParsingCache;
|
|
class Token;
|
|
class DiagnosticEngine;
|
|
|
|
namespace syntax {
|
|
class RawSyntax;
|
|
enum class SyntaxKind;
|
|
}
|
|
|
|
using namespace swift::syntax;
|
|
|
|
enum class SyntaxContextKind {
|
|
Decl,
|
|
Stmt,
|
|
Expr,
|
|
Type,
|
|
Pattern,
|
|
Syntax,
|
|
};
|
|
|
|
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(SyntaxKind::ParenExpr, SyntaxContext);
|
|
/// 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<RC<RawSyntax>> Storage;
|
|
|
|
RC<SyntaxArena> Arena;
|
|
|
|
/// A cache of nodes that can be reused when creating the current syntax
|
|
/// tree
|
|
SyntaxParsingCache *SyntaxCache = nullptr;
|
|
|
|
/// Tokens nodes that have already been created and may be reused in other
|
|
/// parts of the syntax tree.
|
|
RawSyntaxTokenCache TokenCache;
|
|
|
|
RootContextData(SourceFile &SF, DiagnosticEngine &Diags,
|
|
SourceManager &SourceMgr, unsigned BufferID,
|
|
const RC<SyntaxArena> &Arena,
|
|
SyntaxParsingCache *SyntaxCache)
|
|
: SF(SF), Diags(Diags), SourceMgr(SourceMgr), BufferID(BufferID),
|
|
Arena(Arena), SyntaxCache(SyntaxCache) {}
|
|
};
|
|
|
|
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,
|
|
|
|
// Pass through all parts to the parent context.
|
|
Transparent,
|
|
|
|
// Discard all parts in the context.
|
|
Discard,
|
|
|
|
// The node has been loaded from the cache and all parts shall be discarded.
|
|
LoadedFromCache,
|
|
|
|
// 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;
|
|
};
|
|
|
|
// 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);
|
|
|
|
ArrayRef<RC<RawSyntax>> getParts() const {
|
|
return makeArrayRef(getStorage()).drop_front(Offset);
|
|
}
|
|
|
|
RC<RawSyntax> makeUnknownSyntax(SyntaxKind Kind,
|
|
ArrayRef<RC<RawSyntax>> Parts);
|
|
RC<RawSyntax> createSyntaxAs(SyntaxKind Kind, ArrayRef<RC<RawSyntax>> Parts);
|
|
RC<RawSyntax> bridgeAs(SyntaxContextKind Kind, ArrayRef<RC<RawSyntax>> Parts);
|
|
|
|
public:
|
|
/// Construct root context.
|
|
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF,
|
|
unsigned BufferID);
|
|
|
|
/// Designated constructor for child context.
|
|
SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder)
|
|
: RootDataOrParent(CtxtHolder), CtxtHolder(CtxtHolder),
|
|
RootData(CtxtHolder->RootData), Offset(RootData->Storage.size()),
|
|
Enabled(CtxtHolder->isEnabled()) {
|
|
assert(CtxtHolder->isTopOfContextStack() &&
|
|
"SyntaxParsingContext cannot have multiple children");
|
|
assert(CtxtHolder->Mode != AccumulationMode::LoadedFromCache &&
|
|
"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();
|
|
|
|
/// Try loading the current node from the \c SyntaxParsingCache by 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 node from the cache and return the number of
|
|
/// bytes the loaded 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 loadFromCache(size_t LexerOffset);
|
|
|
|
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<RC<RawSyntax>> &getStorage() { return getRootData()->Storage; }
|
|
|
|
const std::vector<RC<RawSyntax>> &getStorage() const {
|
|
return getRootData()->Storage;
|
|
}
|
|
|
|
SyntaxParsingCache *getSyntaxParsingCache() const {
|
|
return getRootData()->SyntaxCache;
|
|
}
|
|
|
|
RawSyntaxTokenCache &getTokenCache() { return getRootData()->TokenCache; }
|
|
|
|
const RC<SyntaxArena> &getArena() const { return getRootData()->Arena; }
|
|
|
|
const SyntaxParsingContext *getRoot() const;
|
|
|
|
/// Add RawSyntax to the parts.
|
|
void addRawSyntax(RC<RawSyntax> Raw);
|
|
|
|
/// Add Token with Trivia to the parts.
|
|
void addToken(Token &Tok, Trivia &LeadingTrivia,
|
|
Trivia &TrailingTrivia);
|
|
|
|
/// Add Syntax to the parts.
|
|
void addSyntax(Syntax Node);
|
|
|
|
template<typename SyntaxNode>
|
|
llvm::Optional<SyntaxNode> popIf() {
|
|
auto &Storage = getStorage();
|
|
assert(Storage.size() > Offset);
|
|
if (auto Node = make<Syntax>(Storage.back()).getAs<SyntaxNode>()) {
|
|
Storage.pop_back();
|
|
return Node;
|
|
}
|
|
return None;
|
|
}
|
|
|
|
TokenSyntax popToken() {
|
|
auto &Storage = getStorage();
|
|
assert(Storage.size() > Offset);
|
|
assert(Storage.back()->getKind() == SyntaxKind::Token);
|
|
auto Node = make<TokenSyntax>(std::move(Storage.back()));
|
|
Storage.pop_back();
|
|
return Node;
|
|
}
|
|
|
|
/// 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);
|
|
|
|
/// 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);
|
|
|
|
/// On destruction, construct a specified kind of RawSyntax node consuming the
|
|
/// collected parts, then append it to the parent context.
|
|
void setCreateSyntax(SyntaxKind Kind) {
|
|
Mode = AccumulationMode::CreateSyntax;
|
|
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; }
|
|
|
|
/// 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.
|
|
RC<RawSyntax> finalizeRoot();
|
|
|
|
/// Make a missing node corresponding to the given token kind and text, and
|
|
/// push this node into the context. The synthesized node can help
|
|
/// the creation of valid syntax nodes.
|
|
void synthesize(tok Kind, StringRef Text = "");
|
|
|
|
/// Make a missing node corresponding to the given node kind, and
|
|
/// push this node into the context.
|
|
void synthesize(SyntaxKind Kind);
|
|
|
|
/// Dump the nodes that are in the storage stack of the SyntaxParsingContext
|
|
LLVM_ATTRIBUTE_DEPRECATED(void dumpStorage() const LLVM_ATTRIBUTE_USED,
|
|
"Only meant for use in the debugger");
|
|
};
|
|
|
|
} // namespace swift
|
|
#endif // SWIFT_SYNTAX_PARSING_CONTEXT_H
|