mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Instead of creating syntax nodes directly, modify the parser to invoke an abstract interface 'SyntaxParseActions' while it is parsing the source code. This decouples the act of parsing from the act of forming a syntax tree representation. 'SyntaxTreeCreator' is an implementation of SyntaxParseActions that handles the logic of creating a syntax tree. To enforce the layering separation of parsing and syntax tree creation, a static library swiftSyntaxParse is introduced to compose the two. This decoupling is important for introducing a syntax parser library for SwiftSyntax to directly access parsing.
323 lines
11 KiB
C++
323 lines
11 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/Parse/ParsedRawSyntaxNode.h"
|
|
#include "swift/Parse/ParsedRawSyntaxRecorder.h"
|
|
|
|
namespace swift {
|
|
|
|
using namespace swift::syntax;
|
|
|
|
class ParsedSyntax;
|
|
class ParsedTokenSyntax;
|
|
class SourceFile;
|
|
enum class tok;
|
|
class Token;
|
|
class DiagnosticEngine;
|
|
|
|
namespace syntax {
|
|
enum class SyntaxKind;
|
|
}
|
|
|
|
enum class SyntaxContextKind {
|
|
Decl,
|
|
Stmt,
|
|
Expr,
|
|
Type,
|
|
Pattern,
|
|
Syntax,
|
|
};
|
|
|
|
enum class SyntaxNodeCreationKind {
|
|
Recorded,
|
|
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(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<ParsedRawSyntaxNode> Storage;
|
|
|
|
ParsedRawSyntaxRecorder Recorder;
|
|
|
|
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;
|
|
|
|
// 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);
|
|
}
|
|
|
|
ParsedRawSyntaxNode makeUnknownSyntax(SyntaxKind Kind,
|
|
ArrayRef<ParsedRawSyntaxNode> Parts);
|
|
ParsedRawSyntaxNode createSyntaxAs(SyntaxKind Kind,
|
|
ArrayRef<ParsedRawSyntaxNode> Parts,
|
|
SyntaxNodeCreationKind nodeCreateK);
|
|
Optional<ParsedRawSyntaxNode> bridgeAs(SyntaxContextKind Kind,
|
|
ArrayRef<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),
|
|
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();
|
|
|
|
/// 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; }
|
|
|
|
/// Add RawSyntax to the parts.
|
|
void addRawSyntax(ParsedRawSyntaxNode Raw);
|
|
|
|
/// Add Token with Trivia to the parts.
|
|
void addToken(Token &Tok, Trivia &LeadingTrivia,
|
|
Trivia &TrailingTrivia);
|
|
|
|
/// Add Syntax to the parts.
|
|
void addSyntax(ParsedSyntax Node);
|
|
|
|
|
|
template<typename SyntaxNode>
|
|
llvm::Optional<SyntaxNode> popIf() {
|
|
auto &Storage = getStorage();
|
|
assert(Storage.size() > Offset);
|
|
if (SyntaxNode::kindof(Storage.back().getKind())) {
|
|
auto rawNode = std::move(Storage.back());
|
|
Storage.pop_back();
|
|
return SyntaxNode(rawNode);
|
|
}
|
|
return None;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// 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.
|
|
ParsedRawSyntaxNode 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
|
|
LLVM_ATTRIBUTE_DEPRECATED(void dumpStorage() const LLVM_ATTRIBUTE_USED,
|
|
"Only meant for use in the debugger");
|
|
};
|
|
|
|
} // namespace swift
|
|
#endif // SWIFT_SYNTAX_PARSING_CONTEXT_H
|