Skip to content

Commit 9c6f5c9

Browse files
committed
Add comments to PropertyTableBuilder
1 parent c79929f commit 9c6f5c9

File tree

1 file changed

+67
-20
lines changed

1 file changed

+67
-20
lines changed

src/compiler/build_tables/property_table_builder.cc

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ using std::map;
2020
namespace tree_sitter {
2121
namespace build_tables {
2222

23+
// A position within a selector for a particular rule set.
24+
// For example, in a selector like `a > b`, this might
25+
// describe the state of having descended into an `a`,
26+
// but not a `b`.
2327
struct PropertyItem {
2428
unsigned rule_id;
2529
unsigned selector_id;
@@ -41,6 +45,9 @@ struct PropertyItem {
4145
}
4246
};
4347

48+
// A set of possible positions within different selectors.
49+
// This directly represents a state of the property-matching
50+
// state machine.
4451
struct PropertyItemSet {
4552
set<PropertyItem> entries;
4653

@@ -49,13 +56,34 @@ struct PropertyItemSet {
4956
}
5057
};
5158

59+
// A set of properties that matched via a certain selector.
60+
// These are ordered according to the usual CSS rules:
61+
// specificity, falling back to the order in the original sheet.
62+
struct PropertySelectorMatch {
63+
unsigned specificity;
64+
unsigned rule_id;
65+
unsigned selector_id;
66+
const PropertySet *property_set;
67+
68+
bool operator<(const PropertySelectorMatch &other) const {
69+
if (specificity < other.specificity) return true;
70+
if (specificity > other.specificity) return false;
71+
if (rule_id < other.rule_id) return true;
72+
if (rule_id > other.rule_id) return false;
73+
return selector_id < other.selector_id;
74+
}
75+
};
76+
5277
} // namespace build_tables
5378
} // namespace tree_sitter
5479

