Initial commit
[yaffs-website] / node_modules / node-sass / src / libsass / src / expand.cpp
1 #include "sass.hpp"
2 #include <iostream>
3 #include <typeinfo>
4
5 #include "ast.hpp"
6 #include "expand.hpp"
7 #include "bind.hpp"
8 #include "eval.hpp"
9 #include "backtrace.hpp"
10 #include "context.hpp"
11 #include "parser.hpp"
12 #include "sass_functions.hpp"
13
14 namespace Sass {
15
16   // simple endless recursion protection
17   const size_t maxRecursion = 500;
18
19   Expand::Expand(Context& ctx, Env* env, Backtrace* bt, std::vector<Selector_List_Obj>* stack)
20   : ctx(ctx),
21     eval(Eval(*this)),
22     recursions(0),
23     in_keyframes(false),
24     at_root_without_rule(false),
25     old_at_root_without_rule(false),
26     env_stack(std::vector<Env*>()),
27     block_stack(std::vector<Block_Ptr>()),
28     call_stack(std::vector<AST_Node_Obj>()),
29     selector_stack(std::vector<Selector_List_Obj>()),
30     media_block_stack(std::vector<Media_Block_Ptr>()),
31     backtrace_stack(std::vector<Backtrace*>())
32   {
33     env_stack.push_back(0);
34     env_stack.push_back(env);
35     block_stack.push_back(0);
36     call_stack.push_back(0);
37     if (stack == NULL) { selector_stack.push_back(0); }
38     else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); }
39     media_block_stack.push_back(0);
40     backtrace_stack.push_back(0);
41     backtrace_stack.push_back(bt);
42   }
43
44   Env* Expand::environment()
45   {
46     if (env_stack.size() > 0)
47       return env_stack.back();
48     return 0;
49   }
50
51   Selector_List_Obj Expand::selector()
52   {
53     if (selector_stack.size() > 0)
54       return selector_stack.back();
55     return 0;
56   }
57
58   Backtrace* Expand::backtrace()
59   {
60     if (backtrace_stack.size() > 0)
61       return backtrace_stack.back();
62     return 0;
63   }
64
65   // blocks create new variable scopes
66   Block_Ptr Expand::operator()(Block_Ptr b)
67   {
68     // create new local environment
69     // set the current env as parent
70     Env env(environment());
71     // copy the block object (add items later)
72     Block_Obj bb = SASS_MEMORY_NEW(Block,
73                                 b->pstate(),
74                                 b->length(),
75                                 b->is_root());
76     // setup block and env stack
77     this->block_stack.push_back(bb);
78     this->env_stack.push_back(&env);
79     // operate on block
80     // this may throw up!
81     this->append_block(b);
82     // revert block and env stack
83     this->block_stack.pop_back();
84     this->env_stack.pop_back();
85     // return copy
86     return bb.detach();
87   }
88
89   Statement_Ptr Expand::operator()(Ruleset_Ptr r)
90   {
91     LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule);
92
93     if (in_keyframes) {
94       Block_Ptr bb = operator()(r->block());
95       Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);
96       if (r->selector()) {
97         if (Selector_List_Ptr s = r->selector()) {
98           selector_stack.push_back(0);
99           k->name(s->eval(eval));
100           selector_stack.pop_back();
101         }
102       }
103       return k.detach();
104     }
105
106     // reset when leaving scope
107     LOCAL_FLAG(at_root_without_rule, false);
108
109     // `&` is allowed in `@at-root`!
110     bool has_parent_selector = false;
111     for (size_t i = 0, L = selector_stack.size(); i < L && !has_parent_selector; i++) {
112       Selector_List_Obj ll = selector_stack.at(i);
113       has_parent_selector = ll != 0 && ll->length() > 0;
114     }
115
116     Selector_List_Obj sel = r->selector();
117     if (sel) sel = sel->eval(eval);
118
119     // check for parent selectors in base level rules
120     if (r->is_root()) {
121       if (Selector_List_Ptr selector_list = Cast<Selector_List>(r->selector())) {
122         for (Complex_Selector_Obj complex_selector : selector_list->elements()) {
123           Complex_Selector_Ptr tail = complex_selector;
124           while (tail) {
125             if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {
126               Parent_Selector_Ptr ptr = Cast<Parent_Selector>(header);
127               if (ptr == NULL || (!ptr->real() || has_parent_selector)) continue;
128               std::string sel_str(complex_selector->to_string(ctx.c_options));
129               error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace());
130             }
131             tail = tail->tail();
132           }
133         }
134       }
135     }
136     else {
137       if (sel->length() == 0 || sel->has_parent_ref()) {
138         if (sel->has_real_parent_ref() && !has_parent_selector) {
139           error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), backtrace());
140         }
141       }
142     }
143
144     selector_stack.push_back(sel);
145     Env env(environment());
146     if (block_stack.back()->is_root()) {
147       env_stack.push_back(&env);
148     }
149     sel->set_media_block(media_block_stack.back());
150     Block_Obj blk = 0;
151     if (r->block()) blk = operator()(r->block());
152     Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset,
153                                   r->pstate(),
154                                   sel,
155                                   blk);
156     selector_stack.pop_back();
157     if (block_stack.back()->is_root()) {
158       env_stack.pop_back();
159     }
160
161     rr->is_root(r->is_root());
162     rr->tabs(r->tabs());
163
164     return rr;
165   }
166
167   Statement_Ptr Expand::operator()(Supports_Block_Ptr f)
168   {
169     Expression_Obj condition = f->condition()->perform(&eval);
170     Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block,
171                                        f->pstate(),
172                                        Cast<Supports_Condition>(condition),
173                                        operator()(f->block()));
174     return ff.detach();
175   }
176
177   Statement_Ptr Expand::operator()(Media_Block_Ptr m)
178   {
179     media_block_stack.push_back(m);
180     Expression_Obj mq = m->media_queries()->perform(&eval);
181     std::string str_mq(mq->to_string(ctx.c_options));
182     char* str = sass_copy_c_string(str_mq.c_str());
183     ctx.strings.push_back(str);
184     Parser p(Parser::from_c_str(str, ctx, mq->pstate()));
185     mq = p.parse_media_queries(); // re-assign now
186     List_Obj ls = Cast<List>(mq->perform(&eval));
187     Block_Obj blk = operator()(m->block());
188     Media_Block_Ptr mm = SASS_MEMORY_NEW(Media_Block,
189                                       m->pstate(),
190                                       ls,
191                                       blk);
192     media_block_stack.pop_back();
193     mm->tabs(m->tabs());
194     return mm;
195   }
196
197   Statement_Ptr Expand::operator()(At_Root_Block_Ptr a)
198   {
199     Block_Obj ab = a->block();
200     Expression_Obj ae = a->expression();
201
202     if (ae) ae = ae->perform(&eval);
203     else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate());
204
205     LOCAL_FLAG(at_root_without_rule, true);
206     LOCAL_FLAG(in_keyframes, false);
207
208                                        ;
209
210     Block_Obj bb = ab ? operator()(ab) : NULL;
211     At_Root_Block_Obj aa = SASS_MEMORY_NEW(At_Root_Block,
212                                         a->pstate(),
213                                         bb,
214                                         Cast<At_Root_Query>(ae));
215     return aa.detach();
216   }
217
218   Statement_Ptr Expand::operator()(Directive_Ptr a)
219   {
220     LOCAL_FLAG(in_keyframes, a->is_keyframes());
221     Block_Ptr ab = a->block();
222     Selector_List_Ptr as = a->selector();
223     Expression_Ptr av = a->value();
224     selector_stack.push_back(0);
225     if (av) av = av->perform(&eval);
226     if (as) as = eval(as);
227     selector_stack.pop_back();
228     Block_Ptr bb = ab ? operator()(ab) : NULL;
229     Directive_Ptr aa = SASS_MEMORY_NEW(Directive,
230                                   a->pstate(),
231                                   a->keyword(),
232                                   as,
233                                   bb,
234                                   av);
235     return aa;
236   }
237
238   Statement_Ptr Expand::operator()(Declaration_Ptr d)
239   {
240     Block_Obj ab = d->block();
241     String_Obj old_p = d->property();
242     Expression_Obj prop = old_p->perform(&eval);
243     String_Obj new_p = Cast<String>(prop);
244     // we might get a color back
245     if (!new_p) {
246       std::string str(prop->to_string(ctx.c_options));
247       new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str);
248     }
249     Expression_Obj value = d->value()->perform(&eval);
250     Block_Obj bb = ab ? operator()(ab) : NULL;
251     if (!bb) {
252       if (!value || (value->is_invisible() && !d->is_important())) return 0;
253     }
254     Declaration_Ptr decl = SASS_MEMORY_NEW(Declaration,
255                                         d->pstate(),
256                                         new_p,
257                                         value,
258                                         d->is_important(),
259                                         bb);
260     decl->tabs(d->tabs());
261     return decl;
262   }
263
264   Statement_Ptr Expand::operator()(Assignment_Ptr a)
265   {
266     Env* env = environment();
267     std::string var(a->variable());
268     if (a->is_global()) {
269       if (a->is_default()) {
270         if (env->has_global(var)) {
271           Expression_Obj e = Cast<Expression>(env->get_global(var));
272           if (!e || e->concrete_type() == Expression::NULL_VAL) {
273             env->set_global(var, a->value()->perform(&eval));
274           }
275         }
276         else {
277           env->set_global(var, a->value()->perform(&eval));
278         }
279       }
280       else {
281         env->set_global(var, a->value()->perform(&eval));
282       }
283     }
284     else if (a->is_default()) {
285       if (env->has_lexical(var)) {
286         auto cur = env;
287         while (cur && cur->is_lexical()) {
288           if (cur->has_local(var)) {
289             if (AST_Node_Obj node = cur->get_local(var)) {
290               Expression_Obj e = Cast<Expression>(node);
291               if (!e || e->concrete_type() == Expression::NULL_VAL) {
292                 cur->set_local(var, a->value()->perform(&eval));
293               }
294             }
295             else {
296               throw std::runtime_error("Env not in sync");
297             }
298             return 0;
299           }
300           cur = cur->parent();
301         }
302         throw std::runtime_error("Env not in sync");
303       }
304       else if (env->has_global(var)) {
305         if (AST_Node_Obj node = env->get_global(var)) {
306           Expression_Obj e = Cast<Expression>(node);
307           if (!e || e->concrete_type() == Expression::NULL_VAL) {
308             env->set_global(var, a->value()->perform(&eval));
309           }
310         }
311       }
312       else if (env->is_lexical()) {
313         env->set_local(var, a->value()->perform(&eval));
314       }
315       else {
316         env->set_local(var, a->value()->perform(&eval));
317       }
318     }
319     else {
320       env->set_lexical(var, a->value()->perform(&eval));
321     }
322     return 0;
323   }
324
325   Statement_Ptr Expand::operator()(Import_Ptr imp)
326   {
327     Import_Obj result = SASS_MEMORY_NEW(Import, imp->pstate());
328     if (imp->import_queries() && imp->import_queries()->size()) {
329       Expression_Obj ex = imp->import_queries()->perform(&eval);
330       result->import_queries(Cast<List>(ex));
331     }
332     for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) {
333       result->urls().push_back(imp->urls()[i]->perform(&eval));
334     }
335     // all resources have been dropped for Input_Stubs
336     // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {}
337     return result.detach();
338   }
339
340   Statement_Ptr Expand::operator()(Import_Stub_Ptr i)
341   {
342     // get parent node from call stack
343     AST_Node_Obj parent = call_stack.back();
344     if (Cast<Block>(parent) == NULL) {
345       error("Import directives may not be used within control directives or mixins.", i->pstate());
346     }
347     // we don't seem to need that actually afterall
348     Sass_Import_Entry import = sass_make_import(
349       i->imp_path().c_str(),
350       i->abs_path().c_str(),
351       0, 0
352     );
353     ctx.import_stack.push_back(import);
354     const std::string& abs_path(i->resource().abs_path);
355     append_block(ctx.sheets.at(abs_path).root);
356     sass_delete_import(ctx.import_stack.back());
357     ctx.import_stack.pop_back();
358     return 0;
359   }
360
361   Statement_Ptr Expand::operator()(Warning_Ptr w)
362   {
363     // eval handles this too, because warnings may occur in functions
364     w->perform(&eval);
365     return 0;
366   }
367
368   Statement_Ptr Expand::operator()(Error_Ptr e)
369   {
370     // eval handles this too, because errors may occur in functions
371     e->perform(&eval);
372     return 0;
373   }
374
375   Statement_Ptr Expand::operator()(Debug_Ptr d)
376   {
377     // eval handles this too, because warnings may occur in functions
378     d->perform(&eval);
379     return 0;
380   }
381
382   Statement_Ptr Expand::operator()(Comment_Ptr c)
383   {
384     eval.is_in_comment = true;
385     Comment_Ptr rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important());
386     eval.is_in_comment = false;
387     // TODO: eval the text, once we're parsing/storing it as a String_Schema
388     return rv;
389   }
390
391   Statement_Ptr Expand::operator()(If_Ptr i)
392   {
393     Env env(environment(), true);
394     env_stack.push_back(&env);
395     call_stack.push_back(i);
396     Expression_Obj rv = i->predicate()->perform(&eval);
397     if (*rv) {
398       append_block(i->block());
399     }
400     else {
401       Block_Ptr alt = i->alternative();
402       if (alt) append_block(alt);
403     }
404     call_stack.pop_back();
405     env_stack.pop_back();
406     return 0;
407   }
408
409   // For does not create a new env scope
410   // But iteration vars are reset afterwards
411   Statement_Ptr Expand::operator()(For_Ptr f)
412   {
413     std::string variable(f->variable());
414     Expression_Obj low = f->lower_bound()->perform(&eval);
415     if (low->concrete_type() != Expression::NUMBER) {
416       throw Exception::TypeMismatch(*low, "integer");
417     }
418     Expression_Obj high = f->upper_bound()->perform(&eval);
419     if (high->concrete_type() != Expression::NUMBER) {
420       throw Exception::TypeMismatch(*high, "integer");
421     }
422     Number_Obj sass_start = Cast<Number>(low);
423     Number_Obj sass_end = Cast<Number>(high);
424     // check if units are valid for sequence
425     if (sass_start->unit() != sass_end->unit()) {
426       std::stringstream msg; msg << "Incompatible units: '"
427         << sass_start->unit() << "' and '"
428         << sass_end->unit() << "'.";
429       error(msg.str(), low->pstate(), backtrace());
430     }
431     double start = sass_start->value();
432     double end = sass_end->value();
433     // only create iterator once in this environment
434     Env env(environment(), true);
435     env_stack.push_back(&env);
436     call_stack.push_back(f);
437     Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), start, sass_end->unit());
438     env.set_local(variable, it);
439     Block_Ptr body = f->block();
440     if (start < end) {
441       if (f->is_inclusive()) ++end;
442       for (double i = start;
443            i < end;
444            ++i) {
445         it = SASS_MEMORY_COPY(it);
446         it->value(i);
447         env.set_local(variable, it);
448         append_block(body);
449       }
450     } else {
451       if (f->is_inclusive()) --end;
452       for (double i = start;
453            i > end;
454            --i) {
455         it = SASS_MEMORY_COPY(it);
456         it->value(i);
457         env.set_local(variable, it);
458         append_block(body);
459       }
460     }
461     call_stack.pop_back();
462     env_stack.pop_back();
463     return 0;
464   }
465
466   // Eval does not create a new env scope
467   // But iteration vars are reset afterwards
468   Statement_Ptr Expand::operator()(Each_Ptr e)
469   {
470     std::vector<std::string> variables(e->variables());
471     Expression_Obj expr = e->list()->perform(&eval);
472     List_Obj list = 0;
473     Map_Obj map;
474     if (expr->concrete_type() == Expression::MAP) {
475       map = Cast<Map>(expr);
476     }
477     else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {
478       Listize listize;
479       Expression_Obj rv = ls->perform(&listize);
480       list = Cast<List>(rv);
481     }
482     else if (expr->concrete_type() != Expression::LIST) {
483       list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);
484       list->append(expr);
485     }
486     else {
487       list = Cast<List>(expr);
488     }
489     // remember variables and then reset them
490     Env env(environment(), true);
491     env_stack.push_back(&env);
492     call_stack.push_back(e);
493     Block_Ptr body = e->block();
494
495     if (map) {
496       for (auto key : map->keys()) {
497         Expression_Obj k = key->perform(&eval);
498         Expression_Obj v = map->at(key)->perform(&eval);
499
500         if (variables.size() == 1) {
501           List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);
502           variable->append(k);
503           variable->append(v);
504           env.set_local(variables[0], variable);
505         } else {
506           env.set_local(variables[0], k);
507           env.set_local(variables[1], v);
508         }
509         append_block(body);
510       }
511     }
512     else {
513       // bool arglist = list->is_arglist();
514       if (list->length() == 1 && Cast<Selector_List>(list)) {
515         list = Cast<List>(list);
516       }
517       for (size_t i = 0, L = list->length(); i < L; ++i) {
518         Expression_Obj e = list->at(i);
519         // unwrap value if the expression is an argument
520         if (Argument_Obj arg = Cast<Argument>(e)) e = arg->value();
521         // check if we got passed a list of args (investigate)
522         if (List_Obj scalars = Cast<List>(e)) {
523           if (variables.size() == 1) {
524             List_Obj var = scalars;
525             // if (arglist) var = (*scalars)[0];
526             env.set_local(variables[0], var);
527           } else {
528             for (size_t j = 0, K = variables.size(); j < K; ++j) {
529               Expression_Obj res = j >= scalars->length()
530                 ? SASS_MEMORY_NEW(Null, expr->pstate())
531                 : (*scalars)[j]->perform(&eval);
532               env.set_local(variables[j], res);
533             }
534           }
535         } else {
536           if (variables.size() > 0) {
537             env.set_local(variables.at(0), e);
538             for (size_t j = 1, K = variables.size(); j < K; ++j) {
539               Expression_Obj res = SASS_MEMORY_NEW(Null, expr->pstate());
540               env.set_local(variables[j], res);
541             }
542           }
543         }
544         append_block(body);
545       }
546     }
547     call_stack.pop_back();
548     env_stack.pop_back();
549     return 0;
550   }
551
552   Statement_Ptr Expand::operator()(While_Ptr w)
553   {
554     Expression_Obj pred = w->predicate();
555     Block_Ptr body = w->block();
556     Env env(environment(), true);
557     env_stack.push_back(&env);
558     call_stack.push_back(w);
559     Expression_Obj cond = pred->perform(&eval);
560     while (!cond->is_false()) {
561       append_block(body);
562       cond = pred->perform(&eval);
563     }
564     call_stack.pop_back();
565     env_stack.pop_back();
566     return 0;
567   }
568
569   Statement_Ptr Expand::operator()(Return_Ptr r)
570   {
571     error("@return may only be used within a function", r->pstate(), backtrace());
572     return 0;
573   }
574
575
576   void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) {
577
578     if (Selector_List_Obj sl = Cast<Selector_List>(s)) {
579       for (Complex_Selector_Obj complex_selector : sl->elements()) {
580         Complex_Selector_Obj tail = complex_selector;
581         while (tail) {
582           if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {
583             if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others
584             std::string sel_str(complex_selector->to_string(ctx.c_options));
585             error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), backtrace());
586           }
587           tail = tail->tail();
588         }
589       }
590     }
591
592
593     Selector_List_Obj contextualized = Cast<Selector_List>(s->perform(&eval));
594     if (contextualized == false) return;
595     for (auto complex_sel : contextualized->elements()) {
596       Complex_Selector_Obj c = complex_sel;
597       if (!c->head() || c->tail()) {
598         std::string sel_str(contextualized->to_string(ctx.c_options));
599         error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace());
600       }
601       Compound_Selector_Obj placeholder = c->head();
602       if (contextualized->is_optional()) placeholder->is_optional(true);
603       for (size_t i = 0, L = extender->length(); i < L; ++i) {
604         Complex_Selector_Obj sel = (*extender)[i];
605         if (!(sel->head() && sel->head()->length() > 0 &&
606             Cast<Parent_Selector>((*sel->head())[0])))
607         {
608           Compound_Selector_Obj hh = SASS_MEMORY_NEW(Compound_Selector, (*extender)[i]->pstate());
609           hh->media_block((*extender)[i]->media_block());
610           Complex_Selector_Obj ssel = SASS_MEMORY_NEW(Complex_Selector, (*extender)[i]->pstate());
611           ssel->media_block((*extender)[i]->media_block());
612           if (sel->has_line_feed()) ssel->has_line_feed(true);
613           Parent_Selector_Obj ps = SASS_MEMORY_NEW(Parent_Selector, (*extender)[i]->pstate());
614           ps->media_block((*extender)[i]->media_block());
615           hh->append(ps);
616           ssel->tail(sel);
617           ssel->head(hh);
618           sel = ssel;
619         }
620         // if (c->has_line_feed()) sel->has_line_feed(true);
621         ctx.subset_map.put(placeholder, std::make_pair(sel, placeholder));
622       }
623     }
624
625   }
626
627   Statement* Expand::operator()(Extension_Ptr e)
628   {
629     if (Selector_List_Ptr extender = selector()) {
630       Selector_List_Ptr sl = e->selector();
631       // abort on invalid selector
632       if (sl == NULL) return NULL;
633       if (Selector_Schema_Ptr schema = sl->schema()) {
634         if (schema->has_real_parent_ref()) {
635           // put root block on stack again (ignore parents)
636           // selector schema must not connect in eval!
637           block_stack.push_back(block_stack.at(1));
638           sl = eval(sl->schema());
639           block_stack.pop_back();
640         } else {
641           selector_stack.push_back(0);
642           sl = eval(sl->schema());
643           selector_stack.pop_back();
644         }
645       }
646       for (Complex_Selector_Obj cs : sl->elements()) {
647         if (!cs.isNull() && !cs->head().isNull()) {
648           cs->head()->media_block(media_block_stack.back());
649         }
650       }
651       selector_stack.push_back(0);
652       expand_selector_list(sl, extender);
653       selector_stack.pop_back();
654     }
655     return 0;
656   }
657
658   Statement_Ptr Expand::operator()(Definition_Ptr d)
659   {
660     Env* env = environment();
661     Definition_Obj dd = SASS_MEMORY_COPY(d);
662     env->local_frame()[d->name() +
663                         (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;
664
665     if (d->type() == Definition::FUNCTION && (
666       Prelexer::calc_fn_call(d->name().c_str()) ||
667       d->name() == "element"    ||
668       d->name() == "expression" ||
669       d->name() == "url"
670     )) {
671       deprecated(
672         "Naming a function \"" + d->name() + "\" is disallowed",
673         "This name conflicts with an existing CSS function with special parse rules.",
674          d->pstate()
675       );
676     }
677
678     // set the static link so we can have lexical scoping
679     dd->environment(env);
680     return 0;
681   }
682
683   Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)
684   {
685
686     if (recursions > maxRecursion) {
687       throw Exception::StackError(*c);
688     }
689
690     recursions ++;
691
692     Env* env = environment();
693     std::string full_name(c->name() + "[m]");
694     if (!env->has(full_name)) {
695       error("no mixin named " + c->name(), c->pstate(), backtrace());
696     }
697     Definition_Obj def = Cast<Definition>((*env)[full_name]);
698     Block_Obj body = def->block();
699     Parameters_Obj params = def->parameters();
700
701     if (c->block() && c->name() != "@content" && !body->has_content()) {
702       error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), backtrace());
703     }
704     Expression_Obj rv = c->arguments()->perform(&eval);
705     Arguments_Obj args = Cast<Arguments>(rv);
706     Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`");
707     backtrace_stack.push_back(&new_bt);
708     ctx.callee_stack.push_back({
709       c->name().c_str(),
710       c->pstate().path,
711       c->pstate().line + 1,
712       c->pstate().column + 1,
713       SASS_CALLEE_MIXIN,
714       { env }
715     });
716
717     Env new_env(def->environment());
718     env_stack.push_back(&new_env);
719     if (c->block()) {
720       // represent mixin content blocks as thunks/closures
721       Definition_Obj thunk = SASS_MEMORY_NEW(Definition,
722                                           c->pstate(),
723                                           "@content",
724                                           SASS_MEMORY_NEW(Parameters, c->pstate()),
725                                           c->block(),
726                                           Definition::MIXIN);
727       thunk->environment(env);
728       new_env.local_frame()["@content[m]"] = thunk;
729     }
730
731     bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval);
732
733     Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate());
734     Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block);
735
736
737     block_stack.push_back(trace_block);
738     for (auto bb : body->elements()) {
739       Statement_Obj ith = bb->perform(this);
740       if (ith) trace->block()->append(ith);
741     }
742     block_stack.pop_back();
743
744     env_stack.pop_back();
745     backtrace_stack.pop_back();
746     ctx.callee_stack.pop_back();
747
748     recursions --;
749     return trace.detach();
750   }
751
752   Statement_Ptr Expand::operator()(Content_Ptr c)
753   {
754     Env* env = environment();
755     // convert @content directives into mixin calls to the underlying thunk
756     if (!env->has("@content[m]")) return 0;
757
758     if (block_stack.back()->is_root()) {
759       selector_stack.push_back(0);
760     }
761
762     Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call,
763                                        c->pstate(),
764                                        "@content",
765                                        SASS_MEMORY_NEW(Arguments, c->pstate()));
766
767     Trace_Obj trace = Cast<Trace>(call->perform(this));
768
769     if (block_stack.back()->is_root()) {
770       selector_stack.pop_back();
771     }
772
773     return trace.detach();
774   }
775
776   // produce an error if something is not implemented
777   inline Statement_Ptr Expand::fallback_impl(AST_Node_Ptr n)
778   {
779     std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();
780     String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err);
781     error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace());
782     return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), msg);
783   }
784
785   // process and add to last block on stack
786   inline void Expand::append_block(Block_Ptr b)
787   {
788     if (b->is_root()) call_stack.push_back(b);
789     for (size_t i = 0, L = b->length(); i < L; ++i) {
790       Statement_Ptr stm = b->at(i);
791       Statement_Obj ith = stm->perform(this);
792       if (ith) block_stack.back()->append(ith);
793     }
794     if (b->is_root()) call_stack.pop_back();
795   }
796
797 }