Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.

Commit 2e2d97a

Browse files
author
Aaron Leung
committed
Working on selector normalization and ordering.
1 parent 4e8099a commit 2e2d97a

File tree

7 files changed

+175
-16
lines changed

7 files changed

+175
-16
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ SOURCES = \
55
constants.cpp context.cpp functions.cpp document.cpp \
66
document_parser.cpp eval_apply.cpp node.cpp \
77
node_factory.cpp node_emitters.cpp prelexer.cpp \
8-
sass_interface.cpp
8+
selector.cpp sass_interface.cpp
99
OBJECTS = $(SOURCES:.cpp=.o)
1010

1111
all: $(OBJECTS)

Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ lib_LTLIBRARIES = libsass.la
44
libsass_la_SOURCES = context.cpp functions.cpp document.cpp \
55
constants.cpp document_parser.cpp eval_apply.cpp node.cpp \
66
node_factory.cpp node_emitters.cpp prelexer.cpp \
7-
sass_interface.cpp
7+
selector.cpp sass_interface.cpp
88
libsass_la_LDFLAGS = -no-undefined -version-info 0:0:0
99

1010
include_HEADERS = sass_interface.h

Makefile.in

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ LTLIBRARIES = $(lib_LTLIBRARIES)
8686
libsass_la_LIBADD =
8787
am_libsass_la_OBJECTS = context.lo functions.lo document.lo \
8888
constants.lo document_parser.lo eval_apply.lo node.lo node_factory.lo \
89-
node_emitters.lo prelexer.lo sass_interface.lo
89+
node_emitters.lo prelexer.lo selector.lo sass_interface.lo
9090
libsass_la_OBJECTS = $(am_libsass_la_OBJECTS)
9191
libsass_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
9292
$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
@@ -247,7 +247,7 @@ lib_LTLIBRARIES = libsass.la
247247
libsass_la_SOURCES = context.cpp functions.cpp document.cpp \
248248
constants.cpp document_parser.cpp eval_apply.cpp node.cpp \
249249
node_factory.cpp node_emitters.cpp prelexer.cpp \
250-
sass_interface.cpp
250+
selector.cpp sass_interface.cpp
251251

252252
libsass_la_LDFLAGS = -no-undefined -version-info 0:0:0
253253
include_HEADERS = sass_interface.h
@@ -355,6 +355,7 @@ distclean-compile:
355355
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_emitters.Plo@am__quote@
356356
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_factory.Plo@am__quote@
357357
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prelexer.Plo@am__quote@
358+
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selector.Plo@am__quote@
358359
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sass_interface.Plo@am__quote@
359360

360361
.cpp.o:

eval_apply.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "eval_apply.hpp"
2+
#include "selector.hpp"
23
#include "constants.hpp"
34
#include "prelexer.hpp"
45
#include "document.hpp"
@@ -106,6 +107,8 @@ namespace Sass {
106107
// }
107108

108109
// expand the body with the newly expanded selector as the prefix
110+
cerr << "ORIGINAL SELECTOR:\t" << expr[2].to_string() << endl;
111+
cerr << "NORMALIZED SELECTOR:\t" << normalize_selector(expr[2], new_Node).to_string() << endl << endl;
109112
expand(expr[1], expr.back(), env, f_env, new_Node, ctx);
110113
} break;
111114

