From 6e507f4c07ef923fb8b89966a2a71c0ff9611394 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sat, 23 Jan 2016 15:47:20 +0100 Subject: [PATCH] [WIP] POC of refactor to support at-root correctly --- src/ast.cpp | 303 ++++++++++++++++++++++++++- src/ast.hpp | 127 ++++++++---- src/ast_factory.hpp | 2 +- src/ast_fwd_decl.hpp | 4 +- src/constants.cpp | 4 +- src/constants.hpp | 3 + src/context.cpp | 4 + src/cssize.cpp | 140 ++++++++++++- src/cssize.hpp | 9 +- src/debugger.hpp | 43 +++- src/environment.cpp | 2 +- src/eval.cpp | 98 ++++++++- src/eval.hpp | 6 +- src/expand.cpp | 65 +++++- src/expand.hpp | 2 +- src/extend.cpp | 4 +- src/extend.hpp | 2 +- src/functions.cpp | 25 ++- src/inspect.cpp | 26 +-- src/inspect.hpp | 4 +- src/lexer.cpp | 3 +- src/lexer.hpp | 19 ++ src/operation.hpp | 8 +- src/output.cpp | 2 +- src/output.hpp | 2 +- src/parser.cpp | 322 ++++++++++++++++++++-------- src/parser.hpp | 46 +++- src/prelexer.cpp | 403 ++++++++++++++++++++++++++++++++++++ src/prelexer.hpp | 19 ++ src/remove_placeholders.cpp | 4 +- src/remove_placeholders.hpp | 2 +- src/sass.hpp | 1 + src/subset_map.hpp | 16 ++ src/util.cpp | 8 +- 34 files changed, 1504 insertions(+), 224 deletions(-) diff --git a/src/ast.cpp b/src/ast.cpp index e1eb07821d..af6c2f8b00 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -5,6 +5,7 @@ #include "extend.hpp" #include "emitter.hpp" #include "color_maps.hpp" +#include "debugger.hpp" #include #include #include @@ -14,17 +15,145 @@ namespace Sass { static Null sass_null(Sass::Null(ParserState("null"))); + Selector_List* parentzia(std::vector stack, Context& ctx) + { + ParserState pstate("[SELECTOR LIST]"); + Selector_List* list = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate); + if (size_t L = stack.size()) { + if (L == 1) return stack.back(); + if (stack.back() == NULL) return 0; + Selector_List* sel = stack.back()->cloneFully(ctx); + // debug_ast(sel, "Sel"); + for (int i = L - 2; i > -1; -- i) { + if (sel->transparent()) continue; + Selector_List* parent = stack.at(i); + if (parent == NULL) break; + parent = parent->cloneFully(ctx); + sel = parent->connect(sel->cloneFully(ctx), ctx); + } + *list += sel; + } + return list; + } + bool Supports_Operator::needs_parens(Supports_Condition* cond) const { return dynamic_cast(cond) || (dynamic_cast(cond) && dynamic_cast(cond)->operand() != operand()); } + Selector_List* Selector_List::connect(Selector_List* ps, Context& ctx) + { + // fucked up the order of arguments + // if (!ps->connect_parent()) return ps; + + Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); + for (size_t si = 0, sL = this->length(); si < sL; ++si) { + for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) { + Complex_Selector* lhs = at(si)->cloneFully(ctx); + Complex_Selector* rhs = ps->at(pi)->cloneFully(ctx); + // std::cerr << "PARENT: " << rhs->to_string() << "\n"; + // std::cerr << "CHILD: " << lhs->to_string() << "\n"; + if (rhs->connectz()) { + if (lhs->tail() == NULL) { + rhs = rhs->cloneFully(ctx); + lhs->tail(rhs); + if (rhs->has_line_feed()) + lhs->has_line_feed(rhs->has_line_feed()); + rhs->has_line_feed(false); + } else { + rhs = rhs->cloneFully(ctx); + lhs->last()->tail(rhs); + } + // std::cerr << "ADDED: " << lhs->to_string() << "\n"; + *ss << lhs; + } else { + // tricky, since loop is inside out! + if (si == 0) *ss << rhs; + } + } + } + return ss; + } + + bool Supports_Negation::needs_parens(Supports_Condition* cond) const { return dynamic_cast(cond) || dynamic_cast(cond); } +// trim from start +static inline std::string &str_ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); + return s; +} + +// trim from end +static inline std::string &str_rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + return s; +} + + void String_Constant::rtrim() + { + value_ = str_rtrim(value_); + } + void String_Constant::ltrim() + { + value_ = str_ltrim(value_); + } + void String_Constant::trim() + { + rtrim(); + ltrim(); + } + + void String_Schema::rtrim() + { + if (!empty()) { + if (String* str = dynamic_cast(last())) str->rtrim(); + } + } + void String_Schema::ltrim() + { + if (!empty()) { + if (String* str = dynamic_cast(first())) str->ltrim(); + } + } + void String_Schema::trim() + { + rtrim(); + ltrim(); + } + + bool At_Root_Query::exclude(std::string str) + { + bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; + List* l = static_cast(value()); + std::string v; + + if (with) + { + if (!l || l->length() == 0) return str.compare("rule") != 0; + for (size_t i = 0, L = l->length(); i < L; ++i) + { + v = unquote((*l)[i]->to_string()); + if (v.compare("all") == 0 || v == str) return false; + } + return true; + } + else + { + if (!l || !l->length()) return str.compare("rule") == 0; + for (size_t i = 0, L = l->length(); i < L; ++i) + { + v = unquote((*l)[i]->to_string()); + if (v.compare("all") == 0 || v == str) return true; + } + return false; + } + } + void AST_Node::update_pstate(const ParserState& pstate) { pstate_.offset += pstate - pstate_ + pstate.offset; @@ -60,7 +189,7 @@ namespace Sass { bool Compound_Selector::has_parent_ref() { for (Simple_Selector* s : *this) { - if (s->has_parent_ref()) return true; + if (s && s->has_parent_ref()) return true; } return false; } @@ -947,6 +1076,125 @@ namespace Sass { } + Selector_List* Selector_List::parentalize(Selector_List* ps, Context& ctx) + { + Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); + for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) { + Selector_List* list = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); + *list << (*ps)[pi]; + for (size_t si = 0, sL = this->length(); si < sL; ++si) { + *ss += (*this)[si]->parentalize(list, ctx); + } + } + return ss; + } + + + Selector_List* Complex_Selector::parentalize(Selector_List* parents, Context& ctx) + { + + Complex_Selector* tail = this->tail(); + Compound_Selector* head = this->head(); + + // first parentize the tail (which may return an expanded list) + Selector_List* tails = tail ? tail->parentalize(parents, ctx) : 0; + + if (head && head->length() > 0) { + + Selector_List* retval = 0; + // we have a parent selector in a simple compound list + // mix parent complex selector into the compound list + if (dynamic_cast((*head)[0]) && + dynamic_cast((*head)[0])->leave_me_alone()) { + retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); + 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* t = (*tails)[n]; + Complex_Selector* parent = (*parents)[i]; + Complex_Selector* s = parent->cloneFully(ctx); + Complex_Selector* ss = this->clone(ctx); + ss->tail(t ? t->clone(ctx) : 0); + Compound_Selector* h = head_->clone(ctx); + if (h->length()) h->erase(h->begin()); + ss->head(h->length() ? h : 0); + s->append(ctx, ss); + *retval << 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* parent = (*parents)[i]; + Complex_Selector* s = parent->cloneFully(ctx); + Complex_Selector* ss = this->clone(ctx); + // 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) { + throw Exception::InvalidParent(parent, ss); + } + ss->tail(tail ? tail->clone(ctx) : 0); + Compound_Selector* h = head_->clone(ctx); + if (h->length()) h->erase(h->begin()); + ss->head(h->length() ? h : 0); + // \/ IMO ruby sass bug \/ + ss->has_line_feed(false); + s->append(ctx, ss); + *retval << 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* cpy = this->clone(ctx); + cpy->tail((*tails)[n]->cloneFully(ctx)); + cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate())); + for (size_t i = 1, L = this->head()->length(); i < L; ++i) + *cpy->head() << (*this->head())[i]; + if (!cpy->head()->length()) cpy->head(0); + *retval << cpy->skip_empty_reference(); + } + } + // have no parent nor tails + else { + Complex_Selector* cpy = this->clone(ctx); + cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate())); + for (size_t i = 1, L = this->head()->length(); i < L; ++i) + *cpy->head() << (*this->head())[i]; + if (!cpy->head()->length()) cpy->head(0); + *retval << cpy->skip_empty_reference(); + } + } + } + // no parent selector in head + else { + retval = this->tails(ctx, tails); + } + + for (Simple_Selector* ss : *head) { + if (Wrapped_Selector* ws = dynamic_cast(ss)) { + if (Selector_List* sl = dynamic_cast(ws->selector())) { + if (parents) ws->selector(sl->parentalize(parents, ctx)); + } + } + } + + return retval; + + } + // has no head + else { + return this->tails(ctx, tails); + } + + // unreachable + return 0; + } Selector_List* Selector_List::parentize(Selector_List* ps, Context& ctx) { Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); @@ -974,7 +1222,8 @@ namespace Sass { Selector_List* retval = 0; // we have a parent selector in a simple compound list // mix parent complex selector into the compound list - if (dynamic_cast((*head)[0])) { + if (dynamic_cast((*head)[0])/* && + !dynamic_cast((*head)[0])->leave_me_alone()*/) { retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); if (parents && parents->length()) { if (tails && tails->length() > 0) { @@ -987,6 +1236,7 @@ namespace Sass { ss->tail(t ? t->clone(ctx) : 0); Compound_Selector* h = head_->clone(ctx); if (h->length()) h->erase(h->begin()); + s->head()->connectz(false); ss->head(h->length() ? h : 0); s->append(ctx, ss); *retval << s; @@ -1011,6 +1261,7 @@ namespace Sass { ss->head(h->length() ? h : 0); // \/ IMO ruby sass bug \/ ss->has_line_feed(false); + s->head()->connectz(false); s->append(ctx, ss); *retval << s; } @@ -1096,7 +1347,8 @@ namespace Sass { if (head && head->length() == 1) { // abort (and return) if it is not a parent selector - if (!dynamic_cast((*head)[0])) break; + Parent_Selector* p = dynamic_cast((*head)[0]); + if (!p || p->leave_me_alone()) break; } // advance to next cur = cur->tail_; @@ -1167,6 +1419,7 @@ namespace Sass { Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); + cpy->transparent(this->transparent()); if (tail()) cpy->tail(tail()->clone(ctx)); return cpy; } @@ -1176,6 +1429,7 @@ namespace Sass { Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); + cpy->transparent(this->transparent()); if (head()) { cpy->head(head()->clone(ctx)); } @@ -1192,6 +1446,8 @@ namespace Sass { Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); + cpy->transparent(this->transparent()); + cpy->connectz(this->connectz()); cpy->extended(this->extended()); return cpy; } @@ -1201,6 +1457,7 @@ namespace Sass { Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); + cpy->transparent(this->transparent()); return cpy; } @@ -1209,6 +1466,7 @@ namespace Sass { Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); + cpy->transparent(this->transparent()); for (size_t i = 0, L = length(); i < L; ++i) { *cpy << (*this)[i]->cloneFully(ctx); } @@ -1242,12 +1500,24 @@ namespace Sass { } } } + auto s = begin(); + while(s != end()) + { + if(*s == NULL) + { + s = erase(s); + } + else + { + ++s; + } + } } bool Selector_List::has_parent_ref() { for (Complex_Selector* s : *this) { - if (s->has_parent_ref()) return true; + if (s && s->has_parent_ref()) return true; } return false; } @@ -1337,6 +1607,7 @@ namespace Sass { // Ignore any parent selectors, until we find the first non Selector_Reference head Compound_Selector* compound_sel = c->head(); Complex_Selector* pIter = complex_sel; + /* while (pIter) { Compound_Selector* pHead = pIter->head(); if (pHead && dynamic_cast(pHead->elements()[0]) == NULL) { @@ -1346,15 +1617,33 @@ namespace Sass { pIter = pIter->tail(); } + */ if (!pIter->head() || pIter->tail()) { error("nested selectors may not be extended", c->pstate()); } - compound_sel->is_optional(extendee->is_optional()); - + Compound_Selector* placeholder = compound_sel; + placeholder->is_optional(extendee->is_optional()); for (size_t i = 0, L = extender->length(); i < L; ++i) { - extends.put(compound_sel->to_str_vec(), std::make_pair((*extender)[i], compound_sel)); + Complex_Selector* sel = (*extender)[i]; + if (!(sel->head() && sel->head()->length() > 0 && + dynamic_cast((*sel->head())[0]))) + { + Compound_Selector* hh = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, (*extender)[i]->pstate()); + hh->media_block((*extender)[i]->media_block()); + Complex_Selector* ssel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, (*extender)[i]->pstate()); + ssel->media_block((*extender)[i]->media_block()); + if (sel->has_line_feed()) ssel->has_line_feed(true); + Parent_Selector* ps = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, (*extender)[i]->pstate()); + ps->media_block((*extender)[i]->media_block()); + *hh << ps; + ssel->tail(sel); + ssel->head(hh); + sel = ssel; + } + // std::cerr << "ADD TO A " << placeholder->to_string() << ": " << sel->to_string() << "\n"; + extends.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder)); } } }; diff --git a/src/ast.hpp b/src/ast.hpp index 4663765e84..5ca93974a2 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -54,6 +54,8 @@ namespace Sass { // Note: most methods follow precision option const double NUMBER_EPSILON = 0.00000000000001; + Selector_List* parentzia(std::vector, Context& ctx); + // ToDo: where does this fit best? // We don't share this with C-API? class Operand { @@ -448,11 +450,13 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////////// class Ruleset : public Has_Block { ADD_PROPERTY(Selector*, selector) + ADD_PROPERTY(bool, connect_parent); ADD_PROPERTY(bool, at_root); ADD_PROPERTY(bool, is_root); + ADD_PROPERTY(bool, chroot); public: Ruleset(ParserState pstate, Selector* s = 0, Block* b = 0) - : Has_Block(pstate, b), selector_(s), at_root_(false), is_root_(false) + : Has_Block(pstate, b), selector_(s), connect_parent_(true), at_root_(false), is_root_(false), chroot_(false) { statement_type(RULESET); } bool is_invisible() const; // nested rulesets need to be hoisted out of their enclosing blocks @@ -508,12 +512,12 @@ namespace Sass { // At-rules -- arbitrary directives beginning with "@" that may have an // optional statement block. /////////////////////////////////////////////////////////////////////// - class At_Rule : public Has_Block { + class Directive : public Has_Block { ADD_PROPERTY(std::string, keyword) ADD_PROPERTY(Selector*, selector) ADD_PROPERTY(Expression*, value) public: - At_Rule(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0) + Directive(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0) : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed { statement_type(DIRECTIVE); } bool bubbles() { return is_keyframes() || is_media(); } @@ -1447,6 +1451,9 @@ namespace Sass { { concrete_type(STRING); } static std::string type_name() { return "string"; } virtual ~String() = 0; + virtual void rtrim() = 0; + virtual void ltrim() = 0; + virtual void trim() = 0; virtual bool operator==(const Expression& rhs) const = 0; ATTACH_OPERATIONS() }; @@ -1475,6 +1482,9 @@ namespace Sass { } return false; } + virtual void rtrim(); + virtual void ltrim(); + virtual void trim(); virtual size_t hash() { @@ -1514,6 +1524,9 @@ namespace Sass { { } std::string type() { return "string"; } static std::string type_name() { return "string"; } + virtual void rtrim(); + virtual void ltrim(); + virtual void trim(); virtual size_t hash() { @@ -1671,42 +1684,15 @@ namespace Sass { ///////////////////////////////////////////////// // At root expressions (for use inside @at-root). ///////////////////////////////////////////////// - class At_Root_Expression : public Expression { + class At_Root_Query : public Expression { private: - ADD_PROPERTY(String*, feature) + ADD_PROPERTY(Expression*, feature) ADD_PROPERTY(Expression*, value) - ADD_PROPERTY(bool, is_interpolated) public: - At_Root_Expression(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false) - : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) + At_Root_Query(ParserState pstate, Expression* f = 0, Expression* v = 0, bool i = false) + : Expression(pstate), feature_(f), value_(v) { } - bool exclude(std::string str) - { - bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; - List* l = static_cast(value()); - std::string v; - - if (with) - { - if (!l || l->length() == 0) return str.compare("rule") != 0; - for (size_t i = 0, L = l->length(); i < L; ++i) - { - v = unquote((*l)[i]->to_string()); - if (v.compare("all") == 0 || v == str) return false; - } - return true; - } - else - { - if (!l || !l->length()) return str.compare("rule") == 0; - for (size_t i = 0, L = l->length(); i < L; ++i) - { - v = unquote((*l)[i]->to_string()); - if (v.compare("all") == 0 || v == str) return true; - } - return false; - } - } + bool exclude(std::string str); ATTACH_OPERATIONS() }; @@ -1714,9 +1700,9 @@ namespace Sass { // At-root. /////////// class At_Root_Block : public Has_Block { - ADD_PROPERTY(At_Root_Expression*, expression) + ADD_PROPERTY(At_Root_Query*, expression) public: - At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Expression* e = 0) + At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Query* e = 0) : Has_Block(pstate, b), expression_(e) { statement_type(ATROOT); } bool is_hoistable() { return true; } @@ -1724,7 +1710,7 @@ namespace Sass { bool exclude_node(Statement* s) { if (s->statement_type() == Statement::DIRECTIVE) { - return expression()->exclude(static_cast(s)->keyword().erase(0, 1)); + return expression()->exclude(static_cast(s)->keyword().erase(0, 1)); } if (s->statement_type() == Statement::MEDIA) { @@ -1738,7 +1724,7 @@ namespace Sass { { return expression()->exclude("supports"); } - if (static_cast(s)->is_keyframes()) + if (static_cast(s)->is_keyframes()) { return expression()->exclude("keyframes"); } @@ -1854,6 +1840,7 @@ namespace Sass { ADD_PROPERTY(bool, has_line_break) // maybe we have optional flag ADD_PROPERTY(bool, is_optional) + ADD_PROPERTY(bool, transparent); // parent block pointers ADD_PROPERTY(Media_Block*, media_block) protected: @@ -1866,14 +1853,19 @@ namespace Sass { has_line_feed_(false), has_line_break_(false), is_optional_(false), + transparent_(false), media_block_(0), hash_(0) { concrete_type(SELECTOR); } + virtual bool contains_placeholder(void) { + return has_placeholder(); + } virtual ~Selector() = 0; virtual size_t hash() = 0; virtual bool has_parent_ref() { return false; } + virtual void set_optional(bool flag) = 0; virtual unsigned long specificity() { return Constants::Specificity_Universal; } @@ -1891,6 +1883,7 @@ namespace Sass { Selector_Schema(ParserState pstate, String* c) : Selector(pstate), contents_(c), at_root_(false) { } + virtual void set_optional(bool flag) {}; virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, contents_->hash()); @@ -1935,6 +1928,9 @@ namespace Sass { } return hash_; } + virtual void set_optional(bool flag) { + is_optional(flag); + } // namespace query functions bool is_universal_ns() const { @@ -1987,9 +1983,11 @@ namespace Sass { // inside strings in declarations (Compound_Selector). // only one simple parent selector means the first case. class Parent_Selector : public Simple_Selector { + ADD_PROPERTY(bool, leave_me_alone) public: Parent_Selector(ParserState pstate) - : Simple_Selector(pstate, "&") + : Simple_Selector(pstate, "&"), + leave_me_alone_(false) { /* has_reference(true); */ } virtual bool has_parent_ref() { return true; }; virtual unsigned long specificity() @@ -2152,6 +2150,9 @@ namespace Sass { Wrapped_Selector(ParserState pstate, std::string n, Selector* sel) : Simple_Selector(pstate, n), selector_(sel) { } + virtual bool contains_placeholder() { + return has_placeholder(); + }; virtual bool has_parent_ref() { // if (has_reference()) return true; if (!selector()) return false; @@ -2193,6 +2194,7 @@ namespace Sass { SourcesSet sources_; ADD_PROPERTY(bool, extended); ADD_PROPERTY(bool, has_parent_reference); + ADD_PROPERTY(bool, connectz); protected: void adjust_after_pushing(Simple_Selector* s) { @@ -2204,14 +2206,20 @@ namespace Sass { : Selector(pstate), Vectorized(s), extended_(false), - has_parent_reference_(false) + has_parent_reference_(false), + connectz_(true) { } - bool contains_placeholder() { + virtual bool contains_placeholder() { for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->has_placeholder()) return true; + if ((*this)[i]->contains_placeholder()) return true; } return false; }; + virtual void set_optional(bool flag) { + is_optional(flag); + for (size_t i = 0, L = length(); i < L; ++i) + { (*this)[i]->set_optional(flag); } + } bool is_universal() const { @@ -2256,7 +2264,8 @@ namespace Sass { bool is_empty_reference() { return length() == 1 && - dynamic_cast((*this)[0]); + dynamic_cast((*this)[0]) && + ! dynamic_cast((*this)[0])->leave_me_alone(); } std::vector to_str_vec(); // sometimes need to convert to a flat "by-value" data structure @@ -2289,11 +2298,15 @@ namespace Sass { ADD_PROPERTY(Complex_Selector*, tail) ADD_PROPERTY(String*, reference); public: - bool contains_placeholder() { + virtual bool contains_placeholder() { if (head() && head()->contains_placeholder()) return true; if (tail() && tail()->contains_placeholder()) return true; return false; }; + bool connectz() { + return (!head_ || head_->connectz()) && + (!tail_ || tail_->connectz()); + } Complex_Selector(ParserState pstate, Combinator c = ANCESTOR_OF, Compound_Selector* h = 0, @@ -2309,6 +2322,7 @@ namespace Sass { } virtual bool has_parent_ref(); + Complex_Selector* skip_empty_reference() { if ((!head_ || !head_->length() || head_->is_empty_reference()) && @@ -2322,6 +2336,12 @@ namespace Sass { return this; } + virtual void set_optional(bool flag) { + is_optional(flag); + if (head_) head_->set_optional(flag); + if (tail_) tail_->set_optional(flag); + } + // can still have a tail bool is_empty_ancestor() const { @@ -2351,6 +2371,7 @@ namespace Sass { size_t length() const; Selector_List* parentize(Selector_List* parents, Context& ctx); + Selector_List* parentalize(Selector_List* parents, Context& ctx); virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = ""); @@ -2441,19 +2462,33 @@ namespace Sass { /////////////////////////////////// class Selector_List : public Selector, public Vectorized { ADD_PROPERTY(std::vector, wspace) + ADD_PROPERTY(bool, connect_parent) protected: void adjust_after_pushing(Complex_Selector* c); public: Selector_List(ParserState pstate, size_t s = 0) - : Selector(pstate), Vectorized(s), wspace_(0) + : Selector(pstate), Vectorized(s), wspace_(0), connect_parent_(true) { } + virtual bool contains_placeholder() { + for (size_t i = 0, L = length(); i < L; ++i) { + if ((*this)[i]->contains_placeholder()) return true; + } + return false; + }; std::string type() { return "list"; } // remove parent selector references // basically unwraps parsed selectors virtual bool has_parent_ref(); void remove_parent_selectors(); + virtual void set_optional(bool flag) { + is_optional(flag); + for (size_t i = 0, L = length(); i < L; ++i) + { (*this)[i]->set_optional(flag); } + } // virtual Selector_Placeholder* find_placeholder(); + Selector_List* connect(Selector_List* parents, Context& ctx); Selector_List* parentize(Selector_List* parents, Context& ctx); + Selector_List* parentalize(Selector_List* parents, Context& ctx); virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = ""); diff --git a/src/ast_factory.hpp b/src/ast_factory.hpp index c2e1830ff8..9a0a9b68e4 100644 --- a/src/ast_factory.hpp +++ b/src/ast_factory.hpp @@ -17,7 +17,7 @@ namespace Sass { Supports_Query* new_Supports_Query(std::string p, size_t l, Supports_Query* f, Block* b); Media_Query* new_Media_Query(std::string p, size_t l, List* q, Block* b); At_Root_Block* new_At_Root_Block(std::string p, size_t l, Selector* sel, Block* b); - At_Rule* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b); + Directive* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b); Keyframe_Rule* new_Keyframe_Rule(std::string p, size_t l, Block* b); Declaration* new_Declaration(std::string p, size_t l, String* prop, List* vals); Assignment* new_Assignment(std::string p, size_t l, std::string var, Expression* val, bool guarded = false); diff --git a/src/ast_fwd_decl.hpp b/src/ast_fwd_decl.hpp index 7d4db19e55..9082ebed9d 100644 --- a/src/ast_fwd_decl.hpp +++ b/src/ast_fwd_decl.hpp @@ -15,7 +15,7 @@ namespace Sass { class Bubble; class Media_Block; class Supports_Block; - class At_Rule; + class Directive; class Keyframe_Rule; class At_Root_Block; class Declaration; @@ -62,7 +62,7 @@ namespace Sass { class Supports_Negation; class Supports_Declaration; class Supports_Interpolation; - class At_Root_Expression; + class At_Root_Query; class Null; class Parent_Selector; // parameters and arguments diff --git a/src/constants.cpp b/src/constants.cpp index 341890b523..b6af8fa22b 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -93,6 +93,8 @@ namespace Sass { extern const char webkit_calc_kwd[] = "-webkit-calc("; extern const char ms_calc_kwd[] = "-ms-calc("; + extern const char almost_any_value_class[] = "\"'\#!;{}"; + // css selector keywords extern const char sel_deep_kwd[] = "/deep/"; @@ -155,7 +157,7 @@ namespace Sass { extern const char selector_combinator_ops[] = "+~>"; // optional modifiers for alternative compare context extern const char attribute_compare_modifiers[] = "~|^$*"; - extern const char selector_lookahead_ops[] = "*&%,()[]"; + extern const char selector_lookahead_ops[] = "*&%,[]"; // byte order marks // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) diff --git a/src/constants.hpp b/src/constants.hpp index 8156d02402..f517420cec 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -93,6 +93,9 @@ namespace Sass { extern const char webkit_calc_kwd[]; extern const char ms_calc_kwd[]; + // char classes for "regular expressions" + extern const char almost_any_value_class[]; + // css selector keywords extern const char sel_deep_kwd[]; diff --git a/src/context.cpp b/src/context.cpp index b382545f35..048c86231d 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -21,6 +21,7 @@ #include "cssize.hpp" #include "listize.hpp" #include "extend.hpp" +#include "debugger.hpp" #include "remove_placeholders.hpp" #include "functions.hpp" #include "sass_functions.hpp" @@ -649,9 +650,12 @@ namespace Sass { Expand expand(*this, &global, &backtrace); Cssize cssize(*this, &backtrace); // expand and eval the tree + // debug_ast(root); root = root->perform(&expand)->block(); // merge and bubble certain rules +// debug_ast(root); root = root->perform(&cssize)->block(); +// debug_ast(root); // should we extend something? if (!subset_map.empty()) { // create crtp visitor object diff --git a/src/cssize.cpp b/src/cssize.cpp index b106fd2169..1dd33bd0f2 100644 --- a/src/cssize.cpp +++ b/src/cssize.cpp @@ -6,6 +6,7 @@ #include "cssize.hpp" #include "context.hpp" #include "backtrace.hpp" +#include "debugger.hpp" namespace Sass { @@ -13,14 +14,113 @@ namespace Sass { : ctx(ctx), block_stack(std::vector()), p_stack(std::vector()), + s_stack(std::vector()), backtrace(bt) - { } + { + s_stack.push_back(0); + } Statement* Cssize::parent() { return p_stack.size() ? p_stack.back() : block_stack.front(); } + Selector_List* Cssize::selector() + { + return s_stack.size() ? s_stack.back() : 0; + } + + void Cssize::expand_selector_list(Selector* s, Selector_List* extender) { + + /* + */ + + Selector_List* contextualized = dynamic_cast(s); + + // debug_ast(s); + if (Selector_List* sl = contextualized) { + for (Complex_Selector* complex_selector : sl->elements()) { + Complex_Selector* tail = complex_selector; + while (tail) { + if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) { + if (dynamic_cast(header) == NULL) continue; // skip all others + std::string sel_str(complex_selector->to_string(ctx.c_options)); + error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), 0); + } + tail = tail->tail(); + } + } + } + + if (contextualized == NULL) return; + for (auto complex_sel : contextualized->elements()) { + Complex_Selector* c = complex_sel; + /* + if (!c->head() || c->tail()) { + std::string sel_str(contextualized->to_string(ctx.c_options)); + error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), 0); + } + */ + Compound_Selector* placeholder = c->head(); + placeholder->is_optional(s->is_optional()); + for (size_t i = 0, L = extender->length(); i < L; ++i) { + Complex_Selector* sel = (*extender)[i]; + if (!(sel->head() && sel->head()->length() > 0 && + dynamic_cast((*sel->head())[0]))) + { + Compound_Selector* hh = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, (*extender)[i]->pstate()); + hh->media_block((*extender)[i]->media_block()); + Complex_Selector* ssel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, (*extender)[i]->pstate()); + ssel->media_block((*extender)[i]->media_block()); + if (sel->has_line_feed()) ssel->has_line_feed(true); + Parent_Selector* ps = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, (*extender)[i]->pstate()); + ps->media_block((*extender)[i]->media_block()); + *hh << ps; + ssel->tail(sel); + ssel->head(hh); + sel = ssel; + } + // std::cerr << "ADD TO W " << placeholder->to_string() << ": " << sel->to_string() << "\n"; + ctx.subset_map.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder)); + } + } + + } + + Statement* Cssize::operator()(Extension* e) + { + if (Selector_List* extender = dynamic_cast(selector())) { + + std::vector stack; + + for (int i = p_stack.size() - 1; i != -1; --i) { + if (Ruleset* rs = dynamic_cast(p_stack.at(i))) { + // if (l->connect_parent() == false) break; + if (Selector_List* r_sel = dynamic_cast(rs->selector())) { + stack.insert(stack.begin(), r_sel); + // extender = r_sel->connect(extender, ctx); + // l->connect_parent(r_sel->connect_parent()); + } + } + } + expand_selector_list(e->selector(), parentzia(stack, ctx)); + return 0; + + for (int i = s_stack.size() - 2; i > -1; --i) { + if (!extender->connect_parent()) break; + if (s_stack.at(i) == NULL) break; + extender = s_stack.at(i)->connect(extender, ctx); + } + s_stack.push_back(0); + Selector* s = e->selector(); + // debug_ast(extender, "ext: "); + extender->connect_parent(true); // HACK + expand_selector_list(s, extender); + s_stack.pop_back(); + } + return 0; + } + Statement* Cssize::operator()(Block* b) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, b->pstate(), b->length(), b->is_root()); @@ -31,7 +131,7 @@ namespace Sass { return bb; } - Statement* Cssize::operator()(At_Rule* r) + Statement* Cssize::operator()(Directive* r) { if (!r->block() || !r->block()->length()) return r; @@ -41,7 +141,7 @@ namespace Sass { } p_stack.push_back(r); - At_Rule* rr = SASS_MEMORY_NEW(ctx.mem, At_Rule, + Directive* rr = SASS_MEMORY_NEW(ctx.mem, Directive, r->pstate(), r->keyword(), r->selector(), @@ -57,7 +157,7 @@ namespace Sass { else { s = static_cast(s)->node(); if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false; - else directive_exists = (static_cast(s)->keyword() == rr->keyword()); + else directive_exists = (static_cast(s)->keyword() == rr->keyword()); } } @@ -65,7 +165,7 @@ namespace Sass { Block* result = SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate()); if (!(directive_exists || rr->is_keyframes())) { - At_Rule* empty_node = static_cast(rr); + Directive* empty_node = static_cast(rr); empty_node->block(SASS_MEMORY_NEW(ctx.mem, Block, rr->block() ? rr->block()->pstate() : rr->pstate())); *result << empty_node; } @@ -93,11 +193,14 @@ namespace Sass { Statement* Cssize::operator()(Ruleset* r) { p_stack.push_back(r); + Selector_List* sel = dynamic_cast(r->selector()); + s_stack.push_back(sel); Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset, r->pstate(), r->selector(), r->block()->perform(this)->block()); // rr->tabs(r->block()->tabs()); + s_stack.pop_back(); p_stack.pop_back(); if (!rr->block()) { @@ -127,6 +230,24 @@ namespace Sass { rules->unshift(rr); } +// if (p_stack.size()) debug_ast(p_stack.back(), "stack: "); +// debug_ast(rr, "in: "); +if (Selector_List* l = dynamic_cast(rr->selector())) { + for (int i = p_stack.size() - 1; i != -1; --i) { + if (Ruleset* rs = dynamic_cast(p_stack.at(i))) { + // if (l->connect_parent() == false) break; + if (Selector_List* r_sel = dynamic_cast(rs->selector())) { +// debug_ast(l); + l = r_sel->connect(l, ctx); + // l->connect_parent(r_sel->connect_parent()); + } + } + } + rr->selector(l); + // debug_ast(l, "out: "); +} + + rules = debubble(rules)->block(); if (!(!rules->length() || @@ -188,6 +309,7 @@ namespace Sass { Statement* Cssize::operator()(At_Root_Block* m) { + // debug_ast(m); bool tmp = false; for (size_t i = 0, L = p_stack.size(); i < L; ++i) { Statement* s = p_stack[i]; @@ -213,7 +335,7 @@ namespace Sass { return bubble(m); } - Statement* Cssize::bubble(At_Rule* m) + Statement* Cssize::bubble(Directive* m) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate()); Has_Block* new_rule = static_cast(shallow_copy(this->parent())); @@ -227,7 +349,7 @@ namespace Sass { Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block() ? m->block()->pstate() : m->pstate()); *wrapper_block << new_rule; - At_Rule* mm = SASS_MEMORY_NEW(ctx.mem, At_Rule, + Directive* mm = SASS_MEMORY_NEW(ctx.mem, Directive, m->pstate(), m->keyword(), m->selector(), @@ -268,6 +390,7 @@ namespace Sass { parent->pstate(), parent->selector(), bb); + new_rule->connect_parent(parent->connect_parent()); new_rule->tabs(parent->tabs()); for (size_t i = 0, L = m->block()->length(); i < L; ++i) { @@ -296,6 +419,7 @@ namespace Sass { parent->pstate(), parent->selector(), bb); + new_rule->connect_parent(parent->connect_parent()); new_rule->tabs(parent->tabs()); for (size_t i = 0, L = m->block()->length(); i < L; ++i) { @@ -374,7 +498,7 @@ namespace Sass { case Statement::BUBBLE: return SASS_MEMORY_NEW(ctx.mem, Bubble, *static_cast(s)); case Statement::DIRECTIVE: - return SASS_MEMORY_NEW(ctx.mem, At_Rule, *static_cast(s)); + return SASS_MEMORY_NEW(ctx.mem, Directive, *static_cast(s)); case Statement::SUPPORTS: return SASS_MEMORY_NEW(ctx.mem, Supports_Block, *static_cast(s)); case Statement::ATROOT: diff --git a/src/cssize.hpp b/src/cssize.hpp index 9d5a34ad5d..03bf455961 100644 --- a/src/cssize.hpp +++ b/src/cssize.hpp @@ -16,6 +16,7 @@ namespace Sass { Context& ctx; std::vector block_stack; std::vector p_stack; + std::vector s_stack; Backtrace* backtrace; Statement* fallback_impl(AST_Node* n); @@ -24,14 +25,18 @@ namespace Sass { Cssize(Context&, Backtrace*); ~Cssize() { } + Selector_List* selector(); + void expand_selector_list(Selector*, Selector_List* extender); + Statement* operator()(Block*); Statement* operator()(Ruleset*); + Statement* operator()(Extension*); // Statement* operator()(Propset*); // Statement* operator()(Bubble*); Statement* operator()(Media_Block*); Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); - Statement* operator()(At_Rule*); + Statement* operator()(Directive*); Statement* operator()(Keyframe_Rule*); // Statement* operator()(Declaration*); // Statement* operator()(Assignment*); @@ -53,7 +58,7 @@ namespace Sass { Statement* parent(); std::vector> slice_by_bubble(Statement*); - Statement* bubble(At_Rule*); + Statement* bubble(Directive*); Statement* bubble(At_Root_Block*); Statement* bubble(Media_Block*); Statement* bubble(Supports_Block*); diff --git a/src/debugger.hpp b/src/debugger.hpp index a4ab39f153..35c4742d9a 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -65,6 +65,14 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << bubble->tabs(); std::cerr << std::endl; + } else if (dynamic_cast(node)) { + At_Root_Query* root_query = dynamic_cast(node); + std::cerr << ind << "At_Root_Query " << root_query; + std::cerr << " <" << root_query->type() << ">"; + std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << std::endl; + debug_ast(root_query->feature(), ind + "F:", env); + debug_ast(root_query->value(), ind + "::", env); } else if (dynamic_cast(node)) { At_Root_Block* root_block = dynamic_cast(node); std::cerr << ind << "At_Root_Block " << root_block; @@ -79,8 +87,13 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " [@media:" << selector->media_block() << "]"; + std::cerr << (selector->transparent() ? " [transparent]" : ""); + std::cerr << (selector->has_placeholder() ? " [has-placeholder]": " -"); + std::cerr << (selector->contains_placeholder() ? " [contains-placeholder]": " -"); + std::cerr << (selector->connect_parent() ? " [connect]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -"); std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); + // std::cerr << (selector->has_parent_ref() ? " [contains-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl; @@ -97,7 +110,10 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) // if (selector->not_selector()) cerr << " [in_declaration]"; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; - std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">"; + // this should not be cleaned by "remove_parent_selector" + if (selector->leave_me_alone()) std::cerr << " [leave me alone] "; + std::cerr << std::endl; // debug_ast(selector->selector(), ind + "->", env); } else if (dynamic_cast(node)) { @@ -106,8 +122,12 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) << " (" << pstate_source_position(node) << ")" << " <" << selector->hash() << ">" << " [weight:" << longToHex(selector->specificity()) << "]" - << " [@media:" << selector->media_block() << "]" - << (selector->is_optional() ? " [is_optional]": " -") + << " [@media:" << selector->media_block() << "]"; + std::cerr << (selector->connectz() ? " [connect]": " -"); + std::cerr << (selector->transparent() ? " [transparent]" : ""); + std::cerr << (selector->has_placeholder() ? " [has-placeholder]": " -"); + std::cerr << (selector->contains_placeholder() ? " [contains-placeholder]": " -"); + std::cerr << (selector->is_optional() ? " [is_optional]": " -") << (selector->has_parent_ref() ? " [has parent]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << (selector->has_line_break() ? " [line-break]": " -") @@ -137,7 +157,11 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " <" << selector->hash() << ">"; std::cerr << " [weight:" << longToHex(selector->specificity()) << "]"; std::cerr << " [@media:" << selector->media_block() << "]"; + std::cerr << (selector->connectz() ? " [connect]": " -"); std::cerr << (selector->extended() ? " [extended]": " -"); + std::cerr << (selector->transparent() ? " [transparent]" : ""); + std::cerr << (selector->has_placeholder() ? " [has-placeholder]": " -"); + std::cerr << (selector->contains_placeholder() ? " [contains-placeholder]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -"); std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); @@ -156,6 +180,9 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; + std::cerr << (selector->transparent() ? " [transparent]" : ""); + std::cerr << (selector->has_placeholder() ? " [has-placeholder]": " -"); + std::cerr << (selector->contains_placeholder() ? " [contains-placeholder]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -"); std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); @@ -203,6 +230,9 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; + std::cerr << (selector->transparent() ? " [transparent]" : ""); + std::cerr << (selector->has_placeholder() ? " [has-placeholder]": " -"); + std::cerr << (selector->contains_placeholder() ? " [contains-placeholder]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -"); std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); @@ -386,9 +416,9 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " " << has_block->tabs() << std::endl; if (has_block->selector()) debug_ast(has_block->selector(), ind + "@"); if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); } - } else if (dynamic_cast(node)) { - At_Rule* block = dynamic_cast(node); - std::cerr << ind << "At_Rule " << block; + } else if (dynamic_cast(node)) { + Directive* block = dynamic_cast(node); + std::cerr << ind << "Directive " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl; debug_ast(block->selector(), ind + "~", env); @@ -437,6 +467,7 @@ inline void debug_ast(AST_Node* node, std::string ind, Env* env) std::cerr << " [indent: " << ruleset->tabs() << "]"; std::cerr << (ruleset->at_root() ? " [@ROOT]" : ""); std::cerr << (ruleset->is_root() ? " [root]" : ""); + std::cerr << (ruleset->connect_parent() ? " [connect]" : ""); std::cerr << std::endl; debug_ast(ruleset->selector(), ind + ">"); debug_ast(ruleset->block(), ind + " "); diff --git a/src/environment.cpp b/src/environment.cpp index a8cbd52c51..c1c369307e 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -180,7 +180,7 @@ namespace Sass { if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) { std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second; if (Value* val = dynamic_cast(i->second)) - { std::cerr << " : " << val->to_string(true, 5); } + { std::cerr << " : " << val->to_string(); } std::cerr << std::endl; } } diff --git a/src/eval.cpp b/src/eval.cpp index a751e4378e..13b8afc0ad 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -21,6 +21,7 @@ #include "backtrace.hpp" #include "lexer.hpp" #include "prelexer.hpp" +#include "debugger.hpp" #include "parser.hpp" #include "expand.hpp" #include "color_maps.hpp" @@ -41,7 +42,8 @@ namespace Sass { Eval::Eval(Expand& exp) : exp(exp), - ctx(exp.ctx) + ctx(exp.ctx), + skip_parentization(false) { } Eval::~Eval() { } @@ -1252,17 +1254,16 @@ namespace Sass { return cc; } - Expression* Eval::operator()(At_Root_Expression* e) + Expression* Eval::operator()(At_Root_Query* e) { Expression* feature = e->feature(); feature = (feature ? feature->perform(this) : 0); Expression* value = e->value(); value = (value ? value->perform(this) : 0); - Expression* ee = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, + Expression* ee = SASS_MEMORY_NEW(ctx.mem, At_Root_Query, e->pstate(), static_cast(feature), - value, - e->is_interpolated()); + value); return ee; } @@ -1650,9 +1651,13 @@ namespace Sass { Selector_List* Eval::operator()(Selector_List* s) { +// debug_ast(s, "pref: "); std::vector rv; Selector_List* sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, s->pstate()); + sl->connect_parent(s->connect_parent()); + sl->is_optional(s->is_optional()); sl->media_block(s->media_block()); + sl->transparent(s->transparent()); for (size_t i = 0, iL = s->length(); i < iL; ++i) { rv.push_back(operator()((*s)[i])); } @@ -1675,12 +1680,58 @@ namespace Sass { } } +/* +// this fixes some, bails other + if (s->has_parent_ref()) { + for (int i = exp.selector_stack.size() - 1; i > -1; --i) { + if (sl->connect_parent()) break; + if (Selector_List* r_sl = exp.selector_stack.at(i)) { + sl = r_sl->connect(sl, ctx); + sl->connect_parent(r_sl->connect_parent()); + if (!sl->connect_parent()) break; + } else break; + } + // } + } +*/ + return sl; } Selector_List* Eval::operator()(Complex_Selector* s) { + // return s->parentize(selector(), ctx); + // debug_ast(s, "in: "); + // if (skip_parentization) { + Selector_List* ls = SASS_MEMORY_NEW(ctx.mem, Selector_List, s->pstate()); + Complex_Selector* ss = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *s); + Compound_Selector* sh = s->head(); + ; + + Selector_List* pls = parentzia(exp.selector_stack, ctx); +// debug_ast(pls, "PARENTZIA: "); + + auto a = s->parentize(pls, ctx); +// debug_ast(a); + return a; + + for (size_t i = 0; sh && i < sh->length(); i++) { + (*sh)[i] = dynamic_cast((*sh)[i]->perform(this)); + } + + if (Complex_Selector* st = ss->tail()) { + if (Selector_List* tails = operator()(st)) { + ss->tail(tails->at(0)); + } + } + + // ss->head(Compound_); + *ls << s; + // ls->remove_parent_selectors(); + return ls; + // }; + return s->parentize(selector(), ctx); } @@ -1701,11 +1752,46 @@ namespace Sass { std::string result_str(s->contents()->perform(this)->to_string(ctx.c_options)); result_str = unquote(Util::rtrim(result_str)) + "{"; Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()); - return operator()(p.parse_selector_list(exp.block_stack.back()->is_root())); + // std::cerr << result_str.c_str() << "]]\n"; + auto a = p.parse_selector_list(exp.block_stack.back()->is_root()); + auto b = operator()(a); + b->connect_parent(!a->has_parent_ref()); + for (auto c : *b) { + // c->head()->connectz(!c->has_parent_ref()); + } + return b; + } + + Wrapped_Selector* Eval::operator()(Wrapped_Selector* w) + { + // debug_ast(w, "w= "); + return w; } Expression* Eval::operator()(Parent_Selector* p) { + + // when we interpolate a parent in eval stage, we actually + // freeze the current nesting. Connected rules (implicit + // parent) are evaluated in cssize, don't confuse these! + + if (Selector_List* pl = exp.selector_stack.back()) { + for (int i = exp.selector_stack.size() - 2; i > -1; --i) { + // if (!pl->connect_parent()) break; + if (exp.selector_stack.at(i) == NULL) break; + pl = exp.selector_stack.at(i)->cloneFully(ctx)->connect(pl->cloneFully(ctx), ctx); + // pl->connect_parent(exp.selector_stack.at(i)->connect_parent()); + + // pl = exp.selector_stack.at(i)->connect(pl, ctx); + // pl->connect_parent(exp.selector_stack.at(i)->connect_parent()); + + } + return pl; + } + return SASS_MEMORY_NEW(ctx.mem, Null, p->pstate()); + + + // if (p->leave_me_alone()) return p; Selector_List* pr = selector(); if (pr) { exp.selector_stack.pop_back(); diff --git a/src/eval.hpp b/src/eval.hpp index 08b8724a70..911410e400 100644 --- a/src/eval.hpp +++ b/src/eval.hpp @@ -19,6 +19,7 @@ namespace Sass { public: Expand& exp; Context& ctx; + bool skip_parentization; Eval(Expand& exp); ~Eval(); @@ -57,7 +58,7 @@ namespace Sass { // Expression* operator()(Selector_List*); Expression* operator()(Media_Query*); Expression* operator()(Media_Query_Expression*); - Expression* operator()(At_Root_Expression*); + Expression* operator()(At_Root_Query*); Expression* operator()(Supports_Operator*); Expression* operator()(Supports_Negation*); Expression* operator()(Supports_Declaration*); @@ -70,11 +71,12 @@ namespace Sass { // these will return selectors Selector_List* operator()(Selector_List*); Selector_List* operator()(Complex_Selector*); + Wrapped_Selector* operator()(Wrapped_Selector*); Attribute_Selector* operator()(Attribute_Selector*); // they don't have any specific implementatio (yet) Type_Selector* operator()(Type_Selector* s) { return s; }; Pseudo_Selector* operator()(Pseudo_Selector* s) { return s; }; - Wrapped_Selector* operator()(Wrapped_Selector* s) { return s; }; + // Wrapped_Selector* operator()(Wrapped_Selector* s) { return s; }; Selector_Qualifier* operator()(Selector_Qualifier* s) { return s; }; Selector_Placeholder* operator()(Selector_Placeholder* s) { return s; }; // actual evaluated selectors diff --git a/src/expand.cpp b/src/expand.cpp index 494807016e..2f249dcb97 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -2,6 +2,7 @@ #include #include +#include "debugger.hpp" #include "expand.hpp" #include "bind.hpp" #include "eval.hpp" @@ -116,14 +117,26 @@ namespace Sass { Expression* ex = r->selector()->perform(&eval); Selector_List* sel = dynamic_cast(ex); if (sel == 0) throw std::runtime_error("Expanded null selector"); - + if (Selector_List* ls = dynamic_cast(r->selector())) { + sel->connect_parent(ls->connect_parent()); + } selector_stack.push_back(sel); Env* env = 0; if (block_stack.back()->is_root()) { env = new Env(environment()); env_stack.push_back(env); } +if (r->chroot()) { + auto q = selector_stack.back(); + selector_stack.push_back(0); + selector_stack.push_back(q); + // selector_stack.push_back(dynamic_cast(r->selector())); +} Block* blk = r->block()->perform(this)->block(); +if (r->chroot()) { + selector_stack.pop_back(); + selector_stack.pop_back(); +} Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset, r->pstate(), sel, @@ -134,6 +147,7 @@ namespace Sass { delete env; } rr->tabs(r->tabs()); + rr->connect_parent(r->connect_parent()); return rr; } @@ -205,17 +219,37 @@ namespace Sass { // if (ab) ab->is_root(true); Expression* ae = a->expression(); if (ae) ae = ae->perform(&eval); - else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, a->pstate()); + else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Query, a->pstate()); + if (auto at_b = dynamic_cast(a->block())) { + for (auto at_bb : *at_b) { + if (auto at_r = dynamic_cast(at_bb)) { + if (dynamic_cast(at_r->selector())) { + } + at_r->chroot(true); + } + } + } Block* bb = ab ? ab->perform(this)->block() : 0; At_Root_Block* aa = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, a->pstate(), bb, - static_cast(ae)); - // aa->block()->is_root(true); + static_cast(ae)); + for (auto x : *bb) { + if (auto r = dynamic_cast(x)) { + if (auto l = dynamic_cast(r->selector())) { + for (auto c : *l) { + if (!c->head()) continue; + c->head()->connectz(false); + } + // debug_ast(l, " XX "); + } + } + } + // debug_ast(bb, " -- "); return aa; } - Statement* Expand::operator()(At_Rule* a) + Statement* Expand::operator()(Directive* a) { LOCAL_FLAG(in_keyframes, a->is_keyframes()); Block* ab = a->block(); @@ -226,7 +260,7 @@ namespace Sass { if (as) as = dynamic_cast(as->perform(&eval)); selector_stack.pop_back(); Block* bb = ab ? ab->perform(this)->block() : 0; - At_Rule* aa = SASS_MEMORY_NEW(ctx.mem, At_Rule, + Directive* aa = SASS_MEMORY_NEW(ctx.mem, Directive, a->pstate(), a->keyword(), as, @@ -557,7 +591,7 @@ namespace Sass { void Expand::expand_selector_list(Selector* s, Selector_List* extender) { - +/* if (Selector_List* sl = dynamic_cast(s)) { for (Complex_Selector* complex_selector : sl->elements()) { Complex_Selector* tail = complex_selector; @@ -601,14 +635,25 @@ namespace Sass { sel = ssel; } // if (c->has_line_feed()) sel->has_line_feed(true); - ctx.subset_map.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder)); + // ctx.subset_map.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder)); } } - +*/ } Statement* Expand::operator()(Extension* e) { + Extension* ee = SASS_MEMORY_NEW(ctx.mem, Extension, *e); + + selector_stack.push_back(0); + if (e->block()) ee->selector(dynamic_cast(e->block()->perform(this))); + eval.skip_parentization = true; + if (e->selector()) ee->selector(dynamic_cast(e->selector()->perform(&eval))); + //debug_ast(e->selector()); + //debug_ast(ee->selector()); + eval.skip_parentization = false; + selector_stack.pop_back(); + return ee; if (Selector_List* extender = dynamic_cast(selector())) { selector_stack.push_back(0); Selector* s = e->selector(); @@ -707,7 +752,7 @@ namespace Sass { { std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name(); String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), err); - error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace()); + error("2unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace()); return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg); } diff --git a/src/expand.hpp b/src/expand.hpp index d23e5280a2..9057dd2df9 100644 --- a/src/expand.hpp +++ b/src/expand.hpp @@ -51,7 +51,7 @@ namespace Sass { Statement* operator()(Media_Block*); Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); - Statement* operator()(At_Rule*); + Statement* operator()(Directive*); Statement* operator()(Declaration*); Statement* operator()(Assignment*); Statement* operator()(Import*); diff --git a/src/extend.cpp b/src/extend.cpp index bc5c06462d..03db7110e5 100644 --- a/src/extend.cpp +++ b/src/extend.cpp @@ -6,6 +6,7 @@ #include "parser.hpp" #include "node.hpp" #include "sass_util.hpp" +#include "debugger.hpp" #include "debug.hpp" #include #include @@ -2054,6 +2055,7 @@ namespace Sass { void Extend::operator()(Ruleset* pRuleset) { + std::cerr << "extend " << pRuleset->selector()->to_string() << "\n"; extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map); pRuleset->block()->perform(this); } @@ -2068,7 +2070,7 @@ namespace Sass { pMediaBlock->block()->perform(this); } - void Extend::operator()(At_Rule* a) + void Extend::operator()(Directive* a) { // Selector_List* ls = dynamic_cast(a->selector()); // selector_stack.push_back(ls); diff --git a/src/extend.hpp b/src/extend.hpp index 7061b3fada..0ace05a72c 100644 --- a/src/extend.hpp +++ b/src/extend.hpp @@ -31,7 +31,7 @@ namespace Sass { void operator()(Ruleset*); void operator()(Supports_Block*); void operator()(Media_Block*); - void operator()(At_Rule*); + void operator()(Directive*); template void fallback(U x) { return fallback_impl(x); } diff --git a/src/functions.cpp b/src/functions.cpp index 98583c9a72..3829281eaa 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -4,6 +4,7 @@ #include "context.hpp" #include "backtrace.hpp" #include "parser.hpp" +#include "debugger.hpp" #include "constants.hpp" #include "inspect.hpp" #include "extend.hpp" @@ -1740,13 +1741,24 @@ namespace Sass { for(;itr != parsedSelectors.end(); ++itr) { Selector_List* child = *itr; std::vector exploded; - Selector_List* rv = child->parentize(result, ctx); + Selector_List* rv = child; + + // debug_ast(rv, "oo: "); + rv = result->connect(rv, ctx); + // rv->remove_parent_selectors(); + // result->remove_parent_selectors(); + // debug_ast(rv, "in: "); + rv = rv->parentalize(result, ctx); + // debug_ast(rv, "out: "); for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) { - exploded.push_back((*rv)[m]); + if (dynamic_cast((*rv)[m])) { + exploded.push_back(exploded.back()); + } else { + exploded.push_back((*rv)[m]); + } } result->elements(exploded); } - Listize listize(ctx.mem); return result->perform(&listize); } @@ -1803,7 +1815,6 @@ namespace Sass { for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx); Complex_Selector* childSeq = (*child)[j]; - Complex_Selector* base = childSeq->tail(); // Must be a simple sequence if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) { @@ -1816,7 +1827,7 @@ namespace Sass { } // Cannot be a Universal selector - Type_Selector* pType = dynamic_cast(base->head()->first()); + Type_Selector* pType = dynamic_cast(childSeq->head()->first()); if(pType && pType->name() == "*") { std::string msg("Can't append `"); msg += childSeq->to_string(); @@ -1829,10 +1840,10 @@ namespace Sass { // TODO: Add check for namespace stuff // append any selectors in childSeq's head - *(parentSeqClone->innermost()->head()) += (base->head()); + *(parentSeqClone->innermost()->head()) += (childSeq->head()); // Set parentSeqClone new tail - parentSeqClone->innermost()->tail( base->tail() ); + parentSeqClone->innermost()->tail( childSeq->tail() ); newElements.push_back(parentSeqClone); } diff --git a/src/inspect.cpp b/src/inspect.cpp index e1a78c2586..0e793c442d 100644 --- a/src/inspect.cpp +++ b/src/inspect.cpp @@ -96,7 +96,7 @@ namespace Sass { at_root_block->block()->perform(this); } - void Inspect::operator()(At_Rule* at_rule) + void Inspect::operator()(Directive* at_rule) { append_indentation(); append_token(at_rule->keyword(), at_rule); @@ -803,20 +803,15 @@ namespace Sass { } } - void Inspect::operator()(At_Root_Expression* ae) + void Inspect::operator()(At_Root_Query* ae) { - if (ae->is_interpolated()) { - ae->feature()->perform(this); - } - else { - append_string("("); - ae->feature()->perform(this); - if (ae->value()) { - append_colon_separator(); - ae->value()->perform(this); - } - append_string(")"); + append_string("("); + ae->feature()->perform(this); + if (ae->value()) { + append_colon_separator(); + ae->value()->perform(this); } + append_string(")"); } void Inspect::operator()(Null* n) @@ -971,6 +966,11 @@ namespace Sass { Complex_Selector* tail = c->tail(); Complex_Selector::Combinator comb = c->combinator(); + if (comb == Complex_Selector::ANCESTOR_OF && (!head || head->empty())) { + if (tail) tail->perform(this); + return; + } + if (c->has_line_feed()) { if (!(c->has_parent_ref())) { append_optional_linefeed(); diff --git a/src/inspect.hpp b/src/inspect.hpp index 873faf40a5..fdbd96e5bf 100644 --- a/src/inspect.hpp +++ b/src/inspect.hpp @@ -28,7 +28,7 @@ namespace Sass { virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); virtual void operator()(At_Root_Block*); - virtual void operator()(At_Rule*); + virtual void operator()(Directive*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Declaration*); virtual void operator()(Assignment*); @@ -72,7 +72,7 @@ namespace Sass { virtual void operator()(Supports_Interpolation*); virtual void operator()(Media_Query*); virtual void operator()(Media_Query_Expression*); - virtual void operator()(At_Root_Expression*); + virtual void operator()(At_Root_Query*); virtual void operator()(Null*); virtual void operator()(Parent_Selector* p); // parameters and arguments diff --git a/src/lexer.cpp b/src/lexer.cpp index 14ad116639..ac75c29498 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -91,7 +91,8 @@ namespace Sass { // valid in a uri (copied from Ruby Sass) bool is_uri_character(const char& chr) { - return unsigned(chr) > 41 && unsigned(chr) < 127; + return (unsigned(chr) > 41 && unsigned(chr) < 127) || + unsigned(chr) == ':' || unsigned(chr) == '/'; } // check if char is within a reduced ascii range diff --git a/src/lexer.hpp b/src/lexer.hpp index 512419a5f1..6e8685b8ac 100644 --- a/src/lexer.hpp +++ b/src/lexer.hpp @@ -142,6 +142,25 @@ namespace Sass { return p == src ? 0 : p; } + // Match for members of char class. + // Regex equivalent: /[^axy]/ + template + const char* neg_class_char(const char* src) { + if (*src == 0) return 0; + const char* cc = neg_char_class; + while (*cc && *src != *cc) ++cc; + return *cc ? 0 : src + 1; + } + + // Match for members of char class. + // Regex equivalent: /[^axy]+/ + template + const char* neg_class_chars(const char* src) { + const char* p = src; + while (neg_class_char(p)) ++p; + return p == src ? 0 : p; + } + // Match all except the supplied one. // Regex equivalent: /[^x]/ template diff --git a/src/operation.hpp b/src/operation.hpp index 86d61f9928..74121b1b24 100644 --- a/src/operation.hpp +++ b/src/operation.hpp @@ -18,7 +18,7 @@ namespace Sass { virtual T operator()(Supports_Block* x) = 0; virtual T operator()(Media_Block* x) = 0; virtual T operator()(At_Root_Block* x) = 0; - virtual T operator()(At_Rule* x) = 0; + virtual T operator()(Directive* x) = 0; virtual T operator()(Keyframe_Rule* x) = 0; virtual T operator()(Declaration* x) = 0; virtual T operator()(Assignment* x) = 0; @@ -61,7 +61,7 @@ namespace Sass { virtual T operator()(Supports_Interpolation* x) = 0; virtual T operator()(Media_Query* x) = 0; virtual T operator()(Media_Query_Expression* x) = 0; - virtual T operator()(At_Root_Expression* x) = 0; + virtual T operator()(At_Root_Query* x) = 0; virtual T operator()(Null* x) = 0; virtual T operator()(Parent_Selector* x) = 0; // parameters and arguments @@ -99,7 +99,7 @@ namespace Sass { T operator()(Supports_Block* x) { return static_cast(this)->fallback(x); } T operator()(Media_Block* x) { return static_cast(this)->fallback(x); } T operator()(At_Root_Block* x) { return static_cast(this)->fallback(x); } - T operator()(At_Rule* x) { return static_cast(this)->fallback(x); } + T operator()(Directive* x) { return static_cast(this)->fallback(x); } T operator()(Keyframe_Rule* x) { return static_cast(this)->fallback(x); } T operator()(Declaration* x) { return static_cast(this)->fallback(x); } T operator()(Assignment* x) { return static_cast(this)->fallback(x); } @@ -142,7 +142,7 @@ namespace Sass { T operator()(Supports_Interpolation* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query_Expression* x) { return static_cast(this)->fallback(x); } - T operator()(At_Root_Expression* x) { return static_cast(this)->fallback(x); } + T operator()(At_Root_Query* x) { return static_cast(this)->fallback(x); } T operator()(Null* x) { return static_cast(this)->fallback(x); } T operator()(Parent_Selector* x) { return static_cast(this)->fallback(x); } // parameters and arguments diff --git a/src/output.cpp b/src/output.cpp index 800e2fc6f5..6c289a1c32 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -309,7 +309,7 @@ namespace Sass { append_scope_closer(); } - void Output::operator()(At_Rule* a) + void Output::operator()(Directive* a) { std::string kwd = a->keyword(); Selector* s = a->selector(); diff --git a/src/output.hpp b/src/output.hpp index 8dd150b59d..a6bad52f1d 100644 --- a/src/output.hpp +++ b/src/output.hpp @@ -38,7 +38,7 @@ namespace Sass { // virtual void operator()(Propset*); virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); - virtual void operator()(At_Rule*); + virtual void operator()(Directive*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Import*); virtual void operator()(Comment*); diff --git a/src/parser.cpp b/src/parser.cpp index f1b5166f08..d0024515eb 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6,6 +6,7 @@ #include "file.hpp" #include "inspect.hpp" #include "constants.hpp" +#include "debugger.hpp" #include "util.hpp" #include "prelexer.hpp" #include "color_maps.hpp" @@ -235,8 +236,11 @@ namespace Sass { } // selector may contain interpolations which need delayed evaluation - else if (!(lookahead_result = lookahead_for_selector(position)).error) - { (*block) << parse_ruleset(lookahead_result, is_root); } + else if (!(lookahead_result = lookahead_for_selector(position)).error && + lookahead_result.found != NULL && *lookahead_result.found == '{') + { + (*block) << parse_ruleset(lookahead_result, is_root); + } // parse multiple specific keyword directives else if (lex < kwd_media >(true)) { (*block) << parse_media_block(); } @@ -251,7 +255,9 @@ namespace Sass { else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } // generic at keyword (keep last) - else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); } + else if (lex< re_special_directive >(true)) { (*block) << parse_special_directive(); } + else if (lex< re_prefixed_directive >(true)) { (*block) << parse_prefixed_directive(); } + else if (lex< at_keyword >(true)) { (*block) << parse_directive(); } else if (block->is_root()) { lex< css_whitespace >(); @@ -491,6 +497,10 @@ namespace Sass { // inherit is_root from parent block // need this info for sanity checks ruleset->is_root(is_root); + ruleset->connect_parent(!ruleset->selector()->has_parent_ref()); + if (Selector_List* l = dynamic_cast(ruleset->selector())) { + l->connect_parent(!ruleset->selector()->has_parent_ref()); + } // return AST Node return ruleset; } @@ -606,7 +616,10 @@ namespace Sass { // now parse the complex selector sel = parse_complex_selector(in_root); - if (!sel) return group; + if (!sel) { + group->connect_parent(!group->has_parent_ref()); + return group; + } sel->has_line_feed(had_linefeed); @@ -625,11 +638,13 @@ namespace Sass { } while (reloop); while (lex_css< kwd_optional >()) { - group->is_optional(true); + group->set_optional(true); } // update for end position group->update_pstate(pstate); if (sel) sel->last()->has_line_break(false); + group->connect_parent(!group->has_parent_ref()); + // debug_ast(group, "gbr: "); return group; } // EO parse_selector_list @@ -692,6 +707,8 @@ namespace Sass { } } +return sel; + // add a parent selector if we are not in a root // also skip adding parent ref if we only have refs if (!sel->has_parent_ref() && !in_at_root && !in_root) { @@ -744,8 +761,11 @@ namespace Sass { else if (lex< exactly<'&'> >(false)) { // this produces a linefeed!? + seq->connectz(false); seq->has_parent_reference(true); - (*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); + Parent_Selector* p = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); + p->leave_me_alone(true); + (*seq) << p; // parent selector only allowed at start // upcoming sass may allow also trailing if (seq->length() > 1) { @@ -781,6 +801,9 @@ namespace Sass { seq->has_line_break(peek_newline()); } + seq->connectz(!seq->has_parent_ref()); + // debug_ast(seq, "seq: "); + // EO while true return seq; @@ -1377,7 +1400,10 @@ namespace Sass { lex< css_comments >(false); if (lex< ampersand >()) { - return SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); } + Parent_Selector* p = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); + p->leave_me_alone(true); + return p; + } if (lex< kwd_important >()) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); } @@ -2135,28 +2161,31 @@ namespace Sass { { ParserState at_source_position = pstate; Block* body = 0; - At_Root_Expression* expr = 0; + At_Root_Query* expr = 0; Lookahead lookahead_result; // stack.push_back(Scope::Root); LOCAL_FLAG(in_at_root, true); - if (lex< exactly<'('> >()) { - expr = parse_at_root_expression(); + if (lex_css< exactly<'('> >()) { + expr = parse_at_root_query(); } - if (peek < exactly<'{'> >()) { - body = parse_block(true); + if (peek_css < exactly<'{'> >()) { + lex (); + body = parse_block(false); } else if ((lookahead_result = lookahead_for_selector(position)).found) { Ruleset* r = parse_ruleset(lookahead_result, false); body = SASS_MEMORY_NEW(ctx.mem, Block, r->pstate(), 1, true); + r->selector()->transparent(true); *body << r; } At_Root_Block* at_root = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, at_source_position, body); if (expr) at_root->expression(expr); // stack.pop_back(); + // debug_ast(at_root); return at_root; } - At_Root_Expression* Parser::parse_at_root_expression() + At_Root_Query* Parser::parse_at_root_query() { if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate); @@ -2164,29 +2193,62 @@ namespace Sass { css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was "); } - Declaration* declaration = parse_declaration(); - List* value = SASS_MEMORY_NEW(ctx.mem, List, declaration->value()->pstate(), 1); + Expression* feature = parse_list(); + if (!lex_css< exactly<':'> >()) error("style declaration must contain a value", pstate); + Expression* expression = parse_list(); + + // Declaration* declaration = parse_declaration(); + List* value = SASS_MEMORY_NEW(ctx.mem, List, feature->pstate(), 1); - if (declaration->value()->concrete_type() == Expression::LIST) { - value = static_cast(declaration->value()); + if (expression->concrete_type() == Expression::LIST) { + value = static_cast(expression); } - else *value << declaration->value(); + else *value << expression; - At_Root_Expression* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, - declaration->pstate(), - declaration->property(), + At_Root_Query* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Query, + value->pstate(), + feature, value); - if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate); + if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate); return cond; } - At_Rule* Parser::parse_at_rule() + + Directive* Parser::parse_special_directive() + { + std::string kwd(lexed); + + if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate); + + Directive* at_rule = SASS_MEMORY_NEW(ctx.mem, Directive, pstate, kwd); + Lookahead lookahead = lookahead_for_include(position); + if (lookahead.found && !lookahead.has_interpolants) { + at_rule->selector(parse_selector_list(true)); + } + + lex < css_comments >(false); + + if (lex < static_property >()) { + at_rule->value(parse_interpolated_chunk(Token(lexed))); + } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) { + at_rule->value(parse_list()); + } + + lex < css_comments >(false); + + if (peek< exactly<'{'> >()) { + at_rule->block(parse_block()); + } + + return at_rule; + } + Directive* Parser::parse_prefixed_directive() { std::string kwd(lexed); if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate); - At_Rule* at_rule = SASS_MEMORY_NEW(ctx.mem, At_Rule, pstate, kwd); + Directive* at_rule = SASS_MEMORY_NEW(ctx.mem, Directive, pstate, kwd); Lookahead lookahead = lookahead_for_include(position); if (lookahead.found && !lookahead.has_interpolants) { at_rule->selector(parse_selector_list(true)); @@ -2209,6 +2271,148 @@ namespace Sass { return at_rule; } + + Directive* Parser::parse_directive() + { + Directive* directive = SASS_MEMORY_NEW(ctx.mem, Directive, pstate, lexed); + Expression* val = parse_almost_any_value(); + // strip left and right if they are of type string + // debug_ast(val); + // std::cerr << "HAASDASD\n"; + directive->value(val); + if (peek< exactly<'{'> >()) { + directive->block(parse_block()); + } else if (!val) { + css_error("most def"); + } + return directive; + } + + Expression* Parser::lex_block_comment() + { + return 0; + } + + Expression* Parser::lex_single_line_comment() + { + return 0; + } + + Expression* Parser::lex_interpolation() + { + if (lex < interpolant >(src)) { + return parse_interpolated_chunk(lexed, true); + } + return 0; + } + + Expression* Parser::lex_interp_uri() + { + // create a string schema by lexing optional interpolations + return lex_interp< re_string_uri_open, re_string_uri_close >(); + } + + Expression* Parser::lex_interp_string() + { + Expression* rv = 0; + if ((rv = lex_interp< re_string_double_open, re_string_double_close >()) != NULL) return rv; + if ((rv = lex_interp< re_string_single_open, re_string_single_close >()) != NULL) return rv; + return rv; + } + + Expression* Parser::lex_almost_any_value_chars() + { + const char* match = + lex < + one_plus < + alternatives < + sequence < + exactly <'\\'>, + any_char + >, + sequence < + negate < + sequence < + exactly < url_kwd >, + exactly <'('> + > + >, + neg_class_char < + almost_any_value_class + > + >, + sequence < + exactly <'/'>, + negate < + alternatives < + exactly <'/'>, + exactly <'*'> + > + > + >, + sequence < + exactly <'\\'>, + exactly <'#'>, + negate < + exactly <'{'> + > + >, + sequence < + exactly <'!'>, + negate < + alpha + > + > + > + > + >(false); + if (match) { + // std::cerr << "[[" << std::string(lexed) << "]\n"; + return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + } + return NULL; + } + + Expression* Parser::lex_almost_any_value_token() + { + Expression* rv = 0; + if (*position == 0) return 0; + if ((rv = lex_almost_any_value_chars()) != NULL) return rv; + if ((rv = lex_block_comment()) != NULL) return rv; + if ((rv = lex_single_line_comment()) != NULL) return rv; + if ((rv = lex_interp_string()) != NULL) return rv; + if ((rv = lex_interp_uri()) != NULL) return rv; + if ((rv = lex_interpolation()) != NULL) return rv; + return rv; + } + + String_Schema* Parser::parse_almost_any_value() + { + + String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); + if (*position == 0) return 0; + lex < spaces >(false); + Expression* token = lex_almost_any_value_token(); + if (!token) return 0; + // std::cerr << "LEX [" << std::string(lexed) << "]\n"; + *schema << token; + if (*position == 0) { + schema->rtrim(); + return schema; + } + + while (token = lex_almost_any_value_token()) { + // std::cerr << "LEX [" << std::string(lexed) << "]\n"; + *schema << token; + } + + lex < css_whitespace >(); + + schema->rtrim(); + +// debug_ast(schema, "RV: "); + return schema; + } Warning* Parser::parse_warning() { if (stack.back() != Scope::Root && @@ -2263,72 +2467,7 @@ namespace Sass { rv.error = p; if (const char* q = peek < - alternatives < - // partial bem selector - sequence < - ampersand, - one_plus < - exactly < '-' > - >, - word_boundary - >, - // main selector matching - one_plus < - alternatives < - // consume whitespace and comments - spaces, block_comment, line_comment, - // match `/deep/` selector (pass-trough) - // there is no functionality for it yet - schema_reference_combinator, - // match selector ops /[*&%,()\[\]]/ - class_char < selector_lookahead_ops >, - // match selector combinators /[>+~]/ - class_char < selector_combinator_ops >, - // match attribute compare operators - alternatives < - exact_match, class_match, dash_match, - prefix_match, suffix_match, substring_match - >, - // main selector match - sequence < - // allow namespace prefix - optional < namespace_schema >, - // modifiers prefixes - alternatives < - sequence < - exactly <'#'>, - // not for interpolation - negate < exactly <'{'> > - >, - // class match - exactly <'.'>, - // single or double colon - optional < pseudo_prefix > - >, - // accept hypens in token - one_plus < sequence < - // can start with hyphens - zero_plus < exactly<'-'> >, - // now the main token - alternatives < - kwd_optional, - exactly <'*'>, - quoted_string, - interpolant, - identifier, - variable, - percentage, - binomial, - dimension, - alnum - > - > >, - // can also end with hyphens - zero_plus < exactly<'-'> > - > - > - > - > + re_selector_list >(p) ) { while (p < q) { @@ -2348,6 +2487,7 @@ namespace Sass { // check expected opening bracket // only after successfull matching if (peek < exactly<'{'> >(q)) rv.found = q; + else if (peek < exactly<'('> >(q)) rv.found = q; // else if (peek < exactly<';'> >(q)) rv.found = q; // else if (peek < exactly<'}'> >(q)) rv.found = q; if (rv.found || *p == 0) rv.error = 0; @@ -2361,7 +2501,7 @@ namespace Sass { } // EO lookahead_for_selector - // used in parse_block_nodes and parse_at_rule + // used in parse_block_nodes and parse_directive // ToDo: actual usage is still not really clear to me? Lookahead Parser::lookahead_for_include(const char* start) { diff --git a/src/parser.hpp b/src/parser.hpp index bbb62c8903..2bcb82dca2 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -130,6 +130,8 @@ namespace Sass { const char* lex(bool lazy = true, bool force = false) { + if (*position == 0) return 0; + // position considered before lexed token // we can skip whitespace or comments for // lazy developers (but we need control) @@ -292,12 +294,24 @@ namespace Sass { Supports_Condition* parse_supports_declaration(); Supports_Condition* parse_supports_condition_in_parens(); At_Root_Block* parse_at_root_block(); - At_Root_Expression* parse_at_root_expression(); - At_Rule* parse_at_rule(); + At_Root_Query* parse_at_root_query(); + String_Schema* parse_almost_any_value(); + Directive* parse_special_directive(); + Directive* parse_prefixed_directive(); + Directive* parse_directive(); Warning* parse_warning(); Error* parse_error(); Debug* parse_debug(); + // try to be more ruby sass + Expression* lex_block_comment(); + Expression* lex_single_line_comment(); + Expression* lex_almost_any_value_token(); + Expression* lex_almost_any_value_chars(); + Expression* lex_interp_string(); + Expression* lex_interp_uri(); + Expression* lex_interpolation(); + // these will throw errors Token lex_variable(); Token lex_identifier(); @@ -313,6 +327,34 @@ namespace Sass { void throw_syntax_error(std::string message, size_t ln = 0); void throw_read_error(std::string message, size_t ln = 0); + + + template + Expression* lex_interp() + { + if (lex < open >(false)) { + String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); + // std::cerr << "LEX [[" << std::string(lexed) << "]]\n"; + *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + if (position[0] == '#' && position[1] == '{') { + Expression* itpl = lex_interpolation(); + if (itpl) *schema << itpl; + while (lex < close >(false)) { + // std::cerr << "LEX [[" << std::string(lexed) << "]]\n"; + *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + if (position[0] == '#' && position[1] == '{') { + Expression* itpl = lex_interpolation(); + if (itpl) *schema << itpl; + } else { + return schema; + } + } + } else { + return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + } + } + return 0; + } }; size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len); diff --git a/src/prelexer.cpp b/src/prelexer.cpp index c3453eccb7..75050ed4c5 100644 --- a/src/prelexer.cpp +++ b/src/prelexer.cpp @@ -16,6 +16,205 @@ namespace Sass { namespace Prelexer { + + +/* + + def string_re(open, close) + /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m + end + end + + # A hash of regular expressions that are used for tokenizing strings. + # + # The key is a `[Symbol, Boolean]` pair. + # The symbol represents which style of quotation to use, + # while the boolean represents whether or not the string + # is following an interpolated segment. + STRING_REGULAR_EXPRESSIONS = { + :double => { + /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m + false => string_re('"', '"'), + true => string_re('', '"') + }, + :single => { + false => string_re("'", "'"), + true => string_re('', "'") + }, + :uri => { + false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + }, + # Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a + # non-standard version of http://www.w3.org/TR/css3-conditional/ + :url_prefix => { + false => /url-prefix\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + }, + :domain => { + false => /domain\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + } + } +*/ +/* + /#{open} + ( + \\. + | + \# (?!\{) + | + [^#{close}\\#] + )* + (#{close}|#\{) + + /m + + false => string_re('"', '"'), + true => string_re('', '"') +*/ + + extern const char string_double_negates[] = "\"\\#"; + const char* re_string_double_close(const char* src) + { + return sequence < + // valid chars + zero_plus < + alternatives < + // escaped char + sequence < + exactly <'\\'>, + any_char + >, + // non interpolate hash + sequence < + exactly <'#'>, + negate < + exactly <'{'> + > + >, + // other valid chars + neg_class_char < + string_double_negates + > + > + >, + // quoted string closer + // or interpolate opening + alternatives < + exactly <'"'>, + lookahead < exactly< hash_lbrace > > + > + >(src); + } + + const char* re_string_double_open(const char* src) + { + return sequence < + // quoted string opener + exactly <'"'>, + // valid chars + zero_plus < + alternatives < + // escaped char + sequence < + exactly <'\\'>, + any_char + >, + // non interpolate hash + sequence < + exactly <'#'>, + negate < + exactly <'{'> + > + >, + // other valid chars + neg_class_char < + string_double_negates + > + > + >, + // quoted string closer + // or interpolate opening + alternatives < + exactly <'"'>, + lookahead < exactly< hash_lbrace > > + > + >(src); + } + + const char* re_string_single_open(const char* src) + { + } + const char* re_string_single_close(const char* src) + { + } + +/* + :uri => { + false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + }, + +*/ + const char* re_string_uri_close(const char* src) + { + return sequence < + non_greedy< + alternatives< + class_char< real_uri_chars >, + uri_character, + NONASCII, + ESCAPE + >, + alternatives< + sequence < optional < W >, exactly <')'> >, + lookahead < exactly< hash_lbrace > > + > + >, + optional < + sequence < optional < W >, exactly <')'> > + > + >(src); + } + + const char* re_string_uri_open(const char* src) + { + return sequence < + exactly <'u'>, + exactly <'r'>, + exactly <'l'>, + exactly <'('>, + W, + non_greedy< + alternatives< + class_char< real_uri_chars >, + uri_character, + NONASCII, + ESCAPE + >, + alternatives< + sequence < W, exactly <')'> >, + exactly< hash_lbrace > + > + > + >(src); + } + + const char* re_string_uri_prefix_open(const char* src) + { + } + const char* re_string_uri_prefix_close(const char* src) + { + } + const char* re_string_domain_open(const char* src) + { + } + const char* re_string_domain_close(const char* src) + { + } + + // Match a line comment (/.*?(?=\n|\r\n?|\Z)/. const char* line_comment(const char* src) { @@ -370,6 +569,133 @@ namespace Sass { return sequence, identifier>(src); } + const char* re_almost_any_value_token(const char* src) { +/* + tok(%r{ + ( + \\. + | + (?!url\() + [^"'/\#!;\{\}] # " + | + /(?![/*]) + | + \#(?!\{) + | + !(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed. + )+ + }xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri || + interpolation(:warn_for_color) +*/ + + return alternatives < + one_plus < + alternatives < + sequence < + exactly <'\\'>, + any_char + >, + sequence < + negate < + sequence < + exactly < url_kwd >, + exactly <'('> + > + >, + neg_class_char < + almost_any_value_class + > + >, + sequence < + exactly <'/'>, + negate < + alternatives < + exactly <'/'>, + exactly <'*'> + > + > + >, + sequence < + exactly <'\\'>, + exactly <'#'>, + negate < + exactly <'{'> + > + >, + sequence < + exactly <'!'>, + negate < + alpha + > + > + > + >, + block_comment, + line_comment, + interpolant, + space, + sequence < + exactly<'u'>, + exactly<'r'>, + exactly<'l'>, + exactly<'('>, + zero_plus < + alternatives < + class_char< real_uri_chars >, + uri_character, + NONASCII, + ESCAPE + > + >, + // false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, + // true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ + + exactly<')'> + > + >(src); + } + /* + DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for, + :each, :while, :if, :else, :extend, :import, :media, :charset, :content, + :_moz_document, :at_root, :error] + */ + const char* re_special_directive(const char* src) { + return alternatives < + word < mixin_kwd >, + word < include_kwd >, + word < function_kwd >, + word < return_kwd >, + word < debug_kwd >, + word < warn_kwd >, + word < for_kwd >, + word < each_kwd >, + word < while_kwd >, + word < if_kwd >, + word < else_kwd >, + word < extend_kwd >, + word < import_kwd >, + word < media_kwd >, + word < charset_kwd >, + word < content_kwd >, + // exactly < moz_document_kwd >, + word < at_root_kwd >, + word < error_kwd > + >(src); + } + + const char* re_prefixed_directive(const char* src) { + return sequence < + optional < + sequence < + exactly <'-'>, + one_plus < alnum >, + exactly <'-'> + > + >, + exactly < supports_kwd > + >(src); + } + const char* re_reference_combinator(const char* src) { return sequence < optional < @@ -1167,6 +1493,83 @@ namespace Sass { >(src); } + const char* re_selector_list(const char* src) { + return alternatives < + // partial bem selector + sequence < + ampersand, + one_plus < + exactly < '-' > + >, + word_boundary, + optional_spaces + >, + // main selector matching + one_plus < + alternatives < + // consume whitespace and comments + spaces, block_comment, line_comment, + // match `/deep/` selector (pass-trough) + // there is no functionality for it yet + schema_reference_combinator, + // match selector ops /[*&%,\[\]]/ + class_char < selector_lookahead_ops >, + // match selector combinators /[>+~]/ + class_char < selector_combinator_ops >, + // match attribute compare operators + sequence < + exactly <'('>, + optional_spaces, + optional , + optional_spaces, + exactly <')'> + >, + alternatives < + exact_match, class_match, dash_match, + prefix_match, suffix_match, substring_match + >, + // main selector match + sequence < + // allow namespace prefix + optional < namespace_schema >, + // modifiers prefixes + alternatives < + sequence < + exactly <'#'>, + // not for interpolation + negate < exactly <'{'> > + >, + // class match + exactly <'.'>, + // single or double colon + optional < pseudo_prefix > + >, + // accept hypens in token + one_plus < sequence < + // can start with hyphens + zero_plus < exactly<'-'> >, + // now the main token + alternatives < + kwd_optional, + exactly <'*'>, + quoted_string, + interpolant, + identifier, + variable, + percentage, + binomial, + dimension, + alnum + > + > >, + // can also end with hyphens + zero_plus < exactly<'-'> > + > + > + > + >(src); + } + const char* type_selector(const char* src) { return sequence< optional, identifier>(src); } diff --git a/src/prelexer.hpp b/src/prelexer.hpp index c8e47cd901..afa4ac9a2a 100644 --- a/src/prelexer.hpp +++ b/src/prelexer.hpp @@ -162,6 +162,19 @@ namespace Sass { return src; } + + // equivalent of STRING_REGULAR_EXPRESSIONS + const char* re_string_double_open(const char* src); + const char* re_string_double_close(const char* src); + const char* re_string_single_open(const char* src); + const char* re_string_single_close(const char* src); + const char* re_string_uri_open(const char* src); + const char* re_string_uri_close(const char* src); + const char* re_string_uri_prefix_open(const char* src); + const char* re_string_uri_prefix_close(const char* src); + const char* re_string_domain_open(const char* src); + const char* re_string_domain_close(const char* src); + // Match a line comment. const char* line_comment(const char* src); const char* line_comment_prefix(const char* src); @@ -218,6 +231,11 @@ namespace Sass { // const char* url_schema(const char* src); // const char* url_value(const char* src); const char* vendor_prefix(const char* src); + + const char* re_special_directive(const char* src); + const char* re_prefixed_directive(const char* src); + const char* re_almost_any_value_token(const char* src); + // Match CSS '@' keywords. const char* at_keyword(const char* src); const char* kwd_import(const char* src); @@ -263,6 +281,7 @@ namespace Sass { const char* kwd_null(const char* src); + const char* re_selector_list(const char* src); const char* re_type_selector(const char* src); const char* re_static_expression(const char* src); diff --git a/src/remove_placeholders.cpp b/src/remove_placeholders.cpp index 5e208885a7..e3c23d9436 100644 --- a/src/remove_placeholders.cpp +++ b/src/remove_placeholders.cpp @@ -2,6 +2,7 @@ #include "remove_placeholders.hpp" #include "context.hpp" #include "inspect.hpp" +#include "debugger.hpp" #include namespace Sass { @@ -34,7 +35,6 @@ namespace Sass { void Remove_Placeholders::operator()(Ruleset* r) { // Create a new selector group without placeholders Selector_List* sl = static_cast(r->selector()); - if (sl) { // Set the new placeholder selector list r->selector(remove_placeholders(sl)); @@ -74,7 +74,7 @@ namespace Sass { } } - void Remove_Placeholders::operator()(At_Rule* a) { + void Remove_Placeholders::operator()(Directive* a) { if (a->block()) a->block()->perform(this); } diff --git a/src/remove_placeholders.hpp b/src/remove_placeholders.hpp index de7e5645ef..ca8b2487de 100644 --- a/src/remove_placeholders.hpp +++ b/src/remove_placeholders.hpp @@ -27,7 +27,7 @@ namespace Sass { void operator()(Block*); void operator()(Ruleset*); void operator()(Media_Block*); - void operator()(At_Rule*); + void operator()(Directive*); template void fallback(U x) { return fallback_impl(x); } diff --git a/src/sass.hpp b/src/sass.hpp index b82fb93091..0ef1323897 100644 --- a/src/sass.hpp +++ b/src/sass.hpp @@ -1,4 +1,5 @@ // must be the first include in all compile units +// #define DEBUG #ifndef SASS_SASS_H #define SASS_SASS_H diff --git a/src/subset_map.hpp b/src/subset_map.hpp index 9eec59bb19..6bffd13188 100644 --- a/src/subset_map.hpp +++ b/src/subset_map.hpp @@ -76,6 +76,7 @@ namespace Sass { std::map, std::set, size_t> > > hash_; public: void put(const std::vector& s, const V& value); + void prepend(const std::vector& s, const V& value); std::vector > > get_kv(const std::vector& s); std::vector get_v(const std::vector& s); bool empty() { return values_.empty(); } @@ -98,6 +99,21 @@ namespace Sass { } } + template + void Subset_Map::prepend(const std::vector& s, const V& value) + { + if (s.empty()) throw "internal error: subset map keys may not be empty"; + size_t index = values_.size(); + values_.push_back(value); + std::set ss; + for (size_t i = 0, S = s.size(); i < S; ++i) + { ss.insert(s[i]); } + for (size_t i = 0, S = s.size(); i < S; ++i) + { + hash_[s[i]].push_back(make_triple(s, ss, index)); + } + } + template std::vector > > Subset_Map::get_kv(const std::vector& s) { diff --git a/src/util.cpp b/src/util.cpp index 8b93f22e07..91861f1198 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -473,7 +473,7 @@ namespace Sass { bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { + if (dynamic_cast(stm)) { return true; } else if (dynamic_cast(stm)) { Block* pChildBlock = ((Has_Block*)stm)->block(); @@ -540,7 +540,7 @@ namespace Sass { // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with // a declaration in it). } - else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { + else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(Directive)) { hasDeclarations = true; } else if (dynamic_cast(stm)) { @@ -565,7 +565,7 @@ namespace Sass { if (b == 0) return false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (typeid(*stm) == typeid(At_Rule)) return true; + if (typeid(*stm) == typeid(Directive)) return true; if (typeid(*stm) == typeid(Declaration)) return true; if (Has_Block* child = dynamic_cast(stm)) { if (isPrintable(child->block(), style)) return true; @@ -581,7 +581,7 @@ namespace Sass { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { + if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(Directive)) { return true; } else if (typeid(*stm) == typeid(Comment)) {