1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! A formatter for SystemRDL.
//!
//! ```text
//! source text
//! |
//! v syntax lossless CST, comments and all
//! v rules one function per node kind
//! v formatter direct emission into a String
//! ```
//!
//! # Why there is no intermediate representation
//!
//! Pretty-printers usually build a document IR (Wadler groups, Oppen's
//! algorithm) because their layout decisions depend on rendered width: whether
//! a list fits on one line cannot be known until everything inside it has been
//! laid out, so the decision has to be deferred and the alternatives measured.
//!
//! None of the rules here are width-dependent. Following the PeakRDL style
//! guide, braces always break, statements are one per line, expressions never
//! break, and a parenthesised list breaks when it holds more than one element.
//! Every one of those is decidable from the tree alone, before a single
//! character is written. With nothing to defer there is nothing for a document
//! IR to represent, so rules write straight into the output buffer.
//!
//! Each decision that does exist is still isolated in its own function
//! returning a layout, rather than being spelled out inline at the point of
//! emission. Should one of them ever need to consult rendered width, it can
//! render flat into a scratch buffer and measure it -- at the size of a
//! register description the cost of that is not worth an IR to avoid.
//!
//! # What the formatter will not do
//!
//! Reformat a file the parser did not fully understand. [`format()`] returns
//! [`FormatError`] when the parse reports errors, because the rules assume a
//! tree shape that error recovery does not guarantee, and rewriting a file
//! whose structure was guessed at is how a formatter corrupts code.
//!
//! Preprocessor directives need no separate rule against them, which is the
//! point of treating even the conditionals as trivia: a `` `ifdef `` whose
//! branches hand a brace back and forth leaves the braces unbalanced, and so is
//! refused by the same check as any other input the parser could not follow.
//! Everything else formats like a comment -- its own line, indented with the
//! code around it, payload untouched. See the module docs in
//! [`crate::syntax::parser`] for why ignoring a conditional cannot corrupt
//! the file.
use crate;
use ;
/// Why no formatted output was produced.
/// Formats SystemRDL source.
///
/// There is nothing to configure, deliberately: a formatter earns its value by
/// ending arguments, not by relocating them into a config file. Indentation is
/// four spaces, which is what the PeakRDL style guide asks for.
///
/// The output is verified before it is returned: see `verify`. A caller that
/// gets `Ok` has a guarantee, not just a hope, that only whitespace moved.
///
/// # Errors
/// [`FormatError::Parse`] if `src` does not parse cleanly. The source is left
/// for the caller to report on rather than being passed through unchanged, so
/// that a broken file is never silently mistaken for a formatted one.
///
/// [`FormatError::Corrupted`] if the formatter has a bug.
/// Checks that formatting moved nothing but whitespace.
///
/// The test suite asserts this over the inputs someone thought to write down.
/// Doing it here instead makes it hold for every input there will ever be,
/// which is what justifies a tool that overwrites source files by default. The
/// cost is one extra lex of the output -- nothing next to the parse that
/// produced it.
///
/// Comments are compared alongside the code, trimmed at the end, because a
/// dropped comment is a real loss even though it changes no behaviour. The
/// trim is what lets the formatter tidy trailing spaces inside one.