@@ -1112,8 +1115,11 @@ namespace Sass {
11121115
for (multimap<Node, Node>::iterator request = extension_requests.lower_bound(sel);
11131116
request != extension_requests.upper_bound(sel);
11141117
++request) {
1115-
group << generate_extension(expr[2], request->second, new_Node);
1118+
Node ext(generate_extension(expr[2], request->second, new_Node));
1119+
if (ext.type() == Node::selector_group) group += ext;
1120+
else group << ext;
11161121
}
1122+
group = remove_duplicate_selectors(group, new_Node);
11171123
group.has_been_extended() = true;
11181124
expr[2] = group;
11191125
}
@@ -1132,13 +1138,16 @@ namespace Sass {
11321138
for (multimap<Node, Node>::iterator request = extension_requests.lower_bound(sel);
11331139
request != extension_requests.upper_bound(sel);
11341140
++request) {
1135-
new_group << generate_extension(group[i], request->second, new_Node);
1141+
Node ext(generate_extension(group[i], request->second, new_Node));
1142+
if (ext.type() == Node::selector_group) new_group += ext;
1143+
else new_group << ext;
11361144
}
11371145
group[i].has_been_extended() = true;
11381146
}
11391147
}
11401148
if (new_group.size() > 0) {
11411149
group.has_been_extended() = true;
1150+
new_group = remove_duplicate_selectors(new_group, new_Node);
11421151
new_group.has_been_extended() = true;
11431152
expr[2] = new_group;
11441153
}

