@@ -20,6 +20,10 @@ using std::map;
2020namespace tree_sitter {
2121namespace 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`.
2327struct 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.
4451struct 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
5580namespace std {
5681
5782using 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.
5987template <>
6088struct 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.
73103template <>
74104struct 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.
85116template <>
86117struct hash <tree_sitter::PropertySet> {
87118 size_t operator ()(const tree_sitter::PropertySet &set) const {
@@ -103,21 +134,6 @@ namespace build_tables {
103134typedef unsigned StateId;
104135typedef 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-
121137struct 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