Expression_Ptr Listize::operator()(Complex_Selector_Ptr sel) { List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), 2); l->from_selector(true); Compound_Selector_Obj head = sel->head(); if (head && !head->is_empty_reference()) { Expression_Ptr hh = head->perform(this); if (hh) l->append(hh); } std::string reference = ! sel->reference() ? "" : sel->reference()->to_string(); switch(sel->combinator()) { case Complex_Selector::PARENT_OF: l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), ">")); break; case Complex_Selector::ADJACENT_TO: l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "+")); break; case Complex_Selector::REFERENCE: l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "/" + reference + "/")); break; case Complex_Selector::PRECEDES: l->append(SASS_MEMORY_NEW(String_Quoted, sel->pstate(), "~")); break; case Complex_Selector::ANCESTOR_OF: break; } Complex_Selector_Obj tail = sel->tail(); if (tail) { Expression_Obj tt = tail->perform(this); if (List_Ptr ls = SASS_MEMORY_CAST(List, tt)) { l->concat(ls); } } if (l->length() == 0) return 0; return l.detach(); }
Expression_Ptr Listize::operator()(Selector_List_Ptr sel) { List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); l->from_selector(true); for (size_t i = 0, L = sel->length(); i < L; ++i) { if (!sel->at(i)) continue; l->append(sel->at(i)->perform(this)); } if (l->length()) return l.detach(); return SASS_MEMORY_NEW(Null, l->pstate()); }
void bind(std::string type, std::string name, Parameters_Obj ps, Arguments_Obj as, Context* ctx, Env* env, Eval* eval) { std::string callee(type + " " + name); std::map<std::string, Parameter_Obj> param_map; for (size_t i = 0, L = as->length(); i < L; ++i) { if (auto str = SASS_MEMORY_CAST(String_Quoted, (*as)[i]->value())) { // force optional quotes (only if needed) if (str->quote_mark()) { str->quote_mark('*'); } } } // Set up a map to ensure named arguments refer to actual parameters. Also // eval each default value left-to-right, wrt env, populating env as we go. for (size_t i = 0, L = ps->length(); i < L; ++i) { Parameter_Obj p = ps->at(i); param_map[p->name()] = p; // if (p->default_value()) { // env->local_frame()[p->name()] = p->default_value()->perform(eval->with(env)); // } } // plug in all args; if we have leftover params, deal with it later size_t ip = 0, LP = ps->length(); size_t ia = 0, LA = as->length(); while (ia < LA) { Argument_Obj a = as->at(ia); if (ip >= LP) { // skip empty rest arguments if (a->is_rest_argument()) { if (List_Obj l = SASS_MEMORY_CAST(List, a->value())) { if (l->length() == 0) { ++ ia; continue; } } } std::stringstream msg; msg << "wrong number of arguments (" << LA << " for " << LP << ")"; msg << " for `" << name << "'"; return error(msg.str(), as->pstate()); } Parameter_Obj p = ps->at(ip); // If the current parameter is the rest parameter, process and break the loop if (p->is_rest_parameter()) { // The next argument by coincidence provides a rest argument if (a->is_rest_argument()) { // We should always get a list for rest arguments if (List_Obj rest = SASS_MEMORY_CAST(List, a->value())) { // create a new list object for wrapped items List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, rest->separator(), true); // wrap each item from list as an argument for (Expression_Obj item : rest->elements()) { if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, item)) { arglist->append(SASS_MEMORY_COPY(arg)); // copy } else { arglist->append(SASS_MEMORY_NEW(Argument, item->pstate(), &item, "", false, false)); } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; } // invalid state else { throw std::runtime_error("invalid state"); } } else if (a->is_keyword_argument()) { // expand keyword arguments into their parameters List_Ptr arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); env->local_frame()[p->name()] = arglist; Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value()); for (auto key : argmap->keys()) { std::string name = unquote(SASS_MEMORY_CAST(String_Constant, key)->value()); arglist->append(SASS_MEMORY_NEW(Argument, key->pstate(), argmap->at(key), "$" + name, false, false)); } } else { // create a new list object for wrapped items List_Obj arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); // consume the next args while (ia < LA) { // get and post inc a = (*as)[ia++]; // maybe we have another list as argument List_Obj ls = SASS_MEMORY_CAST(List, a->value()); // skip any list completely if empty if (ls && ls->empty() && a->is_rest_argument()) continue; Expression_Obj value = a->value(); if (Argument_Obj arg = SASS_MEMORY_CAST(Argument, value)) { arglist->append(&arg); } // check if we have rest argument else if (a->is_rest_argument()) { // preserve the list separator from rest args if (List_Obj rest = SASS_MEMORY_CAST(List, a->value())) { arglist->separator(rest->separator()); for (size_t i = 0, L = rest->size(); i < L; ++i) { Expression_Obj obj = rest->at(i); arglist->append(SASS_MEMORY_NEW(Argument, obj->pstate(), &obj, "", false, false)); } } // no more arguments break; } // wrap all other value types into Argument else { arglist->append(SASS_MEMORY_NEW(Argument, a->pstate(), a->value(), a->name(), false, false)); } } // assign new arglist to environment env->local_frame()[p->name()] = &arglist; } // consumed parameter ++ip; // no more paramaters break; } // If the current argument is the rest argument, extract a value for processing else if (a->is_rest_argument()) { // normal param and rest arg List_Obj arglist = SASS_MEMORY_CAST(List, a->value()); // empty rest arg - treat all args as default values if (!arglist->length()) { break; } else { if (arglist->length() > LP - ip && !ps->has_rest_parameter()) { size_t arg_count = (arglist->length() + LA - 1); std::stringstream msg; msg << callee << " takes " << LP; msg << (LP == 1 ? " argument" : " arguments"); msg << " but " << arg_count; msg << (arg_count == 1 ? " was passed" : " were passed."); deprecated_bind(msg.str(), as->pstate()); while (arglist->length() > LP - ip) { arglist->elements().erase(arglist->elements().end() - 1); } } } // otherwise move one of the rest args into the param, converting to argument if necessary Expression_Obj obj = arglist->at(0); if (!(a = SASS_MEMORY_CAST(Argument, obj))) { Expression_Ptr a_to_convert = &obj; a = SASS_MEMORY_NEW(Argument, a_to_convert->pstate(), a_to_convert, "", false, false); } arglist->elements().erase(arglist->elements().begin()); if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) { ++ia; } } else if (a->is_keyword_argument()) { Map_Obj argmap = SASS_MEMORY_CAST(Map, a->value()); for (auto key : argmap->keys()) { std::string name = "$" + unquote(SASS_MEMORY_CAST(String_Constant, key)->value()); if (!param_map.count(name)) { std::stringstream msg; msg << callee << " has no parameter named " << name; error(msg.str(), a->pstate()); } env->local_frame()[name] = &argmap->at(&key); } ++ia; continue; } else { ++ia; } if (a->name().empty()) { if (env->has_local(p->name())) { std::stringstream msg; msg << "parameter " << p->name() << " provided more than once in call to " << callee; error(msg.str(), a->pstate()); } // ordinal arg -- bind it to the next param env->local_frame()[p->name()] = &a->value(); ++ip; } else { // named arg -- bind it to the appropriately named param if (!param_map.count(a->name())) { std::stringstream msg; msg << callee << " has no parameter named " << a->name(); error(msg.str(), a->pstate()); } if (param_map[a->name()]->is_rest_parameter()) { std::stringstream msg; msg << "argument " << a->name() << " of " << callee << "cannot be used as named argument"; error(msg.str(), a->pstate()); } if (env->has_local(a->name())) { std::stringstream msg; msg << "parameter " << p->name() << "provided more than once in call to " << callee; error(msg.str(), a->pstate()); } env->local_frame()[a->name()] = &a->value(); } } // EO while ia // If we make it here, we're out of args but may have leftover params. // That's only okay if they have default values, or were already bound by // named arguments, or if it's a single rest-param. for (size_t i = ip; i < LP; ++i) { Parameter_Obj leftover = ps->at(i); // cerr << "env for default params:" << endl; // env->print(); // cerr << "********" << endl; if (!env->has_local(leftover->name())) { if (leftover->is_rest_parameter()) { env->local_frame()[leftover->name()] = SASS_MEMORY_NEW(List, leftover->pstate(), 0, SASS_COMMA, true); } else if (leftover->default_value()) { Expression_Ptr dv = leftover->default_value()->perform(eval); env->local_frame()[leftover->name()] = dv; } else { // param is unbound and has no default value -- error throw Exception::MissingArgument(as->pstate(), name, leftover->name(), type); } } } return; }
Block_Ptr Cssize::debubble(Block_Ptr children, Statement_Ptr parent) { Has_Block_Obj previous_parent = 0; std::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children); Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate()); for (size_t i = 0, L = baz.size(); i < L; ++i) { bool is_bubble = baz[i].first; Block_Obj slice = baz[i].second; if (!is_bubble) { if (!parent) { result->append(slice); } else if (previous_parent) { previous_parent->block()->concat(slice); } else { previous_parent = Cast<Has_Block>(SASS_MEMORY_COPY(parent)); previous_parent->block(slice); previous_parent->tabs(parent->tabs()); result->append(previous_parent); } continue; } for (size_t j = 0, K = slice->length(); j < K; ++j) { Statement_Ptr ss; Statement_Obj stm = slice->at(j); // this has to go now here (too bad) Bubble_Obj node = Cast<Bubble>(stm); Media_Block_Ptr m1 = NULL; Media_Block_Ptr m2 = NULL; if (parent) m1 = Cast<Media_Block>(parent); if (node) m2 = Cast<Media_Block>(node->node()); if (!parent || parent->statement_type() != Statement::MEDIA || node->node()->statement_type() != Statement::MEDIA || (m1 && m2 && *m1->media_queries() == *m2->media_queries()) ) { ss = node->node(); } else { List_Obj mq = merge_media_queries( Cast<Media_Block>(node->node()), Cast<Media_Block>(parent) ); if (!mq->length()) continue; if (Media_Block* b = Cast<Media_Block>(node->node())) { b->media_queries(mq); } ss = node->node(); } if (!ss) continue; ss->tabs(ss->tabs() + node->tabs()); ss->group_end(node->group_end()); Block_Obj bb = SASS_MEMORY_NEW(Block, children->pstate(), children->length(), children->is_root()); bb->append(ss->perform(this)); Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, children->pstate(), children->length(), children->is_root()); Block_Ptr wrapper = flatten(bb); wrapper_block->append(wrapper); if (wrapper->length()) { previous_parent = NULL; } if (wrapper_block) { result->append(wrapper_block); } } } return flatten(result); }