1 | #ifndef SASS_AST_H |
2 | #define SASS_AST_H |
3 | |
4 | // sass.hpp must go before all system headers to get the |
5 | // __EXTENSIONS__ fix on Solaris. |
6 | #include "sass.hpp" |
7 | |
8 | #include <typeinfo> |
9 | #include <unordered_map> |
10 | |
11 | #include "sass/base.h" |
12 | #include "ast_helpers.hpp" |
13 | #include "ast_fwd_decl.hpp" |
14 | #include "ast_def_macros.hpp" |
15 | |
16 | #include "file.hpp" |
17 | #include "position.hpp" |
18 | #include "operation.hpp" |
19 | #include "environment.hpp" |
20 | #include "fn_utils.hpp" |
21 | |
22 | namespace Sass { |
23 | |
24 | // ToDo: where does this fit best? |
25 | // We don't share this with C-API? |
26 | class Operand { |
27 | public: |
28 | Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false) |
29 | : operand(operand), ws_before(ws_before), ws_after(ws_after) |
30 | { } |
31 | public: |
32 | enum Sass_OP operand; |
33 | bool ws_before; |
34 | bool ws_after; |
35 | }; |
36 | |
37 | ////////////////////////////////////////////////////////// |
38 | // `hash_combine` comes from boost (functional/hash): |
39 | // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html |
40 | // Boost Software License - Version 1.0 |
41 | // http://www.boost.org/users/license.html |
42 | template <typename T> |
43 | void hash_combine (std::size_t& seed, const T& val) |
44 | { |
45 | seed ^= std::hash<T>()(val) + 0x9e3779b9 |
46 | + (seed<<6) + (seed>>2); |
47 | } |
48 | ////////////////////////////////////////////////////////// |
49 | |
50 | const char* sass_op_to_name(enum Sass_OP op); |
51 | |
52 | const char* sass_op_separator(enum Sass_OP op); |
53 | |
54 | ////////////////////////////////////////////////////////// |
55 | // Abstract base class for all abstract syntax tree nodes. |
56 | ////////////////////////////////////////////////////////// |
57 | class AST_Node : public SharedObj { |
58 | ADD_PROPERTY(SourceSpan, pstate) |
59 | public: |
60 | AST_Node(SourceSpan pstate) |
61 | : pstate_(pstate) |
62 | { } |
63 | AST_Node(const AST_Node* ptr) |
64 | : pstate_(ptr->pstate_) |
65 | { } |
66 | |
67 | // allow implicit conversion to string |
68 | // needed for by SharedPtr implementation |
69 | operator sass::string() { |
70 | return to_string(); |
71 | } |
72 | |
73 | // AST_Node(AST_Node& ptr) = delete; |
74 | |
75 | virtual ~AST_Node() = 0; |
76 | virtual size_t hash() const { return 0; } |
77 | virtual sass::string inspect() const { return to_string(opt: { INSPECT, 5 }); } |
78 | virtual sass::string to_sass() const { return to_string(opt: { TO_SASS, 5 }); } |
79 | virtual sass::string to_string(Sass_Inspect_Options opt) const; |
80 | virtual sass::string to_css(Sass_Inspect_Options opt) const; |
81 | virtual sass::string to_string() const; |
82 | virtual void cloneChildren() {}; |
83 | // generic find function (not fully implemented yet) |
84 | // ToDo: add specific implementations to all children |
85 | virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); }; |
86 | void update_pstate(const SourceSpan& pstate); |
87 | |
88 | // Some objects are not meant to be compared |
89 | // ToDo: maybe fall-back to pointer comparison? |
90 | virtual bool operator== (const AST_Node& rhs) const { |
91 | throw std::runtime_error("operator== not implemented" ); |
92 | } |
93 | |
94 | // We can give some reasonable implementations by using |
95 | // invert operators on the specialized implementations |
96 | virtual bool operator!= (const AST_Node& rhs) const { |
97 | // Unequal if not equal |
98 | return !(*this == rhs); |
99 | } |
100 | |
101 | ATTACH_ABSTRACT_AST_OPERATIONS(AST_Node); |
102 | ATTACH_ABSTRACT_CRTP_PERFORM_METHODS() |
103 | }; |
104 | inline AST_Node::~AST_Node() { } |
105 | |
106 | ////////////////////////////////////////////////////////////////////// |
107 | // define cast template now (need complete type) |
108 | ////////////////////////////////////////////////////////////////////// |
109 | |
110 | template<class T> |
111 | T* Cast(AST_Node* ptr) { |
112 | return ptr && typeid(T) == typeid(*ptr) ? |
113 | static_cast<T*>(ptr) : NULL; |
114 | }; |
115 | |
116 | template<class T> |
117 | const T* Cast(const AST_Node* ptr) { |
118 | return ptr && typeid(T) == typeid(*ptr) ? |
119 | static_cast<const T*>(ptr) : NULL; |
120 | }; |
121 | |
122 | ////////////////////////////////////////////////////////////////////// |
123 | // Abstract base class for expressions. This side of the AST hierarchy |
124 | // represents elements in value contexts, which exist primarily to be |
125 | // evaluated and returned. |
126 | ////////////////////////////////////////////////////////////////////// |
127 | class Expression : public AST_Node { |
128 | public: |
129 | enum Type { |
130 | NONE, |
131 | BOOLEAN, |
132 | NUMBER, |
133 | COLOR, |
134 | STRING, |
135 | LIST, |
136 | MAP, |
137 | SELECTOR, |
138 | NULL_VAL, |
139 | FUNCTION_VAL, |
140 | C_WARNING, |
141 | C_ERROR, |
142 | FUNCTION, |
143 | VARIABLE, |
144 | PARENT, |
145 | NUM_TYPES |
146 | }; |
147 | private: |
148 | // expressions in some contexts shouldn't be evaluated |
149 | ADD_PROPERTY(bool, is_delayed) |
150 | ADD_PROPERTY(bool, is_expanded) |
151 | ADD_PROPERTY(bool, is_interpolant) |
152 | ADD_PROPERTY(Type, concrete_type) |
153 | public: |
154 | Expression(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE); |
155 | virtual operator bool() { return true; } |
156 | virtual ~Expression() { } |
157 | virtual bool is_invisible() const { return false; } |
158 | |
159 | virtual sass::string type() const { return "" ; } |
160 | static sass::string type_name() { return "" ; } |
161 | |
162 | virtual bool is_false() { return false; } |
163 | // virtual bool is_true() { return !is_false(); } |
164 | virtual bool operator< (const Expression& rhs) const { return false; } |
165 | virtual bool operator== (const Expression& rhs) const { return false; } |
166 | inline bool operator>(const Expression& rhs) const { return rhs < *this; } |
167 | inline bool operator!=(const Expression& rhs) const { return !(rhs == *this); } |
168 | virtual bool eq(const Expression& rhs) const { return *this == rhs; }; |
169 | virtual void set_delayed(bool delayed) { is_delayed(is_delayed__: delayed); } |
170 | virtual bool has_interpolant() const { return is_interpolant(); } |
171 | virtual bool is_left_interpolant() const { return is_interpolant(); } |
172 | virtual bool is_right_interpolant() const { return is_interpolant(); } |
173 | ATTACH_VIRTUAL_AST_OPERATIONS(Expression); |
174 | size_t hash() const override { return 0; } |
175 | }; |
176 | |
177 | } |
178 | |
179 | ///////////////////////////////////////////////////////////////////////////////////// |
180 | // Hash method specializations for std::unordered_map to work with Sass::Expression |
181 | ///////////////////////////////////////////////////////////////////////////////////// |
182 | |
183 | namespace std { |
184 | template<> |
185 | struct hash<Sass::ExpressionObj> |
186 | { |
187 | size_t operator()(Sass::ExpressionObj s) const |
188 | { |
189 | return s->hash(); |
190 | } |
191 | }; |
192 | template<> |
193 | struct equal_to<Sass::ExpressionObj> |
194 | { |
195 | bool operator()( Sass::ExpressionObj lhs, Sass::ExpressionObj rhs) const |
196 | { |
197 | return lhs->hash() == rhs->hash(); |
198 | } |
199 | }; |
200 | } |
201 | |
202 | namespace Sass { |
203 | |
204 | ///////////////////////////////////////////////////////////////////////////// |
205 | // Mixin class for AST nodes that should behave like vectors. Uses the |
206 | // "Template Method" design pattern to allow subclasses to adjust their flags |
207 | // when certain objects are pushed. |
208 | ///////////////////////////////////////////////////////////////////////////// |
209 | template <typename T> |
210 | class Vectorized { |
211 | sass::vector<T> elements_; |
212 | protected: |
213 | mutable size_t hash_; |
214 | void reset_hash() { hash_ = 0; } |
215 | virtual void adjust_after_pushing(T element) { } |
216 | public: |
217 | Vectorized(size_t s = 0) : hash_(0) |
218 | { elements_.reserve(s); } |
219 | Vectorized(sass::vector<T> vec) : |
220 | elements_(std::move(vec)), |
221 | hash_(0) |
222 | {} |
223 | virtual ~Vectorized() = 0; |
224 | size_t length() const { return elements_.size(); } |
225 | bool empty() const { return elements_.empty(); } |
226 | void clear() { return elements_.clear(); } |
227 | T& last() { return elements_.back(); } |
228 | T& first() { return elements_.front(); } |
229 | const T& last() const { return elements_.back(); } |
230 | const T& first() const { return elements_.front(); } |
231 | |
232 | bool operator== (const Vectorized<T>& rhs) const { |
233 | // Abort early if sizes do not match |
234 | if (length() != rhs.length()) return false; |
235 | // Otherwise test each node for object equalicy in order |
236 | return std::equal(begin(), end(), rhs.begin(), ObjEqualityFn<T>); |
237 | } |
238 | |
239 | bool operator!= (const Vectorized<T>& rhs) const { |
240 | return !(*this == rhs); |
241 | } |
242 | |
243 | T& operator[](size_t i) { return elements_[i]; } |
244 | virtual const T& at(size_t i) const { return elements_.at(i); } |
245 | virtual T& at(size_t i) { return elements_.at(i); } |
246 | const T& get(size_t i) const { return elements_[i]; } |
247 | const T& operator[](size_t i) const { return elements_[i]; } |
248 | |
249 | // Implicitly get the sass::vector from our object |
250 | // Makes the Vector directly assignable to sass::vector |
251 | // You are responsible to make a copy if needed |
252 | // Note: since this returns the real object, we can't |
253 | // Note: guarantee that the hash will not get out of sync |
254 | operator sass::vector<T>&() { return elements_; } |
255 | operator const sass::vector<T>&() const { return elements_; } |
256 | |
257 | // Explicitly request all elements as a real sass::vector |
258 | // You are responsible to make a copy if needed |
259 | // Note: since this returns the real object, we can't |
260 | // Note: guarantee that the hash will not get out of sync |
261 | sass::vector<T>& elements() { return elements_; } |
262 | const sass::vector<T>& elements() const { return elements_; } |
263 | |
264 | // Insert all items from compatible vector |
265 | void concat(const sass::vector<T>& v) |
266 | { |
267 | if (!v.empty()) reset_hash(); |
268 | elements().insert(end(), v.begin(), v.end()); |
269 | } |
270 | |
271 | // Syntatic sugar for pointers |
272 | void concat(const Vectorized<T>* v) |
273 | { |
274 | if (v != nullptr) { |
275 | return concat(*v); |
276 | } |
277 | } |
278 | |
279 | // Insert one item on the front |
280 | void unshift(T element) |
281 | { |
282 | reset_hash(); |
283 | elements_.insert(begin(), element); |
284 | } |
285 | |
286 | // Remove and return item on the front |
287 | // ToDo: handle empty vectors |
288 | T shift() { |
289 | reset_hash(); |
290 | T first = get(i: 0); |
291 | elements_.erase(begin()); |
292 | return first; |
293 | } |
294 | |
295 | // Insert one item on the back |
296 | // ToDo: rename this to push |
297 | void append(T element) |
298 | { |
299 | reset_hash(); |
300 | elements_.insert(end(), element); |
301 | // ToDo: Mostly used by parameters and arguments |
302 | // ToDo: Find a more elegant way to support this |
303 | adjust_after_pushing(element); |
304 | } |
305 | |
306 | // Check if an item already exists |
307 | // Uses underlying object `operator==` |
308 | // E.g. compares the actual objects |
309 | bool contains(const T& el) const { |
310 | for (const T& rhs : elements_) { |
311 | // Test the underlying objects for equality |
312 | // A std::find checks for pointer equality |
313 | if (ObjEqualityFn(el, rhs)) { |
314 | return true; |
315 | } |
316 | } |
317 | return false; |
318 | } |
319 | |
320 | // This might be better implemented as `operator=`? |
321 | void elements(sass::vector<T> e) { |
322 | reset_hash(); |
323 | elements_ = std::move(e); |
324 | } |
325 | |
326 | virtual size_t hash() const |
327 | { |
328 | if (hash_ == 0) { |
329 | for (const T& el : elements_) { |
330 | hash_combine(hash_, el->hash()); |
331 | } |
332 | } |
333 | return hash_; |
334 | } |
335 | |
336 | template <typename P, typename V> |
337 | typename sass::vector<T>::iterator insert(P position, const V& val) { |
338 | reset_hash(); |
339 | return elements_.insert(position, val); |
340 | } |
341 | |
342 | typename sass::vector<T>::iterator end() { return elements_.end(); } |
343 | typename sass::vector<T>::iterator begin() { return elements_.begin(); } |
344 | typename sass::vector<T>::const_iterator end() const { return elements_.end(); } |
345 | typename sass::vector<T>::const_iterator begin() const { return elements_.begin(); } |
346 | typename sass::vector<T>::iterator erase(typename sass::vector<T>::iterator el) { reset_hash(); return elements_.erase(el); } |
347 | typename sass::vector<T>::const_iterator erase(typename sass::vector<T>::const_iterator el) { reset_hash(); return elements_.erase(el); } |
348 | |
349 | }; |
350 | template <typename T> |
351 | inline Vectorized<T>::~Vectorized() { } |
352 | |
353 | ///////////////////////////////////////////////////////////////////////////// |
354 | // Mixin class for AST nodes that should behave like a hash table. Uses an |
355 | // extra <sass::vector> internally to maintain insertion order for interation. |
356 | ///////////////////////////////////////////////////////////////////////////// |
357 | template <typename K, typename T, typename U> |
358 | class Hashed { |
359 | private: |
360 | std::unordered_map< |
361 | K, T, ObjHash, ObjEquality |
362 | > elements_; |
363 | |
364 | sass::vector<K> _keys; |
365 | sass::vector<T> _values; |
366 | protected: |
367 | mutable size_t hash_; |
368 | K duplicate_key_; |
369 | void reset_hash() { hash_ = 0; } |
370 | void reset_duplicate_key() { duplicate_key_ = {}; } |
371 | virtual void adjust_after_pushing(std::pair<K, T> p) { } |
372 | public: |
373 | Hashed(size_t s = 0) |
374 | : elements_(), |
375 | _keys(), |
376 | _values(), |
377 | hash_(0), duplicate_key_({}) |
378 | { |
379 | _keys.reserve(s); |
380 | _values.reserve(s); |
381 | elements_.reserve(s); |
382 | } |
383 | virtual ~Hashed(); |
384 | size_t length() const { return _keys.size(); } |
385 | bool empty() const { return _keys.empty(); } |
386 | bool has(K k) const { |
387 | return elements_.find(k) != elements_.end(); |
388 | } |
389 | T at(K k) const { |
390 | if (elements_.count(k)) |
391 | { |
392 | return elements_.at(k); |
393 | } |
394 | else { return {}; } |
395 | } |
396 | bool has_duplicate_key() const { return duplicate_key_ != nullptr; } |
397 | K get_duplicate_key() const { return duplicate_key_; } |
398 | const std::unordered_map< |
399 | K, T, ObjHash, ObjEquality |
400 | >& elements() { return elements_; } |
401 | Hashed& operator<<(std::pair<K, T> p) |
402 | { |
403 | reset_hash(); |
404 | |
405 | if (!has(k: p.first)) { |
406 | _keys.push_back(p.first); |
407 | _values.push_back(p.second); |
408 | } |
409 | else if (!duplicate_key_) { |
410 | duplicate_key_ = p.first; |
411 | } |
412 | |
413 | elements_[p.first] = p.second; |
414 | |
415 | adjust_after_pushing(p); |
416 | return *this; |
417 | } |
418 | Hashed& operator+=(Hashed* h) |
419 | { |
420 | if (length() == 0) { |
421 | this->elements_ = h->elements_; |
422 | this->_values = h->_values; |
423 | this->_keys = h->_keys; |
424 | return *this; |
425 | } |
426 | |
427 | for (auto key : h->keys()) { |
428 | *this << std::make_pair(key, h->at(key)); |
429 | } |
430 | |
431 | reset_duplicate_key(); |
432 | return *this; |
433 | } |
434 | const std::unordered_map< |
435 | K, T, ObjHash, ObjEquality |
436 | >& pairs() const { return elements_; } |
437 | |
438 | const sass::vector<K>& keys() const { return _keys; } |
439 | const sass::vector<T>& values() const { return _values; } |
440 | |
441 | // std::unordered_map<ExpressionObj, ExpressionObj>::iterator end() { return elements_.end(); } |
442 | // std::unordered_map<ExpressionObj, ExpressionObj>::iterator begin() { return elements_.begin(); } |
443 | // std::unordered_map<ExpressionObj, ExpressionObj>::const_iterator end() const { return elements_.end(); } |
444 | // std::unordered_map<ExpressionObj, ExpressionObj>::const_iterator begin() const { return elements_.begin(); } |
445 | |
446 | }; |
447 | template <typename K, typename T, typename U> |
448 | inline Hashed<K, T, U>::~Hashed() { } |
449 | |
450 | ///////////////////////////////////////////////////////////////////////// |
451 | // Abstract base class for statements. This side of the AST hierarchy |
452 | // represents elements in expansion contexts, which exist primarily to be |
453 | // rewritten and macro-expanded. |
454 | ///////////////////////////////////////////////////////////////////////// |
455 | class Statement : public AST_Node { |
456 | public: |
457 | enum Type { |
458 | NONE, |
459 | RULESET, |
460 | MEDIA, |
461 | DIRECTIVE, |
462 | SUPPORTS, |
463 | ATROOT, |
464 | BUBBLE, |
465 | CONTENT, |
466 | KEYFRAMERULE, |
467 | DECLARATION, |
468 | ASSIGNMENT, |
469 | IMPORT_STUB, |
470 | IMPORT, |
471 | , |
472 | WARNING, |
473 | RETURN, |
474 | EXTEND, |
475 | ERROR, |
476 | DEBUGSTMT, |
477 | WHILE, |
478 | EACH, |
479 | FOR, |
480 | IF |
481 | }; |
482 | private: |
483 | ADD_PROPERTY(Type, statement_type) |
484 | ADD_PROPERTY(size_t, tabs) |
485 | ADD_PROPERTY(bool, group_end) |
486 | public: |
487 | Statement(SourceSpan pstate, Type st = NONE, size_t t = 0); |
488 | virtual ~Statement() = 0; // virtual destructor |
489 | // needed for rearranging nested rulesets during CSS emission |
490 | virtual bool bubbles(); |
491 | virtual bool has_content(); |
492 | virtual bool is_invisible() const; |
493 | ATTACH_VIRTUAL_AST_OPERATIONS(Statement) |
494 | }; |
495 | inline Statement::~Statement() { } |
496 | |
497 | //////////////////////// |
498 | // Blocks of statements. |
499 | //////////////////////// |
500 | class Block final : public Statement, public Vectorized<Statement_Obj> { |
501 | ADD_PROPERTY(bool, is_root) |
502 | // needed for properly formatted CSS emission |
503 | protected: |
504 | void adjust_after_pushing(Statement_Obj s) override {} |
505 | public: |
506 | Block(SourceSpan pstate, size_t s = 0, bool r = false); |
507 | bool isInvisible() const; |
508 | bool has_content() override; |
509 | ATTACH_AST_OPERATIONS(Block) |
510 | ATTACH_CRTP_PERFORM_METHODS() |
511 | }; |
512 | |
513 | //////////////////////////////////////////////////////////////////////// |
514 | // Abstract base class for statements that contain blocks of statements. |
515 | //////////////////////////////////////////////////////////////////////// |
516 | class ParentStatement : public Statement { |
517 | ADD_PROPERTY(Block_Obj, block) |
518 | public: |
519 | ParentStatement(SourceSpan pstate, Block_Obj b); |
520 | ParentStatement(const ParentStatement* ptr); // copy constructor |
521 | virtual ~ParentStatement() = 0; // virtual destructor |
522 | virtual bool has_content() override; |
523 | }; |
524 | inline ParentStatement::~ParentStatement() { } |
525 | |
526 | ///////////////////////////////////////////////////////////////////////////// |
527 | // Rulesets (i.e., sets of styles headed by a selector and containing a block |
528 | // of style declarations. |
529 | ///////////////////////////////////////////////////////////////////////////// |
530 | class StyleRule final : public ParentStatement { |
531 | ADD_PROPERTY(SelectorListObj, selector) |
532 | ADD_PROPERTY(Selector_Schema_Obj, schema) |
533 | ADD_PROPERTY(bool, is_root); |
534 | public: |
535 | StyleRule(SourceSpan pstate, SelectorListObj s = {}, Block_Obj b = {}); |
536 | bool is_invisible() const override; |
537 | ATTACH_AST_OPERATIONS(StyleRule) |
538 | ATTACH_CRTP_PERFORM_METHODS() |
539 | }; |
540 | |
541 | ///////////////// |
542 | // Bubble. |
543 | ///////////////// |
544 | class Bubble final : public Statement { |
545 | ADD_PROPERTY(Statement_Obj, node) |
546 | ADD_PROPERTY(bool, group_end) |
547 | public: |
548 | Bubble(SourceSpan pstate, Statement_Obj n, Statement_Obj g = {}, size_t t = 0); |
549 | bool bubbles() override; |
550 | ATTACH_AST_OPERATIONS(Bubble) |
551 | ATTACH_CRTP_PERFORM_METHODS() |
552 | }; |
553 | |
554 | ///////////////// |
555 | // Trace. |
556 | ///////////////// |
557 | class Trace final : public ParentStatement { |
558 | ADD_CONSTREF(char, type) |
559 | ADD_CONSTREF(sass::string, name) |
560 | public: |
561 | Trace(SourceSpan pstate, sass::string n, Block_Obj b = {}, char type = 'm'); |
562 | ATTACH_AST_OPERATIONS(Trace) |
563 | ATTACH_CRTP_PERFORM_METHODS() |
564 | }; |
565 | |
566 | /////////////////////////////////////////////////////////////////////// |
567 | // At-rules -- arbitrary directives beginning with "@" that may have an |
568 | // optional statement block. |
569 | /////////////////////////////////////////////////////////////////////// |
570 | class AtRule final : public ParentStatement { |
571 | ADD_CONSTREF(sass::string, keyword) |
572 | ADD_PROPERTY(SelectorListObj, selector) |
573 | ADD_PROPERTY(ExpressionObj, value) |
574 | public: |
575 | AtRule(SourceSpan pstate, sass::string kwd, SelectorListObj sel = {}, Block_Obj b = {}, ExpressionObj val = {}); |
576 | bool bubbles() override; |
577 | bool is_media(); |
578 | bool is_keyframes(); |
579 | ATTACH_AST_OPERATIONS(AtRule) |
580 | ATTACH_CRTP_PERFORM_METHODS() |
581 | }; |
582 | |
583 | /////////////////////////////////////////////////////////////////////// |
584 | // Keyframe-rules -- the child blocks of "@keyframes" nodes. |
585 | /////////////////////////////////////////////////////////////////////// |
586 | class Keyframe_Rule final : public ParentStatement { |
587 | // according to css spec, this should be <keyframes-name> |
588 | // <keyframes-name> = <custom-ident> | <string> |
589 | ADD_PROPERTY(SelectorListObj, name) |
590 | public: |
591 | Keyframe_Rule(SourceSpan pstate, Block_Obj b); |
592 | ATTACH_AST_OPERATIONS(Keyframe_Rule) |
593 | ATTACH_CRTP_PERFORM_METHODS() |
594 | }; |
595 | |
596 | //////////////////////////////////////////////////////////////////////// |
597 | // Declarations -- style rules consisting of a property name and values. |
598 | //////////////////////////////////////////////////////////////////////// |
599 | class Declaration final : public ParentStatement { |
600 | ADD_PROPERTY(String_Obj, property) |
601 | ADD_PROPERTY(ExpressionObj, value) |
602 | ADD_PROPERTY(bool, is_important) |
603 | ADD_PROPERTY(bool, is_custom_property) |
604 | ADD_PROPERTY(bool, is_indented) |
605 | public: |
606 | Declaration(SourceSpan pstate, String_Obj prop, ExpressionObj val, bool i = false, bool c = false, Block_Obj b = {}); |
607 | bool is_invisible() const override; |
608 | ATTACH_AST_OPERATIONS(Declaration) |
609 | ATTACH_CRTP_PERFORM_METHODS() |
610 | }; |
611 | |
612 | ///////////////////////////////////// |
613 | // Assignments -- variable and value. |
614 | ///////////////////////////////////// |
615 | class Assignment final : public Statement { |
616 | ADD_CONSTREF(sass::string, variable) |
617 | ADD_PROPERTY(ExpressionObj, value) |
618 | ADD_PROPERTY(bool, is_default) |
619 | ADD_PROPERTY(bool, is_global) |
620 | public: |
621 | Assignment(SourceSpan pstate, sass::string var, ExpressionObj val, bool is_default = false, bool is_global = false); |
622 | ATTACH_AST_OPERATIONS(Assignment) |
623 | ATTACH_CRTP_PERFORM_METHODS() |
624 | }; |
625 | |
626 | //////////////////////////////////////////////////////////////////////////// |
627 | // Import directives. CSS and Sass import lists can be intermingled, so it's |
628 | // necessary to store a list of each in an Import node. |
629 | //////////////////////////////////////////////////////////////////////////// |
630 | class Import final : public Statement { |
631 | sass::vector<ExpressionObj> urls_; |
632 | sass::vector<Include> incs_; |
633 | ADD_PROPERTY(List_Obj, import_queries); |
634 | public: |
635 | Import(SourceSpan pstate); |
636 | sass::vector<Include>& incs(); |
637 | sass::vector<ExpressionObj>& urls(); |
638 | ATTACH_AST_OPERATIONS(Import) |
639 | ATTACH_CRTP_PERFORM_METHODS() |
640 | }; |
641 | |
642 | // not yet resolved single import |
643 | // so far we only know requested name |
644 | class Import_Stub final : public Statement { |
645 | Include resource_; |
646 | public: |
647 | Import_Stub(SourceSpan pstate, Include res); |
648 | Include resource(); |
649 | sass::string imp_path(); |
650 | sass::string abs_path(); |
651 | ATTACH_AST_OPERATIONS(Import_Stub) |
652 | ATTACH_CRTP_PERFORM_METHODS() |
653 | }; |
654 | |
655 | ////////////////////////////// |
656 | // The Sass `@warn` directive. |
657 | ////////////////////////////// |
658 | class WarningRule final : public Statement { |
659 | ADD_PROPERTY(ExpressionObj, message) |
660 | public: |
661 | WarningRule(SourceSpan pstate, ExpressionObj msg); |
662 | ATTACH_AST_OPERATIONS(WarningRule) |
663 | ATTACH_CRTP_PERFORM_METHODS() |
664 | }; |
665 | |
666 | /////////////////////////////// |
667 | // The Sass `@error` directive. |
668 | /////////////////////////////// |
669 | class ErrorRule final : public Statement { |
670 | ADD_PROPERTY(ExpressionObj, message) |
671 | public: |
672 | ErrorRule(SourceSpan pstate, ExpressionObj msg); |
673 | ATTACH_AST_OPERATIONS(ErrorRule) |
674 | ATTACH_CRTP_PERFORM_METHODS() |
675 | }; |
676 | |
677 | /////////////////////////////// |
678 | // The Sass `@debug` directive. |
679 | /////////////////////////////// |
680 | class DebugRule final : public Statement { |
681 | ADD_PROPERTY(ExpressionObj, value) |
682 | public: |
683 | DebugRule(SourceSpan pstate, ExpressionObj val); |
684 | ATTACH_AST_OPERATIONS(DebugRule) |
685 | ATTACH_CRTP_PERFORM_METHODS() |
686 | }; |
687 | |
688 | /////////////////////////////////////////// |
689 | // CSS comments. These may be interpolated. |
690 | /////////////////////////////////////////// |
691 | class final : public Statement { |
692 | ADD_PROPERTY(String_Obj, ) |
693 | ADD_PROPERTY(bool, ) |
694 | public: |
695 | (SourceSpan pstate, String_Obj txt, bool is_important); |
696 | virtual bool () const override; |
697 | ATTACH_AST_OPERATIONS(Comment) |
698 | ATTACH_CRTP_PERFORM_METHODS() |
699 | }; |
700 | |
701 | //////////////////////////////////// |
702 | // The Sass `@if` control directive. |
703 | //////////////////////////////////// |
704 | class If final : public ParentStatement { |
705 | ADD_PROPERTY(ExpressionObj, predicate) |
706 | ADD_PROPERTY(Block_Obj, alternative) |
707 | public: |
708 | If(SourceSpan pstate, ExpressionObj pred, Block_Obj con, Block_Obj alt = {}); |
709 | virtual bool has_content() override; |
710 | ATTACH_AST_OPERATIONS(If) |
711 | ATTACH_CRTP_PERFORM_METHODS() |
712 | }; |
713 | |
714 | ///////////////////////////////////// |
715 | // The Sass `@for` control directive. |
716 | ///////////////////////////////////// |
717 | class ForRule final : public ParentStatement { |
718 | ADD_CONSTREF(sass::string, variable) |
719 | ADD_PROPERTY(ExpressionObj, lower_bound) |
720 | ADD_PROPERTY(ExpressionObj, upper_bound) |
721 | ADD_PROPERTY(bool, is_inclusive) |
722 | public: |
723 | ForRule(SourceSpan pstate, sass::string var, ExpressionObj lo, ExpressionObj hi, Block_Obj b, bool inc); |
724 | ATTACH_AST_OPERATIONS(ForRule) |
725 | ATTACH_CRTP_PERFORM_METHODS() |
726 | }; |
727 | |
728 | ////////////////////////////////////// |
729 | // The Sass `@each` control directive. |
730 | ////////////////////////////////////// |
731 | class EachRule final : public ParentStatement { |
732 | ADD_PROPERTY(sass::vector<sass::string>, variables) |
733 | ADD_PROPERTY(ExpressionObj, list) |
734 | public: |
735 | EachRule(SourceSpan pstate, sass::vector<sass::string> vars, ExpressionObj lst, Block_Obj b); |
736 | ATTACH_AST_OPERATIONS(EachRule) |
737 | ATTACH_CRTP_PERFORM_METHODS() |
738 | }; |
739 | |
740 | /////////////////////////////////////// |
741 | // The Sass `@while` control directive. |
742 | /////////////////////////////////////// |
743 | class WhileRule final : public ParentStatement { |
744 | ADD_PROPERTY(ExpressionObj, predicate) |
745 | public: |
746 | WhileRule(SourceSpan pstate, ExpressionObj pred, Block_Obj b); |
747 | ATTACH_AST_OPERATIONS(WhileRule) |
748 | ATTACH_CRTP_PERFORM_METHODS() |
749 | }; |
750 | |
751 | ///////////////////////////////////////////////////////////// |
752 | // The @return directive for use inside SassScript functions. |
753 | ///////////////////////////////////////////////////////////// |
754 | class Return final : public Statement { |
755 | ADD_PROPERTY(ExpressionObj, value) |
756 | public: |
757 | Return(SourceSpan pstate, ExpressionObj val); |
758 | ATTACH_AST_OPERATIONS(Return) |
759 | ATTACH_CRTP_PERFORM_METHODS() |
760 | }; |
761 | |
762 | ///////////////////////////////////////////////////////////////////////////// |
763 | // Definitions for both mixins and functions. The two cases are distinguished |
764 | // by a type tag. |
765 | ///////////////////////////////////////////////////////////////////////////// |
766 | class Definition final : public ParentStatement { |
767 | public: |
768 | enum Type { MIXIN, FUNCTION }; |
769 | ADD_CONSTREF(sass::string, name) |
770 | ADD_PROPERTY(Parameters_Obj, parameters) |
771 | ADD_PROPERTY(Env*, environment) |
772 | ADD_PROPERTY(Type, type) |
773 | ADD_PROPERTY(Native_Function, native_function) |
774 | ADD_PROPERTY(Sass_Function_Entry, c_function) |
775 | ADD_PROPERTY(void*, cookie) |
776 | ADD_PROPERTY(bool, is_overload_stub) |
777 | ADD_PROPERTY(Signature, signature) |
778 | public: |
779 | Definition(SourceSpan pstate, |
780 | sass::string n, |
781 | Parameters_Obj params, |
782 | Block_Obj b, |
783 | Type t); |
784 | Definition(SourceSpan pstate, |
785 | Signature sig, |
786 | sass::string n, |
787 | Parameters_Obj params, |
788 | Native_Function func_ptr, |
789 | bool overload_stub = false); |
790 | Definition(SourceSpan pstate, |
791 | Signature sig, |
792 | sass::string n, |
793 | Parameters_Obj params, |
794 | Sass_Function_Entry c_func); |
795 | ATTACH_AST_OPERATIONS(Definition) |
796 | ATTACH_CRTP_PERFORM_METHODS() |
797 | }; |
798 | |
799 | ////////////////////////////////////// |
800 | // Mixin calls (i.e., `@include ...`). |
801 | ////////////////////////////////////// |
802 | class Mixin_Call final : public ParentStatement { |
803 | ADD_CONSTREF(sass::string, name) |
804 | ADD_PROPERTY(Arguments_Obj, arguments) |
805 | ADD_PROPERTY(Parameters_Obj, block_parameters) |
806 | public: |
807 | Mixin_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Parameters_Obj b_params = {}, Block_Obj b = {}); |
808 | ATTACH_AST_OPERATIONS(Mixin_Call) |
809 | ATTACH_CRTP_PERFORM_METHODS() |
810 | }; |
811 | |
812 | /////////////////////////////////////////////////// |
813 | // The @content directive for mixin content blocks. |
814 | /////////////////////////////////////////////////// |
815 | class Content final : public Statement { |
816 | ADD_PROPERTY(Arguments_Obj, arguments) |
817 | public: |
818 | Content(SourceSpan pstate, Arguments_Obj args); |
819 | ATTACH_AST_OPERATIONS(Content) |
820 | ATTACH_CRTP_PERFORM_METHODS() |
821 | }; |
822 | |
823 | //////////////////////////////////////////////////////////////////////////// |
824 | // Arithmetic negation (logical negation is just an ordinary function call). |
825 | //////////////////////////////////////////////////////////////////////////// |
826 | class Unary_Expression final : public Expression { |
827 | public: |
828 | enum Type { PLUS, MINUS, NOT, SLASH }; |
829 | private: |
830 | HASH_PROPERTY(Type, optype) |
831 | HASH_PROPERTY(ExpressionObj, operand) |
832 | mutable size_t hash_; |
833 | public: |
834 | Unary_Expression(SourceSpan pstate, Type t, ExpressionObj o); |
835 | const sass::string type_name(); |
836 | virtual bool operator==(const Expression& rhs) const override; |
837 | size_t hash() const override; |
838 | ATTACH_AST_OPERATIONS(Unary_Expression) |
839 | ATTACH_CRTP_PERFORM_METHODS() |
840 | }; |
841 | |
842 | //////////////////////////////////////////////////////////// |
843 | // Individual argument objects for mixin and function calls. |
844 | //////////////////////////////////////////////////////////// |
845 | class Argument final : public Expression { |
846 | HASH_PROPERTY(ExpressionObj, value) |
847 | HASH_CONSTREF(sass::string, name) |
848 | ADD_PROPERTY(bool, is_rest_argument) |
849 | ADD_PROPERTY(bool, is_keyword_argument) |
850 | mutable size_t hash_; |
851 | public: |
852 | Argument(SourceSpan pstate, ExpressionObj val, sass::string n = "" , bool rest = false, bool keyword = false); |
853 | void set_delayed(bool delayed) override; |
854 | bool operator==(const Expression& rhs) const override; |
855 | size_t hash() const override; |
856 | ATTACH_AST_OPERATIONS(Argument) |
857 | ATTACH_CRTP_PERFORM_METHODS() |
858 | }; |
859 | |
860 | //////////////////////////////////////////////////////////////////////// |
861 | // Argument lists -- in their own class to facilitate context-sensitive |
862 | // error checking (e.g., ensuring that all ordinal arguments precede all |
863 | // named arguments). |
864 | //////////////////////////////////////////////////////////////////////// |
865 | class Arguments final : public Expression, public Vectorized<Argument_Obj> { |
866 | ADD_PROPERTY(bool, has_named_arguments) |
867 | ADD_PROPERTY(bool, has_rest_argument) |
868 | ADD_PROPERTY(bool, has_keyword_argument) |
869 | protected: |
870 | void adjust_after_pushing(Argument_Obj a) override; |
871 | public: |
872 | Arguments(SourceSpan pstate); |
873 | void set_delayed(bool delayed) override; |
874 | Argument_Obj get_rest_argument(); |
875 | Argument_Obj get_keyword_argument(); |
876 | ATTACH_AST_OPERATIONS(Arguments) |
877 | ATTACH_CRTP_PERFORM_METHODS() |
878 | }; |
879 | |
880 | |
881 | // A Media StyleRule before it has been evaluated |
882 | // Could be already final or an interpolation |
883 | class MediaRule final : public ParentStatement { |
884 | ADD_PROPERTY(List_Obj, schema) |
885 | public: |
886 | MediaRule(SourceSpan pstate, Block_Obj block = {}); |
887 | |
888 | bool bubbles() override { return true; }; |
889 | bool is_invisible() const override { return false; }; |
890 | ATTACH_AST_OPERATIONS(MediaRule) |
891 | ATTACH_CRTP_PERFORM_METHODS() |
892 | }; |
893 | |
894 | // A Media StyleRule after it has been evaluated |
895 | // Representing the static or resulting css |
896 | class CssMediaRule final : public ParentStatement, |
897 | public Vectorized<CssMediaQuery_Obj> { |
898 | public: |
899 | CssMediaRule(SourceSpan pstate, Block_Obj b); |
900 | bool bubbles() override { return true; }; |
901 | bool isInvisible() const { return empty(); } |
902 | bool is_invisible() const override { return false; }; |
903 | |
904 | public: |
905 | // Hash and equality implemtation from vector |
906 | size_t hash() const override { return Vectorized::hash(); } |
907 | // Check if two instances are considered equal |
908 | bool operator== (const CssMediaRule& rhs) const { |
909 | return Vectorized::operator== (rhs); |
910 | } |
911 | bool operator!=(const CssMediaRule& rhs) const { |
912 | // Invert from equality |
913 | return !(*this == rhs); |
914 | } |
915 | |
916 | ATTACH_AST_OPERATIONS(CssMediaRule) |
917 | ATTACH_CRTP_PERFORM_METHODS() |
918 | }; |
919 | |
920 | // Media Queries after they have been evaluated |
921 | // Representing the static or resulting css |
922 | class CssMediaQuery final : public AST_Node { |
923 | |
924 | // The modifier, probably either "not" or "only". |
925 | // This may be `null` if no modifier is in use. |
926 | ADD_PROPERTY(sass::string, modifier); |
927 | |
928 | // The media type, for example "screen" or "print". |
929 | // This may be `null`. If so, [features] will not be empty. |
930 | ADD_PROPERTY(sass::string, type); |
931 | |
932 | // Feature queries, including parentheses. |
933 | ADD_PROPERTY(sass::vector<sass::string>, features); |
934 | |
935 | public: |
936 | CssMediaQuery(SourceSpan pstate); |
937 | |
938 | // Check if two instances are considered equal |
939 | bool operator== (const CssMediaQuery& rhs) const; |
940 | bool operator!=(const CssMediaQuery& rhs) const { |
941 | // Invert from equality |
942 | return !(*this == rhs); |
943 | } |
944 | |
945 | // Returns true if this query is empty |
946 | // Meaning it has no type and features |
947 | bool empty() const { |
948 | return type_.empty() |
949 | && modifier_.empty() |
950 | && features_.empty(); |
951 | } |
952 | |
953 | // Whether this media query matches all media types. |
954 | bool matchesAllTypes() const { |
955 | return type_.empty() || Util::equalsLiteral(lit: "all" , test: type_); |
956 | } |
957 | |
958 | // Merges this with [other] and adds a query that matches the intersection |
959 | // of both inputs to [result]. Returns false if the result is unrepresentable |
960 | CssMediaQuery_Obj merge(CssMediaQuery_Obj& other); |
961 | |
962 | ATTACH_AST_OPERATIONS(CssMediaQuery) |
963 | ATTACH_CRTP_PERFORM_METHODS() |
964 | }; |
965 | |
966 | //////////////////////////////////////////////////// |
967 | // Media queries (replaced by MediaRule at al). |
968 | // ToDo: only used for interpolation case |
969 | //////////////////////////////////////////////////// |
970 | class Media_Query final : public Expression, |
971 | public Vectorized<Media_Query_ExpressionObj> { |
972 | ADD_PROPERTY(String_Obj, media_type) |
973 | ADD_PROPERTY(bool, is_negated) |
974 | ADD_PROPERTY(bool, is_restricted) |
975 | public: |
976 | Media_Query(SourceSpan pstate, String_Obj t = {}, size_t s = 0, bool n = false, bool r = false); |
977 | ATTACH_AST_OPERATIONS(Media_Query) |
978 | ATTACH_CRTP_PERFORM_METHODS() |
979 | }; |
980 | |
981 | //////////////////////////////////////////////////// |
982 | // Media expressions (for use inside media queries). |
983 | // ToDo: only used for interpolation case |
984 | //////////////////////////////////////////////////// |
985 | class Media_Query_Expression final : public Expression { |
986 | ADD_PROPERTY(ExpressionObj, feature) |
987 | ADD_PROPERTY(ExpressionObj, value) |
988 | ADD_PROPERTY(bool, is_interpolated) |
989 | public: |
990 | Media_Query_Expression(SourceSpan pstate, ExpressionObj f, ExpressionObj v, bool i = false); |
991 | ATTACH_AST_OPERATIONS(Media_Query_Expression) |
992 | ATTACH_CRTP_PERFORM_METHODS() |
993 | }; |
994 | |
995 | ///////////////////////////////////////////////// |
996 | // At root expressions (for use inside @at-root). |
997 | ///////////////////////////////////////////////// |
998 | class At_Root_Query final : public Expression { |
999 | private: |
1000 | ADD_PROPERTY(ExpressionObj, feature) |
1001 | ADD_PROPERTY(ExpressionObj, value) |
1002 | public: |
1003 | At_Root_Query(SourceSpan pstate, ExpressionObj f = {}, ExpressionObj v = {}, bool i = false); |
1004 | bool exclude(sass::string str); |
1005 | ATTACH_AST_OPERATIONS(At_Root_Query) |
1006 | ATTACH_CRTP_PERFORM_METHODS() |
1007 | }; |
1008 | |
1009 | /////////// |
1010 | // At-root. |
1011 | /////////// |
1012 | class AtRootRule final : public ParentStatement { |
1013 | ADD_PROPERTY(At_Root_Query_Obj, expression) |
1014 | public: |
1015 | AtRootRule(SourceSpan pstate, Block_Obj b = {}, At_Root_Query_Obj e = {}); |
1016 | bool bubbles() override; |
1017 | bool exclude_node(Statement_Obj s); |
1018 | ATTACH_AST_OPERATIONS(AtRootRule) |
1019 | ATTACH_CRTP_PERFORM_METHODS() |
1020 | }; |
1021 | |
1022 | ///////////////////////////////////////////////////////// |
1023 | // Individual parameter objects for mixins and functions. |
1024 | ///////////////////////////////////////////////////////// |
1025 | class Parameter final : public AST_Node { |
1026 | ADD_CONSTREF(sass::string, name) |
1027 | ADD_PROPERTY(ExpressionObj, default_value) |
1028 | ADD_PROPERTY(bool, is_rest_parameter) |
1029 | public: |
1030 | Parameter(SourceSpan pstate, sass::string n, ExpressionObj def = {}, bool rest = false); |
1031 | ATTACH_AST_OPERATIONS(Parameter) |
1032 | ATTACH_CRTP_PERFORM_METHODS() |
1033 | }; |
1034 | |
1035 | ///////////////////////////////////////////////////////////////////////// |
1036 | // Parameter lists -- in their own class to facilitate context-sensitive |
1037 | // error checking (e.g., ensuring that all optional parameters follow all |
1038 | // required parameters). |
1039 | ///////////////////////////////////////////////////////////////////////// |
1040 | class Parameters final : public AST_Node, public Vectorized<Parameter_Obj> { |
1041 | ADD_PROPERTY(bool, has_optional_parameters) |
1042 | ADD_PROPERTY(bool, has_rest_parameter) |
1043 | protected: |
1044 | void adjust_after_pushing(Parameter_Obj p) override; |
1045 | public: |
1046 | Parameters(SourceSpan pstate); |
1047 | ATTACH_AST_OPERATIONS(Parameters) |
1048 | ATTACH_CRTP_PERFORM_METHODS() |
1049 | }; |
1050 | |
1051 | } |
1052 | |
1053 | #include "ast_values.hpp" |
1054 | #include "ast_supports.hpp" |
1055 | #include "ast_selectors.hpp" |
1056 | |
1057 | #ifdef __clang__ |
1058 | |
1059 | // #pragma clang diagnostic pop |
1060 | // #pragma clang diagnostic push |
1061 | |
1062 | #endif |
1063 | |
1064 | #endif |
1065 | |