[writer] Allow for out of order text generation

Add Insert() methods to TextBuffer.
Allows generators to insert helper functions at the top of the output without requiring a scan of the program before generation.

Change-Id: I05f67ad05d189f2249e35cfac99536afccb5578d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57140
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
Ben Clayton 2021-07-07 11:09:00 +00:00 committed by Tint LUCI CQ
parent facb5ced00
commit ea1a4680d4
2 changed files with 106 additions and 45 deletions

View File

@ -39,17 +39,16 @@ std::string TextGenerator::TrimSuffix(std::string str,
return str; return str;
} }
TextGenerator::LineWriter::LineWriter(TextGenerator* generator) TextGenerator::LineWriter::LineWriter(TextBuffer* buf) : buffer(buf) {}
: gen(generator) {}
TextGenerator::LineWriter::LineWriter(LineWriter&& other) { TextGenerator::LineWriter::LineWriter(LineWriter&& other) {
gen = other.gen; buffer = other.buffer;
other.gen = nullptr; other.buffer = nullptr;
} }
TextGenerator::LineWriter::~LineWriter() { TextGenerator::LineWriter::~LineWriter() {
if (gen) { if (buffer) {
gen->current_buffer_->Append(os.str()); buffer->Append(os.str());
} }
} }
@ -68,12 +67,47 @@ void TextGenerator::TextBuffer::Append(const std::string& line) {
lines.emplace_back(Line{current_indent, line}); lines.emplace_back(Line{current_indent, line});
} }
void TextGenerator::TextBuffer::Insert(const std::string& line,
size_t before,
uint32_t indent) {
if (before >= lines.size()) {
diag::List d;
TINT_ICE(Writer, d)
<< "TextBuffer::Insert() called with before >= lines.size()\n"
<< " before:" << before << "\n"
<< " lines.size(): " << lines.size();
return;
}
lines.insert(lines.begin() + before, Line{indent, line});
}
void TextGenerator::TextBuffer::Append(const TextBuffer& tb) { void TextGenerator::TextBuffer::Append(const TextBuffer& tb) {
for (auto& line : tb.lines) { for (auto& line : tb.lines) {
// TODO(bclayton): inefficent, consider optimizing
lines.emplace_back(Line{current_indent + line.indent, line.content}); lines.emplace_back(Line{current_indent + line.indent, line.content});
} }
} }
void TextGenerator::TextBuffer::Insert(const TextBuffer& tb,
size_t before,
uint32_t indent) {
if (before >= lines.size()) {
diag::List d;
TINT_ICE(Writer, d)
<< "TextBuffer::Insert() called with before >= lines.size()\n"
<< " before:" << before << "\n"
<< " lines.size(): " << lines.size();
return;
}
size_t idx = 0;
for (auto& line : tb.lines) {
// TODO(bclayton): inefficent, consider optimizing
lines.insert(lines.begin() + before + idx,
Line{indent + line.indent, line.content});
idx++;
}
}
std::string TextGenerator::TextBuffer::String() const { std::string TextGenerator::TextBuffer::String() const {
std::stringstream ss; std::stringstream ss;
for (auto& line : lines) { for (auto& line : lines) {
@ -96,11 +130,14 @@ TextGenerator::ScopedParen::~ScopedParen() {
} }
TextGenerator::ScopedIndent::ScopedIndent(TextGenerator* generator) TextGenerator::ScopedIndent::ScopedIndent(TextGenerator* generator)
: gen(generator) { : ScopedIndent(generator->current_buffer_) {}
gen->increment_indent();
TextGenerator::ScopedIndent::ScopedIndent(TextBuffer* buffer)
: buffer_(buffer) {
buffer_->IncrementIndent();
} }
TextGenerator::ScopedIndent::~ScopedIndent() { TextGenerator::ScopedIndent::~ScopedIndent() {
gen->decrement_indent(); buffer_->DecrementIndent();
} }
} // namespace writer } // namespace writer

View File

@ -60,38 +60,6 @@ class TextGenerator {
std::string TrimSuffix(std::string str, const std::string& suffix); std::string TrimSuffix(std::string str, const std::string& suffix);
protected: protected:
/// LineWriter is a helper that acts as a string buffer, who's content is
/// emitted to the TextGenerator as a single line on destruction.
struct LineWriter {
public:
/// Constructor
/// @param generator the TextGenerator that the LineWriter will append its
/// content to on destruction
explicit LineWriter(TextGenerator* generator);
/// Move constructor
/// @param rhs the LineWriter to move
LineWriter(LineWriter&& rhs);
/// Destructor
~LineWriter();
/// @returns the ostringstream
operator std::ostream &() { return os; }
/// @param rhs the value to write to the line
/// @returns the ostream so calls can be chained
template <typename T>
std::ostream& operator<<(T&& rhs) {
return os << std::forward<T>(rhs);
}
private:
LineWriter(const LineWriter&) = delete;
LineWriter& operator=(const LineWriter&) = delete;
std::ostringstream os;
TextGenerator* gen;
};
/// Line holds a single line of text /// Line holds a single line of text
struct Line { struct Line {
/// The indentation of the line in whitespaces /// The indentation of the line in whitespaces
@ -120,10 +88,23 @@ class TextGenerator {
/// @param line the line to append to the TextBuffer /// @param line the line to append to the TextBuffer
void Append(const std::string& line); void Append(const std::string& line);
/// Inserts the line to the TextBuffer before the line with index `before`
/// @param line the line to append to the TextBuffer
/// @param before the zero-based index of the line to insert the text before
/// @param indent the indentation to apply to the inserted lines
void Insert(const std::string& line, size_t before, uint32_t indent);
/// Appends the lines of `tb` to the end of this TextBuffer /// Appends the lines of `tb` to the end of this TextBuffer
/// @param tb the TextBuffer to append to the end of this TextBuffer /// @param tb the TextBuffer to append to the end of this TextBuffer
void Append(const TextBuffer& tb); void Append(const TextBuffer& tb);
/// Inserts the lines of `tb` to the TextBuffer before the line with index
/// `before`
/// @param tb the TextBuffer to insert into this TextBuffer
/// @param before the zero-based index of the line to insert the text before
/// @param indent the indentation to apply to the inserted lines
void Insert(const TextBuffer& tb, size_t before, uint32_t indent);
/// @returns the buffer's content as a single string /// @returns the buffer's content as a single string
std::string String() const; std::string String() const;
@ -135,6 +116,39 @@ class TextGenerator {
std::vector<Line> lines; std::vector<Line> lines;
}; };
/// LineWriter is a helper that acts as a string buffer, who's content is
/// emitted to the TextBuffer as a single line on destruction.
struct LineWriter {
public:
/// Constructor
/// @param buffer the TextBuffer that the LineWriter will append its
/// content to on destruction, at the end of the buffer.
explicit LineWriter(TextBuffer* buffer);
/// Move constructor
/// @param rhs the LineWriter to move
LineWriter(LineWriter&& rhs);
/// Destructor
~LineWriter();
/// @returns the ostringstream
operator std::ostream &() { return os; }
/// @param rhs the value to write to the line
/// @returns the ostream so calls can be chained
template <typename T>
std::ostream& operator<<(T&& rhs) {
return os << std::forward<T>(rhs);
}
private:
LineWriter(const LineWriter&) = delete;
LineWriter& operator=(const LineWriter&) = delete;
std::ostringstream os;
TextBuffer* buffer;
};
/// Helper for writing a '(' on construction and a ')' destruction. /// Helper for writing a '(' on construction and a ')' destruction.
struct ScopedParen { struct ScopedParen {
/// Constructor /// Constructor
@ -154,7 +168,11 @@ class TextGenerator {
/// indentation on destruction. /// indentation on destruction.
struct ScopedIndent { struct ScopedIndent {
/// Constructor /// Constructor
/// @param generator the TextGenerator that the ScopedIndent will indent /// @param buffer the TextBuffer that the ScopedIndent will indent
explicit ScopedIndent(TextBuffer* buffer);
/// Constructor
/// @param generator ScopedIndent will indent the generator's
/// `current_buffer_`
explicit ScopedIndent(TextGenerator* generator); explicit ScopedIndent(TextGenerator* generator);
/// Destructor /// Destructor
~ScopedIndent(); ~ScopedIndent();
@ -163,7 +181,7 @@ class TextGenerator {
ScopedIndent(ScopedIndent&& rhs) = delete; ScopedIndent(ScopedIndent&& rhs) = delete;
ScopedIndent(const ScopedIndent&) = delete; ScopedIndent(const ScopedIndent&) = delete;
ScopedIndent& operator=(const ScopedIndent&) = delete; ScopedIndent& operator=(const ScopedIndent&) = delete;
TextGenerator* gen; TextBuffer* buffer_;
}; };
/// @returns the resolved type of the ast::Expression `expr` /// @returns the resolved type of the ast::Expression `expr`
@ -184,8 +202,14 @@ class TextGenerator {
return builder_.TypeOf(type_decl); return builder_.TypeOf(type_decl);
} }
/// @returns a new LineWriter, used for buffering and writing a line to out_ /// @returns a new LineWriter, used for buffering and writing a line to
LineWriter line() { return LineWriter(this); } /// the end of #current_buffer_.
LineWriter line() { return LineWriter(current_buffer_); }
/// @param buffer the TextBuffer to write the line to
/// @returns a new LineWriter, used for buffering and writing a line to
/// the end of `buffer`.
LineWriter line(TextBuffer* buffer) { return LineWriter(buffer); }
/// The program /// The program
Program const* const program_; Program const* const program_;