forked from chipsalliance/verible
-
Notifications
You must be signed in to change notification settings - Fork 0
/
state_node.h
178 lines (148 loc) · 7.48 KB
/
state_node.h
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
172
173
174
175
176
177
178
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VERIBLE_COMMON_FORMATTING_STATE_NODE_H_
#define VERIBLE_COMMON_FORMATTING_STATE_NODE_H_
#include <cstddef>
#include <iosfwd>
#include <iterator>
#include <memory>
#include <stack>
#include <vector>
#include "common/formatting/basic_format_style.h"
#include "common/formatting/format_token.h"
#include "common/formatting/unwrapped_line.h"
#include "common/util/container_iterator_range.h"
namespace verible {
// A StateNode is used to keep a formatting state as the tokens of an
// UnwrappedLine are searched left to right. Each StateNode represents one
// formatting decision: wrap or not-wrap. Each StateNode maintains a pointer
// to its parent state, which is used for backtracking once a solution
// is reached. StateNode is language-agnostic.
// StateNode is purely an implementation detail of line_wrap_searcher.cc.
struct StateNode {
typedef std::vector<PreFormatToken> path_type;
typedef container_iterator_range<path_type::const_iterator> range_type;
// The StateNode that has an edge to this StateNode, to backtrack once a final
// state is reached.
std::shared_ptr<const StateNode> prev_state;
// Iterator range marking the unexplored decisions beyond the current token.
// TODO(fangism): make the iterator type a template parameter. Might help
// with mocking and testing.
range_type undecided_path;
// Explores one of the SpacingDecision choices.
SpacingDecision spacing_choice = SpacingDecision::Preserve;
// The current column position, this increases with every token that is
// appended onto the current line, and resets to the indentation level
// (plus wrapping) with every line break.
int current_column = 0;
// The total cost along this decision path. This monotonically increases
// with each decision explored.
int cumulative_cost = 0;
// Kludge: in the event of a wrapped multi-line token, the current_column
// position and the raw token text length are insufficient to infer what
// the spaces before the format token are because current_column is only
// based on the substring of text after the last newline. To be able to
// reconstruct the pre-format-token spacing, we must record it.
// This is initialized with a sentinel value of -1 as a safety precaution
// to guard against accidental use.
int wrap_multiline_token_spaces_before = -1;
// Keeps track of column positions of every level of wrapping, as determined
// by balanced group delimiters such as braces, brackets, parentheses.
// These column positions correspond to either the current indentation level
// plus wrapping or the column position of the nearest group-opening
// delimiter.
// TODO(b/135730018): re-implement to minimize copying of stacks.
// For example, use pointer or iterator to previous stack update.
std::stack<int> wrap_column_positions;
// Constructor for the root node of the search path, with no parent.
// This automatically places the first token at the beginning of a new line
// for position tracking purposes.
// If the UnwrappedLine has only one token or is empty, the initial state
// will be Done().
StateNode(const UnwrappedLine& uwline, const BasicFormatStyle& style);
// Constructor for nodes that represent new wrap decision trees to explore.
// 'spacing_choice' reflects the decision being explored, e.g. append, wrap,
// preserve.
StateNode(const std::shared_ptr<const StateNode>& parent,
const BasicFormatStyle& style, SpacingDecision spacing_choice);
// Returns true when the undecided_path is empty.
// The search is over when there are no more decisions to explore.
bool Done() const { return undecided_path.begin() == undecided_path.end(); }
// Returns a reference to the token that is being acted upon in this state.
const PreFormatToken& GetCurrentToken() const {
// The undecided_path always starts at the position after the current token.
return *(undecided_path.begin() - 1);
}
// Returns a reference to the token that considered for wrapping vs.
// appending.
const PreFormatToken& GetNextToken() const {
// The undecided_path always starts at the position after the current token.
return *undecided_path.begin();
}
// Returns pointer to previous state before this decision node.
// This functions as a forward-iterator going up the state ancestry chain.
const StateNode* next() const { return prev_state.get(); }
// Returns true if this state was initialized with an unwrapped line and
// has no parent state.
bool IsRootState() const { return prev_state == nullptr; }
// Returns the total number of nodes in state ancestry, including itself.
// This occurs in O(N) time, and is only suitable for testing/debug.
size_t Depth() const {
size_t depth = 1;
const auto* iter = this;
while (!iter->IsRootState()) {
++depth;
iter = iter->prev_state.get();
}
return depth;
}
// Produce next state by appending a token if the result stays under the
// column limit, or breaking onto a new line if required.
static std::shared_ptr<const StateNode> AppendIfItFits(
const std::shared_ptr<const StateNode>& current_state,
const BasicFormatStyle& style);
// Repeatedly apply AppendIfItFits() until Done() with formatting.
// TODO(b/134711965): We may want a variant that preserves spaces too.
static std::shared_ptr<const StateNode> QuickFinish(
const std::shared_ptr<const StateNode>& current_state,
const BasicFormatStyle& style);
// Comparator provides an ordering of which paths should be explored
// when maintained in a priority queue. For Dijsktra-style algorithms,
// we want to explore the min-cost paths first.
bool operator<(const StateNode& r) const {
return cumulative_cost < r.cumulative_cost ||
// TODO(b/145558510): Favor solutions that use fewer lines.
// To do that would require counting number of wrap decisions,
// which is slow, unless we keep track of that number in StateNode.
// tie-breaker: All else being equal, use terminal column position.
(cumulative_cost == r.cumulative_cost &&
current_column < r.current_column);
}
// Applies decisions from a path search to the set of format tokens in a
// FormattedExcerpt. 'this' is the last decision in a tree that encodes
// wrap decisions (through ancestry chain: prev_state) all the way back to
// the first token in the original UnwrappedLine (that was used to
// initialize the root state).
void ReconstructFormatDecisions(FormattedExcerpt*) const;
private:
const PreFormatToken& _GetPreviousToken() const;
int _UpdateColumnPosition();
void _UpdateCumulativeCost(const BasicFormatStyle&, int column_for_penalty);
void _OpenGroupBalance(const BasicFormatStyle&);
void _CloseGroupBalance();
};
// Human-readable representation for debugging only.
std::ostream& operator<<(std::ostream&, const StateNode&);
} // namespace verible
#endif // VERIBLE_COMMON_FORMATTING_STATE_NODE_H_