sassc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +24 -0
  8. data/Rakefile +21 -0
  9. data/ext/libsass/.editorconfig +15 -0
  10. data/ext/libsass/.gitattributes +2 -0
  11. data/ext/libsass/.gitignore +61 -0
  12. data/ext/libsass/.travis.yml +38 -0
  13. data/ext/libsass/COPYING +25 -0
  14. data/ext/libsass/INSTALL +1 -0
  15. data/ext/libsass/LICENSE +25 -0
  16. data/ext/libsass/Makefile +223 -0
  17. data/ext/libsass/Makefile.am +145 -0
  18. data/ext/libsass/Readme.md +93 -0
  19. data/ext/libsass/appveyor.yml +76 -0
  20. data/ext/libsass/ast.cpp +581 -0
  21. data/ext/libsass/ast.hpp +1949 -0
  22. data/ext/libsass/ast_def_macros.hpp +16 -0
  23. data/ext/libsass/ast_factory.hpp +87 -0
  24. data/ext/libsass/ast_fwd_decl.hpp +72 -0
  25. data/ext/libsass/b64/cencode.h +32 -0
  26. data/ext/libsass/b64/encode.h +77 -0
  27. data/ext/libsass/backtrace.hpp +81 -0
  28. data/ext/libsass/base64vlq.cpp +43 -0
  29. data/ext/libsass/base64vlq.hpp +28 -0
  30. data/ext/libsass/bind.cpp +187 -0
  31. data/ext/libsass/bind.hpp +18 -0
  32. data/ext/libsass/cencode.c +102 -0
  33. data/ext/libsass/color_names.hpp +324 -0
  34. data/ext/libsass/configure.ac +130 -0
  35. data/ext/libsass/constants.cpp +144 -0
  36. data/ext/libsass/constants.hpp +145 -0
  37. data/ext/libsass/context.cpp +507 -0
  38. data/ext/libsass/context.hpp +150 -0
  39. data/ext/libsass/contextualize.cpp +157 -0
  40. data/ext/libsass/contextualize.hpp +65 -0
  41. data/ext/libsass/copy_c_str.cpp +13 -0
  42. data/ext/libsass/copy_c_str.hpp +5 -0
  43. data/ext/libsass/debug.hpp +39 -0
  44. data/ext/libsass/environment.hpp +75 -0
  45. data/ext/libsass/error_handling.cpp +28 -0
  46. data/ext/libsass/error_handling.hpp +28 -0
  47. data/ext/libsass/eval.cpp +1149 -0
  48. data/ext/libsass/eval.hpp +80 -0
  49. data/ext/libsass/expand.cpp +430 -0
  50. data/ext/libsass/expand.hpp +77 -0
  51. data/ext/libsass/extconf.rb +6 -0
  52. data/ext/libsass/extend.cpp +1962 -0
  53. data/ext/libsass/extend.hpp +50 -0
  54. data/ext/libsass/file.cpp +291 -0
  55. data/ext/libsass/file.hpp +18 -0
  56. data/ext/libsass/functions.cpp +1565 -0
  57. data/ext/libsass/functions.hpp +187 -0
  58. data/ext/libsass/inspect.cpp +727 -0
  59. data/ext/libsass/inspect.hpp +108 -0
  60. data/ext/libsass/json.cpp +1411 -0
  61. data/ext/libsass/json.hpp +117 -0
  62. data/ext/libsass/kwd_arg_macros.hpp +23 -0
  63. data/ext/libsass/m4/.gitkeep +0 -0
  64. data/ext/libsass/mapping.hpp +17 -0
  65. data/ext/libsass/memory_manager.hpp +54 -0
  66. data/ext/libsass/node.cpp +251 -0
  67. data/ext/libsass/node.hpp +122 -0
  68. data/ext/libsass/operation.hpp +153 -0
  69. data/ext/libsass/output_compressed.cpp +401 -0
  70. data/ext/libsass/output_compressed.hpp +95 -0
  71. data/ext/libsass/output_nested.cpp +364 -0
  72. data/ext/libsass/output_nested.hpp +108 -0
  73. data/ext/libsass/parser.cpp +2016 -0
  74. data/ext/libsass/parser.hpp +264 -0
  75. data/ext/libsass/paths.hpp +69 -0
  76. data/ext/libsass/position.hpp +22 -0
  77. data/ext/libsass/posix/getopt.c +562 -0
  78. data/ext/libsass/posix/getopt.h +95 -0
  79. data/ext/libsass/prelexer.cpp +688 -0
  80. data/ext/libsass/prelexer.hpp +513 -0
  81. data/ext/libsass/remove_placeholders.cpp +59 -0
  82. data/ext/libsass/remove_placeholders.hpp +43 -0
  83. data/ext/libsass/res/resource.rc +35 -0
  84. data/ext/libsass/sass.cpp +33 -0
  85. data/ext/libsass/sass.h +60 -0
  86. data/ext/libsass/sass2scss.cpp +834 -0
  87. data/ext/libsass/sass2scss.h +110 -0
  88. data/ext/libsass/sass_context.cpp +709 -0
  89. data/ext/libsass/sass_context.h +120 -0
  90. data/ext/libsass/sass_functions.cpp +137 -0
  91. data/ext/libsass/sass_functions.h +90 -0
  92. data/ext/libsass/sass_interface.cpp +277 -0
  93. data/ext/libsass/sass_interface.h +97 -0
  94. data/ext/libsass/sass_util.cpp +136 -0
  95. data/ext/libsass/sass_util.hpp +259 -0
  96. data/ext/libsass/sass_values.cpp +337 -0
  97. data/ext/libsass/sass_values.h +124 -0
  98. data/ext/libsass/script/bootstrap +10 -0
  99. data/ext/libsass/script/branding +10 -0
  100. data/ext/libsass/script/ci-build-libsass +72 -0
  101. data/ext/libsass/script/ci-install-compiler +4 -0
  102. data/ext/libsass/script/ci-install-deps +19 -0
  103. data/ext/libsass/script/ci-report-coverage +25 -0
  104. data/ext/libsass/script/coveralls-debug +32 -0
  105. data/ext/libsass/script/spec +5 -0
  106. data/ext/libsass/script/tap-driver +652 -0
  107. data/ext/libsass/script/tap-runner +1 -0
  108. data/ext/libsass/source_map.cpp +133 -0
  109. data/ext/libsass/source_map.hpp +46 -0
  110. data/ext/libsass/subset_map.hpp +145 -0
  111. data/ext/libsass/support/libsass.pc.in +11 -0
  112. data/ext/libsass/test-driver +127 -0
  113. data/ext/libsass/test/test_node.cpp +98 -0
  114. data/ext/libsass/test/test_paths.cpp +29 -0
  115. data/ext/libsass/test/test_selector_difference.cpp +28 -0
  116. data/ext/libsass/test/test_specificity.cpp +28 -0
  117. data/ext/libsass/test/test_subset_map.cpp +472 -0
  118. data/ext/libsass/test/test_superselector.cpp +71 -0
  119. data/ext/libsass/test/test_unification.cpp +33 -0
  120. data/ext/libsass/to_c.cpp +61 -0
  121. data/ext/libsass/to_c.hpp +44 -0
  122. data/ext/libsass/to_string.cpp +29 -0
  123. data/ext/libsass/to_string.hpp +32 -0
  124. data/ext/libsass/token.hpp +32 -0
  125. data/ext/libsass/units.cpp +54 -0
  126. data/ext/libsass/units.hpp +10 -0
  127. data/ext/libsass/utf8.h +34 -0
  128. data/ext/libsass/utf8/checked.h +327 -0
  129. data/ext/libsass/utf8/core.h +329 -0
  130. data/ext/libsass/utf8/unchecked.h +228 -0
  131. data/ext/libsass/utf8_string.cpp +102 -0
  132. data/ext/libsass/utf8_string.hpp +36 -0
  133. data/ext/libsass/util.cpp +189 -0
  134. data/ext/libsass/util.hpp +26 -0
  135. data/ext/libsass/win/libsass.filters +291 -0
  136. data/ext/libsass/win/libsass.sln +28 -0
  137. data/ext/libsass/win/libsass.vcxproj +255 -0
  138. data/lib/sassc.rb +6 -0
  139. data/lib/sassc/engine.rb +13 -0
  140. data/lib/sassc/native.rb +44 -0
  141. data/lib/sassc/native/native_context_api.rb +140 -0
  142. data/lib/sassc/native/native_functions_api.rb +41 -0
  143. data/lib/sassc/native/sass_input_style.rb +11 -0
  144. data/lib/sassc/native/sass_output_style.rb +10 -0
  145. data/lib/sassc/native/sass_value.rb +95 -0
  146. data/lib/sassc/native/string_list.rb +8 -0
  147. data/lib/sassc/version.rb +3 -0
  148. data/sassc.gemspec +43 -0
  149. data/test/smoke_test.rb +171 -0
  150. data/test/test_helper.rb +4 -0
  151. metadata +281 -0
