9 #include "backtrace.hpp"
10 #include "context.hpp"
12 #include "sass_functions.hpp"
16 // simple endless recursion protection
17 const size_t maxRecursion = 500;
19 Expand::Expand(Context& ctx, Env* env, Backtrace* bt, std::vector<Selector_List_Obj>* stack)
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*>())
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);
44 Env* Expand::environment()
46 if (env_stack.size() > 0)
47 return env_stack.back();
51 Selector_List_Obj Expand::selector()
53 if (selector_stack.size() > 0)
54 return selector_stack.back();
58 Backtrace* Expand::backtrace()
60 if (backtrace_stack.size() > 0)
61 return backtrace_stack.back();
65 // blocks create new variable scopes
66 Block_Ptr Expand::operator()(Block_Ptr b)
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,
76 // setup block and env stack
77 this->block_stack.push_back(bb);
78 this->env_stack.push_back(&env);
81 this->append_block(b);
82 // revert block and env stack
83 this->block_stack.pop_back();
84 this->env_stack.pop_back();
89 Statement_Ptr Expand::operator()(Ruleset_Ptr r)
91 LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule);
94 Block_Ptr bb = operator()(r->block());
95 Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);
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();
106 // reset when leaving scope
107 LOCAL_FLAG(at_root_without_rule, false);
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;
116 Selector_List_Obj sel = r->selector();
117 if (sel) sel = sel->eval(eval);
119 // check for parent selectors in base level rules
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;
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());
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());
144 selector_stack.push_back(sel);
145 Env env(environment());
146 if (block_stack.back()->is_root()) {
147 env_stack.push_back(&env);
149 sel->set_media_block(media_block_stack.back());
151 if (r->block()) blk = operator()(r->block());
152 Ruleset_Ptr rr = SASS_MEMORY_NEW(Ruleset,
156 selector_stack.pop_back();
157 if (block_stack.back()->is_root()) {
158 env_stack.pop_back();
161 rr->is_root(r->is_root());
167 Statement_Ptr Expand::operator()(Supports_Block_Ptr f)
169 Expression_Obj condition = f->condition()->perform(&eval);
170 Supports_Block_Obj ff = SASS_MEMORY_NEW(Supports_Block,
172 Cast<Supports_Condition>(condition),
173 operator()(f->block()));
177 Statement_Ptr Expand::operator()(Media_Block_Ptr m)
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,
192 media_block_stack.pop_back();
197 Statement_Ptr Expand::operator()(At_Root_Block_Ptr a)
199 Block_Obj ab = a->block();
200 Expression_Obj ae = a->expression();
202 if (ae) ae = ae->perform(&eval);
203 else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate());
205 LOCAL_FLAG(at_root_without_rule, true);
206 LOCAL_FLAG(in_keyframes, false);
210 Block_Obj bb = ab ? operator()(ab) : NULL;
211 At_Root_Block_Obj aa = SASS_MEMORY_NEW(At_Root_Block,
214 Cast<At_Root_Query>(ae));
218 Statement_Ptr Expand::operator()(Directive_Ptr a)
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,
238 Statement_Ptr Expand::operator()(Declaration_Ptr d)
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
246 std::string str(prop->to_string(ctx.c_options));
247 new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str);
249 Expression_Obj value = d->value()->perform(&eval);
250 Block_Obj bb = ab ? operator()(ab) : NULL;
252 if (!value || (value->is_invisible() && !d->is_important())) return 0;
254 Declaration_Ptr decl = SASS_MEMORY_NEW(Declaration,
260 decl->tabs(d->tabs());
264 Statement_Ptr Expand::operator()(Assignment_Ptr a)
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));
277 env->set_global(var, a->value()->perform(&eval));
281 env->set_global(var, a->value()->perform(&eval));
284 else if (a->is_default()) {
285 if (env->has_lexical(var)) {
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));
296 throw std::runtime_error("Env not in sync");
302 throw std::runtime_error("Env not in sync");
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));
312 else if (env->is_lexical()) {
313 env->set_local(var, a->value()->perform(&eval));
316 env->set_local(var, a->value()->perform(&eval));
320 env->set_lexical(var, a->value()->perform(&eval));
325 Statement_Ptr Expand::operator()(Import_Ptr imp)
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));
332 for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) {
333 result->urls().push_back(imp->urls()[i]->perform(&eval));
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();
340 Statement_Ptr Expand::operator()(Import_Stub_Ptr i)
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());
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(),
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();
361 Statement_Ptr Expand::operator()(Warning_Ptr w)
363 // eval handles this too, because warnings may occur in functions
368 Statement_Ptr Expand::operator()(Error_Ptr e)
370 // eval handles this too, because errors may occur in functions
375 Statement_Ptr Expand::operator()(Debug_Ptr d)
377 // eval handles this too, because warnings may occur in functions
382 Statement_Ptr Expand::operator()(Comment_Ptr c)
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
391 Statement_Ptr Expand::operator()(If_Ptr i)
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);
398 append_block(i->block());
401 Block_Ptr alt = i->alternative();
402 if (alt) append_block(alt);
404 call_stack.pop_back();
405 env_stack.pop_back();
409 // For does not create a new env scope
410 // But iteration vars are reset afterwards
411 Statement_Ptr Expand::operator()(For_Ptr f)
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");
418 Expression_Obj high = f->upper_bound()->perform(&eval);
419 if (high->concrete_type() != Expression::NUMBER) {
420 throw Exception::TypeMismatch(*high, "integer");
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());
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();
441 if (f->is_inclusive()) ++end;
442 for (double i = start;
445 it = SASS_MEMORY_COPY(it);
447 env.set_local(variable, it);
451 if (f->is_inclusive()) --end;
452 for (double i = start;
455 it = SASS_MEMORY_COPY(it);
457 env.set_local(variable, it);
461 call_stack.pop_back();
462 env_stack.pop_back();
466 // Eval does not create a new env scope
467 // But iteration vars are reset afterwards
468 Statement_Ptr Expand::operator()(Each_Ptr e)
470 std::vector<std::string> variables(e->variables());
471 Expression_Obj expr = e->list()->perform(&eval);
474 if (expr->concrete_type() == Expression::MAP) {
475 map = Cast<Map>(expr);
477 else if (Selector_List_Ptr ls = Cast<Selector_List>(expr)) {
479 Expression_Obj rv = ls->perform(&listize);
480 list = Cast<List>(rv);
482 else if (expr->concrete_type() != Expression::LIST) {
483 list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);
487 list = Cast<List>(expr);
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();
496 for (auto key : map->keys()) {
497 Expression_Obj k = key->perform(&eval);
498 Expression_Obj v = map->at(key)->perform(&eval);
500 if (variables.size() == 1) {
501 List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);
504 env.set_local(variables[0], variable);
506 env.set_local(variables[0], k);
507 env.set_local(variables[1], v);
513 // bool arglist = list->is_arglist();
514 if (list->length() == 1 && Cast<Selector_List>(list)) {
515 list = Cast<List>(list);
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);
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);
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);
547 call_stack.pop_back();
548 env_stack.pop_back();
552 Statement_Ptr Expand::operator()(While_Ptr w)
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()) {
562 cond = pred->perform(&eval);
564 call_stack.pop_back();
565 env_stack.pop_back();
569 Statement_Ptr Expand::operator()(Return_Ptr r)
571 error("@return may only be used within a function", r->pstate(), backtrace());
576 void Expand::expand_selector_list(Selector_Obj s, Selector_List_Obj extender) {
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;
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());
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());
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])))
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());
620 // if (c->has_line_feed()) sel->has_line_feed(true);
621 ctx.subset_map.put(placeholder, std::make_pair(sel, placeholder));
627 Statement* Expand::operator()(Extension_Ptr e)
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();
641 selector_stack.push_back(0);
642 sl = eval(sl->schema());
643 selector_stack.pop_back();
646 for (Complex_Selector_Obj cs : sl->elements()) {
647 if (!cs.isNull() && !cs->head().isNull()) {
648 cs->head()->media_block(media_block_stack.back());
651 selector_stack.push_back(0);
652 expand_selector_list(sl, extender);
653 selector_stack.pop_back();
658 Statement_Ptr Expand::operator()(Definition_Ptr d)
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;
665 if (d->type() == Definition::FUNCTION && (
666 Prelexer::calc_fn_call(d->name().c_str()) ||
667 d->name() == "element" ||
668 d->name() == "expression" ||
672 "Naming a function \"" + d->name() + "\" is disallowed",
673 "This name conflicts with an existing CSS function with special parse rules.",
678 // set the static link so we can have lexical scoping
679 dd->environment(env);
683 Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)
686 if (recursions > maxRecursion) {
687 throw Exception::StackError(*c);
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());
697 Definition_Obj def = Cast<Definition>((*env)[full_name]);
698 Block_Obj body = def->block();
699 Parameters_Obj params = def->parameters();
701 if (c->block() && c->name() != "@content" && !body->has_content()) {
702 error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), backtrace());
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({
711 c->pstate().line + 1,
712 c->pstate().column + 1,
717 Env new_env(def->environment());
718 env_stack.push_back(&new_env);
720 // represent mixin content blocks as thunks/closures
721 Definition_Obj thunk = SASS_MEMORY_NEW(Definition,
724 SASS_MEMORY_NEW(Parameters, c->pstate()),
727 thunk->environment(env);
728 new_env.local_frame()["@content[m]"] = thunk;
731 bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval);
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);
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);
742 block_stack.pop_back();
744 env_stack.pop_back();
745 backtrace_stack.pop_back();
746 ctx.callee_stack.pop_back();
749 return trace.detach();
752 Statement_Ptr Expand::operator()(Content_Ptr c)
754 Env* env = environment();
755 // convert @content directives into mixin calls to the underlying thunk
756 if (!env->has("@content[m]")) return 0;
758 if (block_stack.back()->is_root()) {
759 selector_stack.push_back(0);
762 Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call,
765 SASS_MEMORY_NEW(Arguments, c->pstate()));
767 Trace_Obj trace = Cast<Trace>(call->perform(this));
769 if (block_stack.back()->is_root()) {
770 selector_stack.pop_back();
773 return trace.detach();
776 // produce an error if something is not implemented
777 inline Statement_Ptr Expand::fallback_impl(AST_Node_Ptr n)
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);
785 // process and add to last block on stack
786 inline void Expand::append_block(Block_Ptr b)
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);
794 if (b->is_root()) call_stack.pop_back();