CIRCT 24.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: 0 is no wrapping, saturates to kMaxMargin.
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 PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent = 0,
192 uint32_t currentColumn = 0,
193 uint32_t maxStartingIndent = kMaxMargin / 2,
194 Listener *listener = nullptr)
195 : defaultFrame{baseIndent, PrintBreaks::Inconsistent}, indent(baseIndent),
196 margin(std::min(kMaxMargin, margin)),
197 maxStartingIndent(std::max(maxStartingIndent, this->margin)), os(os),
198 listener(listener), noWrap(this->margin == 0) {
199 // Compute space left using computed margin.
200 space = this->margin - std::max(currentColumn, baseIndent);
201
202 // Ensure first print advances to at least baseIndent.
204 baseIndent > currentColumn ? baseIndent - currentColumn : 0;
205 }
207
208 /// Add token for printing. In Oppen, this is "scan".
209 void add(Token t);
210
211 /// Add a range of tokens.
212 template <typename R>
213 void addTokens(R &&tokens) {
214 // Don't invoke listener until range processed, we own it now.
215 {
216 llvm::SaveAndRestore<bool> save(donotClear, true);
217 for (Token &t : tokens)
218 add(t);
219 }
220 // Invoke it now if appropriate.
221 if (scanStack.empty())
222 clear();
223 }
224
225 void eof();
226
227 void setListener(Listener *newListener) { listener = newListener; };
228 auto *getListener() const { return listener; }
229
230 static constexpr uint32_t kInfinity = (1U << 15) - 1;
231
232 /// Largest supported target line length.
233 static constexpr uint32_t kMaxMargin = kInfinity / 2;
234
235private:
236 /// Format token with tracked size.
238 Token token; /// underlying token
239 int32_t size; /// calculate size when positive.
240 };
241
242 /// Breaking style for a printStack entry.
243 /// This is "Breaks" values with extra for "Fits".
244 /// Breaks::Never is "AlwaysFits" here.
246
247 /// Printing information for active scope, stored in printStack.
248 struct PrintEntry {
249 uint32_t offset;
251 };
252
253 /// Print out tokens we know sizes for, and drop from token buffer.
254 void advanceLeft();
255
256 /// Break encountered, set sizes of begin/breaks in scanStack we now know.
257 void checkStack();
258
259 /// Check if there's enough tokens to hit width, if so print.
260 /// If scan size is wider than line, it's infinity.
261 void checkStream();
262
263 /// Print a token, maintaining printStack for context.
264 void print(const FormattedToken &f);
265
266 /// Clear token buffer, scanStack must be empty.
267 void clear();
268
269 /// Reset leftTotal and tokenOffset, rebase size data and scanStack indices.
270 void rebaseIfNeeded();
271
272 /// Get current printing frame.
274 return printStack.empty() ? defaultFrame : printStack.back();
275 }
276
277 /// Characters left on this line.
278 int32_t space;
279
280 /// Sizes: printed, enqueued
281 int32_t leftTotal;
282 int32_t rightTotal;
283
284 /// Unprinted tokens, combination of 'token' and 'size' in Oppen.
285 std::deque<FormattedToken> tokens;
286 /// index of first token, for resolving scanStack entries.
287 uint32_t tokenOffset = 0;
288
289 /// Stack of begin/break tokens, adjust by tokenOffset to index into tokens.
290 std::deque<uint32_t> scanStack;
291
292 /// Stack of printing contexts (indentation + breaking behavior).
293 SmallVector<PrintEntry> printStack;
294
295 /// Printing context when stack is empty.
297
298 /// Number of "AlwaysFits" on print stack.
299 uint32_t alwaysFits = 0;
300
301 /// Current indentation level
302 uint32_t indent;
303
304 /// Whitespace to print before next, tracked to avoid trailing whitespace.
306
307 /// Target line width.
308 const uint32_t margin;
309
310 /// Maximum starting indentation level (default=kInfinity/4).
311 /// Useful to continue indentation past margin while still providing a limit
312 /// to avoid pathological output and for consumption by tools with limits.
313 const uint32_t maxStartingIndent;
314
315 /// Output stream.
316 llvm::raw_ostream &os;
317
318 /// Hook for Token storage events.
319 Listener *listener = nullptr;
320
321 /// Flag to identify a state when the clear cannot be called.
322 bool donotClear = false;
323
324 /// Disables line wrapping on non-newline whitespace
325 const bool noWrap;
326
327 /// Threshold for walking scan state and "rebasing" totals/offsets.
328 static constexpr decltype(leftTotal) rebaseThreshold =
329 1UL << (std::numeric_limits<decltype(leftTotal)>::digits - 3);
330};
331
332} // end namespace pretty
333} // end namespace circt
334
335#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.
static constexpr uint32_t kMaxMargin
Largest supported target line length.
PrintBreaks
Breaking style for a printStack entry.
void setListener(Listener *newListener)
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.
PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent=0, uint32_t currentColumn=0, uint32_t maxStartingIndent=kMaxMargin/2, Listener *listener=nullptr)
PrettyPrinter for specified stream.
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)