From ea1a4680d49013ddd9dfd3d475ac7039ef054db9 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 7 Jul 2021 11:09:00 +0000 Subject: [PATCH] [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 Commit-Queue: Ben Clayton Auto-Submit: Ben Clayton Reviewed-by: James Price --- src/writer/text_generator.cc | 55 +++++++++++++++++---- src/writer/text_generator.h | 96 ++++++++++++++++++++++-------------- 2 files changed, 106 insertions(+), 45 deletions(-) diff --git a/src/writer/text_generator.cc b/src/writer/text_generator.cc index 4dc86d4899..27b2d286a6 100644 --- a/src/writer/text_generator.cc +++ b/src/writer/text_generator.cc @@ -39,17 +39,16 @@ std::string TextGenerator::TrimSuffix(std::string str, return str; } -TextGenerator::LineWriter::LineWriter(TextGenerator* generator) - : gen(generator) {} +TextGenerator::LineWriter::LineWriter(TextBuffer* buf) : buffer(buf) {} TextGenerator::LineWriter::LineWriter(LineWriter&& other) { - gen = other.gen; - other.gen = nullptr; + buffer = other.buffer; + other.buffer = nullptr; } TextGenerator::LineWriter::~LineWriter() { - if (gen) { - gen->current_buffer_->Append(os.str()); + if (buffer) { + buffer->Append(os.str()); } } @@ -68,12 +67,47 @@ void TextGenerator::TextBuffer::Append(const std::string& 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) { for (auto& line : tb.lines) { + // TODO(bclayton): inefficent, consider optimizing 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::stringstream ss; for (auto& line : lines) { @@ -96,11 +130,14 @@ TextGenerator::ScopedParen::~ScopedParen() { } TextGenerator::ScopedIndent::ScopedIndent(TextGenerator* generator) - : gen(generator) { - gen->increment_indent(); + : ScopedIndent(generator->current_buffer_) {} + +TextGenerator::ScopedIndent::ScopedIndent(TextBuffer* buffer) + : buffer_(buffer) { + buffer_->IncrementIndent(); } TextGenerator::ScopedIndent::~ScopedIndent() { - gen->decrement_indent(); + buffer_->DecrementIndent(); } } // namespace writer diff --git a/src/writer/text_generator.h b/src/writer/text_generator.h index 885c319661..1e75a52086 100644 --- a/src/writer/text_generator.h +++ b/src/writer/text_generator.h @@ -60,38 +60,6 @@ class TextGenerator { std::string TrimSuffix(std::string str, const std::string& suffix); 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 - std::ostream& operator<<(T&& rhs) { - return os << std::forward(rhs); - } - - private: - LineWriter(const LineWriter&) = delete; - LineWriter& operator=(const LineWriter&) = delete; - - std::ostringstream os; - TextGenerator* gen; - }; - /// Line holds a single line of text struct Line { /// The indentation of the line in whitespaces @@ -120,10 +88,23 @@ class TextGenerator { /// @param line the line to append to the TextBuffer 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 /// @param tb the TextBuffer to append to the end of this TextBuffer 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 std::string String() const; @@ -135,6 +116,39 @@ class TextGenerator { std::vector 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 + std::ostream& operator<<(T&& rhs) { + return os << std::forward(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. struct ScopedParen { /// Constructor @@ -154,7 +168,11 @@ class TextGenerator { /// indentation on destruction. struct ScopedIndent { /// 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); /// Destructor ~ScopedIndent(); @@ -163,7 +181,7 @@ class TextGenerator { ScopedIndent(ScopedIndent&& rhs) = delete; ScopedIndent(const ScopedIndent&) = delete; ScopedIndent& operator=(const ScopedIndent&) = delete; - TextGenerator* gen; + TextBuffer* buffer_; }; /// @returns the resolved type of the ast::Expression `expr` @@ -184,8 +202,14 @@ class TextGenerator { return builder_.TypeOf(type_decl); } - /// @returns a new LineWriter, used for buffering and writing a line to out_ - LineWriter line() { return LineWriter(this); } + /// @returns a new LineWriter, used for buffering and writing a line to + /// 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 Program const* const program_;