From bfece18c8c7764d041a150bdbd3e9cc02f32873b Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 18 Mar 2018 02:50:01 +0100 Subject: [PATCH] Move AST selectors into own compile units Split compare and unify also into own units. --- Makefile.conf | 3 + src/ast.cpp | 1609 ----------------------------------- src/ast.hpp | 846 +----------------- src/ast_sel_cmp.cpp | 534 ++++++++++++ src/ast_sel_unify.cpp | 249 ++++++ src/ast_selectors.cpp | 909 ++++++++++++++++++++ src/ast_selectors.hpp | 883 +++++++++++++++++++ win/libsass.targets | 4 + win/libsass.vcxproj.filters | 12 + 9 files changed, 2599 insertions(+), 2450 deletions(-) create mode 100644 src/ast_sel_cmp.cpp create mode 100644 src/ast_sel_unify.cpp create mode 100644 src/ast_selectors.cpp create mode 100644 src/ast_selectors.hpp diff --git a/Makefile.conf b/Makefile.conf index 2725c6bf06..ab498a38cc 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -7,6 +7,9 @@ SOURCES = \ ast.cpp \ + ast_sel_cmp.cpp \ + ast_sel_unify.cpp \ + ast_selectors.cpp \ node.cpp \ context.cpp \ constants.cpp \ diff --git a/src/ast.cpp b/src/ast.cpp index 1e3e5b3569..07a94ca306 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -19,45 +19,6 @@ namespace Sass { static Null sass_null(ParserState("null")); - bool Wrapped_Selector::find ( bool (*f)(AST_Node_Obj) ) - { - // check children first - if (selector_) { - if (selector_->find(f)) return true; - } - // execute last - return f(this); - } - - bool Selector_List::find ( bool (*f)(AST_Node_Obj) ) - { - // check children first - for (Complex_Selector_Obj sel : elements()) { - if (sel->find(f)) return true; - } - // execute last - return f(this); - } - - bool Compound_Selector::find ( bool (*f)(AST_Node_Obj) ) - { - // check children first - for (Simple_Selector_Obj sel : elements()) { - if (sel->find(f)) return true; - } - // execute last - return f(this); - } - - bool Complex_Selector::find ( bool (*f)(AST_Node_Obj) ) - { - // check children first - if (head_ && head_->find(f)) return true; - if (tail_ && tail_->find(f)) return true; - // execute last - return f(this); - } - bool Supports_Operator::needs_parens(Supports_Condition_Obj cond) const { if (Supports_Operator_Obj op = Cast(cond)) { return op->operand() != operand(); @@ -135,1564 +96,6 @@ namespace Sass { pstate_.offset += pstate - pstate_ + pstate.offset; } - bool Simple_Selector::is_ns_eq(const Simple_Selector& r) const - { - // https://github.com/sass/sass/issues/2229 - if ((has_ns_ == r.has_ns_) || - (has_ns_ && ns_.empty()) || - (r.has_ns_ && r.ns_.empty()) - ) { - if (ns_.empty() && r.ns() == "*") return false; - else if (r.ns().empty() && ns() == "*") return false; - else return ns() == r.ns(); - } - 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]; - if (!l && !r) return false; - else if (!r) return false; - else if (!l) return true; - else if (*l != *r) - { return *l < *r; } - } - // just compare the length now - return length() < rhs.length(); - } - - bool Compound_Selector::has_parent_ref() const - { - for (Simple_Selector_Obj s : *this) { - if (s && s->has_parent_ref()) return true; - } - return false; - } - - bool Compound_Selector::has_real_parent_ref() const - { - for (Simple_Selector_Obj s : *this) { - if (s && s->has_real_parent_ref()) return true; - } - return false; - } - - bool Complex_Selector::has_parent_ref() const - { - return (head() && head()->has_parent_ref()) || - (tail() && tail()->has_parent_ref()); - } - - bool Complex_Selector::has_real_parent_ref() const - { - return (head() && head()->has_real_parent_ref()) || - (tail() && tail()->has_real_parent_ref()); - } - - bool Complex_Selector::operator< (const Complex_Selector& rhs) const - { - // const iterators for tails - Complex_Selector_Ptr_Const l = this; - Complex_Selector_Ptr_Const r = &rhs; - Compound_Selector_Ptr l_h = NULL; - Compound_Selector_Ptr r_h = NULL; - if (l) l_h = l->head(); - if (r) r_h = r->head(); - // 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 for valid selectors - if (!l) return !!r; - if (!r) return false; - // both are null - else if (!l_h && !r_h) - { - // check combinator after heads - if (l->combinator() != r->combinator()) - { return l->combinator() < r->combinator(); } - // advance to next tails - l = l->tail(); - r = r->tail(); - // fetch the next headers - l_h = NULL; r_h = NULL; - if (l) l_h = l->head(); - if (r) r_h = r->head(); - } - // one side is null - else if (!r_h) return true; - else if (!l_h) return false; - // heads ok and equal - else if (*l_h == *r_h) - { - // check combinator after heads - if (l->combinator() != r->combinator()) - { return l->combinator() < r->combinator(); } - // advance to next tails - l = l->tail(); - r = r->tail(); - // fetch the next headers - l_h = NULL; r_h = NULL; - if (l) l_h = l->head(); - if (r) r_h = r->head(); - } - // heads are not equal - else return *l_h < *r_h; - } - } - - bool Complex_Selector::operator== (const Complex_Selector& rhs) const - { - // const iterators for tails - Complex_Selector_Ptr_Const l = this; - Complex_Selector_Ptr_Const r = &rhs; - Compound_Selector_Ptr l_h = NULL; - Compound_Selector_Ptr r_h = NULL; - if (l) l_h = l->head(); - if (r) r_h = r->head(); - // 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; - // both are null - if (!l_h && !r_h) - { - // check combinator after heads - if (l->combinator() != r->combinator()) - { return l->combinator() < r->combinator(); } - // advance to next tails - l = l->tail(); - r = r->tail(); - // fetch the next heads - l_h = NULL; r_h = NULL; - if (l) l_h = l->head(); - if (r) r_h = r->head(); - } - // equals if other head is empty - else if ((!l_h && !r_h) || - (!l_h && r_h->empty()) || - (!r_h && l_h->empty()) || - (l_h && r_h && *l_h == *r_h)) - { - // check combinator after heads - if (l->combinator() != r->combinator()) - { return l->combinator() == r->combinator(); } - // advance to next tails - l = l->tail(); - r = r->tail(); - // fetch the next heads - l_h = NULL; r_h = NULL; - if (l) l_h = l->head(); - if (r) r_h = r->head(); - } - // abort - else break; - } - // unreachable - return false; - } - - Compound_Selector_Ptr Compound_Selector::unify_with(Compound_Selector_Ptr rhs) - { - if (empty()) return rhs; - Compound_Selector_Obj unified = SASS_MEMORY_COPY(rhs); - for (size_t i = 0, L = length(); i < L; ++i) - { - if (unified.isNull()) break; - unified = at(i)->unify_with(unified); - } - return unified.detach(); - } - - bool Complex_Selector::operator== (const 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"); - } - - - bool Complex_Selector::operator< (const 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"); - } - - bool Compound_Selector::operator== (const 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"); - } - - bool Compound_Selector::operator< (const 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"); - } - - bool Selector_Schema::operator== (const 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"); - } - - bool Selector_Schema::operator< (const 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"); - } - - bool Simple_Selector::operator== (const Selector& rhs) const - { - if (Simple_Selector_Ptr_Const sp = Cast(&rhs)) return *this == *sp; - return false; - } - - bool Simple_Selector::operator< (const Selector& rhs) const - { - if (Simple_Selector_Ptr_Const sp = Cast(&rhs)) return *this < *sp; - return false; - } - - bool Simple_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; } - else if (name_ == rhs.name_) - { return is_ns_eq(rhs); } - else return false; - } - - bool Simple_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_; - } - - bool Selector_List::operator== (const 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; - } - - // Selector lists can be compared to comma lists - bool Selector_List::operator== (const Expression& 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; - } - - bool Selector_List::operator== (const Selector_List& rhs) const - { - if (&rhs == this) return true; - if (rhs.length() != length()) return false; - std::unordered_set lhs_set; - lhs_set.reserve(length()); - for (const Complex_Selector_Obj &element : elements()) { - lhs_set.insert(element.ptr()); - } - for (const Complex_Selector_Obj &element : rhs.elements()) { - if (lhs_set.find(element.ptr()) == lhs_set.end()) return false; - } - return true; - } - - bool Selector_List::operator< (const Selector& rhs) const - { - if (Selector_List_Ptr_Const sp = Cast(&rhs)) return *this < *sp; - return false; - } - - 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; - } - - Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs) - { - const size_t rsize = rhs->length(); - for (size_t i = 0; i < rsize; ++i) - { if (*this == *rhs->at(i)) return rhs; } - const int lhs_order = this->unification_order(); - size_t i = rsize; - while (i > 0 && lhs_order < rhs->at(i - 1)->unification_order()) --i; - rhs->elements().insert(rhs->elements().begin() + i, this); - return rhs; - } - - Simple_Selector_Ptr Element_Selector::unify_with(Simple_Selector_Ptr rhs) - { - // check if ns can be extended - // true for no ns or universal - if (has_universal_ns()) - { - // but dont extend with universal - // true for valid ns and universal - if (!rhs->is_universal_ns()) - { - // overwrite the name if star is given as name - if (this->name() == "*") { this->name(rhs->name()); } - // now overwrite the namespace name and flag - this->ns(rhs->ns()); this->has_ns(rhs->has_ns()); - // return copy - return this; - } - } - // namespace may changed, check the name now - // overwrite star (but not with another star) - if (name() == "*" && rhs->name() != "*") - { - // simply set the new name - this->name(rhs->name()); - // return copy - return this; - } - // return original - return this; - } - - Compound_Selector_Ptr Element_Selector::unify_with(Compound_Selector_Ptr rhs) - { - // TODO: handle namespaces - - // if the rhs is empty, just return a copy of this - if (rhs->length() == 0) { - rhs->append(this); - return rhs; - } - - Simple_Selector_Ptr rhs_0 = rhs->at(0); - // otherwise, this is a tag name - if (name() == "*") - { - if (typeid(*rhs_0) == typeid(Element_Selector)) - { - // if rhs is universal, just return this tagname + rhs's qualifiers - Element_Selector_Ptr ts = Cast(rhs_0); - rhs->at(0) = this->unify_with(ts); - return rhs; - } - else if (Cast(rhs_0) || Cast(rhs_0)) { - // qualifier is `.class`, so we can prefix with `ns|*.class` - if (has_ns() && !rhs_0->has_ns()) { - if (ns() != "*") rhs->elements().insert(rhs->begin(), this); - } - return rhs; - } - - - return rhs; - } - - if (typeid(*rhs_0) == typeid(Element_Selector)) - { - // if rhs is universal, just return this tagname + rhs's qualifiers - if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0; - // otherwise create new compound and unify first simple selector - rhs->at(0) = this->unify_with(rhs_0); - return rhs; - - } - // else it's a tag name and a bunch of qualifiers -- just append them - if (name() != "*") rhs->elements().insert(rhs->begin(), this); - return rhs; - } - - Compound_Selector_Ptr Class_Selector::unify_with(Compound_Selector_Ptr rhs) - { - rhs->has_line_break(has_line_break()); - return Simple_Selector::unify_with(rhs); - } - - Compound_Selector_Ptr Id_Selector::unify_with(Compound_Selector_Ptr rhs) - { - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { - if (Id_Selector_Ptr sel = Cast(rhs->at(i))) { - if (sel->name() != name()) return 0; - } - } - rhs->has_line_break(has_line_break()); - return Simple_Selector::unify_with(rhs); - } - - Compound_Selector_Ptr Pseudo_Selector::unify_with(Compound_Selector_Ptr rhs) - { - if (is_pseudo_element()) - { - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { - if (Pseudo_Selector_Ptr sel = Cast(rhs->at(i))) { - if (sel->is_pseudo_element() && sel->name() != name()) return 0; - } - } - } - return Simple_Selector::unify_with(rhs); - } - - bool Attribute_Selector::operator< (const Attribute_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(); } - } - - bool Attribute_Selector::operator< (const Simple_Selector& rhs) const - { - if (Attribute_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this < *w; - } - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); - } - - bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const - { - // get optional value state - bool no_lhs_val = value().isNull(); - bool no_rhs_val = rhs.value().isNull(); - // both are null, therefore equal - if (no_lhs_val && no_rhs_val) { - return (name() == rhs.name()) - && (matcher() == rhs.matcher()) - && (is_ns_eq(rhs)); - } - // both are defined, evaluate - if (no_lhs_val == no_rhs_val) { - return (name() == rhs.name()) - && (matcher() == rhs.matcher()) - && (is_ns_eq(rhs)) - && (*value() == *rhs.value()); - } - // not equal - return false; - - } - - bool Attribute_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; - } - return false; - } - - bool Element_Selector::operator< (const Element_Selector& rhs) const - { - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); - } - - bool Element_Selector::operator< (const Simple_Selector& rhs) const - { - if (Element_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this < *w; - } - if (is_ns_eq(rhs)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); - } - - bool Element_Selector::operator== (const Element_Selector& rhs) const - { - return is_ns_eq(rhs) && - name() == rhs.name(); - } - - bool Element_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; - } - return false; - } - - bool Pseudo_Selector::operator== (const Pseudo_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(); - } - else return false; - } - - bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const - { - if (Pseudo_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this == *w; - } - return is_ns_eq(rhs) && - name() == rhs.name(); - } - - bool Pseudo_Selector::operator< (const Pseudo_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(); } - return ns() < rhs.ns(); - } - - bool Pseudo_Selector::operator< (const Simple_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(); - } - - bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const - { - if (is_ns_eq(rhs) && name() == rhs.name()) - { return *(selector()) == *(rhs.selector()); } - else return false; - } - - bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const - { - if (Wrapped_Selector_Ptr_Const w = Cast(&rhs)) - { - return *this == *w; - } - return is_ns_eq(rhs) && - name() == rhs.name(); - } - - 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)) - { return name() < rhs.name(); } - return ns() < rhs.ns(); - } - - bool Wrapped_Selector::operator< (const Simple_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(); - } - - bool Wrapped_Selector::is_superselector_of(Wrapped_Selector_Ptr_Const sub) const - { - if (this->name() != sub->name()) return false; - if (this->name() == ":current") return false; - if (Selector_List_Obj rhs_list = Cast(sub->selector())) { - if (Selector_List_Obj lhs_list = Cast(selector())) { - return lhs_list->is_superselector_of(rhs_list); - } - } - coreError("is_superselector expected a Selector_List", sub->pstate()); - return false; - } - - bool Compound_Selector::is_superselector_of(Selector_List_Ptr_Const rhs, std::string wrapped) const - { - for (Complex_Selector_Obj item : rhs->elements()) { - if (is_superselector_of(item, wrapped)) return true; - } - return false; - } - - bool Compound_Selector::is_superselector_of(Complex_Selector_Ptr_Const rhs, std::string wrapped) const - { - if (rhs->head()) return is_superselector_of(rhs->head(), wrapped); - return false; - } - - bool Compound_Selector::is_superselector_of(Compound_Selector_Ptr_Const rhs, std::string wrapping) const - { - Compound_Selector_Ptr_Const lhs = this; - Simple_Selector_Ptr lbase = lhs->base(); - Simple_Selector_Ptr rbase = rhs->base(); - - // Check if pseudo-elements are the same between the selectors - - std::set lpsuedoset, rpsuedoset; - for (size_t i = 0, L = length(); i < L; ++i) - { - if ((*this)[i]->is_pseudo_element()) { - std::string pseudo((*this)[i]->to_string()); - pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving - lpsuedoset.insert(pseudo); - } - } - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { - if ((*rhs)[i]->is_pseudo_element()) { - std::string pseudo((*rhs)[i]->to_string()); - pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving - rpsuedoset.insert(pseudo); - } - } - if (lpsuedoset != rpsuedoset) { - return false; - } - - // would like to replace this without stringification - // https://github.com/sass/sass/issues/2229 - // SimpleSelectorSet lset, rset; - std::set lset, rset; - - if (lbase && rbase) - { - if (lbase->to_string() == rbase->to_string()) { - for (size_t i = 1, L = length(); i < L; ++i) - { lset.insert((*this)[i]->to_string()); } - for (size_t i = 1, L = rhs->length(); i < L; ++i) - { rset.insert((*rhs)[i]->to_string()); } - return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); - } - return false; - } - - for (size_t i = 0, iL = length(); i < iL; ++i) - { - Selector_Obj wlhs = (*this)[i]; - // very special case for wrapped matches selector - if (Wrapped_Selector_Obj wrapped = Cast(wlhs)) { - if (wrapped->name() == ":not") { - if (Selector_List_Obj not_list = Cast(wrapped->selector())) { - if (not_list->is_superselector_of(rhs, wrapped->name())) return false; - } else { - throw std::runtime_error("wrapped not selector is not a list"); - } - } - if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { - wlhs = wrapped->selector(); - if (Selector_List_Obj list = Cast(wrapped->selector())) { - if (Compound_Selector_Ptr_Const comp = Cast(rhs)) { - if (!wrapping.empty() && wrapping != wrapped->name()) return false; - if (wrapping.empty() || wrapping != wrapped->name()) {; - if (list->is_superselector_of(comp, wrapped->name())) return true; - } - } - } - } - Simple_Selector_Ptr rhs_sel = NULL; - if (rhs->elements().size() > i) rhs_sel = (*rhs)[i]; - if (Wrapped_Selector_Ptr wrapped_r = Cast(rhs_sel)) { - if (wrapped->name() == wrapped_r->name()) { - if (wrapped->is_superselector_of(wrapped_r)) { - continue; - }} - } - } - // match from here on as strings - lset.insert(wlhs->to_string()); - } - - for (size_t n = 0, nL = rhs->length(); n < nL; ++n) - { - Selector_Obj r = (*rhs)[n]; - if (Wrapped_Selector_Obj wrapped = Cast(r)) { - if (wrapped->name() == ":not") { - if (Selector_List_Obj ls = Cast(wrapped->selector())) { - ls->remove_parent_selectors(); - if (is_superselector_of(ls, wrapped->name())) return false; - } - } - if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { - if (!wrapping.empty()) { - if (wrapping != wrapped->name()) return false; - } - if (Selector_List_Obj ls = Cast(wrapped->selector())) { - ls->remove_parent_selectors(); - return (is_superselector_of(ls, wrapped->name())); - } - } - } - rset.insert(r->to_string()); - } - - //for (auto l : lset) { cerr << "l: " << l << endl; } - //for (auto r : rset) { cerr << "r: " << r << endl; } - - if (lset.empty()) return true; - // return true if rset contains all the elements of lset - return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); - - } - - // create complex selector (ancestor of) from compound selector - Complex_Selector_Obj Compound_Selector::to_complex() - { - // create an intermediate complex selector - return SASS_MEMORY_NEW(Complex_Selector, - pstate(), - Complex_Selector::ANCESTOR_OF, - this, - {}); - } - - Selector_List_Ptr Complex_Selector::unify_with(Complex_Selector_Ptr other) - { - - // get last tails (on the right side) - Complex_Selector_Obj l_last = this->mutable_last(); - Complex_Selector_Obj r_last = other->mutable_last(); - - // check valid pointers (assertion) - SASS_ASSERT(l_last, "lhs is null"); - SASS_ASSERT(r_last, "rhs is null"); - - // Not sure about this check, but closest way I could check - // was to see if this is a ruby 'SimpleSequence' equivalent. - // It seems to do the job correctly as some specs react to this - if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0; - if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0; - - // get the headers for the last tails - Compound_Selector_Obj l_last_head = l_last->head(); - Compound_Selector_Obj r_last_head = r_last->head(); - - // check valid head pointers (assertion) - SASS_ASSERT(l_last_head, "lhs head is null"); - SASS_ASSERT(r_last_head, "rhs head is null"); - - // get the unification of the last compound selectors - Compound_Selector_Obj unified = r_last_head->unify_with(l_last_head); - - // abort if we could not unify heads - if (unified == 0) return 0; - - // check for universal (star: `*`) selector - bool is_universal = l_last_head->is_universal() || - r_last_head->is_universal(); - - if (is_universal) - { - // move the head - l_last->head({}); - r_last->head(unified); - } - - // create nodes from both selectors - Node lhsNode = complexSelectorToNode(this); - Node rhsNode = complexSelectorToNode(other); - - // overwrite universal base - if (!is_universal) - { - // create some temporaries to convert to node - Complex_Selector_Obj fake = unified->to_complex(); - Node unified_node = complexSelectorToNode(fake); - // add to permutate the list? - rhsNode.plus(unified_node); - } - - // do some magic we inherit from node and extend - Node node = subweave(lhsNode, rhsNode); - Selector_List_Obj result = SASS_MEMORY_NEW(Selector_List, pstate()); - NodeDequePtr col = node.collection(); // move from collection to list - for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++) - { result->append(nodeToComplexSelector(Node::naiveTrim(*it))); } - - // only return if list has some entries - return result->length() ? result.detach() : 0; - - } - - bool Compound_Selector::operator== (const Compound_Selector& rhs) const - { - if (&rhs == this) return true; - if (rhs.length() != length()) return false; - std::unordered_set lhs_set; - lhs_set.reserve(length()); - for (const Simple_Selector_Obj &element : elements()) { - lhs_set.insert(element.ptr()); - } - for (const Simple_Selector_Obj &element : rhs.elements()) { - if (lhs_set.find(element.ptr()) == lhs_set.end()) return false; - } - return true; - } - - bool Complex_Selector::is_superselector_of(Compound_Selector_Ptr_Const rhs, std::string wrapping) const - { - return last()->head() && last()->head()->is_superselector_of(rhs, wrapping); - } - - bool Complex_Selector::is_superselector_of(Complex_Selector_Ptr_Const rhs, std::string wrapping) const - { - Complex_Selector_Ptr_Const lhs = this; - // check for selectors with leading or trailing combinators - if (!lhs->head() || !rhs->head()) - { return false; } - Complex_Selector_Ptr_Const l_innermost = lhs->last(); - if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF) - { return false; } - Complex_Selector_Ptr_Const r_innermost = rhs->last(); - if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF) - { return false; } - // more complex (i.e., longer) selectors are always more specific - size_t l_len = lhs->length(), r_len = rhs->length(); - if (l_len > r_len) - { return false; } - - if (l_len == 1) - { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); } - - // we have to look one tail deeper, since we cary the - // combinator around for it (which is important here) - if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) { - Complex_Selector_Obj lhs_tail = lhs->tail(); - Complex_Selector_Obj rhs_tail = rhs->tail(); - if (lhs_tail->combinator() != rhs_tail->combinator()) return false; - if (lhs_tail->head() && !rhs_tail->head()) return false; - if (!lhs_tail->head() && rhs_tail->head()) return false; - if (lhs_tail->head() && rhs_tail->head()) { - if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; - } - } - - bool found = false; - Complex_Selector_Ptr_Const marker = rhs; - for (size_t i = 0, L = rhs->length(); i < L; ++i) { - if (i == L-1) - { return false; } - if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping)) - { found = true; break; } - marker = marker->tail(); - } - if (!found) - { return false; } - - /* - Hmm, I hope I have the logic right: - - if lhs has a combinator: - if !(marker has a combinator) return false - if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false - return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost) - else if marker has a combinator: - if !(marker.combinator == ">") return false - return lhs.tail.is_superselector_of(marker.tail) - else - return lhs.tail.is_superselector_of(marker.tail) - */ - if (lhs->combinator() != Complex_Selector::ANCESTOR_OF) - { - if (marker->combinator() == Complex_Selector::ANCESTOR_OF) - { return false; } - if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator())) - { return false; } - return lhs->tail()->is_superselector_of(marker->tail()); - } - else if (marker->combinator() != Complex_Selector::ANCESTOR_OF) - { - if (marker->combinator() != Complex_Selector::PARENT_OF) - { return false; } - return lhs->tail()->is_superselector_of(marker->tail()); - } - return lhs->tail()->is_superselector_of(marker->tail()); - } - - size_t Complex_Selector::length() const - { - // TODO: make this iterative - if (!tail()) return 1; - return 1 + tail()->length(); - } - - // append another complex selector at the end - // check if we need to append some headers - // then we need to check for the combinator - // only then we can safely set the new tail - void Complex_Selector::append(Complex_Selector_Obj ss, Backtraces& traces) - { - - Complex_Selector_Obj t = ss->tail(); - Combinator c = ss->combinator(); - String_Obj r = ss->reference(); - Compound_Selector_Obj h = ss->head(); - - if (ss->has_line_feed()) has_line_feed(true); - if (ss->has_line_break()) has_line_break(true); - - // append old headers - if (h && h->length()) { - if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) { - traces.push_back(Backtrace(pstate())); - throw Exception::InvalidParent(this, traces, ss); - } else if (last()->head_ && last()->head_->length()) { - Compound_Selector_Obj rh = last()->head(); - size_t i; - size_t L = h->length(); - if (Cast(h->first())) { - if (Class_Selector_Ptr cs = Cast(rh->last())) { - Class_Selector_Ptr sqs = SASS_MEMORY_COPY(cs); - sqs->name(sqs->name() + (*h)[0]->name()); - sqs->pstate((*h)[0]->pstate()); - (*rh)[rh->length()-1] = sqs; - rh->pstate(h->pstate()); - for (i = 1; i < L; ++i) rh->append((*h)[i]); - } else if (Id_Selector_Ptr is = Cast(rh->last())) { - Id_Selector_Ptr sqs = SASS_MEMORY_COPY(is); - sqs->name(sqs->name() + (*h)[0]->name()); - sqs->pstate((*h)[0]->pstate()); - (*rh)[rh->length()-1] = sqs; - rh->pstate(h->pstate()); - for (i = 1; i < L; ++i) rh->append((*h)[i]); - } else if (Element_Selector_Ptr ts = Cast(rh->last())) { - Element_Selector_Ptr tss = SASS_MEMORY_COPY(ts); - tss->name(tss->name() + (*h)[0]->name()); - tss->pstate((*h)[0]->pstate()); - (*rh)[rh->length()-1] = tss; - rh->pstate(h->pstate()); - for (i = 1; i < L; ++i) rh->append((*h)[i]); - } else if (Placeholder_Selector_Ptr ps = Cast(rh->last())) { - Placeholder_Selector_Ptr pss = SASS_MEMORY_COPY(ps); - pss->name(pss->name() + (*h)[0]->name()); - pss->pstate((*h)[0]->pstate()); - (*rh)[rh->length()-1] = pss; - rh->pstate(h->pstate()); - for (i = 1; i < L; ++i) rh->append((*h)[i]); - } else { - last()->head_->concat(h); - } - } else { - last()->head_->concat(h); - } - } else if (last()->head_) { - last()->head_->concat(h); - } - } else { - // std::cerr << "has no or empty head\n"; - } - - Complex_Selector_Ptr last = mutable_last(); - if (last) { - if (last->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) { - Complex_Selector_Ptr inter = SASS_MEMORY_NEW(Complex_Selector, pstate()); - inter->reference(r); - inter->combinator(c); - inter->tail(t); - last->tail(inter); - } else { - if (last->combinator() == ANCESTOR_OF) { - last->combinator(c); - last->reference(r); - } - last->tail(t); - } - } - - } - - Selector_List_Obj Selector_List::eval(Eval& eval) - { - Selector_List_Obj list = schema() ? - eval(schema()) : eval(this); - list->schema(schema()); - return list; - } - - Selector_List_Ptr Selector_List::resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent) - { - if (!this->has_parent_ref()) return this; - Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate()); - for (size_t si = 0, sL = this->length(); si < sL; ++si) { - Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent); - ss->concat(rv); - } - return ss; - } - - Selector_List_Ptr Complex_Selector::resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent) - { - Complex_Selector_Obj tail = this->tail(); - Compound_Selector_Obj head = this->head(); - Selector_List_Ptr parents = pstack.back(); - - if (!this->has_real_parent_ref() && !implicit_parent) { - Selector_List_Ptr retval = SASS_MEMORY_NEW(Selector_List, pstate(), 1); - retval->append(this); - return retval; - } - - // first resolve_parent_refs the tail (which may return an expanded list) - Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, traces, implicit_parent) : 0; - - if (head && head->length() > 0) { - - Selector_List_Obj retval; - // we have a parent selector in a simple compound list - // mix parent complex selector into the compound list - if (Cast((*head)[0])) { - retval = SASS_MEMORY_NEW(Selector_List, pstate()); - - // it turns out that real parent references reach - // across @at-root rules, which comes unexpected - if (parents == NULL && head->has_real_parent_ref()) { - int i = pstack.size() - 1; - while (!parents && i > -1) { - parents = pstack.at(i--); - } - } - - if (parents && parents->length()) { - if (tails && tails->length() > 0) { - for (size_t n = 0, nL = tails->length(); n < nL; ++n) { - for (size_t i = 0, iL = parents->length(); i < iL; ++i) { - Complex_Selector_Obj t = (*tails)[n]; - Complex_Selector_Obj parent = (*parents)[i]; - Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent); - Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this); - ss->tail(t ? SASS_MEMORY_CLONE(t) : NULL); - Compound_Selector_Obj h = SASS_MEMORY_COPY(head_); - // remove parent selector from sequence - if (h->length()) { - h->erase(h->begin()); - ss->head(h); - } else { - ss->head({}); - } - // adjust for parent selector (1 char) - // if (h->length()) { - // ParserState state(h->at(0)->pstate()); - // state.offset.column += 1; - // state.column -= 1; - // (*h)[0]->pstate(state); - // } - // keep old parser state - s->pstate(pstate()); - // append new tail - s->append(ss, traces); - retval->append(s); - } - } - } - // have no tails but parents - // loop above is inside out - else { - for (size_t i = 0, iL = parents->length(); i < iL; ++i) { - Complex_Selector_Obj parent = (*parents)[i]; - Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent); - Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this); - // this is only if valid if the parent has no trailing op - // otherwise we cannot append more simple selectors to head - if (parent->last()->combinator() != ANCESTOR_OF) { - traces.push_back(Backtrace(pstate())); - throw Exception::InvalidParent(parent, traces, ss); - } - ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL); - Compound_Selector_Obj h = SASS_MEMORY_COPY(head_); - // remove parent selector from sequence - if (h->length()) { - h->erase(h->begin()); - ss->head(h); - } else { - ss->head({}); - } - // \/ IMO ruby sass bug \/ - ss->has_line_feed(false); - // adjust for parent selector (1 char) - // if (h->length()) { - // ParserState state(h->at(0)->pstate()); - // state.offset.column += 1; - // state.column -= 1; - // (*h)[0]->pstate(state); - // } - // keep old parser state - s->pstate(pstate()); - // append new tail - s->append(ss, traces); - retval->append(s); - } - } - } - // have no parent but some tails - else { - if (tails && tails->length() > 0) { - for (size_t n = 0, nL = tails->length(); n < nL; ++n) { - Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this); - cpy->tail(SASS_MEMORY_CLONE(tails->at(n))); - cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate())); - for (size_t i = 1, L = this->head()->length(); i < L; ++i) - cpy->head()->append((*this->head())[i]); - if (!cpy->head()->length()) cpy->head({}); - retval->append(cpy->skip_empty_reference()); - } - } - // have no parent nor tails - else { - Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this); - cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate())); - for (size_t i = 1, L = this->head()->length(); i < L; ++i) - cpy->head()->append((*this->head())[i]); - if (!cpy->head()->length()) cpy->head({}); - retval->append(cpy->skip_empty_reference()); - } - } - } - // no parent selector in head - else { - retval = this->tails(tails); - } - - for (Simple_Selector_Obj ss : head->elements()) { - if (Wrapped_Selector_Ptr ws = Cast(ss)) { - if (Selector_List_Ptr sl = Cast(ws->selector())) { - if (parents) ws->selector(sl->resolve_parent_refs(pstack, traces, implicit_parent)); - } - } - } - - return retval.detach(); - - } - // has no head - return this->tails(tails); - } - - Selector_List_Ptr Complex_Selector::tails(Selector_List_Ptr tails) - { - Selector_List_Ptr rv = SASS_MEMORY_NEW(Selector_List, pstate_); - if (tails && tails->length()) { - for (size_t i = 0, iL = tails->length(); i < iL; ++i) { - Complex_Selector_Obj pr = SASS_MEMORY_CLONE(this); - pr->tail(tails->at(i)); - rv->append(pr); - } - } - else { - rv->append(this); - } - return rv; - } - - // return the last tail that is defined - Complex_Selector_Ptr_Const Complex_Selector::first() const - { - // declare variables used in loop - Complex_Selector_Ptr_Const cur = this; - Compound_Selector_Ptr_Const head; - // processing loop - while (cur) - { - // get the head - head = cur->head_.ptr(); - // abort (and return) if it is not a parent selector - if (!head || head->length() != 1 || !Cast((*head)[0])) { - break; - } - // advance to next - cur = cur->tail_; - } - // result - return cur; - } - - Complex_Selector_Ptr Complex_Selector::mutable_first() - { - return const_cast(first()); - } - - // return the last tail that is defined - Complex_Selector_Ptr_Const Complex_Selector::last() const - { - Complex_Selector_Ptr_Const cur = this; - Complex_Selector_Ptr_Const nxt = cur; - // loop until last - while (nxt) { - cur = nxt; - nxt = cur->tail_.ptr(); - } - return cur; - } - - Complex_Selector_Ptr Complex_Selector::mutable_last() - { - return const_cast(last()); - } - - Complex_Selector::Combinator Complex_Selector::clear_innermost() - { - Combinator c; - if (!tail() || tail()->tail() == nullptr) - { c = combinator(); combinator(ANCESTOR_OF); tail({}); } - else - { c = tail_->clear_innermost(); } - return c; - } - - void Complex_Selector::set_innermost(Complex_Selector_Obj val, Combinator c) - { - if (!tail_) - { tail_ = val; combinator(c); } - else - { tail_->set_innermost(val, c); } - } - - void Complex_Selector::cloneChildren() - { - if (head()) head(SASS_MEMORY_CLONE(head())); - if (tail()) tail(SASS_MEMORY_CLONE(tail())); - } - - void Compound_Selector::cloneChildren() - { - for (size_t i = 0, l = length(); i < l; i++) { - at(i) = SASS_MEMORY_CLONE(at(i)); - } - } - - void Selector_List::cloneChildren() - { - for (size_t i = 0, l = length(); i < l; i++) { - at(i) = SASS_MEMORY_CLONE(at(i)); - } - } - - void Wrapped_Selector::cloneChildren() - { - selector(SASS_MEMORY_CLONE(selector())); - } - - // remove parent selector references - // basically unwraps parsed selectors - void Selector_List::remove_parent_selectors() - { - // Check every rhs selector against left hand list - for(size_t i = 0, L = length(); i < L; ++i) { - if (!(*this)[i]->head()) continue; - if ((*this)[i]->head()->is_empty_reference()) { - // simply move to the next tail if we have "no" combinator - if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) { - if ((*this)[i]->tail()) { - if ((*this)[i]->has_line_feed()) { - (*this)[i]->tail()->has_line_feed(true); - } - (*this)[i] = (*this)[i]->tail(); - } - } - // otherwise remove the first item from head - else { - (*this)[i]->head()->erase((*this)[i]->head()->begin()); - } - } - } - } - - size_t Wrapped_Selector::hash() const - { - if (hash_ == 0) { - hash_combine(hash_, Simple_Selector::hash()); - if (selector_) hash_combine(hash_, selector_->hash()); - } - return hash_; - } - bool Wrapped_Selector::has_parent_ref() const { - // if (has_reference()) return true; - if (!selector()) return false; - return selector()->has_parent_ref(); - } - bool Wrapped_Selector::has_real_parent_ref() const { - // if (has_reference()) return true; - if (!selector()) return false; - return selector()->has_real_parent_ref(); - } - unsigned long Wrapped_Selector::specificity() const - { - return selector_ ? selector_->specificity() : 0; - } - - - bool Selector_List::has_parent_ref() const - { - for (Complex_Selector_Obj s : elements()) { - if (s && s->has_parent_ref()) return true; - } - return false; - } - - bool Selector_List::has_real_parent_ref() const - { - for (Complex_Selector_Obj s : elements()) { - if (s && s->has_real_parent_ref()) return true; - } - return false; - } - - bool Selector_Schema::has_parent_ref() const - { - if (String_Schema_Obj schema = Cast(contents())) { - return schema->length() > 0 && Cast(schema->at(0)) != NULL; - } - return false; - } - - bool Selector_Schema::has_real_parent_ref() const - { - if (String_Schema_Obj schema = Cast(contents())) { - if (schema->length() == 0) return false; - return Cast(schema->at(0)) != nullptr; - } - return false; - } - - void Selector_List::adjust_after_pushing(Complex_Selector_Obj c) - { - // if (c->has_reference()) has_reference(true); - } - - // it's a superselector if every selector of the right side - // list is a superselector of the given left side selector - bool Complex_Selector::is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping) const - { - // Check every rhs selector against left hand list - for(size_t i = 0, L = sub->length(); i < L; ++i) { - if (!is_superselector_of((*sub)[i], wrapping)) return false; - } - return true; - } - - // it's a superselector if every selector of the right side - // list is a superselector of the given left side selector - bool Selector_List::is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping) const - { - // Check every rhs selector against left hand list - for(size_t i = 0, L = sub->length(); i < L; ++i) { - if (!is_superselector_of((*sub)[i], wrapping)) return false; - } - return true; - } - - // it's a superselector if every selector on the right side - // is a superselector of any one of the left side selectors - bool Selector_List::is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapping) const - { - // Check every lhs selector against right hand - for(size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->is_superselector_of(sub, wrapping)) return true; - } - return false; - } - - // it's a superselector if every selector on the right side - // is a superselector of any one of the left side selectors - bool Selector_List::is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapping) const - { - // Check every lhs selector against right hand - for(size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->is_superselector_of(sub)) return true; - } - return false; - } - - Selector_List_Ptr Selector_List::unify_with(Selector_List_Ptr rhs) { - std::vector unified_complex_selectors; - // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` - for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { - Complex_Selector_Obj seq1 = (*this)[lhs_i]; - for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { - Complex_Selector_Ptr seq2 = rhs->at(rhs_i); - - Selector_List_Obj result = seq1->unify_with(seq2); - if( result ) { - for(size_t i = 0, L = result->length(); i < L; ++i) { - unified_complex_selectors.push_back( (*result)[i] ); - } - } - } - } - - // Creates the final Selector_List by combining all the complex selectors - Selector_List_Ptr final_result = SASS_MEMORY_NEW(Selector_List, pstate()); - for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) { - final_result->append(*itr); - } - return final_result; - } - - void Selector_List::populate_extends(Selector_List_Obj extendee, Subset_Map& extends) - { - - Selector_List_Ptr extender = this; - for (auto complex_sel : extendee->elements()) { - Complex_Selector_Obj c = complex_sel; - - - // Ignore any parent selectors, until we find the first non Selectorerence head - Compound_Selector_Obj compound_sel = c->head(); - Complex_Selector_Obj pIter = complex_sel; - while (pIter) { - Compound_Selector_Obj pHead = pIter->head(); - if (pHead && Cast(pHead->elements()[0]) == NULL) { - compound_sel = pHead; - break; - } - - pIter = pIter->tail(); - } - - if (!pIter->head() || pIter->tail()) { - coreError("nested selectors may not be extended", c->pstate()); - } - - compound_sel->is_optional(extendee->is_optional()); - - for (size_t i = 0, L = extender->length(); i < L; ++i) { - extends.put(compound_sel, std::make_pair((*extender)[i], compound_sel)); - } - } - }; - - void Compound_Selector::append(Simple_Selector_Obj element) - { - Vectorized::append(element); - pstate_.offset += element->pstate().offset; - } - - Compound_Selector_Ptr Compound_Selector::minus(Compound_Selector_Ptr rhs) - { - Compound_Selector_Ptr result = SASS_MEMORY_NEW(Compound_Selector, pstate()); - // result->has_parent_reference(has_parent_reference()); - - // not very efficient because it needs to preserve order - for (size_t i = 0, L = length(); i < L; ++i) - { - bool found = false; - std::string thisSelector((*this)[i]->to_string()); - for (size_t j = 0, M = rhs->length(); j < M; ++j) - { - if (thisSelector == (*rhs)[j]->to_string()) - { - found = true; - break; - } - } - if (!found) result->append((*this)[i]); - } - - return result; - } - - void Compound_Selector::mergeSources(ComplexSelectorSet& sources) - { - for (ComplexSelectorSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) { - this->sources_.insert(SASS_MEMORY_CLONE(*iterator)); - } - } - Argument_Obj Arguments::get_rest_argument() { if (this->has_rest_argument()) { @@ -2117,14 +520,6 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Supports_Operator); IMPLEMENT_AST_OPERATORS(Supports_Negation); - IMPLEMENT_AST_OPERATORS(Compound_Selector); - IMPLEMENT_AST_OPERATORS(Complex_Selector); - IMPLEMENT_AST_OPERATORS(Element_Selector); - IMPLEMENT_AST_OPERATORS(Class_Selector); - IMPLEMENT_AST_OPERATORS(Id_Selector); - IMPLEMENT_AST_OPERATORS(Pseudo_Selector); - IMPLEMENT_AST_OPERATORS(Wrapped_Selector); - IMPLEMENT_AST_OPERATORS(Selector_List); IMPLEMENT_AST_OPERATORS(Ruleset); IMPLEMENT_AST_OPERATORS(Media_Block); IMPLEMENT_AST_OPERATORS(Custom_Warning); @@ -2140,7 +535,6 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Boolean); IMPLEMENT_AST_OPERATORS(Color); IMPLEMENT_AST_OPERATORS(Null); - IMPLEMENT_AST_OPERATORS(Parent_Selector); IMPLEMENT_AST_OPERATORS(Parent_Reference); IMPLEMENT_AST_OPERATORS(Import); IMPLEMENT_AST_OPERATORS(Import_Stub); @@ -2164,7 +558,6 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(At_Root_Query); IMPLEMENT_AST_OPERATORS(Variable); IMPLEMENT_AST_OPERATORS(Comment); - IMPLEMENT_AST_OPERATORS(Attribute_Selector); IMPLEMENT_AST_OPERATORS(Supports_Interpolation); IMPLEMENT_AST_OPERATORS(Supports_Declaration); IMPLEMENT_AST_OPERATORS(Supports_Condition); @@ -2178,8 +571,6 @@ namespace Sass { IMPLEMENT_AST_OPERATORS(Trace); IMPLEMENT_AST_OPERATORS(Keyframe_Rule); IMPLEMENT_AST_OPERATORS(Bubble); - IMPLEMENT_AST_OPERATORS(Selector_Schema); - IMPLEMENT_AST_OPERATORS(Placeholder_Selector); IMPLEMENT_AST_OPERATORS(Definition); IMPLEMENT_AST_OPERATORS(Declaration); } diff --git a/src/ast.hpp b/src/ast.hpp index dfc59f12c2..d080e23c7a 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -2154,236 +2154,6 @@ namespace Sass { ATTACH_CRTP_PERFORM_METHODS() }; - ///////////////////////////////////////// - // Abstract base class for CSS selectors. - ///////////////////////////////////////// - class Selector : public Expression { - // ADD_PROPERTY(bool, has_reference) - // line break before list separator - ADD_PROPERTY(bool, has_line_feed) - // line break after list separator - ADD_PROPERTY(bool, has_line_break) - // maybe we have optional flag - ADD_PROPERTY(bool, is_optional) - // parent block pointers - - // must not be a reference counted object - // otherwise we create circular references - ADD_PROPERTY(Media_Block_Ptr, media_block) - protected: - mutable size_t hash_; - public: - Selector(ParserState pstate) - : Expression(pstate), - has_line_feed_(false), - has_line_break_(false), - is_optional_(false), - media_block_(0), - hash_(0) - { concrete_type(SELECTOR); } - Selector(const Selector* ptr) - : Expression(ptr), - // has_reference_(ptr->has_reference_), - has_line_feed_(ptr->has_line_feed_), - has_line_break_(ptr->has_line_break_), - is_optional_(ptr->is_optional_), - media_block_(ptr->media_block_), - hash_(ptr->hash_) - { concrete_type(SELECTOR); } - virtual ~Selector() = 0; - size_t hash() const override = 0; - virtual unsigned long specificity() const = 0; - virtual int unification_order() const = 0; - virtual void set_media_block(Media_Block_Ptr mb) { - media_block(mb); - } - virtual bool has_parent_ref() const { - return false; - } - virtual bool has_real_parent_ref() const { - return false; - } - // dispatch to correct handlers - virtual bool operator<(const Selector& rhs) const = 0; - virtual bool operator==(const Selector& rhs) const = 0; - bool operator>(const Selector& rhs) const { return rhs < *this; }; - bool operator!=(const Selector& rhs) const { return !(rhs == *this); }; - ATTACH_VIRTUAL_AST_OPERATIONS(Selector); - }; - inline Selector::~Selector() { } - - ///////////////////////////////////////////////////////////////////////// - // Interpolated selectors -- the interpolated String will be expanded and - // re-parsed into a normal selector class. - ///////////////////////////////////////////////////////////////////////// - class Selector_Schema final : public AST_Node { - ADD_PROPERTY(String_Obj, contents) - ADD_PROPERTY(bool, connect_parent); - // must not be a reference counted object - // otherwise we create circular references - ADD_PROPERTY(Media_Block_Ptr, media_block) - // store computed hash - mutable size_t hash_; - public: - Selector_Schema(ParserState pstate, String_Obj c) - : AST_Node(pstate), - contents_(c), - connect_parent_(true), - media_block_(NULL), - hash_(0) - { } - Selector_Schema(const Selector_Schema* ptr) - : AST_Node(ptr), - contents_(ptr->contents_), - connect_parent_(ptr->connect_parent_), - media_block_(ptr->media_block_), - hash_(ptr->hash_) - { } - bool has_parent_ref() const; - bool has_real_parent_ref() const; - bool operator<(const Selector& rhs) const; - bool operator==(const Selector& rhs) const; - // selector schema is not yet a final selector, so we do not - // have a specificity for it yet. We need to - unsigned long specificity() const { return 0; } - size_t hash() const override { - if (hash_ == 0) { - hash_combine(hash_, contents_->hash()); - } - return hash_; - } - ATTACH_AST_OPERATIONS(Selector_Schema) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////// - // Abstract base class for simple selectors. - //////////////////////////////////////////// - class Simple_Selector : public Selector { - ADD_CONSTREF(std::string, ns) - ADD_CONSTREF(std::string, name) - ADD_PROPERTY(Simple_Type, simple_type) - ADD_PROPERTY(bool, has_ns) - public: - 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) { - has_ns_ = true; - ns_ = n.substr(0, pos); - name_ = n.substr(pos + 1); - } - } - Simple_Selector(const Simple_Selector* ptr) - : Selector(ptr), - ns_(ptr->ns_), - name_(ptr->name_), - has_ns_(ptr->has_ns_) - { simple_type(SIMPLE); } - std::string ns_name() const - { - std::string name(""); - if (has_ns_) - name += ns_ + "|"; - return name + name_; - } - virtual size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, std::hash()(SELECTOR)); - hash_combine(hash_, std::hash()(simple_type())); - hash_combine(hash_, std::hash()(ns())); - hash_combine(hash_, std::hash()(name())); - } - return hash_; - } - // namespace compare functions - bool is_ns_eq(const Simple_Selector& r) const; - // namespace query functions - bool is_universal_ns() const - { - return has_ns_ && ns_ == "*"; - } - bool has_universal_ns() const - { - return !has_ns_ || ns_ == "*"; - } - bool is_empty_ns() const - { - return !has_ns_ || ns_ == ""; - } - bool has_empty_ns() const - { - return has_ns_ && ns_ == ""; - } - bool has_qualified_ns() const - { - return has_ns_ && ns_ != "" && ns_ != "*"; - } - // name query functions - virtual bool is_universal() const - { - return name_ == "*"; - } - - virtual bool has_placeholder() { - return false; - } - - virtual ~Simple_Selector() = 0; - virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); - virtual bool is_pseudo_element() const { return false; } - - virtual bool is_superselector_of(Compound_Selector_Ptr_Const sub) const { return false; } - - bool operator==(const Selector& rhs) const final override; - 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() { } - - - ////////////////////////////////// - // The Parent Selector Expression. - ////////////////////////////////// - // parent selectors can occur in selectors but also - // inside strings in declarations (Compound_Selector). - // only one simple parent selector means the first case. - class Parent_Selector final : public Simple_Selector { - ADD_PROPERTY(bool, real) - public: - Parent_Selector(ParserState pstate, bool r = true) - : Simple_Selector(pstate, "&"), real_(r) - { /* has_reference(true); */ } - Parent_Selector(const Parent_Selector* ptr) - : Simple_Selector(ptr), real_(ptr->real_) - { /* has_reference(true); */ } - 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(); }; - unsigned long specificity() const override - { - return 0; - } - int unification_order() const override - { - throw std::runtime_error("unification_order for Parent_Selector is undefined"); - } - std::string type() const override { return "selector"; } - static std::string type_name() { return "selector"; } - ATTACH_AST_OPERATIONS(Parent_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - ////////////////////////////////// // The Parent Reference Expression. ////////////////////////////////// @@ -2402,620 +2172,14 @@ namespace Sass { ATTACH_CRTP_PERFORM_METHODS() }; - ///////////////////////////////////////////////////////////////////////// - // Placeholder selectors (e.g., "%foo") for use in extend-only selectors. - ///////////////////////////////////////////////////////////////////////// - class Placeholder_Selector final : public Simple_Selector { - public: - Placeholder_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { } - Placeholder_Selector(const Placeholder_Selector* ptr) - : Simple_Selector(ptr) - { } - unsigned long specificity() const override - { - return Constants::Specificity_Base; - } - int unification_order() const override - { - return Constants::UnificationOrder_Placeholder; - } - bool has_placeholder() override { - return true; - } - virtual ~Placeholder_Selector() {}; - ATTACH_AST_OPERATIONS(Placeholder_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ///////////////////////////////////////////////////////////////////// - // Element selectors (and the universal selector) -- e.g., div, span, *. - ///////////////////////////////////////////////////////////////////// - class Element_Selector final : public Simple_Selector { - public: - Element_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { } - Element_Selector(const Element_Selector* ptr) - : Simple_Selector(ptr) - { } - unsigned long specificity() const override - { - if (name() == "*") return 0; - else return Constants::Specificity_Element; - } - int unification_order() const override - { - return Constants::UnificationOrder_Element; - } - 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 Element_Selector& rhs) const; - ATTACH_AST_OPERATIONS(Element_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////// - // Class selectors -- i.e., .foo. - //////////////////////////////////////////////// - class Class_Selector final : public Simple_Selector { - public: - Class_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { } - Class_Selector(const Class_Selector* ptr) - : Simple_Selector(ptr) - { } - unsigned long specificity() const override - { - return Constants::Specificity_Class; - } - int unification_order() const override - { - return Constants::UnificationOrder_Class; - } - Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; - ATTACH_AST_OPERATIONS(Class_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////// - // ID selectors -- i.e., #foo. - //////////////////////////////////////////////// - class Id_Selector final : public Simple_Selector { - public: - Id_Selector(ParserState pstate, std::string n) - : Simple_Selector(pstate, n) - { } - Id_Selector(const Id_Selector* ptr) - : Simple_Selector(ptr) - { } - unsigned long specificity() const override - { - return Constants::Specificity_ID; - } - int unification_order() const override - { - return Constants::UnificationOrder_Id; - } - Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; - ATTACH_AST_OPERATIONS(Id_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - /////////////////////////////////////////////////// - // Attribute selectors -- e.g., [src*=".jpg"], etc. - /////////////////////////////////////////////////// - class Attribute_Selector final : public Simple_Selector { - ADD_CONSTREF(std::string, matcher) - // this cannot be changed to obj atm!!!!!!????!!!!!!! - ADD_PROPERTY(String_Obj, value) // might be interpolated - ADD_PROPERTY(char, modifier); - 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); } - Attribute_Selector(const Attribute_Selector* ptr) - : Simple_Selector(ptr), - matcher_(ptr->matcher_), - value_(ptr->value_), - modifier_(ptr->modifier_) - { simple_type(ATTR_SEL); } - size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, Simple_Selector::hash()); - hash_combine(hash_, std::hash()(matcher())); - if (value_) hash_combine(hash_, value_->hash()); - } - return hash_; - } - unsigned long specificity() const override - { - return Constants::Specificity_Attr; - } - int unification_order() const override - { - 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 Attribute_Selector& rhs) const; - ATTACH_AST_OPERATIONS(Attribute_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ////////////////////////////////////////////////////////////////// - // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc. - ////////////////////////////////////////////////////////////////// - /* '::' starts a pseudo-element, ':' a pseudo-class */ - /* Except :first-line, :first-letter, :before and :after */ - /* Note that pseudo-elements are restricted to one per selector */ - /* and occur only in the last simple_selector_sequence. */ - inline bool is_pseudo_class_element(const std::string& name) - { - return name == ":before" || - name == ":after" || - name == ":first-line" || - name == ":first-letter"; - } - - // Pseudo Selector cannot have any namespace? - class Pseudo_Selector final : public Simple_Selector { - ADD_PROPERTY(String_Obj, expression) - public: - Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr = {}) - : Simple_Selector(pstate, n), expression_(expr) - { simple_type(PSEUDO_SEL); } - Pseudo_Selector(const Pseudo_Selector* ptr) - : Simple_Selector(ptr), expression_(ptr->expression_) - { simple_type(PSEUDO_SEL); } - - // A pseudo-element is made of two colons (::) followed by the name. - // The `::` notation is introduced by the current document in order to - // establish a discrimination between pseudo-classes and pseudo-elements. - // For compatibility with existing style sheets, user agents must also - // accept the previous one-colon notation for pseudo-elements introduced - // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and - // :after). This compatibility is not allowed for the new pseudo-elements - // introduced in this specification. - bool is_pseudo_element() const override - { - return (name_[0] == ':' && name_[1] == ':') - || is_pseudo_class_element(name_); - } - size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, Simple_Selector::hash()); - if (expression_) hash_combine(hash_, expression_->hash()); - } - return hash_; - } - unsigned long specificity() const override - { - if (is_pseudo_element()) - return Constants::Specificity_Element; - return Constants::Specificity_Pseudo; - } - int unification_order() const override - { - if (is_pseudo_element()) - 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 Pseudo_Selector& rhs) const; - Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; - ATTACH_AST_OPERATIONS(Pseudo_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - ///////////////////////////////////////////////// - // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir) - ///////////////////////////////////////////////// - class Wrapped_Selector final : public Simple_Selector { - ADD_PROPERTY(Selector_List_Obj, selector) - public: - Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel) - : Simple_Selector(pstate, n), selector_(sel) - { simple_type(WRAPPED_SEL); } - Wrapped_Selector(const Wrapped_Selector* ptr) - : Simple_Selector(ptr), selector_(ptr->selector_) - { simple_type(WRAPPED_SEL); } - using Simple_Selector::is_superselector_of; - bool is_superselector_of(Wrapped_Selector_Ptr_Const sub) const; - // Selectors inside the negation pseudo-class are counted like any - // other, but the negation itself does not count as a pseudo-class. - size_t hash() const override; - bool has_parent_ref() const override; - bool has_real_parent_ref() const override; - unsigned long specificity() const override; - int unification_order() const override - { - 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 Wrapped_Selector& rhs) const; - void cloneChildren() override; - ATTACH_AST_OPERATIONS(Wrapped_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////////////////////////////////// - // Simple selector sequences. Maintains flags indicating whether it contains - // any parent references or placeholders, to simplify expansion. - //////////////////////////////////////////////////////////////////////////// - class Compound_Selector final : public Selector, public Vectorized { - private: - ComplexSelectorSet sources_; - ADD_PROPERTY(bool, extended); - ADD_PROPERTY(bool, has_parent_reference); - protected: - void adjust_after_pushing(Simple_Selector_Obj s) override - { - // if (s->has_reference()) has_reference(true); - // if (s->has_placeholder()) has_placeholder(true); - } - public: - Compound_Selector(ParserState pstate, size_t s = 0) - : Selector(pstate), - Vectorized(s), - extended_(false), - has_parent_reference_(false) - { } - Compound_Selector(const Compound_Selector* ptr) - : Selector(ptr), - Vectorized(*ptr), - extended_(ptr->extended_), - has_parent_reference_(ptr->has_parent_reference_) - { } - bool contains_placeholder() { - for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->has_placeholder()) return true; - } - return false; - }; - - void append(Simple_Selector_Obj element) override; - - bool is_universal() const - { - return length() == 1 && (*this)[0]->is_universal(); - } - - Complex_Selector_Obj to_complex(); - Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs); - // virtual Placeholder_Selector_Ptr find_placeholder(); - bool has_parent_ref() const override; - bool has_real_parent_ref() const override; - Simple_Selector_Ptr base() const { - if (length() == 0) return 0; - // ToDo: why is this needed? - if (Cast((*this)[0])) - return (*this)[0]; - return 0; - } - bool is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapped = "") const; - bool is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapped = "") const; - bool is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapped = "") const; - size_t hash() const override - { - if (Selector::hash_ == 0) { - hash_combine(Selector::hash_, std::hash()(SELECTOR)); - if (length()) hash_combine(Selector::hash_, Vectorized::hash()); - } - return Selector::hash_; - } - unsigned long specificity() const override - { - int sum = 0; - for (size_t i = 0, L = length(); i < L; ++i) - { sum += (*this)[i]->specificity(); } - return sum; - } - int unification_order() const override - { - throw std::runtime_error("unification_order for Compound_Selector is undefined"); - } - - bool has_placeholder() - { - if (length() == 0) return false; - if (Simple_Selector_Obj ss = elements().front()) { - if (ss->has_placeholder()) return true; - } - return false; - } - - bool is_empty_reference() - { - return length() == 1 && - Cast((*this)[0]); - } - - 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 Compound_Selector& rhs) const; - bool operator==(const Compound_Selector& rhs) const; - inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); } - - ComplexSelectorSet& sources() { return sources_; } - void clearSources() { sources_.clear(); } - void mergeSources(ComplexSelectorSet& sources); - - Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs); - void cloneChildren() override; - ATTACH_AST_OPERATIONS(Compound_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - //////////////////////////////////////////////////////////////////////////// - // General selectors -- i.e., simple sequences combined with one of the four - // CSS selector combinators (">", "+", "~", and whitespace). Essentially a - // linked list. - //////////////////////////////////////////////////////////////////////////// - class Complex_Selector final : public Selector { - public: - enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE }; - private: - HASH_CONSTREF(Combinator, combinator) - HASH_PROPERTY(Compound_Selector_Obj, head) - HASH_PROPERTY(Complex_Selector_Obj, tail) - HASH_PROPERTY(String_Obj, reference); - public: - bool contains_placeholder() { - if (head() && head()->contains_placeholder()) return true; - if (tail() && tail()->contains_placeholder()) return true; - return false; - }; - Complex_Selector(ParserState pstate, - Combinator c = ANCESTOR_OF, - Compound_Selector_Obj h = {}, - Complex_Selector_Obj t = {}, - String_Obj r = {}) - : Selector(pstate), - combinator_(c), - head_(h), tail_(t), - reference_(r) - {} - Complex_Selector(const Complex_Selector* ptr) - : Selector(ptr), - combinator_(ptr->combinator_), - head_(ptr->head_), tail_(ptr->tail_), - reference_(ptr->reference_) - {}; - bool has_parent_ref() const override; - bool has_real_parent_ref() const override; - - Complex_Selector_Obj skip_empty_reference() - { - if ((!head_ || !head_->length() || head_->is_empty_reference()) && - combinator() == Combinator::ANCESTOR_OF) - { - if (!tail_) return {}; - tail_->has_line_feed_ = this->has_line_feed_; - // tail_->has_line_break_ = this->has_line_break_; - return tail_->skip_empty_reference(); - } - return this; - } - - // can still have a tail - bool is_empty_ancestor() const - { - return (!head() || head()->length() == 0) && - combinator() == Combinator::ANCESTOR_OF; - } - - Selector_List_Ptr tails(Selector_List_Ptr tails); - - // front returns the first real tail - // skips over parent and empty ones - Complex_Selector_Ptr_Const first() const; - Complex_Selector_Ptr mutable_first(); - - // last returns the last real tail - Complex_Selector_Ptr_Const last() const; - Complex_Selector_Ptr mutable_last(); - - size_t length() const; - Selector_List_Ptr resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent = true); - bool is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapping = "") const; - bool is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapping = "") const; - bool is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping = "") const; - Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs); - Combinator clear_innermost(); - void append(Complex_Selector_Obj, Backtraces& traces); - void set_innermost(Complex_Selector_Obj, Combinator); - size_t hash() const override - { - if (hash_ == 0) { - hash_combine(hash_, std::hash()(SELECTOR)); - hash_combine(hash_, std::hash()(combinator_)); - if (head_) hash_combine(hash_, head_->hash()); - if (tail_) hash_combine(hash_, tail_->hash()); - } - return hash_; - } - unsigned long specificity() const override - { - int sum = 0; - if (head()) sum += head()->specificity(); - if (tail()) sum += tail()->specificity(); - return sum; - } - int unification_order() const override - { - throw std::runtime_error("unification_order for Complex_Selector is undefined"); - } - void set_media_block(Media_Block_Ptr mb) override { - media_block(mb); - if (tail_) tail_->set_media_block(mb); - if (head_) head_->set_media_block(mb); - } - bool has_placeholder() { - if (head_ && head_->has_placeholder()) return true; - if (tail_ && tail_->has_placeholder()) return true; - 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 Complex_Selector& rhs) const; - bool operator==(const Complex_Selector& rhs) const; - inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); } - const ComplexSelectorSet sources() - { - //s = Set.new - //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)} - //s - - ComplexSelectorSet srcs; - - Compound_Selector_Obj pHead = head(); - Complex_Selector_Obj pTail = tail(); - - if (pHead) { - const ComplexSelectorSet& headSources = pHead->sources(); - srcs.insert(headSources.begin(), headSources.end()); - } - - if (pTail) { - const ComplexSelectorSet& tailSources = pTail->sources(); - srcs.insert(tailSources.begin(), tailSources.end()); - } - - return srcs; - } - void addSources(ComplexSelectorSet& sources) { - // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m} - Complex_Selector_Ptr pIter = this; - while (pIter) { - Compound_Selector_Ptr pHead = pIter->head(); - - if (pHead) { - pHead->mergeSources(sources); - } - - pIter = pIter->tail(); - } - } - void clearSources() { - Complex_Selector_Ptr pIter = this; - while (pIter) { - Compound_Selector_Ptr pHead = pIter->head(); +} - if (pHead) { - pHead->clearSources(); - } +#include "ast_selectors.hpp" - pIter = pIter->tail(); - } - } - - void cloneChildren() override; - ATTACH_AST_OPERATIONS(Complex_Selector) - ATTACH_CRTP_PERFORM_METHODS() - }; - - /////////////////////////////////// - // Comma-separated selector groups. - /////////////////////////////////// - class Selector_List final : public Selector, public Vectorized { - ADD_PROPERTY(Selector_Schema_Obj, schema) - ADD_CONSTREF(std::vector, wspace) - protected: - void adjust_after_pushing(Complex_Selector_Obj c) override; - public: - Selector_List(ParserState pstate, size_t s = 0) - : Selector(pstate), - Vectorized(s), - schema_({}), - wspace_(0) - { } - Selector_List(const Selector_List* ptr) - : Selector(ptr), - Vectorized(*ptr), - schema_(ptr->schema_), - wspace_(ptr->wspace_) - { } - std::string type() const override { return "list"; } - // remove parent selector references - // basically unwraps parsed selectors - bool has_parent_ref() const override; - bool has_real_parent_ref() const override; - void remove_parent_selectors(); - Selector_List_Ptr resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent = true); - bool is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapping = "") const; - bool is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapping = "") const; - bool is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping = "") const; - Selector_List_Ptr unify_with(Selector_List_Ptr); - void populate_extends(Selector_List_Obj, Subset_Map&); - Selector_List_Obj eval(Eval& eval); - size_t hash() const override - { - if (Selector::hash_ == 0) { - hash_combine(Selector::hash_, std::hash()(SELECTOR)); - hash_combine(Selector::hash_, Vectorized::hash()); - } - return Selector::hash_; - } - unsigned long specificity() const override - { - unsigned long sum = 0; - unsigned long specificity; - for (size_t i = 0, L = length(); i < L; ++i) - { - specificity = (*this)[i]->specificity(); - if (sum < specificity) sum = specificity; - } - return sum; - } - int unification_order() const override - { - throw std::runtime_error("unification_order for Selector_List is undefined"); - } - void set_media_block(Media_Block_Ptr mb) override { - media_block(mb); - for (Complex_Selector_Obj cs : elements()) { - cs->set_media_block(mb); - } - } - bool has_placeholder() { - for (Complex_Selector_Obj cs : elements()) { - if (cs->has_placeholder()) return true; - } - 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; - // Selector Lists can be compared to comma lists - bool operator==(const Expression& rhs) const override; - void cloneChildren() override; - ATTACH_AST_OPERATIONS(Selector_List) - ATTACH_CRTP_PERFORM_METHODS() - }; +#ifdef __clang__ - // compare function for sorting and probably other other uses - struct cmp_complex_selector { inline bool operator() (const Complex_Selector_Obj l, const Complex_Selector_Obj r) { return (*l < *r); } }; - struct cmp_compound_selector { inline bool operator() (const Compound_Selector_Obj l, const Compound_Selector_Obj r) { return (*l < *r); } }; - struct cmp_simple_selector { inline bool operator() (const Simple_Selector_Obj l, const Simple_Selector_Obj r) { return (*l < *r); } }; +#pragma clang diagnostic pop -} +#endif #endif diff --git a/src/ast_sel_cmp.cpp b/src/ast_sel_cmp.cpp new file mode 100644 index 0000000000..4c3485a6cf --- /dev/null +++ b/src/ast_sel_cmp.cpp @@ -0,0 +1,534 @@ +#include "sass.hpp" +#include "ast.hpp" +#include "context.hpp" +#include "node.hpp" +#include "eval.hpp" +#include "extend.hpp" +#include "emitter.hpp" +#include "color_maps.hpp" +#include "ast_fwd_decl.hpp" +#include +#include +#include +#include +#include +#include +#include + +#include "ast_selectors.hpp" + +namespace Sass { + + 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]; + if (!l && !r) return false; + else if (!r) return false; + else if (!l) return true; + else if (*l != *r) + { return *l < *r; } + } + // just compare the length now + return length() < rhs.length(); + } + + bool Compound_Selector::operator== (const Compound_Selector& rhs) const + { + if (&rhs == this) return true; + if (rhs.length() != length()) return false; + std::unordered_set lhs_set; + lhs_set.reserve(length()); + for (const Simple_Selector_Obj &element : elements()) { + lhs_set.insert(element.ptr()); + } + // there is no break?! + for (const Simple_Selector_Obj &element : rhs.elements()) { + if (lhs_set.find(element.ptr()) == lhs_set.end()) return false; + } + return true; + } + + bool Complex_Selector::operator< (const Complex_Selector& rhs) const + { + // const iterators for tails + Complex_Selector_Ptr_Const l = this; + Complex_Selector_Ptr_Const r = &rhs; + Compound_Selector_Ptr l_h = NULL; + Compound_Selector_Ptr r_h = NULL; + if (l) l_h = l->head(); + if (r) r_h = r->head(); + // 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 for valid selectors + if (!l) return !!r; + if (!r) return false; + // both are null + else if (!l_h && !r_h) + { + // check combinator after heads + if (l->combinator() != r->combinator()) + { return l->combinator() < r->combinator(); } + // advance to next tails + l = l->tail(); + r = r->tail(); + // fetch the next headers + l_h = NULL; r_h = NULL; + if (l) l_h = l->head(); + if (r) r_h = r->head(); + } + // one side is null + else if (!r_h) return true; + else if (!l_h) return false; + // heads ok and equal + else if (*l_h == *r_h) + { + // check combinator after heads + if (l->combinator() != r->combinator()) + { return l->combinator() < r->combinator(); } + // advance to next tails + l = l->tail(); + r = r->tail(); + // fetch the next headers + l_h = NULL; r_h = NULL; + if (l) l_h = l->head(); + if (r) r_h = r->head(); + } + // heads are not equal + else return *l_h < *r_h; + } + } + + bool Complex_Selector::operator== (const Complex_Selector& rhs) const + { + // const iterators for tails + Complex_Selector_Ptr_Const l = this; + Complex_Selector_Ptr_Const r = &rhs; + Compound_Selector_Ptr l_h = NULL; + Compound_Selector_Ptr r_h = NULL; + if (l) l_h = l->head(); + if (r) r_h = r->head(); + // 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; + // both are null + if (!l_h && !r_h) + { + // check combinator after heads + if (l->combinator() != r->combinator()) + { return l->combinator() < r->combinator(); } + // advance to next tails + l = l->tail(); + r = r->tail(); + // fetch the next heads + l_h = NULL; r_h = NULL; + if (l) l_h = l->head(); + if (r) r_h = r->head(); + } + // equals if other head is empty + else if ((!l_h && !r_h) || + (!l_h && r_h->empty()) || + (!r_h && l_h->empty()) || + (l_h && r_h && *l_h == *r_h)) + { + // check combinator after heads + if (l->combinator() != r->combinator()) + { return l->combinator() == r->combinator(); } + // advance to next tails + l = l->tail(); + r = r->tail(); + // fetch the next heads + l_h = NULL; r_h = NULL; + if (l) l_h = l->head(); + if (r) r_h = r->head(); + } + // abort + else break; + } + // unreachable + return false; + } + + bool Complex_Selector::operator== (const 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"); + } + + + bool Complex_Selector::operator< (const 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"); + } + + bool Compound_Selector::operator== (const 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"); + } + + bool Compound_Selector::operator< (const 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"); + } + + bool Selector_Schema::operator== (const 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"); + } + + bool Selector_Schema::operator< (const 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"); + } + + bool Simple_Selector::operator== (const Selector& rhs) const + { + if (Simple_Selector_Ptr_Const sp = Cast(&rhs)) return *this == *sp; + return false; + } + + bool Simple_Selector::operator< (const Selector& rhs) const + { + if (Simple_Selector_Ptr_Const sp = Cast(&rhs)) return *this < *sp; + return false; + } + + bool Simple_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; } + else if (name_ == rhs.name_) + { return is_ns_eq(rhs); } + else return false; + } + + bool Simple_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_; + } + + bool Selector_List::operator== (const 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; + } + + // Selector lists can be compared to comma lists + bool Selector_List::operator== (const Expression& 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; + } + + 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 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?! + } + + bool Selector_List::operator< (const Selector& rhs) const + { + if (Selector_List_Ptr_Const sp = Cast(&rhs)) return *this < *sp; + return false; + } + + 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 Attribute_Selector::operator< (const Attribute_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(); } + } + + bool Attribute_Selector::operator< (const Simple_Selector& rhs) const + { + if (Attribute_Selector_Ptr_Const w = Cast(&rhs)) + { + return *this < *w; + } + if (is_ns_eq(rhs)) + { return name() < rhs.name(); } + return ns() < rhs.ns(); + } + + bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const + { + // get optional value state + bool no_lhs_val = value().isNull(); + bool no_rhs_val = rhs.value().isNull(); + // both are null, therefore equal + if (no_lhs_val && no_rhs_val) { + return (name() == rhs.name()) + && (matcher() == rhs.matcher()) + && (is_ns_eq(rhs)); + } + // both are defined, evaluate + if (no_lhs_val == no_rhs_val) { + return (name() == rhs.name()) + && (matcher() == rhs.matcher()) + && (is_ns_eq(rhs)) + && (*value() == *rhs.value()); + } + // not equal + return false; + + } + + bool Attribute_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; + } + return false; + } + + bool Element_Selector::operator< (const Element_Selector& rhs) const + { + if (is_ns_eq(rhs)) + { return name() < rhs.name(); } + return ns() < rhs.ns(); + } + + bool Element_Selector::operator< (const Simple_Selector& rhs) const + { + if (Element_Selector_Ptr_Const w = Cast(&rhs)) + { + return *this < *w; + } + if (is_ns_eq(rhs)) + { return name() < rhs.name(); } + return ns() < rhs.ns(); + } + + bool Element_Selector::operator== (const Element_Selector& rhs) const + { + return is_ns_eq(rhs) && + name() == rhs.name(); + } + + bool Element_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; + } + return false; + } + + bool Pseudo_Selector::operator== (const Pseudo_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(); + } + else return false; + } + + bool Pseudo_Selector::operator== (const Simple_Selector& rhs) const + { + if (Pseudo_Selector_Ptr_Const w = Cast(&rhs)) + { + return *this == *w; + } + return is_ns_eq(rhs) && + name() == rhs.name(); + } + + bool Pseudo_Selector::operator< (const Pseudo_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(); } + return ns() < rhs.ns(); + } + + bool Pseudo_Selector::operator< (const Simple_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(); + } + + bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const + { + if (is_ns_eq(rhs) && name() == rhs.name()) + { return *(selector()) == *(rhs.selector()); } + else return false; + } + + bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const + { + if (Wrapped_Selector_Ptr_Const w = Cast(&rhs)) + { + return *this == *w; + } + return is_ns_eq(rhs) && + name() == rhs.name(); + } + + 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)) + { return name() < rhs.name(); } + return ns() < rhs.ns(); + } + + bool Wrapped_Selector::operator< (const Simple_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(); + } + +} \ No newline at end of file diff --git a/src/ast_sel_unify.cpp b/src/ast_sel_unify.cpp new file mode 100644 index 0000000000..30e1b6e068 --- /dev/null +++ b/src/ast_sel_unify.cpp @@ -0,0 +1,249 @@ +#include "sass.hpp" +#include "ast.hpp" +#include "context.hpp" +#include "node.hpp" +#include "eval.hpp" +#include "extend.hpp" +#include "emitter.hpp" +#include "color_maps.hpp" +#include "ast_fwd_decl.hpp" +#include +#include +#include +#include +#include +#include +#include + +#include "ast_selectors.hpp" + +namespace Sass { + + Compound_Selector_Ptr Compound_Selector::unify_with(Compound_Selector_Ptr rhs) + { + if (empty()) return rhs; + Compound_Selector_Obj unified = SASS_MEMORY_COPY(rhs); + for (size_t i = 0, L = length(); i < L; ++i) + { + if (unified.isNull()) break; + unified = at(i)->unify_with(unified); + } + return unified.detach(); + } + + Compound_Selector_Ptr Simple_Selector::unify_with(Compound_Selector_Ptr rhs) + { + const size_t rsize = rhs->length(); + for (size_t i = 0; i < rsize; ++i) + { if (*this == *rhs->at(i)) return rhs; } + const int lhs_order = this->unification_order(); + size_t i = rsize; + while (i > 0 && lhs_order < rhs->at(i - 1)->unification_order()) --i; + rhs->elements().insert(rhs->elements().begin() + i, this); + return rhs; + } + + + Simple_Selector_Ptr Element_Selector::unify_with(Simple_Selector_Ptr rhs) + { + // check if ns can be extended + // true for no ns or universal + if (has_universal_ns()) + { + // but dont extend with universal + // true for valid ns and universal + if (!rhs->is_universal_ns()) + { + // overwrite the name if star is given as name + if (this->name() == "*") { this->name(rhs->name()); } + // now overwrite the namespace name and flag + this->ns(rhs->ns()); this->has_ns(rhs->has_ns()); + // return copy + return this; + } + } + // namespace may changed, check the name now + // overwrite star (but not with another star) + if (name() == "*" && rhs->name() != "*") + { + // simply set the new name + this->name(rhs->name()); + // return copy + return this; + } + // return original + return this; + } + + Compound_Selector_Ptr Element_Selector::unify_with(Compound_Selector_Ptr rhs) + { + // TODO: handle namespaces + + // if the rhs is empty, just return a copy of this + if (rhs->length() == 0) { + rhs->append(this); + return rhs; + } + + Simple_Selector_Ptr rhs_0 = rhs->at(0); + // otherwise, this is a tag name + if (name() == "*") + { + if (typeid(*rhs_0) == typeid(Element_Selector)) + { + // if rhs is universal, just return this tagname + rhs's qualifiers + Element_Selector_Ptr ts = Cast(rhs_0); + rhs->at(0) = this->unify_with(ts); + return rhs; + } + else if (Cast(rhs_0) || Cast(rhs_0)) { + // qualifier is `.class`, so we can prefix with `ns|*.class` + if (has_ns() && !rhs_0->has_ns()) { + if (ns() != "*") rhs->elements().insert(rhs->begin(), this); + } + return rhs; + } + + return rhs; + } + + if (typeid(*rhs_0) == typeid(Element_Selector)) + { + // if rhs is universal, just return this tagname + rhs's qualifiers + if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0; + // otherwise create new compound and unify first simple selector + rhs->at(0) = this->unify_with(rhs_0); + return rhs; + + } + // else it's a tag name and a bunch of qualifiers -- just append them + if (name() != "*") rhs->elements().insert(rhs->begin(), this); + return rhs; + } + + Compound_Selector_Ptr Class_Selector::unify_with(Compound_Selector_Ptr rhs) + { + rhs->has_line_break(has_line_break()); + return Simple_Selector::unify_with(rhs); + } + + Compound_Selector_Ptr Id_Selector::unify_with(Compound_Selector_Ptr rhs) + { + for (size_t i = 0, L = rhs->length(); i < L; ++i) + { + if (Id_Selector_Ptr sel = Cast(rhs->at(i))) { + if (sel->name() != name()) return 0; + } + } + rhs->has_line_break(has_line_break()); + return Simple_Selector::unify_with(rhs); + } + + Compound_Selector_Ptr Pseudo_Selector::unify_with(Compound_Selector_Ptr rhs) + { + if (is_pseudo_element()) + { + for (size_t i = 0, L = rhs->length(); i < L; ++i) + { + if (Pseudo_Selector_Ptr sel = Cast(rhs->at(i))) { + if (sel->is_pseudo_element() && sel->name() != name()) return 0; + } + } + } + return Simple_Selector::unify_with(rhs); + } + + Selector_List_Ptr Complex_Selector::unify_with(Complex_Selector_Ptr other) + { + + // get last tails (on the right side) + Complex_Selector_Obj l_last = this->mutable_last(); + Complex_Selector_Obj r_last = other->mutable_last(); + + // check valid pointers (assertion) + SASS_ASSERT(l_last, "lhs is null"); + SASS_ASSERT(r_last, "rhs is null"); + + // Not sure about this check, but closest way I could check + // was to see if this is a ruby 'SimpleSequence' equivalent. + // It seems to do the job correctly as some specs react to this + if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0; + if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0; + + // get the headers for the last tails + Compound_Selector_Obj l_last_head = l_last->head(); + Compound_Selector_Obj r_last_head = r_last->head(); + + // check valid head pointers (assertion) + SASS_ASSERT(l_last_head, "lhs head is null"); + SASS_ASSERT(r_last_head, "rhs head is null"); + + // get the unification of the last compound selectors + Compound_Selector_Obj unified = r_last_head->unify_with(l_last_head); + + // abort if we could not unify heads + if (unified == 0) return 0; + + // check for universal (star: `*`) selector + bool is_universal = l_last_head->is_universal() || + r_last_head->is_universal(); + + if (is_universal) + { + // move the head + l_last->head({}); + r_last->head(unified); + } + + // create nodes from both selectors + Node lhsNode = complexSelectorToNode(this); + Node rhsNode = complexSelectorToNode(other); + + // overwrite universal base + if (!is_universal) + { + // create some temporaries to convert to node + Complex_Selector_Obj fake = unified->to_complex(); + Node unified_node = complexSelectorToNode(fake); + // add to permutate the list? + rhsNode.plus(unified_node); + } + + // do some magic we inherit from node and extend + Node node = subweave(lhsNode, rhsNode); + Selector_List_Obj result = SASS_MEMORY_NEW(Selector_List, pstate()); + NodeDequePtr col = node.collection(); // move from collection to list + for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++) + { result->append(nodeToComplexSelector(Node::naiveTrim(*it))); } + + // only return if list has some entries + return result->length() ? result.detach() : 0; + + } + + Selector_List_Ptr Selector_List::unify_with(Selector_List_Ptr rhs) { + std::vector unified_complex_selectors; + // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` + for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { + Complex_Selector_Obj seq1 = (*this)[lhs_i]; + for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { + Complex_Selector_Ptr seq2 = rhs->at(rhs_i); + + Selector_List_Obj result = seq1->unify_with(seq2); + if( result ) { + for(size_t i = 0, L = result->length(); i < L; ++i) { + unified_complex_selectors.push_back( (*result)[i] ); + } + } + } + } + + // Creates the final Selector_List by combining all the complex selectors + Selector_List_Ptr final_result = SASS_MEMORY_NEW(Selector_List, pstate()); + for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) { + final_result->append(*itr); + } + return final_result; + } + +} diff --git a/src/ast_selectors.cpp b/src/ast_selectors.cpp new file mode 100644 index 0000000000..b1b7840eec --- /dev/null +++ b/src/ast_selectors.cpp @@ -0,0 +1,909 @@ +#include "sass.hpp" +#include "ast.hpp" +#include "context.hpp" +#include "node.hpp" +#include "eval.hpp" +#include "extend.hpp" +#include "emitter.hpp" +#include "color_maps.hpp" +#include "ast_fwd_decl.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace Sass { + + bool Wrapped_Selector::find ( bool (*f)(AST_Node_Obj) ) + { + // check children first + if (selector_) { + if (selector_->find(f)) return true; + } + // execute last + return f(this); + } + + bool Selector_List::find ( bool (*f)(AST_Node_Obj) ) + { + // check children first + for (Complex_Selector_Obj sel : elements()) { + if (sel->find(f)) return true; + } + // execute last + return f(this); + } + + bool Compound_Selector::find ( bool (*f)(AST_Node_Obj) ) + { + // check children first + for (Simple_Selector_Obj sel : elements()) { + if (sel->find(f)) return true; + } + // execute last + return f(this); + } + + bool Complex_Selector::find ( bool (*f)(AST_Node_Obj) ) + { + // check children first + if (head_ && head_->find(f)) return true; + if (tail_ && tail_->find(f)) return true; + // execute last + return f(this); + } + + bool Simple_Selector::is_ns_eq(const Simple_Selector& r) const + { + // https://github.com/sass/sass/issues/2229 + if ((has_ns_ == r.has_ns_) || + (has_ns_ && ns_.empty()) || + (r.has_ns_ && r.ns_.empty()) + ) { + if (ns_.empty() && r.ns() == "*") return false; + else if (r.ns().empty() && ns() == "*") return false; + else return ns() == r.ns(); + } + return false; + } + + bool Compound_Selector::has_parent_ref() const + { + for (Simple_Selector_Obj s : *this) { + if (s && s->has_parent_ref()) return true; + } + return false; + } + + bool Compound_Selector::has_real_parent_ref() const + { + for (Simple_Selector_Obj s : *this) { + if (s && s->has_real_parent_ref()) return true; + } + return false; + } + + bool Complex_Selector::has_parent_ref() const + { + return (head() && head()->has_parent_ref()) || + (tail() && tail()->has_parent_ref()); + } + + bool Complex_Selector::has_real_parent_ref() const + { + return (head() && head()->has_real_parent_ref()) || + (tail() && tail()->has_real_parent_ref()); + } + + bool Wrapped_Selector::is_superselector_of(Wrapped_Selector_Ptr_Const sub) const + { + if (this->name() != sub->name()) return false; + if (this->name() == ":current") return false; + if (Selector_List_Obj rhs_list = Cast(sub->selector())) { + if (Selector_List_Obj lhs_list = Cast(selector())) { + return lhs_list->is_superselector_of(rhs_list); + } + } + coreError("is_superselector expected a Selector_List", sub->pstate()); + return false; + } + + bool Compound_Selector::is_superselector_of(Selector_List_Ptr_Const rhs, std::string wrapped) const + { + for (Complex_Selector_Obj item : rhs->elements()) { + if (is_superselector_of(item, wrapped)) return true; + } + return false; + } + + bool Compound_Selector::is_superselector_of(Complex_Selector_Ptr_Const rhs, std::string wrapped) const + { + if (rhs->head()) return is_superselector_of(rhs->head(), wrapped); + return false; + } + + bool Compound_Selector::is_superselector_of(Compound_Selector_Ptr_Const rhs, std::string wrapping) const + { + Compound_Selector_Ptr_Const lhs = this; + Simple_Selector_Ptr lbase = lhs->base(); + Simple_Selector_Ptr rbase = rhs->base(); + + // Check if pseudo-elements are the same between the selectors + + std::set lpsuedoset, rpsuedoset; + for (size_t i = 0, L = length(); i < L; ++i) + { + if ((*this)[i]->is_pseudo_element()) { + std::string pseudo((*this)[i]->to_string()); + pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving + lpsuedoset.insert(pseudo); + } + } + for (size_t i = 0, L = rhs->length(); i < L; ++i) + { + if ((*rhs)[i]->is_pseudo_element()) { + std::string pseudo((*rhs)[i]->to_string()); + pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving + rpsuedoset.insert(pseudo); + } + } + if (lpsuedoset != rpsuedoset) { + return false; + } + + // would like to replace this without stringification + // https://github.com/sass/sass/issues/2229 + // SimpleSelectorSet lset, rset; + std::set lset, rset; + + if (lbase && rbase) + { + if (lbase->to_string() == rbase->to_string()) { + for (size_t i = 1, L = length(); i < L; ++i) + { lset.insert((*this)[i]->to_string()); } + for (size_t i = 1, L = rhs->length(); i < L; ++i) + { rset.insert((*rhs)[i]->to_string()); } + return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); + } + return false; + } + + for (size_t i = 0, iL = length(); i < iL; ++i) + { + Selector_Obj wlhs = (*this)[i]; + // very special case for wrapped matches selector + if (Wrapped_Selector_Obj wrapped = Cast(wlhs)) { + if (wrapped->name() == ":not") { + if (Selector_List_Obj not_list = Cast(wrapped->selector())) { + if (not_list->is_superselector_of(rhs, wrapped->name())) return false; + } else { + throw std::runtime_error("wrapped not selector is not a list"); + } + } + if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { + wlhs = wrapped->selector(); + if (Selector_List_Obj list = Cast(wrapped->selector())) { + if (Compound_Selector_Ptr_Const comp = Cast(rhs)) { + if (!wrapping.empty() && wrapping != wrapped->name()) return false; + if (wrapping.empty() || wrapping != wrapped->name()) {; + if (list->is_superselector_of(comp, wrapped->name())) return true; + } + } + } + } + Simple_Selector_Ptr rhs_sel = NULL; + if (rhs->elements().size() > i) rhs_sel = (*rhs)[i]; + if (Wrapped_Selector_Ptr wrapped_r = Cast(rhs_sel)) { + if (wrapped->name() == wrapped_r->name()) { + if (wrapped->is_superselector_of(wrapped_r)) { + continue; + }} + } + } + // match from here on as strings + lset.insert(wlhs->to_string()); + } + + for (size_t n = 0, nL = rhs->length(); n < nL; ++n) + { + Selector_Obj r = (*rhs)[n]; + if (Wrapped_Selector_Obj wrapped = Cast(r)) { + if (wrapped->name() == ":not") { + if (Selector_List_Obj ls = Cast(wrapped->selector())) { + ls->remove_parent_selectors(); + if (is_superselector_of(ls, wrapped->name())) return false; + } + } + if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { + if (!wrapping.empty()) { + if (wrapping != wrapped->name()) return false; + } + if (Selector_List_Obj ls = Cast(wrapped->selector())) { + ls->remove_parent_selectors(); + return (is_superselector_of(ls, wrapped->name())); + } + } + } + rset.insert(r->to_string()); + } + + //for (auto l : lset) { cerr << "l: " << l << endl; } + //for (auto r : rset) { cerr << "r: " << r << endl; } + + if (lset.empty()) return true; + // return true if rset contains all the elements of lset + return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); + + } + + // create complex selector (ancestor of) from compound selector + Complex_Selector_Obj Compound_Selector::to_complex() + { + // create an intermediate complex selector + return SASS_MEMORY_NEW(Complex_Selector, + pstate(), + Complex_Selector::ANCESTOR_OF, + this, + {}); + } + + bool Complex_Selector::is_superselector_of(Compound_Selector_Ptr_Const rhs, std::string wrapping) const + { + return last()->head() && last()->head()->is_superselector_of(rhs, wrapping); + } + + bool Complex_Selector::is_superselector_of(Complex_Selector_Ptr_Const rhs, std::string wrapping) const + { + Complex_Selector_Ptr_Const lhs = this; + // check for selectors with leading or trailing combinators + if (!lhs->head() || !rhs->head()) + { return false; } + Complex_Selector_Ptr_Const l_innermost = lhs->last(); + if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF) + { return false; } + Complex_Selector_Ptr_Const r_innermost = rhs->last(); + if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF) + { return false; } + // more complex (i.e., longer) selectors are always more specific + size_t l_len = lhs->length(), r_len = rhs->length(); + if (l_len > r_len) + { return false; } + + if (l_len == 1) + { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); } + + // we have to look one tail deeper, since we cary the + // combinator around for it (which is important here) + if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) { + Complex_Selector_Obj lhs_tail = lhs->tail(); + Complex_Selector_Obj rhs_tail = rhs->tail(); + if (lhs_tail->combinator() != rhs_tail->combinator()) return false; + if (lhs_tail->head() && !rhs_tail->head()) return false; + if (!lhs_tail->head() && rhs_tail->head()) return false; + if (lhs_tail->head() && rhs_tail->head()) { + if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; + } + } + + bool found = false; + Complex_Selector_Ptr_Const marker = rhs; + for (size_t i = 0, L = rhs->length(); i < L; ++i) { + if (i == L-1) + { return false; } + if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping)) + { found = true; break; } + marker = marker->tail(); + } + if (!found) + { return false; } + + /* + Hmm, I hope I have the logic right: + + if lhs has a combinator: + if !(marker has a combinator) return false + if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false + return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost) + else if marker has a combinator: + if !(marker.combinator == ">") return false + return lhs.tail.is_superselector_of(marker.tail) + else + return lhs.tail.is_superselector_of(marker.tail) + */ + if (lhs->combinator() != Complex_Selector::ANCESTOR_OF) + { + if (marker->combinator() == Complex_Selector::ANCESTOR_OF) + { return false; } + if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator())) + { return false; } + return lhs->tail()->is_superselector_of(marker->tail()); + } + else if (marker->combinator() != Complex_Selector::ANCESTOR_OF) + { + if (marker->combinator() != Complex_Selector::PARENT_OF) + { return false; } + return lhs->tail()->is_superselector_of(marker->tail()); + } + return lhs->tail()->is_superselector_of(marker->tail()); + } + + size_t Complex_Selector::length() const + { + // TODO: make this iterative + if (!tail()) return 1; + return 1 + tail()->length(); + } + + // append another complex selector at the end + // check if we need to append some headers + // then we need to check for the combinator + // only then we can safely set the new tail + void Complex_Selector::append(Complex_Selector_Obj ss, Backtraces& traces) + { + + Complex_Selector_Obj t = ss->tail(); + Combinator c = ss->combinator(); + String_Obj r = ss->reference(); + Compound_Selector_Obj h = ss->head(); + + if (ss->has_line_feed()) has_line_feed(true); + if (ss->has_line_break()) has_line_break(true); + + // append old headers + if (h && h->length()) { + if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) { + traces.push_back(Backtrace(pstate())); + throw Exception::InvalidParent(this, traces, ss); + } else if (last()->head_ && last()->head_->length()) { + Compound_Selector_Obj rh = last()->head(); + size_t i; + size_t L = h->length(); + if (Cast(h->first())) { + if (Class_Selector_Ptr cs = Cast(rh->last())) { + Class_Selector_Ptr sqs = SASS_MEMORY_COPY(cs); + sqs->name(sqs->name() + (*h)[0]->name()); + sqs->pstate((*h)[0]->pstate()); + (*rh)[rh->length()-1] = sqs; + rh->pstate(h->pstate()); + for (i = 1; i < L; ++i) rh->append((*h)[i]); + } else if (Id_Selector_Ptr is = Cast(rh->last())) { + Id_Selector_Ptr sqs = SASS_MEMORY_COPY(is); + sqs->name(sqs->name() + (*h)[0]->name()); + sqs->pstate((*h)[0]->pstate()); + (*rh)[rh->length()-1] = sqs; + rh->pstate(h->pstate()); + for (i = 1; i < L; ++i) rh->append((*h)[i]); + } else if (Element_Selector_Ptr ts = Cast(rh->last())) { + Element_Selector_Ptr tss = SASS_MEMORY_COPY(ts); + tss->name(tss->name() + (*h)[0]->name()); + tss->pstate((*h)[0]->pstate()); + (*rh)[rh->length()-1] = tss; + rh->pstate(h->pstate()); + for (i = 1; i < L; ++i) rh->append((*h)[i]); + } else if (Placeholder_Selector_Ptr ps = Cast(rh->last())) { + Placeholder_Selector_Ptr pss = SASS_MEMORY_COPY(ps); + pss->name(pss->name() + (*h)[0]->name()); + pss->pstate((*h)[0]->pstate()); + (*rh)[rh->length()-1] = pss; + rh->pstate(h->pstate()); + for (i = 1; i < L; ++i) rh->append((*h)[i]); + } else { + last()->head_->concat(h); + } + } else { + last()->head_->concat(h); + } + } else if (last()->head_) { + last()->head_->concat(h); + } + } else { + // std::cerr << "has no or empty head\n"; + } + + Complex_Selector_Ptr last = mutable_last(); + if (last) { + if (last->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) { + Complex_Selector_Ptr inter = SASS_MEMORY_NEW(Complex_Selector, pstate()); + inter->reference(r); + inter->combinator(c); + inter->tail(t); + last->tail(inter); + } else { + if (last->combinator() == ANCESTOR_OF) { + last->combinator(c); + last->reference(r); + } + last->tail(t); + } + } + + } + + Selector_List_Obj Selector_List::eval(Eval& eval) + { + Selector_List_Obj list = schema() ? + eval(schema()) : eval(this); + list->schema(schema()); + return list; + } + + Selector_List_Ptr Selector_List::resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent) + { + if (!this->has_parent_ref()) return this; + Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate()); + for (size_t si = 0, sL = this->length(); si < sL; ++si) { + Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent); + ss->concat(rv); + } + return ss; + } + + Selector_List_Ptr Complex_Selector::resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent) + { + Complex_Selector_Obj tail = this->tail(); + Compound_Selector_Obj head = this->head(); + Selector_List_Ptr parents = pstack.back(); + + if (!this->has_real_parent_ref() && !implicit_parent) { + Selector_List_Ptr retval = SASS_MEMORY_NEW(Selector_List, pstate(), 1); + retval->append(this); + return retval; + } + + // first resolve_parent_refs the tail (which may return an expanded list) + Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, traces, implicit_parent) : 0; + + if (head && head->length() > 0) { + + Selector_List_Obj retval; + // we have a parent selector in a simple compound list + // mix parent complex selector into the compound list + if (Cast((*head)[0])) { + retval = SASS_MEMORY_NEW(Selector_List, pstate()); + + // it turns out that real parent references reach + // across @at-root rules, which comes unexpected + if (parents == NULL && head->has_real_parent_ref()) { + int i = pstack.size() - 1; + while (!parents && i > -1) { + parents = pstack.at(i--); + } + } + + if (parents && parents->length()) { + if (tails && tails->length() > 0) { + for (size_t n = 0, nL = tails->length(); n < nL; ++n) { + for (size_t i = 0, iL = parents->length(); i < iL; ++i) { + Complex_Selector_Obj t = (*tails)[n]; + Complex_Selector_Obj parent = (*parents)[i]; + Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent); + Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this); + ss->tail(t ? SASS_MEMORY_CLONE(t) : NULL); + Compound_Selector_Obj h = SASS_MEMORY_COPY(head_); + // remove parent selector from sequence + if (h->length()) { + h->erase(h->begin()); + ss->head(h); + } else { + ss->head({}); + } + // adjust for parent selector (1 char) + // if (h->length()) { + // ParserState state(h->at(0)->pstate()); + // state.offset.column += 1; + // state.column -= 1; + // (*h)[0]->pstate(state); + // } + // keep old parser state + s->pstate(pstate()); + // append new tail + s->append(ss, traces); + retval->append(s); + } + } + } + // have no tails but parents + // loop above is inside out + else { + for (size_t i = 0, iL = parents->length(); i < iL; ++i) { + Complex_Selector_Obj parent = (*parents)[i]; + Complex_Selector_Obj s = SASS_MEMORY_CLONE(parent); + Complex_Selector_Obj ss = SASS_MEMORY_CLONE(this); + // this is only if valid if the parent has no trailing op + // otherwise we cannot append more simple selectors to head + if (parent->last()->combinator() != ANCESTOR_OF) { + traces.push_back(Backtrace(pstate())); + throw Exception::InvalidParent(parent, traces, ss); + } + ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL); + Compound_Selector_Obj h = SASS_MEMORY_COPY(head_); + // remove parent selector from sequence + if (h->length()) { + h->erase(h->begin()); + ss->head(h); + } else { + ss->head({}); + } + // \/ IMO ruby sass bug \/ + ss->has_line_feed(false); + // adjust for parent selector (1 char) + // if (h->length()) { + // ParserState state(h->at(0)->pstate()); + // state.offset.column += 1; + // state.column -= 1; + // (*h)[0]->pstate(state); + // } + // keep old parser state + s->pstate(pstate()); + // append new tail + s->append(ss, traces); + retval->append(s); + } + } + } + // have no parent but some tails + else { + if (tails && tails->length() > 0) { + for (size_t n = 0, nL = tails->length(); n < nL; ++n) { + Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this); + cpy->tail(SASS_MEMORY_CLONE(tails->at(n))); + cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate())); + for (size_t i = 1, L = this->head()->length(); i < L; ++i) + cpy->head()->append((*this->head())[i]); + if (!cpy->head()->length()) cpy->head({}); + retval->append(cpy->skip_empty_reference()); + } + } + // have no parent nor tails + else { + Complex_Selector_Obj cpy = SASS_MEMORY_CLONE(this); + cpy->head(SASS_MEMORY_NEW(Compound_Selector, head->pstate())); + for (size_t i = 1, L = this->head()->length(); i < L; ++i) + cpy->head()->append((*this->head())[i]); + if (!cpy->head()->length()) cpy->head({}); + retval->append(cpy->skip_empty_reference()); + } + } + } + // no parent selector in head + else { + retval = this->tails(tails); + } + + for (Simple_Selector_Obj ss : head->elements()) { + if (Wrapped_Selector_Ptr ws = Cast(ss)) { + if (Selector_List_Ptr sl = Cast(ws->selector())) { + if (parents) ws->selector(sl->resolve_parent_refs(pstack, traces, implicit_parent)); + } + } + } + + return retval.detach(); + + } + // has no head + return this->tails(tails); + } + + Selector_List_Ptr Complex_Selector::tails(Selector_List_Ptr tails) + { + Selector_List_Ptr rv = SASS_MEMORY_NEW(Selector_List, pstate_); + if (tails && tails->length()) { + for (size_t i = 0, iL = tails->length(); i < iL; ++i) { + Complex_Selector_Obj pr = SASS_MEMORY_CLONE(this); + pr->tail(tails->at(i)); + rv->append(pr); + } + } + else { + rv->append(this); + } + return rv; + } + + // return the last tail that is defined + Complex_Selector_Ptr_Const Complex_Selector::first() const + { + // declare variables used in loop + Complex_Selector_Ptr_Const cur = this; + Compound_Selector_Ptr_Const head; + // processing loop + while (cur) + { + // get the head + head = cur->head_.ptr(); + // abort (and return) if it is not a parent selector + if (!head || head->length() != 1 || !Cast((*head)[0])) { + break; + } + // advance to next + cur = cur->tail_; + } + // result + return cur; + } + + Complex_Selector_Ptr Complex_Selector::mutable_first() + { + return const_cast(first()); + } + + // return the last tail that is defined + Complex_Selector_Ptr_Const Complex_Selector::last() const + { + Complex_Selector_Ptr_Const cur = this; + Complex_Selector_Ptr_Const nxt = cur; + // loop until last + while (nxt) { + cur = nxt; + nxt = cur->tail_.ptr(); + } + return cur; + } + + Complex_Selector_Ptr Complex_Selector::mutable_last() + { + return const_cast(last()); + } + + Complex_Selector::Combinator Complex_Selector::clear_innermost() + { + Combinator c; + if (!tail() || tail()->tail() == nullptr) + { c = combinator(); combinator(ANCESTOR_OF); tail({}); } + else + { c = tail_->clear_innermost(); } + return c; + } + + void Complex_Selector::set_innermost(Complex_Selector_Obj val, Combinator c) + { + if (!tail_) + { tail_ = val; combinator(c); } + else + { tail_->set_innermost(val, c); } + } + + void Complex_Selector::cloneChildren() + { + if (head()) head(SASS_MEMORY_CLONE(head())); + if (tail()) tail(SASS_MEMORY_CLONE(tail())); + } + + void Compound_Selector::cloneChildren() + { + for (size_t i = 0, l = length(); i < l; i++) { + at(i) = SASS_MEMORY_CLONE(at(i)); + } + } + + void Selector_List::cloneChildren() + { + for (size_t i = 0, l = length(); i < l; i++) { + at(i) = SASS_MEMORY_CLONE(at(i)); + } + } + + void Wrapped_Selector::cloneChildren() + { + selector(SASS_MEMORY_CLONE(selector())); + } + + // remove parent selector references + // basically unwraps parsed selectors + void Selector_List::remove_parent_selectors() + { + // Check every rhs selector against left hand list + for(size_t i = 0, L = length(); i < L; ++i) { + if (!(*this)[i]->head()) continue; + if ((*this)[i]->head()->is_empty_reference()) { + // simply move to the next tail if we have "no" combinator + if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) { + if ((*this)[i]->tail()) { + if ((*this)[i]->has_line_feed()) { + (*this)[i]->tail()->has_line_feed(true); + } + (*this)[i] = (*this)[i]->tail(); + } + } + // otherwise remove the first item from head + else { + (*this)[i]->head()->erase((*this)[i]->head()->begin()); + } + } + } + } + + size_t Wrapped_Selector::hash() const + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + if (selector_) hash_combine(hash_, selector_->hash()); + } + return hash_; + } + bool Wrapped_Selector::has_parent_ref() const { + // if (has_reference()) return true; + if (!selector()) return false; + return selector()->has_parent_ref(); + } + bool Wrapped_Selector::has_real_parent_ref() const { + // if (has_reference()) return true; + if (!selector()) return false; + return selector()->has_real_parent_ref(); + } + unsigned long Wrapped_Selector::specificity() const + { + return selector_ ? selector_->specificity() : 0; + } + + + bool Selector_List::has_parent_ref() const + { + for (Complex_Selector_Obj s : elements()) { + if (s && s->has_parent_ref()) return true; + } + return false; + } + + bool Selector_List::has_real_parent_ref() const + { + for (Complex_Selector_Obj s : elements()) { + if (s && s->has_real_parent_ref()) return true; + } + return false; + } + + bool Selector_Schema::has_parent_ref() const + { + if (String_Schema_Obj schema = Cast(contents())) { + return schema->length() > 0 && Cast(schema->at(0)) != NULL; + } + return false; + } + + bool Selector_Schema::has_real_parent_ref() const + { + if (String_Schema_Obj schema = Cast(contents())) { + if (schema->length() == 0) return false; + return Cast(schema->at(0)) != nullptr; + } + return false; + } + + void Selector_List::adjust_after_pushing(Complex_Selector_Obj c) + { + // if (c->has_reference()) has_reference(true); + } + + // it's a superselector if every selector of the right side + // list is a superselector of the given left side selector + bool Complex_Selector::is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping) const + { + // Check every rhs selector against left hand list + for(size_t i = 0, L = sub->length(); i < L; ++i) { + if (!is_superselector_of((*sub)[i], wrapping)) return false; + } + return true; + } + + // it's a superselector if every selector of the right side + // list is a superselector of the given left side selector + bool Selector_List::is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping) const + { + // Check every rhs selector against left hand list + for(size_t i = 0, L = sub->length(); i < L; ++i) { + if (!is_superselector_of((*sub)[i], wrapping)) return false; + } + return true; + } + + // it's a superselector if every selector on the right side + // is a superselector of any one of the left side selectors + bool Selector_List::is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapping) const + { + // Check every lhs selector against right hand + for(size_t i = 0, L = length(); i < L; ++i) { + if ((*this)[i]->is_superselector_of(sub, wrapping)) return true; + } + return false; + } + + // it's a superselector if every selector on the right side + // is a superselector of any one of the left side selectors + bool Selector_List::is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapping) const + { + // Check every lhs selector against right hand + for(size_t i = 0, L = length(); i < L; ++i) { + if ((*this)[i]->is_superselector_of(sub)) return true; + } + return false; + } + + void Selector_List::populate_extends(Selector_List_Obj extendee, Subset_Map& extends) + { + + Selector_List_Ptr extender = this; + for (auto complex_sel : extendee->elements()) { + Complex_Selector_Obj c = complex_sel; + + + // Ignore any parent selectors, until we find the first non Selectorerence head + Compound_Selector_Obj compound_sel = c->head(); + Complex_Selector_Obj pIter = complex_sel; + while (pIter) { + Compound_Selector_Obj pHead = pIter->head(); + if (pHead && Cast(pHead->elements()[0]) == NULL) { + compound_sel = pHead; + break; + } + + pIter = pIter->tail(); + } + + if (!pIter->head() || pIter->tail()) { + coreError("nested selectors may not be extended", c->pstate()); + } + + compound_sel->is_optional(extendee->is_optional()); + + for (size_t i = 0, L = extender->length(); i < L; ++i) { + extends.put(compound_sel, std::make_pair((*extender)[i], compound_sel)); + } + } + }; + + void Compound_Selector::append(Simple_Selector_Obj element) + { + Vectorized::append(element); + pstate_.offset += element->pstate().offset; + } + + Compound_Selector_Ptr Compound_Selector::minus(Compound_Selector_Ptr rhs) + { + Compound_Selector_Ptr result = SASS_MEMORY_NEW(Compound_Selector, pstate()); + // result->has_parent_reference(has_parent_reference()); + + // not very efficient because it needs to preserve order + for (size_t i = 0, L = length(); i < L; ++i) + { + bool found = false; + std::string thisSelector((*this)[i]->to_string()); + for (size_t j = 0, M = rhs->length(); j < M; ++j) + { + if (thisSelector == (*rhs)[j]->to_string()) + { + found = true; + break; + } + } + if (!found) result->append((*this)[i]); + } + + return result; + } + + void Compound_Selector::mergeSources(ComplexSelectorSet& sources) + { + for (ComplexSelectorSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) { + this->sources_.insert(SASS_MEMORY_CLONE(*iterator)); + } + } + + IMPLEMENT_AST_OPERATORS(Selector_Schema); + IMPLEMENT_AST_OPERATORS(Placeholder_Selector); + IMPLEMENT_AST_OPERATORS(Parent_Selector); + IMPLEMENT_AST_OPERATORS(Attribute_Selector); + IMPLEMENT_AST_OPERATORS(Compound_Selector); + IMPLEMENT_AST_OPERATORS(Complex_Selector); + IMPLEMENT_AST_OPERATORS(Element_Selector); + IMPLEMENT_AST_OPERATORS(Class_Selector); + IMPLEMENT_AST_OPERATORS(Id_Selector); + IMPLEMENT_AST_OPERATORS(Pseudo_Selector); + IMPLEMENT_AST_OPERATORS(Wrapped_Selector); + IMPLEMENT_AST_OPERATORS(Selector_List); + +} \ No newline at end of file diff --git a/src/ast_selectors.hpp b/src/ast_selectors.hpp new file mode 100644 index 0000000000..4e993035c8 --- /dev/null +++ b/src/ast_selectors.hpp @@ -0,0 +1,883 @@ +#ifndef SASS_AST_SEL_H +#define SASS_AST_SEL_H + +#include "sass.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "sass/base.h" +#include "ast_fwd_decl.hpp" + +#include "util.hpp" +#include "units.hpp" +#include "context.hpp" +#include "position.hpp" +#include "constants.hpp" +#include "operation.hpp" +#include "position.hpp" +#include "inspect.hpp" +#include "source_map.hpp" +#include "environment.hpp" +#include "error_handling.hpp" +#include "ast_def_macros.hpp" +#include "ast_fwd_decl.hpp" +#include "source_map.hpp" +#include "fn_utils.hpp" + +#include "sass.h" + +namespace Sass { + + ///////////////////////////////////////// + // Abstract base class for CSS selectors. + ///////////////////////////////////////// + class Selector : public Expression { + // ADD_PROPERTY(bool, has_reference) + // line break before list separator + ADD_PROPERTY(bool, has_line_feed) + // line break after list separator + ADD_PROPERTY(bool, has_line_break) + // maybe we have optional flag + ADD_PROPERTY(bool, is_optional) + // parent block pointers + + // must not be a reference counted object + // otherwise we create circular references + ADD_PROPERTY(Media_Block_Ptr, media_block) + protected: + mutable size_t hash_; + public: + Selector(ParserState pstate) + : Expression(pstate), + has_line_feed_(false), + has_line_break_(false), + is_optional_(false), + media_block_(0), + hash_(0) + { concrete_type(SELECTOR); } + Selector(const Selector* ptr) + : Expression(ptr), + // has_reference_(ptr->has_reference_), + has_line_feed_(ptr->has_line_feed_), + has_line_break_(ptr->has_line_break_), + is_optional_(ptr->is_optional_), + media_block_(ptr->media_block_), + hash_(ptr->hash_) + { concrete_type(SELECTOR); } + virtual ~Selector() = 0; + size_t hash() const override = 0; + virtual unsigned long specificity() const = 0; + virtual int unification_order() const = 0; + virtual void set_media_block(Media_Block_Ptr mb) { + media_block(mb); + } + virtual bool has_parent_ref() const { + return false; + } + virtual bool has_real_parent_ref() const { + return false; + } + // dispatch to correct handlers + virtual bool operator<(const Selector& rhs) const = 0; + virtual bool operator==(const Selector& rhs) const = 0; + bool operator>(const Selector& rhs) const { return rhs < *this; }; + bool operator!=(const Selector& rhs) const { return !(rhs == *this); }; + ATTACH_VIRTUAL_AST_OPERATIONS(Selector); + }; + inline Selector::~Selector() { } + + ///////////////////////////////////////////////////////////////////////// + // Interpolated selectors -- the interpolated String will be expanded and + // re-parsed into a normal selector class. + ///////////////////////////////////////////////////////////////////////// + class Selector_Schema final : public AST_Node { + ADD_PROPERTY(String_Obj, contents) + ADD_PROPERTY(bool, connect_parent); + // must not be a reference counted object + // otherwise we create circular references + ADD_PROPERTY(Media_Block_Ptr, media_block) + // store computed hash + mutable size_t hash_; + public: + Selector_Schema(ParserState pstate, String_Obj c) + : AST_Node(pstate), + contents_(c), + connect_parent_(true), + media_block_(NULL), + hash_(0) + { } + Selector_Schema(const Selector_Schema* ptr) + : AST_Node(ptr), + contents_(ptr->contents_), + connect_parent_(ptr->connect_parent_), + media_block_(ptr->media_block_), + hash_(ptr->hash_) + { } + bool has_parent_ref() const; + bool has_real_parent_ref() const; + bool operator<(const Selector& rhs) const; + bool operator==(const Selector& rhs) const; + // selector schema is not yet a final selector, so we do not + // have a specificity for it yet. We need to + unsigned long specificity() const { return 0; } + size_t hash() const override { + if (hash_ == 0) { + hash_combine(hash_, contents_->hash()); + } + return hash_; + } + ATTACH_AST_OPERATIONS(Selector_Schema) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////// + // Abstract base class for simple selectors. + //////////////////////////////////////////// + class Simple_Selector : public Selector { + ADD_CONSTREF(std::string, ns) + ADD_CONSTREF(std::string, name) + ADD_PROPERTY(Simple_Type, simple_type) + ADD_PROPERTY(bool, has_ns) + public: + 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) { + has_ns_ = true; + ns_ = n.substr(0, pos); + name_ = n.substr(pos + 1); + } + } + Simple_Selector(const Simple_Selector* ptr) + : Selector(ptr), + ns_(ptr->ns_), + name_(ptr->name_), + has_ns_(ptr->has_ns_) + { simple_type(SIMPLE); } + std::string ns_name() const + { + std::string name(""); + if (has_ns_) + name += ns_ + "|"; + return name + name_; + } + virtual size_t hash() const override + { + if (hash_ == 0) { + hash_combine(hash_, std::hash()(SELECTOR)); + hash_combine(hash_, std::hash()(simple_type())); + hash_combine(hash_, std::hash()(ns())); + hash_combine(hash_, std::hash()(name())); + } + return hash_; + } + // namespace compare functions + bool is_ns_eq(const Simple_Selector& r) const; + // namespace query functions + bool is_universal_ns() const + { + return has_ns_ && ns_ == "*"; + } + bool has_universal_ns() const + { + return !has_ns_ || ns_ == "*"; + } + bool is_empty_ns() const + { + return !has_ns_ || ns_ == ""; + } + bool has_empty_ns() const + { + return has_ns_ && ns_ == ""; + } + bool has_qualified_ns() const + { + return has_ns_ && ns_ != "" && ns_ != "*"; + } + // name query functions + virtual bool is_universal() const + { + return name_ == "*"; + } + + virtual bool has_placeholder() { + return false; + } + + virtual ~Simple_Selector() = 0; + virtual Compound_Selector_Ptr unify_with(Compound_Selector_Ptr); + virtual bool is_pseudo_element() const { return false; } + + virtual bool is_superselector_of(Compound_Selector_Ptr_Const sub) const { return false; } + + bool operator==(const Selector& rhs) const final override; + 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() { } + + + ////////////////////////////////// + // The Parent Selector Expression. + ////////////////////////////////// + // parent selectors can occur in selectors but also + // inside strings in declarations (Compound_Selector). + // only one simple parent selector means the first case. + class Parent_Selector final : public Simple_Selector { + ADD_PROPERTY(bool, real) + public: + Parent_Selector(ParserState pstate, bool r = true) + : Simple_Selector(pstate, "&"), real_(r) + { /* has_reference(true); */ } + Parent_Selector(const Parent_Selector* ptr) + : Simple_Selector(ptr), real_(ptr->real_) + { /* has_reference(true); */ } + 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(); }; + unsigned long specificity() const override + { + return 0; + } + int unification_order() const override + { + throw std::runtime_error("unification_order for Parent_Selector is undefined"); + } + std::string type() const override { return "selector"; } + static std::string type_name() { return "selector"; } + ATTACH_AST_OPERATIONS(Parent_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + + ///////////////////////////////////////////////////////////////////////// + // Placeholder selectors (e.g., "%foo") for use in extend-only selectors. + ///////////////////////////////////////////////////////////////////////// + class Placeholder_Selector final : public Simple_Selector { + public: + Placeholder_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { } + Placeholder_Selector(const Placeholder_Selector* ptr) + : Simple_Selector(ptr) + { } + unsigned long specificity() const override + { + return Constants::Specificity_Base; + } + int unification_order() const override + { + return Constants::UnificationOrder_Placeholder; + } + bool has_placeholder() override { + return true; + } + virtual ~Placeholder_Selector() {}; + ATTACH_AST_OPERATIONS(Placeholder_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ///////////////////////////////////////////////////////////////////// + // Element selectors (and the universal selector) -- e.g., div, span, *. + ///////////////////////////////////////////////////////////////////// + class Element_Selector final : public Simple_Selector { + public: + Element_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { } + Element_Selector(const Element_Selector* ptr) + : Simple_Selector(ptr) + { } + unsigned long specificity() const override + { + if (name() == "*") return 0; + else return Constants::Specificity_Element; + } + int unification_order() const override + { + return Constants::UnificationOrder_Element; + } + 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 Element_Selector& rhs) const; + ATTACH_AST_OPERATIONS(Element_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////// + // Class selectors -- i.e., .foo. + //////////////////////////////////////////////// + class Class_Selector final : public Simple_Selector { + public: + Class_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { } + Class_Selector(const Class_Selector* ptr) + : Simple_Selector(ptr) + { } + unsigned long specificity() const override + { + return Constants::Specificity_Class; + } + int unification_order() const override + { + return Constants::UnificationOrder_Class; + } + Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; + ATTACH_AST_OPERATIONS(Class_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////// + // ID selectors -- i.e., #foo. + //////////////////////////////////////////////// + class Id_Selector final : public Simple_Selector { + public: + Id_Selector(ParserState pstate, std::string n) + : Simple_Selector(pstate, n) + { } + Id_Selector(const Id_Selector* ptr) + : Simple_Selector(ptr) + { } + unsigned long specificity() const override + { + return Constants::Specificity_ID; + } + int unification_order() const override + { + return Constants::UnificationOrder_Id; + } + Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; + ATTACH_AST_OPERATIONS(Id_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + /////////////////////////////////////////////////// + // Attribute selectors -- e.g., [src*=".jpg"], etc. + /////////////////////////////////////////////////// + class Attribute_Selector final : public Simple_Selector { + ADD_CONSTREF(std::string, matcher) + // this cannot be changed to obj atm!!!!!!????!!!!!!! + ADD_PROPERTY(String_Obj, value) // might be interpolated + ADD_PROPERTY(char, modifier); + 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); } + Attribute_Selector(const Attribute_Selector* ptr) + : Simple_Selector(ptr), + matcher_(ptr->matcher_), + value_(ptr->value_), + modifier_(ptr->modifier_) + { simple_type(ATTR_SEL); } + size_t hash() const override + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + hash_combine(hash_, std::hash()(matcher())); + if (value_) hash_combine(hash_, value_->hash()); + } + return hash_; + } + unsigned long specificity() const override + { + return Constants::Specificity_Attr; + } + int unification_order() const override + { + 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 Attribute_Selector& rhs) const; + ATTACH_AST_OPERATIONS(Attribute_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ////////////////////////////////////////////////////////////////// + // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc. + ////////////////////////////////////////////////////////////////// + /* '::' starts a pseudo-element, ':' a pseudo-class */ + /* Except :first-line, :first-letter, :before and :after */ + /* Note that pseudo-elements are restricted to one per selector */ + /* and occur only in the last simple_selector_sequence. */ + inline bool is_pseudo_class_element(const std::string& name) + { + return name == ":before" || + name == ":after" || + name == ":first-line" || + name == ":first-letter"; + } + + // Pseudo Selector cannot have any namespace? + class Pseudo_Selector final : public Simple_Selector { + ADD_PROPERTY(String_Obj, expression) + public: + Pseudo_Selector(ParserState pstate, std::string n, String_Obj expr = {}) + : Simple_Selector(pstate, n), expression_(expr) + { simple_type(PSEUDO_SEL); } + Pseudo_Selector(const Pseudo_Selector* ptr) + : Simple_Selector(ptr), expression_(ptr->expression_) + { simple_type(PSEUDO_SEL); } + + // A pseudo-element is made of two colons (::) followed by the name. + // The `::` notation is introduced by the current document in order to + // establish a discrimination between pseudo-classes and pseudo-elements. + // For compatibility with existing style sheets, user agents must also + // accept the previous one-colon notation for pseudo-elements introduced + // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and + // :after). This compatibility is not allowed for the new pseudo-elements + // introduced in this specification. + bool is_pseudo_element() const override + { + return (name_[0] == ':' && name_[1] == ':') + || is_pseudo_class_element(name_); + } + size_t hash() const override + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + if (expression_) hash_combine(hash_, expression_->hash()); + } + return hash_; + } + unsigned long specificity() const override + { + if (is_pseudo_element()) + return Constants::Specificity_Element; + return Constants::Specificity_Pseudo; + } + int unification_order() const override + { + if (is_pseudo_element()) + 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 Pseudo_Selector& rhs) const; + Compound_Selector_Ptr unify_with(Compound_Selector_Ptr) override; + ATTACH_AST_OPERATIONS(Pseudo_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + ///////////////////////////////////////////////// + // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir) + ///////////////////////////////////////////////// + class Wrapped_Selector final : public Simple_Selector { + ADD_PROPERTY(Selector_List_Obj, selector) + public: + Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel) + : Simple_Selector(pstate, n), selector_(sel) + { simple_type(WRAPPED_SEL); } + Wrapped_Selector(const Wrapped_Selector* ptr) + : Simple_Selector(ptr), selector_(ptr->selector_) + { simple_type(WRAPPED_SEL); } + using Simple_Selector::is_superselector_of; + bool is_superselector_of(Wrapped_Selector_Ptr_Const sub) const; + // Selectors inside the negation pseudo-class are counted like any + // other, but the negation itself does not count as a pseudo-class. + size_t hash() const override; + bool has_parent_ref() const override; + bool has_real_parent_ref() const override; + unsigned long specificity() const override; + int unification_order() const override + { + 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 Wrapped_Selector& rhs) const; + void cloneChildren() override; + ATTACH_AST_OPERATIONS(Wrapped_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////////////////////////////////// + // Simple selector sequences. Maintains flags indicating whether it contains + // any parent references or placeholders, to simplify expansion. + //////////////////////////////////////////////////////////////////////////// + class Compound_Selector final : public Selector, public Vectorized { + private: + ComplexSelectorSet sources_; + ADD_PROPERTY(bool, extended); + ADD_PROPERTY(bool, has_parent_reference); + protected: + void adjust_after_pushing(Simple_Selector_Obj s) override + { + // if (s->has_reference()) has_reference(true); + // if (s->has_placeholder()) has_placeholder(true); + } + public: + Compound_Selector(ParserState pstate, size_t s = 0) + : Selector(pstate), + Vectorized(s), + extended_(false), + has_parent_reference_(false) + { } + Compound_Selector(const Compound_Selector* ptr) + : Selector(ptr), + Vectorized(*ptr), + extended_(ptr->extended_), + has_parent_reference_(ptr->has_parent_reference_) + { } + bool contains_placeholder() { + for (size_t i = 0, L = length(); i < L; ++i) { + if ((*this)[i]->has_placeholder()) return true; + } + return false; + }; + + void append(Simple_Selector_Obj element) override; + + bool is_universal() const + { + return length() == 1 && (*this)[0]->is_universal(); + } + + Complex_Selector_Obj to_complex(); + Compound_Selector_Ptr unify_with(Compound_Selector_Ptr rhs); + // virtual Placeholder_Selector_Ptr find_placeholder(); + bool has_parent_ref() const override; + bool has_real_parent_ref() const override; + Simple_Selector_Ptr base() const { + if (length() == 0) return 0; + // ToDo: why is this needed? + if (Cast((*this)[0])) + return (*this)[0]; + return 0; + } + bool is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapped = "") const; + bool is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapped = "") const; + bool is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapped = "") const; + size_t hash() const override + { + if (Selector::hash_ == 0) { + hash_combine(Selector::hash_, std::hash()(SELECTOR)); + if (length()) hash_combine(Selector::hash_, Vectorized::hash()); + } + return Selector::hash_; + } + unsigned long specificity() const override + { + int sum = 0; + for (size_t i = 0, L = length(); i < L; ++i) + { sum += (*this)[i]->specificity(); } + return sum; + } + int unification_order() const override + { + throw std::runtime_error("unification_order for Compound_Selector is undefined"); + } + + bool has_placeholder() + { + if (length() == 0) return false; + if (Simple_Selector_Obj ss = elements().front()) { + if (ss->has_placeholder()) return true; + } + return false; + } + + bool is_empty_reference() + { + return length() == 1 && + Cast((*this)[0]); + } + + 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 Compound_Selector& rhs) const; + bool operator==(const Compound_Selector& rhs) const; + inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); } + + ComplexSelectorSet& sources() { return sources_; } + void clearSources() { sources_.clear(); } + void mergeSources(ComplexSelectorSet& sources); + + Compound_Selector_Ptr minus(Compound_Selector_Ptr rhs); + void cloneChildren() override; + ATTACH_AST_OPERATIONS(Compound_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + //////////////////////////////////////////////////////////////////////////// + // General selectors -- i.e., simple sequences combined with one of the four + // CSS selector combinators (">", "+", "~", and whitespace). Essentially a + // linked list. + //////////////////////////////////////////////////////////////////////////// + class Complex_Selector final : public Selector { + public: + enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE }; + private: + HASH_CONSTREF(Combinator, combinator) + HASH_PROPERTY(Compound_Selector_Obj, head) + HASH_PROPERTY(Complex_Selector_Obj, tail) + HASH_PROPERTY(String_Obj, reference); + public: + bool contains_placeholder() { + if (head() && head()->contains_placeholder()) return true; + if (tail() && tail()->contains_placeholder()) return true; + return false; + }; + Complex_Selector(ParserState pstate, + Combinator c = ANCESTOR_OF, + Compound_Selector_Obj h = {}, + Complex_Selector_Obj t = {}, + String_Obj r = {}) + : Selector(pstate), + combinator_(c), + head_(h), tail_(t), + reference_(r) + {} + Complex_Selector(const Complex_Selector* ptr) + : Selector(ptr), + combinator_(ptr->combinator_), + head_(ptr->head_), tail_(ptr->tail_), + reference_(ptr->reference_) + {}; + bool has_parent_ref() const override; + bool has_real_parent_ref() const override; + + Complex_Selector_Obj skip_empty_reference() + { + if ((!head_ || !head_->length() || head_->is_empty_reference()) && + combinator() == Combinator::ANCESTOR_OF) + { + if (!tail_) return {}; + tail_->has_line_feed_ = this->has_line_feed_; + // tail_->has_line_break_ = this->has_line_break_; + return tail_->skip_empty_reference(); + } + return this; + } + + // can still have a tail + bool is_empty_ancestor() const + { + return (!head() || head()->length() == 0) && + combinator() == Combinator::ANCESTOR_OF; + } + + Selector_List_Ptr tails(Selector_List_Ptr tails); + + // front returns the first real tail + // skips over parent and empty ones + Complex_Selector_Ptr_Const first() const; + Complex_Selector_Ptr mutable_first(); + + // last returns the last real tail + Complex_Selector_Ptr_Const last() const; + Complex_Selector_Ptr mutable_last(); + + size_t length() const; + Selector_List_Ptr resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent = true); + bool is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapping = "") const; + bool is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapping = "") const; + bool is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping = "") const; + Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs); + Combinator clear_innermost(); + void append(Complex_Selector_Obj, Backtraces& traces); + void set_innermost(Complex_Selector_Obj, Combinator); + size_t hash() const override + { + if (hash_ == 0) { + hash_combine(hash_, std::hash()(SELECTOR)); + hash_combine(hash_, std::hash()(combinator_)); + if (head_) hash_combine(hash_, head_->hash()); + if (tail_) hash_combine(hash_, tail_->hash()); + } + return hash_; + } + unsigned long specificity() const override + { + int sum = 0; + if (head()) sum += head()->specificity(); + if (tail()) sum += tail()->specificity(); + return sum; + } + int unification_order() const override + { + throw std::runtime_error("unification_order for Complex_Selector is undefined"); + } + void set_media_block(Media_Block_Ptr mb) override { + media_block(mb); + if (tail_) tail_->set_media_block(mb); + if (head_) head_->set_media_block(mb); + } + bool has_placeholder() { + if (head_ && head_->has_placeholder()) return true; + if (tail_ && tail_->has_placeholder()) return true; + 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 Complex_Selector& rhs) const; + bool operator==(const Complex_Selector& rhs) const; + inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); } + const ComplexSelectorSet sources() + { + //s = Set.new + //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)} + //s + + ComplexSelectorSet srcs; + + Compound_Selector_Obj pHead = head(); + Complex_Selector_Obj pTail = tail(); + + if (pHead) { + const ComplexSelectorSet& headSources = pHead->sources(); + srcs.insert(headSources.begin(), headSources.end()); + } + + if (pTail) { + const ComplexSelectorSet& tailSources = pTail->sources(); + srcs.insert(tailSources.begin(), tailSources.end()); + } + + return srcs; + } + void addSources(ComplexSelectorSet& sources) { + // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m} + Complex_Selector_Ptr pIter = this; + while (pIter) { + Compound_Selector_Ptr pHead = pIter->head(); + + if (pHead) { + pHead->mergeSources(sources); + } + + pIter = pIter->tail(); + } + } + void clearSources() { + Complex_Selector_Ptr pIter = this; + while (pIter) { + Compound_Selector_Ptr pHead = pIter->head(); + + if (pHead) { + pHead->clearSources(); + } + + pIter = pIter->tail(); + } + } + + void cloneChildren() override; + ATTACH_AST_OPERATIONS(Complex_Selector) + ATTACH_CRTP_PERFORM_METHODS() + }; + + /////////////////////////////////// + // Comma-separated selector groups. + /////////////////////////////////// + class Selector_List final : public Selector, public Vectorized { + ADD_PROPERTY(Selector_Schema_Obj, schema) + ADD_CONSTREF(std::vector, wspace) + protected: + void adjust_after_pushing(Complex_Selector_Obj c) override; + public: + Selector_List(ParserState pstate, size_t s = 0) + : Selector(pstate), + Vectorized(s), + schema_({}), + wspace_(0) + { } + Selector_List(const Selector_List* ptr) + : Selector(ptr), + Vectorized(*ptr), + schema_(ptr->schema_), + wspace_(ptr->wspace_) + { } + std::string type() const override { return "list"; } + // remove parent selector references + // basically unwraps parsed selectors + bool has_parent_ref() const override; + bool has_real_parent_ref() const override; + void remove_parent_selectors(); + Selector_List_Ptr resolve_parent_refs(SelectorStack& pstack, Backtraces& traces, bool implicit_parent = true); + bool is_superselector_of(Compound_Selector_Ptr_Const sub, std::string wrapping = "") const; + bool is_superselector_of(Complex_Selector_Ptr_Const sub, std::string wrapping = "") const; + bool is_superselector_of(Selector_List_Ptr_Const sub, std::string wrapping = "") const; + Selector_List_Ptr unify_with(Selector_List_Ptr); + void populate_extends(Selector_List_Obj, Subset_Map&); + Selector_List_Obj eval(Eval& eval); + size_t hash() const override + { + if (Selector::hash_ == 0) { + hash_combine(Selector::hash_, std::hash()(SELECTOR)); + hash_combine(Selector::hash_, Vectorized::hash()); + } + return Selector::hash_; + } + unsigned long specificity() const override + { + unsigned long sum = 0; + unsigned long specificity; + for (size_t i = 0, L = length(); i < L; ++i) + { + specificity = (*this)[i]->specificity(); + if (sum < specificity) sum = specificity; + } + return sum; + } + int unification_order() const override + { + throw std::runtime_error("unification_order for Selector_List is undefined"); + } + void set_media_block(Media_Block_Ptr mb) override { + media_block(mb); + for (Complex_Selector_Obj cs : elements()) { + cs->set_media_block(mb); + } + } + bool has_placeholder() { + for (Complex_Selector_Obj cs : elements()) { + if (cs->has_placeholder()) return true; + } + 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; + // Selector Lists can be compared to comma lists + bool operator==(const Expression& rhs) const override; + void cloneChildren() override; + ATTACH_AST_OPERATIONS(Selector_List) + ATTACH_CRTP_PERFORM_METHODS() + }; + + // compare function for sorting and probably other other uses + struct cmp_complex_selector { inline bool operator() (const Complex_Selector_Obj l, const Complex_Selector_Obj r) { return (*l < *r); } }; + struct cmp_compound_selector { inline bool operator() (const Compound_Selector_Obj l, const Compound_Selector_Obj r) { return (*l < *r); } }; + struct cmp_simple_selector { inline bool operator() (const Simple_Selector_Obj l, const Simple_Selector_Obj r) { return (*l < *r); } }; + +} + +#endif diff --git a/win/libsass.targets b/win/libsass.targets index 62780aeac5..7c17408cf2 100644 --- a/win/libsass.targets +++ b/win/libsass.targets @@ -11,6 +11,7 @@ + @@ -77,6 +78,9 @@ + + + diff --git a/win/libsass.vcxproj.filters b/win/libsass.vcxproj.filters index 407fac7830..9ac1d14ab1 100644 --- a/win/libsass.vcxproj.filters +++ b/win/libsass.vcxproj.filters @@ -48,6 +48,9 @@ Headers + + Headers + Headers @@ -242,6 +245,15 @@ Sources + + Sources + + + Sources + + + Sources + Sources