// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include "node.hpp" #include "context.hpp" #include "parser.hpp" namespace Sass { Node Node::createCombinator(const Complex_Selector::Combinator& combinator) { NodeDequePtr null; return Node(COMBINATOR, combinator, NULL /*pSelector*/, null /*pCollection*/); } Node Node::createSelector(const Complex_Selector& pSelector) { NodeDequePtr null; Complex_Selector* pStripped = SASS_MEMORY_COPY(&pSelector); pStripped->tail({}); pStripped->combinator(Complex_Selector::ANCESTOR_OF); Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/); n.got_line_feed = pSelector.has_line_feed(); return n; } Node Node::createCollection() { NodeDequePtr pEmptyCollection = std::make_shared(); return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pEmptyCollection); } Node Node::createCollection(const NodeDeque& values) { NodeDequePtr pShallowCopiedCollection = std::make_shared(values); return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pShallowCopiedCollection); } Node Node::createNil() { NodeDequePtr null; return Node(NIL, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, null /*pCollection*/); } Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& pCollection) : got_line_feed(false), mType(type), mCombinator(combinator), mpSelector(pSelector), mpCollection(pCollection) { if (pSelector) got_line_feed = pSelector->has_line_feed(); } Node Node::klone() const { NodeDequePtr pNewCollection = std::make_shared(); if (mpCollection) { for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toClone = *iter; pNewCollection->push_back(toClone.klone()); } } Node n(mType, mCombinator, mpSelector ? SASS_MEMORY_COPY(mpSelector) : NULL, pNewCollection); n.got_line_feed = got_line_feed; return n; } bool Node::contains(const Node& potentialChild) const { bool found = false; for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toTest = *iter; if (toTest == potentialChild) { found = true; break; } } return found; } bool Node::operator==(const Node& rhs) const { if (this->type() != rhs.type()) { return false; } if (this->isCombinator()) { return this->combinator() == rhs.combinator(); } else if (this->isNil()) { return true; // no state to check } else if (this->isSelector()){ return *this->selector() == *rhs.selector(); } else if (this->isCollection()) { if (this->collection()->size() != rhs.collection()->size()) { return false; } for (NodeDeque::iterator lhsIter = this->collection()->begin(), lhsIterEnd = this->collection()->end(), rhsIter = rhs.collection()->begin(); lhsIter != lhsIterEnd; lhsIter++, rhsIter++) { if (*lhsIter != *rhsIter) { return false; } } return true; } // We shouldn't get here. throw "Comparing unknown node types. A new type was probably added and this method wasn't implemented for it."; } void Node::plus(Node& rhs) { if (!this->isCollection() || !rhs.isCollection()) { throw "Both the current node and rhs must be collections."; } this->collection()->insert(this->collection()->end(), rhs.collection()->begin(), rhs.collection()->end()); } #ifdef DEBUG std::ostream& operator<<(std::ostream& os, const Node& node) { if (node.isCombinator()) { switch (node.combinator()) { case Complex_Selector::ANCESTOR_OF: os << "\" \""; break; case Complex_Selector::PARENT_OF: os << "\">\""; break; case Complex_Selector::PRECEDES: os << "\"~\""; break; case Complex_Selector::ADJACENT_TO: os << "\"+\""; break; case Complex_Selector::REFERENCE: os << "\"/\""; break; } } else if (node.isNil()) { os << "nil"; } else if (node.isSelector()){ os << node.selector()->head()->to_string(); } else if (node.isCollection()) { os << "["; for (NodeDeque::iterator iter = node.collection()->begin(), iterBegin = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { if (iter != iterBegin) { os << ", "; } os << (*iter); } os << "]"; } return os; } #endif Node complexSelectorToNode(Complex_Selector* pToConvert) { if (pToConvert == NULL) { return Node::createNil(); } Node node = Node::createCollection(); node.got_line_feed = pToConvert->has_line_feed(); bool has_lf = pToConvert->has_line_feed(); // unwrap the selector from parent ref if (pToConvert->head() && pToConvert->head()->has_parent_ref()) { Complex_Selector_Obj tail = pToConvert->tail(); if (tail) tail->has_line_feed(pToConvert->has_line_feed()); pToConvert = tail; } while (pToConvert) { bool empty_parent_ref = pToConvert->head() && pToConvert->head()->is_empty_reference(); // the first Complex_Selector may contain a dummy head pointer, skip it. if (pToConvert->head() && !empty_parent_ref) { node.collection()->push_back(Node::createSelector(*pToConvert)); if (has_lf) node.collection()->back().got_line_feed = has_lf; if (pToConvert->head() || empty_parent_ref) { if (pToConvert->tail()) { pToConvert->tail()->has_line_feed(pToConvert->has_line_feed()); } } has_lf = false; } if (pToConvert->combinator() != Complex_Selector::ANCESTOR_OF) { node.collection()->push_back(Node::createCombinator(pToConvert->combinator())); if (has_lf) node.collection()->back().got_line_feed = has_lf; has_lf = false; } if (pToConvert && empty_parent_ref && pToConvert->tail()) { // pToConvert->tail()->has_line_feed(pToConvert->has_line_feed()); } pToConvert = pToConvert->tail(); } return node; } Complex_Selector* nodeToComplexSelector(const Node& toConvert) { if (toConvert.isNil()) { return NULL; } if (!toConvert.isCollection()) { throw "The node to convert to a Complex_Selector* must be a collection type or nil."; } NodeDeque& childNodes = *toConvert.collection(); std::string noPath(""); Complex_Selector_Obj pFirst = SASS_MEMORY_NEW(Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, {}, {}); 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, {}, {})); 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* fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[NODE]"), 1); Parent_Selector* 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); } // A very naive trim function, which removes duplicates in a node // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs Node Node::naiveTrim(Node& seqses) { std::vector res; std::vector known; NodeDeque::reverse_iterator seqsesIter = seqses.collection()->rbegin(), seqsesIterEnd = seqses.collection()->rend(); for (; seqsesIter != seqsesIterEnd; ++seqsesIter) { Node& seqs1 = *seqsesIter; if( seqs1.isSelector() ) { Complex_Selector_Obj sel = seqs1.selector(); std::vector::iterator it; bool found = false; for (it = known.begin(); it != known.end(); ++it) { if (**it == *sel) { found = true; break; } } if( !found ) { known.push_back(seqs1.selector()); res.push_back(&seqs1); } } else { res.push_back(&seqs1); } } Node result = Node::createCollection(); for (size_t i = res.size() - 1; i != std::string::npos; --i) { result.collection()->push_back(*res[i]); } return result; } }