Complex_Selector_Ptr nodeToComplexSelector(const Node& toConvert, Context& ctx) { if (toConvert.isNil()) { return NULL; } if (!toConvert.isCollection()) { throw "The node to convert to a Complex_Selector_Ptr must be a collection type or nil."; } NodeDeque& childNodes = *toConvert.collection(); std::string noPath(""); Position noPosition(-1, -1, -1); Complex_Selector_Obj pFirst = SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL); Complex_Selector_Obj pCurrent = pFirst; if (toConvert.isSelector()) pFirst->has_line_feed(toConvert.got_line_feed); if (toConvert.isCombinator()) pFirst->has_line_feed(toConvert.got_line_feed); for (NodeDeque::iterator childIter = childNodes.begin(), childIterEnd = childNodes.end(); childIter != childIterEnd; childIter++) { Node& child = *childIter; if (child.isSelector()) { // JMA - need to clone the selector, because they can end up getting shared across Node // collections, and can result in an infinite loop during the call to parentSuperselector() pCurrent->tail(SASS_MEMORY_COPY(child.selector())); // if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed); pCurrent = pCurrent->tail(); } else if (child.isCombinator()) { pCurrent->combinator(child.combinator()); if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed); // if the next node is also a combinator, create another Complex_Selector to hold it so it doesn't replace the current combinator if (childIter+1 != childIterEnd) { Node& nextNode = *(childIter+1); if (nextNode.isCombinator()) { pCurrent->tail(SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL)); if (nextNode.got_line_feed) pCurrent->tail()->has_line_feed(nextNode.got_line_feed); pCurrent = pCurrent->tail(); } } } else { throw "The node to convert's children must be only combinators or selectors."; } } // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass Compound_Selector_Ptr fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[NODE]"), 1); Parent_Selector_Ptr selectorRef = SASS_MEMORY_NEW(Parent_Selector, ParserState("[NODE]")); fakeHead->elements().push_back(selectorRef); if (toConvert.got_line_feed) pFirst->has_line_feed(toConvert.got_line_feed); // pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed); pFirst->head(fakeHead); return SASS_MEMORY_COPY(pFirst); }
Node Node::createSelector(Complex_Selector_Ptr pSelector, Context& ctx) { NodeDequePtr null; Complex_Selector_Ptr pStripped = SASS_MEMORY_COPY(pSelector); pStripped->tail(NULL); pStripped->combinator(Complex_Selector::ANCESTOR_OF); Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/); if (pSelector) n.got_line_feed = pSelector->has_line_feed(); return n; }
Node Node::klone(Context& ctx) const { NodeDequePtr pNewCollection = std::make_shared<NodeDeque>(); if (mpCollection) { for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toClone = *iter; pNewCollection->push_back(toClone.klone(ctx)); } } Node n(mType, mCombinator, mpSelector ? SASS_MEMORY_COPY(mpSelector) : NULL, pNewCollection); n.got_line_feed = got_line_feed; return n; }
Statement_Ptr Cssize::bubble(At_Root_Block_Ptr m) { if (!m || !m->block()) return NULL; Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent())); Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); if (new_rule) { new_rule->block(bb); new_rule->tabs(this->parent()->tabs()); new_rule->block()->concat(m->block()); wrapper_block->append(new_rule); } At_Root_Block_Ptr mm = SASS_MEMORY_NEW(At_Root_Block, m->pstate(), wrapper_block, m->expression()); Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; }
Statement_Ptr Cssize::bubble(Directive_Ptr m) { Block_Ptr bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); Has_Block_Obj new_rule = Cast<Has_Block>(SASS_MEMORY_COPY(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs()); new_rule->block()->concat(m->block()); Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate()); wrapper_block->append(new_rule); Directive_Obj mm = SASS_MEMORY_NEW(Directive, m->pstate(), m->keyword(), m->selector(), wrapper_block); if (m->value()) mm->value(m->value()); Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; }
Statement_Ptr Cssize::bubble(Media_Block_Ptr m) { Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs()); new_rule->block()->concat(m->block()); Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); wrapper_block->append(new_rule); Media_Block_Obj mm = SASS_MEMORY_NEW(Media_Block, m->pstate(), m->media_queries(), wrapper_block); mm->tabs(m->tabs()); return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); }
Statement_Ptr Cssize::bubble(Supports_Block_Ptr m) { Ruleset_Obj parent = Cast<Ruleset>(SASS_MEMORY_COPY(this->parent())); Block_Ptr bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); Ruleset_Ptr new_rule = SASS_MEMORY_NEW(Ruleset, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs()); new_rule->block()->concat(m->block()); Block_Ptr wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); wrapper_block->append(new_rule); Supports_Block_Ptr mm = SASS_MEMORY_NEW(Supports_Block, m->pstate(), m->condition(), wrapper_block); mm->tabs(m->tabs()); Bubble_Ptr bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; }
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; }
/* static function, throws OperationError, has no traces but optional pstate for returned value */ Value_Ptr op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed) { double lval = lhs.value(); double rval = rhs.value(); if (op == Sass_OP::MOD && rval == 0) { return SASS_MEMORY_NEW(String_Quoted, pstate, "NaN"); } if (op == Sass_OP::DIV && rval == 0) { std::string result(lval ? "Infinity" : "NaN"); return SASS_MEMORY_NEW(String_Quoted, pstate, result); } size_t l_n_units = lhs.numerators.size(); size_t l_d_units = lhs.numerators.size(); size_t r_n_units = rhs.denominators.size(); size_t r_d_units = rhs.denominators.size(); // optimize out the most common and simplest case if (l_n_units == r_n_units && l_d_units == r_d_units) { if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) { if (lhs.numerators == rhs.numerators) { if (lhs.denominators == rhs.denominators) { Number_Ptr v = SASS_MEMORY_COPY(&lhs); v->value(ops[op](lval, rval)); return v; } } } } Number_Obj v = SASS_MEMORY_COPY(&lhs); if (lhs.is_unitless() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) { v->numerators = rhs.numerators; v->denominators = rhs.denominators; } if (op == Sass_OP::MUL) { v->value(ops[op](lval, rval)); v->numerators.insert(v->numerators.end(), rhs.numerators.begin(), rhs.numerators.end() ); v->denominators.insert(v->denominators.end(), rhs.denominators.begin(), rhs.denominators.end() ); v->reduce(); } else if (op == Sass_OP::DIV) { v->value(ops[op](lval, rval)); v->numerators.insert(v->numerators.end(), rhs.denominators.begin(), rhs.denominators.end() ); v->denominators.insert(v->denominators.end(), rhs.numerators.begin(), rhs.numerators.end() ); v->reduce(); } else { Number ln(lhs), rn(rhs); ln.reduce(); rn.reduce(); double f(rn.convert_factor(ln)); v->value(ops[op](lval, rn.value() * f)); } v->pstate(pstate); return v.detach(); }
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); }