Skip to content

Commit d755478

Browse files
committed
Move Selector_Schema beneath Selector_List
This greatly simplifies the handling with selectors. We always store a `Selector_List` and handle interpolation as needed. Selectors are evaluated in expand, which might be in a for loop. Therefore we either eval the list or the schema to get the result (caching might be possible).
1 parent df2fb87 commit d755478

File tree

5 files changed

+95
-64
lines changed

5 files changed

+95
-64
lines changed

src/ast.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "ast.hpp"
33
#include "context.hpp"
44
#include "node.hpp"
5+
#include "eval.hpp"
56
#include "extend.hpp"
67
#include "emitter.hpp"
78
#include "color_maps.hpp"
@@ -1185,7 +1186,14 @@ namespace Sass {
11851186
}
11861187
}
11871188

1189+
}
11881190

1191+
Selector_List_Obj Selector_List::eval(Eval& eval)
1192+
{
1193+
Selector_List_Obj list = schema() ?
1194+
eval(schema()) : eval(this);
1195+
list->schema(schema());
1196+
return list;
11891197
}
11901198

11911199
Selector_List_Ptr Selector_List::resolve_parent_refs(Context& ctx, std::vector<Selector_List_Obj>& pstack, bool implicit_parent)
@@ -1472,6 +1480,30 @@ namespace Sass {
14721480
}
14731481
}
14741482

