1 // sass.hpp must go before all system headers to get the 2 // __EXTENSIONS__ fix on Solaris. 3 #include "sass.hpp" 4 5 #include "ast.hpp" 6 #include "permutate.hpp" 7 #include "dart_helpers.hpp" 8 9 namespace Sass { 10 11 // ########################################################################## 12 // Returns whether or not [compound] contains a `::root` selector. 13 // ########################################################################## hasRoot(const CompoundSelector * compound)14 bool hasRoot(const CompoundSelector* compound) 15 { 16 // Libsass does not yet know the root selector 17 return false; 18 } 19 // EO hasRoot 20 21 // ########################################################################## 22 // Returns whether a [CompoundSelector] may contain only 23 // one simple selector of the same type as [simple]. 24 // ########################################################################## isUnique(const SimpleSelector * simple)25 bool isUnique(const SimpleSelector* simple) 26 { 27 if (Cast<IDSelector>(simple)) return true; 28 if (const PseudoSelector * pseudo = Cast<PseudoSelector>(simple)) { 29 if (pseudo->is_pseudo_element()) return true; 30 } 31 return false; 32 } 33 // EO isUnique 34 35 // ########################################################################## 36 // Returns whether [complex1] and [complex2] need to be unified to 37 // produce a valid combined selector. This is necessary when both 38 // selectors contain the same unique simple selector, such as an ID. 39 // ########################################################################## mustUnify(const sass::vector<SelectorComponentObj> & complex1,const sass::vector<SelectorComponentObj> & complex2)40 bool mustUnify( 41 const sass::vector<SelectorComponentObj>& complex1, 42 const sass::vector<SelectorComponentObj>& complex2) 43 { 44 45 sass::vector<const SimpleSelector*> uniqueSelectors1; 46 for (const SelectorComponent* component : complex1) { 47 if (const CompoundSelector * compound = component->getCompound()) { 48 for (const SimpleSelector* sel : compound->elements()) { 49 if (isUnique(sel)) { 50 uniqueSelectors1.push_back(sel); 51 } 52 } 53 } 54 } 55 if (uniqueSelectors1.empty()) return false; 56 57 // ToDo: unsure if this is correct 58 for (const SelectorComponent* component : complex2) { 59 if (const CompoundSelector * compound = component->getCompound()) { 60 for (const SimpleSelector* sel : compound->elements()) { 61 if (isUnique(sel)) { 62 for (auto check : uniqueSelectors1) { 63 if (*check == *sel) return true; 64 } 65 } 66 } 67 } 68 } 69 70 return false; 71 72 } 73 // EO isUnique 74 75 // ########################################################################## 76 // Helper function used by `weaveParents` 77 // ########################################################################## cmpGroups(const sass::vector<SelectorComponentObj> & group1,const sass::vector<SelectorComponentObj> & group2,sass::vector<SelectorComponentObj> & select)78 bool cmpGroups( 79 const sass::vector<SelectorComponentObj>& group1, 80 const sass::vector<SelectorComponentObj>& group2, 81 sass::vector<SelectorComponentObj>& select) 82 { 83 84 if (group1.size() == group2.size() && std::equal(group1.begin(), group1.end(), group2.begin(), PtrObjEqualityFn<SelectorComponent>)) { 85 select = group1; 86 return true; 87 } 88 89 if (!Cast<CompoundSelector>(group1.front())) { 90 select = {}; 91 return false; 92 } 93 if (!Cast<CompoundSelector>(group2.front())) { 94 select = {}; 95 return false; 96 } 97 98 if (complexIsParentSuperselector(group1, group2)) { 99 select = group2; 100 return true; 101 } 102 if (complexIsParentSuperselector(group2, group1)) { 103 select = group1; 104 return true; 105 } 106 107 if (!mustUnify(group1, group2)) { 108 select = {}; 109 return false; 110 } 111 112 sass::vector<sass::vector<SelectorComponentObj>> unified 113 = unifyComplex({ group1, group2 }); 114 if (unified.empty()) return false; 115 if (unified.size() > 1) return false; 116 select = unified.front(); 117 return true; 118 } 119 // EO cmpGroups 120 121 // ########################################################################## 122 // Helper function used by `weaveParents` 123 // ########################################################################## 124 template <class T> checkForEmptyChild(const T & item)125 bool checkForEmptyChild(const T& item) { 126 return item.empty(); 127 } 128 // EO checkForEmptyChild 129 130 // ########################################################################## 131 // Helper function used by `weaveParents` 132 // ########################################################################## cmpChunkForEmptySequence(const sass::vector<sass::vector<SelectorComponentObj>> & seq,const sass::vector<SelectorComponentObj> & group)133 bool cmpChunkForEmptySequence( 134 const sass::vector<sass::vector<SelectorComponentObj>>& seq, 135 const sass::vector<SelectorComponentObj>& group) 136 { 137 return seq.empty(); 138 } 139 // EO cmpChunkForEmptySequence 140 141 // ########################################################################## 142 // Helper function used by `weaveParents` 143 // ########################################################################## cmpChunkForParentSuperselector(const sass::vector<sass::vector<SelectorComponentObj>> & seq,const sass::vector<SelectorComponentObj> & group)144 bool cmpChunkForParentSuperselector( 145 const sass::vector<sass::vector<SelectorComponentObj>>& seq, 146 const sass::vector<SelectorComponentObj>& group) 147 { 148 return seq.empty() || complexIsParentSuperselector(seq.front(), group); 149 } 150 // EO cmpChunkForParentSuperselector 151 152 // ########################################################################## 153 // Returns all orderings of initial subseqeuences of [queue1] and [queue2]. 154 // The [done] callback is used to determine the extent of the initial 155 // subsequences. It's called with each queue until it returns `true`. 156 // Destructively removes the initial subsequences of [queue1] and [queue2]. 157 // For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|` denoting 158 // the boundary of the initial subsequence), this would return `[(A B C 1 2), 159 // (1 2 A B C)]`. The queues would then contain `(D E)` and `(3 4 5)`. 160 // ########################################################################## 161 template <class T> getChunks(sass::vector<T> & queue1,sass::vector<T> & queue2,const T & group,bool (* done)(const sass::vector<T> &,const T &))162 sass::vector<sass::vector<T>> getChunks( 163 sass::vector<T>& queue1, sass::vector<T>& queue2, 164 const T& group, bool(*done)(const sass::vector<T>&, const T&) 165 ) { 166 167 sass::vector<T> chunk1; 168 while (!done(queue1, group)) { 169 chunk1.push_back(queue1.front()); 170 queue1.erase(queue1.begin()); 171 } 172 173 sass::vector<T> chunk2; 174 while (!done(queue2, group)) { 175 chunk2.push_back(queue2.front()); 176 queue2.erase(queue2.begin()); 177 } 178 179 if (chunk1.empty() && chunk2.empty()) return {}; 180 else if (chunk1.empty()) return { chunk2 }; 181 else if (chunk2.empty()) return { chunk1 }; 182 183 sass::vector<T> choice1(chunk1), choice2(chunk2); 184 std::move(std::begin(chunk2), std::end(chunk2), 185 std::inserter(choice1, std::end(choice1))); 186 std::move(std::begin(chunk1), std::end(chunk1), 187 std::inserter(choice2, std::end(choice2))); 188 return { choice1, choice2 }; 189 } 190 // EO getChunks 191 192 // ########################################################################## 193 // If the first element of [queue] has a `::root` 194 // selector, removes and returns that element. 195 // ########################################################################## getFirstIfRoot(sass::vector<SelectorComponentObj> & queue)196 CompoundSelectorObj getFirstIfRoot(sass::vector<SelectorComponentObj>& queue) { 197 if (queue.empty()) return {}; 198 SelectorComponent* first = queue.front(); 199 if (CompoundSelector* sel = Cast<CompoundSelector>(first)) { 200 if (!hasRoot(sel)) return {}; 201 queue.erase(queue.begin()); 202 return sel; 203 } 204 return {}; 205 } 206 // EO getFirstIfRoot 207 208 // ########################################################################## 209 // Returns [complex], grouped into sub-lists such that no sub-list 210 // contains two adjacent [ComplexSelector]s. For example, 211 // `(A B > C D + E ~ > G)` is grouped into `[(A) (B > C) (D + E ~ > G)]`. 212 // ########################################################################## groupSelectors(const sass::vector<SelectorComponentObj> & components)213 sass::vector<sass::vector<SelectorComponentObj>> groupSelectors( 214 const sass::vector<SelectorComponentObj>& components) 215 { 216 bool lastWasCompound = false; 217 sass::vector<SelectorComponentObj> group; 218 sass::vector<sass::vector<SelectorComponentObj>> groups; 219 for (size_t i = 0; i < components.size(); i += 1) { 220 if (CompoundSelector* compound = components[i]->getCompound()) { 221 if (lastWasCompound) { 222 groups.push_back(group); 223 group.clear(); 224 } 225 group.push_back(compound); 226 lastWasCompound = true; 227 } 228 else if (SelectorCombinator* combinator = components[i]->getCombinator()) { 229 group.push_back(combinator); 230 lastWasCompound = false; 231 } 232 } 233 if (!group.empty()) { 234 groups.push_back(group); 235 } 236 return groups; 237 } 238 // EO groupSelectors 239 240 // ########################################################################## 241 // Extracts leading [Combinator]s from [components1] and [components2] 242 // and merges them together into a single list of combinators. 243 // If there are no combinators to be merged, returns an empty list. 244 // If the combinators can't be merged, returns `null`. 245 // ########################################################################## mergeInitialCombinators(sass::vector<SelectorComponentObj> & components1,sass::vector<SelectorComponentObj> & components2,sass::vector<SelectorComponentObj> & result)246 bool mergeInitialCombinators( 247 sass::vector<SelectorComponentObj>& components1, 248 sass::vector<SelectorComponentObj>& components2, 249 sass::vector<SelectorComponentObj>& result) 250 { 251 252 sass::vector<SelectorComponentObj> combinators1; 253 while (!components1.empty() && Cast<SelectorCombinator>(components1.front())) { 254 SelectorCombinatorObj front = Cast<SelectorCombinator>(components1.front()); 255 components1.erase(components1.begin()); 256 combinators1.push_back(front); 257 } 258 259 sass::vector<SelectorComponentObj> combinators2; 260 while (!components2.empty() && Cast<SelectorCombinator>(components2.front())) { 261 SelectorCombinatorObj front = Cast<SelectorCombinator>(components2.front()); 262 components2.erase(components2.begin()); 263 combinators2.push_back(front); 264 } 265 266 // If neither sequence of combinators is a subsequence 267 // of the other, they cannot be merged successfully. 268 sass::vector<SelectorComponentObj> LCS = lcs<SelectorComponentObj>(combinators1, combinators2); 269 270 if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) { 271 result = combinators2; 272 return true; 273 } 274 if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) { 275 result = combinators1; 276 return true; 277 } 278 279 return false; 280 281 } 282 // EO mergeInitialCombinators 283 284 // ########################################################################## 285 // Extracts trailing [Combinator]s, and the selectors to which they apply, 286 // from [components1] and [components2] and merges them together into a 287 // single list. If there are no combinators to be merged, returns an 288 // empty list. If the sequences can't be merged, returns `null`. 289 // ########################################################################## mergeFinalCombinators(sass::vector<SelectorComponentObj> & components1,sass::vector<SelectorComponentObj> & components2,sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> & result)290 bool mergeFinalCombinators( 291 sass::vector<SelectorComponentObj>& components1, 292 sass::vector<SelectorComponentObj>& components2, 293 sass::vector<sass::vector<sass::vector<SelectorComponentObj>>>& result) 294 { 295 296 if (components1.empty() || !Cast<SelectorCombinator>(components1.back())) { 297 if (components2.empty() || !Cast<SelectorCombinator>(components2.back())) { 298 return true; 299 } 300 } 301 302 sass::vector<SelectorComponentObj> combinators1; 303 while (!components1.empty() && Cast<SelectorCombinator>(components1.back())) { 304 SelectorCombinatorObj back = Cast<SelectorCombinator>(components1.back()); 305 components1.erase(components1.end() - 1); 306 combinators1.push_back(back); 307 } 308 309 sass::vector<SelectorComponentObj> combinators2; 310 while (!components2.empty() && Cast<SelectorCombinator>(components2.back())) { 311 SelectorCombinatorObj back = Cast<SelectorCombinator>(components2.back()); 312 components2.erase(components2.end() - 1); 313 combinators2.push_back(back); 314 } 315 316 // reverse now as we used push_back (faster than new alloc) 317 std::reverse(combinators1.begin(), combinators1.end()); 318 std::reverse(combinators2.begin(), combinators2.end()); 319 320 if (combinators1.size() > 1 || combinators2.size() > 1) { 321 // If there are multiple combinators, something hacky's going on. If one 322 // is a supersequence of the other, use that, otherwise give up. 323 auto LCS = lcs<SelectorComponentObj>(combinators1, combinators2); 324 if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) { 325 result.push_back({ combinators2 }); 326 } 327 else if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) { 328 result.push_back({ combinators1 }); 329 } 330 else { 331 return false; 332 } 333 return true; 334 } 335 336 // This code looks complicated, but it's actually just a bunch of special 337 // cases for interactions between different combinators. 338 SelectorCombinatorObj combinator1, combinator2; 339 if (!combinators1.empty()) combinator1 = combinators1.back(); 340 if (!combinators2.empty()) combinator2 = combinators2.back(); 341 342 if (!combinator1.isNull() && !combinator2.isNull()) { 343 344 CompoundSelector* compound1 = Cast<CompoundSelector>(components1.back()); 345 CompoundSelector* compound2 = Cast<CompoundSelector>(components2.back()); 346 347 components1.pop_back(); 348 components2.pop_back(); 349 350 if (combinator1->isGeneralCombinator() && combinator2->isGeneralCombinator()) { 351 352 if (compound1->isSuperselectorOf(compound2)) { 353 result.push_back({ { compound2, combinator2 } }); 354 } 355 else if (compound2->isSuperselectorOf(compound1)) { 356 result.push_back({ { compound1, combinator1 } }); 357 } 358 else { 359 sass::vector<sass::vector<SelectorComponentObj>> choices; 360 choices.push_back({ compound1, combinator1, compound2, combinator2 }); 361 choices.push_back({ compound2, combinator2, compound1, combinator1 }); 362 if (CompoundSelector* unified = compound1->unifyWith(compound2)) { 363 choices.push_back({ unified, combinator1 }); 364 } 365 result.push_back(choices); 366 } 367 } 368 else if ((combinator1->isGeneralCombinator() && combinator2->isAdjacentCombinator()) || 369 (combinator1->isAdjacentCombinator() && combinator2->isGeneralCombinator())) { 370 371 CompoundSelector* followingSiblingSelector = combinator1->isGeneralCombinator() ? compound1 : compound2; 372 CompoundSelector* nextSiblingSelector = combinator1->isGeneralCombinator() ? compound2 : compound1; 373 SelectorCombinator* followingSiblingCombinator = combinator1->isGeneralCombinator() ? combinator1 : combinator2; 374 SelectorCombinator* nextSiblingCombinator = combinator1->isGeneralCombinator() ? combinator2 : combinator1; 375 376 if (followingSiblingSelector->isSuperselectorOf(nextSiblingSelector)) { 377 result.push_back({ { nextSiblingSelector, nextSiblingCombinator } }); 378 } 379 else { 380 CompoundSelectorObj unified = compound1->unifyWith(compound2); 381 sass::vector<sass::vector<SelectorComponentObj>> items; 382 383 if (!unified.isNull()) { 384 items.push_back({ 385 unified, nextSiblingCombinator 386 }); 387 } 388 389 items.insert(items.begin(), { 390 followingSiblingSelector, 391 followingSiblingCombinator, 392 nextSiblingSelector, 393 nextSiblingCombinator, 394 }); 395 396 result.push_back(items); 397 } 398 399 } 400 else if (combinator1->isChildCombinator() && (combinator2->isAdjacentCombinator() || combinator2->isGeneralCombinator())) { 401 result.push_back({ { compound2, combinator2 } }); 402 components1.push_back(compound1); 403 components1.push_back(combinator1); 404 } 405 else if (combinator2->isChildCombinator() && (combinator1->isAdjacentCombinator() || combinator1->isGeneralCombinator())) { 406 result.push_back({ { compound1, combinator1 } }); 407 components2.push_back(compound2); 408 components2.push_back(combinator2); 409 } 410 else if (*combinator1 == *combinator2) { 411 CompoundSelectorObj unified = compound1->unifyWith(compound2); 412 if (unified.isNull()) return false; 413 result.push_back({ { unified, combinator1 } }); 414 } 415 else { 416 return false; 417 } 418 419 return mergeFinalCombinators(components1, components2, result); 420 421 } 422 else if (!combinator1.isNull()) { 423 424 if (combinator1->isChildCombinator() && !components2.empty()) { 425 const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back()); 426 const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back()); 427 if (back1 && back2 && back2->isSuperselectorOf(back1)) { 428 components2.pop_back(); 429 } 430 } 431 432 result.push_back({ { components1.back(), combinator1 } }); 433 434 components1.pop_back(); 435 436 return mergeFinalCombinators(components1, components2, result); 437 438 } 439 440 if (combinator2->isChildCombinator() && !components1.empty()) { 441 const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back()); 442 const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back()); 443 if (back1 && back2 && back1->isSuperselectorOf(back2)) { 444 components1.pop_back(); 445 } 446 } 447 448 result.push_back({ { components2.back(), combinator2 } }); 449 450 components2.pop_back(); 451 452 return mergeFinalCombinators(components1, components2, result); 453 454 } 455 // EO mergeFinalCombinators 456 457 // ########################################################################## 458 // Expands "parenthesized selectors" in [complexes]. That is, if 459 // we have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually 460 // expands into `.D .C, .D (.A .B)`, and this function translates 461 // `.D (.A .B)` into `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B` 462 // would also be required, but including merged selectors results in 463 // exponential output for very little gain. The selector `.D (.A .B)` 464 // is represented as the list `[[.D], [.A, .B]]`. 465 // ########################################################################## weave(const sass::vector<sass::vector<SelectorComponentObj>> & complexes)466 sass::vector<sass::vector<SelectorComponentObj>> weave( 467 const sass::vector<sass::vector<SelectorComponentObj>>& complexes) { 468 469 sass::vector<sass::vector<SelectorComponentObj>> prefixes; 470 471 prefixes.push_back(complexes.at(0)); 472 473 for (size_t i = 1; i < complexes.size(); i += 1) { 474 475 if (complexes[i].empty()) { 476 continue; 477 } 478 const sass::vector<SelectorComponentObj>& complex = complexes[i]; 479 SelectorComponent* target = complex.back(); 480 if (complex.size() == 1) { 481 for (auto& prefix : prefixes) { 482 prefix.push_back(target); 483 } 484 continue; 485 } 486 487 sass::vector<SelectorComponentObj> parents(complex); 488 489 parents.pop_back(); 490 491 sass::vector<sass::vector<SelectorComponentObj>> newPrefixes; 492 for (sass::vector<SelectorComponentObj> prefix : prefixes) { 493 sass::vector<sass::vector<SelectorComponentObj>> 494 parentPrefixes = weaveParents(prefix, parents); 495 if (parentPrefixes.empty()) continue; 496 for (auto& parentPrefix : parentPrefixes) { 497 parentPrefix.push_back(target); 498 newPrefixes.push_back(parentPrefix); 499 } 500 } 501 prefixes = newPrefixes; 502 503 } 504 return prefixes; 505 506 } 507 // EO weave 508 509 // ########################################################################## 510 // Interweaves [parents1] and [parents2] as parents of the same target 511 // selector. Returns all possible orderings of the selectors in the 512 // inputs (including using unification) that maintain the relative 513 // ordering of the input. For example, given `.foo .bar` and `.baz .bang`, 514 // this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`, 515 // `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`, 516 // and so on until `.baz .bang .foo .bar`. Semantically, for selectors A 517 // and B, this returns all selectors `AB_i` such that the union over all i 518 // of elements matched by `AB_i X` is identical to the intersection of all 519 // elements matched by `A X` and all elements matched by `B X`. Some `AB_i` 520 // are elided to reduce the size of the output. 521 // ########################################################################## weaveParents(sass::vector<SelectorComponentObj> queue1,sass::vector<SelectorComponentObj> queue2)522 sass::vector<sass::vector<SelectorComponentObj>> weaveParents( 523 sass::vector<SelectorComponentObj> queue1, 524 sass::vector<SelectorComponentObj> queue2) 525 { 526 527 sass::vector<SelectorComponentObj> leads; 528 sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> trails; 529 if (!mergeInitialCombinators(queue1, queue2, leads)) return {}; 530 if (!mergeFinalCombinators(queue1, queue2, trails)) return {}; 531 // list comes out in reverse order for performance 532 std::reverse(trails.begin(), trails.end()); 533 534 // Make sure there's at most one `:root` in the output. 535 // Note: does not yet do anything in libsass (no root selector) 536 CompoundSelectorObj root1 = getFirstIfRoot(queue1); 537 CompoundSelectorObj root2 = getFirstIfRoot(queue2); 538 539 if (!root1.isNull() && !root2.isNull()) { 540 CompoundSelectorObj root = root1->unifyWith(root2); 541 if (root.isNull()) return {}; // null 542 queue1.insert(queue1.begin(), root); 543 queue2.insert(queue2.begin(), root); 544 } 545 else if (!root1.isNull()) { 546 queue2.insert(queue2.begin(), root1); 547 } 548 else if (!root2.isNull()) { 549 queue1.insert(queue1.begin(), root2); 550 } 551 552 // group into sub-lists so no sub-list contains two adjacent ComplexSelectors. 553 sass::vector<sass::vector<SelectorComponentObj>> groups1 = groupSelectors(queue1); 554 sass::vector<sass::vector<SelectorComponentObj>> groups2 = groupSelectors(queue2); 555 556 // The main array to store our choices that will be permutated 557 sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> choices; 558 559 // append initial combinators 560 choices.push_back({ leads }); 561 562 sass::vector<sass::vector<SelectorComponentObj>> LCS = 563 lcs<sass::vector<SelectorComponentObj>>(groups1, groups2, cmpGroups); 564 565 for (auto group : LCS) { 566 567 // Create junks from groups1 and groups2 568 sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> 569 chunks = getChunks<sass::vector<SelectorComponentObj>>( 570 groups1, groups2, group, cmpChunkForParentSuperselector); 571 572 // Create expanded array by flattening chunks2 inner 573 sass::vector<sass::vector<SelectorComponentObj>> 574 expanded = flattenInner(chunks); 575 576 // Prepare data structures 577 choices.push_back(expanded); 578 choices.push_back({ group }); 579 if (!groups1.empty()) { 580 groups1.erase(groups1.begin()); 581 } 582 if (!groups2.empty()) { 583 groups2.erase(groups2.begin()); 584 } 585 586 } 587 588 // Create junks from groups1 and groups2 589 sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> 590 chunks = getChunks<sass::vector<SelectorComponentObj>>( 591 groups1, groups2, {}, cmpChunkForEmptySequence); 592 593 // Append chunks with inner arrays flattened 594 choices.emplace_back(flattenInner(chunks)); 595 596 // append all trailing selectors to choices 597 std::move(std::begin(trails), std::end(trails), 598 std::inserter(choices, std::end(choices))); 599 600 // move all non empty items to the front, then erase the trailing ones 601 choices.erase(std::remove_if(choices.begin(), choices.end(), checkForEmptyChild 602 <sass::vector<sass::vector<SelectorComponentObj>>>), choices.end()); 603 604 // permutate all possible paths through selectors 605 sass::vector<sass::vector<SelectorComponentObj>> 606 results = flattenInner(permutate(choices)); 607 608 return results; 609 610 } 611 // EO weaveParents 612 613 // ########################################################################## 614 // ########################################################################## 615 616 } 617