[incrParse] Reparse a node if the next leaf node has been modified

This commit is contained in:
Alex Hoppen
2018-05-02 14:49:01 -07:00
parent de9737c946
commit 60d11d24f8
5 changed files with 129 additions and 5 deletions

View File

@@ -15,6 +15,32 @@
#include "swift/Syntax/SyntaxNodes.h"
namespace {
/// A single edit to the original source file in which a continuous range of
/// characters have been replaced by a new string
struct SourceEdit {
/// The byte offset from which on characters were replaced.
size_t Start;
/// The byte offset to which on characters were replaced.
size_t End;
/// The length of the string that replaced the range described above.
size_t ReplacementLength;
/// The length of the range that has been replaced
size_t originalLength() { return End - Start; }
/// Check if the characters replaced by this edit fall into the given range
/// or are directly adjacent to it
bool intersectsOrTouchesRange(size_t RangeStart, size_t RangeEnd) {
return !(End <= RangeStart || Start >= RangeEnd);
}
};
} // anonymous namespace
namespace swift {
using namespace swift::syntax;
@@ -23,11 +49,29 @@ class SyntaxParsingCache {
/// The syntax tree prior to the edit
SourceFileSyntax OldSyntaxTree;
/// The edits that were made from the source file that created this cache to
/// the source file that is now parsed incrementally
llvm::SmallVector<SourceEdit, 4> Edits;
public:
SyntaxParsingCache(SourceFileSyntax OldSyntaxTree)
: OldSyntaxTree(OldSyntaxTree) {}
/// Add an edit that transformed the source file which created this cache into
/// the source file that is now being parsed incrementally. The order in which
/// the edits are added using this method needs to be the same order in which
/// the edits were applied to the source file.
void addEdit(size_t Start, size_t End, size_t ReplacementLength) {
Edits.push_back({Start, End, ReplacementLength});
}
/// Check if a syntax node of the given kind at the given position can be
/// reused for a new syntax tree.
llvm::Optional<Syntax> lookUp(size_t NewPosition, SyntaxKind Kind) const;
private:
llvm::Optional<Syntax> lookUpFrom(Syntax Node, size_t Position,
SyntaxKind Kind) const;
};
} // namespace swift

View File

@@ -108,4 +108,8 @@ struct ScalarEnumerationTraits<swift::syntax::SyntaxKind> {
} // end namespace yaml
} // end namespace llvm
namespace llvm {
raw_ostream &operator<<(raw_ostream &OS, swift::syntax::SyntaxKind Kind);
} // end namespace llvm
#endif // SWIFT_SYNTAX_KIND_H

View File

@@ -15,12 +15,29 @@
using namespace swift;
using namespace swift::syntax;
llvm::Optional<Syntax> lookUpFrom(Syntax Node, size_t Position,
SyntaxKind Kind) {
llvm::Optional<Syntax> SyntaxParsingCache::lookUpFrom(Syntax Node,
size_t Position,
SyntaxKind Kind) const {
if (Node.getAbsolutePosition().getOffset() == Position &&
Node.getKind() == Kind) {
// Check if this node has been edited. If it has, we cannot reuse it.
bool NodeEdited = false;
auto NodeStart = Node.getAbsolutePosition().getOffset();
auto NodeEnd = NodeStart + Node.getTextLength();
for (auto Edit : Edits) {
if (Edit.intersectsOrTouchesRange(NodeStart, NodeEnd)) {
NodeEdited = true;
}
}
// FIXME: Node can also not be reused if an edit has been made in the next
// token's leading trivia
if (!NodeEdited) {
return Node;
}
}
for (size_t I = 0, E = Node.getNumChildren(); I < E; ++I) {
llvm::Optional<Syntax> Child = Node.getChild(I);
@@ -38,6 +55,15 @@ llvm::Optional<Syntax> lookUpFrom(Syntax Node, size_t Position,
llvm::Optional<Syntax> SyntaxParsingCache::lookUp(size_t NewPosition,
SyntaxKind Kind) const {
// FIXME: Transform the new position into the position in the old file
return lookUpFrom(OldSyntaxTree, NewPosition, Kind);
// Undo the edits in reverse order
size_t OldPosition = NewPosition;
for (auto I = Edits.rbegin(), E = Edits.rend(); I != E; ++I) {
auto Edit = *I;
if (Edit.End < OldPosition) {
OldPosition =
OldPosition - Edit.ReplacementLength + Edit.originalLength();
}
}
return lookUpFrom(OldSyntaxTree, OldPosition, Kind);
}

View File

@@ -19,6 +19,7 @@
#include "swift/Syntax/SyntaxKind.h"
#include "swift/Syntax/TokenKinds.h"
#include "llvm/Support/raw_ostream.h"
namespace swift {
@@ -123,3 +124,21 @@ SyntaxKind getUnknownKind(SyntaxKind Kind) {
}
} // end namespace syntax
} // end namespace swift
llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS,
swift::syntax::SyntaxKind Kind) {
switch (Kind) {
% for node in SYNTAX_NODES:
case swift::syntax::SyntaxKind::${node.syntax_kind}:
OS << "${node.syntax_kind}";
break;
% end
case swift::syntax::SyntaxKind::Token:
OS << "TokenSyntax";
break;
case swift::syntax::SyntaxKind::Unknown:
OS << "UnknownSyntax";
break;
}
return OS;
}

View File

@@ -35,6 +35,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Regex.h"
using namespace swift;
using namespace swift::syntax;
@@ -111,6 +112,17 @@ OldSyntaxTreeFilename("old-syntax-tree-filename",
llvm::cl::desc("Path to the serialized syntax tree of "
"the pre-edit file"));
static llvm::cl::list<std::string>
IncrementalEdits("incremental-edit",
llvm::cl::desc("An edit that was applied to reach the input "
"file from the source file that generated the "
"old syntax tree in the format "
"<start>:<end>=<replacement> where <start> and "
"<end> are byte offsets in the original file "
"and <replacement> is the string that shall "
"replace the selected range. "
"Can be passed multiple times."));
static llvm::cl::opt<std::string>
OutputFilename("output-filename",
llvm::cl::desc("Path to the output file"));
@@ -321,6 +333,25 @@ int doIncrementalParse(const char *MainExecutablePath,
}
SyntaxParsingCache *Cache = new SyntaxParsingCache(OldSyntaxTree.getValue());
for (auto EditPattern : options::IncrementalEdits) {
llvm::Regex MatchRegex("([0-9]+):([0-9]+)=(.*)");
SmallVector<StringRef, 4> Matches;
if (!MatchRegex.match(EditPattern, &Matches)) {
llvm::errs() << "Invalid edit pattern: " << EditPattern;
return EXIT_FAILURE;
}
int EditStart, EditEnd;
if (Matches[1].getAsInteger(10, EditStart)) {
llvm::errs() << "Could not parse edit start as integer: " << EditStart;
return EXIT_FAILURE;
}
if (Matches[2].getAsInteger(10, EditEnd)) {
llvm::errs() << "Could not parse edit end as integer: " << EditStart;
return EXIT_FAILURE;
}
Cache->addEdit(EditStart, EditEnd, /*ReplacmentLength=*/Matches[3].size());
}
// Parse the new libSyntax tree incrementally
CompilerInstance Instance;
SourceFile *SF =