From 891a29f9b620634c22d78f79d6972efd37a0cb9c Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Thu, 26 Mar 2015 13:54:53 -0700 Subject: [PATCH 01/13] Added stub selector functions --- ast.cpp | 1 + ast.hpp | 4 +++- context.cpp | 9 +++++++++ eval.cpp | 6 +++--- expand.cpp | 3 +++ functions.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- functions.hpp | 23 ++++++++++++++++++++--- 7 files changed, 82 insertions(+), 8 deletions(-) diff --git a/ast.cpp b/ast.cpp index 85036afebf..2a578506b4 100644 --- a/ast.cpp +++ b/ast.cpp @@ -1,5 +1,6 @@ #include "ast.hpp" #include "context.hpp" +#include "contextualize.hpp" #include "to_string.hpp" #include #include diff --git a/ast.hpp b/ast.hpp index 0eea9a9451..c7d3242985 100644 --- a/ast.hpp +++ b/ast.hpp @@ -633,9 +633,11 @@ namespace Sass { // by a type tag. ///////////////////////////////////////////////////////////////////////////// struct Backtrace; + class Contextualize; typedef Environment Env; typedef const char* Signature; - typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*); + typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Contextualize*, Backtrace*); +// typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Contextualize*, Backtrace*); typedef const char* Signature; class Definition : public Has_Block { public: diff --git a/context.cpp b/context.cpp index cb020449d4..eabcd34c50 100644 --- a/context.cpp +++ b/context.cpp @@ -540,6 +540,15 @@ namespace Sass { // Misc Functions register_function(ctx, inspect_sig, inspect, env); register_function(ctx, unique_id_sig, unique_id, env); + // Selector functions + register_function(ctx, selector_nest_sig, selector_nest, env); + register_function(ctx, selector_append_sig, selector_append, env); + register_function(ctx, selector_extend_sig, selector_extend, env); + register_function(ctx, selector_replace_sig, selector_replace, env); + register_function(ctx, selector_unify_sig, selector_unify, env); + register_function(ctx, is_superselector_sig, is_superselector, env); + register_function(ctx, simple_selectors_sig, simple_selectors, env); + register_function(ctx, selector_parse_sig, selector_parse, env); } void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs) diff --git a/eval.cpp b/eval.cpp index cec4947a92..c78bd78ad5 100644 --- a/eval.cpp +++ b/eval.cpp @@ -585,7 +585,7 @@ namespace Sass { Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); backtrace = &here; - result = func(*env, *old_env, ctx, def->signature(), c->pstate(), backtrace); + result = func(*env, *old_env, ctx, def->signature(), c->pstate(), contextualize, backtrace); backtrace = here.parent; env = old_env; @@ -624,7 +624,7 @@ namespace Sass { } else if (sass_value_get_tag(c_val) == SASS_WARNING) { error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace); } - result = cval_to_astnode(c_val, ctx, backtrace, c->pstate()); + result = cval_to_astnode(c_val, ctx, backtrace, c->pstate ()); backtrace = here.parent; sass_delete_value(c_args); @@ -650,7 +650,7 @@ namespace Sass { Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); backtrace = &here; - result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->pstate(), backtrace); + result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->pstate(), contextualize, backtrace); backtrace = here.parent; env = old_env; diff --git a/expand.cpp b/expand.cpp index ac00e89ff1..058b746f25 100644 --- a/expand.cpp +++ b/expand.cpp @@ -201,6 +201,9 @@ namespace Sass { String* old_p = d->property(); String* new_p = static_cast(old_p->perform(eval->with(env, backtrace))); Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back(); + + // Contextualize_Eval* contextual = contextualize_eval->with(selector_stack.back(), env, backtrace); + Expression* value = d->value()->perform(eval->with(p, env, backtrace)); if (value->is_invisible() && !d->is_important()) return 0; Declaration* decl = new (ctx.mem) Declaration(d->pstate(), diff --git a/functions.cpp b/functions.cpp index 4e69a25bb6..899d308386 100644 --- a/functions.cpp +++ b/functions.cpp @@ -1,6 +1,8 @@ #include "functions.hpp" #include "ast.hpp" #include "context.hpp" +#include "contextualize.hpp" +#include "contextualize_eval.hpp" #include "backtrace.hpp" #include "parser.hpp" #include "constants.hpp" @@ -1566,6 +1568,46 @@ namespace Sass { ss << "u" << setfill('0') << setw(8) << std::hex << distributed; return new (ctx.mem) String_Constant(pstate, ss.str()); } - + + Signature selector_nest_sig = "selector-nest($selectors...)"; + BUILT_IN(selector_nest) + { + return new (ctx.mem) String_Constant(pstate, "selector_nest"); + } + Signature selector_append_sig = "selector-append($selectors...)"; + BUILT_IN(selector_append) + { + return new (ctx.mem) String_Constant(pstate, "selector_append"); + } + Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; + BUILT_IN(selector_extend) + { + return new (ctx.mem) String_Constant(pstate, "selector_extend"); + } + Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; + BUILT_IN(selector_replace) + { + return new (ctx.mem) String_Constant(pstate, "selector_replace"); + } + Signature selector_unify_sig = "selector-unify($selectors1, $selector2)"; + BUILT_IN(selector_unify) + { + return new (ctx.mem) String_Constant(pstate, "selector_unify"); + } + Signature is_superselector_sig = "is-superselector($super, $sub)"; + BUILT_IN(is_superselector) + { + return new (ctx.mem) String_Constant(pstate, "is_superselector"); + } + Signature simple_selectors_sig = "simple_selectors($selector)"; + BUILT_IN(simple_selectors) + { + return new (ctx.mem) String_Constant(pstate, "simple_selector"); + } + Signature selector_parse_sig = "selector-parse($selector)"; + BUILT_IN(selector_parse) + { + return new (ctx.mem) String_Constant(pstate, "selector_parse"); + } } } diff --git a/functions.hpp b/functions.hpp index 4d825c1915..f614448c41 100644 --- a/functions.hpp +++ b/functions.hpp @@ -8,17 +8,18 @@ #include "sass_functions.h" #define BUILT_IN(name) Expression*\ -name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace) +name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Contextualize* p_contextualize, Backtrace* backtrace) namespace Sass { class Context; + class Contextualize; struct Backtrace; class AST_Node; class Expression; class Definition; typedef Environment Env; typedef const char* Signature; - typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*); + typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Contextualize*, Backtrace*); Definition* make_native_function(Signature, Native_Function, Context& ctx); Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx); @@ -102,6 +103,15 @@ namespace Sass { extern Signature keywords_sig; extern Signature set_nth_sig; extern Signature unique_id_sig; + extern Signature selector_nest_sig; + extern Signature selector_append_sig; + extern Signature selector_extend_sig; + extern Signature selector_replace_sig; + extern Signature selector_unify_sig; + extern Signature is_superselector_sig; + extern Signature simple_selectors_sig; + extern Signature selector_parse_sig; + BUILT_IN(rgb); BUILT_IN(rgba_4); @@ -177,7 +187,14 @@ namespace Sass { BUILT_IN(keywords); BUILT_IN(set_nth); BUILT_IN(unique_id); - + BUILT_IN(selector_nest); + BUILT_IN(selector_append); + BUILT_IN(selector_extend); + BUILT_IN(selector_replace); + BUILT_IN(selector_unify); + BUILT_IN(is_superselector); + BUILT_IN(simple_selectors); + BUILT_IN(selector_parse); } } From 48c39a67b41560d43bad51d30a8a2e2796548ac0 Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Thu, 26 Mar 2015 15:29:20 -0700 Subject: [PATCH 02/13] Initial version of selector-parse --- functions.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/functions.cpp b/functions.cpp index 899d308386..c8522964f9 100644 --- a/functions.cpp +++ b/functions.cpp @@ -78,6 +78,8 @@ namespace Sass { template T* get_arg(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace) { + auto rawVal = env[argname]; + // Minimal error handling -- the expectation is that built-ins will be written correctly! T* val = dynamic_cast(env[argname]); if (!val) { @@ -1607,7 +1609,26 @@ namespace Sass { Signature selector_parse_sig = "selector-parse($selector)"; BUILT_IN(selector_parse) { - return new (ctx.mem) String_Constant(pstate, "selector_parse"); + To_String to_string; + + Expression* exp = ARG("$selector", Expression); + String_Constant* s; + + // Catch & as variable + s = dynamic_cast(exp); + if(!s) { + s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); + } + + string result_str(s->value()); + result_str += '{'; // the parser looks for a brace to end the selector + + Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); + p.block_stack.push_back(p_contextualize->parent->last_block()); + p.last_media_block = p_contextualize->parent->media_block(); + + Selector_List* sel = p.parse_selector_group(); + return new (ctx.mem) String_Constant(pstate, sel->perform(&to_string)); } } } From 27010fdaee2546d423f89e97ff7fb4eee17a223f Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Thu, 26 Mar 2015 16:51:24 -0700 Subject: [PATCH 03/13] Initial version of simple-selectors --- functions.cpp | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/functions.cpp b/functions.cpp index c8522964f9..529d894ab4 100644 --- a/functions.cpp +++ b/functions.cpp @@ -1579,12 +1579,12 @@ namespace Sass { Signature selector_append_sig = "selector-append($selectors...)"; BUILT_IN(selector_append) { - return new (ctx.mem) String_Constant(pstate, "selector_append"); + return new (ctx.mem) String_Constant(pstate, "selector_append NOT YET IMPLEMENTED"); } Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; BUILT_IN(selector_extend) { - return new (ctx.mem) String_Constant(pstate, "selector_extend"); + return new (ctx.mem) String_Constant(pstate, "selector_extend NOT YET IMPLEMENTED"); } Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; BUILT_IN(selector_replace) @@ -1594,18 +1594,47 @@ namespace Sass { Signature selector_unify_sig = "selector-unify($selectors1, $selector2)"; BUILT_IN(selector_unify) { - return new (ctx.mem) String_Constant(pstate, "selector_unify"); + return new (ctx.mem) String_Constant(pstate, "selector_unify NOT YET IMPLEMENTED"); } Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) { - return new (ctx.mem) String_Constant(pstate, "is_superselector"); + return new (ctx.mem) String_Constant(pstate, "is_superselector NOT YET IMPLEMENTED"); } Signature simple_selectors_sig = "simple_selectors($selector)"; BUILT_IN(simple_selectors) { - return new (ctx.mem) String_Constant(pstate, "simple_selector"); + To_String to_string; + + Expression* exp = ARG("$selector", Expression); + String_Constant* s; + + // Catch & as variable + s = dynamic_cast(exp); + if(!s) { + s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); + } + + string result_str(s->value()); + result_str += ';'; // the parser looks for a brace to end the selector + + Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); + p.block_stack.push_back(p_contextualize->parent->last_block()); + p.last_media_block = p_contextualize->parent->media_block(); + + Compound_Selector* sel = p.parse_simple_selector_sequence(); + + List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA); + for (size_t i = 0, L = sel->length(); i < L; ++i) { + Simple_Selector* ss = (*sel)[i]; + string ss_string = ss->perform(&to_string) ; + + *l << new (ctx.mem) String_Constant(ss->pstate(), ss_string); + } + + return l; } + Signature selector_parse_sig = "selector-parse($selector)"; BUILT_IN(selector_parse) { From 7a374fd080f9a3deed6fb6d74dc28dc983aabc54 Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Fri, 27 Mar 2015 16:23:31 -0700 Subject: [PATCH 04/13] Added selector function `is-super-selector` --- ast.cpp | 38 ++++++++++++++++++++++++++++++++++++++ ast.hpp | 1 + functions.cpp | 34 +++++++++++++++++++++++++++++----- functions.hpp | 2 ++ 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/ast.cpp b/ast.cpp index 2a578506b4..7ce4a03159 100644 --- a/ast.cpp +++ b/ast.cpp @@ -493,6 +493,44 @@ namespace Sass { this->mCachedSelector(this->perform(&to_string)); #endif } + + + + // For every selector in RHS, see if we have /any/ selectors which are a super-selector of it + bool Selector_List::is_superselector_of(Sass::Selector_List *rhs) { + +#ifdef DEBUG + To_String to_string; +#endif + + // For every selector in RHS, see if it matches /any/ of our selectors + for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { + Complex_Selector* seq1 = (*rhs)[rhs_i]; +#ifdef DEBUG + string seq1_string = seq1->perform(&to_string); +#endif + + bool any = false; + for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { + Complex_Selector* seq2 = (*this)[lhs_i]; +#ifdef DEBUG + string seq2_string = seq2->perform(&to_string); +#endif + bool is_superselector = seq2->is_superselector_of(seq1); + if( is_superselector ) { + any = true; + break; + } + } + + // Seq1 did not match any of our selectors - whole thing is no good, abort + if(!any) { + return false; + } + } + return true; + } + /* not used anymore - remove? Selector_Placeholder* Selector_List::find_placeholder() diff --git a/ast.hpp b/ast.hpp index c7d3242985..e438d43308 100644 --- a/ast.hpp +++ b/ast.hpp @@ -2167,6 +2167,7 @@ namespace Sass { : Selector(pstate), Vectorized(s), wspace_(0) { } // virtual Selector_Placeholder* find_placeholder(); + bool is_superselector_of(Selector_List* rhs); virtual unsigned long specificity() { unsigned long sum = 0; diff --git a/functions.cpp b/functions.cpp index 529d894ab4..9d42cb3870 100644 --- a/functions.cpp +++ b/functions.cpp @@ -32,6 +32,7 @@ #define ARG(argname, argtype) get_arg(argname, env, sig, pstate, backtrace) #define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, pstate, lo, hi, backtrace) #define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, backtrace, ctx) +#define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, backtrace, ctx, p_contextualize) namespace Sass { using std::stringstream; @@ -77,9 +78,7 @@ namespace Sass { template T* get_arg(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace) - { - auto rawVal = env[argname]; - + { // Minimal error handling -- the expectation is that built-ins will be written correctly! T* val = dynamic_cast(env[argname]); if (!val) { @@ -121,6 +120,27 @@ namespace Sass { } return val; } + + Selector_List* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contextualize_eval) { + To_String to_string; + Expression* exp = ARG(argname, Expression); + String_Constant* s; + + // Catch & as variable + s = dynamic_cast(exp); + if(!s) { + s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); + } + + string result_str(s->value()); + result_str += ';'; // the parser looks for a brace to end the selector + + Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); + p.block_stack.push_back(contextualize_eval->parent->last_block()); + p.last_media_block = contextualize_eval->parent->media_block(); + + return p.parse_selector_group(); + } #ifdef __MINGW32__ uint64_t GetSeed() @@ -1599,7 +1619,10 @@ namespace Sass { Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) { - return new (ctx.mem) String_Constant(pstate, "is_superselector NOT YET IMPLEMENTED"); + Selector_List* super = ARGSEL("$super"); + Selector_List* sub = ARGSEL("$sub"); + bool result = super->is_superselector_of(sub); + return new (ctx.mem) Boolean(pstate, result); } Signature simple_selectors_sig = "simple_selectors($selector)"; BUILT_IN(simple_selectors) @@ -1628,10 +1651,11 @@ namespace Sass { for (size_t i = 0, L = sel->length(); i < L; ++i) { Simple_Selector* ss = (*sel)[i]; string ss_string = ss->perform(&to_string) ; - + *l << new (ctx.mem) String_Constant(ss->pstate(), ss_string); } + return l; } diff --git a/functions.hpp b/functions.hpp index f614448c41..b4fbeb55c9 100644 --- a/functions.hpp +++ b/functions.hpp @@ -195,6 +195,8 @@ namespace Sass { BUILT_IN(is_superselector); BUILT_IN(simple_selectors); BUILT_IN(selector_parse); + + } } From d24be46696d2bc9449a20d32a3dec44c50e5dc50 Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Sun, 29 Mar 2015 01:38:30 -0700 Subject: [PATCH 05/13] Initial half working version of selector-unify --- ast.cpp | 172 ++++++++++++++++++++++++ ast.hpp | 10 +- expand.cpp | 2 +- extend.cpp | 347 ++++++++++++++++++++++++++++++++++++++++++++----- extend.hpp | 4 + functions.cpp | 307 +++++++++++++++++++++++++++++++++---------- node.cpp | 32 ++++- node.hpp | 4 +- subset_map.hpp | 16 +++ 9 files changed, 795 insertions(+), 99 deletions(-) diff --git a/ast.cpp b/ast.cpp index 7ce4a03159..6d5b41fe90 100644 --- a/ast.cpp +++ b/ast.cpp @@ -1,6 +1,9 @@ #include "ast.hpp" #include "context.hpp" #include "contextualize.hpp" +#include "node.hpp" +#include "sass_util.hpp" +#include "extend.hpp" #include "to_string.hpp" #include #include @@ -532,6 +535,175 @@ namespace Sass { } + // For every selector in RHS, see if we have /any/ selectors which are a super-selector of it + bool Selector_List::is_superselector_of(Sass::Selector_List *rhs) { + +#ifdef DEBUG + To_String to_string; +#endif + + // For every selector in RHS, see if it matches /any/ of our selectors + for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { + Complex_Selector* seq1 = (*rhs)[rhs_i]; +#ifdef DEBUG + string seq1_string = seq1->perform(&to_string); +#endif + + bool any = false; + for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { + Complex_Selector* seq2 = (*this)[lhs_i]; +#ifdef DEBUG + string seq2_string = seq2->perform(&to_string); +#endif + bool is_superselector = seq2->is_superselector_of(seq1); + if( is_superselector ) { + any = true; + break; + } + } + + // Seq1 did not match any of our selectors - whole thing is no good, abort + if(!any) { + return false; + } + } + return true; + } + + Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { +#ifdef DEBUG + To_String to_string; + string lhs_string = perform(&to_string); + string rhs_string = rhs->perform(&to_string); + + auto counter = 0; + + std::cout << "\n\n\n---------------------\n\n\n" << std::endl; + std::cout << "Unifying " << this->perform(&to_string) << " with:" << rhs->perform(&to_string) << std::endl; + std::cout << "\n\n\n---------------------\n\n\n" << std::endl; +#endif + +// Store only unique Selector_List returned by Complex_Selector::unify_with +// std::set< Selector_List*, std::function< bool(Selector_List*, Selector_List*) > > unique_selector_list([] ( Selector_List* lhs, Selector_List* rhs ) { +// return *lhs == *rhs; +// } ); + + vector unified_complex_selectors; + // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` + for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { + Complex_Selector* seq1 = (*this)[lhs_i]; + for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { + Complex_Selector* seq2 = (*rhs)[rhs_i]; + +#ifdef DEBUG + string seq1_string = seq1->perform(&to_string); + string seq2_string = seq2->perform(&to_string); + counter++; +#endif + Selector_List* result = seq1->unify_with(seq2, ctx); + if( result ) { + for(size_t i = 0, L = result->length(); i < L; ++i) { +#ifdef DEBUG + std::cout << "Counter:" << counter << " result:" << (*result)[i]->perform(&to_string) << std::endl; +#endif + unified_complex_selectors.push_back( (*result)[i] ); + } + } + } + } + + // Creates the final Selector_List by combining all the complex selectors + Selector_List* final_result = new (ctx.mem) Selector_List(pstate()); + for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) { + *final_result << *itr; + } + + return final_result; + } + + void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) { + To_String to_string; + + Selector_List* extender = this; + for (auto complex_sel : extendee->elements()) { + Complex_Selector* c = complex_sel; + + + // 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) { + compound_sel = pHead; + break; + } + + pIter = pIter->tail(); + } + + if (!pIter->head() || pIter->tail()) { + error("nested selectors may not be extended", c->pstate()); + } + + compound_sel->is_optional(extendee->is_optional()); + + for (size_t i = 0, L = extender->length(); i < L; ++i) { + // let's test this out + cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl; + extends.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel)); + } + } + }; + + + Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx) { + To_String to_string; + + Compound_Selector* thisBase = base(); + Compound_Selector* rhsBase = other->base(); + + if( thisBase == 0 || rhsBase == 0 ) return 0; + + // Not sure about this check, but closest way I could check to see if this is a ruby 'SimpleSequence' equivalent + if( tail()->combinator() != Combinator::ANCESTOR_OF || other->tail()->combinator() != Combinator::ANCESTOR_OF ) return 0; + + Compound_Selector* unified = rhsBase->unify_with(thisBase, ctx); + if( unified == 0 ) return 0; + + Node lhsNode = complexSelectorToNode(this, ctx); + Node rhsNode = complexSelectorToNode(other, ctx); + + // Create a temp Complex_Selector, turn it into a Node, and combine it with the existing RHS node + Complex_Selector* fakeComplexSelector = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, unified, NULL); + Node unifiedNode = complexSelectorToNode(fakeComplexSelector, ctx); + rhsNode.plus(unifiedNode); + + Node node = Extend::StaticSubweave(lhsNode, rhsNode, ctx); + +#ifdef DEBUG + std::cout << "Node:" << node << std::endl; +#endif + + Selector_List* result = new (ctx.mem) Selector_List(pstate()); + for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { + Node childNode = *iter; + childNode = Node::naiveTrim(childNode, ctx); + + Complex_Selector* childNodeAsComplexSelector = nodeToComplexSelector(childNode, ctx); + if( childNodeAsComplexSelector ) { (*result) << childNodeAsComplexSelector; } + } + +#ifdef DEBUG +// To_String to_string; + string lhs_string = result->perform(&to_string); +#endif + + return result->length() ? result : 0; + } + + + /* not used anymore - remove? Selector_Placeholder* Selector_List::find_placeholder() { diff --git a/ast.hpp b/ast.hpp index e438d43308..0fcb71f40b 100644 --- a/ast.hpp +++ b/ast.hpp @@ -163,6 +163,7 @@ namespace Sass { size_t length() const { return elements_.size(); } bool empty() const { return elements_.empty(); } T last() { return elements_.back(); } + T first() { return elements_.front(); } T& operator[](size_t i) { return elements_[i]; } const T& operator[](size_t i) const { return elements_[i]; } Vectorized& operator<<(T element) @@ -2083,6 +2084,8 @@ namespace Sass { size_t length(); bool is_superselector_of(Compound_Selector*); bool is_superselector_of(Complex_Selector*); + Selector_List* unify_with(Complex_Selector* rhs, Context& ctx); + // virtual Selector_Placeholder* find_placeholder(); Combinator clear_innermost(); void set_innermost(Complex_Selector*, Combinator); @@ -2151,6 +2154,8 @@ namespace Sass { }; typedef deque ComplexSelectorDeque; + + typedef Subset_Map > ExtensionSubsetMap; /////////////////////////////////// // Comma-separated selector groups. @@ -2167,7 +2172,10 @@ namespace Sass { : Selector(pstate), Vectorized(s), wspace_(0) { } // virtual Selector_Placeholder* find_placeholder(); - bool is_superselector_of(Selector_List* rhs); + bool is_superselector_of(Selector_List* other); + Selector_List* unify_with(Selector_List*, Context&); + void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&); + virtual unsigned long specificity() { unsigned long sum = 0; diff --git a/expand.cpp b/expand.cpp index 058b746f25..a37f2074ef 100644 --- a/expand.cpp +++ b/expand.cpp @@ -496,7 +496,7 @@ namespace Sass { // { target_vec.push_back((*compound_sel)[i]->perform(&to_string)); } for (size_t i = 0, L = extender->length(); i < L; ++i) { // let's test this out - // cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl; + cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl; ctx.subset_map.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel)); } } diff --git a/extend.cpp b/extend.cpp index ede8e8d73c..5e97501b6a 100644 --- a/extend.cpp +++ b/extend.cpp @@ -593,6 +593,8 @@ namespace Sass { DEBUG_PRINTLN(TRIM, "RESULT: " << result) } + DEBUG_PRINTLN(TRIM, "FINAL RESULT: " << result) + return result; } @@ -1294,6 +1296,289 @@ namespace Sass { return pathsResult; } + + Node Extend::StaticTrim(Node& seqses, Context& ctx) { + // See the comments in the above ruby code before embarking on understanding this function. + + // Avoid poor performance in extreme cases. + if (seqses.collection()->size() > 100) { + return seqses; + } + + + DEBUG_PRINTLN(TRIM, "TRIM: " << seqses) + + + Node result = Node::createCollection(); + result.plus(seqses); + + DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result) + + // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're + // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track + // of the index manually. + int toTrimIndex = 0; + + for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) { + Node& seqs1 = *seqsesIter; + + DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex) + + Node tempResult = Node::createCollection(); + + for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) { + Node& seq1 = *seqs1Iter; + + Complex_Selector* pSeq1 = nodeToComplexSelector(seq1, ctx); + + // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code + // for a good description of sources. + // + // TODO: I'm pretty sure there's a bug in the sources code. It was implemented for sass-spec's 182_test_nested_extend_loop test. + // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We + // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My + // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely + // a guess though. + int maxSpecificity = 0; + SourcesSet sources = pSeq1->sources(); + + DEBUG_PRINTLN(TRIM, "TRIMASDF SEQ1: " << seq1) + DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIMASDF SOURCES: ")) + + for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) { + const Complex_Selector* const pCurrentSelector = *sourcesSetIterator; + maxSpecificity = max(maxSpecificity, pCurrentSelector->specificity()); + } + + DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity) + + bool isMoreSpecificOuter = false; + + int resultIndex = 0; + + for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) { + Node& seqs2 = *resultIter; + + DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1) + DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2) + + // Do not compare the same sequence to itself. The ruby call we're trying to + // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision. + // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is + // derived from seqses and seqs2 is derived from result. + if (seqs1.collection() == seqs2.collection()) { + DEBUG_PRINTLN(TRIM, "CONTINUE") + continue; + } + + bool isMoreSpecificInner = false; + + for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) { + Node& seq2 = *seqs2Iter; + + Complex_Selector* pSeq2 = nodeToComplexSelector(seq2, ctx); + + DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity()) + DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false")) + DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false")) + + isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1); + + if (isMoreSpecificInner) { + DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC") + break; + } + } + + // If we found something more specific, we're done. Let the outer loop know and stop iterating. + if (isMoreSpecificInner) { + isMoreSpecificOuter = true; + break; + } + + resultIndex++; + } + + if (!isMoreSpecificOuter) { + DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1) + tempResult.collection()->push_back(seq1); + } + + } + + DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result) + DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult) + (*result.collection())[toTrimIndex] = tempResult; + + toTrimIndex++; + + DEBUG_PRINTLN(TRIM, "RESULT: " << result) + } + + return result; + } + + Node Extend::StaticSubweave(Node& one, Node& two, Context& ctx) { + // Check for the simple cases + if (one.collection()->size() == 0) { + Node out = Node::createCollection(); + out.collection()->push_back(two); + return out; + } + if (two.collection()->size() == 0) { + Node out = Node::createCollection(); + out.collection()->push_back(one); + return out; + } + + + + Node seq1 = Node::createCollection(); + seq1.plus(one); + Node seq2 = Node::createCollection(); + seq2.plus(two); + + DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1) + DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2) + + Node init = mergeInitialOps(seq1, seq2, ctx); + if (init.isNil()) { + return Node::createNil(); + } + + DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init) + + Node res = Node::createCollection(); + Node fin = mergeFinalOps(seq1, seq2, ctx, res); + if (fin.isNil()) { + return Node::createNil(); + } + + DEBUG_PRINTLN(SUBWEAVE, "FIN: " << fin) + + + // Moving this line up since fin isn't modified between now and when it happened before + // fin.map {|sel| sel.is_a?(Array) ? sel : [sel]} + + for (NodeDeque::iterator finIter = fin.collection()->begin(), finEndIter = fin.collection()->end(); + finIter != finEndIter; ++finIter) { + + Node& childNode = *finIter; + + if (!childNode.isCollection()) { + Node wrapper = Node::createCollection(); + wrapper.collection()->push_back(childNode); + childNode = wrapper; + } + + } + + DEBUG_PRINTLN(SUBWEAVE, "FIN MAPPED: " << fin) + + + + Node groupSeq1 = groupSelectors(seq1, ctx); + DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1) + + Node groupSeq2 = groupSelectors(seq2, ctx); + DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2) + + + ComplexSelectorDeque groupSeq1Converted; + nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted, ctx); + + ComplexSelectorDeque groupSeq2Converted; + nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted, ctx); + + ComplexSelectorDeque out; + LcsCollectionComparator collectionComparator(ctx); + lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, ctx, out); + Node seqLcs = complexSelectorDequeToNode(out, ctx); + + DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs) + + + Node initWrapper = Node::createCollection(); + initWrapper.collection()->push_back(init); + Node diff = Node::createCollection(); + diff.collection()->push_back(initWrapper); + + DEBUG_PRINTLN(SUBWEAVE, "DIFF INIT: " << diff) + + + while (!seqLcs.collection()->empty()) { + ParentSuperselectorChunker superselectorChunker(seqLcs, ctx); + Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker); + diff.collection()->push_back(chunksResult); + + Node lcsWrapper = Node::createCollection(); + lcsWrapper.collection()->push_back(seqLcs.collection()->front()); + seqLcs.collection()->pop_front(); + diff.collection()->push_back(lcsWrapper); + + groupSeq1.collection()->pop_front(); + groupSeq2.collection()->pop_front(); + } + + DEBUG_PRINTLN(SUBWEAVE, "DIFF POST LCS: " << diff) + + + DEBUG_PRINTLN(SUBWEAVE, "CHUNKS: ONE=" << groupSeq1 << " TWO=" << groupSeq2) + + + SubweaveEmptyChunker emptyChunker; + Node chunksResult = chunks(groupSeq1, groupSeq2, emptyChunker); + diff.collection()->push_back(chunksResult); + + + DEBUG_PRINTLN(SUBWEAVE, "DIFF POST CHUNKS: " << diff) + + + diff.collection()->insert(diff.collection()->end(), fin.collection()->begin(), fin.collection()->end()); + + DEBUG_PRINTLN(SUBWEAVE, "DIFF POST FIN MAPPED: " << diff) + + // JMA - filter out the empty nodes (use a new collection, since iterator erase() invalidates the old collection) + Node diffFiltered = Node::createCollection(); + for (NodeDeque::iterator diffIter = diff.collection()->begin(), diffEndIter = diff.collection()->end(); + diffIter != diffEndIter; ++diffIter) { + Node& node = *diffIter; + if (node.collection() && !node.collection()->empty()) { + diffFiltered.collection()->push_back(node); + } + } + diff = diffFiltered; + + DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff) + + + Node pathsResult = paths(diff, ctx); + + DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult) + + + // We're flattening in place + for (NodeDeque::iterator pathsIter = pathsResult.collection()->begin(), pathsEndIter = pathsResult.collection()->end(); + pathsIter != pathsEndIter; ++pathsIter) { + + Node& child = *pathsIter; + child = flatten(child, ctx); + } + + DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult) + + + /* + TODO: implement + rejected = mapped.reject {|p| path_has_two_subjects?(p)} + $stderr.puts "REJECTED: #{rejected}" + */ + + + return pathsResult; + + } + /* // disabled to avoid clang warning [-Wunused-function] static Node subweaveNaive(const Node& one, const Node& two, Context& ctx) { @@ -1449,9 +1734,7 @@ namespace Sass { return befores; } - - - + // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively // call extendComplexSelector again. static Node extendComplexSelector( @@ -1517,7 +1800,7 @@ namespace Sass { Complex_Selector& seq = groupedPair.first; vector& group = groupedPair.second; -// DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) Compound_Selector* pSels = new (ctx.mem) Compound_Selector(pSelector->pstate()); @@ -1532,7 +1815,7 @@ namespace Sass { -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: ")) Complex_Selector* pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge) @@ -1544,8 +1827,8 @@ namespace Sass { Compound_Selector* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx); -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->base(); @@ -1557,9 +1840,9 @@ namespace Sass { pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx); -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: ")) if (!pUnifiedSelector || pUnifiedSelector->length() == 0) { continue; @@ -1750,7 +2033,9 @@ namespace Sass { ExtensionSubsetMap& subsetMap, set seen) { - pComplexSelector->tail()->has_line_feed(pComplexSelector->has_line_feed()); + if( pComplexSelector->tail() ) { + pComplexSelector->tail()->has_line_feed(pComplexSelector->has_line_feed()); + } Node complexSelector = complexSelectorToNode(pComplexSelector, ctx); DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) @@ -1848,23 +2133,21 @@ namespace Sass { return extendedSelectors; } - - - + /* This is the equivalent of ruby's CommaSequence.do_extend. - */ - static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool& extendedSomething) { - + */ + Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething) { + To_String to_string(&ctx); - + Selector_List* pNewSelectors = new (ctx.mem) Selector_List(pSelectorList->pstate(), pSelectorList->length()); - + extendedSomething = false; - + for (size_t index = 0, length = pSelectorList->length(); index < length; index++) { Complex_Selector* pSelector = (*pSelectorList)[index]; - + // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that. // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to // run through the extend code (which does a data model transformation), check if there is anything to extend before doing @@ -1874,26 +2157,32 @@ namespace Sass { *pNewSelectors << pSelector; continue; } - + extendedSomething = true; - + set seen; Node extendedSelectors = extendComplexSelector(pSelector, ctx, subsetMap, seen); - + + DEBUG_PRINTLN(EXTEND_COMPLEX, "extendedSelectors: " << extendedSelectors) + if (!pSelector->has_placeholder()) { if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) { *pNewSelectors << pSelector; } } - - for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { + + for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { + if(isReplace && iterator == iteratorBegin) continue; + Node& childNode = *iterator; + DEBUG_PRINTLN(EXTEND_COMPLEX, "\tchildNode: " << childNode) + *pNewSelectors << nodeToComplexSelector(childNode, ctx); } } - + return pNewSelectors; - + } @@ -1943,7 +2232,7 @@ namespace Sass { } bool extendedSomething = false; - Selector_List* pNewSelectorList = extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, extendedSomething); + Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, false, extendedSomething); if (extendedSomething && pNewSelectorList) { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->perform(&to_string)) diff --git a/extend.hpp b/extend.hpp index 5b282a9f20..f475568d69 100644 --- a/extend.hpp +++ b/extend.hpp @@ -14,6 +14,7 @@ namespace Sass { using namespace std; class Context; + class Node; typedef Subset_Map > ExtensionSubsetMap; @@ -38,6 +39,9 @@ namespace Sass { template void fallback(U x) { return fallback_impl(x); } + + static Node StaticSubweave(Node& one, Node& two, Context& ctx); + static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething); }; } diff --git a/functions.cpp b/functions.cpp index 9d42cb3870..5d7aae99be 100644 --- a/functions.cpp +++ b/functions.cpp @@ -8,6 +8,8 @@ #include "constants.hpp" #include "to_string.hpp" #include "inspect.hpp" +#include "extend.hpp" +#include "node.hpp" #include "eval.hpp" #include "util.hpp" #include "utf8_string.hpp" @@ -32,7 +34,7 @@ #define ARG(argname, argtype) get_arg(argname, env, sig, pstate, backtrace) #define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, pstate, lo, hi, backtrace) #define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, backtrace, ctx) -#define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, backtrace, ctx, p_contextualize) +#define ARGSEL(argname, seltype, contextualize) get_arg_sel(argname, env, sig, pstate, backtrace, ctx, contextualize) namespace Sass { using std::stringstream; @@ -78,7 +80,7 @@ namespace Sass { template T* get_arg(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace) - { + { // Minimal error handling -- the expectation is that built-ins will be written correctly! T* val = dynamic_cast(env[argname]); if (!val) { @@ -121,26 +123,51 @@ namespace Sass { return val; } - Selector_List* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contextualize_eval) { - To_String to_string; + //////////////////////////////////////////////// + // RETREIVE SELECTOR FROM ARGS HELPER FUNCTIONS + //////////////////////////////////////////////// +#define create_sel_parser(CONTEXTUALIZE) \ +To_String to_string; \ +String_Constant* s; \ + \ +/* Catch '&' as variable. Bug? */ \ +s = dynamic_cast(exp); \ +if(!s) { \ + s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); \ +} \ + \ +string result_str(s->value()); \ +result_str += ';';/* the parser looks for a brace to end the selector*/ \ + \ +Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); \ +if( CONTEXTUALIZE->parent ) { \ + p.block_stack.push_back(CONTEXTUALIZE->parent->last_block()); \ + p.last_media_block = CONTEXTUALIZE->parent->media_block(); \ +} \ + + template + T* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contexualize); + + template <> + Complex_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contexualize) { Expression* exp = ARG(argname, Expression); - String_Constant* s; - - // Catch & as variable - s = dynamic_cast(exp); - if(!s) { - s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); - } - - string result_str(s->value()); - result_str += ';'; // the parser looks for a brace to end the selector - - Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); - p.block_stack.push_back(contextualize_eval->parent->last_block()); - p.last_media_block = contextualize_eval->parent->media_block(); - + create_sel_parser(contexualize); + return p.parse_selector_combination(); + } + + template <> + Selector_List* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contexualize) { + Expression* exp = ARG(argname, Expression); + create_sel_parser(contexualize); return p.parse_selector_group(); } + + template <> + Compound_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contexualize) { + Expression* exp = ARG(argname, Expression); + create_sel_parser(contexualize); + return p.parse_simple_selector_sequence(); + } #ifdef __MINGW32__ uint64_t GetSeed() @@ -1594,60 +1621,225 @@ namespace Sass { Signature selector_nest_sig = "selector-nest($selectors...)"; BUILT_IN(selector_nest) { - return new (ctx.mem) String_Constant(pstate, "selector_nest"); + To_String to_string; + List* arglist = ARG("$selectors", List); + + // Not enough parameters + if( arglist->length() == 0 ) + error("$selectors: At least one selector must be passed", pstate); + + // Parse args into vector of selectors + vector parsedSelectors; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression* exp = dynamic_cast(arglist->value_at_index(i)); + create_sel_parser(p_contextualize); + Selector_List* sel = p.parse_selector_group(); + + parsedSelectors.push_back(sel); + } + + // Nothing to do + if( parsedSelectors.empty() ) { + return new (ctx.mem) Null(pstate); + } + + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. + std::vector::iterator itr = parsedSelectors.begin(); + Selector_List* result = *itr; + ++itr; + + for(;itr != parsedSelectors.end(); ++itr) { + Selector_List* child = *itr; + vector newElements; + + // For every COMPLEX_SELECTOR in `child` + // For every COMPLEX_SELECTOR in `result` + // let parentSeqClone equal a copy of result->elements[i] + // let childSeq equal child->elements[j] + // Set childSeq as the new innermost tail of parentSeqClone + // Add parentSeqClone to the newElements + // Replace result->elements with newElements + for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { + for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { + Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx); + Complex_Selector* childSeq = (*child)[j]; + + parentSeqClone->innermost()->tail(childSeq); // Set seq as the new tail of parentSeqClone + newElements.push_back(parentSeqClone); + } + } + + result->elements(newElements); + } + +#ifdef DEBUG + string s = result->perform(&to_string); + result->mCachedSelector(s); +// std::cout << "\n\tEndChild:" << result->mCachedSelector() << std::endl; +#endif + + Listize listize(ctx); + return result->perform(&listize); } + Signature selector_append_sig = "selector-append($selectors...)"; BUILT_IN(selector_append) { - return new (ctx.mem) String_Constant(pstate, "selector_append NOT YET IMPLEMENTED"); + To_String to_string; + List* arglist = ARG("$selectors", List); + + // Not enough parameters + if( arglist->length() == 0 ) + error("$selectors: At least one selector must be passed", pstate); + + // Parse args into vector of selectors + vector parsedSelectors; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression* exp = dynamic_cast(arglist->value_at_index(i)); + create_sel_parser(p_contextualize); + Selector_List* sel = p.parse_selector_group(); + + parsedSelectors.push_back(sel); + } + + // Nothing to do + if( parsedSelectors.empty() ) { + return new (ctx.mem) Null(pstate); + } + + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. + std::vector::iterator itr = parsedSelectors.begin(); + Selector_List* result = *itr; + ++itr; + + for(;itr != parsedSelectors.end(); ++itr) { + Selector_List* child = *itr; + vector newElements; + + // For every COMPLEX_SELECTOR in `result` + // For every COMPLEX_SELECTOR in `child` + // let parentSeqClone equal a copy of result->elements[i] + // let childSeq equal child->elements[j] + // Append all of childSeq head elements into parentSeqClone + // Set the innermost tail of parentSeqClone, to childSeq's tail + // Replace result->elements with newElements + for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { + 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 ) { + string msg("Can't append `"); + msg += childSeq->perform(&to_string); + msg += "` to `"; + msg += parentSeqClone->perform(&to_string);; + msg += "`"; + error(msg, pstate, backtrace); + } + + // Cannot be a Universal selector + Type_Selector* pType = dynamic_cast(base->head()->first()); + if(pType && pType->name() == "*") { + string msg("Can't append `"); + msg += childSeq->perform(&to_string); + msg += "` to `"; + msg += parentSeqClone->perform(&to_string);; + msg += "`"; + error(msg, pstate, backtrace); + } + + // MG TODO: Add check for namespace stuff + + // append any selectors in childSeq's head + *(parentSeqClone->tail()->head()) += (base->head()); + + // Set parentSeqClone new tail + parentSeqClone->innermost()->tail( base->tail() ); + + newElements.push_back(parentSeqClone); + } + } + + result->elements(newElements); + } + +#ifdef DEBUG + string s = result->perform(&to_string); + result->mCachedSelector(s); +// std::cout << "\n\result:" << result->mCachedSelector() << std::endl; +#endif + + Listize listize(ctx); + return result->perform(&listize); } + Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; BUILT_IN(selector_extend) { - return new (ctx.mem) String_Constant(pstate, "selector_extend NOT YET IMPLEMENTED"); + To_String to_string; + + Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); + Selector_List* extendee = ARGSEL("$extendee", Selector_List, p_contextualize); + Selector_List* extender = ARGSEL("$extender", Selector_List, p_contextualize); + + ExtensionSubsetMap subset_map; + extender->populate_extends(extendee, ctx, subset_map); + + bool extendedSomething; + Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething); + + Listize listize(ctx); + return result->perform(&listize); } + Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; BUILT_IN(selector_replace) { - return new (ctx.mem) String_Constant(pstate, "selector_replace"); + Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); + Selector_List* original = ARGSEL("$original", Selector_List, p_contextualize); + Selector_List* replacement = ARGSEL("$replacement", Selector_List, p_contextualize); + + ExtensionSubsetMap subset_map; + replacement->populate_extends(original, ctx, subset_map); + + bool extendedSomething; + Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething); + + Listize listize(ctx); + return result->perform(&listize); } - Signature selector_unify_sig = "selector-unify($selectors1, $selector2)"; + + Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; BUILT_IN(selector_unify) { - return new (ctx.mem) String_Constant(pstate, "selector_unify NOT YET IMPLEMENTED"); + Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize); + Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize); + + Selector_List* result = selector1->unify_with(selector2, ctx); + Listize listize(ctx); + return result->perform(&listize); } + Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) { - Selector_List* super = ARGSEL("$super"); - Selector_List* sub = ARGSEL("$sub"); + Selector_List* super = ARGSEL("$super", Selector_List, p_contextualize); + Selector_List* sub = ARGSEL("$sub", Selector_List, p_contextualize); + bool result = super->is_superselector_of(sub); return new (ctx.mem) Boolean(pstate, result); } + Signature simple_selectors_sig = "simple_selectors($selector)"; BUILT_IN(simple_selectors) { - To_String to_string; - - Expression* exp = ARG("$selector", Expression); - String_Constant* s; - - // Catch & as variable - s = dynamic_cast(exp); - if(!s) { - s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); - } - - string result_str(s->value()); - result_str += ';'; // the parser looks for a brace to end the selector - - Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); - p.block_stack.push_back(p_contextualize->parent->last_block()); - p.last_media_block = p_contextualize->parent->media_block(); - - Compound_Selector* sel = p.parse_simple_selector_sequence(); + Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize); + To_String to_string; List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA); + for (size_t i = 0, L = sel->length(); i < L; ++i) { Simple_Selector* ss = (*sel)[i]; string ss_string = ss->perform(&to_string) ; @@ -1662,26 +1854,9 @@ namespace Sass { Signature selector_parse_sig = "selector-parse($selector)"; BUILT_IN(selector_parse) { - To_String to_string; - - Expression* exp = ARG("$selector", Expression); - String_Constant* s; - - // Catch & as variable - s = dynamic_cast(exp); - if(!s) { - s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); - } - - string result_str(s->value()); - result_str += '{'; // the parser looks for a brace to end the selector - - Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); - p.block_stack.push_back(p_contextualize->parent->last_block()); - p.last_media_block = p_contextualize->parent->media_block(); - - Selector_List* sel = p.parse_selector_group(); - return new (ctx.mem) String_Constant(pstate, sel->perform(&to_string)); + Selector_List* sel = ARGSEL("$selector", Selector_List, p_contextualize); + Listize listize(ctx); + return sel->perform(&listize); } } } diff --git a/node.cpp b/node.cpp index 7ed2d55505..9504d24e65 100644 --- a/node.cpp +++ b/node.cpp @@ -1,3 +1,5 @@ +#include + #include "node.hpp" #include "to_string.hpp" #include "context.hpp" @@ -247,5 +249,33 @@ namespace Sass { return pFirst; } - + // A very naive trim function, which removes duplicates in a node + // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs + Node Node::naiveTrim(Node& seqses, Context& ctx) { + + Node result = Node::createCollection(); + + To_String to_string; + std::set< Complex_Selector*, std::function< bool(Complex_Selector*, Complex_Selector*) > > sel_set([&] ( Complex_Selector* lhs, Complex_Selector* rhs ) { + bool result = lhs->perform(&to_string) < rhs->perform(&to_string); + return result; + } ); + + // Add all selectors we don't already have, everything else just add it blindly + for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) { + Node& seqs1 = *seqsesIter; + if( seqs1.isSelector() ) { + auto found = sel_set.find( seqs1.selector() ); + if( found == sel_set.end() ) { + sel_set.insert(seqs1.selector()); + result.collection()->push_back(seqs1); + } + } else { + result.collection()->push_back(seqs1); + } + } + + return result; + } + } diff --git a/node.hpp b/node.hpp index 6aa1156819..f6d871ae38 100644 --- a/node.hpp +++ b/node.hpp @@ -69,6 +69,8 @@ namespace Sass { static Node createCollection(const NodeDeque& values); static Node createNil(); + + static Node naiveTrim(Node& seqses, Context& ctx); Node clone(Context& ctx) const; @@ -113,7 +115,7 @@ namespace Sass { #endif Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx); Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx); - + bool nodesEqual(const Node& one, const Node& two, bool simpleSelectorOrderDependent); } diff --git a/subset_map.hpp b/subset_map.hpp index 580c0cc145..42f841e4d2 100644 --- a/subset_map.hpp +++ b/subset_map.hpp @@ -91,6 +91,15 @@ namespace Sass { if (s.empty()) throw "internal error: subset map keys may not be empty"; size_t index = values_.size(); values_.push_back(value); + +#ifdef DEBUG + // OUTPUT S + std::cout << "Subset_Map::put key="; + for (auto n : s) + std::cout << n << ' '; + std::cout << std::endl; +#endif + set ss; for (size_t i = 0, S = s.size(); i < S; ++i) { ss.insert(s[i]); } @@ -104,6 +113,13 @@ namespace Sass { template vector > > Subset_Map::get_kv(const vector& s) { +#ifdef DEBUG + std::cout << "Subset_Map::get key="; + for (auto n : s) + std::cout << n << ' '; + std::cout << std::endl; +#endif + vector sorted = s; sort(sorted.begin(), sorted.end()); vector > > indices; From 4a910b212caa079de6bdfd6795e2c1934f346075 Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Mon, 30 Mar 2015 14:29:54 -0700 Subject: [PATCH 06/13] Working selector_unify but results not compacted --- ast.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ast.cpp b/ast.cpp index 6d5b41fe90..78a4ffa962 100644 --- a/ast.cpp +++ b/ast.cpp @@ -571,6 +571,8 @@ namespace Sass { } Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { + + #ifdef DEBUG To_String to_string; string lhs_string = perform(&to_string); From badb7a2310c5c8b53007227cbbf27d8869936ef1 Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Mon, 30 Mar 2015 20:19:23 -0700 Subject: [PATCH 07/13] # This is a combination of 2 commits. # The first commit's message is: Moved naiveTrim to Node # The 2nd commit message will be skipped: # Fixed some non-wrapped DEBUG statements causing compile errors --- node.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/node.cpp b/node.cpp index 9504d24e65..ccac750c57 100644 --- a/node.cpp +++ b/node.cpp @@ -1,5 +1,3 @@ -#include - #include "node.hpp" #include "to_string.hpp" #include "context.hpp" From 90f7e0bb5e522f67c8a71ae507a3a88805687713 Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Mon, 30 Mar 2015 20:19:23 -0700 Subject: [PATCH 08/13] Moved naiveTrim to Node --- node.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node.cpp b/node.cpp index ccac750c57..9504d24e65 100644 --- a/node.cpp +++ b/node.cpp @@ -1,3 +1,5 @@ +#include + #include "node.hpp" #include "to_string.hpp" #include "context.hpp" From fca71027b22f5707ee39b3f176ee71dfbb4f303a Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Thu, 2 Apr 2015 00:36:05 -0700 Subject: [PATCH 09/13] Templatized ARGSEL, for retrieving selector arguments --- functions.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/functions.cpp b/functions.cpp index 5d7aae99be..bc552dbfce 100644 --- a/functions.cpp +++ b/functions.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef __MINGW32__ #include "windows.h" @@ -123,6 +124,30 @@ namespace Sass { return val; } + // GET SELECTOR ARGS +#define create_sel_parser() \ + To_String to_string; \ + Expression* exp = ARG(argname, Expression); \ + String_Constant* s; \ + \ + /* Catch '&' as variable. Bug? */ \ + s = dynamic_cast(exp); \ + if(!s) { \ + s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); \ + } \ + \ + string result_str(s->value()); \ + result_str += ';';/* the parser looks for a brace to end the selector*/ \ + \ + Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); \ + if( contextualize_eval->parent ) { \ + p.block_stack.push_back(contextualize_eval->parent->last_block()); \ + p.last_media_block = contextualize_eval->parent->media_block(); \ + } \ + + template + T* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contextualize_eval); + //////////////////////////////////////////////// // RETREIVE SELECTOR FROM ARGS HELPER FUNCTIONS //////////////////////////////////////////////// @@ -168,6 +193,12 @@ if( CONTEXTUALIZE->parent ) { \ create_sel_parser(contexualize); return p.parse_simple_selector_sequence(); } + + template <> + Compound_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contextualize_eval) { + create_sel_parser(); + return p.parse_simple_selector_sequence(); + } #ifdef __MINGW32__ uint64_t GetSeed() From 80af2a73e2f53b24d3b246224deb20a1939af12d Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Thu, 2 Apr 2015 20:18:53 -0700 Subject: [PATCH 10/13] Initial version of selector-append, not working for exotic inputs --- functions.cpp | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/functions.cpp b/functions.cpp index bc552dbfce..5c824d1dc1 100644 --- a/functions.cpp +++ b/functions.cpp @@ -124,29 +124,30 @@ namespace Sass { return val; } - // GET SELECTOR ARGS -#define create_sel_parser() \ - To_String to_string; \ - Expression* exp = ARG(argname, Expression); \ - String_Constant* s; \ - \ - /* Catch '&' as variable. Bug? */ \ - s = dynamic_cast(exp); \ - if(!s) { \ - s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); \ - } \ - \ - string result_str(s->value()); \ - result_str += ';';/* the parser looks for a brace to end the selector*/ \ - \ - Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); \ - if( contextualize_eval->parent ) { \ - p.block_stack.push_back(contextualize_eval->parent->last_block()); \ - p.last_media_block = contextualize_eval->parent->media_block(); \ - } \ - + //////////////////////////////////////////////// + // RETREIVE SELECTOR FROM ARGS HELPER FUNCTIONS + //////////////////////////////////////////////// +#define create_sel_parser(CONTEXTUALIZE) \ +To_String to_string; \ +String_Constant* s; \ + \ +/* Catch '&' as variable. Bug? */ \ +s = dynamic_cast(exp); \ +if(!s) { \ + s = new (ctx.mem) String_Constant(pstate, exp->perform(&to_string)); \ +} \ + \ +string result_str(s->value()); \ +result_str += ';';/* the parser looks for a brace to end the selector*/ \ + \ +Parser p = Parser::from_c_str(result_str.c_str(), ctx, pstate); \ +if( CONTEXTUALIZE->parent ) { \ + p.block_stack.push_back(CONTEXTUALIZE->parent->last_block()); \ + p.last_media_block = CONTEXTUALIZE->parent->media_block(); \ +} \ + template - T* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contextualize_eval); + T* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contexualize); //////////////////////////////////////////////// // RETREIVE SELECTOR FROM ARGS HELPER FUNCTIONS From 49fc8892789354e59af78294af81ff49fdf142a4 Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Thu, 9 Apr 2015 14:43:00 -0700 Subject: [PATCH 11/13] Fixed edge case when callign replace, and result has length of 1 --- extend.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extend.cpp b/extend.cpp index 5e97501b6a..ba807cd47b 100644 --- a/extend.cpp +++ b/extend.cpp @@ -2172,7 +2172,8 @@ namespace Sass { } for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { - if(isReplace && iterator == iteratorBegin) continue; + // When it is a replace, skip the first one, unless there is only one + if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue; Node& childNode = *iterator; DEBUG_PRINTLN(EXTEND_COMPLEX, "\tchildNode: " << childNode) From 91ee85f79b22dab5788681605152016d6717321d Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Thu, 23 Apr 2015 16:19:46 -0700 Subject: [PATCH 12/13] Fixed duplicate entries --- ast.cpp | 36 --------------- extend.cpp | 121 -------------------------------------------------- functions.cpp | 6 --- 3 files changed, 163 deletions(-) diff --git a/ast.cpp b/ast.cpp index 78a4ffa962..75c81ca6dc 100644 --- a/ast.cpp +++ b/ast.cpp @@ -499,42 +499,6 @@ namespace Sass { - // For every selector in RHS, see if we have /any/ selectors which are a super-selector of it - bool Selector_List::is_superselector_of(Sass::Selector_List *rhs) { - -#ifdef DEBUG - To_String to_string; -#endif - - // For every selector in RHS, see if it matches /any/ of our selectors - for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { - Complex_Selector* seq1 = (*rhs)[rhs_i]; -#ifdef DEBUG - string seq1_string = seq1->perform(&to_string); -#endif - - bool any = false; - for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { - Complex_Selector* seq2 = (*this)[lhs_i]; -#ifdef DEBUG - string seq2_string = seq2->perform(&to_string); -#endif - bool is_superselector = seq2->is_superselector_of(seq1); - if( is_superselector ) { - any = true; - break; - } - } - - // Seq1 did not match any of our selectors - whole thing is no good, abort - if(!any) { - return false; - } - } - return true; - } - - // For every selector in RHS, see if we have /any/ selectors which are a super-selector of it bool Selector_List::is_superselector_of(Sass::Selector_List *rhs) { diff --git a/extend.cpp b/extend.cpp index ba807cd47b..3e1c2ab9d1 100644 --- a/extend.cpp +++ b/extend.cpp @@ -1297,127 +1297,6 @@ namespace Sass { } - Node Extend::StaticTrim(Node& seqses, Context& ctx) { - // See the comments in the above ruby code before embarking on understanding this function. - - // Avoid poor performance in extreme cases. - if (seqses.collection()->size() > 100) { - return seqses; - } - - - DEBUG_PRINTLN(TRIM, "TRIM: " << seqses) - - - Node result = Node::createCollection(); - result.plus(seqses); - - DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result) - - // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're - // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track - // of the index manually. - int toTrimIndex = 0; - - for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) { - Node& seqs1 = *seqsesIter; - - DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex) - - Node tempResult = Node::createCollection(); - - for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) { - Node& seq1 = *seqs1Iter; - - Complex_Selector* pSeq1 = nodeToComplexSelector(seq1, ctx); - - // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code - // for a good description of sources. - // - // TODO: I'm pretty sure there's a bug in the sources code. It was implemented for sass-spec's 182_test_nested_extend_loop test. - // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We - // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My - // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely - // a guess though. - int maxSpecificity = 0; - SourcesSet sources = pSeq1->sources(); - - DEBUG_PRINTLN(TRIM, "TRIMASDF SEQ1: " << seq1) - DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIMASDF SOURCES: ")) - - for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) { - const Complex_Selector* const pCurrentSelector = *sourcesSetIterator; - maxSpecificity = max(maxSpecificity, pCurrentSelector->specificity()); - } - - DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity) - - bool isMoreSpecificOuter = false; - - int resultIndex = 0; - - for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) { - Node& seqs2 = *resultIter; - - DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1) - DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2) - - // Do not compare the same sequence to itself. The ruby call we're trying to - // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision. - // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is - // derived from seqses and seqs2 is derived from result. - if (seqs1.collection() == seqs2.collection()) { - DEBUG_PRINTLN(TRIM, "CONTINUE") - continue; - } - - bool isMoreSpecificInner = false; - - for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) { - Node& seq2 = *seqs2Iter; - - Complex_Selector* pSeq2 = nodeToComplexSelector(seq2, ctx); - - DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity()) - DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false")) - DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false")) - - isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1); - - if (isMoreSpecificInner) { - DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC") - break; - } - } - - // If we found something more specific, we're done. Let the outer loop know and stop iterating. - if (isMoreSpecificInner) { - isMoreSpecificOuter = true; - break; - } - - resultIndex++; - } - - if (!isMoreSpecificOuter) { - DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1) - tempResult.collection()->push_back(seq1); - } - - } - - DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result) - DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult) - (*result.collection())[toTrimIndex] = tempResult; - - toTrimIndex++; - - DEBUG_PRINTLN(TRIM, "RESULT: " << result) - } - - return result; - } - Node Extend::StaticSubweave(Node& one, Node& two, Context& ctx) { // Check for the simple cases if (one.collection()->size() == 0) { diff --git a/functions.cpp b/functions.cpp index 5c824d1dc1..bc5fbf9cd0 100644 --- a/functions.cpp +++ b/functions.cpp @@ -195,12 +195,6 @@ if( CONTEXTUALIZE->parent ) { \ return p.parse_simple_selector_sequence(); } - template <> - Compound_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx, Contextualize* contextualize_eval) { - create_sel_parser(); - return p.parse_simple_selector_sequence(); - } - #ifdef __MINGW32__ uint64_t GetSeed() { From 74600d348b53808cf720c17a816f471962a571be Mon Sep 17 00:00:00 2001 From: Mario Gonzalez Date: Wed, 29 Apr 2015 16:06:16 -0700 Subject: [PATCH 13/13] Removed DEBUG code comments --- ast.cpp | 48 ------------------------------------------------ functions.cpp | 13 +------------ 2 files changed, 1 insertion(+), 60 deletions(-) diff --git a/ast.cpp b/ast.cpp index 75c81ca6dc..c4883ce1b9 100644 --- a/ast.cpp +++ b/ast.cpp @@ -497,28 +497,15 @@ namespace Sass { #endif } - // For every selector in RHS, see if we have /any/ selectors which are a super-selector of it bool Selector_List::is_superselector_of(Sass::Selector_List *rhs) { - -#ifdef DEBUG - To_String to_string; -#endif - // For every selector in RHS, see if it matches /any/ of our selectors for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { Complex_Selector* seq1 = (*rhs)[rhs_i]; -#ifdef DEBUG - string seq1_string = seq1->perform(&to_string); -#endif - bool any = false; for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { Complex_Selector* seq2 = (*this)[lhs_i]; -#ifdef DEBUG - string seq2_string = seq2->perform(&to_string); -#endif bool is_superselector = seq2->is_superselector_of(seq1); if( is_superselector ) { any = true; @@ -536,24 +523,6 @@ namespace Sass { Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { - -#ifdef DEBUG - To_String to_string; - string lhs_string = perform(&to_string); - string rhs_string = rhs->perform(&to_string); - - auto counter = 0; - - std::cout << "\n\n\n---------------------\n\n\n" << std::endl; - std::cout << "Unifying " << this->perform(&to_string) << " with:" << rhs->perform(&to_string) << std::endl; - std::cout << "\n\n\n---------------------\n\n\n" << std::endl; -#endif - -// Store only unique Selector_List returned by Complex_Selector::unify_with -// std::set< Selector_List*, std::function< bool(Selector_List*, Selector_List*) > > unique_selector_list([] ( Selector_List* lhs, Selector_List* rhs ) { -// return *lhs == *rhs; -// } ); - vector unified_complex_selectors; // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { @@ -561,17 +530,9 @@ namespace Sass { for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { Complex_Selector* seq2 = (*rhs)[rhs_i]; -#ifdef DEBUG - string seq1_string = seq1->perform(&to_string); - string seq2_string = seq2->perform(&to_string); - counter++; -#endif Selector_List* result = seq1->unify_with(seq2, ctx); if( result ) { for(size_t i = 0, L = result->length(); i < L; ++i) { -#ifdef DEBUG - std::cout << "Counter:" << counter << " result:" << (*result)[i]->perform(&to_string) << std::endl; -#endif unified_complex_selectors.push_back( (*result)[i] ); } } @@ -647,10 +608,6 @@ namespace Sass { Node node = Extend::StaticSubweave(lhsNode, rhsNode, ctx); -#ifdef DEBUG - std::cout << "Node:" << node << std::endl; -#endif - Selector_List* result = new (ctx.mem) Selector_List(pstate()); for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { Node childNode = *iter; @@ -659,11 +616,6 @@ namespace Sass { Complex_Selector* childNodeAsComplexSelector = nodeToComplexSelector(childNode, ctx); if( childNodeAsComplexSelector ) { (*result) << childNodeAsComplexSelector; } } - -#ifdef DEBUG -// To_String to_string; - string lhs_string = result->perform(&to_string); -#endif return result->length() ? result : 0; } diff --git a/functions.cpp b/functions.cpp index bc5fbf9cd0..dd103f9f51 100644 --- a/functions.cpp +++ b/functions.cpp @@ -1698,12 +1698,7 @@ if( CONTEXTUALIZE->parent ) { \ result->elements(newElements); } -#ifdef DEBUG - string s = result->perform(&to_string); - result->mCachedSelector(s); -// std::cout << "\n\tEndChild:" << result->mCachedSelector() << std::endl; -#endif - + Listize listize(ctx); return result->perform(&listize); } @@ -1791,12 +1786,6 @@ if( CONTEXTUALIZE->parent ) { \ result->elements(newElements); } -#ifdef DEBUG - string s = result->perform(&to_string); - result->mCachedSelector(s); -// std::cout << "\n\result:" << result->mCachedSelector() << std::endl; -#endif - Listize listize(ctx); return result->perform(&listize); }