@@ -0,0 +1,77 @@
1
+ #define SASS_EXPAND
2
+
3
+ #include <vector>
4
+ #include <map>
5
+ #include <iostream>
6
+
7
+ #ifndef SASS_AST
8
+ #include "ast.hpp"
9
+ #endif
10
+
11
+ #ifndef SASS_OPERATION
12
+ #include "operation.hpp"
13
+ #endif
14
+
15
+ #ifndef SASS_ENVIRONMENT
16
+ #include "environment.hpp"
17
+ #endif
18
+
19
+ namespace Sass {
20
+ using namespace std;
21
+
22
+ struct Context;
23
+ class Eval;
24
+ class Contextualize;
25
+ typedef Environment<AST_Node*> Env;
26
+ struct Backtrace;
27
+
28
+ class Expand : public Operation_CRTP<Statement*, Expand> {
29
+
30
+ Context& ctx;
31
+ Eval* eval;
32
+ Contextualize* contextualize;
33
+ Env* env;
34
+ vector<Block*> block_stack;
35
+ vector<String*> property_stack;
36
+ vector<Selector*> selector_stack;
37
+ Backtrace* backtrace;
38
+
39
+ Statement* fallback_impl(AST_Node* n);
40
+
41
+ public:
42
+ Expand(Context&, Eval*, Contextualize*, Env*, Backtrace*);
43
+ virtual ~Expand() { }
44
+
45
+ using Operation<Statement*>::operator();
46
+
47
+ Statement* operator()(Block*);
48
+ Statement* operator()(Ruleset*);
49
+ Statement* operator()(Propset*);
50
+ Statement* operator()(Media_Block*);
51
+ Statement* operator()(Feature_Block*);
52
+ Statement* operator()(At_Rule*);
53
+ Statement* operator()(Declaration*);
54
+ Statement* operator()(Assignment*);
55
+ Statement* operator()(Import*);
56
+ Statement* operator()(Import_Stub*);
57
+ Statement* operator()(Warning*);
58
+ Statement* operator()(Error*);
59
+ Statement* operator()(Debug*);
60
+ Statement* operator()(Comment*);
61
+ Statement* operator()(If*);
62
+ Statement* operator()(For*);
63
+ Statement* operator()(Each*);
64
+ Statement* operator()(While*);
65
+ Statement* operator()(Return*);
66
+ Statement* operator()(Extension*);
67
+ Statement* operator()(Definition*);
68
+ Statement* operator()(Mixin_Call*);
69
+ Statement* operator()(Content*);
70
+
71
+ template <typename U>
72
+ Statement* fallback(U x) { return fallback_impl(x); }
73
+
74
+ void append_block(Block*);
75
+ };
76
+
77
+ }
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+ # .. more stuff
3
+ #$LIBPATH.push(Config::CONFIG['libdir'])
4
+ $CFLAGS << " #{ENV["CFLAGS"]}"
5
+ $LIBS << " #{ENV["LIBS"]}"
6
+ create_makefile("libsass")
@@ -0,0 +1,1962 @@
1
+ #include "extend.hpp"
2
+ #include "context.hpp"
3
+ #include "contextualize.hpp"
4
+ #include "to_string.hpp"
5
+ #include "backtrace.hpp"
6
+ #include "paths.hpp"
7
+ #include "parser.hpp"
8
+ #ifndef SASS_AST
9
+ #include "node.hpp"
10
+ #endif
11
+ #include "sass_util.hpp"
12
+ #include "debug.hpp"
13
+ #include <iostream>
14
+ #include <deque>
15
+
16
+ /*
17
+ NOTES:
18
+
19
+ - The print* functions print to cerr. This allows our testing frameworks (like sass-spec) to ignore the output, which
20
+ is very helpful when debugging. The format of the output is mainly to wrap things in square brackets to match what
21
+ ruby already outputs (to make comparisons easier).
22
+
23
+ - For the direct porting effort, we're trying to port method-for-method until we get all the tests passing.
24
+ Where applicable, I've tried to include the ruby code above the function for reference until all our tests pass.
25
+ The ruby code isn't always directly portable, so I've tried to include any modified ruby code that was actually
26
+ used for the porting.
27
+
28
+ - DO NOT try to optimize yet. We get a tremendous benefit out of comparing the output of each stage of the extend to the ruby
29
+ output at the same stage. This makes it much easier to determine where problems are. Try to keep as close to
30
+ the ruby code as you can until we have all the sass-spec tests passing. Then, we should optimize. However, if you see
31
+ something that could probably be optimized, let's not forget it. Add a // TODO: or // IMPROVEMENT: comment.
32
+
33
+ - Coding conventions in this file (these may need to be changed before merging back into master)
34
+ - Very basic hungarian notation:
35
+ p prefix for pointers (pSelector)
36
+ no prefix for value types and references (selector)
37
+ - Use STL iterators where possible
38
+ - prefer verbose naming over terse naming
39
+ - use typedefs for STL container types for make maintenance easier
40
+
41
+ - You may see a lot of comments that say "// TODO: is this the correct combinator?". See the comment referring to combinators
42
+ in extendCompoundSelector for a more extensive explanation of my confusion. I think our divergence in data model from ruby
43
+ sass causes this to be necessary.
44
+
45
+
46
+ GLOBAL TODOS:
47
+
48
+ - wrap the contents of the print functions in DEBUG preprocesser conditionals so they will be optimized away in non-debug mode.
49
+
50
+ - consider making the extend* functions member functions to avoid passing around ctx and subsetMap map around. This has the
51
+ drawback that the implementation details of the operator are then exposed to the outside world, which is not ideal and
52
+ can cause additional compile time dependencies.
53
+
54
+ - mark the helper methods in this file static to given them compilation unit linkage.
55
+
56
+ - implement parent directive matching
57
+
58
+ - fix compilation warnings for unused Extend members if we really don't need those references anymore.
59
+ */
60
+
61
+
62
+ namespace Sass {
63
+
64
+
65
+ typedef pair<Complex_Selector*, Compound_Selector*> ExtensionPair;
66
+ typedef vector<ExtensionPair> SubsetMapEntries;
67
+
68
+
69
+
70
+ #ifdef DEBUG
71
+
72
+ // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp
73
+ ostream& operator<<(ostream& os, const Complex_Selector::Combinator combinator) {
74
+ switch (combinator) {
75
+ case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;
76
+ case Complex_Selector::PARENT_OF: os << "\">\""; break;
77
+ case Complex_Selector::PRECEDES: os << "\"~\""; break;
78
+ case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;
79
+ }
80
+
81
+ return os;
82
+ }
83
+
84
+
85
+ ostream& operator<<(ostream& os, Compound_Selector& compoundSelector) {
86
+ To_String to_string;
87
+ os << compoundSelector.perform(&to_string);
88
+ return os;
89
+ }
90
+
91
+
92
+ // Print a string representation of a Compound_Selector
93
+ static void printCompoundSelector(Compound_Selector* pCompoundSelector, const char* message=NULL, bool newline=true) {
94
+ To_String to_string;
95
+
96
+ if (message) {
97
+ cerr << message;
98
+ }
99
+
100
+ if (pCompoundSelector) {
101
+ cerr << *pCompoundSelector;
102
+ } else {
103
+ cerr << "NULL";
104
+ }
105
+
106
+ if (newline) {
107
+ cerr << endl;
108
+ }
109
+ }
110
+
111
+
112
+ ostream& operator<<(ostream& os, Complex_Selector& complexSelector) {
113
+ To_String to_string;
114
+
115
+ os << "[";
116
+ Complex_Selector* pIter = &complexSelector;
117
+ bool first = true;
118
+ while (pIter) {
119
+ if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) {
120
+ if (!first) {
121
+ os << ", ";
122
+ }
123
+ first = false;
124
+ os << pIter->combinator();
125
+ }
126
+
127
+ if (!first) {
128
+ os << ", ";
129
+ }
130
+ first = false;
131
+
132
+ if (pIter->head()) {
133
+ os << pIter->head()->perform(&to_string);
134
+ } else {
135
+ os << "NULL_HEAD";
136
+ }
137
+
138
+ pIter = pIter->tail();
139
+ }
140
+ os << "]";
141
+
142
+ return os;
143
+ }
144
+
145
+
146
+ // Print a string representation of a Complex_Selector
147
+ static void printComplexSelector(Complex_Selector* pComplexSelector, const char* message=NULL, bool newline=true) {
148
+ To_String to_string;
149
+
150
+ if (message) {
151
+ cerr << message;
152
+ }
153
+
154
+ if (pComplexSelector) {
155
+ cerr << *pComplexSelector;
156
+ } else {
157
+ cerr << "NULL";
158
+ }
159
+
160
+ if (newline) {
161
+ cerr << endl;
162
+ }
163
+ }
164
+
165
+
166
+ // Print a string representation of a SourcesSet
167
+ static void printSourcesSet(SourcesSet& sources, Context& ctx, const char* message=NULL, bool newline=true) {
168
+ To_String to_string;
169
+
170
+ if (message) {
171
+ cerr << message;
172
+ }
173
+
174
+ // Convert to a deque of strings so we can sort since order doesn't matter in a set. This should cut down on
175
+ // the differences we see when debug printing.
176
+ typedef deque<string> SourceStrings;
177
+ SourceStrings sourceStrings;
178
+ for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {
179
+ Complex_Selector* pSource = *iterator;
180
+ stringstream sstream;
181
+ sstream << complexSelectorToNode(pSource, ctx);
182
+ sourceStrings.push_back(sstream.str());
183
+ }
184
+
185
+ // Sort to get consistent output
186
+ std::sort(sourceStrings.begin(), sourceStrings.end());
187
+
188
+ cerr << "SourcesSet[";
189
+ for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) {
190
+ string source = *iterator;
191
+ if (iterator != sourceStrings.begin()) {
192
+ cerr << ", ";
193
+ }
194
+ cerr << source;
195
+ }
196
+ cerr << "]";
197
+
198
+ if (newline) {
199
+ cerr << endl;
200
+ }
201
+ }
202
+
203
+
204
+ ostream& operator<<(ostream& os, SubsetMapEntries& entries) {
205
+ os << "SUBSET_MAP_ENTRIES[";
206
+
207
+ for (SubsetMapEntries::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {
208
+ Complex_Selector* pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge)
209
+ Compound_Selector* pExtCompoundSelector = iterator->second; // The stuff after the @extend
210
+
211
+ if (iterator != entries.begin()) {
212
+ os << ", ";
213
+ }
214
+
215
+ os << "(";
216
+
217
+ if (pExtComplexSelector) {
218
+ cerr << *pExtComplexSelector;
219
+ } else {
220
+ cerr << "NULL";
221
+ }
222
+
223
+ os << " -> ";
224
+
225
+ if (pExtCompoundSelector) {
226
+ cerr << *pExtCompoundSelector;
227
+ } else {
228
+ cerr << "NULL";
229
+ }
230
+
231
+ os << ")";
232
+
233
+ }
234
+
235
+ os << "]";
236
+
237
+ return os;
238
+ }
239
+ #endif
240
+ static bool parentSuperselector(Complex_Selector* pOne, Complex_Selector* pTwo, Context& ctx) {
241
+ // TODO: figure out a better way to create a Complex_Selector from scratch
242
+ // TODO: There's got to be a better way. This got ugly quick...
243
+ Position noPosition;
244
+ Type_Selector fakeParent("", noPosition, "temp");
245
+ Compound_Selector fakeHead("", noPosition, 1 /*size*/);
246
+ fakeHead.elements().push_back(&fakeParent);
247
+ Complex_Selector fakeParentContainer("", noPosition, Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);
248
+
249
+ pOne->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);
250
+ pTwo->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);
251
+
252
+ bool isSuperselector = pOne->is_superselector_of(pTwo);
253
+
254
+ pOne->clear_innermost();
255
+ pTwo->clear_innermost();
256
+
257
+ return isSuperselector;
258
+ }
259
+
260
+ void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out, Context& ctx) {
261
+ for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {
262
+ Node& child = *iter;
263
+ out.push_back(nodeToComplexSelector(child, ctx));
264
+ }
265
+ }
266
+
267
+ Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque, Context& ctx) {
268
+ Node result = Node::createCollection();
269
+
270
+ for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) {
271
+ Complex_Selector* pChild = *iter;
272
+ result.collection()->push_back(complexSelectorToNode(pChild, ctx));
273
+ }
274
+
275
+ return result;
276
+ }
277
+
278
+ class LcsCollectionComparator {
279
+ public:
280
+ LcsCollectionComparator(Context& ctx) : mCtx(ctx) {}
281
+
282
+ Context& mCtx;
283
+
284
+ bool operator()(Complex_Selector* pOne, Complex_Selector* pTwo, Complex_Selector*& pOut) const {
285
+ /*
286
+ This code is based on the following block from ruby sass' subweave
287
+ do |s1, s2|
288
+ next s1 if s1 == s2
289
+ next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
290
+ next s2 if parent_superselector?(s1, s2)
291
+ next s1 if parent_superselector?(s2, s1)
292
+ end
293
+ */
294
+
295
+ if (selectors_equal(*pOne, *pTwo, true /*simpleSelectorOrderDependent*/)) {
296
+ pOut = pOne;
297
+ return true;
298
+ }
299
+
300
+ if (pOne->combinator() != Complex_Selector::ANCESTOR_OF || pTwo->combinator() != Complex_Selector::ANCESTOR_OF) {
301
+ return false;
302
+ }
303
+
304
+ if (parentSuperselector(pOne, pTwo, mCtx)) {
305
+ pOut = pTwo;
306
+ return true;
307
+ }
308
+
309
+ if (parentSuperselector(pTwo, pOne, mCtx)) {
310
+ pOut = pOne;
311
+ return true;
312
+ }
313
+
314
+ return false;
315
+ }
316
+ };
317
+
318
+
319
+ /*
320
+ This is the equivalent of ruby's Sass::Util.lcs_backtrace.
321
+
322
+ # Computes a single longest common subsequence for arrays x and y.
323
+ # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS
324
+ */
325
+ void lcs_backtrace(const LCSTable& c, ComplexSelectorDeque& x, ComplexSelectorDeque& y, int i, int j, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {
326
+ //DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)
327
+ // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output
328
+
329
+ if (i == 0 || j == 0) {
330
+ DEBUG_PRINTLN(LCS, "RETURNING EMPTY")
331
+ return;
332
+ }
333
+
334
+
335
+ Complex_Selector* pCompareOut = NULL;
336
+ if (comparator(x[i], y[j], pCompareOut)) {
337
+ DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")
338
+ lcs_backtrace(c, x, y, i - 1, j - 1, comparator, out);
339
+ out.push_back(pCompareOut);
340
+ return;
341
+ }
342
+
343
+ if (c[i][j - 1] > c[i - 1][j]) {
344
+ DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")
345
+ lcs_backtrace(c, x, y, i, j - 1, comparator, out);
346
+ return;
347
+ }
348
+
349
+ DEBUG_PRINTLN(LCS, "FINAL RETURN")
350
+ lcs_backtrace(c, x, y, i - 1, j, comparator, out);
351
+ return;
352
+ }
353
+
354
+ /*
355
+ This is the equivalent of ruby's Sass::Util.lcs_table.
356
+
357
+ # Calculates the memoization table for the Least Common Subsequence algorithm.
358
+ # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS
359
+ */
360
+ void lcs_table(const ComplexSelectorDeque& x, const ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, LCSTable& out) {
361
+ //DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)
362
+ // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output
363
+
364
+ LCSTable c(x.size(), vector<int>(y.size()));
365
+
366
+ // These shouldn't be necessary since the vector will be initialized to 0 already.
367
+ // x.size.times {|i| c[i][0] = 0}
368
+ // y.size.times {|j| c[0][j] = 0}
369
+
370
+ for (size_t i = 1; i < x.size(); i++) {
371
+ for (size_t j = 1; j < y.size(); j++) {
372
+ Complex_Selector* pCompareOut = NULL;
373
+
374
+ if (comparator(x[i], y[j], pCompareOut)) {
375
+ c[i][j] = c[i - 1][j - 1] + 1;
376
+ } else {
377
+ c[i][j] = max(c[i][j - 1], c[i - 1][j]);
378
+ }
379
+ }
380
+ }
381
+
382
+ out = c;
383
+ }
384
+
385
+ /*
386
+ This is the equivalent of ruby's Sass::Util.lcs.
387
+
388
+ # Computes a single longest common subsequence for `x` and `y`.
389
+ # If there are more than one longest common subsequences,
390
+ # the one returned is that which starts first in `x`.
391
+
392
+ # @param x [NodeCollection]
393
+ # @param y [NodeCollection]
394
+ # @comparator An equality check between elements of `x` and `y`.
395
+ # @return [NodeCollection] The LCS
396
+
397
+ http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
398
+ */
399
+ void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, Context& ctx, ComplexSelectorDeque& out) {
400
+ //DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)
401
+ // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output
402
+
403
+ x.push_front(NULL);
404
+ y.push_front(NULL);
405
+
406
+ LCSTable table;
407
+ lcs_table(x, y, comparator, table);
408
+
409
+ return lcs_backtrace(table, x, y, static_cast<int>(x.size()) - 1, static_cast<int>(y.size()) - 1, comparator, out);
410
+ }
411
+
412
+
413
+ /*
414
+ This is the equivalent of ruby's Sequence.trim.
415
+
416
+ The following is the modified version of the ruby code that was more portable to C++. You
417
+ should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.
418
+
419
+ # Avoid truly horrific quadratic behavior. TODO: I think there
420
+ # may be a way to get perfect trimming without going quadratic.
421
+ return seqses if seqses.size > 100
422
+
423
+ # Keep the results in a separate array so we can be sure we aren't
424
+ # comparing against an already-trimmed selector. This ensures that two
425
+ # identical selectors don't mutually trim one another.
426
+ result = seqses.dup
427
+
428
+ # This is n^2 on the sequences, but only comparing between
429
+ # separate sequences should limit the quadratic behavior.
430
+ seqses.each_with_index do |seqs1, i|
431
+ tempResult = []
432
+
433
+ for seq1 in seqs1 do
434
+ max_spec = 0
435
+ for seq in _sources(seq1) do
436
+ max_spec = [max_spec, seq.specificity].max
437
+ end
438
+
439
+
440
+ isMoreSpecificOuter = false
441
+ for seqs2 in result do
442
+ if seqs1.equal?(seqs2) then
443
+ next
444
+ end
445
+
446
+ # Second Law of Extend: the specificity of a generated selector
447
+ # should never be less than the specificity of the extending
448
+ # selector.
449
+ #
450
+ # See https://github.com/nex3/sass/issues/324.
451
+ isMoreSpecificInner = false
452
+ for seq2 in seqs2 do
453
+ isMoreSpecificInner = _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)
454
+ if isMoreSpecificInner then
455
+ break
456
+ end
457
+ end
458
+
459
+ if isMoreSpecificInner then
460
+ isMoreSpecificOuter = true
461
+ break
462
+ end
463
+ end
464
+
465
+ if !isMoreSpecificOuter then
466
+ tempResult.push(seq1)
467
+ end
468
+ end
469
+
470
+ result[i] = tempResult
471
+
472
+ end
473
+
474
+ result
475
+ */
476
+ /*
477
+ - IMPROVEMENT: We could probably work directly in the output trimmed deque.
478
+ */
479
+ static Node trim(Node& seqses, Context& ctx) {
480
+ // See the comments in the above ruby code before embarking on understanding this function.
481
+
482
+ // Avoid poor performance in extreme cases.
483
+ if (seqses.collection()->size() > 100) {
484
+ return seqses;
485
+ }
486
+
487
+
488
+ DEBUG_PRINTLN(TRIM, "TRIM: " << seqses)
489
+
490
+
491
+ Node result = Node::createCollection();
492
+ result.plus(seqses);
493
+
494
+ DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result)
495
+
496
+ // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're
497
+ // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track
498
+ // of the index manually.
499
+ int toTrimIndex = 0;
500
+
501
+ for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) {
502
+ Node& seqs1 = *seqsesIter;
503
+
504
+ DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex)
505
+
506
+ Node tempResult = Node::createCollection();
507
+
508
+ for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) {
509
+ Node& seq1 = *seqs1Iter;
510
+
511
+ Complex_Selector* pSeq1 = nodeToComplexSelector(seq1, ctx);
512
+
513
+ // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code
514
+ // for a good description of sources.
515
+ //
516
+ // 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.
517
+ // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We
518
+ // 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
519
+ // 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
520
+ // a guess though.
521
+ int maxSpecificity = 0;
522
+ SourcesSet sources = pSeq1->sources();
523
+
524
+ DEBUG_PRINTLN(TRIM, "TRIMASDF SEQ1: " << seq1)
525
+ DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIMASDF SOURCES: "))
526
+
527
+ for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {
528
+ const Complex_Selector* const pCurrentSelector = *sourcesSetIterator;
529
+ maxSpecificity = max(maxSpecificity, pCurrentSelector->specificity());
530
+ }
531
+
532
+ DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity)
533
+
534
+ bool isMoreSpecificOuter = false;
535
+
536
+ int resultIndex = 0;
537
+
538
+ for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) {
539
+ Node& seqs2 = *resultIter;
540
+
541
+ DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1)
542
+ DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2)
543
+
544
+ // Do not compare the same sequence to itself. The ruby call we're trying to
545
+ // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision.
546
+ // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is
547
+ // derived from seqses and seqs2 is derived from result.
548
+ if (seqs1.collection() == seqs2.collection()) {
549
+ DEBUG_PRINTLN(TRIM, "CONTINUE")
550
+ continue;
551
+ }
552
+
553
+ bool isMoreSpecificInner = false;
554
+
555
+ for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) {
556
+ Node& seq2 = *seqs2Iter;
557
+
558
+ Complex_Selector* pSeq2 = nodeToComplexSelector(seq2, ctx);
559
+
560
+ DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity())
561
+ DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))
562
+ DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false"))
563
+
564
+ isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1);
565
+
566
+ if (isMoreSpecificInner) {
567
+ DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC")
568
+ break;
569
+ }
570
+ }
571
+
572
+ // If we found something more specific, we're done. Let the outer loop know and stop iterating.
573
+ if (isMoreSpecificInner) {
574
+ isMoreSpecificOuter = true;
575
+ break;
576
+ }
577
+
578
+ resultIndex++;
579
+ }
580
+
581
+ if (!isMoreSpecificOuter) {
582
+ DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1)
583
+ tempResult.collection()->push_back(seq1);
584
+ }
585
+
586
+ }
587
+
588
+ DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result)
589
+ DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult)
590
+ (*result.collection())[toTrimIndex] = tempResult;
591
+
592
+ toTrimIndex++;
593
+
594
+ DEBUG_PRINTLN(TRIM, "RESULT: " << result)
595
+ }
596
+
597
+ return result;
598
+ }
599
+
600
+
601
+
602
+ static bool parentSuperselector(const Node& one, const Node& two, Context& ctx) {
603
+ // TODO: figure out a better way to create a Complex_Selector from scratch
604
+ // TODO: There's got to be a better way. This got ugly quick...
605
+ Position noPosition;
606
+ Type_Selector fakeParent("", noPosition, "temp");
607
+ Compound_Selector fakeHead("", noPosition, 1 /*size*/);
608
+ fakeHead.elements().push_back(&fakeParent);
609
+ Complex_Selector fakeParentContainer("", noPosition, Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);
610
+
611
+ Complex_Selector* pOneWithFakeParent = nodeToComplexSelector(one, ctx);
612
+ pOneWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);
613
+ Complex_Selector* pTwoWithFakeParent = nodeToComplexSelector(two, ctx);
614
+ pTwoWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);
615
+
616
+ return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent);
617
+ }
618
+
619
+
620
+ class ParentSuperselectorChunker {
621
+ public:
622
+ ParentSuperselectorChunker(Node& lcs, Context& ctx) : mLcs(lcs), mCtx(ctx) {}
623
+ Node& mLcs;
624
+ Context& mCtx;
625
+
626
+ bool operator()(const Node& seq) const {
627
+ // {|s| parent_superselector?(s.first, lcs.first)}
628
+ return parentSuperselector(seq.collection()->front(), mLcs.collection()->front(), mCtx);
629
+ }
630
+ };
631
+
632
+ class SubweaveEmptyChunker {
633
+ public:
634
+ bool operator()(const Node& seq) const {
635
+ // {|s| s.empty?}
636
+
637
+ return seq.collection()->empty();
638
+ }
639
+ };
640
+
641
+ /*
642
+ # Takes initial subsequences of `seq1` and `seq2` and returns all
643
+ # orderings of those subsequences. The initial subsequences are determined
644
+ # by a block.
645
+ #
646
+ # Destructively removes the initial subsequences of `seq1` and `seq2`.
647
+ #
648
+ # For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`
649
+ # denoting the boundary of the initial subsequence), this would return
650
+ # `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and
651
+ # `(3 4 5)`.
652
+ #
653
+ # @param seq1 [Array]
654
+ # @param seq2 [Array]
655
+ # @yield [a] Used to determine when to cut off the initial subsequences.
656
+ # Called repeatedly for each sequence until it returns true.
657
+ # @yieldparam a [Array] A final subsequence of one input sequence after
658
+ # cutting off some initial subsequence.
659
+ # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence
660
+ # here.
661
+ # @return [Array<Array>] All possible orderings of the initial subsequences.
662
+ def chunks(seq1, seq2)
663
+ chunk1 = []
664
+ chunk1 << seq1.shift until yield seq1
665
+ chunk2 = []
666
+ chunk2 << seq2.shift until yield seq2
667
+ return [] if chunk1.empty? && chunk2.empty?
668
+ return [chunk2] if chunk1.empty?
669
+ return [chunk1] if chunk2.empty?
670
+ [chunk1 + chunk2, chunk2 + chunk1]
671
+ end
672
+ */
673
+ template<typename ChunkerType>
674
+ static Node chunks(Node& seq1, Node& seq2, const ChunkerType& chunker) {
675
+ Node chunk1 = Node::createCollection();
676
+ while (!chunker(seq1)) {
677
+ chunk1.collection()->push_back(seq1.collection()->front());
678
+ seq1.collection()->pop_front();
679
+ }
680
+
681
+ Node chunk2 = Node::createCollection();
682
+ while (!chunker(seq2)) {
683
+ chunk2.collection()->push_back(seq2.collection()->front());
684
+ seq2.collection()->pop_front();
685
+ }
686
+
687
+ if (chunk1.collection()->empty() && chunk2.collection()->empty()) {
688
+ DEBUG_PRINTLN(CHUNKS, "RETURNING BOTH EMPTY")
689
+ return Node::createCollection();
690
+ }
691
+
692
+ if (chunk1.collection()->empty()) {
693
+ Node chunk2Wrapper = Node::createCollection();
694
+ chunk2Wrapper.collection()->push_back(chunk2);
695
+ DEBUG_PRINTLN(CHUNKS, "RETURNING ONE EMPTY")
696
+ return chunk2Wrapper;
697
+ }
698
+
699
+ if (chunk2.collection()->empty()) {
700
+ Node chunk1Wrapper = Node::createCollection();
701
+ chunk1Wrapper.collection()->push_back(chunk1);
702
+ DEBUG_PRINTLN(CHUNKS, "RETURNING TWO EMPTY")
703
+ return chunk1Wrapper;
704
+ }
705
+
706
+ Node perms = Node::createCollection();
707
+
708
+ Node firstPermutation = Node::createCollection();
709
+ firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());
710
+ firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());
711
+ perms.collection()->push_back(firstPermutation);
712
+
713
+ Node secondPermutation = Node::createCollection();
714
+ secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());
715
+ secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());
716
+ perms.collection()->push_back(secondPermutation);
717
+
718
+ DEBUG_PRINTLN(CHUNKS, "RETURNING PERM")
719
+
720
+ return perms;
721
+ }
722
+
723
+
724
+ static Node groupSelectors(Node& seq, Context& ctx) {
725
+ Node newSeq = Node::createCollection();
726
+
727
+ Node tail = Node::createCollection();
728
+ tail.plus(seq);
729
+
730
+ while (!tail.collection()->empty()) {
731
+ Node head = Node::createCollection();
732
+
733
+ do {
734
+ head.collection()->push_back(tail.collection()->front());
735
+ tail.collection()->pop_front();
736
+ } while (!tail.collection()->empty() && (head.collection()->back().isCombinator() || tail.collection()->front().isCombinator()));
737
+
738
+ newSeq.collection()->push_back(head);
739
+ }
740
+
741
+ return newSeq;
742
+ }
743
+
744
+
745
+ static void getAndRemoveInitialOps(Node& seq, Node& ops) {
746
+ NodeDeque& seqCollection = *(seq.collection());
747
+ NodeDeque& opsCollection = *(ops.collection());
748
+
749
+ while (seqCollection.size() > 0 && seqCollection.front().isCombinator()) {
750
+ opsCollection.push_back(seqCollection.front());
751
+ seqCollection.pop_front();
752
+ }
753
+ }
754
+
755
+
756
+ static void getAndRemoveFinalOps(Node& seq, Node& ops) {
757
+ NodeDeque& seqCollection = *(seq.collection());
758
+ NodeDeque& opsCollection = *(ops.collection());
759
+
760
+ while (seqCollection.size() > 0 && seqCollection.back().isCombinator()) {
761
+ opsCollection.push_back(seqCollection.back()); // Purposefully reversed to match ruby code
762
+ seqCollection.pop_back();
763
+ }
764
+ }
765
+
766
+
767
+ /*
768
+ def merge_initial_ops(seq1, seq2)
769
+ ops1, ops2 = [], []
770
+ ops1 << seq1.shift while seq1.first.is_a?(String)
771
+ ops2 << seq2.shift while seq2.first.is_a?(String)
772
+
773
+ newline = false
774
+ newline ||= !!ops1.shift if ops1.first == "\n"
775
+ newline ||= !!ops2.shift if ops2.first == "\n"
776
+
777
+ # If neither sequence is a subsequence of the other, they cannot be
778
+ # merged successfully
779
+ lcs = Sass::Util.lcs(ops1, ops2)
780
+ return unless lcs == ops1 || lcs == ops2
781
+ return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
782
+ end
783
+ */
784
+ static Node mergeInitialOps(Node& seq1, Node& seq2, Context& ctx) {
785
+ Node ops1 = Node::createCollection();
786
+ Node ops2 = Node::createCollection();
787
+
788
+ getAndRemoveInitialOps(seq1, ops1);
789
+ getAndRemoveInitialOps(seq2, ops2);
790
+
791
+ // TODO: Do we have this information available to us?
792
+ // newline = false
793
+ // newline ||= !!ops1.shift if ops1.first == "\n"
794
+ // newline ||= !!ops2.shift if ops2.first == "\n"
795
+
796
+ // If neither sequence is a subsequence of the other, they cannot be merged successfully
797
+ DefaultLcsComparator lcsDefaultComparator;
798
+ Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx);
799
+
800
+ if (!(opsLcs == ops1 || opsLcs == ops2)) {
801
+ return Node::createNil();
802
+ }
803
+
804
+ // TODO: more newline logic
805
+ // return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
806
+
807
+ return (ops1.collection()->size() > ops2.collection()->size() ? ops1 : ops2);
808
+ }
809
+
810
+
811
+ /*
812
+ def merge_final_ops(seq1, seq2, res = [])
813
+
814
+
815
+ # This code looks complicated, but it's actually just a bunch of special
816
+ # cases for interactions between different combinators.
817
+ op1, op2 = ops1.first, ops2.first
818
+ if op1 && op2
819
+ sel1 = seq1.pop
820
+ sel2 = seq2.pop
821
+ if op1 == '~' && op2 == '~'
822
+ if sel1.superselector?(sel2)
823
+ res.unshift sel2, '~'
824
+ elsif sel2.superselector?(sel1)
825
+ res.unshift sel1, '~'
826
+ else
827
+ merged = sel1.unify(sel2.members, sel2.subject?)
828
+ res.unshift [
829
+ [sel1, '~', sel2, '~'],
830
+ [sel2, '~', sel1, '~'],
831
+ ([merged, '~'] if merged)
832
+ ].compact
833
+ end
834
+ elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~')
835
+ if op1 == '~'
836
+ tilde_sel, plus_sel = sel1, sel2
837
+ else
838
+ tilde_sel, plus_sel = sel2, sel1
839
+ end
840
+
841
+ if tilde_sel.superselector?(plus_sel)
842
+ res.unshift plus_sel, '+'
843
+ else
844
+ merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
845
+ res.unshift [
846
+ [tilde_sel, '~', plus_sel, '+'],
847
+ ([merged, '+'] if merged)
848
+ ].compact
849
+ end
850
+ elsif op1 == '>' && %w[~ +].include?(op2)
851
+ res.unshift sel2, op2
852
+ seq1.push sel1, op1
853
+ elsif op2 == '>' && %w[~ +].include?(op1)
854
+ res.unshift sel1, op1
855
+ seq2.push sel2, op2
856
+ elsif op1 == op2
857
+ return unless merged = sel1.unify(sel2.members, sel2.subject?)
858
+ res.unshift merged, op1
859
+ else
860
+ # Unknown selector combinators can't be unified
861
+ return
862
+ end
863
+ return merge_final_ops(seq1, seq2, res)
864
+ elsif op1
865
+ seq2.pop if op1 == '>' && seq2.last && seq2.last.superselector?(seq1.last)
866
+ res.unshift seq1.pop, op1
867
+ return merge_final_ops(seq1, seq2, res)
868
+ else # op2
869
+ seq1.pop if op2 == '>' && seq1.last && seq1.last.superselector?(seq2.last)
870
+ res.unshift seq2.pop, op2
871
+ return merge_final_ops(seq1, seq2, res)
872
+ end
873
+ end
874
+ */
875
+ static Node mergeFinalOps(Node& seq1, Node& seq2, Context& ctx, Node& res) {
876
+
877
+ Node ops1 = Node::createCollection();
878
+ Node ops2 = Node::createCollection();
879
+
880
+ getAndRemoveFinalOps(seq1, ops1);
881
+ getAndRemoveFinalOps(seq2, ops2);
882
+
883
+ // TODO: do we have newlines to remove?
884
+ // ops1.reject! {|o| o == "\n"}
885
+ // ops2.reject! {|o| o == "\n"}
886
+
887
+ if (ops1.collection()->empty() && ops2.collection()->empty()) {
888
+ return res;
889
+ }
890
+
891
+ if (ops1.collection()->size() > 1 || ops2.collection()->size() > 1) {
892
+ DefaultLcsComparator lcsDefaultComparator;
893
+ Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx);
894
+
895
+ // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up.
896
+
897
+ if (!(opsLcs == ops1 || opsLcs == ops2)) {
898
+ return Node::createNil();
899
+ }
900
+
901
+ if (ops1.collection()->size() > ops2.collection()->size()) {
902
+ res.collection()->insert(res.collection()->begin(), ops1.collection()->rbegin(), ops1.collection()->rend());
903
+ } else {
904
+ res.collection()->insert(res.collection()->begin(), ops2.collection()->rbegin(), ops2.collection()->rend());
905
+ }
906
+
907
+ return res;
908
+ }
909
+
910
+ if (!ops1.collection()->empty() && !ops2.collection()->empty()) {
911
+
912
+ Node op1 = ops1.collection()->front();
913
+ Node op2 = ops2.collection()->front();
914
+
915
+ Node sel1 = seq1.collection()->back();
916
+ seq1.collection()->pop_back();
917
+
918
+ Node sel2 = seq2.collection()->back();
919
+ seq2.collection()->pop_back();
920
+
921
+ if (op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::PRECEDES) {
922
+
923
+ if (sel1.selector()->is_superselector_of(sel2.selector())) {
924
+
925
+ res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);
926
+ res.collection()->push_front(sel2);
927
+
928
+ } else if (sel2.selector()->is_superselector_of(sel1.selector())) {
929
+
930
+ res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);
931
+ res.collection()->push_front(sel1);
932
+
933
+ } else {
934
+
935
+ DEBUG_PRINTLN(ALL, "sel1: " << sel1)
936
+ DEBUG_PRINTLN(ALL, "sel2: " << sel2)
937
+
938
+ Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result
939
+ // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)
940
+ Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);
941
+ pMergedWrapper->head(pMerged);
942
+
943
+ DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))
944
+
945
+ Node newRes = Node::createCollection();
946
+
947
+ Node firstPerm = Node::createCollection();
948
+ firstPerm.collection()->push_back(sel1);
949
+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
950
+ firstPerm.collection()->push_back(sel2);
951
+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
952
+ newRes.collection()->push_back(firstPerm);
953
+
954
+ Node secondPerm = Node::createCollection();
955
+ secondPerm.collection()->push_back(sel2);
956
+ secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
957
+ secondPerm.collection()->push_back(sel1);
958
+ secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
959
+ newRes.collection()->push_back(secondPerm);
960
+
961
+ if (pMerged) {
962
+ Node mergedPerm = Node::createCollection();
963
+ mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));
964
+ mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
965
+ newRes.collection()->push_back(mergedPerm);
966
+ }
967
+
968
+ res.collection()->push_front(newRes);
969
+
970
+ DEBUG_PRINTLN(ALL, "RESULT: " << res)
971
+
972
+ }
973
+
974
+ } else if (((op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::ADJACENT_TO)) || ((op1.combinator() == Complex_Selector::ADJACENT_TO && op2.combinator() == Complex_Selector::PRECEDES))) {
975
+
976
+ Node tildeSel = sel1;
977
+ Node tildeOp = op1;
978
+ Node plusSel = sel2;
979
+ Node plusOp = op2;
980
+ if (op1.combinator() != Complex_Selector::PRECEDES) {
981
+ tildeSel = sel2;
982
+ tildeOp = op2;
983
+ plusSel = sel1;
984
+ plusOp = op1;
985
+ }
986
+
987
+ if (tildeSel.selector()->is_superselector_of(plusSel.selector())) {
988
+
989
+ res.collection()->push_front(plusOp);
990
+ res.collection()->push_front(plusSel);
991
+
992
+ } else {
993
+
994
+ DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel)
995
+ DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel)
996
+
997
+ Complex_Selector* pMergedWrapper = plusSel.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result
998
+ // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
999
+ Compound_Selector* pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head(), ctx);
1000
+ pMergedWrapper->head(pMerged);
1001
+
1002
+ DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))
1003
+
1004
+ Node newRes = Node::createCollection();
1005
+
1006
+ Node firstPerm = Node::createCollection();
1007
+ firstPerm.collection()->push_back(tildeSel);
1008
+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
1009
+ firstPerm.collection()->push_back(plusSel);
1010
+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));
1011
+ newRes.collection()->push_back(firstPerm);
1012
+
1013
+ if (pMerged) {
1014
+ Node mergedPerm = Node::createCollection();
1015
+ mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));
1016
+ mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));
1017
+ newRes.collection()->push_back(mergedPerm);
1018
+ }
1019
+
1020
+ res.collection()->push_front(newRes);
1021
+
1022
+ DEBUG_PRINTLN(ALL, "RESULT: " << res)
1023
+
1024
+ }
1025
+ } else if (op1.combinator() == Complex_Selector::PARENT_OF && (op2.combinator() == Complex_Selector::PRECEDES || op2.combinator() == Complex_Selector::ADJACENT_TO)) {
1026
+
1027
+ res.collection()->push_front(op2);
1028
+ res.collection()->push_front(sel2);
1029
+
1030
+ seq2.collection()->push_back(sel1);
1031
+ seq2.collection()->push_back(op1);
1032
+
1033
+ } else if (op2.combinator() == Complex_Selector::PARENT_OF && (op1.combinator() == Complex_Selector::PRECEDES || op1.combinator() == Complex_Selector::ADJACENT_TO)) {
1034
+
1035
+ res.collection()->push_front(op1);
1036
+ res.collection()->push_front(sel1);
1037
+
1038
+ seq2.collection()->push_back(sel2);
1039
+ seq2.collection()->push_back(op2);
1040
+
1041
+ } else if (op1.combinator() == op2.combinator()) {
1042
+
1043
+ DEBUG_PRINTLN(ALL, "sel1: " << sel1)
1044
+ DEBUG_PRINTLN(ALL, "sel2: " << sel2)
1045
+
1046
+ Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result
1047
+ // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)
1048
+ Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);
1049
+ pMergedWrapper->head(pMerged);
1050
+
1051
+ DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))
1052
+
1053
+ if (!pMerged) {
1054
+ return Node::createNil();
1055
+ }
1056
+
1057
+ res.collection()->push_front(op1);
1058
+ res.collection()->push_front(Node::createSelector(pMergedWrapper, ctx));
1059
+
1060
+ DEBUG_PRINTLN(ALL, "RESULT: " << res)
1061
+
1062
+ } else {
1063
+ return Node::createNil();
1064
+ }
1065
+
1066
+ return mergeFinalOps(seq1, seq2, ctx, res);
1067
+
1068
+ } else if (!ops1.collection()->empty()) {
1069
+
1070
+ Node op1 = ops1.collection()->front();
1071
+
1072
+ if (op1.combinator() == Complex_Selector::PARENT_OF && !seq2.collection()->empty() && seq2.collection()->back().selector()->is_superselector_of(seq1.collection()->back().selector())) {
1073
+ seq2.collection()->pop_back();
1074
+ }
1075
+
1076
+ // TODO: consider unshift(NodeCollection, Node)
1077
+ res.collection()->push_front(op1);
1078
+ res.collection()->push_front(seq1.collection()->back());
1079
+ seq1.collection()->pop_back();
1080
+
1081
+ return mergeFinalOps(seq1, seq2, ctx, res);
1082
+
1083
+ } else { // !ops2.collection()->empty()
1084
+
1085
+ Node op2 = ops2.collection()->front();
1086
+
1087
+ if (op2.combinator() == Complex_Selector::PARENT_OF && !seq1.collection()->empty() && seq1.collection()->back().selector()->is_superselector_of(seq2.collection()->back().selector())) {
1088
+ seq1.collection()->pop_back();
1089
+ }
1090
+
1091
+ res.collection()->push_front(op2);
1092
+ res.collection()->push_front(seq2.collection()->back());
1093
+ seq2.collection()->pop_back();
1094
+
1095
+ return mergeFinalOps(seq1, seq2, ctx, res);
1096
+
1097
+ }
1098
+
1099
+ }
1100
+
1101
+
1102
+ /*
1103
+ This is the equivalent of ruby's Sequence.subweave.
1104
+
1105
+ Here is the original subweave code for reference during porting.
1106
+
1107
+ def subweave(seq1, seq2)
1108
+ return [seq2] if seq1.empty?
1109
+ return [seq1] if seq2.empty?
1110
+
1111
+ seq1, seq2 = seq1.dup, seq2.dup
1112
+ return unless init = merge_initial_ops(seq1, seq2)
1113
+ return unless fin = merge_final_ops(seq1, seq2)
1114
+ seq1 = group_selectors(seq1)
1115
+ seq2 = group_selectors(seq2)
1116
+ lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
1117
+ next s1 if s1 == s2
1118
+ next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
1119
+ next s2 if parent_superselector?(s1, s2)
1120
+ next s1 if parent_superselector?(s2, s1)
1121
+ end
1122
+
1123
+ diff = [[init]]
1124
+ until lcs.empty?
1125
+ diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]
1126
+ seq1.shift
1127
+ seq2.shift
1128
+ end
1129
+ diff << chunks(seq1, seq2) {|s| s.empty?}
1130
+ diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}
1131
+ diff.reject! {|c| c.empty?}
1132
+
1133
+ result = Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)}
1134
+
1135
+ result
1136
+ end
1137
+ */
1138
+ static Node subweave(Node& one, Node& two, Context& ctx) {
1139
+ // Check for the simple cases
1140
+ if (one.collection()->size() == 0) {
1141
+ Node out = Node::createCollection();
1142
+ out.collection()->push_back(two);
1143
+ return out;
1144
+ }
1145
+ if (two.collection()->size() == 0) {
1146
+ Node out = Node::createCollection();
1147
+ out.collection()->push_back(one);
1148
+ return out;
1149
+ }
1150
+
1151
+
1152
+
1153
+ Node seq1 = Node::createCollection();
1154
+ seq1.plus(one);
1155
+ Node seq2 = Node::createCollection();
1156
+ seq2.plus(two);
1157
+
1158
+ DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1)
1159
+ DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2)
1160
+
1161
+ Node init = mergeInitialOps(seq1, seq2, ctx);
1162
+ if (init.isNil()) {
1163
+ return Node::createNil();
1164
+ }
1165
+
1166
+ DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init)
1167
+
1168
+ Node res = Node::createCollection();
1169
+ Node fin = mergeFinalOps(seq1, seq2, ctx, res);
1170
+ if (fin.isNil()) {
1171
+ return Node::createNil();
1172
+ }
1173
+
1174
+ DEBUG_PRINTLN(SUBWEAVE, "FIN: " << fin)
1175
+
1176
+
1177
+ // Moving this line up since fin isn't modified between now and when it happened before
1178
+ // fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}
1179
+
1180
+ for (NodeDeque::iterator finIter = fin.collection()->begin(), finEndIter = fin.collection()->end();
1181
+ finIter != finEndIter; ++finIter) {
1182
+
1183
+ Node& childNode = *finIter;
1184
+
1185
+ if (!childNode.isCollection()) {
1186
+ Node wrapper = Node::createCollection();
1187
+ wrapper.collection()->push_back(childNode);
1188
+ childNode = wrapper;
1189
+ }
1190
+
1191
+ }
1192
+
1193
+ DEBUG_PRINTLN(SUBWEAVE, "FIN MAPPED: " << fin)
1194
+
1195
+
1196
+
1197
+ Node groupSeq1 = groupSelectors(seq1, ctx);
1198
+ DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1)
1199
+
1200
+ Node groupSeq2 = groupSelectors(seq2, ctx);
1201
+ DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2)
1202
+
1203
+
1204
+ ComplexSelectorDeque groupSeq1Converted;
1205
+ nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted, ctx);
1206
+
1207
+ ComplexSelectorDeque groupSeq2Converted;
1208
+ nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted, ctx);
1209
+
1210
+ ComplexSelectorDeque out;
1211
+ LcsCollectionComparator collectionComparator(ctx);
1212
+ lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, ctx, out);
1213
+ Node seqLcs = complexSelectorDequeToNode(out, ctx);
1214
+
1215
+ DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs)
1216
+
1217
+
1218
+ Node initWrapper = Node::createCollection();
1219
+ initWrapper.collection()->push_back(init);
1220
+ Node diff = Node::createCollection();
1221
+ diff.collection()->push_back(initWrapper);
1222
+
1223
+ DEBUG_PRINTLN(SUBWEAVE, "DIFF INIT: " << diff)
1224
+
1225
+
1226
+ while (!seqLcs.collection()->empty()) {
1227
+ ParentSuperselectorChunker superselectorChunker(seqLcs, ctx);
1228
+ Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker);
1229
+ diff.collection()->push_back(chunksResult);
1230
+
1231
+ Node lcsWrapper = Node::createCollection();
1232
+ lcsWrapper.collection()->push_back(seqLcs.collection()->front());
1233
+ seqLcs.collection()->pop_front();
1234
+ diff.collection()->push_back(lcsWrapper);
1235
+
1236
+ groupSeq1.collection()->pop_front();
1237
+ groupSeq2.collection()->pop_front();
1238
+ }
1239
+
1240
+ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST LCS: " << diff)
1241
+
1242
+
1243
+ DEBUG_PRINTLN(SUBWEAVE, "CHUNKS: ONE=" << groupSeq1 << " TWO=" << groupSeq2)
1244
+
1245
+
1246
+ SubweaveEmptyChunker emptyChunker;
1247
+ Node chunksResult = chunks(groupSeq1, groupSeq2, emptyChunker);
1248
+ diff.collection()->push_back(chunksResult);
1249
+
1250
+
1251
+ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST CHUNKS: " << diff)
1252
+
1253
+
1254
+ diff.collection()->insert(diff.collection()->end(), fin.collection()->begin(), fin.collection()->end());
1255
+
1256
+ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST FIN MAPPED: " << diff)
1257
+
1258
+ // JMA - filter out the empty nodes (use a new collection, since iterator erase() invalidates the old collection)
1259
+ Node diffFiltered = Node::createCollection();
1260
+ for (NodeDeque::iterator diffIter = diff.collection()->begin(), diffEndIter = diff.collection()->end();
1261
+ diffIter != diffEndIter; ++diffIter) {
1262
+ Node& node = *diffIter;
1263
+ if (node.collection() && !node.collection()->empty()) {
1264
+ diffFiltered.collection()->push_back(node);
1265
+ }
1266
+ }
1267
+ diff = diffFiltered;
1268
+
1269
+ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff)
1270
+
1271
+
1272
+ Node pathsResult = paths(diff, ctx);
1273
+
1274
+ DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult)
1275
+
1276
+
1277
+ // We're flattening in place
1278
+ for (NodeDeque::iterator pathsIter = pathsResult.collection()->begin(), pathsEndIter = pathsResult.collection()->end();
1279
+ pathsIter != pathsEndIter; ++pathsIter) {
1280
+
1281
+ Node& child = *pathsIter;
1282
+ child = flatten(child, ctx);
1283
+ }
1284
+
1285
+ DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult)
1286
+
1287
+
1288
+ /*
1289
+ TODO: implement
1290
+ rejected = mapped.reject {|p| path_has_two_subjects?(p)}
1291
+ $stderr.puts "REJECTED: #{rejected}"
1292
+ */
1293
+
1294
+
1295
+ return pathsResult;
1296
+
1297
+ }
1298
+ /*
1299
+ // disabled to avoid clang warning [-Wunused-function]
1300
+ static Node subweaveNaive(const Node& one, const Node& two, Context& ctx) {
1301
+ Node out = Node::createCollection();
1302
+
1303
+ // Check for the simple cases
1304
+ if (one.isNil()) {
1305
+ out.collection()->push_back(two.clone(ctx));
1306
+ } else if (two.isNil()) {
1307
+ out.collection()->push_back(one.clone(ctx));
1308
+ } else {
1309
+ // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B
1310
+ // See https://gist.github.com/nex3/7609394 for details.
1311
+
1312
+ Node firstPerm = one.clone(ctx);
1313
+ Node twoCloned = two.clone(ctx);
1314
+ firstPerm.plus(twoCloned);
1315
+ out.collection()->push_back(firstPerm);
1316
+
1317
+ Node secondPerm = two.clone(ctx);
1318
+ Node oneCloned = one.clone(ctx);
1319
+ secondPerm.plus(oneCloned );
1320
+ out.collection()->push_back(secondPerm);
1321
+ }
1322
+
1323
+ return out;
1324
+ }
1325
+ */
1326
+
1327
+
1328
+ /*
1329
+ This is the equivalent of ruby's Sequence.weave.
1330
+
1331
+ The following is the modified version of the ruby code that was more portable to C++. You
1332
+ should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.
1333
+
1334
+ def weave(path)
1335
+ # This function works by moving through the selector path left-to-right,
1336
+ # building all possible prefixes simultaneously. These prefixes are
1337
+ # `befores`, while the remaining parenthesized suffixes is `afters`.
1338
+ befores = [[]]
1339
+ afters = path.dup
1340
+
1341
+ until afters.empty?
1342
+ current = afters.shift.dup
1343
+ last_current = [current.pop]
1344
+
1345
+ tempResult = []
1346
+
1347
+ for before in befores do
1348
+ sub = subweave(before, current)
1349
+ if sub.nil?
1350
+ next
1351
+ end
1352
+
1353
+ for seqs in sub do
1354
+ tempResult.push(seqs + last_current)
1355
+ end
1356
+ end
1357
+
1358
+ befores = tempResult
1359
+
1360
+ end
1361
+
1362
+ return befores
1363
+ end
1364
+ */
1365
+ /*
1366
+ def weave(path)
1367
+ befores = [[]]
1368
+ afters = path.dup
1369
+
1370
+ until afters.empty?
1371
+ current = afters.shift.dup
1372
+
1373
+ last_current = [current.pop]
1374
+
1375
+
1376
+ tempResult = []
1377
+
1378
+ for before in befores do
1379
+ sub = subweave(before, current)
1380
+
1381
+ if sub.nil?
1382
+ next []
1383
+ end
1384
+
1385
+
1386
+ for seqs in sub do
1387
+ toPush = seqs + last_current
1388
+
1389
+ tempResult.push(seqs + last_current)
1390
+ end
1391
+
1392
+ end
1393
+
1394
+ befores = tempResult
1395
+
1396
+ end
1397
+
1398
+ return befores
1399
+ end
1400
+ */
1401
+ static Node weave(Node& path, Context& ctx) {
1402
+
1403
+ DEBUG_PRINTLN(WEAVE, "WEAVE: " << path)
1404
+
1405
+ Node befores = Node::createCollection();
1406
+ befores.collection()->push_back(Node::createCollection());
1407
+
1408
+ Node afters = Node::createCollection();
1409
+ afters.plus(path);
1410
+
1411
+ while (!afters.collection()->empty()) {
1412
+ Node current = afters.collection()->front().clone(ctx);
1413
+ afters.collection()->pop_front();
1414
+ DEBUG_PRINTLN(WEAVE, "CURRENT: " << current)
1415
+
1416
+ Node last_current = Node::createCollection();
1417
+ last_current.collection()->push_back(current.collection()->back());
1418
+ current.collection()->pop_back();
1419
+ DEBUG_PRINTLN(WEAVE, "CURRENT POST POP: " << current)
1420
+ DEBUG_PRINTLN(WEAVE, "LAST CURRENT: " << last_current)
1421
+
1422
+ Node tempResult = Node::createCollection();
1423
+
1424
+ for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) {
1425
+ Node& before = *beforesIter;
1426
+
1427
+ Node sub = subweave(before, current, ctx);
1428
+
1429
+ DEBUG_PRINTLN(WEAVE, "SUB: " << sub)
1430
+
1431
+ if (sub.isNil()) {
1432
+ return Node::createCollection();
1433
+ }
1434
+
1435
+ for (NodeDeque::iterator subIter = sub.collection()->begin(), subEndIter = sub.collection()->end(); subIter != subEndIter; subIter++) {
1436
+ Node& seqs = *subIter;
1437
+
1438
+ Node toPush = Node::createCollection();
1439
+ toPush.plus(seqs);
1440
+ toPush.plus(last_current);
1441
+
1442
+ tempResult.collection()->push_back(toPush);
1443
+
1444
+ }
1445
+ }
1446
+
1447
+ befores = tempResult;
1448
+
1449
+ }
1450
+
1451
+ return befores;
1452
+ }
1453
+
1454
+
1455
+
1456
+ // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively
1457
+ // call extendComplexSelector again.
1458
+ static Node extendComplexSelector(
1459
+ Complex_Selector* pComplexSelector,
1460
+ Context& ctx,
1461
+ ExtensionSubsetMap& subsetMap,
1462
+ set<Compound_Selector> seen);
1463
+
1464
+
1465
+
1466
+ /*
1467
+ This is the equivalent of ruby's SimpleSequence.do_extend.
1468
+
1469
+ // TODO: I think I have some modified ruby code to put here. Check.
1470
+ */
1471
+ /*
1472
+ ISSUES:
1473
+ - Previous TODO: Do we need to group the results by extender?
1474
+ - What does subject do in?: next unless unified = seq.members.last.unify(self_without_sel, subject?)
1475
+ - IMPROVEMENT: The search for uniqueness at the end is not ideal since it's has to loop over everything...
1476
+ - IMPROVEMENT: Check if the final search for uniqueness is doing anything that extendComplexSelector isn't already doing...
1477
+ */
1478
+ template<typename KeyType>
1479
+ class GroupByToAFunctor {
1480
+ public:
1481
+ KeyType operator()(ExtensionPair& extPair) const {
1482
+ Complex_Selector* pSelector = extPair.first;
1483
+ return *pSelector;
1484
+ }
1485
+ };
1486
+ static Node extendCompoundSelector(
1487
+ Compound_Selector* pSelector,
1488
+ Context& ctx,
1489
+ ExtensionSubsetMap& subsetMap,
1490
+ set<Compound_Selector> seen) {
1491
+
1492
+ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))
1493
+
1494
+ Node extendedSelectors = Node::createCollection();
1495
+
1496
+ To_String to_string;
1497
+
1498
+ SubsetMapEntries entries = subsetMap.get_v(pSelector->to_str_vec());
1499
+
1500
+
1501
+ typedef vector<pair<Complex_Selector, vector<ExtensionPair> > > GroupedByToAResult;
1502
+
1503
+ GroupByToAFunctor<Complex_Selector> extPairKeyFunctor;
1504
+ GroupedByToAResult arr;
1505
+ group_by_to_a(entries, extPairKeyFunctor, arr);
1506
+
1507
+
1508
+ typedef pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;
1509
+ typedef vector<SelsNewSeqPair> SelsNewSeqPairCollection;
1510
+
1511
+
1512
+ SelsNewSeqPairCollection holder;
1513
+
1514
+
1515
+ for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) {
1516
+ pair<Complex_Selector, vector<ExtensionPair> >& groupedPair = *groupedIter;
1517
+
1518
+ Complex_Selector& seq = groupedPair.first;
1519
+ vector<ExtensionPair>& group = groupedPair.second;
1520
+
1521
+ // DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: "))
1522
+
1523
+
1524
+ Compound_Selector* pSels = new (ctx.mem) Compound_Selector(pSelector->path(), pSelector->position());
1525
+ for (vector<ExtensionPair>::iterator groupIter = group.begin(), groupIterEnd = group.end(); groupIter != groupIterEnd; groupIter++) {
1526
+ ExtensionPair& pair = *groupIter;
1527
+ Compound_Selector* pCompound = pair.second;
1528
+ for (size_t index = 0; index < pCompound->length(); index++) {
1529
+ Simple_Selector* pSimpleSelector = (*pCompound)[index];
1530
+ (*pSels) << pSimpleSelector;
1531
+ }
1532
+ }
1533
+
1534
+
1535
+
1536
+ // DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: "))
1537
+
1538
+
1539
+ Complex_Selector* pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge)
1540
+ Compound_Selector* pExtCompoundSelector = pSels; // All the simple selectors to be replaced from the current compound selector from all extensions
1541
+
1542
+
1543
+
1544
+ // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL?
1545
+ Compound_Selector* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx);
1546
+
1547
+
1548
+ // DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: "))
1549
+ // DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: "))
1550
+
1551
+
1552
+ Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->base();
1553
+ Compound_Selector* pUnifiedSelector = NULL;
1554
+
1555
+ if (!pInnermostCompoundSelector) {
1556
+ pInnermostCompoundSelector = new (ctx.mem) Compound_Selector(pSelector->path(), pSelector->position());
1557
+ }
1558
+
1559
+ pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx);
1560
+
1561
+ // DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: "))
1562
+ // DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))
1563
+ // DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: "))
1564
+
1565
+ if (!pUnifiedSelector || pUnifiedSelector->length() == 0) {
1566
+ continue;
1567
+ }
1568
+
1569
+
1570
+
1571
+ // TODO: implement the parent directive match (if necessary based on test failures)
1572
+ // next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?
1573
+
1574
+
1575
+
1576
+
1577
+ // TODO: This seems a little fishy to me. See if it causes any problems. From the ruby, we should be able to just
1578
+ // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more
1579
+ // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered
1580
+ // out and aren't operated on.
1581
+ Complex_Selector* pNewSelector = pExtComplexSelector->cloneFully(ctx);
1582
+ Complex_Selector* pNewInnerMost = new (ctx.mem) Complex_Selector(pSelector->path(), pSelector->position(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL);
1583
+ Complex_Selector::Combinator combinator = pNewSelector->clear_innermost();
1584
+ pNewSelector->set_innermost(pNewInnerMost, combinator);
1585
+
1586
+ #ifdef DEBUG
1587
+ SourcesSet debugSet;
1588
+ debugSet = pNewSelector->sources();
1589
+ if (debugSet.size() > 0) {
1590
+ throw "The new selector should start with no sources. Something needs to be cloned to fix this.";
1591
+ }
1592
+ debugSet = pExtComplexSelector->sources();
1593
+ if (debugSet.size() > 0) {
1594
+ throw "The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this.";
1595
+ }
1596
+ #endif
1597
+
1598
+
1599
+
1600
+ // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending.
1601
+ DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector, ctx))
1602
+
1603
+ DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, ctx, "SOURCES NEW SEQ BEGIN: "))
1604
+
1605
+ SourcesSet newSourcesSet = pSelector->sources();
1606
+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES THIS EXTEND: "))
1607
+
1608
+ newSourcesSet.insert(pExtComplexSelector);
1609
+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES WITH NEW SOURCE: "))
1610
+
1611
+ pNewSelector->addSources(newSourcesSet, ctx);
1612
+
1613
+ DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet newSet = pNewSelector->sources(); printSourcesSet(newSet, ctx, "SOURCES ON NEW SELECTOR AFTER ADD: "))
1614
+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: "))
1615
+
1616
+
1617
+
1618
+ holder.push_back(make_pair(pSels, pNewSelector));
1619
+ }
1620
+
1621
+
1622
+ for (SelsNewSeqPairCollection::iterator holderIter = holder.begin(), holderIterEnd = holder.end(); holderIter != holderIterEnd; holderIter++) {
1623
+ SelsNewSeqPair& pair = *holderIter;
1624
+
1625
+ Compound_Selector* pSels = pair.first;
1626
+ Complex_Selector* pNewSelector = pair.second;
1627
+
1628
+
1629
+ if (seen.find(*pSels) != seen.end()) {
1630
+ continue;
1631
+ }
1632
+
1633
+
1634
+ set<Compound_Selector> recurseSeen(seen);
1635
+ recurseSeen.insert(*pSels);
1636
+
1637
+
1638
+ DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx))
1639
+
1640
+ Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subsetMap, recurseSeen);
1641
+
1642
+ DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors)
1643
+
1644
+ for (NodeDeque::iterator iterator = recurseExtendedSelectors.collection()->begin(), endIterator = recurseExtendedSelectors.collection()->end();
1645
+ iterator != endIterator; ++iterator) {
1646
+ Node& newSelector = *iterator;
1647
+
1648
+ // DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << extendedSelectors)
1649
+ // DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/));
1650
+
1651
+ if (!extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/)) {
1652
+ // DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR")
1653
+ extendedSelectors.collection()->push_back(newSelector);
1654
+ }
1655
+ }
1656
+ }
1657
+
1658
+ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: "))
1659
+
1660
+ return extendedSelectors;
1661
+ }
1662
+
1663
+
1664
+ static bool complexSelectorHasExtension(
1665
+ Complex_Selector* pComplexSelector,
1666
+ Context& ctx,
1667
+ ExtensionSubsetMap& subsetMap) {
1668
+
1669
+ bool hasExtension = false;
1670
+
1671
+ Complex_Selector* pIter = pComplexSelector;
1672
+
1673
+ while (!hasExtension && pIter) {
1674
+ Compound_Selector* pHead = pIter->head();
1675
+
1676
+ if (pHead) {
1677
+ SubsetMapEntries entries = subsetMap.get_v(pHead->to_str_vec());
1678
+
1679
+ hasExtension = entries.size() > 0;
1680
+ }
1681
+
1682
+ pIter = pIter->tail();
1683
+ }
1684
+
1685
+ return hasExtension;
1686
+ }
1687
+
1688
+
1689
+ /*
1690
+ This is the equivalent of ruby's Sequence.do_extend.
1691
+
1692
+ // TODO: I think I have some modified ruby code to put here. Check.
1693
+ */
1694
+ /*
1695
+ ISSUES:
1696
+ - check to automatically include combinators doesn't transfer over to libsass' data model where
1697
+ the combinator and compound selector are one unit
1698
+ next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
1699
+ */
1700
+ static Node extendComplexSelector(
1701
+ Complex_Selector* pComplexSelector,
1702
+ Context& ctx,
1703
+ ExtensionSubsetMap& subsetMap,
1704
+ set<Compound_Selector> seen) {
1705
+
1706
+ Node complexSelector = complexSelectorToNode(pComplexSelector, ctx);
1707
+
1708
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector)
1709
+
1710
+ Node extendedNotExpanded = Node::createCollection();
1711
+
1712
+ for (NodeDeque::iterator complexSelIter = complexSelector.collection()->begin(), complexSelIterEnd = complexSelector.collection()->end(); complexSelIter != complexSelIterEnd; ++complexSelIter) {
1713
+ Node& sseqOrOp = *complexSelIter;
1714
+
1715
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "LOOP: " << sseqOrOp)
1716
+
1717
+ // If it's not a selector (meaning it's a combinator), just include it automatically
1718
+ if (!sseqOrOp.isSelector()) {
1719
+ // Wrap our Combinator in two collections to match ruby. This is essentially making a collection Node
1720
+ // with one collection child. The collection child represents a Complex_Selector that is only a combinator.
1721
+ Node outer = Node::createCollection();
1722
+ Node inner = Node::createCollection();
1723
+ outer.collection()->push_back(inner);
1724
+ inner.collection()->push_back(sseqOrOp);
1725
+ extendedNotExpanded.collection()->push_back(outer);
1726
+ continue;
1727
+ }
1728
+
1729
+ Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head();
1730
+
1731
+ Node extended = extendCompoundSelector(pCompoundSelector, ctx, subsetMap, seen);
1732
+
1733
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended)
1734
+
1735
+
1736
+ // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with an ruby Array instead of a Sequence
1737
+ // due to the member mapping: choices = extended.map {|seq| seq.members}
1738
+ Complex_Selector* pJustCurrentCompoundSelector = sseqOrOp.selector();
1739
+
1740
+ bool isSuperselector = false;
1741
+ for (NodeDeque::iterator iterator = extended.collection()->begin(), endIterator = extended.collection()->end();
1742
+ iterator != endIterator; ++iterator) {
1743
+ Node& childNode = *iterator;
1744
+ Complex_Selector* pExtensionSelector = nodeToComplexSelector(childNode, ctx);
1745
+ if (pExtensionSelector->is_superselector_of(pJustCurrentCompoundSelector)) {
1746
+ isSuperselector = true;
1747
+ break;
1748
+ }
1749
+ }
1750
+
1751
+ if (!isSuperselector) {
1752
+ extended.collection()->push_front(complexSelectorToNode(pJustCurrentCompoundSelector, ctx));
1753
+ }
1754
+
1755
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended)
1756
+
1757
+ // Aggregate our current extensions
1758
+ extendedNotExpanded.collection()->push_back(extended);
1759
+ }
1760
+
1761
+
1762
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << extendedNotExpanded)
1763
+
1764
+
1765
+
1766
+ // Ruby Equivalent: paths
1767
+ Node paths = Sass::paths(extendedNotExpanded, ctx);
1768
+
1769
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths)
1770
+
1771
+
1772
+
1773
+ // Ruby Equivalent: weave
1774
+ Node weaves = Node::createCollection();
1775
+
1776
+ for (NodeDeque::iterator pathsIter = paths.collection()->begin(), pathsEndIter = paths.collection()->end(); pathsIter != pathsEndIter; ++pathsIter) {
1777
+ Node& path = *pathsIter;
1778
+ Node weaved = weave(path, ctx);
1779
+ weaves.collection()->push_back(weaved);
1780
+ }
1781
+
1782
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves)
1783
+
1784
+
1785
+
1786
+ // Ruby Equivalent: trim
1787
+ Node trimmed = trim(weaves, ctx);
1788
+
1789
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed)
1790
+
1791
+
1792
+ // Ruby Equivalent: flatten
1793
+ Node extendedSelectors = flatten(trimmed, ctx, 1);
1794
+
1795
+ DEBUG_PRINTLN(EXTEND_COMPLEX, ">>>>> EXTENDED: " << extendedSelectors)
1796
+
1797
+
1798
+ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX END: " << complexSelector)
1799
+
1800
+
1801
+ return extendedSelectors;
1802
+ }
1803
+
1804
+
1805
+
1806
+ /*
1807
+ This is the equivalent of ruby's CommaSequence.do_extend.
1808
+ */
1809
+ static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool& extendedSomething) {
1810
+
1811
+ To_String to_string;
1812
+
1813
+ Selector_List* pNewSelectors = new (ctx.mem) Selector_List(pSelectorList->path(), pSelectorList->position(), pSelectorList->length());
1814
+
1815
+ extendedSomething = false;
1816
+
1817
+ for (size_t index = 0, length = pSelectorList->length(); index < length; index++) {
1818
+ Complex_Selector* pSelector = (*pSelectorList)[index];
1819
+
1820
+ // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that.
1821
+ // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to
1822
+ // run through the extend code (which does a data model transformation), check if there is anything to extend before doing
1823
+ // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps
1824
+ // when debugging).
1825
+ if (!complexSelectorHasExtension(pSelector, ctx, subsetMap)) {
1826
+ *pNewSelectors << pSelector;
1827
+ continue;
1828
+ }
1829
+
1830
+ extendedSomething = true;
1831
+
1832
+ set<Compound_Selector> seen;
1833
+ Node extendedSelectors = extendComplexSelector(pSelector, ctx, subsetMap, seen);
1834
+
1835
+ if (!pSelector->has_placeholder()) {
1836
+ if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {
1837
+ *pNewSelectors << pSelector;
1838
+ }
1839
+ }
1840
+
1841
+ for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) {
1842
+ Node& childNode = *iterator;
1843
+ *pNewSelectors << nodeToComplexSelector(childNode, ctx);
1844
+ }
1845
+ }
1846
+
1847
+ return pNewSelectors;
1848
+
1849
+ }
1850
+
1851
+
1852
+ bool shouldExtendBlock(Block* b) {
1853
+
1854
+ // If a block is empty, there's no reason to extend it since any rules placed on this block
1855
+ // won't have any output. The main benefit of this is for structures like:
1856
+ //
1857
+ // .a {
1858
+ // .b {
1859
+ // x: y;
1860
+ // }
1861
+ // }
1862
+ //
1863
+ // We end up visiting two rulesets (one with the selector .a and the other with the selector .a .b).
1864
+ // In this case, we don't want to try to pull rules onto .a since they won't get output anyway since
1865
+ // there are no child statements. However .a .b should have extensions applied.
1866
+
1867
+ for (size_t i = 0, L = b->length(); i < L; ++i) {
1868
+ Statement* stm = (*b)[i];
1869
+
1870
+ if (typeid(*stm) == typeid(Ruleset)) {
1871
+ // Do nothing. This doesn't count as a statement that causes extension since we'll iterate over this rule set in a future visit and try to extend it.
1872
+ }
1873
+ else {
1874
+ return true;
1875
+ }
1876
+ }
1877
+
1878
+ return false;
1879
+
1880
+ }
1881
+
1882
+
1883
+ // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change.
1884
+ template <typename ObjectType>
1885
+ static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subsetMap) {
1886
+ To_String to_string;
1887
+
1888
+ DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))
1889
+
1890
+ // Ruby sass seems to filter nodes that don't have any content well before we get here. I'm not sure the repercussions
1891
+ // of doing so, so for now, let's just not extend things that won't be output later.
1892
+ if (!shouldExtendBlock(pObject->block())) {
1893
+ DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT")
1894
+ return;
1895
+ }
1896
+
1897
+ bool extendedSomething = false;
1898
+ Selector_List* pNewSelectorList = extendSelectorList(static_cast<Selector_List*>(pObject->selector()), ctx, subsetMap, extendedSomething);
1899
+
1900
+ if (extendedSomething && pNewSelectorList) {
1901
+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))
1902
+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->perform(&to_string))
1903
+
1904
+ // re-parse in order to restructure expanded placeholder nodes correctly.
1905
+ //
1906
+ // TODO: I don't know if this is needed, but it was in the original C++ implementation, so I kept it. Try running the tests without re-parsing.
1907
+ pObject->selector(
1908
+ Parser::from_c_str(
1909
+ (pNewSelectorList->perform(&to_string) + ";").c_str(),
1910
+ ctx,
1911
+ pNewSelectorList->path(),
1912
+ pNewSelectorList->position()
1913
+ ).parse_selector_group()
1914
+ );
1915
+ } else {
1916
+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING")
1917
+ }
1918
+ }
1919
+
1920
+
1921
+
1922
+ Extend::Extend(Context& ctx, ExtensionSubsetMap& ssm)
1923
+ : ctx(ctx), subset_map(ssm)
1924
+ { }
1925
+
1926
+ void Extend::operator()(Block* b)
1927
+ {
1928
+ for (size_t i = 0, L = b->length(); i < L; ++i) {
1929
+ (*b)[i]->perform(this);
1930
+ }
1931
+ }
1932
+
1933
+ void Extend::operator()(Ruleset* pRuleset)
1934
+ {
1935
+ extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map);
1936
+
1937
+ pRuleset->block()->perform(this);
1938
+ }
1939
+
1940
+ void Extend::operator()(Feature_Block* pFeatureBlock)
1941
+ {
1942
+ if (pFeatureBlock->selector()) {
1943
+ extendObjectWithSelectorAndBlock(pFeatureBlock, ctx, subset_map);
1944
+ }
1945
+
1946
+ pFeatureBlock->block()->perform(this);
1947
+ }
1948
+
1949
+ void Extend::operator()(Media_Block* pMediaBlock)
1950
+ {
1951
+ if (pMediaBlock->selector()) {
1952
+ extendObjectWithSelectorAndBlock(pMediaBlock, ctx, subset_map);
1953
+ }
1954
+
1955
+ pMediaBlock->block()->perform(this);
1956
+ }
1957
+
1958
+ void Extend::operator()(At_Rule* a)
1959
+ {
1960
+ if (a->block()) a->block()->perform(this);
1961
+ }
1962
+ }