From 7a29ca89e94108e45b9e1ad6e840deccb2282123 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 18 Mar 2018 03:43:12 +0100 Subject: [PATCH] Cleanup and fix selector compare operators --- src/ast.hpp | 10 +- src/ast_sel_cmp.cpp | 860 ++++++++++++++++++++++++++++++------------ src/ast_selectors.hpp | 124 ++++-- 3 files changed, 725 insertions(+), 269 deletions(-) diff --git a/src/ast.hpp b/src/ast.hpp index 5b376052a..697924c4b 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -148,12 +148,6 @@ namespace Sass { VARIABLE, NUM_TYPES }; - enum Simple_Type { - SIMPLE, - ATTR_SEL, - PSEUDO_SEL, - WRAPPED_SEL, - }; private: // expressions in some contexts shouldn't be evaluated ADD_PROPERTY(bool, is_delayed) @@ -183,7 +177,10 @@ namespace Sass { static std::string type_name() { return ""; } virtual bool is_false() { return false; } // virtual bool is_true() { return !is_false(); } + virtual bool operator< (const Expression& rhs) const { return false; } virtual bool operator== (const Expression& rhs) const { return false; } + inline bool operator>(const Expression& rhs) const { return rhs < *this; } + inline bool operator!=(const Expression& rhs) const { return !(rhs == *this); } virtual bool eq(const Expression& rhs) const { return *this == rhs; }; virtual void set_delayed(bool delayed) { is_delayed(delayed); } virtual bool has_interpolant() const { return is_interpolant(); } @@ -244,6 +241,7 @@ namespace Sass { T& operator[](size_t i) { return elements_[i]; } virtual const T& at(size_t i) const { return elements_.at(i); } virtual T& at(size_t i) { return elements_.at(i); } + const T& get(size_t i) const { return elements_[i]; } const T& operator[](size_t i) const { return elements_[i]; } virtual void append(T element) { diff --git a/src/ast_sel_cmp.cpp b/src/ast_sel_cmp.cpp index 4c3485a6c..f09b4178f 100644 --- a/src/ast_sel_cmp.cpp +++ b/src/ast_sel_cmp.cpp @@ -19,13 +19,171 @@ namespace Sass { + /*#########################################################################*/ + /*#########################################################################*/ + + bool Selector_List::operator== (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) { return *this == *sl; } + else if (auto ss = Cast(&rhs)) { return *this == *ss; } + else if (auto cpx = Cast(&rhs)) { return *this == *cpx; } + else if (auto cpd = Cast(&rhs)) { return *this == *cpd; } + else if (auto ls = Cast(&rhs)) { return *this == *ls; } + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Selector_List::operator< (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) { return *this < *sl; } + else if (auto ss = Cast(&rhs)) { return *this < *ss; } + else if (auto cpx = Cast(&rhs)) { return *this < *cpx; } + else if (auto cpd = Cast(&rhs)) { return *this < *cpd; } + else if (auto ls = Cast(&rhs)) { return *this < *ls; } + throw std::runtime_error("invalid selector base classes to compare"); + } + + // Selector lists can be compared to comma lists + bool Selector_List::operator== (const Expression& rhs) const + { + if (auto l = Cast(&rhs)) { return *this == *l; } + if (auto s = Cast(&rhs)) { return *this == *s; } + if (Cast(&rhs) || Cast(&rhs)) { return false; } + throw std::runtime_error("invalid selector base classes to compare"); + } + + // Selector lists can be compared to comma lists + bool Selector_List::operator< (const Expression& rhs) const + { + if (auto l = Cast(&rhs)) { return *this < *l; } + if (auto s = Cast(&rhs)) { return *this < *s; } + if (Cast(&rhs) || Cast(&rhs)) { return true; } + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Complex_Selector::operator== (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this == *sl; + if (auto ss = Cast(&rhs)) return *this == *ss; + if (auto cs = Cast(&rhs)) return *this == *cs; + if (auto ch = Cast(&rhs)) return *this == *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Complex_Selector::operator< (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this < *sl; + if (auto ss = Cast(&rhs)) return *this < *ss; + if (auto cs = Cast(&rhs)) return *this < *cs; + if (auto ch = Cast(&rhs)) return *this < *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Compound_Selector::operator== (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this == *sl; + if (auto ss = Cast(&rhs)) return *this == *ss; + if (auto cs = Cast(&rhs)) return *this == *cs; + if (auto ch = Cast(&rhs)) return *this == *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Compound_Selector::operator< (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this < *sl; + if (auto ss = Cast(&rhs)) return *this < *ss; + if (auto cs = Cast(&rhs)) return *this < *cs; + if (auto ch = Cast(&rhs)) return *this < *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Selector_Schema::operator== (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this == *sl; + if (auto ss = Cast(&rhs)) return *this == *ss; + if (auto cs = Cast(&rhs)) return *this == *cs; + if (auto ch = Cast(&rhs)) return *this == *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Selector_Schema::operator< (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this < *sl; + if (auto ss = Cast(&rhs)) return *this < *ss; + if (auto cs = Cast(&rhs)) return *this < *cs; + if (auto ch = Cast(&rhs)) return *this < *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Simple_Selector::operator== (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this == *sl; + if (auto ss = Cast(&rhs)) return *this == *ss; + if (auto cs = Cast(&rhs)) return *this == *cs; + if (auto ch = Cast(&rhs)) return *this == *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + bool Simple_Selector::operator< (const Selector& rhs) const + { + if (auto sl = Cast(&rhs)) return *this < *sl; + if (auto ss = Cast(&rhs)) return *this < *ss; + if (auto cs = Cast(&rhs)) return *this < *cs; + if (auto ch = Cast(&rhs)) return *this < *ch; + throw std::runtime_error("invalid selector base classes to compare"); + } + + /*#########################################################################*/ + /*#########################################################################*/ + + bool Selector_List::operator< (const Selector_List& rhs) const + { + size_t l = rhs.length(); + if (length() < l) l = length(); + for (size_t i = 0; i < l; i ++) { + if (*at(i) < *rhs.at(i)) return true; + } + return false; + } + + bool Selector_List::operator== (const Selector_List& rhs) const + { + // for array access + size_t i = 0, n = 0; + size_t iL = length(); + size_t nL = rhs.length(); + // create temporary vectors to sort them for compare + std::vector l_lst = this->elements(); + std::vector r_lst = rhs.elements(); + std::sort(l_lst.begin(), l_lst.end(), OrderNodes()); + std::sort(r_lst.begin(), r_lst.end(), OrderNodes()); + // process loop + while (true) + { + // first check for valid index + if (i == iL) return iL == nL; + else if (n == nL) return iL == nL; + // access the vector items + Complex_Selector_Ptr l = l_lst[i]; + Complex_Selector_Ptr r = r_lst[n]; + // skip nulls + if (!l) ++i; + else if (!r) ++n; + // do the check + else if (*l != *r) break; + // advance + ++i; ++n; + } + // not equal + return false; + } + bool Compound_Selector::operator< (const Compound_Selector& rhs) const { size_t L = std::min(length(), rhs.length()); for (size_t i = 0; i < L; ++i) { - Simple_Selector_Obj l = (*this)[i]; - Simple_Selector_Obj r = rhs[i]; + Simple_Selector_Ptr l = (*this)[i]; + Simple_Selector_Ptr r = rhs[i]; if (!l && !r) return false; else if (!r) return false; else if (!l) return true; @@ -133,24 +291,6 @@ namespace Sass { // process all tails while (true) { - #ifdef DEBUG - // skip empty ancestor first - if (l && l->is_empty_ancestor()) - { - l_h = NULL; - l = l->tail(); - if (l) l_h = l->head(); - continue; - } - // skip empty ancestor first - if (r && r->is_empty_ancestor()) - { - r_h = NULL; - r = r->tail(); - if (r) r_h = r->head(); - continue; - } - #endif // check the pointers if (!r) return !l; if (!l) return !r; @@ -192,190 +332,325 @@ namespace Sass { return false; } - bool Complex_Selector::operator== (const Selector& rhs) const + /*#########################################################################*/ + /*#########################################################################*/ + + bool Selector_List::operator== (const Complex_Selector& rhs) const { - if (const Selector_List* sl = Cast(&rhs)) return *this == *sl; - if (const Simple_Selector* sp = Cast(&rhs)) return *this == *sp; - if (const Complex_Selector* cs = Cast(&rhs)) return *this == *cs; - if (const Compound_Selector* ch = Cast(&rhs)) return *this == *ch; - throw std::runtime_error("invalid selector base classes to compare"); + size_t len = length(); + if (len > 1) return false; + if (len == 0) return rhs.empty(); + return *at(0) == rhs; } + bool Selector_List::operator< (const Complex_Selector& rhs) const + { + size_t len = length(); + if (len > 1) return false; + if (len == 0) return !rhs.empty(); + return *at(0) < rhs; + } - bool Complex_Selector::operator< (const Selector& rhs) const + bool Selector_List::operator== (const Compound_Selector& rhs) const { - if (const Selector_List* sl = Cast(&rhs)) return *this < *sl; - if (const Simple_Selector* sp = Cast(&rhs)) return *this < *sp; - if (const Complex_Selector* cs = Cast(&rhs)) return *this < *cs; - if (const Compound_Selector* ch = Cast(&rhs)) return *this < *ch; - throw std::runtime_error("invalid selector base classes to compare"); + size_t len = length(); + if (len > 1) return false; + if (len == 0) return rhs.empty(); + return *at(0) == rhs; } - bool Compound_Selector::operator== (const Selector& rhs) const + bool Selector_List::operator< (const Compound_Selector& rhs) const { - if (const Selector_List* sl = Cast(&rhs)) return *this == *sl; - if (const Simple_Selector* sp = Cast(&rhs)) return *this == *sp; - if (const Complex_Selector* cs = Cast(&rhs)) return *this == *cs; - if (const Compound_Selector* ch = Cast(&rhs)) return *this == *ch; - throw std::runtime_error("invalid selector base classes to compare"); + size_t len = length(); + if (len > 1) return false; + if (len == 0) return !rhs.empty(); + return *at(0) < rhs; } - bool Compound_Selector::operator< (const Selector& rhs) const + bool Selector_List::operator== (const Simple_Selector& rhs) const { - if (const Selector_List* sl = Cast(&rhs)) return *this < *sl; - if (const Simple_Selector* sp = Cast(&rhs)) return *this < *sp; - if (const Complex_Selector* cs = Cast(&rhs)) return *this < *cs; - if (const Compound_Selector* ch = Cast(&rhs)) return *this < *ch; - throw std::runtime_error("invalid selector base classes to compare"); + size_t len = length(); + if (len > 1) return false; + if (len == 0) return rhs.empty(); + return *at(0) == rhs; } - bool Selector_Schema::operator== (const Selector& rhs) const + bool Selector_List::operator< (const Simple_Selector& rhs) const { - if (const Selector_List* sl = Cast(&rhs)) return *this == *sl; - if (const Simple_Selector* sp = Cast(&rhs)) return *this == *sp; - if (const Complex_Selector* cs = Cast(&rhs)) return *this == *cs; - if (const Compound_Selector* ch = Cast(&rhs)) return *this == *ch; - throw std::runtime_error("invalid selector base classes to compare"); + size_t len = length(); + if (len > 1) return false; + if (len == 0) return !rhs.empty(); + return *at(0) < rhs; } - bool Selector_Schema::operator< (const Selector& rhs) const + /***************************************************************************/ + /***************************************************************************/ + + bool Complex_Selector::operator== (const Selector_List& rhs) const { - if (const Selector_List* sl = Cast(&rhs)) return *this < *sl; - if (const Simple_Selector* sp = Cast(&rhs)) return *this < *sp; - if (const Complex_Selector* cs = Cast(&rhs)) return *this < *cs; - if (const Compound_Selector* ch = Cast(&rhs)) return *this < *ch; - throw std::runtime_error("invalid selector base classes to compare"); + size_t len = rhs.length(); + if (len > 1) return false; + if (len == 0) return empty(); + return *this == *rhs.at(0); } - bool Simple_Selector::operator== (const Selector& rhs) const + bool Complex_Selector::operator< (const Selector_List& rhs) const { - if (Simple_Selector_Ptr_Const sp = Cast(&rhs)) return *this == *sp; - return false; + size_t len = rhs.length(); + if (len > 1) return true; + if (len == 0) return false; + return *this < *rhs.at(0); } - bool Simple_Selector::operator< (const Selector& rhs) const + bool Complex_Selector::operator== (const Compound_Selector& rhs) const { - if (Simple_Selector_Ptr_Const sp = Cast(&rhs)) return *this < *sp; - return false; + if (tail()) return false; + if (!head()) return rhs.empty(); + return *head() == rhs; } - bool Simple_Selector::operator== (const Simple_Selector& rhs) const + bool Complex_Selector::operator< (const Compound_Selector& rhs) const { - // solve the double dispatch problem by using RTTI information via dynamic cast - if (const Pseudo_Selector* lhs = Cast(this)) {return *lhs == rhs; } - else if (const Wrapped_Selector* lhs = Cast(this)) {return *lhs == rhs; } - else if (const Element_Selector* lhs = Cast(this)) {return *lhs == rhs; } - else if (const Attribute_Selector* lhs = Cast(this)) {return *lhs == rhs; } - else if (name_ == rhs.name_) - { return is_ns_eq(rhs); } - else return false; + if (tail()) return false; + if (!head()) return !rhs.empty(); + return *head() < rhs; } - bool Simple_Selector::operator< (const Simple_Selector& rhs) const + bool Complex_Selector::operator== (const Simple_Selector& rhs) const { - // solve the double dispatch problem by using RTTI information via dynamic cast - if (const Pseudo_Selector* lhs = Cast(this)) {return *lhs < rhs; } - else if (const Wrapped_Selector* lhs = Cast(this)) {return *lhs < rhs; } - else if (const Element_Selector* lhs = Cast(this)) {return *lhs < rhs; } - else if (const Attribute_Selector* lhs = Cast(this)) {return *lhs < rhs; } - if (is_ns_eq(rhs)) - { return name_ < rhs.name_; } - return ns_ < rhs.ns_; + if (tail()) return false; + if (!head()) return rhs.empty(); + return *head() == rhs; } - bool Selector_List::operator== (const Selector& rhs) const + bool Complex_Selector::operator< (const Simple_Selector& rhs) const { - // solve the double dispatch problem by using RTTI information via dynamic cast - if (Selector_List_Ptr_Const sl = Cast(&rhs)) { return *this == *sl; } - else if (Complex_Selector_Ptr_Const cpx = Cast(&rhs)) { return *this == *cpx; } - else if (Compound_Selector_Ptr_Const cpd = Cast(&rhs)) { return *this == *cpd; } - // no compare method - return this == &rhs; + if (tail()) return false; + if (!head()) return !rhs.empty(); + return *head() < rhs; } - // Selector lists can be compared to comma lists - bool Selector_List::operator== (const Expression& rhs) const + /***************************************************************************/ + /***************************************************************************/ + + bool Compound_Selector::operator== (const Selector_List& rhs) const { - // solve the double dispatch problem by using RTTI information via dynamic cast - if (List_Ptr_Const ls = Cast(&rhs)) { return *ls == *this; } - if (Selector_Ptr_Const ls = Cast(&rhs)) { return *this == *ls; } - // compare invalid (maybe we should error?) - return false; + size_t len = rhs.length(); + if (len > 1) return false; + if (len == 0) return empty(); + return *this == *rhs.at(0); } - bool Selector_List::operator== (const Selector_List& rhs) const + bool Compound_Selector::operator< (const Selector_List& rhs) const { - // for array access - size_t i = 0, n = 0; - size_t iL = length(); - size_t nL = rhs.length(); - // create temporary vectors and sort them - std::vector l_lst = this->elements(); - std::vector r_lst = rhs.elements(); - std::sort(l_lst.begin(), l_lst.end(), OrderNodes()); - std::sort(r_lst.begin(), r_lst.end(), OrderNodes()); - // process loop - while (true) - { - // first check for valid index - if (i == iL) return iL == nL; - else if (n == nL) return iL == nL; - // the access the vector items - Complex_Selector_Obj l = l_lst[i]; - Complex_Selector_Obj r = r_lst[n]; - // skip nulls - if (!l) ++i; - else if (!r) ++n; - // do the check - else if (*l != *r) - { return false; } - // advance - ++i; ++n; - } - // there is no break?! + size_t len = rhs.length(); + if (len > 1) return true; + if (len == 0) return false; + return *this < *rhs.at(0); } - bool Selector_List::operator< (const Selector& rhs) const + bool Compound_Selector::operator== (const Complex_Selector& rhs) const { - if (Selector_List_Ptr_Const sp = Cast(&rhs)) return *this < *sp; - return false; + if (rhs.tail()) return false; + if (!rhs.head()) return empty(); + return *this == *rhs.head(); } - bool Selector_List::operator< (const Selector_List& rhs) const + bool Compound_Selector::operator< (const Complex_Selector& rhs) const { - size_t l = rhs.length(); - if (length() < l) l = length(); - for (size_t i = 0; i < l; i ++) { - if (*at(i) < *rhs.at(i)) return true; + if (rhs.tail()) return true; + if (!rhs.head()) return false; + return *this < *rhs.head(); + } + + bool Compound_Selector::operator< (const Simple_Selector& rhs) const + { + size_t len = length(); + if (len > 1) return false; + if (len == 0) return rhs.empty(); + return *at(0) == rhs; + } + + bool Compound_Selector::operator== (const Simple_Selector& rhs) const + { + size_t len = length(); + if (len > 1) return false; + if (len == 0) return !rhs.empty(); + return *at(0) < rhs; + } + + /***************************************************************************/ + /***************************************************************************/ + + bool Simple_Selector::operator== (const Selector_List& rhs) const + { + size_t len = rhs.length(); + if (len > 1) return false; + if (len == 0) return empty(); + return *this == *rhs.at(0); + } + + bool Simple_Selector::operator< (const Selector_List& rhs) const + { + size_t len = rhs.length(); + if (len > 1) return true; + if (len == 0) return false; + return *this < *rhs.at(0); + } + + bool Simple_Selector::operator== (const Complex_Selector& rhs) const + { + if (rhs.tail()) return false; + if (!rhs.head()) return empty(); + return *this == *rhs.head(); + } + + bool Simple_Selector::operator< (const Complex_Selector& rhs) const + { + if (rhs.tail()) return true; + if (!rhs.head()) return false; + return *this < *rhs.head(); + } + + bool Simple_Selector::operator== (const Compound_Selector& rhs) const + { + size_t len = rhs.length(); + if (len > 1) return false; + if (len == 0) return empty(); + return *this == *rhs.at(0); + } + + bool Simple_Selector::operator< (const Compound_Selector& rhs) const + { + size_t len = rhs.length(); + if (len > 1) return true; + if (len == 0) return false; + return *this < *rhs.at(0); + } + + /*#########################################################################*/ + /*#########################################################################*/ + + bool Simple_Selector::operator== (const Simple_Selector& rhs) const + { + switch (simple_type()) { + case ID_SEL: return (const Id_Selector&) *this == rhs; break; + case TYPE_SEL: return (const Element_Selector&) *this == rhs; break; + case CLASS_SEL: return (const Class_Selector&) *this == rhs; break; + case PARENT_SEL: return (const Parent_Selector&) *this == rhs; break; + case PSEUDO_SEL: return (const Pseudo_Selector&) *this == rhs; break; + case WRAPPED_SEL: return (const Wrapped_Selector&) *this == rhs; break; + case ATTRIBUTE_SEL: return (const Attribute_Selector&) *this == rhs; break; + case PLACEHOLDER_SEL: return (const Placeholder_Selector&) *this == rhs; break; } return false; } - bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const + /***************************************************************************/ + /***************************************************************************/ + + bool Id_Selector::operator== (const Simple_Selector& rhs) const { - if (is_ns_eq(rhs)) { - if (name() == rhs.name()) { - if (matcher() == rhs.matcher()) { - bool no_lhs_val = value().isNull(); - bool no_rhs_val = rhs.value().isNull(); - if (no_lhs_val && no_rhs_val) return false; // equal - else if (no_lhs_val) return true; // lhs is null - else if (no_rhs_val) return false; // rhs is null - return *value() < *rhs.value(); // both are given - } else { return matcher() < rhs.matcher(); } - } else { return name() < rhs.name(); } - } else { return ns() < rhs.ns(); } + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; } - bool Attribute_Selector::operator< (const Simple_Selector& rhs) const + bool Element_Selector::operator== (const Simple_Selector& rhs) const { - if (Attribute_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this < *w; + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; + } + + bool Class_Selector::operator== (const Simple_Selector& rhs) const + { + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; + } + + bool Parent_Selector::operator== (const Simple_Selector& rhs) const + { + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; + } + + bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const + { + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; + } + + bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const + { + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; + } + + bool Attribute_Selector::operator== (const Simple_Selector& rhs) const + { + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; + } + + bool Placeholder_Selector::operator== (const Simple_Selector& rhs) const + { + auto sel = Cast(&rhs); + return sel ? *this == *sel : false; + } + + /***************************************************************************/ + /***************************************************************************/ + + bool Id_Selector::operator== (const Id_Selector& rhs) const + { + // ID has no namespacing + return name() == rhs.name(); + } + + bool Element_Selector::operator== (const Element_Selector& rhs) const + { + return is_ns_eq(rhs) && name() == rhs.name(); + } + + bool Class_Selector::operator== (const Class_Selector& rhs) const + { + // Class has no namespacing + return name() == rhs.name(); + } + + bool Parent_Selector::operator== (const Parent_Selector& rhs) const + { + // Parent has no namespacing + return name() == rhs.name(); + } + + bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const + { + std::string lname = name(); + std::string rname = rhs.name(); + if (is_pseudo_class_element(lname)) { + if (rname[0] == ':' && rname[1] == ':') { + lname = lname.substr(1, std::string::npos); + } } - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); + // right hand is special pseudo (single colon) + if (is_pseudo_class_element(rname)) { + if (lname[0] == ':' && lname[1] == ':') { + lname = lname.substr(1, std::string::npos); + } + } + // Pseudo has no namespacing + if (lname != rname) return false; + String_Obj lhs_ex = expression(); + String_Obj rhs_ex = rhs.expression(); + if (rhs_ex && lhs_ex) return *lhs_ex == *rhs_ex; + else return lhs_ex.ptr() == rhs_ex.ptr(); + } + + bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const + { + // Wrapped has no namespacing + if (name() != rhs.name()) return false; + return *(selector()) == *(rhs.selector()); } bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const @@ -398,137 +673,260 @@ namespace Sass { } // not equal return false; + } + bool Placeholder_Selector::operator== (const Placeholder_Selector& rhs) const + { + // Placeholder has no namespacing + return name() == rhs.name(); } - bool Attribute_Selector::operator== (const Simple_Selector& rhs) const + /*#########################################################################*/ + /*#########################################################################*/ + + bool Simple_Selector::operator< (const Simple_Selector& rhs) const { - if (Attribute_Selector_Ptr_Const w = Cast(&rhs)) - { - return is_ns_eq(rhs) && - name() == rhs.name() && - *this == *w; + switch (simple_type()) { + case ID_SEL: return (const Id_Selector&) *this < rhs; break; + case TYPE_SEL: return (const Element_Selector&) *this < rhs; break; + case CLASS_SEL: return (const Class_Selector&) *this < rhs; break; + case PARENT_SEL: return (const Parent_Selector&) *this < rhs; break; + case PSEUDO_SEL: return (const Pseudo_Selector&) *this < rhs; break; + case WRAPPED_SEL: return (const Wrapped_Selector&) *this < rhs; break; + case ATTRIBUTE_SEL: return (const Attribute_Selector&) *this < rhs; break; + case PLACEHOLDER_SEL: return (const Placeholder_Selector&) *this < rhs; break; } return false; } - bool Element_Selector::operator< (const Element_Selector& rhs) const + /***************************************************************************/ + /***************************************************************************/ + + bool Id_Selector::operator< (const Simple_Selector& rhs) const { - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); + switch (rhs.simple_type()) { + case TYPE_SEL: return '#' < 's'; break; + case CLASS_SEL: return '#' < '.'; break; + case PARENT_SEL: return '#' < '&'; break; + case PSEUDO_SEL: return '#' < ':'; break; + case WRAPPED_SEL: return '#' < '('; break; + case ATTRIBUTE_SEL: return '#' < '['; break; + case PLACEHOLDER_SEL: return '#' < '%'; break; + case ID_SEL: /* let if fall through */ break; + } + const Id_Selector& sel = + (const Id_Selector&) rhs; + return *this < sel; } bool Element_Selector::operator< (const Simple_Selector& rhs) const { - if (Element_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this < *w; + switch (rhs.simple_type()) { + case ID_SEL: return 'e' < '#'; break; + case CLASS_SEL: return 'e' < '.'; break; + case PARENT_SEL: return 'e' < '&'; break; + case PSEUDO_SEL: return 'e' < ':'; break; + case WRAPPED_SEL: return 'e' < '('; break; + case ATTRIBUTE_SEL: return 'e' < '['; break; + case PLACEHOLDER_SEL: return 'e' < '%'; break; + case TYPE_SEL: /* let if fall through */ break; } - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); + const Element_Selector& sel = + (const Element_Selector&) rhs; + return *this < sel; + } + + bool Class_Selector::operator< (const Simple_Selector& rhs) const + { + switch (rhs.simple_type()) { + case ID_SEL: return '.' < '#'; break; + case TYPE_SEL: return '.' < 's'; break; + case PARENT_SEL: return '.' < '&'; break; + case PSEUDO_SEL: return '.' < ':'; break; + case WRAPPED_SEL: return '.' < '('; break; + case ATTRIBUTE_SEL: return '.' < '['; break; + case PLACEHOLDER_SEL: return '.' < '%'; break; + case CLASS_SEL: /* let if fall through */ break; + } + const Class_Selector& sel = + (const Class_Selector&) rhs; + return *this < sel; } - bool Element_Selector::operator== (const Element_Selector& rhs) const + bool Pseudo_Selector::operator< (const Simple_Selector& rhs) const { - return is_ns_eq(rhs) && - name() == rhs.name(); + switch (rhs.simple_type()) { + case ID_SEL: return ':' < '#'; break; + case TYPE_SEL: return ':' < 's'; break; + case CLASS_SEL: return ':' < '.'; break; + case PARENT_SEL: return ':' < '&'; break; + case WRAPPED_SEL: return ':' < '('; break; + case ATTRIBUTE_SEL: return ':' < '['; break; + case PLACEHOLDER_SEL: return ':' < '%'; break; + case PSEUDO_SEL: /* let if fall through */ break; + } + const Pseudo_Selector& sel = + (const Pseudo_Selector&) rhs; + return *this < sel; } - bool Element_Selector::operator== (const Simple_Selector& rhs) const + bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const { - if (Element_Selector_Ptr_Const w = Cast(&rhs)) - { - return is_ns_eq(rhs) && - name() == rhs.name() && - *this == *w; + switch (rhs.simple_type()) { + case ID_SEL: return '(' < '#'; break; + case TYPE_SEL: return '(' < 's'; break; + case CLASS_SEL: return '(' < '.'; break; + case PARENT_SEL: return '(' < '&'; break; + case PSEUDO_SEL: return '(' < ':'; break; + case ATTRIBUTE_SEL: return '(' < '['; break; + case PLACEHOLDER_SEL: return '(' < '%'; break; + case WRAPPED_SEL: /* let if fall through */ break; } - return false; + const Wrapped_Selector& sel = + (const Wrapped_Selector&) rhs; + return *this < sel; + } + + bool Parent_Selector::operator< (const Simple_Selector& rhs) const + { + switch (rhs.simple_type()) { + case ID_SEL: return '&' < '#'; break; + case TYPE_SEL: return '&' < 's'; break; + case CLASS_SEL: return '&' < '.'; break; + case PSEUDO_SEL: return '&' < ':'; break; + case WRAPPED_SEL: return '&' < '('; break; + case ATTRIBUTE_SEL: return '&' < '['; break; + case PLACEHOLDER_SEL: return '&' < '%'; break; + case PARENT_SEL: /* let if fall through */ break; + } + const Parent_Selector& sel = + (const Parent_Selector&) rhs; + return *this < sel; } - bool Pseudo_Selector::operator== (const Pseudo_Selector& rhs) const + bool Attribute_Selector::operator< (const Simple_Selector& rhs) const { - if (is_ns_eq(rhs) && name() == rhs.name()) - { - String_Obj lhs_ex = expression(); - String_Obj rhs_ex = rhs.expression(); - if (rhs_ex && lhs_ex) return *lhs_ex == *rhs_ex; - else return lhs_ex.ptr() == rhs_ex.ptr(); + switch (rhs.simple_type()) { + case ID_SEL: return '[' < '#'; break; + case TYPE_SEL: return '[' < 'e'; break; + case CLASS_SEL: return '[' < '.'; break; + case PARENT_SEL: return '[' < '&'; break; + case PSEUDO_SEL: return '[' < ':'; break; + case WRAPPED_SEL: return '[' < '('; break; + case PLACEHOLDER_SEL: return '[' < '%'; break; + case ATTRIBUTE_SEL: /* let if fall through */ break; + } + const Attribute_Selector& sel = + (const Attribute_Selector&) rhs; + return *this < sel; + } + + bool Placeholder_Selector::operator< (const Simple_Selector& rhs) const + { + switch (rhs.simple_type()) { + case ID_SEL: return '%' < '#'; break; + case TYPE_SEL: return '%' < 's'; break; + case CLASS_SEL: return '%' < '.'; break; + case PARENT_SEL: return '%' < '&'; break; + case PSEUDO_SEL: return '%' < ':'; break; + case WRAPPED_SEL: return '%' < '('; break; + case ATTRIBUTE_SEL: return '%' < '['; break; + case PLACEHOLDER_SEL: /* let if fall through */ break; } - else return false; + const Placeholder_Selector& sel = + (const Placeholder_Selector&) rhs; + return *this < sel; } - bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const + /***************************************************************************/ + /***************************************************************************/ + + bool Id_Selector::operator< (const Id_Selector& rhs) const { - if (Pseudo_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this == *w; - } - return is_ns_eq(rhs) && - name() == rhs.name(); + // ID has no namespacing + return name() < rhs.name(); } - bool Pseudo_Selector::operator< (const Pseudo_Selector& rhs) const + bool Element_Selector::operator< (const Element_Selector& rhs) const { - if (is_ns_eq(rhs) && name() == rhs.name()) - { - String_Obj lhs_ex = expression(); - String_Obj rhs_ex = rhs.expression(); - if (rhs_ex && lhs_ex) return *lhs_ex < *rhs_ex; - else return lhs_ex.ptr() < rhs_ex.ptr(); - } if (is_ns_eq(rhs)) - { return name() < rhs.name(); } + { + if (rhs.has_ns_ && has_ns_) + return name() < rhs.name(); + if (!rhs.has_ns_ && !has_ns_) + return name() < rhs.name(); + return true; + } return ns() < rhs.ns(); } - bool Pseudo_Selector::operator< (const Simple_Selector& rhs) const + bool Class_Selector::operator< (const Class_Selector& rhs) const { - if (Pseudo_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this < *w; - } - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); + // Class has no namespacing + return name() < rhs.name(); } - bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const + bool Parent_Selector::operator< (const Parent_Selector& rhs) const { - if (is_ns_eq(rhs) && name() == rhs.name()) - { return *(selector()) == *(rhs.selector()); } - else return false; + // Parent has no namespacing + return name() < rhs.name(); } - bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const + bool Pseudo_Selector::operator< (const Pseudo_Selector& rhs) const { - if (Wrapped_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this == *w; + std::string lname = name(); + std::string rname = rhs.name(); + if (is_pseudo_class_element(lname)) { + if (rname[0] == ':' && rname[1] == ':') { + lname = lname.substr(1, std::string::npos); + } + } + // right hand is special pseudo (single colon) + if (is_pseudo_class_element(rname)) { + if (lname[0] == ':' && lname[1] == ':') { + lname = lname.substr(1, std::string::npos); + } } - return is_ns_eq(rhs) && - name() == rhs.name(); + // Peudo has no namespacing + if (lname != rname) + { return lname < rname; } + String_Obj lhs_ex = expression(); + String_Obj rhs_ex = rhs.expression(); + if (rhs_ex && lhs_ex) return *lhs_ex < *rhs_ex; + else return lhs_ex.ptr() < rhs_ex.ptr(); } bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const { - if (is_ns_eq(rhs) && name() == rhs.name()) - { return *(selector()) < *(rhs.selector()); } - if (is_ns_eq(rhs)) + // Wrapped has no namespacing + if (name() != rhs.name()) { return name() < rhs.name(); } - return ns() < rhs.ns(); + return *(selector()) < *(rhs.selector()); } - bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const + bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const { - if (Wrapped_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this < *w; - } - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); + if (is_ns_eq(rhs)) { + if (name() != rhs.name()) + { return name() < rhs.name(); } + if (matcher() != rhs.matcher()) + { return matcher() < rhs.matcher(); } + bool no_lhs_val = value().isNull(); + bool no_rhs_val = rhs.value().isNull(); + if (no_lhs_val && no_rhs_val) return false; // equal + else if (no_lhs_val) return true; // lhs is null + else if (no_rhs_val) return false; // rhs is null + return *value() < *rhs.value(); // both are given + } else { return ns() < rhs.ns(); } } + bool Placeholder_Selector::operator< (const Placeholder_Selector& rhs) const + { + // Placeholder has no namespacing + return name() < rhs.name(); + } + + /*#########################################################################*/ + /*#########################################################################*/ + } \ No newline at end of file diff --git a/src/ast_selectors.hpp b/src/ast_selectors.hpp index 4e993035c..9e520c3b9 100644 --- a/src/ast_selectors.hpp +++ b/src/ast_selectors.hpp @@ -139,6 +139,18 @@ namespace Sass { // Abstract base class for simple selectors. //////////////////////////////////////////// class Simple_Selector : public Selector { + public: + enum Simple_Type { + ID_SEL, + TYPE_SEL, + CLASS_SEL, + PSEUDO_SEL, + PARENT_SEL, + WRAPPED_SEL, + ATTRIBUTE_SEL, + PLACEHOLDER_SEL, + }; + public: ADD_CONSTREF(std::string, ns) ADD_CONSTREF(std::string, name) ADD_PROPERTY(Simple_Type, simple_type) @@ -147,7 +159,6 @@ namespace Sass { Simple_Selector(ParserState pstate, std::string n = "") : Selector(pstate), ns_(""), name_(n), has_ns_(false) { - simple_type(SIMPLE); size_t pos = n.find('|'); // found some namespace if (pos != std::string::npos) { @@ -161,7 +172,7 @@ namespace Sass { ns_(ptr->ns_), name_(ptr->name_), has_ns_(ptr->has_ns_) - { simple_type(SIMPLE); } + { } std::string ns_name() const { std::string name(""); @@ -179,6 +190,9 @@ namespace Sass { } return hash_; } + bool empty() const { + return ns().empty() && name().empty(); + } // namespace compare functions bool is_ns_eq(const Simple_Selector& r) const; // namespace query functions @@ -218,15 +232,20 @@ namespace Sass { virtual bool is_superselector_of(Compound_Selector_Ptr_Const sub) const { return false; } + bool operator<(const Selector& rhs) const final override; bool operator==(const Selector& rhs) const final override; + virtual bool operator<(const Selector_List& rhs) const; + virtual bool operator==(const Selector_List& rhs) const; + virtual bool operator<(const Complex_Selector& rhs) const; + virtual bool operator==(const Complex_Selector& rhs) const; + virtual bool operator<(const Compound_Selector& rhs) const; + virtual bool operator==(const Compound_Selector& rhs) const; + virtual bool operator<(const Simple_Selector& rhs) const; virtual bool operator==(const Simple_Selector& rhs) const; - inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); } - bool operator<(const Selector& rhs) const final override; - virtual bool operator<(const Simple_Selector& rhs) const; - // default implementation should work for most of the simple selectors (otherwise overload) ATTACH_VIRTUAL_AST_OPERATIONS(Simple_Selector); ATTACH_CRTP_PERFORM_METHODS(); + }; inline Simple_Selector::~Simple_Selector() { } @@ -242,10 +261,10 @@ namespace Sass { public: Parent_Selector(ParserState pstate, bool r = true) : Simple_Selector(pstate, "&"), real_(r) - { /* has_reference(true); */ } + { simple_type(PARENT_SEL); } Parent_Selector(const Parent_Selector* ptr) : Simple_Selector(ptr), real_(ptr->real_) - { /* has_reference(true); */ } + { simple_type(PARENT_SEL); } bool is_real_parent_ref() const { return real(); }; bool has_parent_ref() const override { return true; }; bool has_real_parent_ref() const override { return is_real_parent_ref(); }; @@ -259,6 +278,10 @@ namespace Sass { } std::string type() const override { return "selector"; } static std::string type_name() { return "selector"; } + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; + bool operator<(const Parent_Selector& rhs) const; + bool operator==(const Parent_Selector& rhs) const; ATTACH_AST_OPERATIONS(Parent_Selector) ATTACH_CRTP_PERFORM_METHODS() }; @@ -271,10 +294,10 @@ namespace Sass { public: Placeholder_Selector(ParserState pstate, std::string n) : Simple_Selector(pstate, n) - { } + { simple_type(PLACEHOLDER_SEL); } Placeholder_Selector(const Placeholder_Selector* ptr) : Simple_Selector(ptr) - { } + { simple_type(PLACEHOLDER_SEL); } unsigned long specificity() const override { return Constants::Specificity_Base; @@ -287,6 +310,10 @@ namespace Sass { return true; } virtual ~Placeholder_Selector() {}; + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; + bool operator<(const Placeholder_Selector& rhs) const; + bool operator==(const Placeholder_Selector& rhs) const; ATTACH_AST_OPERATIONS(Placeholder_Selector) ATTACH_CRTP_PERFORM_METHODS() }; @@ -298,10 +325,10 @@ namespace Sass { public: Element_Selector(ParserState pstate, std::string n) : Simple_Selector(pstate, n) - { } + { simple_type(TYPE_SEL); } Element_Selector(const Element_Selector* ptr) : Simple_Selector(ptr) - { } + { simple_type(TYPE_SEL); } unsigned long specificity() const override { if (name() == "*") return 0; @@ -313,10 +340,10 @@ namespace Sass { } Simple_Selector_Ptr unify_with(Simple_Selector_Ptr); Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; - bool operator==(const Simple_Selector& rhs) const override; - bool operator==(const Element_Selector& rhs) const; - bool operator<(const Simple_Selector& rhs) const override; + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; bool operator<(const Element_Selector& rhs) const; + bool operator==(const Element_Selector& rhs) const; ATTACH_AST_OPERATIONS(Element_Selector) ATTACH_CRTP_PERFORM_METHODS() }; @@ -328,10 +355,10 @@ namespace Sass { public: Class_Selector(ParserState pstate, std::string n) : Simple_Selector(pstate, n) - { } + { simple_type(CLASS_SEL); } Class_Selector(const Class_Selector* ptr) : Simple_Selector(ptr) - { } + { simple_type(CLASS_SEL); } unsigned long specificity() const override { return Constants::Specificity_Class; @@ -341,6 +368,10 @@ namespace Sass { return Constants::UnificationOrder_Class; } Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; + bool operator<(const Class_Selector& rhs) const; + bool operator==(const Class_Selector& rhs) const; ATTACH_AST_OPERATIONS(Class_Selector) ATTACH_CRTP_PERFORM_METHODS() }; @@ -352,10 +383,10 @@ namespace Sass { public: Id_Selector(ParserState pstate, std::string n) : Simple_Selector(pstate, n) - { } + { simple_type(ID_SEL); } Id_Selector(const Id_Selector* ptr) : Simple_Selector(ptr) - { } + { simple_type(ID_SEL); } unsigned long specificity() const override { return Constants::Specificity_ID; @@ -365,6 +396,10 @@ namespace Sass { return Constants::UnificationOrder_Id; } Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; + bool operator<(const Id_Selector& rhs) const; + bool operator==(const Id_Selector& rhs) const; ATTACH_AST_OPERATIONS(Id_Selector) ATTACH_CRTP_PERFORM_METHODS() }; @@ -380,13 +415,13 @@ namespace Sass { public: Attribute_Selector(ParserState pstate, std::string n, std::string m, String_Obj v, char o = 0) : Simple_Selector(pstate, n), matcher_(m), value_(v), modifier_(o) - { simple_type(ATTR_SEL); } + { simple_type(ATTRIBUTE_SEL); } Attribute_Selector(const Attribute_Selector* ptr) : Simple_Selector(ptr), matcher_(ptr->matcher_), value_(ptr->value_), modifier_(ptr->modifier_) - { simple_type(ATTR_SEL); } + { simple_type(ATTRIBUTE_SEL); } size_t hash() const override { if (hash_ == 0) { @@ -404,10 +439,10 @@ namespace Sass { { return Constants::UnificationOrder_Attribute; } - bool operator==(const Simple_Selector& rhs) const override; - bool operator==(const Attribute_Selector& rhs) const; - bool operator<(const Simple_Selector& rhs) const override; + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; bool operator<(const Attribute_Selector& rhs) const; + bool operator==(const Attribute_Selector& rhs) const; ATTACH_AST_OPERATIONS(Attribute_Selector) ATTACH_CRTP_PERFORM_METHODS() }; @@ -471,10 +506,10 @@ namespace Sass { return Constants::UnificationOrder_PseudoElement; return Constants::UnificationOrder_PseudoClass; } - bool operator==(const Simple_Selector& rhs) const override; - bool operator==(const Pseudo_Selector& rhs) const; - bool operator<(const Simple_Selector& rhs) const override; + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; bool operator<(const Pseudo_Selector& rhs) const; + bool operator==(const Pseudo_Selector& rhs) const; Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; ATTACH_AST_OPERATIONS(Pseudo_Selector) ATTACH_CRTP_PERFORM_METHODS() @@ -505,10 +540,10 @@ namespace Sass { return Constants::UnificationOrder_Wrapped; } bool find ( bool (*f)(AST_Node_Obj) ) override; - bool operator==(const Simple_Selector& rhs) const override; - bool operator==(const Wrapped_Selector& rhs) const; - bool operator<(const Simple_Selector& rhs) const override; + bool operator<(const Simple_Selector& rhs) const final override; + bool operator==(const Simple_Selector& rhs) const final override; bool operator<(const Wrapped_Selector& rhs) const; + bool operator==(const Wrapped_Selector& rhs) const; void cloneChildren() override; ATTACH_AST_OPERATIONS(Wrapped_Selector) ATTACH_CRTP_PERFORM_METHODS() @@ -607,11 +642,17 @@ namespace Sass { } bool find ( bool (*f)(AST_Node_Obj) ) override; + bool operator<(const Selector& rhs) const override; bool operator==(const Selector& rhs) const override; + bool operator<(const Selector_List& rhs) const; + bool operator==(const Selector_List& rhs) const; + bool operator<(const Complex_Selector& rhs) const; + bool operator==(const Complex_Selector& rhs) const; bool operator<(const Compound_Selector& rhs) const; bool operator==(const Compound_Selector& rhs) const; - inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); } + bool operator<(const Simple_Selector& rhs) const; + bool operator==(const Simple_Selector& rhs) const; ComplexSelectorSet& sources() { return sources_; } void clearSources() { sources_.clear(); } @@ -658,6 +699,11 @@ namespace Sass { head_(ptr->head_), tail_(ptr->tail_), reference_(ptr->reference_) {}; + bool empty() const { + return (!tail() || tail()->empty()) + && (!head() || head()->empty()) + && combinator_ == ANCESTOR_OF; + } bool has_parent_ref() const override; bool has_real_parent_ref() const override; @@ -733,11 +779,18 @@ namespace Sass { return false; } bool find ( bool (*f)(AST_Node_Obj) ) override; + bool operator<(const Selector& rhs) const override; bool operator==(const Selector& rhs) const override; + bool operator<(const Selector_List& rhs) const; + bool operator==(const Selector_List& rhs) const; bool operator<(const Complex_Selector& rhs) const; bool operator==(const Complex_Selector& rhs) const; - inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); } + bool operator<(const Compound_Selector& rhs) const; + bool operator==(const Compound_Selector& rhs) const; + bool operator<(const Simple_Selector& rhs) const; + bool operator==(const Simple_Selector& rhs) const; + const ComplexSelectorSet sources() { //s = Set.new @@ -866,7 +919,14 @@ namespace Sass { bool operator==(const Selector& rhs) const override; bool operator<(const Selector_List& rhs) const; bool operator==(const Selector_List& rhs) const; + bool operator<(const Complex_Selector& rhs) const; + bool operator==(const Complex_Selector& rhs) const; + bool operator<(const Compound_Selector& rhs) const; + bool operator==(const Compound_Selector& rhs) const; + bool operator<(const Simple_Selector& rhs) const; + bool operator==(const Simple_Selector& rhs) const; // Selector Lists can be compared to comma lists + bool operator<(const Expression& rhs) const override; bool operator==(const Expression& rhs) const override; void cloneChildren() override; ATTACH_AST_OPERATIONS(Selector_List)