5580
namespace std {
5681

5782
using tree_sitter::util::hash_combine;
5883

84+
// PropertyItemSets must be hashed because in the process of building
85+
// the table, we maintain a map of existing property item sets to
86+
// state ids.
5987
template <>
6088
struct hash<tree_sitter::build_tables::PropertyItemSet> {
6189
size_t operator()(const tree_sitter::build_tables::PropertyItemSet &item_set) const {
@@ -70,6 +98,8 @@ struct hash<tree_sitter::build_tables::PropertyItemSet> {
7098
}
7199
};
72100

101+
// PropertyTransitions must be hashed because we represent state
102+
// transitions as a map of PropertyTransitions to successor PropertyItemSets.
73103
template <>
74104
struct hash<tree_sitter::PropertyTransition> {
75105
size_t operator()(const tree_sitter::PropertyTransition &transition) const {
@@ -82,6 +112,7 @@ struct hash<tree_sitter::PropertyTransition> {
82112
}
83113
};
84114

115+
// PropertySets must be hashed so that we can use a map to dedup them.
85116
template <>
86117
struct hash<tree_sitter::PropertySet> {
87118
size_t operator()(const tree_sitter::PropertySet &set) const {
@@ -103,21 +134,6 @@ namespace build_tables {
103134
typedef unsigned StateId;
104135
typedef unsigned PropertySetId;
105136

106-
struct PropertySelectorMatch {
107-
unsigned specificity;
108-
unsigned rule_id;
109-
unsigned selector_id;
110-
const PropertySet *property_set;
111-
112-
bool operator<(const PropertySelectorMatch &other) const {
113-
if (specificity < other.specificity) return true;
114-
if (specificity > other.specificity) return false;
115-
if (rule_id < other.rule_id) return true;
116-
if (rule_id > other.rule_id) return false;
117-
return selector_id < other.selector_id;
118-
}
119-
};
120-
121137
struct PropertyTableBuilder {
122138
PropertySheet sheet;
123139
PropertyTable result;
@@ -150,6 +166,8 @@ struct PropertyTableBuilder {
150166
return result;
151167
}
152168

169+
// Different item sets can actually produce the same state, so the
170+
// states need to be explicitly deduped as a post-processing step.
153171
void remove_duplicate_states() {
154172
map<StateId, StateId> replacements;
155173

@@ -210,6 +228,8 @@ struct PropertyTableBuilder {
210228
}
211229
}
212230

231+
// Get the next part of the selector that needs to be matched for a given item.
232+
// This returns null if the item has consumed its entire selector.
213233
const PropertySelectorStep *next_step_for_item(const PropertyItem &item) {
214234
const PropertySelector &selector = sheet[item.rule_id].selectors[item.selector_id];
215235
if (item.step_id < selector.size()) {
@@ -219,6 +239,8 @@ struct PropertyTableBuilder {
219239
}
220240
}
221241

242+
// Get the previous part of the selector that was matched for a given item.
243+
// This returns null if the item has not consumed anything.
222244
const PropertySelectorStep *prev_step_for_item(const PropertyItem &item) {
223245
if (item.step_id > 0) {
224246
return &sheet[item.rule_id].selectors[item.selector_id][item.step_id];
@@ -235,27 +257,36 @@ struct PropertyTableBuilder {
235257
return result;
236258
}
237259

238-
bool step_is_superset(const PropertySelectorStep &step, const PropertyTransition &transition) {
260+
// Check if the given state transition matches the given part of a selector.
261+
bool step_matches_transition(const PropertySelectorStep &step, const PropertyTransition &transition) {
239262
return
240263
step.type == transition.type &&
241264
step.named == transition.named &&
242265
(step.index == transition.index || step.index == -1);
243266
}
244267

245268
void populate_state(const PropertyItemSet &item_set, StateId state_id) {
246-
std::unordered_map<PropertyTransition, PropertyItemSet> transitions;
247-
std::vector<PropertySelectorMatch> selector_matches;
269+
unordered_map<PropertyTransition, PropertyItemSet> transitions;
270+
vector<PropertySelectorMatch> selector_matches;
248271

249272
for (const PropertyItem &item : item_set.entries) {
250273
const PropertySelectorStep *next_step = next_step_for_item(item);
274+
275+
// If this item has more elements to match for its selector, then
276+
// there's a state transition for elements that match the next
277+
// part of the selector.
251278
if (next_step) {
252279
transitions[PropertyTransition{
253280
next_step->type,
254281
next_step->named,
255282
next_step->index,
256283
0
257284
}] = PropertyItemSet();
258-
} else {
285+
}
286+
287+
// If the item has matched its entire selector, then the property set
288+
// for the item's rule applies in this state.
289+
else {
259290
const PropertyRule &rule = sheet[item.rule_id];
260291
selector_matches.push_back(PropertySelectorMatch {
261292
specificity_for_selector(rule.selectors[item.selector_id]),
@@ -266,6 +297,8 @@ struct PropertyTableBuilder {
266297
}
267298
}
268299

300+
// For each element that follows an item in this set,
301+
// compute the next item set after descending through that element.
269302
for (auto &pair : transitions) {
270303
PropertyTransition transition = pair.first;
271304
PropertyItemSet &next_item_set = pair.second;
@@ -274,11 +307,18 @@ struct PropertyTableBuilder {
274307
const PropertySelectorStep *next_step = next_step_for_item(item);
275308
const PropertySelectorStep *prev_step = prev_step_for_item(item);
276309
if (next_step) {
277-
if (step_is_superset(*next_step, transition)) {
310+
311+
// If the element matches the next part of the item, advance the
312+
// item to the next part of its selector.
313+
if (step_matches_transition(*next_step, transition)) {
278314
PropertyItem next_item = item;
279315
next_item.step_id++;
280316
next_item_set.entries.insert(next_item);
281317
}
318+
319+
// If the element does not match, and the item is in the middle
320+
// of an immediate child selector, then remove it from the
321+
// next item set. Otherwise, keep it unchanged.
282322
if (!prev_step || !prev_step->is_immediate) {
283323
next_item_set.entries.insert(item);
284324
}
@@ -289,6 +329,9 @@ struct PropertyTableBuilder {
289329
result.states[state_id].transitions.push_back(transition);
290330
}
291331

332+
// Compute the default successor item set - the item set that
333+
// we should advance to if the next element doesn't match any
334+
// of the next elements in the item set's selectors.
292335
PropertyItemSet default_next_item_set;
293336
for (const PropertyItem &item : item_set.entries) {
294337
const PropertySelectorStep *next_step = next_step_for_item(item);
@@ -300,6 +343,9 @@ struct PropertyTableBuilder {
300343

301344
result.states[state_id].default_next_state_id = add_state(default_next_item_set);
302345

346+
// Sort the matching property sets by ascending specificity and by
347+
// their order in the sheet. This way, more specific selectors and later
348+
// rules will override less specific selectors and earlier rules.
303349
PropertySet properties;
304350
std::sort(selector_matches.begin(), selector_matches.end());
305351
for (auto &match : selector_matches) {
@@ -308,6 +354,7 @@ struct PropertyTableBuilder {
308354
}
309355
}
310356

357+
// Add the final property set to the deduped list.
311358
result.states[state_id].property_set_id = add_property_set(properties);
312359
}
313360

0 commit comments

Comments
 (0)