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 | |
14 | Cssize::Cssize(Context& ctx) |
15 | : traces(ctx.traces), |
16 | block_stack(BlockStack()), |
17 | p_stack(sass::vector<Statement*>()) |
18 | { } |
19 | |
20 | Statement* Cssize::parent() |
21 | { |
22 | return p_stack.size() ? p_stack.back() : block_stack.front(); |
23 | } |
24 | |
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(x: bb); |
30 | append_block(b, bb); |
31 | block_stack.pop_back(); |
32 | return bb.detach(); |
33 | } |
34 | |
35 | Statement* Cssize::operator()(Trace* t) |
36 | { |
37 | traces.push_back(x: Backtrace(t->pstate())); |
38 | auto result = t->block()->perform(op: this); |
39 | traces.pop_back(); |
40 | return result; |
41 | } |
42 | |
43 | Statement* Cssize::operator()(Declaration* d) |
44 | { |
45 | String_Obj property = Cast<String>(ptr: d->property()); |
46 | |
47 | if (Declaration* dd = Cast<Declaration>(ptr: parent())) { |
48 | String_Obj parent_property = Cast<String>(ptr: 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(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(is_indented__: d->is_indented()); |
64 | dd->tabs(tabs__: d->tabs()); |
65 | |
66 | p_stack.push_back(x: dd); |
67 | Block_Obj bb = d->block() ? operator()(b: 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(element: 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 | |
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(x: 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(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>(ptr: s); |
108 | s = s_obj->node(); |
109 | if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false; |
110 | else directive_exists = (Cast<AtRule>(ptr: 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>(ptr: rr); |
119 | empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate())); |
120 | result->append(element: 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(children: db, parent: rr); |
126 | for (size_t i = 0, L = ss->length(); i < L; ++i) { |
127 | result->append(element: ss->at(i)); |
128 | } |
129 | |
130 | return result; |
131 | } |
132 | |
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(name__: r->name()); |
141 | |
142 | return debubble(children: rr->block(), parent: rr); |
143 | } |
144 | |
145 | Statement* Cssize::operator()(StyleRule* r) |
146 | { |
147 | p_stack.push_back(x: 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()(b: 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>(ptr: bb) == NULL) { |
156 | error(msg: "Illegal nesting: Only properties may be nested beneath properties." , pstate: 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(is_root__: r->is_root()); |
164 | // rr->tabs(r->block()->tabs()); |
165 | p_stack.pop_back(); |
166 | |
167 | if (!rr->block()) { |
168 | error(msg: "Illegal nesting: Only properties may be nested beneath properties." , pstate: 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(element: s); |
177 | if (!bubblable(s)) props->append(element: s); |
178 | } |
179 | |
180 | if (props->length()) |
181 | { |
182 | Block_Obj pb = SASS_MEMORY_NEW(Block, rr->block()->pstate()); |
183 | pb->concat(v: props); |
184 | rr->block(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(tabs__: stm->tabs() + 1); |
190 | } |
191 | |
192 | rules->unshift(element: rr); |
193 | } |
194 | |
195 | Block* ptr = rules; |
196 | rules = debubble(children: 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(group_end__: true); |
208 | } |
209 | return rules; |
210 | } |
211 | |
212 | Statement* Cssize::operator()(Null* m) |
213 | { |
214 | return 0; |
215 | } |
216 | |
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(x: m); |
230 | |
231 | CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block()); |
232 | mm->concat(v: m->elements()); |
233 | mm->block(block__: operator()(b: m->block())); |
234 | mm->tabs(tabs__: m->tabs()); |
235 | |
236 | p_stack.pop_back(); |
237 | |
238 | return debubble(children: mm->block(), parent: mm); |
239 | } |
240 | |
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(x: m); |
250 | |
251 | SupportsRuleObj mm = SASS_MEMORY_NEW(SupportsRule, |
252 | m->pstate(), |
253 | m->condition(), |
254 | operator()(m->block())); |
255 | mm->tabs(tabs__: m->tabs()); |
256 | |
257 | p_stack.pop_back(); |
258 | |
259 | return debubble(children: mm->block(), parent: mm); |
260 | } |
261 | |
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()(b: 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(tabs__: stm->tabs() + m->tabs()); |
277 | } |
278 | if (bb->length() && bubblable(bb->last())) bb->last()->group_end(group_end__: m->group_end()); |
279 | return bb; |
280 | } |
281 | |
282 | if (m->exclude_node(s: parent())) |
283 | { |
284 | return SASS_MEMORY_NEW(Bubble, m->pstate(), m); |
285 | } |
286 | |
287 | return bubble(m); |
288 | } |
289 | |
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(block__: bb); |
295 | new_rule->tabs(tabs__: this->parent()->tabs()); |
296 | new_rule->block()->concat(v: m->block()); |
297 | |
298 | Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate()); |
299 | wrapper_block->append(element: 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(value__: m->value()); |
306 | |
307 | Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); |
308 | return bubble; |
309 | } |
310 | |
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(block__: bb); |
319 | new_rule->tabs(tabs__: this->parent()->tabs()); |
320 | new_rule->block()->concat(v: m->block()); |
321 | wrapper_block->append(element: 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 | |
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(tabs__: parent->tabs()); |
342 | new_rule->block()->concat(v: m->block()); |
343 | |
344 | Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); |
345 | wrapper_block->append(element: new_rule); |
346 | SupportsRule* mm = SASS_MEMORY_NEW(SupportsRule, |
347 | m->pstate(), |
348 | m->condition(), |
349 | wrapper_block); |
350 | |
351 | mm->tabs(tabs__: m->tabs()); |
352 | |
353 | Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); |
354 | return bubble; |
355 | } |
356 | |
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(tabs__: parent->tabs()); |
367 | new_rule->block()->concat(v: m->block()); |
368 | |
369 | Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); |
370 | wrapper_block->append(element: new_rule); |
371 | CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, |
372 | m->pstate(), |
373 | wrapper_block); |
374 | mm->concat(v: m->elements()); |
375 | |
376 | mm->tabs(tabs__: m->tabs()); |
377 | |
378 | return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); |
379 | } |
380 | |
381 | bool Cssize::bubblable(Statement* s) |
382 | { |
383 | return Cast<StyleRule>(ptr: s) || (s && s->bubbles()); |
384 | } |
385 | |
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>(ptr: ss)) { |
392 | Block_Obj bs = flatten(b: bb); |
393 | for (size_t j = 0, K = bs->length(); j < K; ++j) { |
394 | result->append(element: bs->at(i: j)); |
395 | } |
396 | } |
397 | else { |
398 | result->append(element: ss); |
399 | } |
400 | } |
401 | return result; |
402 | } |
403 | |
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>(ptr: value) != NULL; |
411 | |
412 | if (!results.empty() && results.back().first == key) |
413 | { |
414 | Block_Obj wrapper_block = results.back().second; |
415 | wrapper_block->append(element: value); |
416 | } |
417 | else |
418 | { |
419 | Block* wrapper_block = SASS_MEMORY_NEW(Block, value->pstate()); |
420 | wrapper_block->append(element: value); |
421 | results.push_back(x: std::make_pair(x&: key, y&: wrapper_block)); |
422 | } |
423 | } |
424 | return results; |
425 | } |
426 | |
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(b: 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(element: slice); |
440 | } |
441 | else if (previous_parent) { |
442 | previous_parent->block()->concat(v: slice); |
443 | } |
444 | else { |
445 | previous_parent = SASS_MEMORY_COPY(parent); |
446 | previous_parent->block(block__: slice); |
447 | previous_parent->tabs(tabs__: parent->tabs()); |
448 | |
449 | result->append(element: 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(i: j); |
458 | // this has to go now here (too bad) |
459 | Bubble_Obj node = Cast<Bubble>(ptr: stm); |
460 | |
461 | CssMediaRule* rule1 = NULL; |
462 | CssMediaRule* rule2 = NULL; |
463 | if (parent) rule1 = Cast<CssMediaRule>(ptr: parent); |
464 | if (node) rule2 = Cast<CssMediaRule>(ptr: 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(tabs__: ss->tabs() + node->tabs()); |
476 | ss->group_end(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(op: this); |
483 | if (evaled) bb->append(element: 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(b: bb); |
491 | wrapper_block->append(element: wrapper); |
492 | |
493 | if (wrapper->length()) { |
494 | previous_parent = {}; |
495 | } |
496 | |
497 | if (wrapper_block) { |
498 | result->append(element: wrapper_block); |
499 | } |
500 | } |
501 | } |
502 | |
503 | return flatten(b: result); |
504 | } |
505 | |
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(op: this); |
510 | if (Block_Obj bb = Cast<Block>(ptr: ith)) { |
511 | for (size_t j = 0, K = bb->length(); j < K; ++j) { |
512 | cur->append(element: bb->at(i: j)); |
513 | } |
514 | } |
515 | else if (ith) { |
516 | cur->append(element: ith); |
517 | } |
518 | } |
519 | } |
520 | |
521 | } |
522 | |