Skip to content

Commit

Permalink
Add unify selector function from @onedayitwillmake [WIP]
Browse files Browse the repository at this point in the history
sass#1261 [WIP]
  • Loading branch information
mgreter committed Jun 5, 2015
1 parent 671e92c commit 0df5161
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 7 deletions.
61 changes: 61 additions & 0 deletions ast.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "ast.hpp"
#include "context.hpp"
#include "node.hpp"
#include "extend.hpp"
#include "debugger.hpp"
#include "to_string.hpp"
#include <set>
Expand Down Expand Up @@ -444,6 +446,40 @@ size_t n = 0;
return false;
}

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::subweave(lhsNode, rhsNode, ctx);

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; }
}
return result->length() ? result : 0;
}

bool Compound_Selector::operator==(const Compound_Selector& rhs) const {
To_String to_string;

Expand Down Expand Up @@ -781,6 +817,31 @@ size_t n = 0;
return false;
}

Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) {
vector<Complex_Selector*> 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];

Selector_List* result = seq1->unify_with(seq2, ctx);
if( result ) {
for(size_t i = 0, L = result->length(); i < L; ++i) {
unified_complex_selectors.push_back( (*result)[i] );
}
}
}
}

// Creates the final Selector_List by combining all the complex selectors
Selector_List* 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;
}

vector<string> Compound_Selector::to_str_vec()
{
To_String to_string;
Expand Down
2 changes: 2 additions & 0 deletions ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2021,6 +2021,7 @@ namespace Sass {
virtual bool is_superselector_of(Complex_Selector* sub, string wrapping = "");
virtual bool is_superselector_of(Selector_List* sub, string wrapping = "");
// virtual Selector_Placeholder* find_placeholder();
Selector_List* unify_with(Complex_Selector* rhs, Context& ctx);
Combinator clear_innermost();
void set_innermost(Complex_Selector*, Combinator);
virtual unsigned long specificity() const
Expand Down Expand Up @@ -2111,6 +2112,7 @@ namespace Sass {
// basically unwraps parsed selectors
void remove_parent_selectors();
// virtual Selector_Placeholder* find_placeholder();
Selector_List* unify_with(Selector_List*, Context&);
virtual bool is_superselector_of(Compound_Selector* sub, string wrapping = "");
virtual bool is_superselector_of(Complex_Selector* sub, string wrapping = "");
virtual bool is_superselector_of(Selector_List* sub, string wrapping = "");
Expand Down
12 changes: 7 additions & 5 deletions extend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ namespace Sass {
result
end
*/
static Node subweave(Node& one, Node& two, Context& ctx) {
Node Extend::subweave(Node& one, Node& two, Context& ctx) {
// Check for the simple cases
if (one.collection()->size() == 0) {
Node out = Node::createCollection();
Expand Down Expand Up @@ -1441,7 +1441,7 @@ namespace Sass {
for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) {
Node& before = *beforesIter;

Node sub = subweave(before, current, ctx);
Node sub = Extend::subweave(before, current, ctx);

DEBUG_PRINTLN(WEAVE, "SUB: " << sub)

Expand Down Expand Up @@ -1883,7 +1883,7 @@ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);;
/*
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);

Expand Down Expand Up @@ -1923,7 +1923,9 @@ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);;
}
}

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) {
// 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;
*pNewSelectors << nodeToComplexSelector(childNode, ctx);
}
Expand Down Expand Up @@ -1980,7 +1982,7 @@ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);;
}

bool extendedSomething = false;
Selector_List* pNewSelectorList = extendSelectorList(static_cast<Selector_List*>(pObject->selector()), ctx, subsetMap, extendedSomething);
Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast<Selector_List*>(pObject->selector()), ctx, subsetMap, false, extendedSomething);

if (extendedSomething && pNewSelectorList) {
DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))
Expand Down
4 changes: 4 additions & 0 deletions extend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Sass {
using namespace std;

class Context;
class Node;

typedef Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;

Expand All @@ -24,7 +25,10 @@ namespace Sass {

void fallback_impl(AST_Node* n) { };


public:
static Node subweave(Node& one, Node& two, Context& ctx);
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething);
Extend(Context&, ExtensionSubsetMap&);
virtual ~Extend() { }

Expand Down
4 changes: 2 additions & 2 deletions functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1632,9 +1632,9 @@ namespace Sass {
string sub_src = sel2->perform(&to_string) + "{";
Selector_List* selector1 = Parser::parse_selector(sup_src.c_str(), ctx, ctx.mem);
Selector_List* selector2 = Parser::parse_selector(sub_src.c_str(), ctx, ctx.mem);
// Selector_List* result = selector1->unify_with(selector2, ctx);
Selector_List* result = selector1->unify_with(selector2, ctx);
Listize listize(ctx);
return selector2->perform(&listize);
return result->perform(&listize);
}

Signature is_superselector_sig = "is-superselector($super, $sub)";
Expand Down
27 changes: 27 additions & 0 deletions node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,5 +284,32 @@ 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;
}
}
1 change: 1 addition & 0 deletions node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ namespace Sass {
static Node createCollection(const NodeDeque& values);

static Node createNil();
static Node naiveTrim(Node& seqses, Context& ctx);

Node clone(Context& ctx) const;

Expand Down

0 comments on commit 0df5161

Please sign in to comment.