1 // sass.hpp must go before all system headers to get the 2 // __EXTENSIONS__ fix on Solaris. 3 #include "sass.hpp" 4 5 #include <iostream> 6 #include <typeinfo> 7 #include <vector> 8 9 #include "cssize.hpp" 10 #include "context.hpp" 11 12 namespace Sass { 13 Cssize(Context & ctx)14 Cssize::Cssize(Context& ctx) 15 : traces(ctx.traces), 16 block_stack(BlockStack()), 17 p_stack(sass::vector<Statement*>()) 18 { } 19 parent()20 Statement* Cssize::parent() 21 { 22 return p_stack.size() ? p_stack.back() : block_stack.front(); 23 } 24 operator ()(Block * b)25 Block* Cssize::operator()(Block* b) 26 { 27 Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root()); 28 // bb->tabs(b->tabs()); 29 block_stack.push_back(bb); 30 append_block(b, bb); 31 block_stack.pop_back(); 32 return bb.detach(); 33 } 34 operator ()(Trace * t)35 Statement* Cssize::operator()(Trace* t) 36 { 37 traces.push_back(Backtrace(t->pstate())); 38 auto result = t->block()->perform(this); 39 traces.pop_back(); 40 return result; 41 } 42 operator ()(Declaration * d)43 Statement* Cssize::operator()(Declaration* d) 44 { 45 String_Obj property = Cast<String>(d->property()); 46 47 if (Declaration* dd = Cast<Declaration>(parent())) { 48 String_Obj parent_property = Cast<String>(dd->property()); 49 property = SASS_MEMORY_NEW(String_Constant, 50 d->property()->pstate(), 51 parent_property->to_string() + "-" + property->to_string()); 52 if (!dd->value()) { 53 d->tabs(dd->tabs() + 1); 54 } 55 } 56 57 Declaration_Obj dd = SASS_MEMORY_NEW(Declaration, 58 d->pstate(), 59 property, 60 d->value(), 61 d->is_important(), 62 d->is_custom_property()); 63 dd->is_indented(d->is_indented()); 64 dd->tabs(d->tabs()); 65 66 p_stack.push_back(dd); 67 Block_Obj bb = d->block() ? operator()(d->block()) : NULL; 68 p_stack.pop_back(); 69 70 if (bb && bb->length()) { 71 if (dd->value() && !dd->value()->is_invisible()) { 72 bb->unshift(dd); 73 } 74 return bb.detach(); 75 } 76 else if (dd->value() && !dd->value()->is_invisible()) { 77 return dd.detach(); 78 } 79 80 return 0; 81 } 82 operator ()(AtRule * r)83 Statement* Cssize::operator()(AtRule* r) 84 { 85 if (!r->block() || !r->block()->length()) return r; 86 87 if (parent()->statement_type() == Statement::RULESET) 88 { 89 return r->is_keyframes() ? SASS_MEMORY_NEW(Bubble, r->pstate(), r) : bubble(r); 90 } 91 92 p_stack.push_back(r); 93 AtRuleObj rr = SASS_MEMORY_NEW(AtRule, 94 r->pstate(), 95 r->keyword(), 96 r->selector(), 97 r->block() ? operator()(r->block()) : 0); 98 if (r->value()) rr->value(r->value()); 99 p_stack.pop_back(); 100 101 bool directive_exists = false; 102 size_t L = rr->block() ? rr->block()->length() : 0; 103 for (size_t i = 0; i < L && !directive_exists; ++i) { 104 Statement_Obj s = r->block()->at(i); 105 if (s->statement_type() != Statement::BUBBLE) directive_exists = true; 106 else { 107 Bubble_Obj s_obj = Cast<Bubble>(s); 108 s = s_obj->node(); 109 if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false; 110 else directive_exists = (Cast<AtRule>(s)->keyword() == rr->keyword()); 111 } 112 113 } 114 115 Block* result = SASS_MEMORY_NEW(Block, rr->pstate()); 116 if (!(directive_exists || rr->is_keyframes())) 117 { 118 AtRule* empty_node = Cast<AtRule>(rr); 119 empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate())); 120 result->append(empty_node); 121 } 122 123 Block_Obj db = rr->block(); 124 if (db.isNull()) db = SASS_MEMORY_NEW(Block, rr->pstate()); 125 Block_Obj ss = debubble(db, rr); 126 for (size_t i = 0, L = ss->length(); i < L; ++i) { 127 result->append(ss->at(i)); 128 } 129 130 return result; 131 } 132 operator ()(Keyframe_Rule * r)133 Statement* Cssize::operator()(Keyframe_Rule* r) 134 { 135 if (!r->block() || !r->block()->length()) return r; 136 137 Keyframe_Rule_Obj rr = SASS_MEMORY_NEW(Keyframe_Rule, 138 r->pstate(), 139 operator()(r->block())); 140 if (!r->name().isNull()) rr->name(r->name()); 141 142 return debubble(rr->block(), rr); 143 } 144 operator ()(StyleRule * r)145 Statement* Cssize::operator()(StyleRule* r) 146 { 147 p_stack.push_back(r); 148 // this can return a string schema 149 // string schema is not a statement! 150 // r->block() is already a string schema 151 // and that is coming from propset expand 152 Block* bb = operator()(r->block()); 153 // this should protect us (at least a bit) from our mess 154 // fixing this properly is harder that it should be ... 155 if (Cast<Statement>(bb) == NULL) { 156 error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces); 157 } 158 StyleRuleObj rr = SASS_MEMORY_NEW(StyleRule, 159 r->pstate(), 160 r->selector(), 161 bb); 162 163 rr->is_root(r->is_root()); 164 // rr->tabs(r->block()->tabs()); 165 p_stack.pop_back(); 166 167 if (!rr->block()) { 168 error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces); 169 } 170 171 Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate()); 172 Block* rules = SASS_MEMORY_NEW(Block, rr->block()->pstate()); 173 for (size_t i = 0, L = rr->block()->length(); i < L; i++) 174 { 175 Statement* s = rr->block()->at(i); 176 if (bubblable(s)) rules->append(s); 177 if (!bubblable(s)) props->append(s); 178 } 179 180 if (props->length()) 181 { 182 Block_Obj pb = SASS_MEMORY_NEW(Block, rr->block()->pstate()); 183 pb->concat(props); 184 rr->block(pb); 185 186 for (size_t i = 0, L = rules->length(); i < L; i++) 187 { 188 Statement* stm = rules->at(i); 189 stm->tabs(stm->tabs() + 1); 190 } 191 192 rules->unshift(rr); 193 } 194 195 Block* ptr = rules; 196 rules = debubble(rules); 197 void* lp = ptr; 198 void* rp = rules; 199 if (lp != rp) { 200 Block_Obj obj = ptr; 201 } 202 203 if (!(!rules->length() || 204 !bubblable(rules->last()) || 205 parent()->statement_type() == Statement::RULESET)) 206 { 207 rules->last()->group_end(true); 208 } 209 return rules; 210 } 211 operator ()(Null * m)212 Statement* Cssize::operator()(Null* m) 213 { 214 return 0; 215 } 216 operator ()(CssMediaRule * m)217 Statement* Cssize::operator()(CssMediaRule* m) 218 { 219 if (parent()->statement_type() == Statement::RULESET) 220 { 221 return bubble(m); 222 } 223 224 if (parent()->statement_type() == Statement::MEDIA) 225 { 226 return SASS_MEMORY_NEW(Bubble, m->pstate(), m); 227 } 228 229 p_stack.push_back(m); 230 231 CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block()); 232 mm->concat(m->elements()); 233 mm->block(operator()(m->block())); 234 mm->tabs(m->tabs()); 235 236 p_stack.pop_back(); 237 238 return debubble(mm->block(), mm); 239 } 240 operator ()(SupportsRule * m)241 Statement* Cssize::operator()(SupportsRule* m) 242 { 243 if (!m->block()->length()) 244 { return m; } 245 246 if (parent()->statement_type() == Statement::RULESET) 247 { return bubble(m); } 248 249 p_stack.push_back(m); 250 251 SupportsRuleObj mm = SASS_MEMORY_NEW(SupportsRule, 252 m->pstate(), 253 m->condition(), 254 operator()(m->block())); 255 mm->tabs(m->tabs()); 256 257 p_stack.pop_back(); 258 259 return debubble(mm->block(), mm); 260 } 261 operator ()(AtRootRule * m)262 Statement* Cssize::operator()(AtRootRule* m) 263 { 264 bool tmp = false; 265 for (size_t i = 0, L = p_stack.size(); i < L; ++i) { 266 Statement* s = p_stack[i]; 267 tmp |= m->exclude_node(s); 268 } 269 270 if (!tmp && m->block()) 271 { 272 Block* bb = operator()(m->block()); 273 for (size_t i = 0, L = bb->length(); i < L; ++i) { 274 // (bb->elements())[i]->tabs(m->tabs()); 275 Statement_Obj stm = bb->at(i); 276 if (bubblable(stm)) stm->tabs(stm->tabs() + m->tabs()); 277 } 278 if (bb->length() && bubblable(bb->last())) bb->last()->group_end(m->group_end()); 279 return bb; 280 } 281 282 if (m->exclude_node(parent())) 283 { 284 return SASS_MEMORY_NEW(Bubble, m->pstate(), m); 285 } 286 287 return bubble(m); 288 } 289 bubble(AtRule * m)290 Statement* Cssize::bubble(AtRule* m) 291 { 292 Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); 293 ParentStatementObj new_rule = Cast<ParentStatement>(SASS_MEMORY_COPY(this->parent())); 294 new_rule->block(bb); 295 new_rule->tabs(this->parent()->tabs()); 296 new_rule->block()->concat(m->block()); 297 298 Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate()); 299 wrapper_block->append(new_rule); 300 AtRuleObj mm = SASS_MEMORY_NEW(AtRule, 301 m->pstate(), 302 m->keyword(), 303 m->selector(), 304 wrapper_block); 305 if (m->value()) mm->value(m->value()); 306 307 Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); 308 return bubble; 309 } 310 bubble(AtRootRule * m)311 Statement* Cssize::bubble(AtRootRule* m) 312 { 313 if (!m || !m->block()) return NULL; 314 Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); 315 ParentStatementObj new_rule = Cast<ParentStatement>(SASS_MEMORY_COPY(this->parent())); 316 Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); 317 if (new_rule) { 318 new_rule->block(bb); 319 new_rule->tabs(this->parent()->tabs()); 320 new_rule->block()->concat(m->block()); 321 wrapper_block->append(new_rule); 322 } 323 324 AtRootRule* mm = SASS_MEMORY_NEW(AtRootRule, 325 m->pstate(), 326 wrapper_block, 327 m->expression()); 328 Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); 329 return bubble; 330 } 331 bubble(SupportsRule * m)332 Statement* Cssize::bubble(SupportsRule* m) 333 { 334 StyleRuleObj parent = Cast<StyleRule>(SASS_MEMORY_COPY(this->parent())); 335 336 Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); 337 StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule, 338 parent->pstate(), 339 parent->selector(), 340 bb); 341 new_rule->tabs(parent->tabs()); 342 new_rule->block()->concat(m->block()); 343 344 Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); 345 wrapper_block->append(new_rule); 346 SupportsRule* mm = SASS_MEMORY_NEW(SupportsRule, 347 m->pstate(), 348 m->condition(), 349 wrapper_block); 350 351 mm->tabs(m->tabs()); 352 353 Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); 354 return bubble; 355 } 356 bubble(CssMediaRule * m)357 Statement* Cssize::bubble(CssMediaRule* m) 358 { 359 StyleRuleObj parent = Cast<StyleRule>(SASS_MEMORY_COPY(this->parent())); 360 361 Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); 362 StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule, 363 parent->pstate(), 364 parent->selector(), 365 bb); 366 new_rule->tabs(parent->tabs()); 367 new_rule->block()->concat(m->block()); 368 369 Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); 370 wrapper_block->append(new_rule); 371 CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, 372 m->pstate(), 373 wrapper_block); 374 mm->concat(m->elements()); 375 376 mm->tabs(m->tabs()); 377 378 return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); 379 } 380 bubblable(Statement * s)381 bool Cssize::bubblable(Statement* s) 382 { 383 return Cast<StyleRule>(s) || (s && s->bubbles()); 384 } 385 flatten(const Block * b)386 Block* Cssize::flatten(const Block* b) 387 { 388 Block* result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root()); 389 for (size_t i = 0, L = b->length(); i < L; ++i) { 390 Statement* ss = b->at(i); 391 if (const Block* bb = Cast<Block>(ss)) { 392 Block_Obj bs = flatten(bb); 393 for (size_t j = 0, K = bs->length(); j < K; ++j) { 394 result->append(bs->at(j)); 395 } 396 } 397 else { 398 result->append(ss); 399 } 400 } 401 return result; 402 } 403 slice_by_bubble(Block * b)404 sass::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block* b) 405 { 406 sass::vector<std::pair<bool, Block_Obj>> results; 407 408 for (size_t i = 0, L = b->length(); i < L; ++i) { 409 Statement_Obj value = b->at(i); 410 bool key = Cast<Bubble>(value) != NULL; 411 412 if (!results.empty() && results.back().first == key) 413 { 414 Block_Obj wrapper_block = results.back().second; 415 wrapper_block->append(value); 416 } 417 else 418 { 419 Block* wrapper_block = SASS_MEMORY_NEW(Block, value->pstate()); 420 wrapper_block->append(value); 421 results.push_back(std::make_pair(key, wrapper_block)); 422 } 423 } 424 return results; 425 } 426 debubble(Block * children,Statement * parent)427 Block* Cssize::debubble(Block* children, Statement* parent) 428 { 429 ParentStatementObj previous_parent; 430 sass::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children); 431 Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate()); 432 433 for (size_t i = 0, L = baz.size(); i < L; ++i) { 434 bool is_bubble = baz[i].first; 435 Block_Obj slice = baz[i].second; 436 437 if (!is_bubble) { 438 if (!parent) { 439 result->append(slice); 440 } 441 else if (previous_parent) { 442 previous_parent->block()->concat(slice); 443 } 444 else { 445 previous_parent = SASS_MEMORY_COPY(parent); 446 previous_parent->block(slice); 447 previous_parent->tabs(parent->tabs()); 448 449 result->append(previous_parent); 450 } 451 continue; 452 } 453 454 for (size_t j = 0, K = slice->length(); j < K; ++j) 455 { 456 Statement_Obj ss; 457 Statement_Obj stm = slice->at(j); 458 // this has to go now here (too bad) 459 Bubble_Obj node = Cast<Bubble>(stm); 460 461 CssMediaRule* rule1 = NULL; 462 CssMediaRule* rule2 = NULL; 463 if (parent) rule1 = Cast<CssMediaRule>(parent); 464 if (node) rule2 = Cast<CssMediaRule>(node->node()); 465 if (rule1 || rule2) { 466 ss = node->node(); 467 } 468 469 ss = node->node(); 470 471 if (!ss) { 472 continue; 473 } 474 475 ss->tabs(ss->tabs() + node->tabs()); 476 ss->group_end(node->group_end()); 477 478 Block_Obj bb = SASS_MEMORY_NEW(Block, 479 children->pstate(), 480 children->length(), 481 children->is_root()); 482 auto evaled = ss->perform(this); 483 if (evaled) bb->append(evaled); 484 485 Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, 486 children->pstate(), 487 children->length(), 488 children->is_root()); 489 490 Block* wrapper = flatten(bb); 491 wrapper_block->append(wrapper); 492 493 if (wrapper->length()) { 494 previous_parent = {}; 495 } 496 497 if (wrapper_block) { 498 result->append(wrapper_block); 499 } 500 } 501 } 502 503 return flatten(result); 504 } 505 append_block(Block * b,Block * cur)506 void Cssize::append_block(Block* b, Block* cur) 507 { 508 for (size_t i = 0, L = b->length(); i < L; ++i) { 509 Statement_Obj ith = b->at(i)->perform(this); 510 if (Block_Obj bb = Cast<Block>(ith)) { 511 for (size_t j = 0, K = bb->length(); j < K; ++j) { 512 cur->append(bb->at(j)); 513 } 514 } 515 else if (ith) { 516 cur->append(ith); 517 } 518 } 519 } 520 521 } 522