1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | #include "ast.hpp" |
5 | |
6 | namespace Sass { |
7 | |
8 | void str_rtrim(sass::string& str, const sass::string& delimiters = " \f\n\r\t\v" ) |
9 | { |
10 | str.erase( pos: str.find_last_not_of( str: delimiters ) + 1 ); |
11 | } |
12 | |
13 | ///////////////////////////////////////////////////////////////////////// |
14 | ///////////////////////////////////////////////////////////////////////// |
15 | |
16 | PreValue::PreValue(SourceSpan pstate, bool d, bool e, bool i, Type ct) |
17 | : Expression(pstate, d, e, i, ct) |
18 | { } |
19 | PreValue::PreValue(const PreValue* ptr) |
20 | : Expression(ptr) |
21 | { } |
22 | |
23 | ///////////////////////////////////////////////////////////////////////// |
24 | ///////////////////////////////////////////////////////////////////////// |
25 | |
26 | Value::Value(SourceSpan pstate, bool d, bool e, bool i, Type ct) |
27 | : PreValue(pstate, d, e, i, ct) |
28 | { } |
29 | Value::Value(const Value* ptr) |
30 | : PreValue(ptr) |
31 | { } |
32 | |
33 | ///////////////////////////////////////////////////////////////////////// |
34 | ///////////////////////////////////////////////////////////////////////// |
35 | |
36 | List::List(SourceSpan pstate, size_t size, enum Sass_Separator sep, bool argl, bool bracket) |
37 | : Value(pstate), |
38 | Vectorized<ExpressionObj>(size), |
39 | separator_(sep), |
40 | is_arglist_(argl), |
41 | is_bracketed_(bracket), |
42 | from_selector_(false) |
43 | { concrete_type(concrete_type__: LIST); } |
44 | |
45 | List::List(const List* ptr) |
46 | : Value(ptr), |
47 | Vectorized<ExpressionObj>(*ptr), |
48 | separator_(ptr->separator_), |
49 | is_arglist_(ptr->is_arglist_), |
50 | is_bracketed_(ptr->is_bracketed_), |
51 | from_selector_(ptr->from_selector_) |
52 | { concrete_type(concrete_type__: LIST); } |
53 | |
54 | size_t List::hash() const |
55 | { |
56 | if (hash_ == 0) { |
57 | hash_ = std::hash<sass::string>()(sep_string()); |
58 | hash_combine(seed&: hash_, val: std::hash<bool>()(is_bracketed())); |
59 | for (size_t i = 0, L = length(); i < L; ++i) |
60 | hash_combine(seed&: hash_, val: (elements()[i])->hash()); |
61 | } |
62 | return hash_; |
63 | } |
64 | |
65 | void List::set_delayed(bool delayed) |
66 | { |
67 | is_delayed(is_delayed__: delayed); |
68 | // don't set children |
69 | } |
70 | |
71 | bool List::operator< (const Expression& rhs) const |
72 | { |
73 | if (auto r = Cast<List>(ptr: &rhs)) { |
74 | if (length() < r->length()) return true; |
75 | if (length() > r->length()) return false; |
76 | const auto& left = elements(); |
77 | const auto& right = r->elements(); |
78 | for (size_t i = 0; i < left.size(); i += 1) { |
79 | if (*left[i] < *right[i]) return true; |
80 | if (*left[i] == *right[i]) continue; |
81 | return false; |
82 | } |
83 | return false; |
84 | } |
85 | // compare/sort by type |
86 | return type() < rhs.type(); |
87 | } |
88 | |
89 | bool List::operator== (const Expression& rhs) const |
90 | { |
91 | if (auto r = Cast<List>(ptr: &rhs)) { |
92 | if (length() != r->length()) return false; |
93 | if (separator() != r->separator()) return false; |
94 | if (is_bracketed() != r->is_bracketed()) return false; |
95 | for (size_t i = 0, L = length(); i < L; ++i) { |
96 | auto rv = r->at(i); |
97 | auto lv = this->at(i); |
98 | if (!lv && rv) return false; |
99 | else if (!rv && lv) return false; |
100 | else if (*lv != *rv) return false; |
101 | } |
102 | return true; |
103 | } |
104 | return false; |
105 | } |
106 | |
107 | size_t List::size() const { |
108 | if (!is_arglist_) return length(); |
109 | // arglist expects a list of arguments |
110 | // so we need to break before keywords |
111 | for (size_t i = 0, L = length(); i < L; ++i) { |
112 | ExpressionObj obj = this->at(i); |
113 | if (Argument* arg = Cast<Argument>(ptr: obj)) { |
114 | if (!arg->name().empty()) return i; |
115 | } |
116 | } |
117 | return length(); |
118 | } |
119 | |
120 | |
121 | ExpressionObj List::value_at_index(size_t i) { |
122 | ExpressionObj obj = this->at(i); |
123 | if (is_arglist_) { |
124 | if (Argument* arg = Cast<Argument>(ptr: obj)) { |
125 | return arg->value(); |
126 | } else { |
127 | return obj; |
128 | } |
129 | } else { |
130 | return obj; |
131 | } |
132 | } |
133 | |
134 | ///////////////////////////////////////////////////////////////////////// |
135 | ///////////////////////////////////////////////////////////////////////// |
136 | |
137 | Map::Map(SourceSpan pstate, size_t size) |
138 | : Value(pstate), |
139 | Hashed(size) |
140 | { concrete_type(concrete_type__: MAP); } |
141 | |
142 | Map::Map(const Map* ptr) |
143 | : Value(ptr), |
144 | Hashed(*ptr) |
145 | { concrete_type(concrete_type__: MAP); } |
146 | |
147 | bool Map::operator< (const Expression& rhs) const |
148 | { |
149 | if (auto r = Cast<Map>(ptr: &rhs)) { |
150 | if (length() < r->length()) return true; |
151 | if (length() > r->length()) return false; |
152 | const auto& lkeys = keys(); |
153 | const auto& rkeys = r->keys(); |
154 | for (size_t i = 0; i < lkeys.size(); i += 1) { |
155 | if (*lkeys[i] < *rkeys[i]) return true; |
156 | if (*lkeys[i] == *rkeys[i]) continue; |
157 | return false; |
158 | } |
159 | const auto& lvals = values(); |
160 | const auto& rvals = r->values(); |
161 | for (size_t i = 0; i < lvals.size(); i += 1) { |
162 | if (*lvals[i] < *rvals[i]) return true; |
163 | if (*lvals[i] == *rvals[i]) continue; |
164 | return false; |
165 | } |
166 | return false; |
167 | } |
168 | // compare/sort by type |
169 | return type() < rhs.type(); |
170 | } |
171 | |
172 | bool Map::operator== (const Expression& rhs) const |
173 | { |
174 | if (auto r = Cast<Map>(ptr: &rhs)) { |
175 | if (length() != r->length()) return false; |
176 | for (auto key : keys()) { |
177 | auto rv = r->at(k: key); |
178 | auto lv = this->at(k: key); |
179 | if (!lv && rv) return false; |
180 | else if (!rv && lv) return false; |
181 | else if (*lv != *rv) return false; |
182 | } |
183 | return true; |
184 | } |
185 | return false; |
186 | } |
187 | |
188 | List_Obj Map::to_list(SourceSpan& pstate) { |
189 | List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA); |
190 | |
191 | for (auto key : keys()) { |
192 | List_Obj l = SASS_MEMORY_NEW(List, pstate, 2); |
193 | l->append(element: key); |
194 | l->append(element: at(k: key)); |
195 | ret->append(element: l); |
196 | } |
197 | |
198 | return ret; |
199 | } |
200 | |
201 | size_t Map::hash() const |
202 | { |
203 | if (hash_ == 0) { |
204 | for (auto key : keys()) { |
205 | hash_combine(seed&: hash_, val: key->hash()); |
206 | hash_combine(seed&: hash_, val: at(k: key)->hash()); |
207 | } |
208 | } |
209 | |
210 | return hash_; |
211 | } |
212 | |
213 | ///////////////////////////////////////////////////////////////////////// |
214 | ///////////////////////////////////////////////////////////////////////// |
215 | |
216 | Binary_Expression::Binary_Expression(SourceSpan pstate, |
217 | Operand op, ExpressionObj lhs, ExpressionObj rhs) |
218 | : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0) |
219 | { } |
220 | |
221 | Binary_Expression::Binary_Expression(const Binary_Expression* ptr) |
222 | : PreValue(ptr), |
223 | op_(ptr->op_), |
224 | left_(ptr->left_), |
225 | right_(ptr->right_), |
226 | hash_(ptr->hash_) |
227 | { } |
228 | |
229 | bool Binary_Expression::is_left_interpolant(void) const |
230 | { |
231 | return is_interpolant() || (left() && left()->is_left_interpolant()); |
232 | } |
233 | bool Binary_Expression::is_right_interpolant(void) const |
234 | { |
235 | return is_interpolant() || (right() && right()->is_right_interpolant()); |
236 | } |
237 | |
238 | const sass::string Binary_Expression::type_name() |
239 | { |
240 | return sass_op_to_name(op: optype()); |
241 | } |
242 | |
243 | const sass::string Binary_Expression::separator() |
244 | { |
245 | return sass_op_separator(op: optype()); |
246 | } |
247 | |
248 | bool Binary_Expression::has_interpolant() const |
249 | { |
250 | return is_left_interpolant() || |
251 | is_right_interpolant(); |
252 | } |
253 | |
254 | void Binary_Expression::set_delayed(bool delayed) |
255 | { |
256 | right()->set_delayed(delayed); |
257 | left()->set_delayed(delayed); |
258 | is_delayed(is_delayed__: delayed); |
259 | } |
260 | |
261 | bool Binary_Expression::operator<(const Expression& rhs) const |
262 | { |
263 | if (auto m = Cast<Binary_Expression>(ptr: &rhs)) { |
264 | return type() < m->type() || |
265 | *left() < *m->left() || |
266 | *right() < *m->right(); |
267 | } |
268 | // compare/sort by type |
269 | return type() < rhs.type(); |
270 | } |
271 | |
272 | bool Binary_Expression::operator==(const Expression& rhs) const |
273 | { |
274 | if (auto m = Cast<Binary_Expression>(ptr: &rhs)) { |
275 | return type() == m->type() && |
276 | *left() == *m->left() && |
277 | *right() == *m->right(); |
278 | } |
279 | return false; |
280 | } |
281 | |
282 | size_t Binary_Expression::hash() const |
283 | { |
284 | if (hash_ == 0) { |
285 | hash_ = std::hash<size_t>()(optype()); |
286 | hash_combine(seed&: hash_, val: left()->hash()); |
287 | hash_combine(seed&: hash_, val: right()->hash()); |
288 | } |
289 | return hash_; |
290 | } |
291 | |
292 | ///////////////////////////////////////////////////////////////////////// |
293 | ///////////////////////////////////////////////////////////////////////// |
294 | |
295 | Function::Function(SourceSpan pstate, Definition_Obj def, bool css) |
296 | : Value(pstate), definition_(def), is_css_(css) |
297 | { concrete_type(concrete_type__: FUNCTION_VAL); } |
298 | |
299 | Function::Function(const Function* ptr) |
300 | : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_) |
301 | { concrete_type(concrete_type__: FUNCTION_VAL); } |
302 | |
303 | bool Function::operator< (const Expression& rhs) const |
304 | { |
305 | if (auto r = Cast<Function>(ptr: &rhs)) { |
306 | auto d1 = Cast<Definition>(ptr: definition()); |
307 | auto d2 = Cast<Definition>(ptr: r->definition()); |
308 | if (d1 == nullptr) return d2 != nullptr; |
309 | else if (d2 == nullptr) return false; |
310 | if (is_css() == r->is_css()) { |
311 | return d1 < d2; |
312 | } |
313 | return r->is_css(); |
314 | } |
315 | // compare/sort by type |
316 | return type() < rhs.type(); |
317 | } |
318 | |
319 | bool Function::operator== (const Expression& rhs) const |
320 | { |
321 | if (auto r = Cast<Function>(ptr: &rhs)) { |
322 | auto d1 = Cast<Definition>(ptr: definition()); |
323 | auto d2 = Cast<Definition>(ptr: r->definition()); |
324 | return d1 && d2 && d1 == d2 && is_css() == r->is_css(); |
325 | } |
326 | return false; |
327 | } |
328 | |
329 | sass::string Function::name() { |
330 | if (definition_) { |
331 | return definition_->name(); |
332 | } |
333 | return "" ; |
334 | } |
335 | |
336 | ///////////////////////////////////////////////////////////////////////// |
337 | ///////////////////////////////////////////////////////////////////////// |
338 | |
339 | Function_Call::Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, void* cookie) |
340 | : PreValue(pstate), sname_(n), arguments_(args), func_(), via_call_(false), cookie_(cookie), hash_(0) |
341 | { concrete_type(concrete_type__: FUNCTION); } |
342 | Function_Call::Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, Function_Obj func) |
343 | : PreValue(pstate), sname_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0) |
344 | { concrete_type(concrete_type__: FUNCTION); } |
345 | Function_Call::Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args) |
346 | : PreValue(pstate), sname_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0) |
347 | { concrete_type(concrete_type__: FUNCTION); } |
348 | |
349 | Function_Call::Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, void* cookie) |
350 | : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(), via_call_(false), cookie_(cookie), hash_(0) |
351 | { concrete_type(concrete_type__: FUNCTION); } |
352 | Function_Call::Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Function_Obj func) |
353 | : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0) |
354 | { concrete_type(concrete_type__: FUNCTION); } |
355 | Function_Call::Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args) |
356 | : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), via_call_(false), cookie_(0), hash_(0) |
357 | { concrete_type(concrete_type__: FUNCTION); } |
358 | |
359 | Function_Call::Function_Call(const Function_Call* ptr) |
360 | : PreValue(ptr), |
361 | sname_(ptr->sname_), |
362 | arguments_(ptr->arguments_), |
363 | func_(ptr->func_), |
364 | via_call_(ptr->via_call_), |
365 | cookie_(ptr->cookie_), |
366 | hash_(ptr->hash_) |
367 | { concrete_type(concrete_type__: FUNCTION); } |
368 | |
369 | bool Function_Call::operator==(const Expression& rhs) const |
370 | { |
371 | if (auto m = Cast<Function_Call>(ptr: &rhs)) { |
372 | if (*sname() != *m->sname()) return false; |
373 | if (arguments()->length() != m->arguments()->length()) return false; |
374 | for (size_t i = 0, L = arguments()->length(); i < L; ++i) |
375 | if (*arguments()->get(i) != *m->arguments()->get(i)) return false; |
376 | return true; |
377 | } |
378 | return false; |
379 | } |
380 | |
381 | size_t Function_Call::hash() const |
382 | { |
383 | if (hash_ == 0) { |
384 | hash_ = std::hash<sass::string>()(name()); |
385 | for (auto argument : arguments()->elements()) |
386 | hash_combine(seed&: hash_, val: argument->hash()); |
387 | } |
388 | return hash_; |
389 | } |
390 | |
391 | sass::string Function_Call::name() const |
392 | { |
393 | return sname(); |
394 | } |
395 | |
396 | bool Function_Call::is_css() { |
397 | if (func_) return func_->is_css(); |
398 | return false; |
399 | } |
400 | |
401 | ///////////////////////////////////////////////////////////////////////// |
402 | ///////////////////////////////////////////////////////////////////////// |
403 | |
404 | Variable::Variable(SourceSpan pstate, sass::string n) |
405 | : PreValue(pstate), name_(n) |
406 | { concrete_type(concrete_type__: VARIABLE); } |
407 | |
408 | Variable::Variable(const Variable* ptr) |
409 | : PreValue(ptr), name_(ptr->name_) |
410 | { concrete_type(concrete_type__: VARIABLE); } |
411 | |
412 | bool Variable::operator==(const Expression& rhs) const |
413 | { |
414 | if (auto e = Cast<Variable>(ptr: &rhs)) { |
415 | return name() == e->name(); |
416 | } |
417 | return false; |
418 | } |
419 | |
420 | size_t Variable::hash() const |
421 | { |
422 | return std::hash<sass::string>()(name()); |
423 | } |
424 | |
425 | ///////////////////////////////////////////////////////////////////////// |
426 | ///////////////////////////////////////////////////////////////////////// |
427 | |
428 | Number::Number(SourceSpan pstate, double val, sass::string u, bool zero) |
429 | : Value(pstate), |
430 | Units(), |
431 | value_(val), |
432 | zero_(zero), |
433 | hash_(0) |
434 | { |
435 | size_t l = 0; |
436 | size_t r; |
437 | if (!u.empty()) { |
438 | bool nominator = true; |
439 | while (true) { |
440 | r = u.find_first_of(s: "*/" , pos: l); |
441 | sass::string unit(u.substr(pos: l, n: r == sass::string::npos ? r : r - l)); |
442 | if (!unit.empty()) { |
443 | if (nominator) numerators.push_back(x: unit); |
444 | else denominators.push_back(x: unit); |
445 | } |
446 | if (r == sass::string::npos) break; |
447 | // ToDo: should error for multiple slashes |
448 | // if (!nominator && u[r] == '/') error(...) |
449 | if (u[r] == '/') |
450 | nominator = false; |
451 | // strange math parsing? |
452 | // else if (u[r] == '*') |
453 | // nominator = true; |
454 | l = r + 1; |
455 | } |
456 | } |
457 | concrete_type(concrete_type__: NUMBER); |
458 | } |
459 | |
460 | Number::Number(const Number* ptr) |
461 | : Value(ptr), |
462 | Units(ptr), |
463 | value_(ptr->value_), zero_(ptr->zero_), |
464 | hash_(ptr->hash_) |
465 | { concrete_type(concrete_type__: NUMBER); } |
466 | |
467 | // cancel out unnecessary units |
468 | void Number::reduce() |
469 | { |
470 | // apply conversion factor |
471 | value_ *= this->Units::reduce(); |
472 | } |
473 | |
474 | void Number::normalize() |
475 | { |
476 | // apply conversion factor |
477 | value_ *= this->Units::normalize(); |
478 | } |
479 | |
480 | size_t Number::hash() const |
481 | { |
482 | if (hash_ == 0) { |
483 | hash_ = std::hash<double>()(value_); |
484 | for (const auto numerator : numerators) |
485 | hash_combine(seed&: hash_, val: std::hash<sass::string>()(numerator)); |
486 | for (const auto denominator : denominators) |
487 | hash_combine(seed&: hash_, val: std::hash<sass::string>()(denominator)); |
488 | } |
489 | return hash_; |
490 | } |
491 | |
492 | bool Number::operator< (const Expression& rhs) const |
493 | { |
494 | if (auto n = Cast<Number>(ptr: &rhs)) { |
495 | return *this < *n; |
496 | } |
497 | return false; |
498 | } |
499 | |
500 | bool Number::operator== (const Expression& rhs) const |
501 | { |
502 | if (auto n = Cast<Number>(ptr: &rhs)) { |
503 | return *this == *n; |
504 | } |
505 | return false; |
506 | } |
507 | |
508 | bool Number::operator== (const Number& rhs) const |
509 | { |
510 | // unitless or only having one unit are equivalent (3.4) |
511 | // therefore we need to reduce the units beforehand |
512 | Number l(*this), r(rhs); l.reduce(); r.reduce(); |
513 | size_t lhs_units = l.numerators.size() + l.denominators.size(); |
514 | size_t rhs_units = r.numerators.size() + r.denominators.size(); |
515 | if (!lhs_units || !rhs_units) { |
516 | return NEAR_EQUAL(l.value(), r.value()); |
517 | } |
518 | // ensure both have same units |
519 | l.normalize(); r.normalize(); |
520 | Units &lhs_unit = l, &rhs_unit = r; |
521 | return lhs_unit == rhs_unit && |
522 | NEAR_EQUAL(l.value(), r.value()); |
523 | } |
524 | |
525 | bool Number::operator< (const Number& rhs) const |
526 | { |
527 | // unitless or only having one unit are equivalent (3.4) |
528 | // therefore we need to reduce the units beforehand |
529 | Number l(*this), r(rhs); l.reduce(); r.reduce(); |
530 | size_t lhs_units = l.numerators.size() + l.denominators.size(); |
531 | size_t rhs_units = r.numerators.size() + r.denominators.size(); |
532 | if (!lhs_units || !rhs_units) { |
533 | return l.value() < r.value(); |
534 | } |
535 | // ensure both have same units |
536 | l.normalize(); r.normalize(); |
537 | Units &lhs_unit = l, &rhs_unit = r; |
538 | if (!(lhs_unit == rhs_unit)) { |
539 | /* ToDo: do we always get useful backtraces? */ |
540 | throw Exception::IncompatibleUnits(rhs, *this); |
541 | } |
542 | if (lhs_unit == rhs_unit) { |
543 | return l.value() < r.value(); |
544 | } else { |
545 | return lhs_unit < rhs_unit; |
546 | } |
547 | } |
548 | |
549 | ///////////////////////////////////////////////////////////////////////// |
550 | ///////////////////////////////////////////////////////////////////////// |
551 | |
552 | Color::Color(SourceSpan pstate, double a, const sass::string disp) |
553 | : Value(pstate), |
554 | disp_(disp), a_(a), |
555 | hash_(0) |
556 | { concrete_type(concrete_type__: COLOR); } |
557 | |
558 | Color::Color(const Color* ptr) |
559 | : Value(ptr->pstate()), |
560 | // reset on copy |
561 | disp_("" ), |
562 | a_(ptr->a_), |
563 | hash_(ptr->hash_) |
564 | { concrete_type(concrete_type__: COLOR); } |
565 | |
566 | bool Color::operator< (const Expression& rhs) const |
567 | { |
568 | if (auto r = Cast<Color_RGBA>(ptr: &rhs)) { |
569 | return *this < *r; |
570 | } |
571 | else if (auto r = Cast<Color_HSLA>(ptr: &rhs)) { |
572 | return *this < *r; |
573 | } |
574 | else if (auto r = Cast<Color>(ptr: &rhs)) { |
575 | return a_ < r->a(); |
576 | } |
577 | // compare/sort by type |
578 | return type() < rhs.type(); |
579 | } |
580 | |
581 | bool Color::operator== (const Expression& rhs) const |
582 | { |
583 | if (auto r = Cast<Color_RGBA>(ptr: &rhs)) { |
584 | return *this == *r; |
585 | } |
586 | else if (auto r = Cast<Color_HSLA>(ptr: &rhs)) { |
587 | return *this == *r; |
588 | } |
589 | else if (auto r = Cast<Color>(ptr: &rhs)) { |
590 | return a_ == r->a(); |
591 | } |
592 | return false; |
593 | } |
594 | |
595 | ///////////////////////////////////////////////////////////////////////// |
596 | ///////////////////////////////////////////////////////////////////////// |
597 | |
598 | Color_RGBA::Color_RGBA(SourceSpan pstate, double r, double g, double b, double a, const sass::string disp) |
599 | : Color(pstate, a, disp), |
600 | r_(r), g_(g), b_(b) |
601 | { concrete_type(concrete_type__: COLOR); } |
602 | |
603 | Color_RGBA::Color_RGBA(const Color_RGBA* ptr) |
604 | : Color(ptr), |
605 | r_(ptr->r_), |
606 | g_(ptr->g_), |
607 | b_(ptr->b_) |
608 | { concrete_type(concrete_type__: COLOR); } |
609 | |
610 | bool Color_RGBA::operator< (const Expression& rhs) const |
611 | { |
612 | if (auto r = Cast<Color_RGBA>(ptr: &rhs)) { |
613 | if (r_ < r->r()) return true; |
614 | if (r_ > r->r()) return false; |
615 | if (g_ < r->g()) return true; |
616 | if (g_ > r->g()) return false; |
617 | if (b_ < r->b()) return true; |
618 | if (b_ > r->b()) return false; |
619 | if (a_ < r->a()) return true; |
620 | if (a_ > r->a()) return false; |
621 | return false; // is equal |
622 | } |
623 | // compare/sort by type |
624 | return type() < rhs.type(); |
625 | } |
626 | |
627 | bool Color_RGBA::operator== (const Expression& rhs) const |
628 | { |
629 | if (auto r = Cast<Color_RGBA>(ptr: &rhs)) { |
630 | return r_ == r->r() && |
631 | g_ == r->g() && |
632 | b_ == r->b() && |
633 | a_ == r->a(); |
634 | } |
635 | return false; |
636 | } |
637 | |
638 | size_t Color_RGBA::hash() const |
639 | { |
640 | if (hash_ == 0) { |
641 | hash_ = std::hash<sass::string>()("RGBA" ); |
642 | hash_combine(seed&: hash_, val: std::hash<double>()(a_)); |
643 | hash_combine(seed&: hash_, val: std::hash<double>()(r_)); |
644 | hash_combine(seed&: hash_, val: std::hash<double>()(g_)); |
645 | hash_combine(seed&: hash_, val: std::hash<double>()(b_)); |
646 | } |
647 | return hash_; |
648 | } |
649 | |
650 | Color_HSLA* Color_RGBA::copyAsHSLA() const |
651 | { |
652 | |
653 | // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV |
654 | double r = r_ / 255.0; |
655 | double g = g_ / 255.0; |
656 | double b = b_ / 255.0; |
657 | |
658 | double max = std::max(a: r, b: std::max(a: g, b: b)); |
659 | double min = std::min(a: r, b: std::min(a: g, b: b)); |
660 | double delta = max - min; |
661 | |
662 | double h = 0; |
663 | double s; |
664 | double l = (max + min) / 2.0; |
665 | |
666 | if (NEAR_EQUAL(max, min)) { |
667 | h = s = 0; // achromatic |
668 | } |
669 | else { |
670 | if (l < 0.5) s = delta / (max + min); |
671 | else s = delta / (2.0 - max - min); |
672 | |
673 | if (r == max) h = (g - b) / delta + (g < b ? 6 : 0); |
674 | else if (g == max) h = (b - r) / delta + 2; |
675 | else if (b == max) h = (r - g) / delta + 4; |
676 | } |
677 | |
678 | // HSL hsl_struct; |
679 | h = h * 60; |
680 | s = s * 100; |
681 | l = l * 100; |
682 | |
683 | return SASS_MEMORY_NEW(Color_HSLA, |
684 | pstate(), h, s, l, a(), "" |
685 | ); |
686 | } |
687 | |
688 | Color_RGBA* Color_RGBA::copyAsRGBA() const |
689 | { |
690 | return SASS_MEMORY_COPY(this); |
691 | } |
692 | |
693 | ///////////////////////////////////////////////////////////////////////// |
694 | ///////////////////////////////////////////////////////////////////////// |
695 | |
696 | Color_HSLA::Color_HSLA(SourceSpan pstate, double h, double s, double l, double a, const sass::string disp) |
697 | : Color(pstate, a, disp), |
698 | h_(absmod(n: h, r: 360.0)), |
699 | s_(clip(n: s, lower: 0.0, upper: 100.0)), |
700 | l_(clip(n: l, lower: 0.0, upper: 100.0)) |
701 | // hash_(0) |
702 | { concrete_type(concrete_type__: COLOR); } |
703 | |
704 | Color_HSLA::Color_HSLA(const Color_HSLA* ptr) |
705 | : Color(ptr), |
706 | h_(ptr->h_), |
707 | s_(ptr->s_), |
708 | l_(ptr->l_) |
709 | // hash_(ptr->hash_) |
710 | { concrete_type(concrete_type__: COLOR); } |
711 | |
712 | bool Color_HSLA::operator< (const Expression& rhs) const |
713 | { |
714 | if (auto r = Cast<Color_HSLA>(ptr: &rhs)) { |
715 | if (h_ < r->h()) return true; |
716 | if (h_ > r->h()) return false; |
717 | if (s_ < r->s()) return true; |
718 | if (s_ > r->s()) return false; |
719 | if (l_ < r->l()) return true; |
720 | if (l_ > r->l()) return false; |
721 | if (a_ < r->a()) return true; |
722 | if (a_ > r->a()) return false; |
723 | return false; // is equal |
724 | } |
725 | // compare/sort by type |
726 | return type() < rhs.type(); |
727 | } |
728 | |
729 | bool Color_HSLA::operator== (const Expression& rhs) const |
730 | { |
731 | if (auto r = Cast<Color_HSLA>(ptr: &rhs)) { |
732 | return h_ == r->h() && |
733 | s_ == r->s() && |
734 | l_ == r->l() && |
735 | a_ == r->a(); |
736 | } |
737 | return false; |
738 | } |
739 | |
740 | size_t Color_HSLA::hash() const |
741 | { |
742 | if (hash_ == 0) { |
743 | hash_ = std::hash<sass::string>()("HSLA" ); |
744 | hash_combine(seed&: hash_, val: std::hash<double>()(a_)); |
745 | hash_combine(seed&: hash_, val: std::hash<double>()(h_)); |
746 | hash_combine(seed&: hash_, val: std::hash<double>()(s_)); |
747 | hash_combine(seed&: hash_, val: std::hash<double>()(l_)); |
748 | } |
749 | return hash_; |
750 | } |
751 | |
752 | // hue to RGB helper function |
753 | double h_to_rgb(double m1, double m2, double h) |
754 | { |
755 | h = absmod(n: h, r: 1.0); |
756 | if (h*6.0 < 1) return m1 + (m2 - m1)*h*6; |
757 | if (h*2.0 < 1) return m2; |
758 | if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6; |
759 | return m1; |
760 | } |
761 | |
762 | Color_RGBA* Color_HSLA::copyAsRGBA() const |
763 | { |
764 | |
765 | double h = absmod(n: h_ / 360.0, r: 1.0); |
766 | double s = clip(n: s_ / 100.0, lower: 0.0, upper: 1.0); |
767 | double l = clip(n: l_ / 100.0, lower: 0.0, upper: 1.0); |
768 | |
769 | // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color. |
770 | double m2; |
771 | if (l <= 0.5) m2 = l*(s+1.0); |
772 | else m2 = (l+s)-(l*s); |
773 | double m1 = (l*2.0)-m2; |
774 | // round the results -- consider moving this into the Color constructor |
775 | double r = (h_to_rgb(m1, m2, h: h + 1.0/3.0) * 255.0); |
776 | double g = (h_to_rgb(m1, m2, h) * 255.0); |
777 | double b = (h_to_rgb(m1, m2, h: h - 1.0/3.0) * 255.0); |
778 | |
779 | return SASS_MEMORY_NEW(Color_RGBA, |
780 | pstate(), r, g, b, a(), "" |
781 | ); |
782 | } |
783 | |
784 | Color_HSLA* Color_HSLA::copyAsHSLA() const |
785 | { |
786 | return SASS_MEMORY_COPY(this); |
787 | } |
788 | |
789 | ///////////////////////////////////////////////////////////////////////// |
790 | ///////////////////////////////////////////////////////////////////////// |
791 | |
792 | Custom_Error::Custom_Error(SourceSpan pstate, sass::string msg) |
793 | : Value(pstate), message_(msg) |
794 | { concrete_type(concrete_type__: C_ERROR); } |
795 | |
796 | Custom_Error::Custom_Error(const Custom_Error* ptr) |
797 | : Value(ptr), message_(ptr->message_) |
798 | { concrete_type(concrete_type__: C_ERROR); } |
799 | |
800 | bool Custom_Error::operator< (const Expression& rhs) const |
801 | { |
802 | if (auto r = Cast<Custom_Error>(ptr: &rhs)) { |
803 | return message() < r->message(); |
804 | } |
805 | // compare/sort by type |
806 | return type() < rhs.type(); |
807 | } |
808 | |
809 | bool Custom_Error::operator== (const Expression& rhs) const |
810 | { |
811 | if (auto r = Cast<Custom_Error>(ptr: &rhs)) { |
812 | return message() == r->message(); |
813 | } |
814 | return false; |
815 | } |
816 | |
817 | ///////////////////////////////////////////////////////////////////////// |
818 | ///////////////////////////////////////////////////////////////////////// |
819 | |
820 | Custom_Warning::Custom_Warning(SourceSpan pstate, sass::string msg) |
821 | : Value(pstate), message_(msg) |
822 | { concrete_type(concrete_type__: C_WARNING); } |
823 | |
824 | Custom_Warning::Custom_Warning(const Custom_Warning* ptr) |
825 | : Value(ptr), message_(ptr->message_) |
826 | { concrete_type(concrete_type__: C_WARNING); } |
827 | |
828 | bool Custom_Warning::operator< (const Expression& rhs) const |
829 | { |
830 | if (auto r = Cast<Custom_Warning>(ptr: &rhs)) { |
831 | return message() < r->message(); |
832 | } |
833 | // compare/sort by type |
834 | return type() < rhs.type(); |
835 | } |
836 | |
837 | bool Custom_Warning::operator== (const Expression& rhs) const |
838 | { |
839 | if (auto r = Cast<Custom_Warning>(ptr: &rhs)) { |
840 | return message() == r->message(); |
841 | } |
842 | return false; |
843 | } |
844 | |
845 | ///////////////////////////////////////////////////////////////////////// |
846 | ///////////////////////////////////////////////////////////////////////// |
847 | |
848 | Boolean::Boolean(SourceSpan pstate, bool val) |
849 | : Value(pstate), value_(val), |
850 | hash_(0) |
851 | { concrete_type(concrete_type__: BOOLEAN); } |
852 | |
853 | Boolean::Boolean(const Boolean* ptr) |
854 | : Value(ptr), |
855 | value_(ptr->value_), |
856 | hash_(ptr->hash_) |
857 | { concrete_type(concrete_type__: BOOLEAN); } |
858 | |
859 | bool Boolean::operator< (const Expression& rhs) const |
860 | { |
861 | if (auto r = Cast<Boolean>(ptr: &rhs)) { |
862 | return (value() < r->value()); |
863 | } |
864 | return false; |
865 | } |
866 | |
867 | bool Boolean::operator== (const Expression& rhs) const |
868 | { |
869 | if (auto r = Cast<Boolean>(ptr: &rhs)) { |
870 | return (value() == r->value()); |
871 | } |
872 | return false; |
873 | } |
874 | |
875 | size_t Boolean::hash() const |
876 | { |
877 | if (hash_ == 0) { |
878 | hash_ = std::hash<bool>()(value_); |
879 | } |
880 | return hash_; |
881 | } |
882 | |
883 | ///////////////////////////////////////////////////////////////////////// |
884 | ///////////////////////////////////////////////////////////////////////// |
885 | |
886 | String::String(SourceSpan pstate, bool delayed) |
887 | : Value(pstate, delayed) |
888 | { concrete_type(concrete_type__: STRING); } |
889 | String::String(const String* ptr) |
890 | : Value(ptr) |
891 | { concrete_type(concrete_type__: STRING); } |
892 | |
893 | ///////////////////////////////////////////////////////////////////////// |
894 | ///////////////////////////////////////////////////////////////////////// |
895 | |
896 | String_Schema::String_Schema(SourceSpan pstate, size_t size, bool css) |
897 | : String(pstate), Vectorized<PreValueObj>(size), css_(css), hash_(0) |
898 | { concrete_type(concrete_type__: STRING); } |
899 | |
900 | String_Schema::String_Schema(const String_Schema* ptr) |
901 | : String(ptr), |
902 | Vectorized<PreValueObj>(*ptr), |
903 | css_(ptr->css_), |
904 | hash_(ptr->hash_) |
905 | { concrete_type(concrete_type__: STRING); } |
906 | |
907 | void String_Schema::rtrim() |
908 | { |
909 | if (!empty()) { |
910 | if (String* str = Cast<String>(ptr: last())) str->rtrim(); |
911 | } |
912 | } |
913 | |
914 | bool String_Schema::is_left_interpolant(void) const |
915 | { |
916 | return length() && first()->is_left_interpolant(); |
917 | } |
918 | bool String_Schema::is_right_interpolant(void) const |
919 | { |
920 | return length() && last()->is_right_interpolant(); |
921 | } |
922 | |
923 | bool String_Schema::operator< (const Expression& rhs) const |
924 | { |
925 | if (auto r = Cast<String_Schema>(ptr: &rhs)) { |
926 | if (length() < r->length()) return true; |
927 | if (length() > r->length()) return false; |
928 | for (size_t i = 0, L = length(); i < L; ++i) { |
929 | if (*get(i) < *r->get(i)) return true; |
930 | if (*get(i) == *r->get(i)) continue; |
931 | return false; |
932 | } |
933 | // Is equal |
934 | return false; |
935 | } |
936 | // compare/sort by type |
937 | return type() < rhs.type(); |
938 | } |
939 | |
940 | bool String_Schema::operator== (const Expression& rhs) const |
941 | { |
942 | if (auto r = Cast<String_Schema>(ptr: &rhs)) { |
943 | if (length() != r->length()) return false; |
944 | for (size_t i = 0, L = length(); i < L; ++i) { |
945 | auto rv = (*r)[i]; |
946 | auto lv = (*this)[i]; |
947 | if (*lv != *rv) return false; |
948 | } |
949 | return true; |
950 | } |
951 | return false; |
952 | } |
953 | |
954 | bool String_Schema::has_interpolants() |
955 | { |
956 | for (auto el : elements()) { |
957 | if (el->is_interpolant()) return true; |
958 | } |
959 | return false; |
960 | } |
961 | |
962 | size_t String_Schema::hash() const |
963 | { |
964 | if (hash_ == 0) { |
965 | for (auto string : elements()) |
966 | hash_combine(seed&: hash_, val: string->hash()); |
967 | } |
968 | return hash_; |
969 | } |
970 | |
971 | void String_Schema::set_delayed(bool delayed) |
972 | { |
973 | is_delayed(is_delayed__: delayed); |
974 | } |
975 | |
976 | ///////////////////////////////////////////////////////////////////////// |
977 | ///////////////////////////////////////////////////////////////////////// |
978 | |
979 | String_Constant::String_Constant(SourceSpan pstate, sass::string val, bool css) |
980 | : String(pstate), quote_mark_(0), value_(read_css_string(str: val, css)), hash_(0) |
981 | { } |
982 | String_Constant::String_Constant(SourceSpan pstate, const char* beg, bool css) |
983 | : String(pstate), quote_mark_(0), value_(read_css_string(str: sass::string(beg), css)), hash_(0) |
984 | { } |
985 | String_Constant::String_Constant(SourceSpan pstate, const char* beg, const char* end, bool css) |
986 | : String(pstate), quote_mark_(0), value_(read_css_string(str: sass::string(beg, end-beg), css)), hash_(0) |
987 | { } |
988 | String_Constant::String_Constant(SourceSpan pstate, const Token& tok, bool css) |
989 | : String(pstate), quote_mark_(0), value_(read_css_string(str: sass::string(tok.begin, tok.end), css)), hash_(0) |
990 | { } |
991 | |
992 | String_Constant::String_Constant(const String_Constant* ptr) |
993 | : String(ptr), |
994 | quote_mark_(ptr->quote_mark_), |
995 | value_(ptr->value_), |
996 | hash_(ptr->hash_) |
997 | { } |
998 | |
999 | bool String_Constant::is_invisible() const { |
1000 | return value_.empty() && quote_mark_ == 0; |
1001 | } |
1002 | |
1003 | bool String_Constant::operator< (const Expression& rhs) const |
1004 | { |
1005 | if (auto qstr = Cast<String_Quoted>(ptr: &rhs)) { |
1006 | return value() < qstr->value(); |
1007 | } |
1008 | else if (auto cstr = Cast<String_Constant>(ptr: &rhs)) { |
1009 | return value() < cstr->value(); |
1010 | } |
1011 | // compare/sort by type |
1012 | return type() < rhs.type(); |
1013 | } |
1014 | |
1015 | bool String_Constant::operator== (const Expression& rhs) const |
1016 | { |
1017 | if (auto qstr = Cast<String_Quoted>(ptr: &rhs)) { |
1018 | return value() == qstr->value(); |
1019 | } |
1020 | else if (auto cstr = Cast<String_Constant>(ptr: &rhs)) { |
1021 | return value() == cstr->value(); |
1022 | } |
1023 | return false; |
1024 | } |
1025 | |
1026 | sass::string String_Constant::inspect() const |
1027 | { |
1028 | return quote(value_, q: '*'); |
1029 | } |
1030 | |
1031 | void String_Constant::rtrim() |
1032 | { |
1033 | str_rtrim(str&: value_); |
1034 | } |
1035 | |
1036 | size_t String_Constant::hash() const |
1037 | { |
1038 | if (hash_ == 0) { |
1039 | hash_ = std::hash<sass::string>()(value_); |
1040 | } |
1041 | return hash_; |
1042 | } |
1043 | |
1044 | ///////////////////////////////////////////////////////////////////////// |
1045 | ///////////////////////////////////////////////////////////////////////// |
1046 | |
1047 | String_Quoted::String_Quoted(SourceSpan pstate, sass::string val, char q, |
1048 | bool keep_utf8_escapes, bool skip_unquoting, |
1049 | bool strict_unquoting, bool css) |
1050 | : String_Constant(pstate, val, css) |
1051 | { |
1052 | if (skip_unquoting == false) { |
1053 | value_ = unquote(value_, q: "e_mark_, keep_utf8_sequences: keep_utf8_escapes, strict: strict_unquoting); |
1054 | } |
1055 | if (q && quote_mark_) quote_mark_ = q; |
1056 | } |
1057 | |
1058 | String_Quoted::String_Quoted(const String_Quoted* ptr) |
1059 | : String_Constant(ptr) |
1060 | { } |
1061 | |
1062 | bool String_Quoted::operator< (const Expression& rhs) const |
1063 | { |
1064 | if (auto qstr = Cast<String_Quoted>(ptr: &rhs)) { |
1065 | return value() < qstr->value(); |
1066 | } |
1067 | else if (auto cstr = Cast<String_Constant>(ptr: &rhs)) { |
1068 | return value() < cstr->value(); |
1069 | } |
1070 | // compare/sort by type |
1071 | return type() < rhs.type(); |
1072 | } |
1073 | |
1074 | bool String_Quoted::operator== (const Expression& rhs) const |
1075 | { |
1076 | if (auto qstr = Cast<String_Quoted>(ptr: &rhs)) { |
1077 | return value() == qstr->value(); |
1078 | } |
1079 | else if (auto cstr = Cast<String_Constant>(ptr: &rhs)) { |
1080 | return value() == cstr->value(); |
1081 | } |
1082 | return false; |
1083 | } |
1084 | |
1085 | sass::string String_Quoted::inspect() const |
1086 | { |
1087 | return quote(value_, q: '*'); |
1088 | } |
1089 | |
1090 | ///////////////////////////////////////////////////////////////////////// |
1091 | ///////////////////////////////////////////////////////////////////////// |
1092 | |
1093 | Null::Null(SourceSpan pstate) |
1094 | : Value(pstate) |
1095 | { concrete_type(concrete_type__: NULL_VAL); } |
1096 | |
1097 | Null::Null(const Null* ptr) : Value(ptr) |
1098 | { concrete_type(concrete_type__: NULL_VAL); } |
1099 | |
1100 | bool Null::operator< (const Expression& rhs) const |
1101 | { |
1102 | if (Cast<Null>(ptr: &rhs)) { |
1103 | return false; |
1104 | } |
1105 | // compare/sort by type |
1106 | return type() < rhs.type(); |
1107 | } |
1108 | |
1109 | bool Null::operator== (const Expression& rhs) const |
1110 | { |
1111 | return Cast<Null>(ptr: &rhs) != nullptr; |
1112 | } |
1113 | |
1114 | size_t Null::hash() const |
1115 | { |
1116 | return -1; |
1117 | } |
1118 | |
1119 | ///////////////////////////////////////////////////////////////////////// |
1120 | ///////////////////////////////////////////////////////////////////////// |
1121 | |
1122 | Parent_Reference::Parent_Reference(SourceSpan pstate) |
1123 | : Value(pstate) |
1124 | { concrete_type(concrete_type__: PARENT); } |
1125 | |
1126 | Parent_Reference::Parent_Reference(const Parent_Reference* ptr) |
1127 | : Value(ptr) |
1128 | { concrete_type(concrete_type__: PARENT); } |
1129 | |
1130 | ///////////////////////////////////////////////////////////////////////// |
1131 | ///////////////////////////////////////////////////////////////////////// |
1132 | |
1133 | IMPLEMENT_AST_OPERATORS(List); |
1134 | IMPLEMENT_AST_OPERATORS(Map); |
1135 | IMPLEMENT_AST_OPERATORS(Binary_Expression); |
1136 | IMPLEMENT_AST_OPERATORS(Function); |
1137 | IMPLEMENT_AST_OPERATORS(Function_Call); |
1138 | IMPLEMENT_AST_OPERATORS(Variable); |
1139 | IMPLEMENT_AST_OPERATORS(Number); |
1140 | IMPLEMENT_AST_OPERATORS(Color_RGBA); |
1141 | IMPLEMENT_AST_OPERATORS(Color_HSLA); |
1142 | IMPLEMENT_AST_OPERATORS(Custom_Error); |
1143 | IMPLEMENT_AST_OPERATORS(Custom_Warning); |
1144 | IMPLEMENT_AST_OPERATORS(Boolean); |
1145 | IMPLEMENT_AST_OPERATORS(String_Schema); |
1146 | IMPLEMENT_AST_OPERATORS(String_Constant); |
1147 | IMPLEMENT_AST_OPERATORS(String_Quoted); |
1148 | IMPLEMENT_AST_OPERATORS(Null); |
1149 | IMPLEMENT_AST_OPERATORS(Parent_Reference); |
1150 | |
1151 | ///////////////////////////////////////////////////////////////////////// |
1152 | ///////////////////////////////////////////////////////////////////////// |
1153 | |
1154 | } |
1155 | |