CIRCT 20.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 PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent = 0,
192 uint32_t currentColumn = 0,
193 uint32_t maxStartingIndent = kInfinity / 4,
194 Listener *listener = nullptr)
195 : space(margin - std::max(currentColumn, baseIndent)),
196 defaultFrame{baseIndent, PrintBreaks::Inconsistent}, indent(baseIndent),
200 assert(maxStartingIndent > baseIndent);
201 assert(margin > currentColumn);
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
232private:
233 /// Format token with tracked size.
235 Token token; /// underlying token
236 int32_t size; /// calculate size when positive.
237 };
238
239 /// Breaking style for a printStack entry.
240 /// This is "Breaks" values with extra for "Fits".
241 /// Breaks::Never is "AlwaysFits" here.
243
244 /// Printing information for active scope, stored in printStack.
245 struct PrintEntry {
246 uint32_t offset;
248 };
249
250 /// Print out tokens we know sizes for, and drop from token buffer.
251 void advanceLeft();
252
253 /// Break encountered, set sizes of begin/breaks in scanStack we now know.
254 void checkStack();
255
256 /// Check if there's enough tokens to hit width, if so print.
257 /// If scan size is wider than line, it's infinity.
258 void checkStream();
259
260 /// Print a token, maintaining printStack for context.
261 void print(const FormattedToken &f);
262
263 /// Clear token buffer, scanStack must be empty.
264 void clear();
265
266 /// Reset leftTotal and tokenOffset, rebase size data and scanStack indices.
267 void rebaseIfNeeded();
268
269 /// Get current printing frame.
271 return printStack.empty() ? defaultFrame : printStack.back();
272 }
273
274 /// Characters left on this line.
275 int32_t space;
276
277 /// Sizes: printed, enqueued
278 int32_t leftTotal;
279 int32_t rightTotal;
280
281 /// Unprinted tokens, combination of 'token' and 'size' in Oppen.
282 std::deque<FormattedToken> tokens;
283 /// index of first token, for resolving scanStack entries.
284 uint32_t tokenOffset = 0;
285
286 /// Stack of begin/break tokens, adjust by tokenOffset to index into tokens.
287 std::deque<uint32_t> scanStack;
288
289 /// Stack of printing contexts (indentation + breaking behavior).
290 SmallVector<PrintEntry> printStack;
291
292 /// Printing context when stack is empty.
294
295 /// Number of "AlwaysFits" on print stack.
296 uint32_t alwaysFits = 0;
297
298 /// Current indentation level
299 uint32_t indent;
300
301 /// Whitespace to print before next, tracked to avoid trailing whitespace.
303
304 /// Target line width.
305 const uint32_t margin;
306
307 /// Maximum starting indentation level (default=kInfinity/4).
308 /// Useful to continue indentation past margin while still providing a limit
309 /// to avoid pathological output and for consumption by tools with limits.
310 const uint32_t maxStartingIndent;
311
312 /// Output stream.
313 llvm::raw_ostream &os;
314
315 /// Hook for Token storage events.
316 Listener *listener = nullptr;
317
318 /// Flag to identify a state when the clear cannot be called.
319 bool donotClear = false;
320
321 /// Threshold for walking scan state and "rebasing" totals/offsets.
322 static constexpr decltype(leftTotal) rebaseThreshold =
323 1UL << (std::numeric_limits<decltype(leftTotal)>::digits - 3);
324};
325
326} // end namespace pretty
327} // end namespace circt
328
329#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)
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).
PrettyPrinter(llvm::raw_ostream &os, uint32_t margin, uint32_t baseIndent=0, uint32_t currentColumn=0, uint32_t maxStartingIndent=kInfinity/4, Listener *listener=nullptr)
PrettyPrinter for specified stream.
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.
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)
union circt::pretty::Token::@5 data
CallbackInfo callbackInfo
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)