CIRCT 23.0.0git
Loading...
Searching...
No Matches
PrettyPrinter.h
Go to the documentation of this file.
1//===- PrettyPrinter.h - Pretty printing ----------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This implements a pretty-printer.
10// "PrettyPrinting", Derek C. Oppen, 1980.
11// https://dx.doi.org/10.1145/357114.357115
12//
13// This was selected as it is linear in number of tokens O(n) and requires
14// memory O(linewidth).
15//
16// See PrettyPrinter.cpp for more information.
17//
18//===----------------------------------------------------------------------===//
19
20#ifndef CIRCT_SUPPORT_PRETTYPRINTER_H
21#define CIRCT_SUPPORT_PRETTYPRINTER_H
22
23#include "circt/Support/LLVM.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/Support/ErrorHandling.h"
27#include "llvm/Support/SaveAndRestore.h"
28
29#include <cstdint>
30#include <deque>
31#include <limits>
32
33namespace circt {
34namespace pretty {
35
36//===----------------------------------------------------------------------===//
37// Tokens
38//===----------------------------------------------------------------------===//
39
40/// Style of breaking within a group:
41/// - Consistent: all fits or all breaks.
42/// - Inconsistent: best fit, break where needed.
43/// - Never: force no breaking including nested groups.
45
46/// Style of indent when starting a group:
47/// - Visual: offset is relative to current column.
48/// - Block: offset is relative to current base indentation.
49enum class IndentStyle { Visual, Block };
50
51class Token {
52public:
53 enum class Kind { String, Break, Begin, End, Callback };
54
55 struct TokenInfo {
56 Kind kind; // Common initial sequence.
57 };
58 struct StringInfo : public TokenInfo {
59 uint32_t len;
60 const char *str;
61 };
62 struct BreakInfo : public TokenInfo {
63 uint32_t spaces; // How many spaces to emit when not broken.
64 int32_t offset; // How many spaces to emit when broken.
65 bool neverbreak; // If set, behaves like break except this always 'fits'.
66 };
67 struct BeginInfo : public TokenInfo {
68 int32_t offset; // Adjust base indentation by this amount.
71 };
72 struct EndInfo : public TokenInfo {
73 // Nothing
74 };
75 // This can be used to associate a callback with the print event on the
76 // tokens stream. Since tokens strictly follow FIFO order on a queue, each
77 // CallbackToken can uniquely identify the data it is associated with, when it
78 // is added and popped from the queue.
79 struct CallbackInfo : public TokenInfo {};
80
81private:
82 union {
90
91protected:
92 template <Kind k, typename T>
93 static auto &getInfoImpl(T &t) {
94 if constexpr (k == Kind::String)
95 return t.data.stringInfo;
96 if constexpr (k == Kind::Break)
97 return t.data.breakInfo;
98 if constexpr (k == Kind::Begin)
99 return t.data.beginInfo;
100 if constexpr (k == Kind::End)
101 return t.data.endInfo;
102 if constexpr (k == Kind::Callback)
103 return t.data.callbackInfo;
104 llvm_unreachable("unhandled token kind");
105 }
106
107 Token(Kind k) { data.info.kind = k; }
108
109public:
110 Kind getKind() const { return data.info.kind; }
111};
112
113/// Helper class to CRTP-derive common functions.
114template <class DerivedT, Token::Kind DerivedKind>
115struct TokenBase : public Token {
116 static bool classof(const Token *t) { return t->getKind() == DerivedKind; }
117
118protected:
119 TokenBase() : Token(DerivedKind) {}
120
121 using InfoType = std::remove_reference_t<std::invoke_result_t<
122 decltype(Token::getInfoImpl<DerivedKind, Token &>), Token &>>;
123
124 InfoType &getInfoMut() { return Token::getInfoImpl<DerivedKind>(*this); }
125
126 const InfoType &getInfo() const {
127 return Token::getInfoImpl<DerivedKind>(*this);
128 }
129
130 template <typename... Args>
131 void initialize(Args &&...args) {
132 getInfoMut() = InfoType{{DerivedKind}, args...};
133 }
134};
135
136/// Token types.
137
138struct StringToken : public TokenBase<StringToken, Token::Kind::String> {
139 StringToken(llvm::StringRef text) {
140 assert(text.size() == (uint32_t)text.size());
141 initialize((uint32_t)text.size(), text.data());
142 }
143 StringRef text() const { return StringRef(getInfo().str, getInfo().len); }
144};
145
146struct BreakToken : public TokenBase<BreakToken, Token::Kind::Break> {
147 BreakToken(uint32_t spaces = 1, int32_t offset = 0, bool neverbreak = false) {
149 }
150 uint32_t spaces() const { return getInfo().spaces; }
151 int32_t offset() const { return getInfo().offset; }
152 bool neverbreak() const { return getInfo().neverbreak; }
153};
154
155struct BeginToken : public TokenBase<BeginToken, Token::Kind::Begin> {
160 int32_t offset() const { return getInfo().offset; }
161 Breaks breaks() const { return getInfo().breaks; }
162 IndentStyle style() const { return getInfo().style; }
163};
164
165struct EndToken : public TokenBase<EndToken, Token::Kind::End> {};
166
167struct CallbackToken : public TokenBase<CallbackToken, Token::Kind::Callback> {
168 CallbackToken() = default;
169};
170
171//===----------------------------------------------------------------------===//
172// PrettyPrinter
173//===----------------------------------------------------------------------===//
174
176public:
177 /// Listener to Token storage events.
178 struct Listener {
179 virtual ~Listener();
180 /// No tokens referencing external memory are present.
181 virtual void clear(){};
182 /// Listener for print event.
183 virtual void print(){};
184 };
185
186 /// PrettyPrinter for specified stream.
187 /// - margin: line width.
188 /// - baseIndent: always indent at least this much (starting 'indent' value).
189 /// - currentColumn: current column, used to calculate space remaining.
190 /// - maxStartingIndent: max column indentation starts at, must be >= margin.
191 /// - noWrap: disable line wrapping; only explicit newlines break.
192 PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent = 0,
193 uint32_t currentColumn = 0,
194 uint32_t maxStartingIndent = kInfinity / 4,
195 Listener *listener = nullptr, bool noWrap = false)
196 : space(margin - std::max(currentColumn, baseIndent)),
197 defaultFrame{baseIndent, PrintBreaks::Inconsistent}, indent(baseIndent),
201 assert(maxStartingIndent > baseIndent);
202 assert(margin > currentColumn);
203 // Ensure first print advances to at least baseIndent.
205 baseIndent > currentColumn ? baseIndent - currentColumn : 0;
206 }
208
209 /// Add token for printing. In Oppen, this is "scan".
210 void add(Token t);
211
212 /// Add a range of tokens.
213 template <typename R>
214 void addTokens(R &&tokens) {
215 // Don't invoke listener until range processed, we own it now.
216 {
217 llvm::SaveAndRestore<bool> save(donotClear, true);
218 for (Token &t : tokens)
219 add(t);
220 }
221 // Invoke it now if appropriate.
222 if (scanStack.empty())
223 clear();
224 }
225
226 void eof();
227
228 void setListener(Listener *newListener) { listener = newListener; };
229 auto *getListener() const { return listener; }
230
231 static constexpr uint32_t kInfinity = (1U << 15) - 1;
232
233private:
234 /// Format token with tracked size.
236 Token token; /// underlying token
237 int32_t size; /// calculate size when positive.
238 };
239
240 /// Breaking style for a printStack entry.
241 /// This is "Breaks" values with extra for "Fits".
242 /// Breaks::Never is "AlwaysFits" here.
244
245 /// Printing information for active scope, stored in printStack.
246 struct PrintEntry {
247 uint32_t offset;
249 };
250
251 /// Print out tokens we know sizes for, and drop from token buffer.
252 void advanceLeft();
253
254 /// Break encountered, set sizes of begin/breaks in scanStack we now know.
255 void checkStack();
256
257 /// Check if there's enough tokens to hit width, if so print.
258 /// If scan size is wider than line, it's infinity.
259 void checkStream();
260
261 /// Print a token, maintaining printStack for context.
262 void print(const FormattedToken &f);
263
264 /// Clear token buffer, scanStack must be empty.
265 void clear();
266
267 /// Reset leftTotal and tokenOffset, rebase size data and scanStack indices.
268 void rebaseIfNeeded();
269
270 /// Get current printing frame.
272 return printStack.empty() ? defaultFrame : printStack.back();
273 }
274
275 /// Characters left on this line.
276 int32_t space;
277
278 /// Sizes: printed, enqueued
279 int32_t leftTotal;
280 int32_t rightTotal;
281
282 /// Unprinted tokens, combination of 'token' and 'size' in Oppen.
283 std::deque<FormattedToken> tokens;
284 /// index of first token, for resolving scanStack entries.
285 uint32_t tokenOffset = 0;
286
287 /// Stack of begin/break tokens, adjust by tokenOffset to index into tokens.
288 std::deque<uint32_t> scanStack;
289
290 /// Stack of printing contexts (indentation + breaking behavior).
291 SmallVector<PrintEntry> printStack;
292
293 /// Printing context when stack is empty.
295
296 /// Number of "AlwaysFits" on print stack.
297 uint32_t alwaysFits = 0;
298
299 /// Current indentation level
300 uint32_t indent;
301
302 /// Whitespace to print before next, tracked to avoid trailing whitespace.
304
305 /// Target line width.
306 const uint32_t margin;
307
308 /// Maximum starting indentation level (default=kInfinity/4).
309 /// Useful to continue indentation past margin while still providing a limit
310 /// to avoid pathological output and for consumption by tools with limits.
311 const uint32_t maxStartingIndent;
312
313 /// Output stream.
314 llvm::raw_ostream &os;
315
316 /// Hook for Token storage events.
317 Listener *listener = nullptr;
318
319 /// Flag to identify a state when the clear cannot be called.
320 bool donotClear = false;
321
322 /// Disables line wrapping on non-newline whitespace
323 const bool noWrap;
324
325 /// Threshold for walking scan state and "rebasing" totals/offsets.
326 static constexpr decltype(leftTotal) rebaseThreshold =
327 1UL << (std::numeric_limits<decltype(leftTotal)>::digits - 3);
328};
329
330} // end namespace pretty
331} // end namespace circt
332
333#endif // CIRCT_SUPPORT_PRETTYPRINTER_H
assert(baseType &&"element must be base type")
SmallVector< PrintEntry > printStack
Stack of printing contexts (indentation + breaking behavior).
void rebaseIfNeeded()
Reset leftTotal and tokenOffset, rebase size data and scanStack indices.
void add(Token t)
Add token for printing. In Oppen, this is "scan".
bool donotClear
Flag to identify a state when the clear cannot be called.
void addTokens(R &&tokens)
Add a range of tokens.
std::deque< FormattedToken > tokens
Unprinted tokens, combination of 'token' and 'size' in Oppen.
Listener * listener
Hook for Token storage events.
PrintBreaks
Breaking style for a printStack entry.
void setListener(Listener *newListener)
PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent=0, uint32_t currentColumn=0, uint32_t maxStartingIndent=kInfinity/4, Listener *listener=nullptr, bool noWrap=false)
PrettyPrinter for specified stream.
uint32_t indent
Current indentation level.
uint32_t tokenOffset
index of first token, for resolving scanStack entries.
const uint32_t maxStartingIndent
Maximum starting indentation level (default=kInfinity/4).
int32_t space
Characters left on this line.
static constexpr uint32_t kInfinity
llvm::raw_ostream & os
Output stream.
static constexpr decltype(leftTotal) rebaseThreshold
Threshold for walking scan state and "rebasing" totals/offsets.
void checkStack()
Break encountered, set sizes of begin/breaks in scanStack we now know.
void print(const FormattedToken &f)
Print a token, maintaining printStack for context.
const uint32_t margin
Target line width.
uint32_t pendingIndentation
Whitespace to print before next, tracked to avoid trailing whitespace.
int32_t leftTotal
Sizes: printed, enqueued.
std::deque< uint32_t > scanStack
Stack of begin/break tokens, adjust by tokenOffset to index into tokens.
const bool noWrap
Disables line wrapping on non-newline whitespace.
void clear()
Clear token buffer, scanStack must be empty.
void advanceLeft()
Print out tokens we know sizes for, and drop from token buffer.
uint32_t alwaysFits
Number of "AlwaysFits" on print stack.
void checkStream()
Check if there's enough tokens to hit width, if so print.
const PrintEntry defaultFrame
Printing context when stack is empty.
auto & getPrintFrame()
Get current printing frame.
static auto & getInfoImpl(T &t)
CallbackInfo callbackInfo
union circt::pretty::Token::@7 data
IndentStyle
Style of indent when starting a group:
Breaks
Style of breaking within a group:
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
IndentStyle style() const
BeginToken(int32_t offset=2, Breaks breaks=Breaks::Inconsistent, IndentStyle style=IndentStyle::Visual)
BreakToken(uint32_t spaces=1, int32_t offset=0, bool neverbreak=false)
Format token with tracked size.
Listener to Token storage events.
virtual void clear()
No tokens referencing external memory are present.
virtual ~Listener()
Destructor, anchor.
virtual void print()
Listener for print event.
Printing information for active scope, stored in printStack.
StringToken(llvm::StringRef text)
Helper class to CRTP-derive common functions.
const InfoType & getInfo() const
std::remove_reference_t< std::invoke_result_t< decltype(Token::getInfoImpl< DerivedKind, Token & >), Token & > > InfoType
void initialize(Args &&...args)
static bool classof(const Token *t)