1 | #ifndef SASS_AST_HELPERS_H |
2 | #define SASS_AST_HELPERS_H |
3 | |
4 | // sass.hpp must go before all system headers to get the |
5 | // __EXTENSIONS__ fix on Solaris. |
6 | #include "sass.hpp" |
7 | #include <algorithm> |
8 | #include <functional> |
9 | #include "util_string.hpp" |
10 | |
11 | namespace Sass { |
12 | |
13 | // ########################################################################### |
14 | // ########################################################################### |
15 | |
16 | // easier to search with name |
17 | const bool DELAYED = true; |
18 | |
19 | // ToDo: should this really be hardcoded |
20 | // Note: most methods follow precision option |
21 | const double NUMBER_EPSILON = 1e-12; |
22 | |
23 | // macro to test if numbers are equal within a small error margin |
24 | #define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON |
25 | |
26 | // ########################################################################### |
27 | // We define various functions and functors here. |
28 | // Functions satisfy the BinaryPredicate requirement |
29 | // Functors are structs used for e.g. unordered_map |
30 | // ########################################################################### |
31 | |
32 | // ########################################################################### |
33 | // Implement compare and hashing operations for raw pointers |
34 | // ########################################################################### |
35 | |
36 | template <class T> |
37 | size_t PtrHashFn(const T* ptr) { |
38 | return std::hash<std::size_t>()((size_t)ptr); |
39 | } |
40 | |
41 | struct PtrHash { |
42 | template <class T> |
43 | size_t operator() (const T* ptr) const { |
44 | return PtrHashFn(ptr); |
45 | } |
46 | }; |
47 | |
48 | template <class T> |
49 | bool PtrEqualityFn(const T* lhs, const T* rhs) { |
50 | return lhs == rhs; // compare raw pointers |
51 | } |
52 | |
53 | struct PtrEquality { |
54 | template <class T> |
55 | bool operator() (const T* lhs, const T* rhs) const { |
56 | return PtrEqualityFn<T>(lhs, rhs); |
57 | } |
58 | }; |
59 | |
60 | // ########################################################################### |
61 | // Implement compare and hashing operations for AST Nodes |
62 | // ########################################################################### |
63 | |
64 | // TODO: get rid of funtions and use ObjEquality<T> |
65 | |
66 | template <class T> |
67 | // Hash the raw pointer instead of object |
68 | size_t ObjPtrHashFn(const T& obj) { |
69 | return PtrHashFn(obj.ptr()); |
70 | } |
71 | |
72 | struct ObjPtrHash { |
73 | template <class T> |
74 | // Hash the raw pointer instead of object |
75 | size_t operator() (const T& obj) const { |
76 | return ObjPtrHashFn(obj); |
77 | } |
78 | }; |
79 | |
80 | template <class T> |
81 | // Hash the object and its content |
82 | size_t ObjHashFn(const T& obj) { |
83 | return obj ? obj->hash() : 0; |
84 | } |
85 | |
86 | struct ObjHash { |
87 | template <class T> |
88 | // Hash the object and its content |
89 | size_t operator() (const T& obj) const { |
90 | return ObjHashFn(obj); |
91 | } |
92 | }; |
93 | |
94 | template <class T> |
95 | // Hash the object behind pointer |
96 | size_t PtrObjHashFn(const T* obj) { |
97 | return obj ? obj->hash() : 0; |
98 | } |
99 | |
100 | struct PtrObjHash { |
101 | template <class T> |
102 | // Hash the object behind pointer |
103 | size_t operator() (const T* obj) const { |
104 | return PtrObjHashFn(obj); |
105 | } |
106 | }; |
107 | |
108 | template <class T> |
109 | // Compare raw pointers to the object |
110 | bool ObjPtrEqualityFn(const T& lhs, const T& rhs) { |
111 | return PtrEqualityFn(lhs.ptr(), rhs.ptr()); |
112 | } |
113 | |
114 | struct ObjPtrEquality { |
115 | template <class T> |
116 | // Compare raw pointers to the object |
117 | bool operator() (const T& lhs, const T& rhs) const { |
118 | return ObjPtrEqualityFn<T>(lhs, rhs); |
119 | } |
120 | }; |
121 | |
122 | template <class T> |
123 | // Compare the objects behind the pointers |
124 | bool PtrObjEqualityFn(const T* lhs, const T* rhs) { |
125 | if (lhs == nullptr) return rhs == nullptr; |
126 | else if (rhs == nullptr) return false; |
127 | else return *lhs == *rhs; |
128 | } |
129 | |
130 | struct PtrObjEquality { |
131 | template <class T> |
132 | // Compare the objects behind the pointers |
133 | bool operator() (const T* lhs, const T* rhs) const { |
134 | return PtrObjEqualityFn<T>(lhs, rhs); |
135 | } |
136 | }; |
137 | |
138 | template <class T> |
139 | // Compare the objects and its contents |
140 | bool ObjEqualityFn(const T& lhs, const T& rhs) { |
141 | return PtrObjEqualityFn(lhs.ptr(), rhs.ptr()); |
142 | } |
143 | |
144 | struct ObjEquality { |
145 | template <class T> |
146 | // Compare the objects and its contents |
147 | bool operator() (const T& lhs, const T& rhs) const { |
148 | return ObjEqualityFn<T>(lhs, rhs); |
149 | } |
150 | }; |
151 | |
152 | // ########################################################################### |
153 | // Implement ordering operations for AST Nodes |
154 | // ########################################################################### |
155 | |
156 | template <class T> |
157 | // Compare the objects behind pointers |
158 | bool PtrObjLessThanFn(const T* lhs, const T* rhs) { |
159 | if (lhs == nullptr) return rhs != nullptr; |
160 | else if (rhs == nullptr) return false; |
161 | else return *lhs < *rhs; |
162 | } |
163 | |
164 | struct PtrObjLessThan { |
165 | template <class T> |
166 | // Compare the objects behind pointers |
167 | bool operator() (const T* lhs, const T* rhs) const { |
168 | return PtrObjLessThanFn<T>(lhs, rhs); |
169 | } |
170 | }; |
171 | |
172 | template <class T> |
173 | // Compare the objects and its content |
174 | bool ObjLessThanFn(const T& lhs, const T& rhs) { |
175 | return PtrObjLessThanFn(lhs.ptr(), rhs.ptr()); |
176 | }; |
177 | |
178 | struct ObjLessThan { |
179 | template <class T> |
180 | // Compare the objects and its content |
181 | bool operator() (const T& lhs, const T& rhs) const { |
182 | return ObjLessThanFn<T>(lhs, rhs); |
183 | } |
184 | }; |
185 | |
186 | // ########################################################################### |
187 | // Some STL helper functions |
188 | // ########################################################################### |
189 | |
190 | // Check if all elements are equal |
191 | template <class X, class Y, |
192 | typename XT = typename X::value_type, |
193 | typename YT = typename Y::value_type> |
194 | bool ListEquality(const X& lhs, const Y& rhs, |
195 | bool(*cmp)(const XT*, const YT*)) |
196 | { |
197 | return lhs.size() == rhs.size() && |
198 | std::equal(lhs.begin(), lhs.end(), |
199 | rhs.begin(), cmp); |
200 | } |
201 | |
202 | // Return if Vector is empty |
203 | template <class T> |
204 | bool listIsEmpty(T* cnt) { |
205 | return cnt && cnt->empty(); |
206 | } |
207 | |
208 | // Erase items from vector that match predicate |
209 | template<class T, class UnaryPredicate> |
210 | void listEraseItemIf(T& vec, UnaryPredicate* predicate) |
211 | { |
212 | vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end()); |
213 | } |
214 | |
215 | // Check that every item in `lhs` is also in `rhs` |
216 | // Note: this works by comparing the raw pointers |
217 | template <typename T> |
218 | bool listIsSubsetOrEqual(const T& lhs, const T& rhs) { |
219 | for (const auto& item : lhs) { |
220 | if (std::find(rhs.begin(), rhs.end(), item) == rhs.end()) |
221 | return false; |
222 | } |
223 | return true; |
224 | } |
225 | |
226 | // ########################################################################## |
227 | // Returns whether [name] is the name of a pseudo-element |
228 | // that can be written with pseudo-class syntax (CSS2 vs CSS3): |
229 | // `:before`, `:after`, `:first-line`, or `:first-letter` |
230 | // ########################################################################## |
231 | inline bool isFakePseudoElement(const sass::string& name) |
232 | { |
233 | return Util::equalsLiteral(lit: "after" , test: name) |
234 | || Util::equalsLiteral(lit: "before" , test: name) |
235 | || Util::equalsLiteral(lit: "first-line" , test: name) |
236 | || Util::equalsLiteral(lit: "first-letter" , test: name); |
237 | } |
238 | |
239 | // ########################################################################## |
240 | // Names of pseudo selectors that take selectors as arguments, |
241 | // and that are subselectors of their arguments. |
242 | // For example, `.foo` is a superselector of `:matches(.foo)`. |
243 | // ########################################################################## |
244 | inline bool isSubselectorPseudo(const sass::string& norm) |
245 | { |
246 | return Util::equalsLiteral(lit: "any" , test: norm) |
247 | || Util::equalsLiteral(lit: "matches" , test: norm) |
248 | || Util::equalsLiteral(lit: "nth-child" , test: norm) |
249 | || Util::equalsLiteral(lit: "nth-last-child" , test: norm); |
250 | } |
251 | // EO isSubselectorPseudo |
252 | |
253 | // ########################################################################### |
254 | // Pseudo-class selectors that take unadorned selectors as arguments. |
255 | // ########################################################################### |
256 | inline bool isSelectorPseudoClass(const sass::string& test) |
257 | { |
258 | return Util::equalsLiteral(lit: "not" , test) |
259 | || Util::equalsLiteral(lit: "matches" , test) |
260 | || Util::equalsLiteral(lit: "current" , test) |
261 | || Util::equalsLiteral(lit: "any" , test) |
262 | || Util::equalsLiteral(lit: "has" , test) |
263 | || Util::equalsLiteral(lit: "host" , test) |
264 | || Util::equalsLiteral(lit: "host-context" , test); |
265 | } |
266 | // EO isSelectorPseudoClass |
267 | |
268 | // ########################################################################### |
269 | // Pseudo-element selectors that take unadorned selectors as arguments. |
270 | // ########################################################################### |
271 | inline bool isSelectorPseudoElement(const sass::string& test) |
272 | { |
273 | return Util::equalsLiteral(lit: "slotted" , test); |
274 | } |
275 | // EO isSelectorPseudoElement |
276 | |
277 | // ########################################################################### |
278 | // Pseudo-element selectors that has binominals |
279 | // ########################################################################### |
280 | inline bool isSelectorPseudoBinominal(const sass::string& test) |
281 | { |
282 | return Util::equalsLiteral(lit: "nth-child" , test) |
283 | || Util::equalsLiteral(lit: "nth-last-child" , test); |
284 | } |
285 | // isSelectorPseudoBinominal |
286 | |
287 | // ########################################################################### |
288 | // ########################################################################### |
289 | |
290 | } |
291 | |
292 | #endif |
293 | |