From 4276b96fccd3024f2b66f45910a27fa64b899be3 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Wed, 14 Oct 2015 03:47:44 +0200 Subject: [PATCH] Improve parent selector handling - Improve compatibility when inspecting - Improve handling with functions (append/nth) --- src/ast.cpp | 14 ++++++++++++-- src/ast.hpp | 4 ++++ src/bind.cpp | 2 -- src/emitter.cpp | 2 ++ src/eval.cpp | 2 +- src/functions.cpp | 23 +++++++++++++++++++---- src/inspect.cpp | 17 +++++++++++++++++ 7 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/ast.cpp b/src/ast.cpp index 5a52bd441..8a959b0c4 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -235,6 +235,16 @@ namespace Sass { 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 (const List* ls = dynamic_cast(&rhs)) { return *this == *ls; } + if (const Selector* ls = dynamic_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 @@ -258,10 +268,10 @@ namespace Sass { // skip nulls if (!l) ++i; else if (!r) ++n; - // do the check now + // do the check else if (*l != *r) { return false; } - // advance now + // advance ++i; ++n; } // no mismatch diff --git a/src/ast.hpp b/src/ast.hpp index c89688cdf..329312f28 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -191,6 +191,7 @@ namespace Sass { T last() { return elements_.back(); } T first() { return elements_.front(); } T& operator[](size_t i) { return elements_[i]; } + virtual const T& at(size_t i) const { return elements_.at(i); } const T& operator[](size_t i) const { return elements_[i]; } Vectorized& operator<<(T element) { @@ -2251,6 +2252,7 @@ namespace Sass { Selector_List(ParserState pstate, size_t s = 0) : Selector(pstate), Vectorized(s), wspace_(0) { } + std::string type() { return "list"; } // remove parent selector references // basically unwraps parsed selectors void remove_parent_selectors(); @@ -2276,6 +2278,8 @@ namespace Sass { Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s virtual bool operator==(const Selector& rhs) const; virtual bool operator==(const Selector_List& rhs) const; + // Selector Lists can be compared to comma lists + virtual bool operator==(const Expression& rhs) const; ATTACH_OPERATIONS() }; diff --git a/src/bind.cpp b/src/bind.cpp index 596d9cbd4..5bb240cb8 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -39,8 +39,6 @@ namespace Sass { size_t ia = 0, LA = as->length(); while (ia < LA) { Argument* a = (*as)[ia]; - // this is only needed for selectors - a->value(a->value()->perform(&listize)); if (ip >= LP) { // skip empty rest arguments if (a->is_rest_argument()) { diff --git a/src/emitter.cpp b/src/emitter.cpp index 8eaaf340c..ffe895743 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -146,6 +146,7 @@ namespace Sass { { if (output_style() == COMPRESSED) return; if (output_style() == COMPACT) return; + if (in_declaration && in_comma_array) return; if (scheduled_linefeed && indentation) scheduled_linefeed = 1; std::string indent = ""; @@ -208,6 +209,7 @@ namespace Sass { void Emitter::append_optional_linefeed() { + if (in_declaration && in_comma_array) return; if (output_style() == COMPACT) { append_mandatory_space(); } else { diff --git a/src/eval.cpp b/src/eval.cpp index 65aeeffca..500304622 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -761,7 +761,7 @@ namespace Sass { value = SASS_MEMORY_NEW(ctx.mem, Null, value->pstate()); } else if (value->concrete_type() == Expression::SELECTOR) { - value = value->perform(this)->perform(&listize); + value = value->perform(this); // ->perform(&listize); } // std::cerr << "\ttype is now: " << typeid(*value).name() << std::endl << std::endl; diff --git a/src/functions.cpp b/src/functions.cpp index 03f634055..72bbf5aa6 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -1188,12 +1188,13 @@ namespace Sass { Signature length_sig = "length($list)"; BUILT_IN(length) { + if (Selector_List* sl = dynamic_cast(env["$list"])) { + return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)sl->length()); + } Expression* v = ARG("$list", Expression); if (v->concrete_type() == Expression::MAP) { Map* map = dynamic_cast(env["$list"]); - return SASS_MEMORY_NEW(ctx.mem, Number, - pstate, - (double)(map ? map->length() : 1)); + return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(map ? map->length() : 1)); } if (v->concrete_type() == Expression::SELECTOR) { if (Compound_Selector* h = dynamic_cast(v)) { @@ -1214,9 +1215,19 @@ namespace Sass { Signature nth_sig = "nth($list, $n)"; BUILT_IN(nth) { + Number* n = ARG("$n", Number); Map* m = dynamic_cast(env["$list"]); + if (Selector_List* sl = dynamic_cast(env["$list"])) { + size_t len = m ? m->length() : sl->length(); + bool empty = m ? m->empty() : sl->empty(); + if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate); + double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1); + if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); + // return (*sl)[static_cast(index)]; + Listize listize(ctx); + return (*sl)[static_cast(index)]->perform(&listize); + } List* l = dynamic_cast(env["$list"]); - Number* n = ARG("$n", Number); if (n->value() == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate); // if the argument isn't a list, then wrap it in a singleton list if (!m && !l) { @@ -1307,6 +1318,10 @@ namespace Sass { { List* l = dynamic_cast(env["$list"]); Expression* v = ARG("$val", Expression); + if (Selector_List* sl = dynamic_cast(env["$list"])) { + Listize listize(ctx); + l = dynamic_cast(sl->perform(&listize)); + } String_Constant* sep = ARG("$separator", String_Constant); if (!l) { l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); diff --git a/src/inspect.cpp b/src/inspect.cpp index 39d7f21aa..02864178c 100644 --- a/src/inspect.cpp +++ b/src/inspect.cpp @@ -738,7 +738,10 @@ namespace Sass { in_wrapped = true; append_token(s->name(), s); append_string("("); + bool was_comma_array = in_comma_array; + in_comma_array = false; s->selector()->perform(this); + in_comma_array = was_comma_array; append_string(")"); in_wrapped = was; } @@ -816,6 +819,14 @@ namespace Sass { void Inspect::operator()(Selector_List* g) { if (g->empty()) return; + + bool was_comma_array = in_comma_array; + if (!in_declaration && in_comma_array) { + append_string("("); + } + + if (in_declaration) in_comma_array = true; + for (size_t i = 0, L = g->length(); i < L; ++i) { if (!in_wrapped && i == 0) append_indentation(); if ((*g)[i] == 0) continue; @@ -825,6 +836,12 @@ namespace Sass { append_comma_separator(); } } + + in_comma_array = was_comma_array; + if (!in_declaration && in_comma_array) { + append_string(")"); + } + } void Inspect::fallback_impl(AST_Node* n)