node.cpp

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ namespace Sass {
6161

6262
string Node::unquote() const
6363
{
64+
Type t = type();
65+
switch (type())
66+
{
67+
case string_constant:
68+
case identifier: {
69+
return token().unquote();
70+
} break;
71+
72+
default: {
73+
// do nothing; fall though to the rest
74+
} break;
75+
}
76+
6477
string intermediate(to_string());
6578
if (!intermediate.empty() && (intermediate[0] == '"' || intermediate[0] == '\'')) {
6679
return intermediate.substr(1, intermediate.length() - 2);
@@ -74,8 +87,11 @@ namespace Sass {
7487
{
7588
Type t = type(), u = rhs.type();
7689

77-
if ((t == identifier || t == string_constant || t == string_schema || t == concatenation) &&
78-
(u == identifier || u == string_constant || u == string_schema || u == concatenation)) {
90+
// if ((t == identifier || t == string_constant || t == string_schema || t == concatenation) &&
91+
// (u == identifier || u == string_constant || u == string_schema || u == concatenation)) {
92+
// return unquote() == rhs.unquote();
93+
// }
94+
if (is_string() && rhs.is_string()) {
7995
return unquote() == rhs.unquote();
8096
}
8197
else if (t != u) {
@@ -126,8 +142,25 @@ namespace Sass {
126142
return boolean_value() == rhs.boolean_value();
127143
} break;
128144

145+
case selector: {
146+
if (has_children() && rhs.has_children() && (size() == rhs.size())) {
147+
for (size_t i = 0, S = size(); i < S; ++i) {
148+
if (at(i) == rhs[i]) continue;
149+
else return false;
150+
}
151+
return true;
152+
}
153+
else {
154+
return false;
155+
}
156+
} break;
157+
158+
case simple_selector: {
159+
if (token() == rhs.token()) return true;
160+
} break;
161+
129162
default: {
130-
return true;
163+
return false;
131164
} break;
132165
}
133166
return false;
@@ -163,12 +196,16 @@ namespace Sass {
163196
}
164197

165198
// comparing identifiers and strings (treat them as comparable)
166-
else if ((lhs_type == identifier || lhs_type == string_constant || lhs_type == value) &&
167-
(rhs_type == identifier || lhs_type == string_constant || rhs_type == value)) {
168-
return token().unquote() < rhs.token().unquote();
199+
else if (is_string() && rhs.is_string()) {
200+
return unquote() < rhs.unquote();
169201
}
170202

171-
// COMPARING SELECTORS -- IMPORTANT FOR INHERITANCE
203+
// else if ((lhs_type == identifier || lhs_type == string_constant || lhs_type == value) &&
204+
// (rhs_type == identifier || lhs_type == string_constant || rhs_type == value)) {
205+
// return token().unquote() < rhs.token().unquote();
206+
// }
207+
208+
// COMPARING SELECTORS -- IMPORTANT FOR ORDERING AND NORMALIZING
172209
else if ((type() >= selector_group && type() <=selector_schema) &&
173210
(rhs.type() >= selector_group && rhs.type() <=selector_schema)) {
174211

@@ -183,13 +220,15 @@ namespace Sass {
183220
return token() < rhs.token();
184221
} break;
185222

223+
// assumes selectors are normalized by the time they're compared
186224
case selector:
187-
case attribute_selector: {
225+
case simple_selector_sequence:
226+
case attribute_selector:
227+
case functional_pseudo:
228+
case pseudo_not: {
188229
return lexicographical_compare(begin(), end(), rhs.begin(), rhs.end());
189230
} break;
190231

191-
192-
193232
default: {
194233
return false;
195234
} break;

selector.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include <set>
2+
3+
#include "selector.hpp"
4+
5+
namespace Sass {
6+
using namespace std;
7+
8+
Node normalize_selector(Node s, Node_Factory& new_Node)
9+
{
10+
switch (s.type())
11+
{
12+
case Node::selector_group: {
13+
Node normalized(new_Node(Node::selector_group, s.path(), s.line(), 1));
14+
set<Node> normalizer;
15+
for (size_t i = 0, S = s.size(); i < S; ++i)
16+
normalizer.insert(normalize_selector(s[i], new_Node));
17+
for (set<Node>::iterator i = normalizer.begin(); i != normalizer.end(); ++i)
18+
normalized << *i;
19+
return normalized;
20+
} break;
21+
22+
case Node::selector: {
23+
Node normalized(new_Node(Node::selector, s.path(), s.line(), s.size()));
24+
for (size_t i = 0, S = s.size(); i < S; ++i)
25+
normalized << normalize_selector(s[i], new_Node);
26+
return normalized;
27+
} break;
28+
29+
case Node::simple_selector_sequence: {
30+
Node normalized(new_Node(Node::simple_selector_sequence, s.path(), s.line(), 1));
31+
set<Node> normalizer;
32+
size_t i = 0;
33+
if (!selector_is_qualifier(s[0])) {
34+
normalized << s[0];
35+
i = 1;
36+
}
37+
for (size_t S = s.size(); i < S; ++i)
38+
normalizer.insert(normalize_selector(s[i], new_Node));
39+
for (set<Node>::iterator i = normalizer.begin(); i != normalizer.end(); ++i)
40+
normalized << *i;
41+
return normalized;
42+
} break;
43+
44+
default: {
45+
return s;
46+
} break;
47+
}
48+
return s;
49+
}
50+
51+
// Remove duplicate selectors from a selector group. Used when extending.
52+
Node remove_duplicate_selectors(Node group, Node_Factory& new_Node)
53+
{
54+
if (group.type() != Node::selector_group) return group;
55+
56+
Node filtered(new_Node(Node::selector_group, group.path(), group.line(), 1));
57+
for (size_t i = 0, S = group.size(); i < S; ++i) {
58+
bool found_dup = false;
59+
for (size_t j = 0; j < filtered.size(); ++j) {
60+
if (group[i] == filtered[j]) {
61+
found_dup = true;
62+
break;
63+
}
64+
}
65+
if (!found_dup) filtered << group[i];
66+
}
67+
68+
return filtered;
69+
}
70+
71+
bool selector_is_qualifier(Node s)
72+
{
73+
switch (s.type())
74+
{
75+
case Node::pseudo:
76+
case Node::pseudo_negation:
77+
case Node::functional_pseudo:
78+
case Node::attribute_selector: {
79+
return true;
80+
} break;
81+
82+
case Node::simple_selector: {
83+
if ((*s.token().begin == '.') || (*s.token().begin == '#')) return true;
84+
} break;
85+
86+
default: {
87+
return false;
88+
} break;
89+
}
90+
return false;
91+
}
92+
93+
// Node selector_is_specialization_of(Node s, Node t)
94+
// {
95+
96+
// }
97+
}

selector.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef SASS_NODE
2+
#include "node.hpp"
3+
#endif
4+
5+
#ifndef SASS_NODE_FACTORY
6+
#include "node_factory.hpp"
7+
#endif
8+
9+
namespace Sass {
10+
Node normalize_selector(Node s, Node_Factory& new_Node);
11+
Node remove_duplicate_selectors(Node group, Node_Factory& new_Node);
12+
bool selector_is_qualifier(Node s);
13+
}

0 commit comments

Comments
 (0)