//===----------- 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, }; } // end namespace swift namespace llvm { raw_ostream &operator<<(raw_ostream &OS, swift::SyntaxContextKind Kind); } // end namespace llvm namespace swift { 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 Storage; ParsedRawSyntaxRecorder Recorder; llvm::BumpPtrAllocator ScratchAlloc; RootContextData(SourceFile &SF, DiagnosticEngine &Diags, SourceManager &SourceMgr, unsigned BufferID, std::shared_ptr 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 // 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 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 getParts() const { return llvm::makeArrayRef(getStorage()).drop_front(Offset); } MutableArrayRef getParts() { return llvm::makeMutableArrayRef(getStorage().data(), getStorage().size()).drop_front(Offset); } ParsedRawSyntaxNode makeUnknownSyntax(SyntaxKind Kind, MutableArrayRef Parts); ParsedRawSyntaxNode createSyntaxAs(SyntaxKind Kind, MutableArrayRef Parts, SyntaxNodeCreationKind nodeCreateK); Optional bridgeAs(SyntaxContextKind Kind, MutableArrayRef Parts); public: /// Construct root context. SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder, SourceFile &SF, unsigned BufferID, std::shared_ptr 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(); } bool isTopOfContextStack() const { return this == CtxtHolder; } SyntaxParsingContext *getParent() const { return RootDataOrParent.get(); } RootContextData *getRootData() { return RootData; } const RootContextData *getRootData() const { return RootData; } std::vector &getStorage() { return getRootData()->Storage; } const std::vector &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 llvm::Optional 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()); void dumpStack(llvm::raw_ostream &OS) const; SWIFT_DEBUG_DUMPER(dumpStack()) { dumpStack(llvm::errs()); } }; } // namespace swift #endif // SWIFT_SYNTAX_PARSING_CONTEXT_H