/src/libsass/src/ast_sel_unify.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // sass.hpp must go before all system headers to get the |
2 | | // __EXTENSIONS__ fix on Solaris. |
3 | | #include "sass.hpp" |
4 | | |
5 | | #include "ast.hpp" |
6 | | |
7 | | namespace Sass { |
8 | | |
9 | | // ########################################################################## |
10 | | // Returns the contents of a [SelectorList] that matches only |
11 | | // elements that are matched by both [complex1] and [complex2]. |
12 | | // If no such list can be produced, returns `null`. |
13 | | // ########################################################################## |
14 | | // ToDo: fine-tune API to avoid unnecessary wrapper allocations |
15 | | // ########################################################################## |
16 | | sass::vector<sass::vector<SelectorComponentObj>> unifyComplex( |
17 | | const sass::vector<sass::vector<SelectorComponentObj>>& complexes) |
18 | 0 | { |
19 | |
|
20 | 0 | SASS_ASSERT(!complexes.empty(), "Can't unify empty list"); |
21 | 0 | if (complexes.size() == 1) return complexes; |
22 | | |
23 | 0 | CompoundSelectorObj unifiedBase = SASS_MEMORY_NEW(CompoundSelector, SourceSpan("[unify]")); |
24 | 0 | for (auto complex : complexes) { |
25 | 0 | SelectorComponentObj base = complex.back(); |
26 | 0 | if (CompoundSelector * comp = base->getCompound()) { |
27 | 0 | if (unifiedBase->empty()) { |
28 | 0 | unifiedBase->concat(comp); |
29 | 0 | } |
30 | 0 | else { |
31 | 0 | for (SimpleSelectorObj simple : comp->elements()) { |
32 | 0 | unifiedBase = simple->unifyWith(unifiedBase); |
33 | 0 | if (unifiedBase.isNull()) return {}; |
34 | 0 | } |
35 | 0 | } |
36 | 0 | } |
37 | 0 | else { |
38 | 0 | return {}; |
39 | 0 | } |
40 | 0 | } |
41 | | |
42 | 0 | sass::vector<sass::vector<SelectorComponentObj>> complexesWithoutBases; |
43 | 0 | for (size_t i = 0; i < complexes.size(); i += 1) { |
44 | 0 | sass::vector<SelectorComponentObj> sel = complexes[i]; |
45 | 0 | sel.pop_back(); // remove last item (base) from the list |
46 | 0 | complexesWithoutBases.push_back(std::move(sel)); |
47 | 0 | } |
48 | |
|
49 | 0 | complexesWithoutBases.back().push_back(unifiedBase); |
50 | |
|
51 | 0 | return weave(complexesWithoutBases); |
52 | |
|
53 | 0 | } |
54 | | // EO unifyComplex |
55 | | |
56 | | // ########################################################################## |
57 | | // Returns a [CompoundSelector] that matches only elements |
58 | | // that are matched by both [compound1] and [compound2]. |
59 | | // If no such selector can be produced, returns `null`. |
60 | | // ########################################################################## |
61 | | CompoundSelector* CompoundSelector::unifyWith(CompoundSelector* rhs) |
62 | 0 | { |
63 | 0 | if (empty()) return rhs; |
64 | 0 | CompoundSelectorObj unified = SASS_MEMORY_COPY(rhs); |
65 | 0 | for (const SimpleSelectorObj& sel : elements()) { |
66 | 0 | unified = sel->unifyWith(unified); |
67 | 0 | if (unified.isNull()) break; |
68 | 0 | } |
69 | 0 | return unified.detach(); |
70 | 0 | } |
71 | | // EO CompoundSelector::unifyWith(CompoundSelector*) |
72 | | |
73 | | // ########################################################################## |
74 | | // Returns the compoments of a [CompoundSelector] that matches only elements |
75 | | // matched by both this and [compound]. By default, this just returns a copy |
76 | | // of [compound] with this selector added to the end, or returns the original |
77 | | // array if this selector already exists in it. Returns `null` if unification |
78 | | // is impossible—for example, if there are multiple ID selectors. |
79 | | // ########################################################################## |
80 | | // This is implemented in `selector/simple.dart` as `SimpleSelector::unify` |
81 | | // ########################################################################## |
82 | | CompoundSelector* SimpleSelector::unifyWith(CompoundSelector* rhs) |
83 | 0 | { |
84 | |
|
85 | 0 | if (rhs->length() == 1) { |
86 | 0 | if (rhs->get(0)->is_universal()) { |
87 | 0 | CompoundSelector* this_compound = SASS_MEMORY_NEW(CompoundSelector, pstate()); |
88 | 0 | this_compound->append(SASS_MEMORY_COPY(this)); |
89 | 0 | CompoundSelector* unified = rhs->get(0)->unifyWith(this_compound); |
90 | 0 | if (unified == nullptr || unified != this_compound) delete this_compound; |
91 | 0 | return unified; |
92 | 0 | } |
93 | 0 | } |
94 | 0 | for (const SimpleSelectorObj& sel : rhs->elements()) { |
95 | 0 | if (*this == *sel) { |
96 | 0 | return rhs; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, rhs->pstate()); |
101 | |
|
102 | 0 | bool addedThis = false; |
103 | 0 | for (auto simple : rhs->elements()) { |
104 | | // Make sure pseudo selectors always come last. |
105 | 0 | if (!addedThis && simple->getPseudoSelector()) { |
106 | 0 | result->append(this); |
107 | 0 | addedThis = true; |
108 | 0 | } |
109 | 0 | result->append(simple); |
110 | 0 | } |
111 | |
|
112 | 0 | if (!addedThis) { |
113 | 0 | result->append(this); |
114 | 0 | } |
115 | 0 | return result.detach(); |
116 | |
|
117 | 0 | } |
118 | | // EO SimpleSelector::unifyWith(CompoundSelector*) |
119 | | |
120 | | // ########################################################################## |
121 | | // This is implemented in `selector/type.dart` as `PseudoSelector::unify` |
122 | | // ########################################################################## |
123 | | CompoundSelector* TypeSelector::unifyWith(CompoundSelector* rhs) |
124 | 0 | { |
125 | 0 | if (rhs->empty()) { |
126 | 0 | rhs->append(this); |
127 | 0 | return rhs; |
128 | 0 | } |
129 | 0 | TypeSelector* type = Cast<TypeSelector>(rhs->at(0)); |
130 | 0 | if (type != nullptr) { |
131 | 0 | SimpleSelector* unified = unifyWith(type); |
132 | 0 | if (unified == nullptr) { |
133 | 0 | return nullptr; |
134 | 0 | } |
135 | 0 | rhs->elements()[0] = unified; |
136 | 0 | } |
137 | 0 | else if (!is_universal() || (has_ns_ && ns_ != "*")) { |
138 | 0 | rhs->insert(rhs->begin(), this); |
139 | 0 | } |
140 | 0 | return rhs; |
141 | 0 | } |
142 | | |
143 | | // ########################################################################## |
144 | | // This is implemented in `selector/id.dart` as `PseudoSelector::unify` |
145 | | // ########################################################################## |
146 | | CompoundSelector* IDSelector::unifyWith(CompoundSelector* rhs) |
147 | 0 | { |
148 | 0 | for (const SimpleSelector* sel : rhs->elements()) { |
149 | 0 | if (const IDSelector* id_sel = Cast<IDSelector>(sel)) { |
150 | 0 | if (id_sel->name() != name()) return nullptr; |
151 | 0 | } |
152 | 0 | } |
153 | 0 | return SimpleSelector::unifyWith(rhs); |
154 | 0 | } |
155 | | |
156 | | // ########################################################################## |
157 | | // This is implemented in `selector/pseudo.dart` as `PseudoSelector::unify` |
158 | | // ########################################################################## |
159 | | CompoundSelector* PseudoSelector::unifyWith(CompoundSelector* compound) |
160 | 0 | { |
161 | |
|
162 | 0 | if (compound->length() == 1 && compound->first()->is_universal()) { |
163 | | // std::cerr << "implement universal pseudo\n"; |
164 | 0 | } |
165 | |
|
166 | 0 | for (const SimpleSelectorObj& sel : compound->elements()) { |
167 | 0 | if (*this == *sel) { |
168 | 0 | return compound; |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, compound->pstate()); |
173 | |
|
174 | 0 | bool addedThis = false; |
175 | 0 | for (auto simple : compound->elements()) { |
176 | | // Make sure pseudo selectors always come last. |
177 | 0 | if (PseudoSelectorObj pseudo = simple->getPseudoSelector()) { |
178 | 0 | if (pseudo->isElement()) { |
179 | | // A given compound selector may only contain one pseudo element. If |
180 | | // [compound] has a different one than [this], unification fails. |
181 | 0 | if (isElement()) { |
182 | 0 | return {}; |
183 | 0 | } |
184 | | // Otherwise, this is a pseudo selector and |
185 | | // should come before pseduo elements. |
186 | 0 | result->append(this); |
187 | 0 | addedThis = true; |
188 | 0 | } |
189 | 0 | } |
190 | 0 | result->append(simple); |
191 | 0 | } |
192 | | |
193 | 0 | if (!addedThis) { |
194 | 0 | result->append(this); |
195 | 0 | } |
196 | |
|
197 | 0 | return result.detach(); |
198 | |
|
199 | 0 | } |
200 | | // EO PseudoSelector::unifyWith(CompoundSelector* |
201 | | |
202 | | // ########################################################################## |
203 | | // This is implemented in `extend/functions.dart` as `unifyUniversalAndElement` |
204 | | // Returns a [SimpleSelector] that matches only elements that are matched by |
205 | | // both [selector1] and [selector2], which must both be either [UniversalSelector]s |
206 | | // or [TypeSelector]s. If no such selector can be produced, returns `null`. |
207 | | // Note: libsass handles universal selector directly within the type selector |
208 | | // ########################################################################## |
209 | | SimpleSelector* TypeSelector::unifyWith(const SimpleSelector* rhs) |
210 | 0 | { |
211 | 0 | bool rhs_ns = false; |
212 | 0 | if (!(is_ns_eq(*rhs) || rhs->is_universal_ns())) { |
213 | 0 | if (!is_universal_ns()) { |
214 | 0 | return nullptr; |
215 | 0 | } |
216 | 0 | rhs_ns = true; |
217 | 0 | } |
218 | 0 | bool rhs_name = false; |
219 | 0 | if (!(name_ == rhs->name() || rhs->is_universal())) { |
220 | 0 | if (!(is_universal())) { |
221 | 0 | return nullptr; |
222 | 0 | } |
223 | 0 | rhs_name = true; |
224 | 0 | } |
225 | 0 | if (rhs_ns) { |
226 | 0 | ns(rhs->ns()); |
227 | 0 | has_ns(rhs->has_ns()); |
228 | 0 | } |
229 | 0 | if (rhs_name) name(rhs->name()); |
230 | 0 | return this; |
231 | 0 | } |
232 | | // EO TypeSelector::unifyWith(const SimpleSelector*) |
233 | | |
234 | | // ########################################################################## |
235 | | // Unify two complex selectors. Internally calls `unifyComplex` |
236 | | // and then wraps the result in newly create ComplexSelectors. |
237 | | // ########################################################################## |
238 | | SelectorList* ComplexSelector::unifyWith(ComplexSelector* rhs) |
239 | 0 | { |
240 | 0 | SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate()); |
241 | 0 | sass::vector<sass::vector<SelectorComponentObj>> rv = |
242 | 0 | unifyComplex({ elements(), rhs->elements() }); |
243 | 0 | for (sass::vector<SelectorComponentObj> items : rv) { |
244 | 0 | ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate()); |
245 | 0 | sel->elements() = std::move(items); |
246 | 0 | list->append(sel); |
247 | 0 | } |
248 | 0 | return list.detach(); |
249 | 0 | } |
250 | | // EO ComplexSelector::unifyWith(ComplexSelector*) |
251 | | |
252 | | // ########################################################################## |
253 | | // only called from the sass function `selector-unify` |
254 | | // ########################################################################## |
255 | | SelectorList* SelectorList::unifyWith(SelectorList* rhs) |
256 | 0 | { |
257 | 0 | SelectorList* slist = SASS_MEMORY_NEW(SelectorList, pstate()); |
258 | | // Unify all of children with RHS's children, |
259 | | // storing the results in `unified_complex_selectors` |
260 | 0 | for (ComplexSelectorObj& seq1 : elements()) { |
261 | 0 | for (ComplexSelectorObj& seq2 : rhs->elements()) { |
262 | 0 | if (SelectorListObj unified = seq1->unifyWith(seq2)) { |
263 | 0 | std::move(unified->begin(), unified->end(), |
264 | 0 | std::inserter(slist->elements(), slist->end())); |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | 0 | return slist; |
269 | 0 | } |
270 | | // EO SelectorList::unifyWith(SelectorList*) |
271 | | |
272 | | // ########################################################################## |
273 | | // ########################################################################## |
274 | | |
275 | | } |