1 // Copyright 2020 The Tint Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SRC_WRITER_TEXT_GENERATOR_H_ 16 #define SRC_WRITER_TEXT_GENERATOR_H_ 17 18 #include <sstream> 19 #include <string> 20 #include <unordered_map> 21 #include <utility> 22 #include <vector> 23 24 #include "src/diagnostic/diagnostic.h" 25 #include "src/program_builder.h" 26 27 namespace tint { 28 namespace writer { 29 30 /// Helper methods for generators which are creating text output 31 class TextGenerator { 32 public: 33 /// Line holds a single line of text 34 struct Line { 35 /// The indentation of the line in whitespaces 36 uint32_t indent = 0; 37 /// The content of the line, without a trailing newline character 38 std::string content; 39 }; 40 41 /// TextBuffer holds a list of lines of text. 42 struct TextBuffer { 43 // Constructor 44 TextBuffer(); 45 46 // Destructor 47 ~TextBuffer(); 48 49 /// IncrementIndent increases the indentation of lines that will be written 50 /// to the TextBuffer 51 void IncrementIndent(); 52 53 /// DecrementIndent decreases the indentation of lines that will be written 54 /// to the TextBuffer 55 void DecrementIndent(); 56 57 /// Appends the line to the end of the TextBuffer 58 /// @param line the line to append to the TextBuffer 59 void Append(const std::string& line); 60 61 /// Inserts the line to the TextBuffer before the line with index `before` 62 /// @param line the line to append to the TextBuffer 63 /// @param before the zero-based index of the line to insert the text before 64 /// @param indent the indentation to apply to the inserted lines 65 void Insert(const std::string& line, size_t before, uint32_t indent); 66 67 /// Appends the lines of `tb` to the end of this TextBuffer 68 /// @param tb the TextBuffer to append to the end of this TextBuffer 69 void Append(const TextBuffer& tb); 70 71 /// Inserts the lines of `tb` to the TextBuffer before the line with index 72 /// `before` 73 /// @param tb the TextBuffer to insert into this TextBuffer 74 /// @param before the zero-based index of the line to insert the text before 75 /// @param indent the indentation to apply to the inserted lines 76 void Insert(const TextBuffer& tb, size_t before, uint32_t indent); 77 78 /// @returns the buffer's content as a single string 79 /// @param indent additional indentation to apply to each line 80 std::string String(uint32_t indent = 0) const; 81 82 /// The current indentation of the TextBuffer. Lines appended to the 83 /// TextBuffer will use this indentation. 84 uint32_t current_indent = 0; 85 86 /// The lines 87 std::vector<Line> lines; 88 }; 89 90 /// Constructor 91 /// @param program the program used by the generator 92 explicit TextGenerator(const Program* program); 93 ~TextGenerator(); 94 95 /// Increment the emitter indent level increment_indent()96 void increment_indent() { current_buffer_->IncrementIndent(); } 97 /// Decrement the emitter indent level decrement_indent()98 void decrement_indent() { current_buffer_->DecrementIndent(); } 99 100 /// @returns the result data result()101 std::string result() const { return main_buffer_.String(); } 102 103 /// @returns the list of diagnostics raised by the generator. Diagnostics()104 const diag::List& Diagnostics() const { return diagnostics_; } 105 106 /// @returns the error error()107 std::string error() const { return diagnostics_.str(); } 108 109 /// @return a new, unique identifier with the given prefix. 110 /// @param prefix optional prefix to apply to the generated identifier. If 111 /// empty "tint_symbol" will be used. 112 std::string UniqueIdentifier(const std::string& prefix = ""); 113 114 /// @param s the semantic structure 115 /// @returns the name of the structure, taking special care of builtin 116 /// structures that start with double underscores. If the structure is a 117 /// builtin, then the returned name will be a unique name without the leading 118 /// underscores. 119 std::string StructName(const sem::Struct* s); 120 121 /// @param str the string 122 /// @param suffix the suffix to remove 123 /// @return returns str without the provided trailing suffix string. If str 124 /// doesn't end with suffix, str is returned unchanged. 125 std::string TrimSuffix(std::string str, const std::string& suffix); 126 127 protected: 128 /// LineWriter is a helper that acts as a string buffer, who's content is 129 /// emitted to the TextBuffer as a single line on destruction. 130 struct LineWriter { 131 public: 132 /// Constructor 133 /// @param buffer the TextBuffer that the LineWriter will append its 134 /// content to on destruction, at the end of the buffer. 135 explicit LineWriter(TextBuffer* buffer); 136 137 /// Move constructor 138 /// @param rhs the LineWriter to move 139 LineWriter(LineWriter&& rhs); 140 /// Destructor 141 ~LineWriter(); 142 143 /// @returns the ostringstream 144 operator std::ostream&() { return os; } 145 146 /// @param rhs the value to write to the line 147 /// @returns the ostream so calls can be chained 148 template <typename T> 149 std::ostream& operator<<(T&& rhs) { 150 return os << std::forward<T>(rhs); 151 } 152 153 private: 154 LineWriter(const LineWriter&) = delete; 155 LineWriter& operator=(const LineWriter&) = delete; 156 157 std::ostringstream os; 158 TextBuffer* buffer; 159 }; 160 161 /// Helper for writing a '(' on construction and a ')' destruction. 162 struct ScopedParen { 163 /// Constructor 164 /// @param stream the std::ostream that will be written to 165 explicit ScopedParen(std::ostream& stream); 166 /// Destructor 167 ~ScopedParen(); 168 169 private: 170 ScopedParen(ScopedParen&& rhs) = delete; 171 ScopedParen(const ScopedParen&) = delete; 172 ScopedParen& operator=(const ScopedParen&) = delete; 173 std::ostream& s; 174 }; 175 176 /// Helper for incrementing indentation on construction and decrementing 177 /// indentation on destruction. 178 struct ScopedIndent { 179 /// Constructor 180 /// @param buffer the TextBuffer that the ScopedIndent will indent 181 explicit ScopedIndent(TextBuffer* buffer); 182 /// Constructor 183 /// @param generator ScopedIndent will indent the generator's 184 /// `current_buffer_` 185 explicit ScopedIndent(TextGenerator* generator); 186 /// Destructor 187 ~ScopedIndent(); 188 189 private: 190 ScopedIndent(ScopedIndent&& rhs) = delete; 191 ScopedIndent(const ScopedIndent&) = delete; 192 ScopedIndent& operator=(const ScopedIndent&) = delete; 193 TextBuffer* buffer_; 194 }; 195 196 /// @returns the resolved type of the ast::Expression `expr` 197 /// @param expr the expression TypeOf(const ast::Expression * expr)198 const sem::Type* TypeOf(const ast::Expression* expr) const { 199 return builder_.TypeOf(expr); 200 } 201 202 /// @returns the resolved type of the ast::Type `type` 203 /// @param type the type TypeOf(const ast::Type * type)204 const sem::Type* TypeOf(const ast::Type* type) const { 205 return builder_.TypeOf(type); 206 } 207 208 /// @returns the resolved type of the ast::TypeDecl `type_decl` 209 /// @param type_decl the type TypeOf(const ast::TypeDecl * type_decl)210 const sem::Type* TypeOf(const ast::TypeDecl* type_decl) const { 211 return builder_.TypeOf(type_decl); 212 } 213 214 /// @returns a new LineWriter, used for buffering and writing a line to 215 /// the end of #current_buffer_. line()216 LineWriter line() { return LineWriter(current_buffer_); } 217 218 /// @param buffer the TextBuffer to write the line to 219 /// @returns a new LineWriter, used for buffering and writing a line to 220 /// the end of `buffer`. line(TextBuffer * buffer)221 static LineWriter line(TextBuffer* buffer) { return LineWriter(buffer); } 222 223 /// The program 224 Program const* const program_; 225 /// A ProgramBuilder that thinly wraps program_ 226 ProgramBuilder builder_; 227 /// Diagnostics generated by the generator 228 diag::List diagnostics_; 229 /// The buffer the TextGenerator is currently appending lines to 230 TextBuffer* current_buffer_ = &main_buffer_; 231 232 private: 233 /// The primary text buffer that the generator will emit 234 TextBuffer main_buffer_; 235 /// Map of builtin structure to unique generated name 236 std::unordered_map<const sem::Struct*, std::string> builtin_struct_names_; 237 }; 238 239 } // namespace writer 240 } // namespace tint 241 242 #endif // SRC_WRITER_TEXT_GENERATOR_H_ 243