1483+
size_t Wrapped_Selector::hash()
1484+
{
1485+
if (hash_ == 0) {
1486+
hash_combine(hash_, Simple_Selector::hash());
1487+
if (selector_) hash_combine(hash_, selector_->hash());
1488+
}
1489+
return hash_;
1490+
}
1491+
bool Wrapped_Selector::has_parent_ref() {
1492+
// if (has_reference()) return true;
1493+
if (!selector()) return false;
1494+
return selector()->has_parent_ref();
1495+
}
1496+
bool Wrapped_Selector::has_real_parent_ref() {
1497+
// if (has_reference()) return true;
1498+
if (!selector()) return false;
1499+
return selector()->has_real_parent_ref();
1500+
}
1501+
unsigned long Wrapped_Selector::specificity() const
1502+
{
1503+
return selector_ ? selector_->specificity() : 0;
1504+
}
1505+
1506+
14751507
bool Selector_List::has_parent_ref()
14761508
{
14771509
for (Complex_Selector_Obj s : elements()) {

src/ast.hpp

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -521,10 +521,10 @@ namespace Sass {
521521
// of style declarations.
522522
/////////////////////////////////////////////////////////////////////////////
523523
class Ruleset : public Has_Block {
524-
ADD_PROPERTY(Selector_Obj, selector)
524+
ADD_PROPERTY(Selector_List_Obj, selector)
525525
ADD_PROPERTY(bool, is_root);
526526
public:
527-
Ruleset(ParserState pstate, Selector_Obj s = 0, Block_Obj b = 0)
527+
Ruleset(ParserState pstate, Selector_List_Obj s = 0, Block_Obj b = 0)
528528
: Has_Block(pstate, b), selector_(s), is_root_(false)
529529
{ statement_type(RULESET); }
530530
Ruleset(const Ruleset* ptr)
@@ -598,10 +598,10 @@ namespace Sass {
598598
///////////////////////////////////////////////////////////////////////
599599
class Directive : public Has_Block {
600600
ADD_CONSTREF(std::string, keyword)
601-
ADD_PROPERTY(Selector_Obj, selector)
601+
ADD_PROPERTY(Selector_List_Obj, selector)
602602
ADD_PROPERTY(Expression_Obj, value)
603603
public:
604-
Directive(ParserState pstate, std::string kwd, Selector_Obj sel = 0, Block_Obj b = 0, Expression_Obj val = 0)
604+
Directive(ParserState pstate, std::string kwd, Selector_List_Obj sel = 0, Block_Obj b = 0, Expression_Obj val = 0)
605605
: Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed
606606
{ statement_type(DIRECTIVE); }
607607
Directive(const Directive* ptr)
@@ -633,7 +633,7 @@ namespace Sass {
633633
class Keyframe_Rule : public Has_Block {
634634
// according to css spec, this should be <keyframes-name>
635635
// <keyframes-name> = <custom-ident> | <string>
636-
ADD_PROPERTY(Selector_Obj, name)
636+
ADD_PROPERTY(Selector_List_Obj, name)
637637
public:
638638
Keyframe_Rule(ParserState pstate, Block_Obj b)
639639
: Has_Block(pstate, b), name_()
@@ -911,9 +911,9 @@ namespace Sass {
911911
// The Sass `@extend` directive.
912912
////////////////////////////////
913913
class Extension : public Statement {
914-
ADD_PROPERTY(Selector_Obj, selector)
914+
ADD_PROPERTY(Selector_List_Obj, selector)
915915
public:
916-
Extension(ParserState pstate, Selector_Obj s)
916+
Extension(ParserState pstate, Selector_List_Obj s)
917917
: Statement(pstate), selector_(s)
918918
{ statement_type(EXTEND); }
919919
Extension(const Extension* ptr)
@@ -2313,19 +2313,26 @@ namespace Sass {
23132313
// Interpolated selectors -- the interpolated String will be expanded and
23142314
// re-parsed into a normal selector class.
23152315
/////////////////////////////////////////////////////////////////////////
2316-
class Selector_Schema : public Selector {
2316+
class Selector_Schema : public AST_Node {
23172317
ADD_PROPERTY(String_Obj, contents)
23182318
ADD_PROPERTY(bool, connect_parent);
2319+
// must not be a reference counted object
2320+
// otherwise we create circular references
2321+
ADD_PROPERTY(Media_Block_Ptr, media_block)
2322+
// store computed hash
2323+
size_t hash_;
23192324
public:
23202325
Selector_Schema(ParserState pstate, String_Obj c)
2321-
: Selector(pstate),
2326+
: AST_Node(pstate),
23222327
contents_(c),
2323-
connect_parent_(true)
2328+
connect_parent_(true),
2329+
media_block_(NULL)
23242330
{ }
23252331
Selector_Schema(const Selector_Schema* ptr)
2326-
: Selector(ptr),
2332+
: AST_Node(ptr),
23272333
contents_(ptr->contents_),
2328-
connect_parent_(ptr->connect_parent_)
2334+
connect_parent_(ptr->connect_parent_),
2335+
media_block_(ptr->media_block_)
23292336
{ }
23302337
virtual bool has_parent_ref();
23312338
virtual bool has_real_parent_ref();
@@ -2657,9 +2664,9 @@ namespace Sass {
26572664
// Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir)
26582665
/////////////////////////////////////////////////
26592666
class Wrapped_Selector : public Simple_Selector {
2660-
ADD_PROPERTY(Selector_Obj, selector)
2667+
ADD_PROPERTY(Selector_List_Obj, selector)
26612668
public:
2662-
Wrapped_Selector(ParserState pstate, std::string n, Selector_Obj sel)
2669+
Wrapped_Selector(ParserState pstate, std::string n, Selector_List_Obj sel)
26632670
: Simple_Selector(pstate, n), selector_(sel)
26642671
{ simple_type(WRAPPED_SEL); }
26652672
Wrapped_Selector(const Wrapped_Selector* ptr)
@@ -2668,28 +2675,10 @@ namespace Sass {
26682675
virtual bool is_superselector_of(Wrapped_Selector_Obj sub);
26692676
// Selectors inside the negation pseudo-class are counted like any
26702677
// other, but the negation itself does not count as a pseudo-class.
2671-
virtual size_t hash()
2672-
{
2673-
if (hash_ == 0) {
2674-
hash_combine(hash_, Simple_Selector::hash());
2675-
if (selector_) hash_combine(hash_, selector_->hash());
2676-
}
2677-
return hash_;
2678-
}
2679-
virtual bool has_parent_ref() {
2680-
// if (has_reference()) return true;
2681-
if (!selector()) return false;
2682-
return selector()->has_parent_ref();
2683-
}
2684-
virtual bool has_real_parent_ref() {
2685-
// if (has_reference()) return true;
2686-
if (!selector()) return false;
2687-
return selector()->has_real_parent_ref();
2688-
}
2689-
virtual unsigned long specificity() const
2690-
{
2691-
return selector_ ? selector_->specificity() : 0;
2692-
}
2678+
virtual size_t hash();
2679+
virtual bool has_parent_ref();
2680+
virtual bool has_real_parent_ref();
2681+
virtual unsigned long specificity() const;
26932682
virtual bool operator==(const Simple_Selector& rhs) const;
26942683
virtual bool operator==(const Wrapped_Selector& rhs) const;
26952684
virtual bool operator<(const Simple_Selector& rhs) const;
@@ -2971,16 +2960,21 @@ namespace Sass {
29712960
// Comma-separated selector groups.
29722961
///////////////////////////////////
29732962
class Selector_List : public Selector, public Vectorized<Complex_Selector_Obj> {
2963+
ADD_PROPERTY(Selector_Schema_Obj, schema)
29742964
ADD_CONSTREF(std::vector<std::string>, wspace)
29752965
protected:
29762966
void adjust_after_pushing(Complex_Selector_Obj c);
29772967
public:
29782968
Selector_List(ParserState pstate, size_t s = 0)
2979-
: Selector(pstate), Vectorized<Complex_Selector_Obj>(s), wspace_(0)
2969+
: Selector(pstate),
2970+
Vectorized<Complex_Selector_Obj>(s),
2971+
schema_(NULL),
2972+
wspace_(0)
29802973
{ }
29812974
Selector_List(const Selector_List* ptr)
29822975
: Selector(ptr),
29832976
Vectorized<Complex_Selector_Obj>(*ptr),
2977+
schema_(ptr->schema_),
29842978
wspace_(ptr->wspace_)
29852979
{ }
29862980
std::string type() { return "list"; }
@@ -2995,6 +2989,7 @@ namespace Sass {
29952989
virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");
29962990
Selector_List_Ptr unify_with(Selector_List_Ptr, Context&);
29972991
void populate_extends(Selector_List_Obj, Context&, Subset_Map&);
2992+
Selector_List_Obj eval(Eval& eval);
29982993
virtual size_t hash()
29992994
{
30002995
if (Selector::hash_ == 0) {

src/debugger.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)
103103
std::cerr << (selector->has_line_break() ? " [line-break]": " -");
104104
std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");
105105
std::cerr << std::endl;
106+
debug_ast(selector->schema(), "#{} ");
106107

107108
for(const Complex_Selector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }
108109

@@ -261,8 +262,6 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)
261262
std::cerr << " (" << pstate_source_position(node) << ")"
262263
<< " [@media:" << selector->media_block() << "]"
263264
<< (selector->connect_parent() ? " [connect-parent]": " -")
264-
<< (selector->has_line_break() ? " [line-break]": " -")
265-
<< (selector->has_line_feed() ? " [line-feed]": " -")
266265
<< std::endl;
267266

268267
debug_ast(selector->contents(), ind + " ");

src/expand.cpp

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,11 @@ namespace Sass {
9494
Block_Ptr bb = operator()(r->block());
9595
Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);
9696
if (r->selector()) {
97-
selector_stack.push_back(0);
98-
k->name(Cast<Selector_List>(r->selector()->perform(&eval)));
99-
selector_stack.pop_back();
97+
if (Selector_List_Ptr s = r->selector()) {
98+
selector_stack.push_back(0);
99+
k->name(s->eval(eval));
100+
selector_stack.pop_back();
101+
}
100102
}
101103
return k.detach();
102104
}
@@ -111,10 +113,8 @@ namespace Sass {
111113
has_parent_selector = ll != 0 && ll->length() > 0;
112114
}
113115

114-
Expression_Obj ex = 0;
115-
if (r->selector()) ex = r->selector()->perform(&eval);
116-
Selector_List_Obj sel = Cast<Selector_List>(ex);
117-
if (sel == 0) throw std::runtime_error("Expanded null selector");
116+
Selector_List_Obj sel = r->selector();
117+
if (sel) sel = sel->eval(eval);
118118

119119
// check for parent selectors in base level rules
120120
if (r->is_root()) {
@@ -219,11 +219,11 @@ namespace Sass {
219219
{
220220
LOCAL_FLAG(in_keyframes, a->is_keyframes());
221221
Block_Ptr ab = a->block();
222-
Selector_Ptr as = a->selector();
222+
Selector_List_Ptr as = a->selector();
223223
Expression_Ptr av = a->value();
224224
selector_stack.push_back(0);
225225
if (av) av = av->perform(&eval);
226-
if (as) as = Cast<Selector>(as->perform(&eval));
226+
if (as) as = eval(as);
227227
selector_stack.pop_back();
228228
Block_Ptr bb = ab ? operator()(ab) : NULL;
229229
Directive_Ptr aa = SASS_MEMORY_NEW(Directive,
@@ -626,28 +626,23 @@ namespace Sass {
626626

627627
Statement* Expand::operator()(Extension_Ptr e)
628628
{
629-
if (Selector_List_Obj extender = Cast<Selector_List>(selector())) {
630-
Selector_Obj s = e->selector();
631-
Selector_List_Obj sl = NULL;
632-
// check if we already have a valid selector list
633-
if ((sl = Cast<Selector_List>(s))) {}
634-
// convert selector schema to a selector list
635-
else if (Selector_Schema_Obj schema = Cast<Selector_Schema>(s)) {
629+
if (Selector_List_Ptr extender = selector()) {
630+
Selector_List_Ptr sl = e->selector();
631+
// abort on invalid selector
632+
if (sl == NULL) return NULL;
633+
if (Selector_Schema_Ptr schema = sl->schema()) {
636634
if (schema->has_real_parent_ref()) {
637635
// put root block on stack again (ignore parents)
638636
// selector schema must not connect in eval!
639637
block_stack.push_back(block_stack.at(1));
640-
sl = eval(schema);
638+
sl = eval(sl->schema());
641639
block_stack.pop_back();
642640
} else {
643641
selector_stack.push_back(0);
644-
sl = eval(schema);
645-
sl->remove_parent_selectors();
642+
sl = eval(sl->schema());
646643
selector_stack.pop_back();
647644
}
648645
}
649-
// abort on invalid selector
650-
if (sl.isNull()) return NULL;
651646
for (Complex_Selector_Obj cs : sl->elements()) {
652647
if (!cs.isNull() && !cs->head().isNull()) {
653648
cs->head()->media_block(media_block_stack.back());

src/parser.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,15 @@ namespace Sass {
248248
else if (lex < kwd_extend >(true)) {
249249
Lookahead lookahead = lookahead_for_include(position);
250250
if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");
251-
Selector_Obj target;
252-
if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found, true);
253-
else target = parse_selector_list(true);
251+
Selector_List_Obj target;
252+
if (!lookahead.has_interpolants) {
253+
target = parse_selector_list(true);
254+
}
255+
else {
256+
target = SASS_MEMORY_NEW(Selector_List, pstate);
257+
target->schema(parse_selector_schema(lookahead.found, true));
258+
}
259+
254260
block->append(SASS_MEMORY_NEW(Extension, pstate, target));
255261
}
256262

@@ -496,7 +502,11 @@ namespace Sass {
496502
Ruleset_Obj ruleset = SASS_MEMORY_NEW(Ruleset, pstate);
497503
// parse selector static or as schema to be evaluated later
498504
if (lookahead.parsable) ruleset->selector(parse_selector_list(false));
499-
else ruleset->selector(parse_selector_schema(lookahead.found, false));
505+
else {
506+
Selector_List_Obj list = SASS_MEMORY_NEW(Selector_List, pstate);
507+
list->schema(parse_selector_schema(lookahead.found, false));
508+
ruleset->selector(list);
509+
}
500510
// then parse the inner block
501511
stack.push_back(Scope::Rules);
502512
ruleset->block(parse_block());

0 commit comments

Comments
 (0)