Initial commit
[yaffs-website] / node_modules / node-sass / src / libsass / src / check_nesting.cpp
1 #include "sass.hpp"
2 #include <vector>
3
4 #include "check_nesting.hpp"
5
6 namespace Sass {
7
8   CheckNesting::CheckNesting()
9   : parents(std::vector<Statement_Ptr>()),
10     parent(0),
11     current_mixin_definition(0)
12   { }
13
14   Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)
15   {
16     Statement_Ptr old_parent = this->parent;
17
18     if (At_Root_Block_Ptr root = Cast<At_Root_Block>(parent)) {
19       std::vector<Statement_Ptr> old_parents = this->parents;
20       std::vector<Statement_Ptr> new_parents;
21
22       for (size_t i = 0, L = this->parents.size(); i < L; i++) {
23         Statement_Ptr p = this->parents.at(i);
24         if (!root->exclude_node(p)) {
25           new_parents.push_back(p);
26         }
27       }
28       this->parents = new_parents;
29
30       for (size_t i = this->parents.size(); i > 0; i--) {
31         Statement_Ptr p = 0;
32         Statement_Ptr gp = 0;
33         if (i > 0) p = this->parents.at(i - 1);
34         if (i > 1) gp = this->parents.at(i - 2);
35
36         if (!this->is_transparent_parent(p, gp)) {
37           this->parent = p;
38           break;
39         }
40       }
41
42       At_Root_Block_Ptr ar = Cast<At_Root_Block>(parent);
43       Statement_Ptr ret = this->visit_children(ar->block());
44
45       this->parent = old_parent;
46       this->parents = old_parents;
47
48       return ret;
49     }
50
51     if (!this->is_transparent_parent(parent, old_parent)) {
52       this->parent = parent;
53     }
54
55     this->parents.push_back(parent);
56
57     Block_Ptr b = Cast<Block>(parent);
58
59     if (!b) {
60       if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {
61         b = bb->block();
62       }
63     }
64
65     if (b) {
66       for (auto n : b->elements()) {
67         n->perform(this);
68       }
69     }
70     this->parent = old_parent;
71     this->parents.pop_back();
72
73     return b;
74   }
75
76
77   Statement_Ptr CheckNesting::operator()(Block_Ptr b)
78   {
79     return this->visit_children(b);
80   }
81
82   Statement_Ptr CheckNesting::operator()(Definition_Ptr n)
83   {
84     if (!this->should_visit(n)) return NULL;
85     if (!is_mixin(n)) {
86       visit_children(n);
87       return n;
88     }
89
90     Definition_Ptr old_mixin_definition = this->current_mixin_definition;
91     this->current_mixin_definition = n;
92
93     visit_children(n);
94
95     this->current_mixin_definition = old_mixin_definition;
96
97     return n;
98   }
99
100   Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s)
101   {
102     Block_Ptr b1 = Cast<Block>(s);
103     Has_Block_Ptr b2 = Cast<Has_Block>(s);
104     return b1 || b2 ? visit_children(s) : s;
105   }
106
107   bool CheckNesting::should_visit(Statement_Ptr node)
108   {
109     if (!this->parent) return true;
110
111     if (Cast<Content>(node))
112     { this->invalid_content_parent(this->parent); }
113
114     if (is_charset(node))
115     { this->invalid_charset_parent(this->parent); }
116
117     if (Cast<Extension>(node))
118     { this->invalid_extend_parent(this->parent); }
119
120     // if (Cast<Import>(node))
121     // { this->invalid_import_parent(this->parent); }
122
123     if (this->is_mixin(node))
124     { this->invalid_mixin_definition_parent(this->parent); }
125
126     if (this->is_function(node))
127     { this->invalid_function_parent(this->parent); }
128
129     if (this->is_function(this->parent))
130     { this->invalid_function_child(node); }
131
132     if (Cast<Declaration>(node))
133     { this->invalid_prop_parent(this->parent); }
134
135     if (Cast<Declaration>(this->parent))
136     { this->invalid_prop_child(node); }
137
138     if (Cast<Return>(node))
139     { this->invalid_return_parent(this->parent); }
140
141     return true;
142   }
143
144   void CheckNesting::invalid_content_parent(Statement_Ptr parent)
145   {
146     if (!this->current_mixin_definition) {
147       throw Exception::InvalidSass(
148         parent->pstate(),
149         "@content may only be used within a mixin."
150       );
151     }
152   }
153
154   void CheckNesting::invalid_charset_parent(Statement_Ptr parent)
155   {
156     if (!(
157         is_root_node(parent)
158     )) {
159       throw Exception::InvalidSass(
160         parent->pstate(),
161         "@charset may only be used at the root of a document."
162       );
163     }
164   }
165
166   void CheckNesting::invalid_extend_parent(Statement_Ptr parent)
167   {
168     if (!(
169         Cast<Ruleset>(parent) ||
170         Cast<Mixin_Call>(parent) ||
171         is_mixin(parent)
172     )) {
173       throw Exception::InvalidSass(
174         parent->pstate(),
175         "Extend directives may only be used within rules."
176       );
177     }
178   }
179
180   // void CheckNesting::invalid_import_parent(Statement_Ptr parent)
181   // {
182   //   for (auto pp : this->parents) {
183   //     if (
184   //         Cast<Each>(pp) ||
185   //         Cast<For>(pp) ||
186   //         Cast<If>(pp) ||
187   //         Cast<While>(pp) ||
188   //         Cast<Trace>(pp) ||
189   //         Cast<Mixin_Call>(pp) ||
190   //         is_mixin(pp)
191   //     ) {
192   //       throw Exception::InvalidSass(
193   //         parent->pstate(),
194   //         "Import directives may not be defined within control directives or other mixins."
195   //       );
196   //     }
197   //   }
198
199   //   if (this->is_root_node(parent)) {
200   //     return;
201   //   }
202
203   //   if (false/*n.css_import?*/) {
204   //     throw Exception::InvalidSass(
205   //       parent->pstate(),
206   //       "CSS import directives may only be used at the root of a document."
207   //     );
208   //   }
209   // }
210
211   void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent)
212   {
213     for (Statement_Ptr pp : this->parents) {
214       if (
215           Cast<Each>(pp) ||
216           Cast<For>(pp) ||
217           Cast<If>(pp) ||
218           Cast<While>(pp) ||
219           Cast<Trace>(pp) ||
220           Cast<Mixin_Call>(pp) ||
221           is_mixin(pp)
222       ) {
223         throw Exception::InvalidSass(
224           parent->pstate(),
225           "Mixins may not be defined within control directives or other mixins."
226         );
227       }
228     }
229   }
230
231   void CheckNesting::invalid_function_parent(Statement_Ptr parent)
232   {
233     for (Statement_Ptr pp : this->parents) {
234       if (
235           Cast<Each>(pp) ||
236           Cast<For>(pp) ||
237           Cast<If>(pp) ||
238           Cast<While>(pp) ||
239           Cast<Trace>(pp) ||
240           Cast<Mixin_Call>(pp) ||
241           is_mixin(pp)
242       ) {
243         throw Exception::InvalidSass(
244           parent->pstate(),
245           "Functions may not be defined within control directives or other mixins."
246         );
247       }
248     }
249   }
250
251   void CheckNesting::invalid_function_child(Statement_Ptr child)
252   {
253     if (!(
254         Cast<Each>(child) ||
255         Cast<For>(child) ||
256         Cast<If>(child) ||
257         Cast<While>(child) ||
258         Cast<Trace>(child) ||
259         Cast<Comment>(child) ||
260         Cast<Debug>(child) ||
261         Cast<Return>(child) ||
262         Cast<Variable>(child) ||
263         // Ruby Sass doesn't distinguish variables and assignments
264         Cast<Assignment>(child) ||
265         Cast<Warning>(child) ||
266         Cast<Error>(child)
267     )) {
268       throw Exception::InvalidSass(
269         child->pstate(),
270         "Functions can only contain variable declarations and control directives."
271       );
272     }
273   }
274
275   void CheckNesting::invalid_prop_child(Statement_Ptr child)
276   {
277     if (!(
278         Cast<Each>(child) ||
279         Cast<For>(child) ||
280         Cast<If>(child) ||
281         Cast<While>(child) ||
282         Cast<Trace>(child) ||
283         Cast<Comment>(child) ||
284         Cast<Declaration>(child) ||
285         Cast<Mixin_Call>(child)
286     )) {
287       throw Exception::InvalidSass(
288         child->pstate(),
289         "Illegal nesting: Only properties may be nested beneath properties."
290       );
291     }
292   }
293
294   void CheckNesting::invalid_prop_parent(Statement_Ptr parent)
295   {
296     if (!(
297         is_mixin(parent) ||
298         is_directive_node(parent) ||
299         Cast<Ruleset>(parent) ||
300         Cast<Keyframe_Rule>(parent) ||
301         Cast<Declaration>(parent) ||
302         Cast<Mixin_Call>(parent)
303     )) {
304       throw Exception::InvalidSass(
305         parent->pstate(),
306         "Properties are only allowed within rules, directives, mixin includes, or other properties."
307       );
308     }
309   }
310
311   void CheckNesting::invalid_return_parent(Statement_Ptr parent)
312   {
313     if (!this->is_function(parent)) {
314       throw Exception::InvalidSass(
315         parent->pstate(),
316         "@return may only be used within a function."
317       );
318     }
319   }
320
321   bool CheckNesting::is_transparent_parent(Statement_Ptr parent, Statement_Ptr grandparent)
322   {
323     bool parent_bubbles = parent && parent->bubbles();
324
325     bool valid_bubble_node = parent_bubbles &&
326                              !is_root_node(grandparent) &&
327                              !is_at_root_node(grandparent);
328
329     return Cast<Import>(parent) ||
330            Cast<Each>(parent) ||
331            Cast<For>(parent) ||
332            Cast<If>(parent) ||
333            Cast<While>(parent) ||
334            Cast<Trace>(parent) ||
335            valid_bubble_node;
336   }
337
338   bool CheckNesting::is_charset(Statement_Ptr n)
339   {
340     Directive_Ptr d = Cast<Directive>(n);
341     return d && d->keyword() == "charset";
342   }
343
344   bool CheckNesting::is_mixin(Statement_Ptr n)
345   {
346     Definition_Ptr def = Cast<Definition>(n);
347     return def && def->type() == Definition::MIXIN;
348   }
349
350   bool CheckNesting::is_function(Statement_Ptr n)
351   {
352     Definition_Ptr def = Cast<Definition>(n);
353     return def && def->type() == Definition::FUNCTION;
354   }
355
356   bool CheckNesting::is_root_node(Statement_Ptr n)
357   {
358     if (Cast<Ruleset>(n)) return false;
359
360     Block_Ptr b = Cast<Block>(n);
361     return b && b->is_root();
362   }
363
364   bool CheckNesting::is_at_root_node(Statement_Ptr n)
365   {
366     return Cast<At_Root_Block>(n) != NULL;
367   }
368
369   bool CheckNesting::is_directive_node(Statement_Ptr n)
370   {
371     return Cast<Directive>(n) ||
372            Cast<Import>(n) ||
373            Cast<Media_Block>(n) ||
374            Cast<Supports_Block>(n);
375   }
376 }