From e44a65c986707aba5492f83604ed609ff2b20674 Mon Sep 17 00:00:00 2001 From: supermario Date: Mon, 1 Jun 2015 19:24:33 -0700 Subject: [PATCH 01/10] Added all missing sass selector functions Reuses existing Complex_Selector_Compare function --- ast.cpp | 102 ++++++++++++++++++++ ast.hpp | 10 ++ context.cpp | 7 ++ extend.cpp | 13 ++- extend.hpp | 4 + functions.cpp | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++ functions.hpp | 15 +++ node.cpp | 25 +++++ node.hpp | 1 + 9 files changed, 425 insertions(+), 5 deletions(-) diff --git a/ast.cpp b/ast.cpp index c93c70c52d..61eca23c35 100644 --- a/ast.cpp +++ b/ast.cpp @@ -1,5 +1,7 @@ #include "ast.hpp" #include "context.hpp" +#include "node.hpp" +#include "extend.hpp" #include "to_string.hpp" #include #include @@ -420,6 +422,43 @@ namespace Sass { // catch-all return false; } + + Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx) { + To_String to_string; + + Compound_Selector* thisBase = base(); + Compound_Selector* rhsBase = other->base(); + + if( thisBase == 0 || rhsBase == 0 ) return 0; + + // Not sure about this check, but closest way I could check to see if this is a ruby 'SimpleSequence' equivalent + if( tail()->combinator() != Combinator::ANCESTOR_OF || other->tail()->combinator() != Combinator::ANCESTOR_OF ) return 0; + + Compound_Selector* unified = rhsBase->unify_with(thisBase, ctx); + if( unified == 0 ) return 0; + + Node lhsNode = complexSelectorToNode(this, ctx); + Node rhsNode = complexSelectorToNode(other, ctx); + + // Create a temp Complex_Selector, turn it into a Node, and combine it with the existing RHS node + Complex_Selector* fakeComplexSelector = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, unified, NULL); + Node unifiedNode = complexSelectorToNode(fakeComplexSelector, ctx); + rhsNode.plus(unifiedNode); + + Node node = Extend::subweave(lhsNode, rhsNode, ctx); + + Selector_List* result = new (ctx.mem) Selector_List(pstate()); + for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { + Node childNode = *iter; + childNode = Node::naiveTrim(childNode, ctx); + + Complex_Selector* childNodeAsComplexSelector = nodeToComplexSelector(childNode, ctx); + if( childNodeAsComplexSelector ) { (*result) << childNodeAsComplexSelector; } + } + + return result->length() ? result : 0; + } + size_t Complex_Selector::length() { @@ -558,6 +597,69 @@ namespace Sass { } return false; } + + Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { + + vector unified_complex_selectors; + // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` + for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { + Complex_Selector* seq1 = (*this)[lhs_i]; + for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { + Complex_Selector* seq2 = (*rhs)[rhs_i]; + + Selector_List* result = seq1->unify_with(seq2, ctx); + if( result ) { + for(size_t i = 0, L = result->length(); i < L; ++i) { + unified_complex_selectors.push_back( (*result)[i] ); + } + } + } + } + + // Creates the final Selector_List by combining all the complex selectors + Selector_List* final_result = new (ctx.mem) Selector_List(pstate()); + for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) { + *final_result << *itr; + } + + return final_result; + } + + void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) { + To_String to_string; + + Selector_List* extender = this; + for (auto complex_sel : extendee->elements()) { + Complex_Selector* c = complex_sel; + + + // Ignore any parent selectors, until we find the first non Selector_Reference head + Compound_Selector* compound_sel = c->head(); + Complex_Selector* pIter = complex_sel; + while (pIter) { + Compound_Selector* pHead = pIter->head(); + if (pHead && dynamic_cast(pHead->elements()[0]) == NULL) { + compound_sel = pHead; + break; + } + + pIter = pIter->tail(); + } + + if (!pIter->head() || pIter->tail()) { + error("nested selectors may not be extended", c->pstate()); + } + + compound_sel->is_optional(extendee->is_optional()); + + for (size_t i = 0, L = extender->length(); i < L; ++i) { + // let's test this out + cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl; + extends.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel)); + } + } + }; + /* not used anymore - remove? Selector_Placeholder* Selector_List::find_placeholder() diff --git a/ast.hpp b/ast.hpp index 193bb109e8..302c862d44 100644 --- a/ast.hpp +++ b/ast.hpp @@ -175,6 +175,7 @@ namespace Sass { size_t length() const { return elements_.size(); } bool empty() const { return elements_.empty(); } T last() { return elements_.back(); } + T first() { return elements_.front(); } T& operator[](size_t i) { return elements_[i]; } const T& operator[](size_t i) const { return elements_[i]; } Vectorized& operator<<(T element) @@ -2022,6 +2023,9 @@ namespace Sass { bool is_superselector_of(Complex_Selector* sub); bool is_superselector_of(Selector_List* sub); // virtual Selector_Placeholder* find_placeholder(); + + Selector_List* unify_with(Complex_Selector* rhs, Context& ctx); + Combinator clear_innermost(); void set_innermost(Complex_Selector*, Combinator); virtual unsigned long specificity() const @@ -2090,6 +2094,8 @@ namespace Sass { typedef deque ComplexSelectorDeque; + typedef Subset_Map > ExtensionSubsetMap; + /////////////////////////////////// // Comma-separated selector groups. /////////////////////////////////// @@ -2108,6 +2114,10 @@ namespace Sass { bool is_superselector_of(Compound_Selector* sub); bool is_superselector_of(Complex_Selector* sub); bool is_superselector_of(Selector_List* sub); + + Selector_List* unify_with(Selector_List*, Context&); + void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&); + virtual unsigned long specificity() { unsigned long sum = 0; diff --git a/context.cpp b/context.cpp index 5620764072..06b0e8cd20 100644 --- a/context.cpp +++ b/context.cpp @@ -540,7 +540,14 @@ namespace Sass { register_function(ctx, inspect_sig, inspect, env); register_function(ctx, unique_id_sig, unique_id, env); // Selector functions + register_function(ctx, selector_nest_sig, selector_nest, env); + register_function(ctx, selector_append_sig, selector_append, env); + register_function(ctx, selector_extend_sig, selector_extend, env); + register_function(ctx, selector_replace_sig, selector_replace, env); + register_function(ctx, selector_unify_sig, selector_unify, env); register_function(ctx, is_superselector_sig, is_superselector, env); + register_function(ctx, simple_selectors_sig, simple_selectors, env); + register_function(ctx, selector_parse_sig, selector_parse, env); } void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs) diff --git a/extend.cpp b/extend.cpp index ede8e8d73c..65fb6ef273 100644 --- a/extend.cpp +++ b/extend.cpp @@ -1134,7 +1134,7 @@ namespace Sass { result end */ - static Node subweave(Node& one, Node& two, Context& ctx) { + Node Extend::subweave(Node& one, Node& two, Context& ctx) { // Check for the simple cases if (one.collection()->size() == 0) { Node out = Node::createCollection(); @@ -1423,7 +1423,7 @@ namespace Sass { for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) { Node& before = *beforesIter; - Node sub = subweave(before, current, ctx); + Node sub = Extend::subweave(before, current, ctx); DEBUG_PRINTLN(WEAVE, "SUB: " << sub) @@ -1854,7 +1854,7 @@ namespace Sass { /* This is the equivalent of ruby's CommaSequence.do_extend. */ - static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool& extendedSomething) { + Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething) { To_String to_string(&ctx); @@ -1886,7 +1886,10 @@ namespace Sass { } } - for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { + for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { + // When it is a replace, skip the first one, unless there is only one + if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue; + Node& childNode = *iterator; *pNewSelectors << nodeToComplexSelector(childNode, ctx); } @@ -1943,7 +1946,7 @@ namespace Sass { } bool extendedSomething = false; - Selector_List* pNewSelectorList = extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, extendedSomething); + Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, false, extendedSomething); if (extendedSomething && pNewSelectorList) { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->perform(&to_string)) diff --git a/extend.hpp b/extend.hpp index 61466f3606..eb2dd9d197 100644 --- a/extend.hpp +++ b/extend.hpp @@ -14,6 +14,7 @@ namespace Sass { using namespace std; class Context; + class Node; typedef Subset_Map > ExtensionSubsetMap; @@ -38,6 +39,9 @@ namespace Sass { template void fallback(U x) { return fallback_impl(x); } + + static Node subweave(Node& one, Node& two, Context& ctx); + static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething); }; } diff --git a/functions.cpp b/functions.cpp index e078a0feba..ee9b42765f 100644 --- a/functions.cpp +++ b/functions.cpp @@ -6,6 +6,7 @@ #include "constants.hpp" #include "to_string.hpp" #include "inspect.hpp" +#include "extend.hpp" #include "eval.hpp" #include "util.hpp" #include "utf8_string.hpp" @@ -117,6 +118,37 @@ namespace Sass { } return val; } + +#define ARGSEL(argname, seltype, contextualize) get_arg_sel(argname, env, sig, pstate, backtrace, ctx) + + template + T* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx); + + template <> + Selector_List* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { + To_String to_string(&ctx, false); + Expression* exp = ARG(argname, Expression); + string exp_src = exp->perform(&to_string) + "{"; + return Parser::parse_selector(exp_src.c_str(), ctx); + } + + template <> + Complex_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { + To_String to_string(&ctx, false); + Expression* exp = ARG(argname, Expression); + string exp_src = exp->perform(&to_string) + "{"; + Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx); + return (sel_list->length() > 0) ? sel_list->first() : 0; + } + + template <> + Compound_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { + To_String to_string(&ctx, false); + Expression* exp = ARG(argname, Expression); + string exp_src = exp->perform(&to_string) + "{"; + Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx); + return (sel_list->length() > 0) ? sel_list->first()->tail()->head() : 0; + } #ifdef __MINGW32__ uint64_t GetSeed() @@ -1573,6 +1605,196 @@ namespace Sass { } // return v; } + Signature selector_nest_sig = "selector-nest($selectors...)"; + BUILT_IN(selector_nest) + { + To_String to_string; + List* arglist = ARG("$selectors", List); + + // Not enough parameters + if( arglist->length() == 0 ) + error("$selectors: At least one selector must be passed", pstate); + + // Parse args into vector of selectors + vector parsedSelectors; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression* exp = dynamic_cast(arglist->value_at_index(i)); + string exp_src = exp->perform(&to_string) + "{"; + Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); + parsedSelectors.push_back(sel); + } + + // Nothing to do + if( parsedSelectors.empty() ) { + return new (ctx.mem) Null(pstate); + } + + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. + std::vector::iterator itr = parsedSelectors.begin(); + Selector_List* result = *itr; + ++itr; + + for(;itr != parsedSelectors.end(); ++itr) { + Selector_List* child = *itr; + vector newElements; + + // For every COMPLEX_SELECTOR in `child` + // For every COMPLEX_SELECTOR in `result` + // let parentSeqClone equal a copy of result->elements[i] + // let childSeq equal child->elements[j] + // Set childSeq as the new innermost tail of parentSeqClone + // Add parentSeqClone to the newElements + // Replace result->elements with newElements + for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { + for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { + Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx); + Complex_Selector* childSeq = (*child)[j]; + + parentSeqClone->innermost()->tail(childSeq); // Set seq as the new tail of parentSeqClone + newElements.push_back(parentSeqClone); + } + } + + result->elements(newElements); + } + + + Listize listize(ctx); + return result->perform(&listize); + } + + Signature selector_append_sig = "selector-append($selectors...)"; + BUILT_IN(selector_append) + { + To_String to_string; + List* arglist = ARG("$selectors", List); + + // Not enough parameters + if( arglist->length() == 0 ) + error("$selectors: At least one selector must be passed", pstate); + + // Parse args into vector of selectors + vector parsedSelectors; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression* exp = dynamic_cast(arglist->value_at_index(i)); + string exp_src = exp->perform(&to_string) + "{"; + Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); + parsedSelectors.push_back(sel); + } + + // Nothing to do + if( parsedSelectors.empty() ) { + return new (ctx.mem) Null(pstate); + } + + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. + std::vector::iterator itr = parsedSelectors.begin(); + Selector_List* result = *itr; + ++itr; + + for(;itr != parsedSelectors.end(); ++itr) { + Selector_List* child = *itr; + vector newElements; + + // For every COMPLEX_SELECTOR in `result` + // For every COMPLEX_SELECTOR in `child` + // let parentSeqClone equal a copy of result->elements[i] + // let childSeq equal child->elements[j] + // Append all of childSeq head elements into parentSeqClone + // Set the innermost tail of parentSeqClone, to childSeq's tail + // Replace result->elements with newElements + for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { + for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { + Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx); + Complex_Selector* childSeq = (*child)[j]; + Complex_Selector* base = childSeq->tail(); + + // Must be a simple sequence + if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) { + string msg("Can't append `"); + msg += childSeq->perform(&to_string); + msg += "` to `"; + msg += parentSeqClone->perform(&to_string);; + msg += "`"; + error(msg, pstate, backtrace); + } + + // Cannot be a Universal selector + Type_Selector* pType = dynamic_cast(base->head()->first()); + if(pType && pType->name() == "*") { + string msg("Can't append `"); + msg += childSeq->perform(&to_string); + msg += "` to `"; + msg += parentSeqClone->perform(&to_string);; + msg += "`"; + error(msg, pstate, backtrace); + } + + // TODO: Add check for namespace stuff + + // append any selectors in childSeq's head + *(parentSeqClone->innermost()->head()) += (base->head()); + + // Set parentSeqClone new tail + parentSeqClone->innermost()->tail( base->tail() ); + + newElements.push_back(parentSeqClone); + } + } + + result->elements(newElements); + } + + Listize listize(ctx); + return result->perform(&listize); + } + + Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; + BUILT_IN(selector_extend) + { + To_String to_string; + + Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); + Selector_List* extendee = ARGSEL("$extendee", Selector_List, p_contextualize); + Selector_List* extender = ARGSEL("$extender", Selector_List, p_contextualize); + + ExtensionSubsetMap subset_map; + extender->populate_extends(extendee, ctx, subset_map); + + bool extendedSomething; + Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething); + + Listize listize(ctx); + return result->perform(&listize); + } + + Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; + BUILT_IN(selector_replace) + { + Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); + Selector_List* original = ARGSEL("$original", Selector_List, p_contextualize); + Selector_List* replacement = ARGSEL("$replacement", Selector_List, p_contextualize); + + ExtensionSubsetMap subset_map; + replacement->populate_extends(original, ctx, subset_map); + + bool extendedSomething; + Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething); + + Listize listize(ctx); + return result->perform(&listize); + } + + Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; + BUILT_IN(selector_unify) + { + Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize); + Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize); + + Selector_List* result = selector1->unify_with(selector2, ctx); + Listize listize(ctx); + return result->perform(&listize); + } Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) @@ -1587,6 +1809,37 @@ namespace Sass { bool result = sel_sup->is_superselector_of(sel_sub); return new (ctx.mem) Boolean(pstate, result); } + + Signature simple_selectors_sig = "simple_selectors($selector)"; + BUILT_IN(simple_selectors) + { + Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize); + + To_String to_string; + List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA); + + for (size_t i = 0, L = sel->length(); i < L; ++i) { + Simple_Selector* ss = (*sel)[i]; + string ss_string = ss->perform(&to_string) ; + + *l << new (ctx.mem) String_Constant(ss->pstate(), ss_string); + } + + + return l; + } + + Signature selector_parse_sig = "selector-parse($selector)"; + BUILT_IN(selector_parse) + { + To_String to_string(&ctx, false); + Expression* exp = ARG("$selector", Expression); + string sel_src = exp->perform(&to_string) + "{"; + Selector_List* sel = Parser::parse_selector(sel_src.c_str(), ctx); + + Listize listize(ctx); + return sel->perform(&listize); + } Signature unique_id_sig = "unique-id()"; BUILT_IN(unique_id) diff --git a/functions.hpp b/functions.hpp index c0307f6ed4..8b993e9176 100644 --- a/functions.hpp +++ b/functions.hpp @@ -101,7 +101,14 @@ namespace Sass { extern Signature keywords_sig; extern Signature set_nth_sig; extern Signature unique_id_sig; + extern Signature selector_nest_sig; + extern Signature selector_append_sig; + extern Signature selector_extend_sig; + extern Signature selector_replace_sig; + extern Signature selector_unify_sig; extern Signature is_superselector_sig; + extern Signature simple_selectors_sig; + extern Signature selector_parse_sig; BUILT_IN(rgb); BUILT_IN(rgba_4); @@ -176,7 +183,15 @@ namespace Sass { BUILT_IN(keywords); BUILT_IN(set_nth); BUILT_IN(unique_id); + BUILT_IN(selector_nest); + BUILT_IN(selector_append); + BUILT_IN(selector_extend); + BUILT_IN(selector_replace); + BUILT_IN(selector_unify); BUILT_IN(is_superselector); + BUILT_IN(simple_selectors); + BUILT_IN(selector_parse); + } } diff --git a/node.cpp b/node.cpp index 7ed2d55505..c963f4cbb7 100644 --- a/node.cpp +++ b/node.cpp @@ -247,5 +247,30 @@ namespace Sass { return 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, Context& ctx) { + + Node result = Node::createCollection(); + + To_String to_string; + std::set< Complex_Selector*, Complex_Selector_Pointer_Compare > sel_set; + + // Add all selectors we don't already have, everything else just add it blindly + for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) { + Node& seqs1 = *seqsesIter; + if( seqs1.isSelector() ) { + auto found = sel_set.find( seqs1.selector() ); + if( found == sel_set.end() ) { + sel_set.insert(seqs1.selector()); + result.collection()->push_back(seqs1); + } + } else { + result.collection()->push_back(seqs1); + } + } + + return result; + } } diff --git a/node.hpp b/node.hpp index 6aa1156819..faaa95eea7 100644 --- a/node.hpp +++ b/node.hpp @@ -69,6 +69,7 @@ namespace Sass { static Node createCollection(const NodeDeque& values); static Node createNil(); + static Node naiveTrim(Node& seqses, Context& ctx); Node clone(Context& ctx) const; From 40ab322c365ca13fdb94f73a18a84297630527cb Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 12 Jun 2015 17:47:07 +0200 Subject: [PATCH 02/10] Rename some parser function and variables Improves and fixes a few minor code issues --- ast.cpp | 88 ++++---------- ast.hpp | 51 ++++----- ast_factory.hpp | 4 +- ast_fwd_decl.hpp | 6 +- context.cpp | 50 +++++--- contextualize_eval.cpp | 2 +- cssize.cpp | 30 ++--- cssize.hpp | 7 +- debugger.hpp | 6 +- emitter.cpp | 2 +- emitter.hpp | 2 +- environment.hpp | 2 +- eval.cpp | 18 ++- eval.hpp | 4 +- expand.cpp | 12 +- expand.hpp | 2 +- extend.cpp | 5 +- extend.hpp | 7 +- functions.cpp | 183 +++++++++++++++--------------- functions.hpp | 2 - inspect.cpp | 14 +-- inspect.hpp | 6 +- json.cpp | 5 + lexer.hpp | 2 +- listize.cpp | 1 + operation.hpp | 14 +-- output.cpp | 4 +- output.hpp | 2 +- parser.cpp | 172 +++++++++++++++------------- parser.hpp | 41 +++---- position.cpp | 6 +- position.hpp | 8 +- posix/getopt.c | 5 + prelexer.cpp | 18 ++- prelexer.hpp | 10 +- sass.h | 6 + sass2scss.cpp | 5 + sass_context.cpp | 26 +++-- sass_functions.cpp | 2 +- sass_util.cpp | 2 +- sass_util.hpp | 2 +- test/test_node.cpp | 2 +- test/test_selector_difference.cpp | 2 +- test/test_specificity.cpp | 2 +- test/test_superselector.cpp | 4 +- test/test_unification.cpp | 2 +- to_string.cpp | 2 +- util.cpp | 33 ++++-- util.hpp | 2 +- 49 files changed, 435 insertions(+), 448 deletions(-) diff --git a/ast.cpp b/ast.cpp index 61eca23c35..4cf7bf3faf 100644 --- a/ast.cpp +++ b/ast.cpp @@ -422,40 +422,40 @@ namespace Sass { // catch-all return false; } - + Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx) { To_String to_string; - + Compound_Selector* thisBase = base(); Compound_Selector* rhsBase = other->base(); - + if( thisBase == 0 || rhsBase == 0 ) return 0; - + // Not sure about this check, but closest way I could check to see if this is a ruby 'SimpleSequence' equivalent if( tail()->combinator() != Combinator::ANCESTOR_OF || other->tail()->combinator() != Combinator::ANCESTOR_OF ) return 0; - + Compound_Selector* unified = rhsBase->unify_with(thisBase, ctx); if( unified == 0 ) return 0; - + Node lhsNode = complexSelectorToNode(this, ctx); Node rhsNode = complexSelectorToNode(other, ctx); - + // Create a temp Complex_Selector, turn it into a Node, and combine it with the existing RHS node Complex_Selector* fakeComplexSelector = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, unified, NULL); Node unifiedNode = complexSelectorToNode(fakeComplexSelector, ctx); rhsNode.plus(unifiedNode); - + Node node = Extend::subweave(lhsNode, rhsNode, ctx); - + Selector_List* result = new (ctx.mem) Selector_List(pstate()); for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { Node childNode = *iter; childNode = Node::naiveTrim(childNode, ctx); - + Complex_Selector* childNodeAsComplexSelector = nodeToComplexSelector(childNode, ctx); if( childNodeAsComplexSelector ) { (*result) << childNodeAsComplexSelector; } } - + return result->length() ? result : 0; } @@ -597,16 +597,15 @@ namespace Sass { } return false; } - + Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { - vector unified_complex_selectors; // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { Complex_Selector* seq1 = (*this)[lhs_i]; for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { Complex_Selector* seq2 = (*rhs)[rhs_i]; - + Selector_List* result = seq1->unify_with(seq2, ctx); if( result ) { for(size_t i = 0, L = result->length(); i < L; ++i) { @@ -615,24 +614,23 @@ namespace Sass { } } } - + // Creates the final Selector_List by combining all the complex selectors Selector_List* final_result = new (ctx.mem) Selector_List(pstate()); for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) { *final_result << *itr; } - return final_result; } - + void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) { To_String to_string; - + Selector_List* extender = this; for (auto complex_sel : extendee->elements()) { Complex_Selector* c = complex_sel; - - + + // Ignore any parent selectors, until we find the first non Selector_Reference head Compound_Selector* compound_sel = c->head(); Complex_Selector* pIter = complex_sel; @@ -642,63 +640,21 @@ namespace Sass { compound_sel = pHead; break; } - + pIter = pIter->tail(); } - + if (!pIter->head() || pIter->tail()) { error("nested selectors may not be extended", c->pstate()); } - + compound_sel->is_optional(extendee->is_optional()); - + for (size_t i = 0, L = extender->length(); i < L; ++i) { - // let's test this out - cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl; extends.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel)); } } }; - - - /* not used anymore - remove? - Selector_Placeholder* Selector_List::find_placeholder() - { - if (has_placeholder()) { - for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->has_placeholder()) return (*this)[i]->find_placeholder(); - } - } - return 0; - }*/ - - /* not used anymore - remove? - Selector_Placeholder* Complex_Selector::find_placeholder() - { - if (has_placeholder()) { - if (head() && head()->has_placeholder()) return head()->find_placeholder(); - else if (tail() && tail()->has_placeholder()) return tail()->find_placeholder(); - } - return 0; - }*/ - - /* not used anymore - remove? - Selector_Placeholder* Compound_Selector::find_placeholder() - { - if (has_placeholder()) { - for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->has_placeholder()) return (*this)[i]->find_placeholder(); - } - // return this; - } - return 0; - }*/ - - /* not used anymore - remove? - Selector_Placeholder* Selector_Placeholder::find_placeholder() - { - return this; - }*/ vector Compound_Selector::to_str_vec() { diff --git a/ast.hpp b/ast.hpp index 302c862d44..4140f78f96 100644 --- a/ast.hpp +++ b/ast.hpp @@ -272,7 +272,7 @@ namespace Sass { RULESET, MEDIA, DIRECTIVE, - FEATURE, + SUPPORTS, ATROOT, BUBBLE, KEYFRAMERULE @@ -398,16 +398,16 @@ namespace Sass { ATTACH_OPERATIONS() }; - /////////////////// - // Feature queries. - /////////////////// - class Feature_Block : public Has_Block { - ADD_PROPERTY(Feature_Query*, feature_queries) + ////////////////// + // Query features. + ////////////////// + class Supports_Block : public Has_Block { + ADD_PROPERTY(Supports_Query*, queries) ADD_PROPERTY(Selector*, selector) public: - Feature_Block(ParserState pstate, Feature_Query* fqs, Block* b) - : Has_Block(pstate, b), feature_queries_(fqs), selector_(0) - { statement_type(FEATURE); } + Supports_Block(ParserState pstate, Supports_Query* queries = 0, Block* block = 0) + : Has_Block(pstate, block), queries_(queries), selector_(0) + { statement_type(SUPPORTS); } bool is_hoistable() { return true; } bool bubbles() { return true; } ATTACH_OPERATIONS() @@ -663,7 +663,6 @@ namespace Sass { ADD_PROPERTY(Native_Function, native_function) ADD_PROPERTY(Sass_Function_Entry, c_function) ADD_PROPERTY(void*, cookie) - ADD_PROPERTY(Context*, ctx) ADD_PROPERTY(bool, is_overload_stub) ADD_PROPERTY(Signature, signature) public: @@ -671,7 +670,6 @@ namespace Sass { string n, Parameters* params, Block* b, - Context* ctx, Type t) : Has_Block(pstate, b), name_(n), @@ -681,7 +679,6 @@ namespace Sass { native_function_(0), c_function_(0), cookie_(0), - ctx_(ctx), is_overload_stub_(false), signature_(0) { } @@ -690,7 +687,6 @@ namespace Sass { string n, Parameters* params, Native_Function func_ptr, - Context* ctx, bool overload_stub = false) : Has_Block(pstate, 0), name_(n), @@ -700,7 +696,6 @@ namespace Sass { native_function_(func_ptr), c_function_(0), cookie_(0), - ctx_(ctx), is_overload_stub_(overload_stub), signature_(sig) { } @@ -709,7 +704,6 @@ namespace Sass { string n, Parameters* params, Sass_Function_Entry c_func, - Context* ctx, bool whatever, bool whatever2) : Has_Block(pstate, 0), @@ -720,7 +714,6 @@ namespace Sass { native_function_(0), c_function_(c_func), cookie_(sass_function_get_cookie(c_func)), - ctx_(ctx), is_overload_stub_(false), signature_(sig) { } @@ -1472,10 +1465,10 @@ namespace Sass { /////////////////// // Feature queries. /////////////////// - class Feature_Query : public Expression, public Vectorized { + class Supports_Query : public Expression, public Vectorized { public: - Feature_Query(ParserState pstate, size_t s = 0) - : Expression(pstate), Vectorized(s) + Supports_Query(ParserState pstate, size_t s = 0) + : Expression(pstate), Vectorized(s) { } ATTACH_OPERATIONS() }; @@ -1483,7 +1476,7 @@ namespace Sass { //////////////////////////////////////////////////////// // Feature expressions (for use inside feature queries). //////////////////////////////////////////////////////// - class Feature_Query_Condition : public Expression, public Vectorized { + class Supports_Condition : public Expression, public Vectorized { public: enum Operand { NONE, AND, OR, NOT }; private: @@ -1492,9 +1485,9 @@ namespace Sass { ADD_PROPERTY(Operand, operand) ADD_PROPERTY(bool, is_root) public: - Feature_Query_Condition(ParserState pstate, size_t s = 0, String* f = 0, + Supports_Condition(ParserState pstate, size_t s = 0, String* f = 0, Expression* v = 0, Operand o = NONE, bool r = false) - : Expression(pstate), Vectorized(s), + : Expression(pstate), Vectorized(s), feature_(f), value_(v), operand_(o), is_root_(r) { } ATTACH_OPERATIONS() @@ -1567,7 +1560,7 @@ namespace Sass { { return expression()->exclude("rule"); } - if (s->statement_type() == Statement::FEATURE) + if (s->statement_type() == Statement::SUPPORTS) { return expression()->exclude("supports"); } @@ -1947,7 +1940,6 @@ namespace Sass { : Selector(pstate), Vectorized(s) { } - Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx); // virtual Selector_Placeholder* find_placeholder(); Simple_Selector* base() @@ -2007,9 +1999,9 @@ namespace Sass { ADD_PROPERTY(Complex_Selector*, tail) public: Complex_Selector(ParserState pstate, - Combinator c, - Compound_Selector* h, - Complex_Selector* t) + Combinator c = ANCESTOR_OF, + Compound_Selector* h = 0, + Complex_Selector* t = 0) : Selector(pstate), combinator_(c), head_(h), tail_(t) { if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); @@ -2023,7 +2015,7 @@ namespace Sass { bool is_superselector_of(Complex_Selector* sub); bool is_superselector_of(Selector_List* sub); // virtual Selector_Placeholder* find_placeholder(); - + Selector_List* unify_with(Complex_Selector* rhs, Context& ctx); Combinator clear_innermost(); @@ -2093,7 +2085,6 @@ namespace Sass { }; typedef deque ComplexSelectorDeque; - typedef Subset_Map > ExtensionSubsetMap; /////////////////////////////////// @@ -2117,7 +2108,7 @@ namespace Sass { Selector_List* unify_with(Selector_List*, Context&); void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&); - + virtual unsigned long specificity() { unsigned long sum = 0; diff --git a/ast_factory.hpp b/ast_factory.hpp index 33646ff8b8..f4a886de86 100644 --- a/ast_factory.hpp +++ b/ast_factory.hpp @@ -15,7 +15,7 @@ namespace Sass { Block* new_Block(string p, size_t l, size_t s = 0, bool r = false); Ruleset* new_Ruleset(string p, size_t l, Selector* s, Block* b); Propset* new_Propset(string p, size_t l, String* pf, Block* b); - Feature_Query* new_Feature_Query(string p, size_t l, Feature_Query* f, Block* b); + Supports_Query* new_Supports_Query(string p, size_t l, Supports_Query* f, Block* b); Media_Query* new_Media_Query(string p, size_t l, List* q, Block* b); At_Root_Block* new_At_Root_Block(string p, size_t l, Selector* sel, Block* b); At_Rule* new_At_Rule(string p, size_t l, string kwd, Selector* sel, Block* b); @@ -67,7 +67,7 @@ namespace Sass { String_Constant* new_String_Constant(string p, size_t l, string val); String_Constant* new_String_Constant(string p, size_t l, const char* beg); String_Constant* new_String_Constant(string p, size_t l, const char* beg, const char* end); - Feature_Query_Condition* new_Feature_Query_Condition(string p, size_t l, String* f, Expression* v); + Supports_Condition* new_Supports_Condition(string p, size_t l, String* f, Expression* v); Media_Expression* new_Media_Expression(string p, size_t l, String* f, Expression* v); Parent_Selector* new_Parent_Selector(string p, size_t l, Selector* s); // parameters and arguments diff --git a/ast_fwd_decl.hpp b/ast_fwd_decl.hpp index 7da1fa7012..d5deca2ca4 100644 --- a/ast_fwd_decl.hpp +++ b/ast_fwd_decl.hpp @@ -16,7 +16,7 @@ namespace Sass { class Propset; class Bubble; class Media_Block; - class Feature_Block; + class Supports_Block; class At_Rule; class Keyframe_Rule; class At_Root_Block; @@ -56,8 +56,8 @@ namespace Sass { class String_Quoted; class Media_Query; class Media_Query_Expression; - class Feature_Query; - class Feature_Query_Condition; + class Supports_Query; + class Supports_Condition; class At_Root_Expression; class Null; class Parent_Selector; diff --git a/context.cpp b/context.cpp index 06b0e8cd20..4b7a3cef19 100644 --- a/context.cpp +++ b/context.cpp @@ -319,38 +319,55 @@ namespace Sass { 0, 0 ); import_stack.push_back(import); - Parser p(Parser::from_c_str(queue[i].source, *this, ParserState(queue[i].abs_path, queue[i].source, i))); + const char* path = sass_strdup(queue[i].abs_path.c_str()); + Parser p(Parser::from_c_str(queue[i].source, *this, ParserState(path, queue[i].source, i))); Block* ast = p.parse(); sass_delete_import(import_stack.back()); import_stack.pop_back(); if (i == 0) root = ast; + // ToDo: we store by load_path, which can lead + // to duplicates if importer reports the same path + // Maybe we should add an error for duplicates!? style_sheets[queue[i].load_path] = ast; } if (root == 0) return 0; - Env tge; + + Env global; // create root environment + // register built-in functions on env + register_built_in_functions(*this, &global); + // register custom functions (defined via C-API) + for (size_t i = 0, S = c_functions.size(); i < S; ++i) + { register_c_function(*this, &global, c_functions[i]); } + // create initial backtrace entry Backtrace backtrace(0, ParserState("", 0), ""); - register_built_in_functions(*this, &tge); - for (size_t i = 0, S = c_functions.size(); i < S; ++i) { - register_c_function(*this, &tge, c_functions[i]); - } - Contextualize contextualize(*this, &tge, &backtrace); + Contextualize contextualize(*this, &global, &backtrace); Listize listize(*this); - Eval eval(*this, &contextualize, &listize, &tge, &backtrace); - Contextualize_Eval contextualize_eval(*this, &eval, &tge, &backtrace); - Expand expand(*this, &eval, &contextualize_eval, &tge, &backtrace); - Cssize cssize(*this, &tge, &backtrace); + Eval eval(*this, &contextualize, &listize, &global, &backtrace); + Contextualize_Eval contextualize_eval(*this, &eval, &global, &backtrace); + // create crtp visitor objects + Expand expand(*this, &eval, &contextualize_eval, &global, &backtrace); + Cssize cssize(*this, &backtrace); + // expand and eval the tree root = root->perform(&expand)->block(); + // merge and bubble certain rules root = root->perform(&cssize)->block(); + // should we extend something? if (!subset_map.empty()) { + // create crtp visitor object Extend extend(*this, subset_map); + // extend tree nodes root->perform(&extend); } + // clean up by removing empty placeholders + // ToDo: maybe we can do this somewhere else? Remove_Placeholders remove_placeholders(*this); root->perform(&remove_placeholders); + // return processed tree return root; } + // EO parse_file Block* Context::parse_string() { @@ -439,12 +456,11 @@ namespace Sass { void register_overload_stub(Context& ctx, string name, Env* env) { Definition* stub = new (ctx.mem) Definition(ParserState("[built-in function]"), - 0, - name, - 0, - 0, - &ctx, - true); + 0, + name, + 0, + 0, + true); (*env)[name + "[f]"] = stub; } diff --git a/contextualize_eval.cpp b/contextualize_eval.cpp index ba2b25d1d5..483bd8a252 100644 --- a/contextualize_eval.cpp +++ b/contextualize_eval.cpp @@ -30,7 +30,7 @@ namespace Sass { To_String to_string; string result_str(s->contents()->perform(eval)->perform(&to_string)); result_str += '{'; // the parser looks for a brace to end the selector - Selector* result_sel = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()).parse_selector_group(); + Selector* result_sel = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()).parse_selector_list(); return result_sel->perform(this); } diff --git a/cssize.cpp b/cssize.cpp index c5ae2959e6..2b01857930 100644 --- a/cssize.cpp +++ b/cssize.cpp @@ -8,9 +8,8 @@ namespace Sass { - Cssize::Cssize(Context& ctx, Env* env, Backtrace* bt) + Cssize::Cssize(Context& ctx, Backtrace* bt) : ctx(ctx), - env(env), block_stack(vector()), p_stack(vector()), backtrace(bt) @@ -23,15 +22,11 @@ namespace Sass { Statement* Cssize::operator()(Block* b) { - Env new_env; - new_env.link(*env); - env = &new_env; Block* bb = new (ctx.mem) Block(b->pstate(), b->length(), b->is_root()); // bb->tabs(b->tabs()); block_stack.push_back(bb); append_block(b); block_stack.pop_back(); - env = env->parent(); return bb; } @@ -161,7 +156,7 @@ namespace Sass { return debubble(mm->block(), mm)->block(); } - Statement* Cssize::operator()(Feature_Block* m) + Statement* Cssize::operator()(Supports_Block* m) { if (!m->block()->length()) { return m; } @@ -171,8 +166,8 @@ namespace Sass { p_stack.push_back(m); - Feature_Block* mm = new (ctx.mem) Feature_Block(m->pstate(), - m->feature_queries(), + Supports_Block* mm = new (ctx.mem) Supports_Block(m->pstate(), + m->queries(), m->block()->perform(this)->block()); mm->tabs(m->tabs()); @@ -252,7 +247,7 @@ namespace Sass { return bubble; } - Statement* Cssize::bubble(Feature_Block* m) + Statement* Cssize::bubble(Supports_Block* m) { Ruleset* parent = static_cast(shallow_copy(this->parent())); @@ -268,8 +263,8 @@ namespace Sass { Block* wrapper_block = new (ctx.mem) Block(m->block()->pstate()); *wrapper_block << new_rule; - Feature_Block* mm = new (ctx.mem) Feature_Block(m->pstate(), - m->feature_queries(), + Supports_Block* mm = new (ctx.mem) Supports_Block(m->pstate(), + m->queries(), wrapper_block); mm->tabs(m->tabs()); @@ -364,8 +359,8 @@ namespace Sass { return new (ctx.mem) Bubble(*static_cast(s)); case Statement::DIRECTIVE: return new (ctx.mem) At_Rule(*static_cast(s)); - case Statement::FEATURE: - return new (ctx.mem) Feature_Block(*static_cast(s)); + case Statement::SUPPORTS: + return new (ctx.mem) Supports_Block(*static_cast(s)); case Statement::ATROOT: return new (ctx.mem) At_Root_Block(*static_cast(s)); case Statement::KEYFRAMERULE: @@ -427,10 +422,9 @@ namespace Sass { else { List* mq = merge_media_queries(static_cast(b->node()), static_cast(parent)); - if (mq->length()) { - static_cast(b->node())->media_queries(mq); - ss = b->node(); - } + if (!mq->length()) continue; + static_cast(b->node())->media_queries(mq); + ss = b->node(); } if (!ss) continue; diff --git a/cssize.hpp b/cssize.hpp index 54cf70f5cb..f6829c88bf 100644 --- a/cssize.hpp +++ b/cssize.hpp @@ -18,7 +18,6 @@ namespace Sass { class Cssize : public Operation_CRTP { Context& ctx; - Env* env; vector block_stack; vector p_stack; Backtrace* backtrace; @@ -26,7 +25,7 @@ namespace Sass { Statement* fallback_impl(AST_Node* n); public: - Cssize(Context&, Env*, Backtrace*); + Cssize(Context&, Backtrace*); virtual ~Cssize() { } using Operation::operator(); @@ -36,7 +35,7 @@ namespace Sass { // Statement* operator()(Propset*); // Statement* operator()(Bubble*); Statement* operator()(Media_Block*); - Statement* operator()(Feature_Block*); + Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); Statement* operator()(At_Rule*); Statement* operator()(Keyframe_Rule*); @@ -63,7 +62,7 @@ namespace Sass { Statement* bubble(At_Rule*); Statement* bubble(At_Root_Block*); Statement* bubble(Media_Block*); - Statement* bubble(Feature_Block*); + Statement* bubble(Supports_Block*); Statement* shallow_copy(Statement*); Statement* debubble(Block* children, Statement* parent = 0); Statement* flatten(Statement*); diff --git a/debugger.hpp b/debugger.hpp index 3a9af65dce..403705988f 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -224,9 +224,9 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) debug_ast(block->media_queries(), ind + " =@ "); debug_ast(block->selector(), ind + " -@ "); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } - } else if (dynamic_cast(node)) { - Feature_Block* block = dynamic_cast(node); - cerr << ind << "Feature_Block " << block; + } else if (dynamic_cast(node)) { + Supports_Block* block = dynamic_cast(node); + cerr << ind << "Supports_Block " << block; cerr << " (" << pstate_source_position(node) << ")"; cerr << " " << block->tabs() << endl; if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } diff --git a/emitter.cpp b/emitter.cpp index 9d7a8a0130..555cefdf52 100644 --- a/emitter.cpp +++ b/emitter.cpp @@ -171,7 +171,7 @@ namespace Sass { void Emitter::append_comma_separator() { - scheduled_space = 0; + // scheduled_space = 0; append_string(","); append_optional_space(); } diff --git a/emitter.hpp b/emitter.hpp index a69f28f5b2..2ee538a24e 100644 --- a/emitter.hpp +++ b/emitter.hpp @@ -18,7 +18,7 @@ namespace Sass { protected: OutputBuffer wbuf; public: - const string buffer(void) { return wbuf.buffer; } + const string& buffer(void) { return wbuf.buffer; } const SourceMap smap(void) { return wbuf.smap; } const OutputBuffer output(void) { return wbuf; } // proxy methods for source maps diff --git a/environment.hpp b/environment.hpp index 3e80e90eff..2f3d797f0c 100644 --- a/environment.hpp +++ b/environment.hpp @@ -42,7 +42,7 @@ namespace Sass { // there is still a parent around // not sure what it is actually use for // I guess we store functions etc. there - bool is_root_scope() const + bool is_global() const { return parent_ && ! parent_->parent_; } diff --git a/eval.cpp b/eval.cpp index 1e4cd77ad2..a55dda62b7 100644 --- a/eval.cpp +++ b/eval.cpp @@ -629,6 +629,7 @@ namespace Sass { env = old_env; } // else if it's a user-defined c function + // convert call into C-API compatible form else if (c_function) { Sass_Function_Fn c_func = sass_function_get_function(c_function); @@ -919,10 +920,7 @@ namespace Sass { { string acc; for (size_t i = 0, L = s->length(); i < L; ++i) { - // if (String_Quoted* str_quoted = dynamic_cast((*s)[i])) { - // if (!str_quoted->is_delayed()) str_quoted->value(string_eval_escapes(str_quoted->value())); - // } - acc += interpolation((*s)[i]); + if ((*s)[i]) acc += interpolation((*s)[i]); } String_Quoted* str = new (ctx.mem) String_Quoted(s->pstate(), acc); if (!str->quote_mark()) { @@ -945,29 +943,29 @@ namespace Sass { return s; } - Expression* Eval::operator()(Feature_Query* q) + Expression* Eval::operator()(Supports_Query* q) { - Feature_Query* qq = new (ctx.mem) Feature_Query(q->pstate(), + Supports_Query* qq = new (ctx.mem) Supports_Query(q->pstate(), q->length()); for (size_t i = 0, L = q->length(); i < L; ++i) { - *qq << static_cast((*q)[i]->perform(this)); + *qq << static_cast((*q)[i]->perform(this)); } return qq; } - Expression* Eval::operator()(Feature_Query_Condition* c) + Expression* Eval::operator()(Supports_Condition* c) { String* feature = c->feature(); Expression* value = c->value(); value = (value ? value->perform(this) : 0); - Feature_Query_Condition* cc = new (ctx.mem) Feature_Query_Condition(c->pstate(), + Supports_Condition* cc = new (ctx.mem) Supports_Condition(c->pstate(), c->length(), feature, value, c->operand(), c->is_root()); for (size_t i = 0, L = c->length(); i < L; ++i) { - *cc << static_cast((*c)[i]->perform(this)); + *cc << static_cast((*c)[i]->perform(this)); } return cc; } diff --git a/eval.hpp b/eval.hpp index 86e95eee83..cabcf712a2 100644 --- a/eval.hpp +++ b/eval.hpp @@ -63,8 +63,8 @@ namespace Sass { Expression* operator()(Media_Query*); Expression* operator()(Media_Query_Expression*); Expression* operator()(At_Root_Expression*); - Expression* operator()(Feature_Query*); - Expression* operator()(Feature_Query_Condition*); + Expression* operator()(Supports_Query*); + Expression* operator()(Supports_Condition*); Expression* operator()(Null*); Expression* operator()(Argument*); Expression* operator()(Arguments*); diff --git a/expand.cpp b/expand.cpp index 2c990b0feb..6019deed54 100644 --- a/expand.cpp +++ b/expand.cpp @@ -72,7 +72,7 @@ namespace Sass { p.source = str.c_str(); p.position = str.c_str(); p.end = str.c_str() + strlen(str.c_str()); - Selector_List* sel_lst = p.parse_selector_group(); + Selector_List* sel_lst = p.parse_selector_list(); // sel_lst->pstate(isp.remap(sel_lst->pstate())); for(size_t i = 0; i < sel_lst->length(); i++) { @@ -137,11 +137,11 @@ namespace Sass { return 0; } - Statement* Expand::operator()(Feature_Block* f) + Statement* Expand::operator()(Supports_Block* f) { - Expression* feature_queries = f->feature_queries()->perform(eval->with(env, backtrace)); - Feature_Block* ff = new (ctx.mem) Feature_Block(f->pstate(), - static_cast(feature_queries), + Expression* queries = f->queries()->perform(eval->with(env, backtrace)); + Supports_Block* ff = new (ctx.mem) Supports_Block(f->pstate(), + static_cast(queries), f->block()->perform(this)->block()); ff->selector(selector_stack.back()); return ff; @@ -534,7 +534,6 @@ namespace Sass { "@content", new (ctx.mem) Parameters(c->pstate()), c->block(), - &ctx, Definition::MIXIN); thunk->environment(env); new_env.local_frame()["@content[m]"] = thunk; @@ -558,6 +557,7 @@ namespace Sass { return call->perform(this); } + // produce an error if something is not implemented inline Statement* Expand::fallback_impl(AST_Node* n) { error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace); diff --git a/expand.hpp b/expand.hpp index 58c6d4ea02..32290eedf5 100644 --- a/expand.hpp +++ b/expand.hpp @@ -46,7 +46,7 @@ namespace Sass { Statement* operator()(Ruleset*); Statement* operator()(Propset*); Statement* operator()(Media_Block*); - Statement* operator()(Feature_Block*); + Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); Statement* operator()(At_Rule*); Statement* operator()(Declaration*); diff --git a/extend.cpp b/extend.cpp index 65fb6ef273..48e163b136 100644 --- a/extend.cpp +++ b/extend.cpp @@ -1596,7 +1596,6 @@ namespace Sass { if (pSelector && pSelector->has_line_feed()) pNewSelector->has_line_feed(true); - // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending. DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector, ctx)) @@ -1961,7 +1960,7 @@ namespace Sass { (pNewSelectorList->perform(&to_string) + ";").c_str(), ctx, pNewSelectorList->pstate() - ).parse_selector_group() + ).parse_selector_list() ); } else { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") @@ -1988,7 +1987,7 @@ namespace Sass { pRuleset->block()->perform(this); } - void Extend::operator()(Feature_Block* pFeatureBlock) + void Extend::operator()(Supports_Block* pFeatureBlock) { if (pFeatureBlock->selector()) { extendObjectWithSelectorAndBlock(pFeatureBlock, ctx, subset_map); diff --git a/extend.hpp b/extend.hpp index eb2dd9d197..1f5a5bb4fd 100644 --- a/extend.hpp +++ b/extend.hpp @@ -26,6 +26,8 @@ namespace Sass { void fallback_impl(AST_Node* n) { } public: + static Node subweave(Node& one, Node& two, Context& ctx); + static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething); Extend(Context&, ExtensionSubsetMap&); virtual ~Extend() { } @@ -33,15 +35,12 @@ namespace Sass { void operator()(Block*); void operator()(Ruleset*); - void operator()(Feature_Block*); + void operator()(Supports_Block*); void operator()(Media_Block*); void operator()(At_Rule*); template void fallback(U x) { return fallback_impl(x); } - - static Node subweave(Node& one, Node& two, Context& ctx); - static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething); }; } diff --git a/functions.cpp b/functions.cpp index ee9b42765f..511600b9ce 100644 --- a/functions.cpp +++ b/functions.cpp @@ -47,7 +47,6 @@ namespace Sass { name, params, func, - &ctx, false); } @@ -68,7 +67,6 @@ namespace Sass { name, params, c_func, - &ctx, false, true); } @@ -118,12 +116,12 @@ namespace Sass { } return val; } - -#define ARGSEL(argname, seltype, contextualize) get_arg_sel(argname, env, sig, pstate, backtrace, ctx) + + #define ARGSEL(argname, seltype, contextualize) get_arg_sel(argname, env, sig, pstate, backtrace, ctx) template T* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx); - + template <> Selector_List* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { To_String to_string(&ctx, false); @@ -131,7 +129,7 @@ namespace Sass { string exp_src = exp->perform(&to_string) + "{"; return Parser::parse_selector(exp_src.c_str(), ctx); } - + template <> Complex_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { To_String to_string(&ctx, false); @@ -140,7 +138,7 @@ namespace Sass { Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx); return (sel_list->length() > 0) ? sel_list->first() : 0; } - + template <> Compound_Selector* get_arg_sel(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { To_String to_string(&ctx, false); @@ -150,7 +148,7 @@ namespace Sass { return (sel_list->length() > 0) ? sel_list->first()->tail()->head() : 0; } -#ifdef __MINGW32__ + #ifdef __MINGW32__ uint64_t GetSeed() { HCRYPTPROV hp = 0; @@ -164,13 +162,13 @@ namespace Sass { return seed; } -#else + #else static random_device rd; uint64_t GetSeed() { - return rd(); - } -#endif + return rd(); + } + #endif // note: the performance of many implementations of // random_device degrades sharply once the entropy pool @@ -854,7 +852,7 @@ namespace Sass { error(msg, pstate, backtrace); } catch (...) { throw; } - // handle any invalid utf8 errors + // return something even if we had an error (-1) return new (ctx.mem) Number(pstate, len); } @@ -945,7 +943,7 @@ namespace Sass { error(msg, pstate, backtrace); } catch (...) { throw; } - // handle any invalid utf8 errors + // return something even if we had an error (-1) return new (ctx.mem) Number(pstate, index); } @@ -1608,13 +1606,13 @@ namespace Sass { Signature selector_nest_sig = "selector-nest($selectors...)"; BUILT_IN(selector_nest) { - To_String to_string; + To_String to_string(&ctx, false); List* arglist = ARG("$selectors", List); - + // Not enough parameters if( arglist->length() == 0 ) error("$selectors: At least one selector must be passed", pstate); - + // Parse args into vector of selectors vector parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) { @@ -1623,21 +1621,21 @@ namespace Sass { Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); parsedSelectors.push_back(sel); } - + // Nothing to do if( parsedSelectors.empty() ) { return new (ctx.mem) Null(pstate); } - + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. std::vector::iterator itr = parsedSelectors.begin(); Selector_List* result = *itr; ++itr; - + for(;itr != parsedSelectors.end(); ++itr) { Selector_List* child = *itr; - vector newElements; - + vector exploded; + // For every COMPLEX_SELECTOR in `child` // For every COMPLEX_SELECTOR in `result` // let parentSeqClone equal a copy of result->elements[i] @@ -1647,32 +1645,30 @@ namespace Sass { // Replace result->elements with newElements for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { - Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx); - Complex_Selector* childSeq = (*child)[j]; - - parentSeqClone->innermost()->tail(childSeq); // Set seq as the new tail of parentSeqClone - newElements.push_back(parentSeqClone); + Complex_Selector* parent = (*result)[i]->cloneFully(ctx); + Complex_Selector* selector = (*child)[j]; + parent->innermost()->tail(selector); + exploded.push_back(parent); } } - - result->elements(newElements); + + result->elements(exploded); } - - + Listize listize(ctx); return result->perform(&listize); } - + Signature selector_append_sig = "selector-append($selectors...)"; BUILT_IN(selector_append) { To_String to_string; List* arglist = ARG("$selectors", List); - + // Not enough parameters if( arglist->length() == 0 ) error("$selectors: At least one selector must be passed", pstate); - + // Parse args into vector of selectors vector parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) { @@ -1681,21 +1677,21 @@ namespace Sass { Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); parsedSelectors.push_back(sel); } - + // Nothing to do if( parsedSelectors.empty() ) { return new (ctx.mem) Null(pstate); } - + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. std::vector::iterator itr = parsedSelectors.begin(); Selector_List* result = *itr; ++itr; - + for(;itr != parsedSelectors.end(); ++itr) { Selector_List* child = *itr; vector newElements; - + // For every COMPLEX_SELECTOR in `result` // For every COMPLEX_SELECTOR in `child` // let parentSeqClone equal a copy of result->elements[i] @@ -1708,7 +1704,7 @@ namespace Sass { Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx); Complex_Selector* childSeq = (*child)[j]; Complex_Selector* base = childSeq->tail(); - + // Must be a simple sequence if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) { string msg("Can't append `"); @@ -1718,7 +1714,7 @@ namespace Sass { msg += "`"; error(msg, pstate, backtrace); } - + // Cannot be a Universal selector Type_Selector* pType = dynamic_cast(base->head()->first()); if(pType && pType->name() == "*") { @@ -1729,71 +1725,101 @@ namespace Sass { msg += "`"; error(msg, pstate, backtrace); } - + // TODO: Add check for namespace stuff - + // append any selectors in childSeq's head *(parentSeqClone->innermost()->head()) += (base->head()); - + // Set parentSeqClone new tail parentSeqClone->innermost()->tail( base->tail() ); - + newElements.push_back(parentSeqClone); } } - + result->elements(newElements); } - + + Listize listize(ctx); + return result->perform(&listize); + } + + Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; + BUILT_IN(selector_unify) + { + Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize); + Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize); + + Selector_List* result = selector1->unify_with(selector2, ctx); Listize listize(ctx); return result->perform(&listize); } - + + Signature simple_selectors_sig = "simple-selectors($selector)"; + BUILT_IN(simple_selectors) + { + Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize); + + To_String to_string; + List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA); + + for (size_t i = 0, L = sel->length(); i < L; ++i) { + Simple_Selector* ss = (*sel)[i]; + string ss_string = ss->perform(&to_string) ; + + *l << new (ctx.mem) String_Constant(ss->pstate(), ss_string); + } + + return l; + } + Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; BUILT_IN(selector_extend) { To_String to_string; - + Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); Selector_List* extendee = ARGSEL("$extendee", Selector_List, p_contextualize); Selector_List* extender = ARGSEL("$extender", Selector_List, p_contextualize); - + ExtensionSubsetMap subset_map; extender->populate_extends(extendee, ctx, subset_map); - + bool extendedSomething; Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething); - + Listize listize(ctx); return result->perform(&listize); } - + Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; BUILT_IN(selector_replace) { Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); Selector_List* original = ARGSEL("$original", Selector_List, p_contextualize); Selector_List* replacement = ARGSEL("$replacement", Selector_List, p_contextualize); - + ExtensionSubsetMap subset_map; replacement->populate_extends(original, ctx, subset_map); - + bool extendedSomething; Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething); - + Listize listize(ctx); return result->perform(&listize); } - - Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; - BUILT_IN(selector_unify) + + Signature selector_parse_sig = "selector-parse($selector)"; + BUILT_IN(selector_parse) { - Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize); - Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize); - - Selector_List* result = selector1->unify_with(selector2, ctx); + To_String to_string(&ctx, false); + Expression* exp = ARG("$selector", Expression); + string sel_src = exp->perform(&to_string) + "{"; + Selector_List* sel = Parser::parse_selector(sel_src.c_str(), ctx); + Listize listize(ctx); - return result->perform(&listize); + return sel->perform(&listize); } Signature is_superselector_sig = "is-superselector($super, $sub)"; @@ -1809,37 +1835,6 @@ namespace Sass { bool result = sel_sup->is_superselector_of(sel_sub); return new (ctx.mem) Boolean(pstate, result); } - - Signature simple_selectors_sig = "simple_selectors($selector)"; - BUILT_IN(simple_selectors) - { - Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize); - - To_String to_string; - List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA); - - for (size_t i = 0, L = sel->length(); i < L; ++i) { - Simple_Selector* ss = (*sel)[i]; - string ss_string = ss->perform(&to_string) ; - - *l << new (ctx.mem) String_Constant(ss->pstate(), ss_string); - } - - - return l; - } - - Signature selector_parse_sig = "selector-parse($selector)"; - BUILT_IN(selector_parse) - { - To_String to_string(&ctx, false); - Expression* exp = ARG("$selector", Expression); - string sel_src = exp->perform(&to_string) + "{"; - Selector_List* sel = Parser::parse_selector(sel_src.c_str(), ctx); - - Listize listize(ctx); - return sel->perform(&listize); - } Signature unique_id_sig = "unique-id()"; BUILT_IN(unique_id) diff --git a/functions.hpp b/functions.hpp index 8b993e9176..96f3cedf1f 100644 --- a/functions.hpp +++ b/functions.hpp @@ -191,8 +191,6 @@ namespace Sass { BUILT_IN(is_superselector); BUILT_IN(simple_selectors); BUILT_IN(selector_parse); - - } } diff --git a/inspect.cpp b/inspect.cpp index ba56a3c8f6..9d1faf59dd 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -76,12 +76,12 @@ namespace Sass { media_block->block()->perform(this); } - void Inspect::operator()(Feature_Block* feature_block) + void Inspect::operator()(Supports_Block* feature_block) { append_indentation(); append_token("@supports", feature_block); append_mandatory_space(); - feature_block->feature_queries()->perform(this); + feature_block->queries()->perform(this); feature_block->block()->perform(this); } @@ -657,7 +657,7 @@ namespace Sass { } } - void Inspect::operator()(Feature_Query* fq) + void Inspect::operator()(Supports_Query* fq) { size_t i = 0; (*fq)[i++]->perform(this); @@ -666,17 +666,17 @@ namespace Sass { } } - void Inspect::operator()(Feature_Query_Condition* fqc) + void Inspect::operator()(Supports_Condition* fqc) { - if (fqc->operand() == Feature_Query_Condition::AND) { + if (fqc->operand() == Supports_Condition::AND) { append_mandatory_space(); append_token("and", fqc); append_mandatory_space(); - } else if (fqc->operand() == Feature_Query_Condition::OR) { + } else if (fqc->operand() == Supports_Condition::OR) { append_mandatory_space(); append_token("or", fqc); append_mandatory_space(); - } else if (fqc->operand() == Feature_Query_Condition::NOT) { + } else if (fqc->operand() == Supports_Condition::NOT) { append_mandatory_space(); append_token("not", fqc); append_mandatory_space(); diff --git a/inspect.hpp b/inspect.hpp index 7cd0f51c18..fccd3ed49d 100644 --- a/inspect.hpp +++ b/inspect.hpp @@ -28,7 +28,7 @@ namespace Sass { virtual void operator()(Ruleset*); virtual void operator()(Propset*); virtual void operator()(Bubble*); - virtual void operator()(Feature_Block*); + virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); virtual void operator()(At_Root_Block*); virtual void operator()(At_Rule*); @@ -65,8 +65,8 @@ namespace Sass { virtual void operator()(String_Schema*); virtual void operator()(String_Constant*); virtual void operator()(String_Quoted*); - virtual void operator()(Feature_Query*); - virtual void operator()(Feature_Query_Condition*); + virtual void operator()(Supports_Query*); + virtual void operator()(Supports_Condition*); virtual void operator()(Media_Query*); virtual void operator()(Media_Query_Expression*); virtual void operator()(At_Root_Expression*); diff --git a/json.cpp b/json.cpp index f92f096a24..32fda7bc98 100644 --- a/json.cpp +++ b/json.cpp @@ -21,6 +21,11 @@ THE SOFTWARE. */ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + #include "json.hpp" #include diff --git a/lexer.hpp b/lexer.hpp index de425d6d8f..0bf7081848 100644 --- a/lexer.hpp +++ b/lexer.hpp @@ -110,7 +110,7 @@ namespace Sass { } // Match for members of char class. - // Regex equivalent: /[axy]/ + // Regex equivalent: /[axy]+/ template const char* class_chars(const char* src) { const char* p = src; diff --git a/listize.cpp b/listize.cpp index 3e27803904..3457d59e63 100644 --- a/listize.cpp +++ b/listize.cpp @@ -17,6 +17,7 @@ namespace Sass { { List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) { + // if (!(*sel)[i]) continue; *l << (*sel)[i]->perform(this); } return l; diff --git a/operation.hpp b/operation.hpp index b470fe8ad8..a3dd6b8636 100644 --- a/operation.hpp +++ b/operation.hpp @@ -19,7 +19,7 @@ namespace Sass { virtual T operator()(Ruleset* x) = 0; virtual T operator()(Propset* x) = 0; virtual T operator()(Bubble* x) = 0; - virtual T operator()(Feature_Block* x) = 0; + virtual T operator()(Supports_Block* x) = 0; virtual T operator()(Media_Block* x) = 0; virtual T operator()(At_Root_Block* x) = 0; virtual T operator()(At_Rule* x) = 0; @@ -55,8 +55,8 @@ namespace Sass { virtual T operator()(Boolean* x) = 0; virtual T operator()(String_Schema* x) = 0; virtual T operator()(String_Constant* x) = 0; - virtual T operator()(Feature_Query* x) = 0; - virtual T operator()(Feature_Query_Condition* x)= 0; + virtual T operator()(Supports_Query* x) = 0; + virtual T operator()(Supports_Condition* x)= 0; virtual T operator()(Media_Query* x) = 0; virtual T operator()(Media_Query_Expression* x) = 0; virtual T operator()(At_Root_Expression* x) = 0; @@ -69,7 +69,6 @@ namespace Sass { virtual T operator()(Arguments* x) = 0; // selectors virtual T operator()(Selector_Schema* x) = 0; - virtual T operator()(Selector_Reference* x) = 0; virtual T operator()(Selector_Placeholder* x) = 0; virtual T operator()(Type_Selector* x) = 0; virtual T operator()(Selector_Qualifier* x) = 0; @@ -94,7 +93,7 @@ namespace Sass { virtual T operator()(Ruleset* x) { return static_cast(this)->fallback(x); } virtual T operator()(Propset* x) { return static_cast(this)->fallback(x); } virtual T operator()(Bubble* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Feature_Block* x) { return static_cast(this)->fallback(x); } + virtual T operator()(Supports_Block* x) { return static_cast(this)->fallback(x); } virtual T operator()(Media_Block* x) { return static_cast(this)->fallback(x); } virtual T operator()(At_Root_Block* x) { return static_cast(this)->fallback(x); } virtual T operator()(At_Rule* x) { return static_cast(this)->fallback(x); } @@ -130,8 +129,8 @@ namespace Sass { virtual T operator()(Boolean* x) { return static_cast(this)->fallback(x); } virtual T operator()(String_Schema* x) { return static_cast(this)->fallback(x); } virtual T operator()(String_Constant* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Feature_Query* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Feature_Query_Condition* x){ return static_cast(this)->fallback(x); } + virtual T operator()(Supports_Query* x) { return static_cast(this)->fallback(x); } + virtual T operator()(Supports_Condition* x) { return static_cast(this)->fallback(x); } virtual T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } virtual T operator()(Media_Query_Expression* x) { return static_cast(this)->fallback(x); } virtual T operator()(At_Root_Expression* x) { return static_cast(this)->fallback(x); } @@ -144,7 +143,6 @@ namespace Sass { virtual T operator()(Arguments* x) { return static_cast(this)->fallback(x); } // selectors virtual T operator()(Selector_Schema* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Selector_Reference* x) { return static_cast(this)->fallback(x); } virtual T operator()(Selector_Placeholder* x) { return static_cast(this)->fallback(x); } virtual T operator()(Type_Selector* x) { return static_cast(this)->fallback(x); } virtual T operator()(Selector_Qualifier* x) { return static_cast(this)->fallback(x); } diff --git a/output.cpp b/output.cpp index b0a9d24e3f..1589d5e6e9 100644 --- a/output.cpp +++ b/output.cpp @@ -196,11 +196,11 @@ namespace Sass { append_scope_closer(); } - void Output::operator()(Feature_Block* f) + void Output::operator()(Supports_Block* f) { if (f->is_invisible()) return; - Feature_Query* q = f->feature_queries(); + Supports_Query* q = f->queries(); Block* b = f->block(); // Filter out feature blocks that aren't printable (process its children though) diff --git a/output.hpp b/output.hpp index 6727866b00..90ef35e103 100644 --- a/output.hpp +++ b/output.hpp @@ -37,7 +37,7 @@ namespace Sass { virtual void operator()(Ruleset*); // virtual void operator()(Propset*); - virtual void operator()(Feature_Block*); + virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); virtual void operator()(At_Rule*); virtual void operator()(Keyframe_Rule*); diff --git a/parser.cpp b/parser.cpp index c570966ecb..2d6dcf737b 100644 --- a/parser.cpp +++ b/parser.cpp @@ -46,7 +46,7 @@ namespace Sass { Parser p = Parser::from_c_str(src, ctx, pstate); // ToDo: ruby sass errors on parent references // ToDo: remap the source-map entries somehow - return p.parse_selector_group(); + return p.parse_selector_list(); } bool Parser::peek_newline(const char* start) @@ -89,7 +89,7 @@ namespace Sass { bool semicolon = false; string error_message; lex< optional_spaces >(); - Selector_Lookahead lookahead_result; + Lookahead lookahead_result; while (position < end) { parse_block_comments(root); if (peek< kwd_import >()) { @@ -114,8 +114,8 @@ namespace Sass { // parse comment blocks - else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) { - Mixin_Call* mixin_call = parse_mixin_call(); + else if (peek< kwd_include_directive >() /* || peek< exactly<'+'> >() */) { + Mixin_Call* mixin_call = parse_include_directive(); (*root) << mixin_call; if (!mixin_call->block()) { semicolon = true; @@ -140,8 +140,8 @@ namespace Sass { else if (peek< kwd_at_root >()) { (*root) << parse_at_root_block(); } - else if (peek< kwd_supports >()) { - (*root) << parse_feature_block(); + else if (peek< kwd_supports_directive >()) { + (*root) << parse_supports_directive(); } else if (peek< kwd_warn >()) { (*root) << parse_warning(); @@ -358,7 +358,7 @@ namespace Sass { else stack.push_back(function_def); Block* body = parse_block(); stack.pop_back(); - Definition* def = new (ctx.mem) Definition(source_position_of_def, name, params, body, &ctx, which_type); + Definition* def = new (ctx.mem) Definition(source_position_of_def, name, params, body, which_type); return def; } @@ -399,9 +399,9 @@ namespace Sass { return p; } - Mixin_Call* Parser::parse_mixin_call() + Mixin_Call* Parser::parse_include_directive() { - lex< kwd_include >() /* || lex< exactly<'+'> >() */; + lex< kwd_include_directive >() /* || lex< exactly<'+'> >() */; if (!lex< identifier >()) error("invalid name in @include directive", pstate); ParserState source_position_of_call = pstate; string name(Util::normalize_underscores(lexed)); @@ -469,7 +469,7 @@ namespace Sass { ParserState var_source_position = pstate; if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate); Expression* val; - Selector_Lookahead lookahead = lookahead_for_value(position); + Lookahead lookahead = lookahead_for_value(position); if (lookahead.has_interpolants && lookahead.found) { val = parse_value_schema(lookahead.found); } else { @@ -487,14 +487,14 @@ namespace Sass { } // a ruleset connects a selector and a block - Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead) + Ruleset* Parser::parse_ruleset(Lookahead lookahead) { Selector* sel; if (lookahead.has_interpolants) { sel = parse_selector_schema(lookahead.found); } else { - sel = parse_selector_group(); + sel = parse_selector_list(); } bool old_in_at_root = in_at_root; ParserState r_source_position = pstate; @@ -544,20 +544,24 @@ namespace Sass { else { // make sure to add the last bits of the string up to the end (if any) if (i < end_of_selector) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, end_of_selector)); - break; + // exit loop + i = end_of_selector; } } // EO until eos - position = end_of_selector; + + // update position + position = i; + Selector_Schema* selector_schema = new (ctx.mem) Selector_Schema(pstate, schema); selector_schema->media_block(last_media_block); selector_schema->last_block(block_stack.back()); - // return parsed result + // update position return selector_schema; } // EO parse_selector_schema - Selector_List* Parser::parse_selector_group() + Selector_List* Parser::parse_selector_list() { bool reloop = true; To_String to_string(&ctx); @@ -574,7 +578,7 @@ namespace Sass { exactly<';'> > >()) break; // in case there are superfluous commas at the end - Complex_Selector* comb = parse_selector_combination(); + Complex_Selector* comb = parse_complex_selector(); if (!comb->has_reference() && !in_at_root) { ParserState sel_source_position = pstate; Selector_Reference* ref = new (ctx.mem) Selector_Reference(sel_source_position); @@ -607,13 +611,13 @@ namespace Sass { (*group) << comb; } while (reloop); - while (lex< optional >()) { + while (lex< kwd_optional >()) { group->is_optional(true); } return group; } - Complex_Selector* Parser::parse_selector_combination() + Complex_Selector* Parser::parse_complex_selector() { Position sel_source_position(-1); Compound_Selector* lhs; @@ -625,7 +629,7 @@ namespace Sass { // parse the left hand side { lhs = 0; } else { - lhs = parse_simple_selector_sequence(); + lhs = parse_compound_selector(); sel_source_position = before_token; lhs->has_line_break(peek_newline()); } @@ -644,12 +648,12 @@ namespace Sass { exactly<'{'>, exactly<'}'>, exactly<';'>, - optional + kwd_optional > >()) // parse combinator between lhs and rhs { rhs = 0; } else { - rhs = parse_selector_combination(); + rhs = parse_complex_selector(); sel_source_position = before_token; } if (!sel_source_position.line) sel_source_position = before_token; @@ -660,7 +664,7 @@ namespace Sass { return cpx; } - Compound_Selector* Parser::parse_simple_selector_sequence() + Compound_Selector* Parser::parse_compound_selector() { Compound_Selector* seq = new (ctx.mem) Compound_Selector(pstate); seq->media_block(last_media_block); @@ -678,10 +682,10 @@ namespace Sass { return seq; } } - if (sawsomething && lex_css< sequence< negate< functional >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) { + if (sawsomething && lex_css< sequence< negate< re_pseudo_selector >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) { // peek for abort conditions (*seq) << new (ctx.mem) Type_Selector(pstate, unquote(lexed)); - } else if (lex_css< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) { + } else if (lex_css< sequence< negate< re_pseudo_selector >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) { // otherwise parse another simple selector (*seq) << new (ctx.mem) Type_Selector(pstate, lexed); sawsomething = true; @@ -722,7 +726,7 @@ namespace Sass { else if (peek< pseudo_not >()) { return parse_negated_selector(); } - else if (peek< exactly<':'> >(position) || peek< functional >()) { + else if (peek< exactly<':'> >(position) || peek< re_pseudo_selector >()) { return parse_pseudo_selector(); } else if (peek< exactly<'['> >(position)) { @@ -746,7 +750,7 @@ namespace Sass { lex< pseudo_not >(); string name(lexed); ParserState nsource_position = pstate; - Selector* negated = parse_selector_group(); + Selector* negated = parse_selector_list(); if (!lex< exactly<')'> >()) { error("negated selector is missing ')'", pstate); } @@ -754,7 +758,7 @@ namespace Sass { } Simple_Selector* Parser::parse_pseudo_selector() { - if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) { + if (lex< sequence< pseudo_prefix, re_pseudo_selector > >() || lex< re_pseudo_selector >()) { string name(lexed); String* expr = 0; ParserState p = pstate; @@ -790,7 +794,7 @@ namespace Sass { expr = new (ctx.mem) String_Constant(p, ""); } else { - wrapped = parse_selector_group(); + wrapped = parse_selector_list(); } if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)", pstate); if (wrapped) { @@ -850,7 +854,7 @@ namespace Sass { { lex< exactly<'{'> >(); bool semicolon = false; - Selector_Lookahead lookahead_result; + Lookahead lookahead_result; Block* block = new (ctx.mem) Block(pstate); block_stack.push_back(block); lex< zero_plus < alternatives < space, line_comment > > >(); @@ -900,8 +904,8 @@ namespace Sass { else if (peek < kwd_while_directive >()) { (*block) << parse_while_directive(); } - else if (lex < kwd_return_directive >()) { - (*block) << new (ctx.mem) Return(pstate, parse_list()); + else if (peek < kwd_return_directive >()) { + (*block) << parse_return_directive(); semicolon = true; } else if (peek< kwd_warn >()) { @@ -922,8 +926,8 @@ namespace Sass { else if (peek< kwd_mixin >() || peek< kwd_function >()) { (*block) << parse_definition(); } - else if (peek< kwd_include >(position)) { - Mixin_Call* the_call = parse_mixin_call(); + else if (peek< kwd_include_directive >(position)) { + Mixin_Call* the_call = parse_include_directive(); (*block) << the_call; // don't need a semicolon after a content block semicolon = (the_call->block()) ? false : true; @@ -942,19 +946,19 @@ namespace Sass { } */ else if (lex< kwd_extend >()) { - Selector_Lookahead lookahead = lookahead_for_extension_target(position); + Lookahead lookahead = lookahead_for_include(position); if (!lookahead.found) error("invalid selector for @extend", pstate); Selector* target; if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found); - else target = parse_selector_group(); + else target = parse_selector_list(); (*block) << new (ctx.mem) Extension(pstate, target); semicolon = true; } else if (peek< kwd_media >()) { (*block) << parse_media_block(); } - else if (peek< kwd_supports >()) { - (*block) << parse_feature_block(); + else if (peek< kwd_supports_directive >()) { + (*block) << parse_supports_directive(); } else if (peek< kwd_at_root >()) { (*block) << parse_at_root_block(); @@ -1028,7 +1032,7 @@ namespace Sass { } else { Expression* value; - Selector_Lookahead lookahead = lookahead_for_value(position); + Lookahead lookahead = lookahead_for_value(position); if (lookahead.found) { if (lookahead.has_interpolants) { value = parse_value_schema(lookahead.found); @@ -1263,7 +1267,7 @@ namespace Sass { Expression* Parser::parse_expression() { - Expression* term1 = parse_term(); + Expression* term1 = parse_operators(); // if it's a singleton, return it (don't wrap it) if (!(peek< exactly<'+'> >(position) || (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || @@ -1275,13 +1279,13 @@ namespace Sass { vector operators; while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) { operators.push_back(lexed.to_string() == "+" ? Binary_Expression::ADD : Binary_Expression::SUB); - operands.push_back(parse_term()); + operands.push_back(parse_operators()); } return fold_operands(term1, operands, operators); } - Expression* Parser::parse_term() + Expression* Parser::parse_operators() { Expression* factor = parse_factor(); // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant @@ -1347,7 +1351,7 @@ namespace Sass { else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) { return parse_identifier_schema(); } - else if (peek< functional >()) { + else if (peek< re_pseudo_selector >()) { return parse_function_call(); } else if (lex< sequence< exactly<'+'>, optional_css_whitespace, negate< number > > >()) { @@ -1373,13 +1377,12 @@ namespace Sass { lex< css_comments >(); if (lex< ampersand >()) { - return new (ctx.mem) Parent_Selector(pstate, parse_selector_group()); } + return new (ctx.mem) Parent_Selector(pstate, parse_selector_list()); } - if (lex< important >()) + if (lex< kwd_important >()) { return new (ctx.mem) String_Constant(pstate, "!important"); } - const char* stop; - if ((stop = peek< value_schema >())) + if (const char* stop = peek< value_schema >()) { return parse_value_schema(stop); } if (lex< kwd_true >()) @@ -1639,7 +1642,6 @@ namespace Sass { return schema; } - // this parses interpolation outside other strings // means the result must not be quoted again later String* Parser::parse_identifier_schema() { @@ -1880,28 +1882,28 @@ namespace Sass { return new (ctx.mem) Media_Query_Expression(feature->pstate(), feature, expression); } - Feature_Block* Parser::parse_feature_block() + Supports_Block* Parser::parse_supports_directive() { - lex< kwd_supports >(); + lex< kwd_supports_directive >(); ParserState supports_source_position = pstate; - Feature_Query* feature_queries = parse_feature_queries(); + Supports_Query* queries = parse_supports_queries(); if (!peek< exactly<'{'> >()) { error("expected '{' in feature query", pstate); } Block* block = parse_block(); - return new (ctx.mem) Feature_Block(supports_source_position, feature_queries, block); + return new (ctx.mem) Supports_Block(supports_source_position, queries, block); } - Feature_Query* Parser::parse_feature_queries() + Supports_Query* Parser::parse_supports_queries() { - Feature_Query* fq = new (ctx.mem) Feature_Query(pstate); - Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(pstate); + Supports_Query* fq = new (ctx.mem) Supports_Query(pstate); + Supports_Condition* cond = new (ctx.mem) Supports_Condition(pstate); cond->is_root(true); while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position)) - (*cond) << parse_feature_query(); + (*cond) << parse_supports_query(); (*fq) << cond; if (fq->empty()) error("expected @supports condition (e.g. (display: flexbox))", pstate); @@ -1909,7 +1911,7 @@ namespace Sass { return fq; } - Feature_Query_Condition* Parser::parse_feature_query() + Supports_Condition* Parser::parse_supports_query() { if (peek< kwd_not >(position)) return parse_supports_negation(); else if (peek< kwd_and >(position)) return parse_supports_conjunction(); @@ -1918,52 +1920,52 @@ namespace Sass { else return parse_supports_declaration(); } - Feature_Query_Condition* Parser::parse_feature_query_in_parens() + Supports_Condition* Parser::parse_feature_query_in_parens() { - Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(pstate); + Supports_Condition* cond = new (ctx.mem) Supports_Condition(pstate); if (!lex< exactly<'('> >()) error("@supports declaration expected '('", pstate); while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position)) - (*cond) << parse_feature_query(); + (*cond) << parse_supports_query(); if (!lex< exactly<')'> >()) error("unclosed parenthesis in @supports declaration", pstate); return (cond->length() == 1) ? (*cond)[0] : cond; } - Feature_Query_Condition* Parser::parse_supports_negation() + Supports_Condition* Parser::parse_supports_negation() { lex< kwd_not >(); - Feature_Query_Condition* cond = parse_feature_query(); - cond->operand(Feature_Query_Condition::NOT); + Supports_Condition* cond = parse_supports_query(); + cond->operand(Supports_Condition::NOT); return cond; } - Feature_Query_Condition* Parser::parse_supports_conjunction() + Supports_Condition* Parser::parse_supports_conjunction() { lex< kwd_and >(); - Feature_Query_Condition* cond = parse_feature_query(); - cond->operand(Feature_Query_Condition::AND); + Supports_Condition* cond = parse_supports_query(); + cond->operand(Supports_Condition::AND); return cond; } - Feature_Query_Condition* Parser::parse_supports_disjunction() + Supports_Condition* Parser::parse_supports_disjunction() { lex< kwd_or >(); - Feature_Query_Condition* cond = parse_feature_query(); - cond->operand(Feature_Query_Condition::OR); + Supports_Condition* cond = parse_supports_query(); + cond->operand(Supports_Condition::OR); return cond; } - Feature_Query_Condition* Parser::parse_supports_declaration() + Supports_Condition* Parser::parse_supports_declaration() { Declaration* declaration = parse_declaration(); - Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(declaration->pstate(), + Supports_Condition* cond = new (ctx.mem) Supports_Condition(declaration->pstate(), 1, declaration->property(), declaration->value()); @@ -1976,7 +1978,7 @@ namespace Sass { ParserState at_source_position = pstate; Block* body = 0; At_Root_Expression* expr = 0; - Selector_Lookahead lookahead_result; + Lookahead lookahead_result; in_at_root = true; if (peek< exactly<'('> >()) { expr = parse_at_root_expression(); @@ -2027,13 +2029,13 @@ namespace Sass { ParserState at_source_position = pstate; Selector* sel = 0; Expression* val = 0; - Selector_Lookahead lookahead = lookahead_for_extension_target(position); + Lookahead lookahead = lookahead_for_include(position); if (lookahead.found) { if (lookahead.has_interpolants) { sel = parse_selector_schema(lookahead.found); } else { - sel = parse_selector_group(); + sel = parse_selector_list(); } } else if (!(peek >() || peek >() || peek >())) { @@ -2064,7 +2066,13 @@ namespace Sass { return new (ctx.mem) Debug(pstate, parse_list()); } - Selector_Lookahead Parser::lookahead_for_selector(const char* start) + Return* Parser::parse_return_directive() + { + lex< kwd_return_directive >(); + return new (ctx.mem) Return(pstate, parse_list()); + } + + Lookahead Parser::lookahead_for_selector(const char* start) { const char* p = start ? start : position; const char* q; @@ -2121,14 +2129,14 @@ namespace Sass { if (*(p - 1) == '}') saw_interpolant = true; } - Selector_Lookahead result; + Lookahead result; result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0; result.has_interpolants = saw_interpolant; return result; } - Selector_Lookahead Parser::lookahead_for_extension_target(const char* start) + Lookahead Parser::lookahead_for_include(const char* start) { const char* p = start ? start : position; const char* q; @@ -2176,13 +2184,13 @@ namespace Sass { (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) || (q = peek< sequence< pseudo_prefix, interpolant > >(p)) || (q = peek< interpolant >(p)) || - (q = peek< optional >(p))) { + (q = peek< kwd_optional >(p))) { p = q; if (*(p - 1) == '}') saw_interpolant = true; saw_stuff = true; } - Selector_Lookahead result; + Lookahead result; result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0; result.has_interpolants = saw_interpolant; @@ -2190,7 +2198,7 @@ namespace Sass { } - Selector_Lookahead Parser::lookahead_for_value(const char* start) + Lookahead Parser::lookahead_for_value(const char* start) { const char* p = start ? start : position; const char* q; @@ -2226,13 +2234,13 @@ namespace Sass { (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) || (q = peek< sequence< pseudo_prefix, interpolant > >(p)) || (q = peek< interpolant >(p)) || - (q = peek< optional >(p))) { + (q = peek< kwd_optional >(p))) { p = q; if (*(p - 1) == '}') saw_interpolant = true; saw_stuff = true; } - Selector_Lookahead result; + Lookahead result; result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0; result.has_interpolants = saw_interpolant; diff --git a/parser.hpp b/parser.hpp index d5cd3123fe..0a8303d47f 100644 --- a/parser.hpp +++ b/parser.hpp @@ -11,8 +11,9 @@ #include "position.hpp" #include "prelexer.hpp" -struct Selector_Lookahead { +struct Lookahead { const char* found; + const char* position; bool has_interpolants; }; @@ -27,7 +28,6 @@ namespace Sass { void add_single_file (Import* imp, string import_path); void import_single_file (Import* imp, string import_path); public: - class AST_Node; enum Syntactic_Context { nothing, mixin_def, function_def }; bool do_import(const string& import_path, Import* imp, vector importers, bool only_one = true); @@ -144,7 +144,7 @@ namespace Sass { // assertion that we actually lexed something if (it_after_token == it_before_token) return 0; - // create new lexed token object (holds all parse result information) + // create new lexed token object (holds the parse results) lexed = Token(position, it_before_token, it_after_token); // advance position (add whitespace before current token) @@ -207,16 +207,16 @@ namespace Sass { Definition* parse_definition(); Parameters* parse_parameters(); Parameter* parse_parameter(); - Mixin_Call* parse_mixin_call(); + Mixin_Call* parse_include_directive(); Arguments* parse_arguments(bool has_url = false); Argument* parse_argument(bool has_url = false); Assignment* parse_assignment(); // Propset* parse_propset(); - Ruleset* parse_ruleset(Selector_Lookahead lookahead); + Ruleset* parse_ruleset(Lookahead lookahead); Selector_Schema* parse_selector_schema(const char* end_of_selector); - Selector_List* parse_selector_group(); - Complex_Selector* parse_selector_combination(); - Compound_Selector* parse_simple_selector_sequence(); + Selector_List* parse_selector_list(); + Complex_Selector* parse_complex_selector(); + Compound_Selector* parse_compound_selector(); Simple_Selector* parse_simple_selector(); Wrapped_Selector* parse_negated_selector(); Simple_Selector* parse_pseudo_selector(); @@ -233,7 +233,7 @@ namespace Sass { Expression* parse_conjunction(); Expression* parse_relation(); Expression* parse_expression(); - Expression* parse_term(); + Expression* parse_operators(); Expression* parse_factor(); Expression* parse_value(); Function_Call* parse_calc_function(); @@ -253,18 +253,19 @@ namespace Sass { For* parse_for_directive(); Each* parse_each_directive(); While* parse_while_directive(); + Return* parse_return_directive(); Media_Block* parse_media_block(); List* parse_media_queries(); Media_Query* parse_media_query(); Media_Query_Expression* parse_media_expression(); - Feature_Block* parse_feature_block(); - Feature_Query* parse_feature_queries(); - Feature_Query_Condition* parse_feature_query(); - Feature_Query_Condition* parse_feature_query_in_parens(); - Feature_Query_Condition* parse_supports_negation(); - Feature_Query_Condition* parse_supports_conjunction(); - Feature_Query_Condition* parse_supports_disjunction(); - Feature_Query_Condition* parse_supports_declaration(); + Supports_Block* parse_supports_directive(); + Supports_Query* parse_supports_queries(); + Supports_Condition* parse_supports_query(); + Supports_Condition* parse_feature_query_in_parens(); + Supports_Condition* parse_supports_negation(); + Supports_Condition* parse_supports_conjunction(); + Supports_Condition* parse_supports_disjunction(); + Supports_Condition* parse_supports_declaration(); At_Root_Block* parse_at_root_block(); At_Root_Expression* parse_at_root_expression(); At_Rule* parse_at_rule(); @@ -274,9 +275,9 @@ namespace Sass { void parse_block_comments(Block* block); - Selector_Lookahead lookahead_for_value(const char* start = 0); - Selector_Lookahead lookahead_for_selector(const char* start = 0); - Selector_Lookahead lookahead_for_extension_target(const char* start = 0); + Lookahead lookahead_for_value(const char* start = 0); + Lookahead lookahead_for_selector(const char* start = 0); + Lookahead lookahead_for_include(const char* start = 0); Expression* fold_operands(Expression* base, vector& operands, Binary_Expression::Type op); Expression* fold_operands(Expression* base, vector& operands, vector& ops); diff --git a/position.cpp b/position.cpp index 31b7a733e1..fae784c557 100644 --- a/position.cpp +++ b/position.cpp @@ -96,13 +96,13 @@ namespace Sass { : Offset(line, column), file(file) { } - ParserState::ParserState(string path, const char* src, const size_t file) + ParserState::ParserState(const char* path, const char* src, const size_t file) : Position(file, 0, 0), path(path), src(src), offset(0, 0), token() { } - ParserState::ParserState(string path, const char* src, Position position, Offset offset) + ParserState::ParserState(const char* path, const char* src, const Position& position, Offset offset) : Position(position), path(path), src(src), offset(offset), token() { } - ParserState::ParserState(string path, const char* src, Token token, Position position, Offset offset) + ParserState::ParserState(const char* path, const char* src, const Token& token, const Position& position, Offset offset) : Position(position), path(path), src(src), offset(offset), token(token) { } Position Position::add(const char* begin, const char* end) diff --git a/position.hpp b/position.hpp index 1d19fcaf6f..eca275722a 100644 --- a/position.hpp +++ b/position.hpp @@ -104,9 +104,9 @@ namespace Sass { class ParserState : public Position { public: // c-tor - ParserState(string path, const char* src = 0, const size_t file = string::npos); - ParserState(string path, const char* src, Position position, Offset offset = Offset(0, 0)); - ParserState(string path, const char* src, Token token, Position position, Offset offset = Offset(0, 0)); + ParserState(const char* path, const char* src = 0, const size_t file = string::npos); + ParserState(const char* path, const char* src, const Position& position, Offset offset = Offset(0, 0)); + ParserState(const char* path, const char* src, const Token& token, const Position& position, Offset offset = Offset(0, 0)); public: // down casts Offset off() { return *this; } @@ -114,7 +114,7 @@ namespace Sass { ParserState pstate() { return *this; } public: - string path; + const char* path; const char* src; Offset offset; Token token; diff --git a/posix/getopt.c b/posix/getopt.c index ac1fda426e..e65eb0d57d 100644 --- a/posix/getopt.c +++ b/posix/getopt.c @@ -49,6 +49,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + #include #include #include diff --git a/prelexer.cpp b/prelexer.cpp index 132a6ee376..ca65af561b 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -151,8 +151,7 @@ namespace Sass { // skip escapes sequence < exactly < '\\' >, - exactly < '\r' >, - exactly < '\n' > + re_linebreak >, escape_seq, // skip interpolants @@ -175,8 +174,7 @@ namespace Sass { // skip escapes sequence < exactly < '\\' >, - exactly < '\r' >, - exactly < '\n' > + re_linebreak >, escape_seq, // skip interpolants @@ -240,7 +238,7 @@ namespace Sass { return word(src); } - const char* kwd_supports(const char* src) { + const char* kwd_supports_directive(const char* src) { return word(src); } @@ -256,7 +254,7 @@ namespace Sass { return word(src); } - const char* kwd_include(const char* src) { + const char* kwd_include_directive(const char* src) { return word(src); } @@ -477,13 +475,13 @@ namespace Sass { filename_schema >(src); // optional trailing slash }*/ // Match CSS "!important" keyword. - const char* important(const char* src) { + const char* kwd_important(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match CSS "!optional" keyword. - const char* optional(const char* src) { + const char* kwd_optional(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); @@ -508,8 +506,8 @@ namespace Sass { const char* functional_schema(const char* src) { return sequence< identifier_schema, exactly<'('> >(src); } - const char* functional(const char* src) { - return sequence< identifier, exactly<'('> >(src); + const char* re_pseudo_selector(const char* src) { + return sequence< identifier, optional < block_comment >, exactly<'('> >(src); } // Match the CSS negation pseudo-class. const char* pseudo_not(const char* src) { diff --git a/prelexer.hpp b/prelexer.hpp index 7d519c6fc0..333a5fc9b1 100644 --- a/prelexer.hpp +++ b/prelexer.hpp @@ -215,13 +215,13 @@ namespace Sass { const char* kwd_with_directive(const char* src); const char* kwd_without_directive(const char* src); const char* kwd_media(const char* src); - const char* kwd_supports(const char* src); + const char* kwd_supports_directive(const char* src); // const char* keyframes(const char* src); // const char* keyf(const char* src); const char* kwd_mixin(const char* src); const char* kwd_function(const char* src); const char* kwd_return_directive(const char* src); - const char* kwd_include(const char* src); + const char* kwd_include_directive(const char* src); const char* kwd_content(const char* src); const char* kwd_extend(const char* src); @@ -276,16 +276,16 @@ namespace Sass { const char* uri_prefix(const char* src); const char* uri_value(const char* src); // Match CSS "!important" keyword. - const char* important(const char* src); + const char* kwd_important(const char* src); // Match CSS "!optional" keyword. - const char* optional(const char* src); + const char* kwd_optional(const char* src); // Match Sass "!default" keyword. const char* default_flag(const char* src); const char* global_flag(const char* src); // Match CSS pseudo-class/element prefixes const char* pseudo_prefix(const char* src); // Match CSS function call openers. - const char* functional(const char* src); + const char* re_pseudo_selector(const char* src); const char* functional_schema(const char* src); const char* pseudo_not(const char* src); // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. diff --git a/sass.h b/sass.h index b2841b5b11..cb5eef2514 100644 --- a/sass.h +++ b/sass.h @@ -1,6 +1,12 @@ #ifndef SASS_H #define SASS_H +#ifdef _MSC_VER +#define _SCL_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + #include #include diff --git a/sass2scss.cpp b/sass2scss.cpp index 54e65382d9..6477c2575a 100644 --- a/sass2scss.cpp +++ b/sass2scss.cpp @@ -1,3 +1,8 @@ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + // include library #include #include diff --git a/sass_context.cpp b/sass_context.cpp index 24e02f1e07..de82bec332 100644 --- a/sass_context.cpp +++ b/sass_context.cpp @@ -226,7 +226,7 @@ extern "C" { string cwd(Sass::File::get_cwd()); JsonNode* json_err = json_mkobject(); json_append_member(json_err, "status", json_mknumber(1)); - json_append_member(json_err, "file", json_mkstring(e.pstate.path.c_str())); + json_append_member(json_err, "file", json_mkstring(e.pstate.path)); json_append_member(json_err, "line", json_mknumber(e.pstate.line+1)); json_append_member(json_err, "column", json_mknumber(e.pstate.column+1)); json_append_member(json_err, "message", json_mkstring(e.message.c_str())); @@ -236,7 +236,9 @@ extern "C" { bool got_newline = false; msg_stream << msg_prefix; for (char chr : e.message) { - if (chr == '\n') { + if (chr == '\r') { + got_newline = true; + } else if (chr == '\n') { got_newline = true; } else if (got_newline) { msg_stream << string(msg_prefix.size(), ' '); @@ -272,9 +274,9 @@ extern "C" { c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); - c_ctx->error_text = strdup(e.message.c_str()); + c_ctx->error_text = sass_strdup(e.message.c_str()); c_ctx->error_status = 1; - c_ctx->error_file = sass_strdup(e.pstate.path.c_str()); + c_ctx->error_file = sass_strdup(e.pstate.path); c_ctx->error_line = e.pstate.line+1; c_ctx->error_column = e.pstate.column+1; c_ctx->error_src = e.pstate.src; @@ -290,7 +292,7 @@ extern "C" { json_append_member(json_err, "message", json_mkstring(ba.what())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); - c_ctx->error_text = strdup(ba.what()); + c_ctx->error_text = sass_strdup(ba.what()); c_ctx->error_status = 2; c_ctx->output_string = 0; c_ctx->source_map_string = 0; @@ -304,7 +306,7 @@ extern "C" { json_append_member(json_err, "message", json_mkstring(e.what())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); - c_ctx->error_text = strdup(e.what()); + c_ctx->error_text = sass_strdup(e.what()); c_ctx->error_status = 3; c_ctx->output_string = 0; c_ctx->source_map_string = 0; @@ -318,7 +320,7 @@ extern "C" { json_append_member(json_err, "message", json_mkstring(e.c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); - c_ctx->error_text = strdup(e.c_str()); + c_ctx->error_text = sass_strdup(e.c_str()); c_ctx->error_status = 4; c_ctx->output_string = 0; c_ctx->source_map_string = 0; @@ -332,7 +334,7 @@ extern "C" { json_append_member(json_err, "message", json_mkstring("unknown")); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); - c_ctx->error_text = strdup("unknown"); + c_ctx->error_text = sass_strdup("unknown"); c_ctx->error_status = 5; c_ctx->output_string = 0; c_ctx->source_map_string = 0; @@ -665,8 +667,8 @@ extern "C" { if (compiler->c_ctx->error_status) return compiler->c_ctx->error_status; compiler->state = SASS_COMPILER_EXECUTED; - Context* cpp_ctx = (Context*) compiler->cpp_ctx; - Block* root = (Block*) compiler->root; + Context* cpp_ctx = compiler->cpp_ctx; + Block* root = compiler->root; // compile the parsed root block try { compiler->c_ctx->output_string = cpp_ctx->compile_block(root); } // pass catched errors to generic error handler @@ -780,9 +782,9 @@ extern "C" { void ADDCALL sass_delete_compiler (struct Sass_Compiler* compiler) { if (compiler == 0) return; - Context* cpp_ctx = (Context*) compiler->cpp_ctx; + Context* cpp_ctx = compiler->cpp_ctx; + if (cpp_ctx) delete(cpp_ctx); compiler->cpp_ctx = 0; - delete cpp_ctx; free(compiler); } diff --git a/sass_functions.cpp b/sass_functions.cpp index 52c037554a..f0db3349d1 100644 --- a/sass_functions.cpp +++ b/sass_functions.cpp @@ -124,7 +124,7 @@ extern "C" { { if (import == 0) return 0; if (import->error) free(import->error); - import->error = error ? strdup(error) : 0; + import->error = error ? sass_strdup(error) : 0; import->line = line ? line : -1; import->column = col ? col : -1; return import; diff --git a/sass_util.cpp b/sass_util.cpp index 7510e718b2..fda191aeff 100644 --- a/sass_util.cpp +++ b/sass_util.cpp @@ -108,7 +108,7 @@ namespace Sass { return flattened end */ - Node flatten(const Node& arr, Context& ctx, int n = -1) { + Node flatten(Node& arr, Context& ctx, int n = -1) { if (n != -1 && n == 0) { return arr; } diff --git a/sass_util.hpp b/sass_util.hpp index 3fc4ec0f0f..acff5b64f7 100644 --- a/sass_util.hpp +++ b/sass_util.hpp @@ -173,7 +173,7 @@ namespace Sass { # @param n [int] The number of levels to flatten # @return [NodeCollection] The flattened array */ - Node flatten(const Node& arr, Context& ctx, int n = -1); + Node flatten(Node& arr, Context& ctx, int n = -1); /* diff --git a/test/test_node.cpp b/test/test_node.cpp index fb6ee2c96f..88ba1b2122 100644 --- a/test/test_node.cpp +++ b/test/test_node.cpp @@ -34,7 +34,7 @@ namespace Sass { static Complex_Selector* createComplexSelector(string src) { string temp(src); temp += ";"; - return (*Parser::from_c_str(temp.c_str(), ctx, "", Position()).parse_selector_group())[0]; + return (*Parser::from_c_str(temp.c_str(), ctx, "", Position()).parse_selector_list())[0]; } diff --git a/test/test_selector_difference.cpp b/test/test_selector_difference.cpp index 9f6e465296..95d78d9ecd 100644 --- a/test/test_selector_difference.cpp +++ b/test/test_selector_difference.cpp @@ -12,7 +12,7 @@ Context ctx = Context::Data(); To_String to_string; Compound_Selector* selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_simple_selector_sequence(); } +{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } void diff(string s, string t) { diff --git a/test/test_specificity.cpp b/test/test_specificity.cpp index c0e15a7a72..7147587d37 100644 --- a/test/test_specificity.cpp +++ b/test/test_specificity.cpp @@ -12,7 +12,7 @@ Context ctx = Context::Data(); To_String to_string; Selector* selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_selector_group(); } +{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_selector_list(); } void spec(string sel) { cout << sel << "\t::\t" << selector(sel + ";")->specificity() << endl; } diff --git a/test/test_superselector.cpp b/test/test_superselector.cpp index 48c8535559..8e25389a7b 100644 --- a/test/test_superselector.cpp +++ b/test/test_superselector.cpp @@ -10,10 +10,10 @@ Context ctx = Context(Context::Data()); To_String to_string; Compound_Selector* compound_selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_simple_selector_sequence(); } +{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } Complex_Selector* complex_selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_selector_combination(); } +{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_complex_selector(); } void check_compound(string s1, string s2) { diff --git a/test/test_unification.cpp b/test/test_unification.cpp index 4d957e4b52..c9a38e0ce3 100644 --- a/test/test_unification.cpp +++ b/test/test_unification.cpp @@ -10,7 +10,7 @@ Context ctx = Context(Context::Data()); To_String to_string; Compound_Selector* selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_simple_selector_sequence(); } +{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } void unify(string lhs, string rhs) { diff --git a/to_string.cpp b/to_string.cpp index 361c47facc..cedd8f4145 100644 --- a/to_string.cpp +++ b/to_string.cpp @@ -20,7 +20,7 @@ namespace Sass { Emitter emitter(ctx); Inspect i(emitter); i.in_declaration = in_declaration; - n->perform(&i); + if (n) n->perform(&i); return i.get_buffer(); } diff --git a/util.cpp b/util.cpp index 778f3241d6..e440facc90 100644 --- a/util.cpp +++ b/util.cpp @@ -1,7 +1,9 @@ -#include +#include #include "ast.hpp" #include "util.hpp" +#include "lexer.hpp" #include "prelexer.hpp" +#include "constants.hpp" #include "utf8/checked.h" namespace Sass { @@ -460,12 +462,23 @@ namespace Sass { bool peek_linefeed(const char* start) { - while (*start) { - if (*start == '\n' || *start == '\r') return true; - if (*start != ' ' && *start != '\t') return false; - ++ start; - } - return false; + using namespace Prelexer; + using namespace Constants; + return sequence < + zero_plus < + alternatives < + exactly <' '>, + exactly <'\t'>, + line_comment, + delimited_by < + slash_star, + star_slash, + false + > + > + >, + re_linebreak + >(start) != 0; } namespace Util { @@ -568,7 +581,7 @@ namespace Sass { return isPrintable(e, style); } - bool isPrintable(Feature_Block* f, Output_Style style) { + bool isPrintable(Supports_Block* f, Output_Style style) { if (f == NULL) { return false; } @@ -661,8 +674,8 @@ namespace Sass { return true; } } - else if (typeid(*stm) == typeid(Feature_Block)) { - Feature_Block* f = (Feature_Block*) stm; + else if (typeid(*stm) == typeid(Supports_Block)) { + Supports_Block* f = (Supports_Block*) stm; if (isPrintable(f, style)) { return true; } diff --git a/util.hpp b/util.hpp index 09d08f43af..10237318f1 100644 --- a/util.hpp +++ b/util.hpp @@ -40,7 +40,7 @@ namespace Sass { bool containsAnyPrintableStatements(Block* b); bool isPrintable(Ruleset* r, Output_Style style = NESTED); - bool isPrintable(Feature_Block* r, Output_Style style = NESTED); + bool isPrintable(Supports_Block* r, Output_Style style = NESTED); bool isPrintable(Media_Block* r, Output_Style style = NESTED); bool isPrintable(Block* b, Output_Style style = NESTED); bool isPrintable(String_Constant* s, Output_Style style = NESTED); From 583eac12a7ac1056e932bebc84b93e780bf0bb1d Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 23 Jun 2015 22:52:03 +0200 Subject: [PATCH 03/10] Adds default CRTP visitor for `String_Quoted` --- inspect.cpp | 3 --- operation.hpp | 2 ++ output.cpp | 18 +++++++----------- to_c.cpp | 4 ++-- to_string.cpp | 5 +++++ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/inspect.cpp b/inspect.cpp index 9d1faf59dd..203b57fd71 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -642,9 +642,6 @@ namespace Sass { void Inspect::operator()(String_Constant* s) { - if (String_Quoted* quoted = dynamic_cast(s)) { - return Inspect::operator()(quoted); - } append_token(s->value(), s); } diff --git a/operation.hpp b/operation.hpp index a3dd6b8636..c4b06aff60 100644 --- a/operation.hpp +++ b/operation.hpp @@ -54,6 +54,7 @@ namespace Sass { virtual T operator()(Color* x) = 0; virtual T operator()(Boolean* x) = 0; virtual T operator()(String_Schema* x) = 0; + virtual T operator()(String_Quoted* x) = 0; virtual T operator()(String_Constant* x) = 0; virtual T operator()(Supports_Query* x) = 0; virtual T operator()(Supports_Condition* x)= 0; @@ -129,6 +130,7 @@ namespace Sass { virtual T operator()(Boolean* x) { return static_cast(this)->fallback(x); } virtual T operator()(String_Schema* x) { return static_cast(this)->fallback(x); } virtual T operator()(String_Constant* x) { return static_cast(this)->fallback(x); } + virtual T operator()(String_Quoted* x) { return static_cast(this)->fallback(x); } virtual T operator()(Supports_Query* x) { return static_cast(this)->fallback(x); } virtual T operator()(Supports_Condition* x) { return static_cast(this)->fallback(x); } virtual T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } diff --git a/output.cpp b/output.cpp index 1589d5e6e9..691f4601d1 100644 --- a/output.cpp +++ b/output.cpp @@ -381,18 +381,14 @@ namespace Sass { void Output::operator()(String_Constant* s) { - if (String_Quoted* quoted = dynamic_cast(s)) { - return Output::operator()(quoted); + string value(s->value()); + if (s->can_compress_whitespace() && output_style() == COMPRESSED) { + value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); + } + if (!in_comment) { + append_token(string_to_output(value), s); } else { - string value(s->value()); - if (s->can_compress_whitespace() && output_style() == COMPRESSED) { - value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); - } - if (!in_comment) { - append_token(string_to_output(value), s); - } else { - append_token(value, s); - } + append_token(value, s); } } diff --git a/to_c.cpp b/to_c.cpp index 869aa82182..3438f618b1 100644 --- a/to_c.cpp +++ b/to_c.cpp @@ -19,11 +19,11 @@ namespace Sass { { return sass_make_color(c->r(), c->g(), c->b(), c->a()); } Sass_Value* To_C::operator()(String_Constant* s) - { + { if (s->quote_mark()) { return sass_make_qstring(s->value().c_str()); } else { - return sass_make_string(s->value().c_str()); + return sass_make_string(s->value().c_str()); } } diff --git a/to_string.cpp b/to_string.cpp index cedd8f4145..fde1fe5d5d 100644 --- a/to_string.cpp +++ b/to_string.cpp @@ -29,6 +29,11 @@ namespace Sass { return s->value(); } + inline string To_String::operator()(String_Quoted* s) + { + return s->value(); + } + inline string To_String::operator()(Null* n) { return ""; } } From 9f5ef6da0e8ec9287cd800bf98174f049591b286 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 7 Jun 2015 23:47:33 +0200 Subject: [PATCH 04/10] Adds major refactoring of parser and eval step --- Makefile | 2 - Makefile.am | 2 - ast.cpp | 379 +++++--- ast.hpp | 143 +-- ast_def_macros.hpp | 25 + ast_fwd_decl.hpp | 1 - b64/cencode.h | 2 +- bind.cpp | 23 +- constants.cpp | 7 + constants.hpp | 5 + context.cpp | 9 +- contextualize.cpp | 148 --- contextualize.hpp | 46 - contextualize_eval.cpp | 93 -- contextualize_eval.hpp | 44 - cssize.cpp | 6 +- debug.hpp | 18 +- debugger.hpp | 128 ++- emitter.cpp | 1 + eval.cpp | 443 +++++---- eval.hpp | 51 +- expand.cpp | 316 ++++--- expand.hpp | 24 +- extend.cpp | 112 +-- extend.hpp | 2 +- functions.cpp | 152 ++- inspect.cpp | 54 +- inspect.hpp | 1 - lexer.hpp | 43 +- listize.cpp | 12 +- listize.hpp | 2 +- node.cpp | 61 +- node.hpp | 2 +- output.cpp | 38 +- parser.cpp | 1764 ++++++++++++++++++----------------- parser.hpp | 32 +- prelexer.cpp | 85 +- prelexer.hpp | 11 +- remove_placeholders.cpp | 26 +- remove_placeholders.hpp | 3 - sass.h | 13 +- sass_util.cpp | 30 +- test/test_node.cpp | 50 +- test/test_superselector.cpp | 2 +- to_string.cpp | 13 +- to_string.hpp | 2 + utf8/core.h | 24 +- util.cpp | 46 +- win/libsass.filters | 6 - win/libsass.vcxproj | 4 - 50 files changed, 2333 insertions(+), 2173 deletions(-) delete mode 100644 contextualize.cpp delete mode 100644 contextualize.hpp delete mode 100644 contextualize_eval.cpp delete mode 100644 contextualize_eval.hpp diff --git a/Makefile b/Makefile index 5187e5744d..55fe41b01d 100644 --- a/Makefile +++ b/Makefile @@ -117,8 +117,6 @@ SOURCES = \ bind.cpp \ constants.cpp \ context.cpp \ - contextualize.cpp \ - contextualize_eval.cpp \ cssize.cpp \ listize.cpp \ error_handling.cpp \ diff --git a/Makefile.am b/Makefile.am index e59d2f8603..22822295a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,8 +46,6 @@ libsass_la_SOURCES = \ bind.cpp bind.hpp \ constants.cpp constants.hpp \ context.cpp context.hpp \ - contextualize.cpp contextualize.hpp \ - contextualize_eval.cpp contextualize_eval.hpp \ error_handling.cpp error_handling.hpp \ eval.cpp eval.hpp \ expand.cpp expand.hpp \ diff --git a/ast.cpp b/ast.cpp index 4cf7bf3faf..ae2e1810a7 100644 --- a/ast.cpp +++ b/ast.cpp @@ -12,6 +12,12 @@ namespace Sass { static Null sass_null(Sass::Null(ParserState("null"))); + void AST_Node::update_pstate(const ParserState& pstate) + { + pstate_.offset += pstate - pstate_ + pstate.offset; + } + + bool Compound_Selector::operator<(const Compound_Selector& rhs) const { To_String to_string; @@ -20,6 +26,17 @@ namespace Sass { const_cast(rhs).perform(&to_string); } + bool Compound_Selector::has_parent_ref() + { + return has_parent_reference(); + } + + bool Complex_Selector::has_parent_ref() + { + return (head() && head()->has_parent_ref()) || + (tail() && tail()->has_parent_ref()); + } + bool Complex_Selector::operator<(const Complex_Selector& rhs) const { To_String to_string; @@ -207,11 +224,41 @@ namespace Sass { return Simple_Selector::unify_with(rhs, ctx); } - bool Compound_Selector::is_superselector_of(Compound_Selector* rhs) + bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub) + { + if (this->name() != sub->name()) return false; + if (this->name() == ":current") return false; + if (Selector_List* rhs_list = dynamic_cast(sub->selector())) { + if (Selector_List* lhs_list = dynamic_cast(selector())) { + return lhs_list->is_superselector_of(rhs_list); + } + error("is_superselector expected a Selector_List", sub->pstate()); + } else { + error("is_superselector expected a Selector_List", sub->pstate()); + } + return false; + } + + bool Compound_Selector::is_superselector_of(Selector_List* rhs, string wrapped) + { + for (Complex_Selector* item : rhs->elements()) { + if (is_superselector_of(item, wrapped)) return true; + } + return false; + } + + bool Compound_Selector::is_superselector_of(Complex_Selector* rhs, string wrapped) + { + if (rhs->head()) return is_superselector_of(rhs->head(), wrapped); + return false; + } + + bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, string wrapping) { To_String to_string; - Simple_Selector* lbase = base(); + Compound_Selector* lhs = this; + Simple_Selector* lbase = lhs->base(); Simple_Selector* rbase = rhs->base(); // Check if pseudo-elements are the same between the selectors @@ -237,49 +284,122 @@ namespace Sass { return false; } - // Check the Simple_Selectors - set lset, rset; - if (!lbase) // no lbase; just see if the left-hand qualifiers are a subset of the right-hand selector + if (lbase && rbase) { - for (size_t i = 0, L = length(); i < L; ++i) - { - Selector* lhs = (*this)[i]; - // very special case for wrapped matches selector - if (Wrapped_Selector* wrapped = dynamic_cast(lhs)) { - if (wrapped->name() == ":matches(" || wrapped->name() == ":-moz-any(") { - if (Selector_List* list = dynamic_cast(wrapped->selector())) { - if (Compound_Selector* comp = dynamic_cast(rhs)) { - if (list->is_superselector_of(comp)) return true; + if (lbase->perform(&to_string) == rbase->perform(&to_string)) { + for (size_t i = 1, L = length(); i < L; ++i) + { lset.insert((*this)[i]->perform(&to_string)); } + for (size_t i = 1, L = rhs->length(); i < L; ++i) + { rset.insert((*rhs)[i]->perform(&to_string)); } + return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); + } + return false; + } + + for (size_t i = 0, iL = length(); i < iL; ++i) + { + Selector* lhs = (*this)[i]; + // very special case for wrapped matches selector + if (Wrapped_Selector* wrapped = dynamic_cast(lhs)) { + if (wrapped->name() == ":not") { + if (Selector_List* not_list = dynamic_cast(wrapped->selector())) { + if (not_list->is_superselector_of(rhs, wrapped->name())) return false; + } else { + throw runtime_error("wrapped not selector is not a list"); + } + } + if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { + lhs = wrapped->selector(); + if (Selector_List* list = dynamic_cast(wrapped->selector())) { + if (Compound_Selector* comp = dynamic_cast(rhs)) { + if (!wrapping.empty() && wrapping != wrapped->name()) return false; + if (wrapping.empty() || wrapping != wrapped->name()) {; + if (list->is_superselector_of(comp, wrapped->name())) return true; } } } } - // match from here on as strings - lset.insert(lhs->perform(&to_string)); + Simple_Selector* rhs_sel = rhs->elements().size() > i ? (*rhs)[i] : 0; + if (Wrapped_Selector* wrapped_r = dynamic_cast(rhs_sel)) { + if (wrapped->name() == wrapped_r->name()) { + if (wrapped->is_superselector_of(wrapped_r)) { + continue; + rset.insert(lhs->perform(&to_string)); + + }} + } } - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { rset.insert((*rhs)[i]->perform(&to_string)); } - return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); + // match from here on as strings + lset.insert(lhs->perform(&to_string)); } - else { // there's an lbase - for (size_t i = 1, L = length(); i < L; ++i) - { lset.insert((*this)[i]->perform(&to_string)); } - if (rbase) - { - if (lbase->perform(&to_string) != rbase->perform(&to_string)) // if there's an rbase, make sure they match - { return false; } - else // the bases do match, so compare qualifiers - { - for (size_t i = 1, L = rhs->length(); i < L; ++i) - { rset.insert((*rhs)[i]->perform(&to_string)); } - return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); + + for (size_t n = 0, nL = rhs->length(); n < nL; ++n) + { + auto r = (*rhs)[n]; + if (Wrapped_Selector* wrapped = dynamic_cast(r)) { + if (wrapped->name() == ":not") { + if (Selector_List* ls = dynamic_cast(wrapped->selector())) { + ls->remove_parent_selectors(); + if (is_superselector_of(ls, wrapped->name())) return false; + } + } + if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { + if (!wrapping.empty()) { + if (wrapping != wrapped->name()) return false; + } + if (Selector_List* ls = dynamic_cast(wrapped->selector())) { + ls->remove_parent_selectors(); + return (is_superselector_of(ls, wrapped->name())); + } } } + rset.insert(r->perform(&to_string)); } - // catch-all - return false; + + //for (auto l : lset) { cerr << "l: " << l << endl; } + //for (auto r : rset) { cerr << "r: " << r << endl; } + + if (lset.size() == 0) return true; + // return true if rset contains all the elements of lset + return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); + + } + + Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx) { + To_String to_string; + Compound_Selector* thisBase = last()->head(); + Compound_Selector* rhsBase = other->last()->head(); + + + if( thisBase == 0 || rhsBase == 0 ) return 0; + + // Not sure about this check, but closest way I could check to see if this is a ruby 'SimpleSequence' equivalent + if( tail()->combinator() != Combinator::ANCESTOR_OF || other->tail()->combinator() != Combinator::ANCESTOR_OF ) return 0; + + Compound_Selector* unified = rhsBase->unify_with(thisBase, ctx); + if( unified == 0 ) return 0; + + Node lhsNode = complexSelectorToNode(this, ctx); + Node rhsNode = complexSelectorToNode(other, ctx); + + // Create a temp Complex_Selector, turn it into a Node, and combine it with the existing RHS node + Complex_Selector* fakeComplexSelector = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, unified, NULL); + Node unifiedNode = complexSelectorToNode(fakeComplexSelector, ctx); + rhsNode.plus(unifiedNode); + + Node node = Extend::subweave(lhsNode, rhsNode, ctx); + + Selector_List* result = new (ctx.mem) Selector_List(pstate()); + for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { + Node childNode = *iter; + childNode = Node::naiveTrim(childNode, ctx); + + Complex_Selector* childNodeAsComplexSelector = nodeToComplexSelector(childNode, ctx); + if( childNodeAsComplexSelector ) { (*result) << childNodeAsComplexSelector; } + } + return result->length() ? result : 0; } bool Compound_Selector::operator==(const Compound_Selector& rhs) const { @@ -308,18 +428,6 @@ namespace Sass { return false; } - // Check the base - - const Simple_Selector* const lbase = base(); - const Simple_Selector* const rbase = rhs.base(); - - if ((lbase && !rbase) || - (!lbase && rbase) || - ((lbase && rbase) && (*lbase != *rbase))) { - return false; - } - - // Check the rest of the SimpleSelectors // Use string representations. We can't create a set of Simple_Selector pointers because std::set == std::set is going to call == // on the pointers to determine equality. I don't know of a way to pass in a comparison object. The one you can specify as part of @@ -340,12 +448,12 @@ namespace Sass { return *pLeft < *pRight; } - bool Complex_Selector::is_superselector_of(Compound_Selector* rhs) + bool Complex_Selector::is_superselector_of(Compound_Selector* rhs, string wrapping) { - return base()->is_superselector_of(rhs); + return last()->head() && last()->head()->is_superselector_of(rhs, wrapping); } - bool Complex_Selector::is_superselector_of(Complex_Selector* rhs) + bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, string wrapping) { Complex_Selector* lhs = this; To_String to_string; @@ -353,10 +461,10 @@ namespace Sass { if (!lhs->head() || !rhs->head()) { return false; } Complex_Selector* l_innermost = lhs->innermost(); - if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF && !l_innermost->tail()) + if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF) { return false; } Complex_Selector* r_innermost = rhs->innermost(); - if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF && !r_innermost->tail()) + if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF) { return false; } // more complex (i.e., longer) selectors are always more specific size_t l_len = lhs->length(), r_len = rhs->length(); @@ -364,7 +472,7 @@ namespace Sass { { return false; } if (l_len == 1) - { return lhs->head()->is_superselector_of(rhs->base()); } + { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); } // we have to look one tail deeper, since we cary the // combinator around for it (which is important here) @@ -372,16 +480,19 @@ namespace Sass { Complex_Selector* lhs_tail = lhs->tail(); Complex_Selector* rhs_tail = rhs->tail(); if (lhs_tail->combinator() != rhs_tail->combinator()) return false; - if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; + if (lhs_tail->head() && !rhs_tail->head()) return false; + if (!lhs_tail->head() && rhs_tail->head()) return false; + if (lhs_tail->head() && lhs_tail->head()) { + if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; + } } - bool found = false; Complex_Selector* marker = rhs; for (size_t i = 0, L = rhs->length(); i < L; ++i) { if (i == L-1) { return false; } - if (lhs->head()->is_superselector_of(marker->head())) + if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping)) { found = true; break; } marker = marker->tail(); } @@ -423,43 +534,6 @@ namespace Sass { return false; } - Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx) { - To_String to_string; - - Compound_Selector* thisBase = base(); - Compound_Selector* rhsBase = other->base(); - - if( thisBase == 0 || rhsBase == 0 ) return 0; - - // Not sure about this check, but closest way I could check to see if this is a ruby 'SimpleSequence' equivalent - if( tail()->combinator() != Combinator::ANCESTOR_OF || other->tail()->combinator() != Combinator::ANCESTOR_OF ) return 0; - - Compound_Selector* unified = rhsBase->unify_with(thisBase, ctx); - if( unified == 0 ) return 0; - - Node lhsNode = complexSelectorToNode(this, ctx); - Node rhsNode = complexSelectorToNode(other, ctx); - - // Create a temp Complex_Selector, turn it into a Node, and combine it with the existing RHS node - Complex_Selector* fakeComplexSelector = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, unified, NULL); - Node unifiedNode = complexSelectorToNode(fakeComplexSelector, ctx); - rhsNode.plus(unifiedNode); - - Node node = Extend::subweave(lhsNode, rhsNode, ctx); - - Selector_List* result = new (ctx.mem) Selector_List(pstate()); - for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { - Node childNode = *iter; - childNode = Node::naiveTrim(childNode, ctx); - - Complex_Selector* childNodeAsComplexSelector = nodeToComplexSelector(childNode, ctx); - if( childNodeAsComplexSelector ) { (*result) << childNodeAsComplexSelector; } - } - - return result->length() ? result : 0; - } - - size_t Complex_Selector::length() { // TODO: make this iterative @@ -467,22 +541,93 @@ namespace Sass { return 1 + tail()->length(); } - Compound_Selector* Complex_Selector::base() - { - if (!tail()) return head(); - else return tail()->base(); - } - Complex_Selector* Complex_Selector::context(Context& ctx) { if (!tail()) return 0; if (!head()) return tail()->context(ctx); Complex_Selector* cpy = new (ctx.mem) Complex_Selector(pstate(), combinator(), head(), tail()->context(ctx)); cpy->media_block(media_block()); - cpy->last_block(last_block()); return cpy; } + Selector_List* Selector_List::parentize(Selector_List* ps, Context& ctx) + { + Selector_List* ss = new (ctx.mem) Selector_List(pstate()); + for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) { + for (size_t si = 0, sL = this->length(); si < sL; ++si) { + *ss << (*this)[si]->parentize((*ps)[pi], ctx); + } + } + // return selector + return ss; + } + + Selector_List* Selector_List::parentize(Complex_Selector* p, Context& ctx) + { + Selector_List* ss = new (ctx.mem) Selector_List(pstate()); + for (size_t i = 0, L = this->length(); i < L; ++i) { + *ss << (*this)[i]->parentize(p, ctx); + } + // return selector + return ss; + } + + Selector_List* Complex_Selector::parentize(Selector_List* ps, Context& ctx) + { + Selector_List* ss = new (ctx.mem) Selector_List(pstate()); + for (size_t i = 0, L = ps->length(); i < L; ++i) { + *ss << this->parentize((*ps)[i], ctx); + } + // return selector + return ss; + } + + Complex_Selector* Complex_Selector::parentize(Complex_Selector* parent, Context& ctx) + { + Complex_Selector* pr = 0; + Compound_Selector* head = this->head(); + // create a new complex selector to return a processed copy + Complex_Selector* ss = new (ctx.mem) Complex_Selector(pstate()); + + // Points to last complex selector + // Moved when resolving parent refs + Complex_Selector* cur = ss; + + // check if compound selector has exactly one parent reference + // if so we need to connect the parent to the current selector + // then we also need to add the remaining simple selector to the new "parent" + if (head) { + // create a new compound and move originals if needed + // we may add the simple selector to the same selector + // with parent refs we may put them in different places + ss->head(new (ctx.mem) Compound_Selector(head->pstate())); + ss->head()->has_parent_reference(head->has_parent_reference()); + ss->head()->has_line_break(head->has_line_break()); + // process simple selectors sequence + for (size_t i = 0, L = head->length(); i < L; ++i) { + // we have a parent selector in a simple selector list + // mix parent complex selector into the compound list + if (dynamic_cast((*head)[i])) { + // clone the parent selector + pr = parent->cloneFully(ctx); + // assign head and tail + cur->head(pr->head()); + cur->tail(pr->tail()); + // move forward + cur = pr->last(); + } else { + // just add simple selector + *cur->head() << (*head)[i]; + } + } + } + if (cur->head()) cur->head(cur->head()->length() ? cur->head() : 0); + // parentize and assign trailing complex selector + if (this->tail()) cur->tail(this->tail()->parentize(parent, ctx)); + // return selector + return ss; + } + Complex_Selector* Complex_Selector::innermost() { if (!tail()) return this; @@ -492,7 +637,7 @@ namespace Sass { Complex_Selector::Combinator Complex_Selector::clear_innermost() { Combinator c; - if (!tail() || tail()->length() == 1) + if (!tail() || tail()->tail() == 0) { c = combinator(); combinator(ANCESTOR_OF); tail(0); } else { c = tail()->clear_innermost(); } @@ -543,10 +688,27 @@ namespace Sass { return 0; }*/ + // remove parent selector references + // basically unwraps parsed selectors + void Selector_List::remove_parent_selectors() + { + // Check every rhs selector against left hand list + for(size_t i = 0, L = length(); i < L; ++i) { + if (!(*this)[i]->head()) continue; + if ((*this)[i]->combinator() != Complex_Selector::ANCESTOR_OF) continue; + if ((*this)[i]->head()->is_empty_reference()) { + Complex_Selector* tail = (*this)[i]->tail(); + if ((*this)[i]->has_line_feed()) { + if (tail) tail->has_line_feed(true); + } + (*this)[i] = tail; + } + } + } + void Selector_List::adjust_after_pushing(Complex_Selector* c) { if (c->has_reference()) has_reference(true); - if (c->has_placeholder()) has_placeholder(true); #ifdef DEBUG To_String to_string; @@ -556,40 +718,40 @@ namespace Sass { // it's a superselector if every selector of the right side // list is a superselector of the given left side selector - bool Complex_Selector::is_superselector_of(Selector_List *sub) + bool Complex_Selector::is_superselector_of(Selector_List *sub, string wrapping) { // Check every rhs selector against left hand list for(size_t i = 0, L = sub->length(); i < L; ++i) { - if (!is_superselector_of((*sub)[i])) return false; + if (!is_superselector_of((*sub)[i], wrapping)) return false; } return true; } // it's a superselector if every selector of the right side // list is a superselector of the given left side selector - bool Selector_List::is_superselector_of(Selector_List *sub) + bool Selector_List::is_superselector_of(Selector_List *sub, string wrapping) { // Check every rhs selector against left hand list for(size_t i = 0, L = sub->length(); i < L; ++i) { - if (!is_superselector_of((*sub)[i])) return false; + if (!is_superselector_of((*sub)[i], wrapping)) return false; } return true; } // it's a superselector if every selector on the right side // is a superselector of any one of the left side selectors - bool Selector_List::is_superselector_of(Compound_Selector *sub) + bool Selector_List::is_superselector_of(Compound_Selector *sub, string wrapping) { // Check every lhs selector against right hand for(size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->is_superselector_of(sub)) return true; + if ((*this)[i]->is_superselector_of(sub, wrapping)) return true; } return false; } // it's a superselector if every selector on the right side // is a superselector of any one of the left side selectors - bool Selector_List::is_superselector_of(Complex_Selector *sub) + bool Selector_List::is_superselector_of(Complex_Selector *sub, string wrapping) { // Check every lhs selector against right hand for(size_t i = 0, L = length(); i < L; ++i) { @@ -636,7 +798,7 @@ namespace Sass { Complex_Selector* pIter = complex_sel; while (pIter) { Compound_Selector* pHead = pIter->head(); - if (pHead && dynamic_cast(pHead->elements()[0]) == NULL) { + if (pHead && dynamic_cast(pHead->elements()[0]) == NULL) { compound_sel = pHead; break; } @@ -670,6 +832,7 @@ namespace Sass { { To_String to_string(&ctx); Compound_Selector* result = new (ctx.mem) Compound_Selector(pstate()); + // result->has_parent_reference(has_parent_reference()); // not very efficient because it needs to preserve order for (size_t i = 0, L = length(); i < L; ++i) diff --git a/ast.hpp b/ast.hpp index 4140f78f96..6156e799e6 100644 --- a/ast.hpp +++ b/ast.hpp @@ -75,6 +75,8 @@ namespace Sass { { } virtual ~AST_Node() = 0; // virtual Block* block() { return 0; } + public: + void update_pstate(const ParserState& pstate); public: Offset off() { return pstate(); } Position pos() { return pstate(); } @@ -180,6 +182,7 @@ namespace Sass { const T& operator[](size_t i) const { return elements_[i]; } Vectorized& operator<<(T element) { + if (!element) return *this; reset_hash(); elements_.push_back(element); adjust_after_pushing(element); @@ -300,6 +303,7 @@ namespace Sass { //////////////////////// class Block : public Statement, public Vectorized { ADD_PROPERTY(bool, is_root) + ADD_PROPERTY(bool, is_at_root); // needed for properly formatted CSS emission ADD_PROPERTY(bool, has_hoistable) ADD_PROPERTY(bool, has_non_hoistable) @@ -313,7 +317,7 @@ namespace Sass { Block(ParserState pstate, size_t s = 0, bool r = false) : Statement(pstate), Vectorized(s), - is_root_(r), has_hoistable_(false), has_non_hoistable_(false) + is_root_(r), is_at_root_(false), has_hoistable_(false), has_non_hoistable_(false) { } Block* block() { return this; } ATTACH_OPERATIONS() @@ -338,9 +342,10 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////////// class Ruleset : public Has_Block { ADD_PROPERTY(Selector*, selector) + ADD_PROPERTY(bool, at_root); public: - Ruleset(ParserState pstate, Selector* s, Block* b) - : Has_Block(pstate, b), selector_(s) + Ruleset(ParserState pstate, Selector* s = 0, Block* b = 0) + : Has_Block(pstate, b), selector_(s), at_root_(false) { statement_type(RULESET); } bool is_invisible(); // nested rulesets need to be hoisted out of their enclosing blocks @@ -379,13 +384,12 @@ namespace Sass { ///////////////// class Media_Block : public Has_Block { ADD_PROPERTY(List*, media_queries) - ADD_PROPERTY(Selector*, selector) public: Media_Block(ParserState pstate, List* mqs, Block* b) - : Has_Block(pstate, b), media_queries_(mqs), selector_(0) + : Has_Block(pstate, b), media_queries_(mqs) { statement_type(MEDIA); } Media_Block(ParserState pstate, List* mqs, Block* b, Selector* s) - : Has_Block(pstate, b), media_queries_(mqs), selector_(s) + : Has_Block(pstate, b), media_queries_(mqs) { statement_type(MEDIA); } bool bubbles() { return true; } bool is_hoistable() { return true; } @@ -403,10 +407,9 @@ namespace Sass { ////////////////// class Supports_Block : public Has_Block { ADD_PROPERTY(Supports_Query*, queries) - ADD_PROPERTY(Selector*, selector) public: Supports_Block(ParserState pstate, Supports_Query* queries = 0, Block* block = 0) - : Has_Block(pstate, block), queries_(queries), selector_(0) + : Has_Block(pstate, block), queries_(queries) { statement_type(SUPPORTS); } bool is_hoistable() { return true; } bool bubbles() { return true; } @@ -422,8 +425,8 @@ namespace Sass { ADD_PROPERTY(Selector*, selector) ADD_PROPERTY(Expression*, value) public: - At_Rule(ParserState pstate, string kwd, Selector* sel = 0, Block* b = 0) - : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(0) // set value manually if needed + At_Rule(ParserState pstate, string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0) + : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed { statement_type(DIRECTIVE); } bool bubbles() { return is_keyframes() || is_media(); } bool is_media() { @@ -460,10 +463,11 @@ namespace Sass { ADD_PROPERTY(String*, property) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_important) + ADD_PROPERTY(bool, is_indented) public: Declaration(ParserState pstate, String* prop, Expression* val, bool i = false) - : Statement(pstate), property_(prop), value_(val), is_important_(i) + : Statement(pstate), property_(prop), value_(val), is_important_(i), is_indented_(false) { } ATTACH_OPERATIONS() }; @@ -774,7 +778,6 @@ namespace Sass { if (hash_ > 0) return hash_; hash_ = std::hash()(separator() == COMMA ? "comma" : "space"); - for (size_t i = 0, L = length(); i < L; ++i) hash_combine(hash_, (elements()[i])->hash()); @@ -1676,22 +1679,6 @@ namespace Sass { ////////////////////////////////////////////////////////////////////////////////////////// inline Expression* List::value_at_index(size_t i) { return is_arglist_ ? ((Argument*)(*this)[i])->value() : (*this)[i]; } - //////////// - // The Parent Selector Expression. - //////////// - class Parent_Selector : public Expression { - ADD_PROPERTY(Selector*, selector) - public: - Parent_Selector(ParserState pstate, Selector* r = 0) - : Expression(pstate), selector_(r) - { concrete_type(SELECTOR); } - virtual Selector* selector() { return selector_; } - string type() { return "selector"; } - static string type_name() { return "selector"; } - - ATTACH_OPERATIONS() - }; - ///////////////////////////////////////// // Abstract base class for CSS selectors. ///////////////////////////////////////// @@ -1705,7 +1692,6 @@ namespace Sass { // maybe we have optional flag ADD_PROPERTY(bool, is_optional) // parent block pointers - ADD_PROPERTY(Block*, last_block) ADD_PROPERTY(Media_Block*, media_block) public: Selector(ParserState pstate, bool r = false, bool h = false) @@ -1731,9 +1717,10 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////// class Selector_Schema : public Selector { ADD_PROPERTY(String*, contents) + ADD_PROPERTY(bool, at_root); public: Selector_Schema(ParserState pstate, String* c) - : Selector(pstate), contents_(c) + : Selector(pstate), contents_(c), at_root_(false) { } ATTACH_OPERATIONS() }; @@ -1748,9 +1735,12 @@ namespace Sass { { } virtual ~Simple_Selector() = 0; virtual Compound_Selector* unify_with(Compound_Selector*, Context&); + virtual bool has_parent_ref() { return false; }; virtual bool is_pseudo_element() { return false; } virtual bool is_pseudo_class() { return false; } + virtual bool is_superselector_of(Compound_Selector* sub) { return false; } + bool operator==(const Simple_Selector& rhs) const; inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); } @@ -1758,20 +1748,25 @@ namespace Sass { }; inline Simple_Selector::~Simple_Selector() { } - ///////////////////////////////////// - // Parent references (i.e., the "&"). - ///////////////////////////////////// - class Selector_Reference : public Simple_Selector { - ADD_PROPERTY(Selector*, selector) - public: - Selector_Reference(ParserState pstate, Selector* r = 0) - : Simple_Selector(pstate), selector_(r) + + ////////////////////////////////// + // The Parent Selector Expression. + ////////////////////////////////// + // parent selectors can occur in selectors but also + // inside strings in declarations (Compound_Selector). + // only one simple parent selector means the first case. + class Parent_Selector : public Simple_Selector { + public: + Parent_Selector(ParserState pstate) + : Simple_Selector(pstate) { has_reference(true); } + virtual bool has_parent_ref() { return true; }; virtual unsigned long specificity() { - if (!selector()) return 0; - return selector()->specificity(); + return 0; } + string type() { return "selector"; } + static string type_name() { return "selector"; } ATTACH_OPERATIONS() }; @@ -1908,6 +1903,7 @@ namespace Sass { Wrapped_Selector(ParserState pstate, string n, Selector* sel) : Simple_Selector(pstate), name_(n), selector_(sel) { } + virtual bool is_superselector_of(Wrapped_Selector* sub); // Selectors inside the negation pseudo-class are counted like any // other, but the negation itself does not count as a pseudo-class. virtual unsigned long specificity() @@ -1929,6 +1925,7 @@ namespace Sass { class Compound_Selector : public Selector, public Vectorized { private: SourcesSet sources_; + ADD_PROPERTY(bool, has_parent_reference); protected: void adjust_after_pushing(Simple_Selector* s) { @@ -1938,23 +1935,33 @@ namespace Sass { public: Compound_Selector(ParserState pstate, size_t s = 0) : Selector(pstate), - Vectorized(s) + Vectorized(s), + has_parent_reference_(false) { } + bool contains_placeholder() { + for (size_t i = 0, L = length(); i < L; ++i) { + if ((*this)[i]->has_placeholder()) return true; + } + return false; + }; Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx); // virtual Selector_Placeholder* find_placeholder(); + virtual bool has_parent_ref(); Simple_Selector* base() { // Implement non-const in terms of const. Safe to const_cast since this method is non-const return const_cast(static_cast(this)->base()); } const Simple_Selector* base() const { - if (length() > 0 && typeid(*(*this)[0]) == typeid(Type_Selector)) + if (length() == 0) return 0; + if (typeid(*(*this)[0]) == typeid(Type_Selector)) return (*this)[0]; +// else cerr << "SERIOUSELY " << "\n"; return 0; } - bool is_superselector_of(Compound_Selector* sub); - // bool is_superselector_of(Complex_Selector* sub); - // bool is_superselector_of(Selector_List* sub); + virtual bool is_superselector_of(Compound_Selector* sub, string wrapped = ""); + virtual bool is_superselector_of(Complex_Selector* sub, string wrapped = ""); + virtual bool is_superselector_of(Selector_List* sub, string wrapped = ""); virtual unsigned long specificity() { int sum = 0; @@ -1965,8 +1972,7 @@ namespace Sass { bool is_empty_reference() { return length() == 1 && - typeid(*(*this)[0]) == typeid(Selector_Reference) && - !static_cast((*this)[0])->selector(); + typeid(*(*this)[0]) == typeid(Parent_Selector); } vector to_str_vec(); // sometimes need to convert to a flat "by-value" data structure @@ -1998,6 +2004,11 @@ namespace Sass { ADD_PROPERTY(Compound_Selector*, head) ADD_PROPERTY(Complex_Selector*, tail) public: + bool contains_placeholder() { + if (head() && head()->contains_placeholder()) return true; + if (tail() && tail()->contains_placeholder()) return true; + return false; + }; Complex_Selector(ParserState pstate, Combinator c = ANCESTOR_OF, Compound_Selector* h = 0, @@ -2007,17 +2018,25 @@ namespace Sass { if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true); } - Compound_Selector* base(); + virtual bool has_parent_ref(); Complex_Selector* context(Context&); Complex_Selector* innermost(); + Complex_Selector* last() { return innermost(); }; + Complex_Selector* first() { + Complex_Selector* s = tail(); + while (s && s->head() && s->head()->length() == 1 && dynamic_cast((*s->head())[0])) { + s = s->tail(); + } + return s; + }; size_t length(); - bool is_superselector_of(Compound_Selector* sub); - bool is_superselector_of(Complex_Selector* sub); - bool is_superselector_of(Selector_List* sub); + Selector_List* parentize(Selector_List* parents, Context& ctx); + Complex_Selector* parentize(Complex_Selector* parent, Context& ctx); + virtual bool is_superselector_of(Compound_Selector* sub, string wrapping = ""); + virtual bool is_superselector_of(Complex_Selector* sub, string wrapping = ""); + virtual bool is_superselector_of(Selector_List* sub, string wrapping = ""); // virtual Selector_Placeholder* find_placeholder(); - Selector_List* unify_with(Complex_Selector* rhs, Context& ctx); - Combinator clear_innermost(); void set_innermost(Complex_Selector*, Combinator); virtual unsigned long specificity() const @@ -2101,19 +2120,27 @@ namespace Sass { Selector_List(ParserState pstate, size_t s = 0) : Selector(pstate), Vectorized(s), wspace_(0) { } + // remove parent selector references + // basically unwraps parsed selectors + void remove_parent_selectors(); // virtual Selector_Placeholder* find_placeholder(); - bool is_superselector_of(Compound_Selector* sub); - bool is_superselector_of(Complex_Selector* sub); - bool is_superselector_of(Selector_List* sub); + Selector_List* parentize(Selector_List* parents, Context& ctx); + Selector_List* parentize(Complex_Selector* parent, Context& ctx); + virtual bool is_superselector_of(Compound_Selector* sub, string wrapping = ""); + virtual bool is_superselector_of(Complex_Selector* sub, string wrapping = ""); + virtual bool is_superselector_of(Selector_List* sub, string wrapping = ""); Selector_List* unify_with(Selector_List*, Context&); void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&); - virtual unsigned long specificity() { unsigned long sum = 0; + unsigned long specificity = 0; for (size_t i = 0, L = length(); i < L; ++i) - { sum += (*this)[i]->specificity(); } + { + specificity = (*this)[i]->specificity(); + if (sum < specificity) sum = specificity; + } return sum; } // vector members() { return elements_; } diff --git a/ast_def_macros.hpp b/ast_def_macros.hpp index b240d430e4..bf165d97de 100644 --- a/ast_def_macros.hpp +++ b/ast_def_macros.hpp @@ -1,6 +1,31 @@ #ifndef SASS_AST_DEF_MACROS_H #define SASS_AST_DEF_MACROS_H +// Helper class to switch a flag and revert once we go out of scope +template +class LocalOption { + private: + T* var; // pointer to original variable + T orig; // copy of the original option + public: + LocalOption(T& var) + { + this->var = &var; + this->orig = var; + } + LocalOption(T& var, T orig) + { + this->var = &var; + this->orig = var; + *(this->var) = orig; + } + ~LocalOption() { + *(this->var) = this->orig; + } +}; + +#define LOCAL_FLAG(name,opt) LocalOption flag_##name(name, opt) + #define ATTACH_OPERATIONS()\ virtual void perform(Operation* op) { (*op)(this); }\ virtual AST_Node* perform(Operation* op) { return (*op)(this); }\ diff --git a/ast_fwd_decl.hpp b/ast_fwd_decl.hpp index d5deca2ca4..a3d6d66f36 100644 --- a/ast_fwd_decl.hpp +++ b/ast_fwd_decl.hpp @@ -69,7 +69,6 @@ namespace Sass { // selectors class Selector; class Selector_Schema; - class Selector_Reference; class Selector_Placeholder; class Type_Selector; class Selector_Qualifier; diff --git a/b64/cencode.h b/b64/cencode.h index cf32131266..1d71e83fdb 100644 --- a/b64/cencode.h +++ b/b64/cencode.h @@ -10,7 +10,7 @@ For details, see http://sourceforge.net/projects/libb64 typedef enum { - step_A, step_B, step_C + step_A, step_B, step_C } base64_encodestep; typedef struct diff --git a/bind.cpp b/bind.cpp index 667c31613f..7a73c1e9ea 100644 --- a/bind.cpp +++ b/bind.cpp @@ -12,8 +12,19 @@ namespace Sass { void bind(string callee, Parameters* ps, Arguments* as, Context& ctx, Env* env, Eval* eval) { + Listize listize(ctx); map param_map; + for (size_t i = 0, L = as->length(); i < L; ++i) { + if (auto str = dynamic_cast((*as)[i]->value())) { + // force optional quotes (only if needed) + if (str->quote_mark()) { + str->quote_mark('*'); + str->is_delayed(true); + } + } + } + // 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) { @@ -29,6 +40,8 @@ namespace Sass { size_t ia = 0, LA = as->length(); while (ia < LA) { Argument* a = (*as)[ia]; + // this is only needed for selectors + a->value(a->value()->perform(&listize)); if (ip >= LP) { // skip empty rest arguments if (a->is_rest_argument()) { @@ -225,15 +238,7 @@ namespace Sass { true); } else if (leftover->default_value()) { - // make sure to eval the default value in the env that we've been populating - Env* old_env = eval->env; - Backtrace* old_bt = eval->backtrace; - Contextualize* old_context = eval->contextualize; - Expression* dv = leftover->default_value()->perform(eval->with(env, eval->backtrace)); - eval->env = old_env; - eval->backtrace = old_bt; - eval->contextualize = old_context; - // dv->perform(&to_string); + Expression* dv = leftover->default_value()->perform(eval); env->local_frame()[leftover->name()] = dv; } else { diff --git a/constants.cpp b/constants.cpp index db918607d3..6af17d13e6 100644 --- a/constants.cpp +++ b/constants.cpp @@ -145,6 +145,13 @@ namespace Sass { // some specific constant character classes // they must be static to be useable by lexer extern const char static_ops[] = "*/%"; + // some character classes for the parser + extern const char selector_list_delims[] = "){};!"; + extern const char complex_selector_delims[] = ",){};!"; + extern const char selector_combinator_ops[] = "+~>"; + // optional modifiers for alternative compare context + extern const char attribute_compare_modifiers[] = "~|^$*"; + extern const char selector_lookahead_ops[] = "*&%,()[]"; // byte order marks // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) diff --git a/constants.hpp b/constants.hpp index d91ae7d1b2..c7900ff74d 100644 --- a/constants.hpp +++ b/constants.hpp @@ -148,6 +148,11 @@ namespace Sass { // some specific constant character classes // they must be static to be useable by lexer extern const char static_ops[]; + extern const char selector_list_delims[]; + extern const char complex_selector_delims[]; + extern const char selector_combinator_ops[]; + extern const char attribute_compare_modifiers[]; + extern const char selector_lookahead_ops[]; // byte order marks // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) diff --git a/context.cpp b/context.cpp index 4b7a3cef19..6348d778cd 100644 --- a/context.cpp +++ b/context.cpp @@ -23,8 +23,6 @@ #include "output.hpp" #include "expand.hpp" #include "eval.hpp" -#include "contextualize.hpp" -#include "contextualize_eval.hpp" #include "cssize.hpp" #include "listize.hpp" #include "extend.hpp" @@ -340,12 +338,8 @@ namespace Sass { { register_c_function(*this, &global, c_functions[i]); } // create initial backtrace entry Backtrace backtrace(0, ParserState("", 0), ""); - Contextualize contextualize(*this, &global, &backtrace); - Listize listize(*this); - Eval eval(*this, &contextualize, &listize, &global, &backtrace); - Contextualize_Eval contextualize_eval(*this, &eval, &global, &backtrace); // create crtp visitor objects - Expand expand(*this, &eval, &contextualize_eval, &global, &backtrace); + Expand expand(*this, &global, &backtrace); Cssize cssize(*this, &backtrace); // expand and eval the tree root = root->perform(&expand)->block(); @@ -363,7 +357,6 @@ namespace Sass { // ToDo: maybe we can do this somewhere else? Remove_Placeholders remove_placeholders(*this); root->perform(&remove_placeholders); - // return processed tree return root; } diff --git a/contextualize.cpp b/contextualize.cpp deleted file mode 100644 index fca78c41cd..0000000000 --- a/contextualize.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "contextualize.hpp" -#include "ast.hpp" -#include "eval.hpp" -#include "backtrace.hpp" -#include "to_string.hpp" -#include "parser.hpp" - -namespace Sass { - - Contextualize::Contextualize(Context& ctx, Env* env, Backtrace* bt, Selector* placeholder, Selector* extender) - : ctx(ctx), env(env), backtrace(bt), parent(0), placeholder(placeholder), extender(extender) - { } - - Contextualize::~Contextualize() { } - - Selector* Contextualize::fallback_impl(AST_Node* n) - { return parent; } - - Contextualize* Contextualize::with(Selector* s, Env* e, Backtrace* bt, Selector* p, Selector* ex) - { - parent = s; - env = e; - backtrace = bt; - placeholder = p; - extender = ex; - return this; - } - - Selector* Contextualize::operator()(Selector_List* s) - { - Selector_List* p = static_cast(parent); - Selector_List* ss = 0; - if (p) { - ss = new (ctx.mem) Selector_List(s->pstate(), p->length() * s->length()); - if (s->length() == 0) { - Complex_Selector* comb = static_cast(parent->perform(this)); - if (parent->has_line_feed()) comb->has_line_feed(true); - if (comb) *ss << comb; - else cerr << "Warning: contextualize returned null" << endl; - } - for (size_t i = 0, L = p->length(); i < L; ++i) { - for (size_t j = 0, L = s->length(); j < L; ++j) { - parent = (*p)[i]; - Complex_Selector* comb = static_cast((*s)[j]->perform(this)); - if (parent->has_line_feed()) comb->has_line_feed(true); - if (comb) *ss << comb; - else cerr << "Warning: contextualize returned null" << endl; - } - } - } - else { - ss = new (ctx.mem) Selector_List(s->pstate(), s->length()); - for (size_t j = 0, L = s->length(); j < L; ++j) { - Complex_Selector* comb = static_cast((*s)[j]->perform(this)); - if (comb) *ss << comb; - } - } - return ss->length() ? ss : 0; - } - - Selector* Contextualize::operator()(Complex_Selector* s) - { - To_String to_string(&ctx); - Complex_Selector* ss = new (ctx.mem) Complex_Selector(*s); - // ss->last_block(s->last_block()); - // ss->media_block(s->media_block()); - Compound_Selector* new_head = 0; - Complex_Selector* new_tail = 0; - if (ss->head()) { - new_head = static_cast(s->head()->perform(this)); - ss->head(new_head); - } - if (ss->tail()) { - new_tail = static_cast(s->tail()->perform(this)); - // new_tail->last_block(s->last_block()); - // new_tail->media_block(s->media_block()); - ss->tail(new_tail); - } - if ((new_head && new_head->has_placeholder()) || (new_tail && new_tail->has_placeholder())) { - ss->has_placeholder(true); - } - else { - ss->has_placeholder(false); - } - if (!ss->head() && ss->combinator() == Complex_Selector::ANCESTOR_OF) { - return ss->tail(); - } - else { - return ss; - } - } - - Selector* Contextualize::operator()(Compound_Selector* s) - { - To_String to_string(&ctx); - if (placeholder && extender && s->perform(&to_string) == placeholder->perform(&to_string)) { - return extender; - } - Compound_Selector* ss = new (ctx.mem) Compound_Selector(s->pstate(), s->length()); - ss->last_block(s->last_block()); - ss->media_block(s->media_block()); - ss->has_line_break(s->has_line_break()); - for (size_t i = 0, L = s->length(); i < L; ++i) { - Simple_Selector* simp = static_cast((*s)[i]->perform(this)); - if (simp) *ss << simp; - } - return ss->length() ? ss : 0; - } - - Selector* Contextualize::operator()(Wrapped_Selector* s) - { - Selector* old_parent = parent; - parent = 0; - Wrapped_Selector* neg = new (ctx.mem) Wrapped_Selector(s->pstate(), - s->name(), - s->selector()->perform(this)); - parent = old_parent; - return neg; - } - - Selector* Contextualize::operator()(Pseudo_Selector* s) - { return s; } - - Selector* Contextualize::operator()(Selector_Qualifier* s) - { return s; } - - Selector* Contextualize::operator()(Type_Selector* s) - { return s; } - - Selector* Contextualize::operator()(Selector_Placeholder* p) - { - To_String to_string(&ctx); - if (placeholder && extender && p->perform(&to_string) == placeholder->perform(&to_string)) { - return extender; - } - else { - return p; - } - } - - Selector* Contextualize::operator()(Selector_Reference* s) - { - if (!parent) return 0; - Selector_Reference* ss = new (ctx.mem) Selector_Reference(*s); - ss->selector(parent); - return ss; - } -} diff --git a/contextualize.hpp b/contextualize.hpp deleted file mode 100644 index 7efccd1c08..0000000000 --- a/contextualize.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef SASS_CONTEXTUALIZE_H -#define SASS_CONTEXTUALIZE_H - -#include "context.hpp" -#include "operation.hpp" -#include "environment.hpp" -#include "ast_fwd_decl.hpp" - -namespace Sass { - struct Backtrace; - - typedef Environment Env; - - class Contextualize : public Operation_CRTP { - - - public: - Context& ctx; - Env* env; - Backtrace* backtrace; - Selector* parent; - Selector* placeholder; - Selector* extender; - - Selector* fallback_impl(AST_Node* n); - Contextualize(Context&, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0); - virtual ~Contextualize(); - Contextualize* with(Selector*, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0); - using Operation::operator(); - - Selector* operator()(Selector_List*); - Selector* operator()(Complex_Selector*); - Selector* operator()(Compound_Selector*); - Selector* operator()(Wrapped_Selector*); - Selector* operator()(Pseudo_Selector*); - Selector* operator()(Selector_Qualifier*); - Selector* operator()(Type_Selector*); - Selector* operator()(Selector_Placeholder*); - Selector* operator()(Selector_Reference*); - - template - Selector* fallback(U x) { return fallback_impl(x); } - }; -} - -#endif diff --git a/contextualize_eval.cpp b/contextualize_eval.cpp deleted file mode 100644 index 483bd8a252..0000000000 --- a/contextualize_eval.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "contextualize_eval.hpp" -#include "ast.hpp" -#include "eval.hpp" -#include "backtrace.hpp" -#include "to_string.hpp" -#include "parser.hpp" - -namespace Sass { - - Contextualize_Eval::Contextualize_Eval(Context& ctx, Eval* eval, Env* env, Backtrace* bt) - : Contextualize(ctx, env, bt), eval(eval) - { } - - Contextualize_Eval::~Contextualize_Eval() { } - - Selector* Contextualize_Eval::fallback_impl(AST_Node* n) - { - return Contextualize::fallback_impl(n); - } - - Contextualize_Eval* Contextualize_Eval::with(Selector* s, Env* e, Backtrace* bt, Selector* p, Selector* ex) - { - Contextualize::with(s, e, bt, p, ex); - eval = eval->with(s, e, bt, p, ex); - return this; - } - - Selector* Contextualize_Eval::operator()(Selector_Schema* s) - { - To_String to_string; - string result_str(s->contents()->perform(eval)->perform(&to_string)); - result_str += '{'; // the parser looks for a brace to end the selector - Selector* result_sel = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()).parse_selector_list(); - return result_sel->perform(this); - } - - Selector* Contextualize_Eval::operator()(Selector_List* s) - { - return Contextualize::operator ()(s); - } - - Selector* Contextualize_Eval::operator()(Complex_Selector* s) - { - return Contextualize::operator ()(s); - } - - Selector* Contextualize_Eval::operator()(Compound_Selector* s) - { - return Contextualize::operator ()(s); - } - - Selector* Contextualize_Eval::operator()(Wrapped_Selector* s) - { - return Contextualize::operator ()(s); - } - - Selector* Contextualize_Eval::operator()(Pseudo_Selector* s) - { - return Contextualize::operator ()(s); - } - - Selector* Contextualize_Eval::operator()(Attribute_Selector* s) - { - // the value might be interpolated; evaluate it - String* v = s->value(); - if (v && eval) { - Eval* eval_with = eval->with(parent, env, backtrace); - v = static_cast(v->perform(eval_with)); - } - To_String toString; - Attribute_Selector* ss = new (ctx.mem) Attribute_Selector(*s); - ss->value(v); - return ss; - } - - Selector* Contextualize_Eval::operator()(Selector_Qualifier* s) - { return Contextualize::operator ()(s); - } - - Selector* Contextualize_Eval::operator()(Type_Selector* s) - { return Contextualize::operator ()(s); - } - - Selector* Contextualize_Eval::operator()(Selector_Placeholder* p) - { - return Contextualize::operator ()(p); - } - - Selector* Contextualize_Eval::operator()(Selector_Reference* s) - { - return Contextualize::operator ()(s); - } -} diff --git a/contextualize_eval.hpp b/contextualize_eval.hpp deleted file mode 100644 index 738b4d606d..0000000000 --- a/contextualize_eval.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef SASS_CONTEXTUALIZE_EVAL_H -#define SASS_CONTEXTUALIZE_EVAL_H - -#include "eval.hpp" -#include "context.hpp" -#include "operation.hpp" -#include "environment.hpp" -#include "ast_fwd_decl.hpp" - -namespace Sass { - struct Backtrace; - - typedef Environment Env; - - class Contextualize_Eval : public Contextualize { - - Eval* eval; - - Selector* fallback_impl(AST_Node* n); - - public: - Contextualize_Eval(Context&, Eval*, Env*, Backtrace*); - virtual ~Contextualize_Eval(); - Contextualize_Eval* with(Selector*, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0); - using Operation::operator(); - - Selector* operator()(Selector_Schema*); - Selector* operator()(Selector_List*); - Selector* operator()(Complex_Selector*); - Selector* operator()(Compound_Selector*); - Selector* operator()(Wrapped_Selector*); - Selector* operator()(Pseudo_Selector*); - Selector* operator()(Attribute_Selector*); - Selector* operator()(Selector_Qualifier*); - Selector* operator()(Type_Selector*); - Selector* operator()(Selector_Placeholder*); - Selector* operator()(Selector_Reference*); - - template - Selector* fallback(U x) { return fallback_impl(x); } - }; -} - -#endif diff --git a/cssize.cpp b/cssize.cpp index 2b01857930..45159e9fb7 100644 --- a/cssize.cpp +++ b/cssize.cpp @@ -292,7 +292,7 @@ namespace Sass { Media_Block* mm = new (ctx.mem) Media_Block(m->pstate(), m->media_queries(), wrapper_block, - m->selector()); + 0); mm->tabs(m->tabs()); @@ -368,7 +368,7 @@ namespace Sass { case Statement::NONE: default: error("unknown internal error; please contact the LibSass maintainers", s->pstate(), backtrace); - String_Constant* msg = new (ctx.mem) String_Constant(ParserState("[WARN]"), string("`CSSize` can't clone ") + typeid(*s).name()); + String_Quoted* msg = new (ctx.mem) String_Quoted(ParserState("[WARN]"), string("`CSSize` can't clone ") + typeid(*s).name()); return new (ctx.mem) Warning(ParserState("[WARN]"), msg); } } @@ -539,7 +539,7 @@ namespace Sass { ); if (!type.empty()) { - mm->media_type(new (ctx.mem) String_Constant(mq1->pstate(), type)); + mm->media_type(new (ctx.mem) String_Quoted(mq1->pstate(), type)); } *mm += mq2; diff --git a/debug.hpp b/debug.hpp index ba7629621d..7295f4ea47 100644 --- a/debug.hpp +++ b/debug.hpp @@ -4,16 +4,16 @@ #include enum dbg_lvl_t : uint32_t { - NONE = 0, - TRIM = 1, - CHUNKS = 2, - SUBWEAVE = 4, - WEAVE = 8, - EXTEND_COMPOUND = 16, - EXTEND_COMPLEX = 32, - LCS = 64, + NONE = 0, + TRIM = 1, + CHUNKS = 2, + SUBWEAVE = 4, + WEAVE = 8, + EXTEND_COMPOUND = 16, + EXTEND_COMPLEX = 32, + LCS = 64, EXTEND_OBJECT = 128, - ALL = UINT32_MAX + ALL = UINT32_MAX }; #ifdef DEBUG diff --git a/debugger.hpp b/debugger.hpp index 403705988f..26d5bf9105 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -3,11 +3,34 @@ #include #include +#include "node.hpp" #include "ast_fwd_decl.hpp" using namespace std; using namespace Sass; +/* +inline void debug_extenstion_map(Sass::ExtensionSubsetMap* map, string ind = "") +{ + if (ind == "") cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + for(auto const &it : map->values()) { + debug_ast(it.first, ind + "first: "); + debug_ast(it.second, ind + "second: "); + } + if (ind == "") cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; +} + +inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") +{ + if (ind == "") cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + for(auto const &pair : *entries) { + debug_ast(pair.first, ind + "first: "); + debug_ast(pair.second, ind + "second: "); + } + if (ind == "") cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; +} +*/ + inline string str_replace(std::string str, const std::string& oldStr, const std::string& newStr) { size_t pos = 0; @@ -64,8 +87,6 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) Selector_List* selector = dynamic_cast(node); cerr << ind << "Selector_List " << selector; cerr << " (" << pstate_source_position(node) << ")"; - cerr << " [block:" << selector->last_block() << "]"; - cerr << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : ""); cerr << " [@media:" << selector->media_block() << "]"; cerr << (selector->is_optional() ? " [is_optional]": " -"); cerr << (selector->has_line_break() ? " [line-break]": " -"); @@ -81,37 +102,40 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) } else if (dynamic_cast(node)) { Parent_Selector* selector = dynamic_cast(node); cerr << ind << "Parent_Selector " << selector; +// if (selector->not_selector()) cerr << " [in_declaration]"; cerr << " (" << pstate_source_position(node) << ")"; cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << endl; - debug_ast(selector->selector(), ind + "->", env); +// debug_ast(selector->selector(), ind + "->", env); } else if (dynamic_cast(node)) { Complex_Selector* selector = dynamic_cast(node); cerr << ind << "Complex_Selector " << selector << " (" << pstate_source_position(node) << ")" - << " [block:" << selector->last_block() << "]" << " [weight:" << longToHex(selector->specificity()) << "]" - << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : "") << " [@media:" << selector->media_block() << "]" << (selector->is_optional() ? " [is_optional]": " -") + << (selector->has_line_feed() ? " [line-feed]": " -") << (selector->has_line_break() ? " [line-break]": " -") - << (selector->has_line_feed() ? " [line-feed]": " -") << " -> "; + << " -- "; + string del; switch (selector->combinator()) { - case Complex_Selector::PARENT_OF: cerr << "{>}"; break; - case Complex_Selector::PRECEDES: cerr << "{~}"; break; - case Complex_Selector::ADJACENT_TO: cerr << "{+}"; break; - case Complex_Selector::ANCESTOR_OF: cerr << "{ }"; break; + case Complex_Selector::PARENT_OF: del = ">"; break; + case Complex_Selector::PRECEDES: del = "~"; break; + case Complex_Selector::ADJACENT_TO: del = "+"; break; + case Complex_Selector::ANCESTOR_OF: del = " "; break; } cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << endl; - debug_ast(selector->head(), ind + " ", env); - debug_ast(selector->tail(), ind + "-", env); + debug_ast(selector->head(), ind + " " /* + "[" + del + "]" */, env); + if (selector->tail()) { + debug_ast(selector->tail(), ind + "{" + del + "}", env); + } else if(del != " ") { + cerr << ind << " |" << del << "| {trailing op}" << endl; + } } else if (dynamic_cast(node)) { Compound_Selector* selector = dynamic_cast(node); cerr << ind << "Compound_Selector " << selector; cerr << " (" << pstate_source_position(node) << ")"; - cerr << " [block:" << selector->last_block() << "]"; cerr << " [weight:" << longToHex(selector->specificity()) << "]"; - // cerr << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : ""); cerr << " [@media:" << selector->media_block() << "]"; cerr << (selector->is_optional() ? " [is_optional]": " -"); cerr << (selector->has_line_break() ? " [line-break]": " -"); @@ -157,18 +181,12 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) Selector_Placeholder* selector = dynamic_cast(node); cerr << ind << "Selector_Placeholder [" << selector->name() << "] " << selector - << " [block:" << selector->last_block() << "]" << " [@media:" << selector->media_block() << "]" << (selector->is_optional() ? " [is_optional]": " -") << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << endl; - } else if (dynamic_cast(node)) { - Selector_Reference* selector = dynamic_cast(node); - cerr << ind << "Selector_Reference " << selector; - cerr << " (" << pstate_source_position(node) << ")"; - cerr << " @ref " << selector->selector() << endl; } else if (dynamic_cast(node)) { Simple_Selector* selector = dynamic_cast(node); cerr << ind << "Simple_Selector " << selector; @@ -178,9 +196,8 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) } else if (dynamic_cast(node)) { Selector_Schema* selector = dynamic_cast(node); cerr << ind << "Selector_Schema " << selector; - cerr << " (" << pstate_source_position(node) << ")"; - cerr << " [block:" << selector->last_block() << "]" - << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : "") + cerr << " (" << pstate_source_position(node) << ")" + << (selector->at_root() && selector->at_root() ? " [@ROOT]" : "") << " [@media:" << selector->media_block() << "]" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") @@ -222,7 +239,6 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) cerr << " (" << pstate_source_position(node) << ")"; cerr << " " << block->tabs() << endl; debug_ast(block->media_queries(), ind + " =@ "); - debug_ast(block->selector(), ind + " -@ "); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Supports_Block* block = dynamic_cast(node); @@ -321,8 +337,8 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) cerr << ind << "At_Rule " << block; cerr << " (" << pstate_source_position(node) << ")"; cerr << " [" << block->keyword() << "] " << block->tabs() << endl; - debug_ast(block->value(), ind + "+", env); debug_ast(block->selector(), ind + "~", env); + debug_ast(block->value(), ind + "+", env); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Each* block = dynamic_cast(node); @@ -360,18 +376,19 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) cerr << " [" << block->name() << "]" << endl; debug_ast(block->arguments(), ind + " args: "); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } - } else if (dynamic_cast(node)) { - Ruleset* ruleset = dynamic_cast(node); + } else if (Ruleset* ruleset = dynamic_cast(node)) { cerr << ind << "Ruleset " << ruleset; cerr << " (" << pstate_source_position(node) << ")"; - cerr << " " << ruleset->tabs() << endl; - debug_ast(ruleset->selector(), ind + " "); - if (ruleset->block()) for(auto i : ruleset->block()->elements()) { debug_ast(i, ind + " ", env); } + cerr << " [indent: " << ruleset->tabs() << "]"; + cerr << (ruleset->at_root() ? " [@ROOT]" : ""); + cerr << endl; + debug_ast(ruleset->selector(), ind + ">"); + debug_ast(ruleset->block(), ind + " "); } else if (dynamic_cast(node)) { Block* block = dynamic_cast(node); cerr << ind << "Block " << block; cerr << " (" << pstate_source_position(node) << ")"; - cerr << " " << block->tabs() << endl; + cerr << " [indent: " << block->tabs() << "]" << endl; for(auto i : block->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Textual* expression = dynamic_cast(node); @@ -556,4 +573,53 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) if (ind == "") cerr << "####################################################################\n"; } +inline void debug_node(Node* node, string ind = "") +{ + if (ind == "") cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; + if (node->isCombinator()) { + cerr << ind; + cerr << "Combinator "; + cerr << node << " "; + if (node->got_line_feed) cerr << "[LF] "; + switch (node->combinator()) { + case Complex_Selector::ADJACENT_TO: cerr << "{+} "; break; + case Complex_Selector::PARENT_OF: cerr << "{>} "; break; + case Complex_Selector::PRECEDES: cerr << "{~} "; break; + case Complex_Selector::REFERENCE: cerr << "{@} "; break; + case Complex_Selector::ANCESTOR_OF: cerr << "{ } "; break; + } + cerr << endl; + // debug_ast(node->combinator(), ind + " "); + } else if (node->isSelector()) { + cerr << ind; + cerr << "Selector "; + cerr << node << " "; + if (node->got_line_feed) cerr << "[LF] "; + cerr << endl; + debug_ast(node->selector(), ind + " "); + } else if (node->isCollection()) { + cerr << ind; + cerr << "Collection "; + cerr << node << " "; + if (node->got_line_feed) cerr << "[LF] "; + cerr << endl; + for(auto n : (*node->collection())) { + debug_node(&n, ind + " "); + } + } else if (node->isNil()) { + cerr << ind; + cerr << "Nil "; + cerr << node << " "; + if (node->got_line_feed) cerr << "[LF] "; + cerr << endl; + } else { + cerr << ind; + cerr << "OTHER "; + cerr << node << " "; + if (node->got_line_feed) cerr << "[LF] "; + cerr << endl; + } + if (ind == "") cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +} + #endif // SASS_DEBUGGER diff --git a/emitter.cpp b/emitter.cpp index 555cefdf52..9bedfde99f 100644 --- a/emitter.cpp +++ b/emitter.cpp @@ -227,6 +227,7 @@ namespace Sass { void Emitter::append_scope_opener(AST_Node* node) { + scheduled_linefeed = 0; append_optional_space(); flush_schedules(); if (node) add_open_mapping(node); diff --git a/eval.cpp b/eval.cpp index a55dda62b7..923c37154a 100644 --- a/eval.cpp +++ b/eval.cpp @@ -12,11 +12,16 @@ #include "util.hpp" #include "to_string.hpp" #include "inspect.hpp" +#include "environment.hpp" +#include "position.hpp" +#include "sass_values.h" #include "to_c.hpp" #include "context.hpp" #include "backtrace.hpp" +#include "lexer.hpp" #include "prelexer.hpp" #include "parser.hpp" +#include "expand.hpp" namespace Sass { using namespace std; @@ -26,7 +31,6 @@ namespace Sass { inline double mul(double x, double y) { return x * y; } inline double div(double x, double y) { return x / y; } // x/0 checked by caller inline double mod(double x, double y) { return abs(fmod(x, y)); } // x/0 checked by caller - typedef double (*bop)(double, double); bop ops[Binary_Expression::NUM_OPS] = { 0, 0, // and, or @@ -34,24 +38,31 @@ namespace Sass { add, sub, mul, div, mod }; - Eval::Eval(Context& ctx, Contextualize* contextualize, Listize* listize, Env* env, Backtrace* bt) - : ctx(ctx), contextualize(contextualize), listize(listize), env(env), backtrace(bt) { } + Eval::Eval(Expand& exp) + : exp(exp), + ctx(exp.ctx), + listize(exp.ctx) + { } Eval::~Eval() { } - Eval* Eval::with(Env* e, Backtrace* bt) // for setting the env before eval'ing an expression + Context& Eval::context() + { + return ctx; + } + + Env* Eval::environment() + { + return exp.environment(); + } + + Selector_List* Eval::selector() { - contextualize = contextualize->with(0, e, bt); - env = e; - backtrace = bt; - return this; + return exp.selector(); } - Eval* Eval::with(Selector* c, Env* e, Backtrace* bt, Selector* p, Selector* ex) // for setting the env before eval'ing an expression + Backtrace* Eval::backtrace() { - contextualize = contextualize->with(c, e, bt, p, ex); - env = e; - backtrace = bt; - return this; + return exp.backtrace(); } Expression* Eval::operator()(Block* b) @@ -66,6 +77,7 @@ namespace Sass { Expression* Eval::operator()(Assignment* a) { + Env* env = exp.environment(); string var(a->variable()); if (a->is_global()) { if (a->is_default()) { @@ -126,14 +138,18 @@ namespace Sass { Expression* Eval::operator()(If* i) { + Expression* rv = 0; + Env env(exp.environment()); + exp.env_stack.push_back(&env); if (*i->predicate()->perform(this)) { - return i->consequent()->perform(this); + rv = i->consequent()->perform(this); } else { Block* alt = i->alternative(); - if (alt) return alt->perform(this); + if (alt) rv = alt->perform(this); } - return 0; + exp.env_stack.pop_back(); + return rv; } // For does not create a new env scope @@ -156,11 +172,12 @@ namespace Sass { stringstream msg; msg << "Incompatible units: '" << sass_start->unit() << "' and '" << sass_end->unit() << "'."; - error(msg.str(), low->pstate(), backtrace); + error(msg.str(), low->pstate(), backtrace()); } double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment + Env* env = exp.environment(); Number* it = new (env->mem) Number(low->pstate(), start, sass_end->unit()); AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0; env->set_local(variable, it); @@ -199,6 +216,7 @@ namespace Sass { { vector variables(e->variables()); Expression* expr = e->list()->perform(this); + Env* env = exp.environment(); List* list = 0; Map* map = 0; if (expr->concrete_type() == Expression::MAP) { @@ -289,6 +307,7 @@ namespace Sass { { Expression* message = w->message()->perform(this); To_String to_string(&ctx); + Env* env = exp.environment(); // try to use generic function if (env->has("@warn[f]")) { @@ -310,7 +329,7 @@ namespace Sass { } string result(unquote(message->perform(&to_string))); - Backtrace top(backtrace, w->pstate(), ""); + Backtrace top(backtrace(), w->pstate(), ""); cerr << "WARNING: " << result; cerr << top.to_string(true); cerr << endl << endl; @@ -321,6 +340,7 @@ namespace Sass { { Expression* message = e->message()->perform(this); To_String to_string(&ctx); + Env* env = exp.environment(); // try to use generic function if (env->has("@error[f]")) { @@ -350,6 +370,7 @@ namespace Sass { { Expression* message = d->value()->perform(this); To_String to_string(&ctx); + Env* env = exp.environment(); // try to use generic function if (env->has("@debug[f]")) { @@ -434,6 +455,7 @@ namespace Sass { Binary_Expression::Type op_type = b->type(); // don't eval delayed expressions (the '/' when used as a separator) if (op_type == Binary_Expression::DIV && b->is_delayed()) return b; + b->is_delayed(false); // if one of the operands is a '/' then make sure it's evaluated Expression* lhs = b->left()->perform(this); lhs->is_delayed(false); @@ -467,11 +489,22 @@ namespace Sass { } else { - rhs->is_delayed(false); - rhs = rhs->perform(this); + // rhs->set_delayed(false); + // rhs = rhs->perform(this); } // upgrade string to number if possible (issue #948) + if (op_type == Binary_Expression::DIV || op_type == Binary_Expression::MUL) { + if (String_Constant* str = dynamic_cast(rhs)) { + const char* start = str->value().c_str(); + if (Prelexer::sequence < Prelexer::number >(start) != 0) { + rhs = new (ctx.mem) Textual(rhs->pstate(), Textual::DIMENSION, str->value()); + rhs->is_delayed(false); rhs = rhs->perform(this); + } + } + } + + // see if it's a relational expression switch(op_type) { case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->pstate(), eq(lhs, rhs, ctx)); case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->pstate(), !eq(lhs, rhs, ctx)); @@ -500,7 +533,7 @@ namespace Sass { } Expression* ex = op_strings(ctx, op_type, lhs, rhs); - if (String_Constant* str = (String_Constant*) ex) + if (String_Constant* str = dynamic_cast(ex)) { if (str->concrete_type() != Expression::STRING) return ex; String_Constant* lstr = dynamic_cast(lhs); @@ -532,10 +565,10 @@ namespace Sass { // Special cases: +/- variables which evaluate to null ouput just +/-, // but +/- null itself outputs the string if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) { - u->operand(new (ctx.mem) String_Constant(u->pstate(), "")); + u->operand(new (ctx.mem) String_Quoted(u->pstate(), "")); } else u->operand(operand); - String_Constant* result = new (ctx.mem) String_Constant(u->pstate(), + String_Constant* result = new (ctx.mem) String_Quoted(u->pstate(), u->perform(&to_string)); return result; } @@ -545,10 +578,10 @@ namespace Sass { Expression* Eval::operator()(Function_Call* c) { - if (backtrace->parent != NULL && backtrace->depth() > Constants::MaxCallStack) { + if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) { ostringstream stm; stm << "Stack depth exceeded max of " << Constants::MaxCallStack; - error(stm.str(), c->pstate(), backtrace); + error(stm.str(), c->pstate(), backtrace()); } string name(Util::normalize_underscores(c->name())); string full_name(name + "[f]"); @@ -557,84 +590,61 @@ namespace Sass { args = static_cast(args->perform(this)); } - // try to use generic function + Env* env = environment(); if (!env->has(full_name)) { - if (env->has("*[f]")) { + if (!env->has("*[f]")) { + // just pass it through as a literal + Function_Call* lit = new (ctx.mem) Function_Call(c->pstate(), + c->name(), + args); + To_String to_string(&ctx); + return new (ctx.mem) String_Quoted(c->pstate(), + lit->perform(&to_string)); + } else { + // call generic function full_name = "*[f]"; } } - // if it doesn't exist, just pass it through as a literal - if (!env->has(full_name)) { - Function_Call* lit = new (ctx.mem) Function_Call(c->pstate(), - c->name(), - args); - To_String to_string(&ctx); - return new (ctx.mem) String_Constant(c->pstate(), - lit->perform(&to_string)); + Definition* def = static_cast((*env)[full_name]); + + if (def->is_overload_stub()) { + stringstream ss; + ss << full_name + << args->length(); + full_name = ss.str(); + string resolved_name(full_name); + if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->pstate()); + def = static_cast((*env)[resolved_name]); } Expression* result = c; - Definition* def = static_cast((*env)[full_name]); Block* body = def->block(); Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); - if (full_name != "if[f]") { - for (size_t i = 0, L = args->length(); i < L; ++i) { - (*args)[i]->value((*args)[i]->value()->perform(this)); - } - } - Parameters* params = def->parameters(); - Env new_env; - new_env.link(def->environment()); - // bind("function " + c->name(), params, args, ctx, &new_env, this); - // Env* old_env = env; - // env = &new_env; - - // Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`"); - // backtrace = &here; - - // if it's user-defined, eval the body - if (body) { + Env fn_env(def->environment()); + exp.env_stack.push_back(&fn_env); - bind("function " + c->name(), params, args, ctx, &new_env, this); - Env* old_env = env; - env = &new_env; - - Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); - backtrace = &here; - - result = body->perform(this); - if (!result) { - error(string("function ") + c->name() + " did not return a value", c->pstate()); - } - backtrace = here.parent; - env = old_env; + if (func || body) { + bind("function " + c->name(), params, args, ctx, &fn_env, this); + Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`"); + exp.backtrace_stack.push_back(&here); + // if it's user-defined, eval the body + if (body) result = body->perform(this); + // if it's native, invoke the underlying CPP function + else result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace()); + if (!result) error(string("function ") + c->name() + " did not return a value", c->pstate()); + exp.backtrace_stack.pop_back(); } - // if it's native, invoke the underlying CPP function - else if (func) { - - bind("function " + c->name(), params, args, ctx, &new_env, this); - Env* old_env = env; - env = &new_env; - Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); - backtrace = &here; - - result = func(*env, *old_env, ctx, def->signature(), c->pstate(), backtrace); - - backtrace = here.parent; - env = old_env; - } // else if it's a user-defined c function // convert call into C-API compatible form else if (c_function) { - Sass_Function_Fn c_func = sass_function_get_function(c_function); if (full_name == "*[f]") { - String_Constant *str = new (ctx.mem) String_Constant(c->pstate(), c->name()); + String_Quoted *str = new (ctx.mem) String_Quoted(c->pstate(), c->name()); Arguments* new_args = new (ctx.mem) Arguments(c->pstate()); *new_args << new (ctx.mem) Argument(c->pstate(), str); *new_args += args; @@ -642,63 +652,33 @@ namespace Sass { } // populates env with default values for params - bind("function " + c->name(), params, args, ctx, &new_env, this); - Env* old_env = env; - env = &new_env; + bind("function " + c->name(), params, args, ctx, &fn_env, this); - Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); - backtrace = &here; + Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`"); + exp.backtrace_stack.push_back(&here); To_C to_c; - union Sass_Value* c_args = sass_make_list(env->local_frame().size(), SASS_COMMA); + union Sass_Value* c_args = sass_make_list(params[0].length(), SASS_COMMA); for(size_t i = 0; i < params[0].length(); i++) { string key = params[0][i]->name(); - AST_Node* node = env->local_frame().at(key); + AST_Node* node = fn_env.get_local(key); Expression* arg = static_cast(node); sass_list_set_value(c_args, i, arg->perform(&to_c)); } Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options); if (sass_value_get_tag(c_val) == SASS_ERROR) { - error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace); + error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace()); } else if (sass_value_get_tag(c_val) == SASS_WARNING) { - error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace); + error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace()); } - result = cval_to_astnode(c_val, ctx, backtrace, c->pstate()); + result = cval_to_astnode(c_val, ctx, backtrace(), c->pstate()); - backtrace = here.parent; + exp.backtrace_stack.pop_back(); sass_delete_value(c_args); if (c_val != c_args) sass_delete_value(c_val); - env = old_env; - } - // else it's an overloaded native function; resolve it - else if (def->is_overload_stub()) { - size_t arity = args->length(); - stringstream ss; - ss << full_name << arity; - string resolved_name(ss.str()); - if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->pstate()); - Definition* resolved_def = static_cast((*env)[resolved_name]); - params = resolved_def->parameters(); - Env newer_env; - newer_env.link(resolved_def->environment()); - bind("function " + c->name(), params, args, ctx, &newer_env, this); - Env* old_env = env; - env = &newer_env; - - Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`"); - backtrace = &here; - - result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->pstate(), backtrace); - - backtrace = here.parent; - env = old_env; } - // link back to function definition - // only do this for custom functions - - // link back to function definition // only do this for custom functions if (result->pstate().file == string::npos) @@ -706,6 +686,7 @@ namespace Sass { result->is_delayed(result->concrete_type() == Expression::STRING); if (!result->is_delayed()) result = result->perform(this); + exp.env_stack.pop_back(); return result; } @@ -723,6 +704,7 @@ namespace Sass { To_String to_string(&ctx); string name(v->name()); Expression* value = 0; + Env* env = environment(); if (env->has(name)) value = static_cast((*env)[name]); else error("Undefined variable: \"" + v->name() + "\".", v->pstate()); // cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << endl; @@ -737,11 +719,7 @@ namespace Sass { if (auto str = dynamic_cast(value)) { value = new (ctx.mem) String_Quoted(*str); } else if (auto str = dynamic_cast(value)) { - if (str->quote_mark()) { - value = new (ctx.mem) String_Quoted(str->pstate(), str->perform(&to_string)); - } else { - value = new (ctx.mem) String_Constant(str->pstate(), unquote(str->value())); - } + value = new (ctx.mem) String_Quoted(str->pstate(), str->perform(&to_string)); } } else if (value->concrete_type() == Expression::LIST) { @@ -759,6 +737,9 @@ namespace Sass { else if (value->concrete_type() == Expression::NULL_VAL) { value = new (ctx.mem) Null(value->pstate()); } + else if (value->concrete_type() == Expression::SELECTOR) { + value = value->perform(this)->perform(&listize); + } // cerr << "\ttype is now: " << typeid(*value).name() << endl << endl; return value; @@ -800,7 +781,7 @@ namespace Sass { break; case Textual::HEX: { if (t->value().substr(0, 1) != "#") { - result = new (ctx.mem) String_Constant(t->pstate(), t->value()); + result = new (ctx.mem) String_Quoted(t->pstate(), t->value()); break; } string hext(t->value().substr(1)); // chop off the '#' @@ -851,10 +832,11 @@ namespace Sass { } string Eval::interpolation(Expression* s) { + Env* env = environment(); if (String_Quoted* str_quoted = dynamic_cast(s)) { if (str_quoted->quote_mark()) { if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) { - return interpolation(new (ctx.mem) String_Constant(*str_quoted)); + return evacuate_escapes(str_quoted->value()); } else { return string_escape(quote(str_quoted->value(), str_quoted->quote_mark())); } @@ -862,10 +844,16 @@ namespace Sass { return evacuate_escapes(str_quoted->value()); } } else if (String_Constant* str_constant = dynamic_cast(s)) { - string str = str_constant->value(); - if (!str_constant->quote_mark()) str = unquote(str); - return evacuate_escapes(str); + return evacuate_escapes(str_constant->value()); + } else if (dynamic_cast(s)) { + To_String to_string(&ctx); + Expression* sel = s->perform(this); + return evacuate_quotes(sel ? sel->perform(&to_string) : ""); + } else if (String_Schema* str_schema = dynamic_cast(s)) { + // To_String to_string(&ctx); + // return evacuate_quotes(str_schema->perform(&to_string)); + string res = ""; for(auto i : str_schema->elements()) res += (interpolation(i)); @@ -893,25 +881,17 @@ namespace Sass { if (!env->has(name)) error("Undefined variable: \"" + var->name() + "\".", var->pstate()); Expression* value = static_cast((*env)[name]); return evacuate_quotes(interpolation(value)); - } else if (Binary_Expression* var = dynamic_cast(s)) { - var->is_delayed(false); - Expression* ex = var->perform(this); - return evacuate_quotes(interpolation(ex)); - } else if (Function_Call* var = dynamic_cast(s)) { - Expression* ex = var->perform(this); - return evacuate_quotes(interpolation(ex)); - } else if (Parent_Selector* var = dynamic_cast(s)) { - Expression* ex = var->perform(this); + } else if (dynamic_cast(s)) { + Expression* ex = s->perform(this); return evacuate_quotes(interpolation(ex)); - } else if (Unary_Expression* var = dynamic_cast(s)) { - Expression* ex = var->perform(this); + } else if (dynamic_cast(s)) { + Expression* ex = s->perform(this); return evacuate_quotes(interpolation(ex)); - } else if (Selector* var = dynamic_cast(s)) { - Expression* ex = var->perform(this); + } else if (dynamic_cast(s)) { + Expression* ex = s->perform(this); return evacuate_quotes(interpolation(ex)); } else { To_String to_string(&ctx); - // to_string.in_decl_list = true; return evacuate_quotes(s->perform(&to_string)); } } @@ -934,7 +914,7 @@ namespace Sass { Expression* Eval::operator()(String_Constant* s) { - if (!s->quote_mark() && !s->is_delayed() && ctx.names_to_colors.count(s->value())) { + if (!s->is_delayed() && ctx.names_to_colors.count(s->value())) { Color* c = new (ctx.mem) Color(*ctx.names_to_colors[s->value()]); c->pstate(s->pstate()); c->disp(s->value()); @@ -943,6 +923,11 @@ namespace Sass { return s; } + Expression* Eval::operator()(String_Quoted* s) + { + return s; + } + Expression* Eval::operator()(Supports_Query* q) { Supports_Query* qq = new (ctx.mem) Supports_Query(q->pstate(), @@ -1004,13 +989,13 @@ namespace Sass { Expression* feature = e->feature(); feature = (feature ? feature->perform(this) : 0); if (feature && dynamic_cast(feature)) { - feature = new (ctx.mem) String_Constant(feature->pstate(), + feature = new (ctx.mem) String_Quoted(feature->pstate(), dynamic_cast(feature)->value()); } Expression* value = e->value(); value = (value ? value->perform(this) : 0); if (value && dynamic_cast(value)) { - value = new (ctx.mem) String_Constant(value->pstate(), + value = new (ctx.mem) String_Quoted(value->pstate(), dynamic_cast(value)->value()); } return new (ctx.mem) Media_Query_Expression(e->pstate(), @@ -1069,19 +1054,6 @@ namespace Sass { return 0; } - Expression* Eval::operator()(Parent_Selector* p) - { - // no idea why both calls are needed - Selector* s = p->perform(contextualize); - if (!s) s = p->selector()->perform(contextualize); - // access to parent selector may return 0 - Selector_List* l = static_cast(s); - // some spec tests cause this (might be a valid case!) - // if (!s) { cerr << "Parent Selector eval error" << endl; } - if (!s) { l = new (ctx.mem) Selector_List(p->pstate()); } - return l->perform(listize); - } - inline Expression* Eval::fallback_impl(AST_Node* n) { return static_cast(n); @@ -1178,7 +1150,7 @@ namespace Sass { double rv = r->value(); Binary_Expression::Type op = b->type(); if (op == Binary_Expression::DIV && !rv) { - return new (ctx.mem) String_Constant(l->pstate(), "Infinity"); + return new (ctx.mem) String_Quoted(l->pstate(), "Infinity"); } if (op == Binary_Expression::MOD && !rv) { error("division by zero", r->pstate()); @@ -1248,7 +1220,7 @@ namespace Sass { string color(r->sixtuplet() && (ctx.output_style != COMPRESSED) ? r->perform(&to_string) : Util::normalize_sixtuplet(r->perform(&to_string))); - return new (ctx.mem) String_Constant(l->pstate(), + return new (ctx.mem) String_Quoted(l->pstate(), l->perform(&to_string) + sep + color); @@ -1389,4 +1361,149 @@ namespace Sass { return e; } + Selector_List* Eval::operator()(Selector_List* s) + { + vector rv; + Selector_List* sl = new (ctx.mem) Selector_List(s->pstate()); + for (size_t i = 0, iL = s->length(); i < iL; ++i) { + rv.push_back(operator()((*s)[i])); + } + + // we should actually permutate parent first + // but here we have permutated the selector first + size_t round = 0; + while (round != string::npos) { + bool abort = true; + for (size_t i = 0, iL = rv.size(); i < iL; ++i) { + if (rv[i]->length() > round) { + *sl << (*rv[i])[round]; + abort = false; + } + } + if (abort) { + round = string::npos; + } else { + ++ round; + } + + } + return sl; + } + + + Selector_List* Eval::operator()(Complex_Selector* s) + { + if (s == 0) return 0; + bool parentized = false; + Complex_Selector* tail = s->tail(); + Compound_Selector* head = s->head(); + Complex_Selector::Combinator combinator = s->combinator(); + Selector_List* sl = new (ctx.mem) Selector_List(s->pstate()); + + if (head) { + // check if we have a parent selector reference (expands to list) + if (head->length() > 0 && dynamic_cast((*head)[0])) { + // do we have any parents to interpolate + Selector_List* pr = selector(); + if (pr && pr->length() > 0) { + for (size_t n = 0, nL = pr->length(); n < nL; ++n) { + if (tail) { + vector rv; + Selector_List* tails = operator()(tail); + for (size_t m = 0, mL = tails->length(); m < mL; ++m) { + Complex_Selector* ns = (*pr)[n]->cloneFully(ctx); + if (s->has_line_feed()) ns->has_line_feed(true); + Complex_Selector* tt = (*tails)[m]; + Complex_Selector* last = ns->last(); + if (combinator != Complex_Selector::ANCESTOR_OF) { + Complex_Selector* cp = 0; + cp = new (ctx.mem) Complex_Selector(s->pstate()); + cp->head(head); cp->tail(tt); + cp->combinator(combinator); + last->tail(cp); + } else { + last->tail(tt); + } + for (size_t i = 1, iL = head->length(); i < iL; ++i) { + // add simple selectors + *last->head() << (*head)[i]; + } + *sl << ns; + } + // EO foreach parentized tail + } else { + Complex_Selector* ns = (*pr)[n]->cloneFully(ctx); + Complex_Selector* last = ns->last(); + ns->combinator(combinator); + for (size_t i = 1, iL = head->length(); i < iL; ++i) { + // add simple selectors + *last->head() << (*head)[i]; + } + *sl << ns; + } + } + parentized = true; + } + + } + + } + + if (parentized == false) { + if (s->tail()) { + Selector_List* tails = operator()(s->tail()); + for (size_t m = 0, mL = tails->length(); m < mL; ++m) { + Complex_Selector* ss = new (ctx.mem) Complex_Selector(*s); + ss->tail((*tails)[m]); + *sl << ss; + } + } + else { + *sl << s; + } + } + + for (size_t i = 0, iL = sl->length(); i < iL; ++i) { + + if (!(*sl)[i]->head()) continue; + if ((*sl)[i]->combinator() != Complex_Selector::ANCESTOR_OF) continue; + if ((*sl)[i]->head()->is_empty_reference()) { + if ((*sl)[i]->has_line_feed()) { + if ((*sl)[i]->tail()) (*sl)[i]->tail()->has_line_feed(true); + } + (*sl)[i] = (*sl)[i]->tail(); + } + + } + + return sl; + } + + Attribute_Selector* Eval::operator()(Attribute_Selector* s) + { + String* attr = s->value(); + if (attr) { attr = static_cast(attr->perform(this)); } + Attribute_Selector* ss = new (ctx.mem) Attribute_Selector(*s); + ss->value(attr); + return ss; + } + + Selector_List* Eval::operator()(Selector_Schema* s) + { + To_String to_string; + // the parser will look for a brace to end the selector + string result_str(s->contents()->perform(this)->perform(&to_string) + "{"); + Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()); + return operator()(p.parse_selector_list(exp.block_stack.back()->is_root())); + } + + Expression* Eval::operator()(Parent_Selector* p) + { + Selector_List* pr = selector(); + exp.selector_stack.pop_back(); + if (pr) pr = operator()(pr); + exp.selector_stack.push_back(pr); + return pr; + } + } diff --git a/eval.hpp b/eval.hpp index cabcf712a2..146f1d7126 100644 --- a/eval.hpp +++ b/eval.hpp @@ -2,38 +2,34 @@ #define SASS_EVAL_H #include - #include "context.hpp" -#include "position.hpp" -#include "operation.hpp" -#include "environment.hpp" -#include "contextualize.hpp" #include "listize.hpp" -#include "sass_values.h" +#include "operation.hpp" namespace Sass { using namespace std; - typedef Environment Env; - struct Backtrace; - class Contextualize; + class Expand; + class Context; class Listize; class Eval : public Operation_CRTP { - Context& ctx; - + private: Expression* fallback_impl(AST_Node* n); - public: - Contextualize* contextualize; - Listize* listize; - Env* env; - Backtrace* backtrace; - Eval(Context&, Contextualize*, Listize*, Env*, Backtrace*); + public: + Expand& exp; + Context& ctx; + Listize listize; + Eval(Expand& exp); virtual ~Eval(); - Eval* with(Env* e, Backtrace* bt); // for setting the env before eval'ing an expression - Eval* with(Selector* c, Env* e, Backtrace* bt, Selector* placeholder = 0, Selector* extender = 0); // for setting the env before eval'ing an expression + + Env* environment(); + Context& context(); + Selector_List* selector(); + Backtrace* backtrace(); + using Operation::operator(); // for evaluating function bodies @@ -59,7 +55,9 @@ namespace Sass { Expression* operator()(Number*); Expression* operator()(Boolean*); Expression* operator()(String_Schema*); + Expression* operator()(String_Quoted*); Expression* operator()(String_Constant*); + // Expression* operator()(Selector_List*); Expression* operator()(Media_Query*); Expression* operator()(Media_Query_Expression*); Expression* operator()(At_Root_Expression*); @@ -69,7 +67,20 @@ namespace Sass { Expression* operator()(Argument*); Expression* operator()(Arguments*); Expression* operator()(Comment*); - Expression* operator()(Parent_Selector* p); + + // these will return selectors + Selector_List* operator()(Selector_List*); + Selector_List* operator()(Complex_Selector*); + Attribute_Selector* operator()(Attribute_Selector*); + // they don't have any specific implementatio (yet) + Type_Selector* operator()(Type_Selector* s) { return s; }; + Pseudo_Selector* operator()(Pseudo_Selector* s) { return s; }; + Wrapped_Selector* operator()(Wrapped_Selector* s) { return s; }; + Selector_Qualifier* operator()(Selector_Qualifier* s) { return s; }; + Selector_Placeholder* operator()(Selector_Placeholder* s) { return s; }; + // actual evaluated selectors + Selector_List* operator()(Selector_Schema*); + Expression* operator()(Parent_Selector*); template Expression* fallback(U x) { return fallback_impl(x); } diff --git a/expand.cpp b/expand.cpp index 6019deed54..b533cce870 100644 --- a/expand.cpp +++ b/expand.cpp @@ -1,10 +1,13 @@ +#ifdef _MSC_VER +#pragma warning(disable : 4503) +#endif + #include #include #include "expand.hpp" #include "bind.hpp" #include "eval.hpp" -#include "contextualize_eval.hpp" #include "to_string.hpp" #include "backtrace.hpp" #include "context.hpp" @@ -12,93 +15,99 @@ namespace Sass { - Expand::Expand(Context& ctx, Eval* eval, Contextualize_Eval* contextualize_eval, Env* env, Backtrace* bt) + Expand::Expand(Context& ctx, Env* env, Backtrace* bt) : ctx(ctx), - eval(eval), - contextualize_eval(contextualize_eval), - env(env), + eval(Eval(*this)), + env_stack(vector()), block_stack(vector()), property_stack(vector()), - selector_stack(vector()), - at_root_selector_stack(vector()), - in_at_root(false), - in_keyframes(false), - backtrace(bt) - { selector_stack.push_back(0); } + selector_stack(vector()), + backtrace_stack(vector()), + in_keyframes(false) + { + env_stack.push_back(0); + env_stack.push_back(env); + block_stack.push_back(0); + property_stack.push_back(0); + selector_stack.push_back(0); + backtrace_stack.push_back(0); + backtrace_stack.push_back(bt); + } + + Context& Expand::context() + { + return ctx; + } + + Env* Expand::environment() + { + if (env_stack.size() > 0) + return env_stack.back(); + return 0; + } + + Selector_List* Expand::selector() + { + if (selector_stack.size() > 0) + return selector_stack.back(); + return 0; + } + Backtrace* Expand::backtrace() + { + if (backtrace_stack.size() > 0) + return backtrace_stack.back(); + return 0; + } + + // blocks create new variable scopes Statement* Expand::operator()(Block* b) { - Env new_env; - new_env.link(*env); - env = &new_env; - Block* bb = new (ctx.mem) Block(b->pstate(), b->length(), b->is_root()); - block_stack.push_back(bb); - append_block(b); - block_stack.pop_back(); - env = env->parent(); + // create new local environment + // set the current env as parent + Env env(environment()); + // copy the block object (add items later) + Block* bb = new (ctx.mem) Block(b->pstate(), + b->length(), + b->is_root()); + // setup block and env stack + this->block_stack.push_back(bb); + this->env_stack.push_back(&env); + // operate on block + this->append_block(b); + // revert block and env stack + this->block_stack.pop_back(); + this->env_stack.pop_back(); + // return copy return bb; } Statement* Expand::operator()(Ruleset* r) { - bool old_in_at_root = in_at_root; - in_at_root = false; + // reset when leaving scope if (in_keyframes) { - To_String to_string; Keyframe_Rule* k = new (ctx.mem) Keyframe_Rule(r->pstate(), r->block()->perform(this)->block()); - if (r->selector()) k->selector(r->selector()->perform(contextualize_eval->with(0, env, backtrace))); - in_at_root = old_in_at_root; - old_in_at_root = false; + if (r->selector()) { + selector_stack.push_back(0); + k->selector(static_cast(r->selector()->perform(&eval))); + selector_stack.pop_back(); + } return k; } - Contextualize_Eval* contextual = contextualize_eval->with(selector_stack.back(), env, backtrace); - // if (old_in_at_root && !r->selector()->has_reference()) - // contextual = contextualize_eval->with(selector_stack.back(), env, backtrace); - - Selector* sel_ctx = r->selector()->perform(contextual); - if (sel_ctx == 0) throw "Cannot expand null selector"; - - Emitter emitter(&ctx); - Inspect isp(emitter); - sel_ctx->perform(&isp); - string str = isp.get_buffer(); - str += ";"; - - Parser p(ctx, r->pstate()); - p.block_stack.push_back(r->selector() ? r->selector()->last_block() : 0); - p.last_media_block = r->selector() ? r->selector()->media_block() : 0; - p.source = str.c_str(); - p.position = str.c_str(); - p.end = str.c_str() + strlen(str.c_str()); - Selector_List* sel_lst = p.parse_selector_list(); - // sel_lst->pstate(isp.remap(sel_lst->pstate())); - - for(size_t i = 0; i < sel_lst->length(); i++) { - - Complex_Selector* pIter = (*sel_lst)[i]; - while (pIter) { - Compound_Selector* pHead = pIter->head(); - // pIter->pstate(isp.remap(pIter->pstate())); - if (pHead) { - // pHead->pstate(isp.remap(pHead->pstate())); - // (*pHead)[0]->pstate(isp.remap((*pHead)[0]->pstate())); - } - pIter = pIter->tail(); - } - } - sel_ctx = sel_lst; + Expression* ex = r->selector()->perform(&eval); + Selector_List* sel = dynamic_cast(ex); + if (sel == 0) throw runtime_error("Expanded null selector"); - selector_stack.push_back(sel_ctx); + selector_stack.push_back(sel); Block* blk = r->block()->perform(this)->block(); Ruleset* rr = new (ctx.mem) Ruleset(r->pstate(), - sel_ctx, + sel, blk); - rr->tabs(r->tabs()); selector_stack.pop_back(); - in_at_root = old_in_at_root; - old_in_at_root = false; + rr->tabs(r->tabs()); + return rr; } @@ -107,28 +116,26 @@ namespace Sass { property_stack.push_back(p->property_fragment()); Block* expanded_block = p->block()->perform(this)->block(); - Block* current_block = block_stack.back(); for (size_t i = 0, L = expanded_block->length(); i < L; ++i) { Statement* stm = (*expanded_block)[i]; - if (typeid(*stm) == typeid(Declaration)) { - Declaration* dec = static_cast(stm); + if (Declaration* dec = static_cast(stm)) { String_Schema* combined_prop = new (ctx.mem) String_Schema(p->pstate()); if (!property_stack.empty()) { *combined_prop << property_stack.back() - << new (ctx.mem) String_Constant(p->pstate(), "-") + << new (ctx.mem) String_Quoted(p->pstate(), "-") << dec->property(); // TODO: eval the prop into a string constant } else { *combined_prop << dec->property(); } dec->property(combined_prop); - *current_block << dec; + *block_stack.back() << dec; } else if (typeid(*stm) == typeid(Comment)) { // drop comments in propsets } else { - error("contents of namespaced properties must result in style declarations only", stm->pstate(), backtrace); + error("contents of namespaced properties must result in style declarations only", stm->pstate(), backtrace()); } } @@ -139,70 +146,67 @@ namespace Sass { Statement* Expand::operator()(Supports_Block* f) { - Expression* queries = f->queries()->perform(eval->with(env, backtrace)); + Expression* queries = f->queries()->perform(&eval); Supports_Block* ff = new (ctx.mem) Supports_Block(f->pstate(), static_cast(queries), f->block()->perform(this)->block()); - ff->selector(selector_stack.back()); + // ff->selector(selector()); return ff; } Statement* Expand::operator()(Media_Block* m) { To_String to_string(&ctx); - Expression* mq = m->media_queries()->perform(eval->with(env, backtrace)); + Expression* mq = m->media_queries()->perform(&eval); mq = Parser::from_c_str(mq->perform(&to_string).c_str(), ctx, mq->pstate()).parse_media_queries(); Media_Block* mm = new (ctx.mem) Media_Block(m->pstate(), static_cast(mq), m->block()->perform(this)->block(), - selector_stack.back()); + 0); mm->tabs(m->tabs()); return mm; } Statement* Expand::operator()(At_Root_Block* a) { - in_at_root = true; - at_root_selector_stack.push_back(0); Block* ab = a->block(); + // if (ab) ab->is_root(true); Expression* ae = a->expression(); - if (ae) ae = ae->perform(eval->with(env, backtrace)); + if (ae) ae = ae->perform(&eval); else ae = new (ctx.mem) At_Root_Expression(a->pstate()); Block* bb = ab ? ab->perform(this)->block() : 0; At_Root_Block* aa = new (ctx.mem) At_Root_Block(a->pstate(), bb, static_cast(ae)); - at_root_selector_stack.pop_back(); - in_at_root = false; + // aa->block()->is_root(true); return aa; } Statement* Expand::operator()(At_Rule* a) { - bool old_in_keyframes = in_keyframes; - in_keyframes = a->is_keyframes(); + LOCAL_FLAG(in_keyframes, a->is_keyframes()); Block* ab = a->block(); Selector* as = a->selector(); Expression* av = a->value(); - if (as) as = as->perform(contextualize_eval->with(0, env, backtrace)); - else if (av) av = av->perform(eval->with(env, backtrace)); + selector_stack.push_back(0); + if (av) av = av->perform(&eval); + if (as) as = dynamic_cast(as->perform(&eval)); + selector_stack.pop_back(); Block* bb = ab ? ab->perform(this)->block() : 0; At_Rule* aa = new (ctx.mem) At_Rule(a->pstate(), a->keyword(), as, - bb); - if (av) aa->value(av); - in_keyframes = old_in_keyframes; + bb, + av); return aa; } Statement* Expand::operator()(Declaration* d) { String* old_p = d->property(); - String* new_p = static_cast(old_p->perform(eval->with(env, backtrace))); - Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back(); - Expression* value = d->value()->perform(eval->with(p, env, backtrace)); - if (value->is_invisible() && !d->is_important()) return 0; + String* new_p = static_cast(old_p->perform(&eval)); + Expression* value = d->value()->perform(&eval); + if (!value || (value->is_invisible() && !d->is_important())) return 0; Declaration* decl = new (ctx.mem) Declaration(d->pstate(), new_p, value, @@ -213,22 +217,22 @@ namespace Sass { Statement* Expand::operator()(Assignment* a) { + Env* env = environment(); string var(a->variable()); - Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back(); if (a->is_global()) { if (a->is_default()) { if (env->has_global(var)) { Expression* e = dynamic_cast(env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { - env->set_global(var, a->value()->perform(eval->with(p, env, backtrace))); + env->set_global(var, a->value()->perform(&eval)); } } else { - env->set_global(var, a->value()->perform(eval->with(p, env, backtrace))); + env->set_global(var, a->value()->perform(&eval)); } } else { - env->set_global(var, a->value()->perform(eval->with(p, env, backtrace))); + env->set_global(var, a->value()->perform(&eval)); } } else if (a->is_default()) { @@ -239,7 +243,7 @@ namespace Sass { if (AST_Node* node = cur->get_local(var)) { Expression* e = dynamic_cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { - cur->set_local(var, a->value()->perform(eval->with(p, env, backtrace))); + cur->set_local(var, a->value()->perform(&eval)); } } else { @@ -255,19 +259,19 @@ namespace Sass { if (AST_Node* node = env->get_global(var)) { Expression* e = dynamic_cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { - env->set_global(var, a->value()->perform(eval->with(p, env, backtrace))); + env->set_global(var, a->value()->perform(&eval)); } } } else if (env->is_lexical()) { - env->set_local(var, a->value()->perform(eval->with(p, env, backtrace))); + env->set_local(var, a->value()->perform(&eval)); } else { - env->set_local(var, a->value()->perform(eval->with(p, env, backtrace))); + env->set_local(var, a->value()->perform(&eval)); } } else { - env->set_lexical(var, a->value()->perform(eval->with(p, env, backtrace))); + env->set_lexical(var, a->value()->perform(&eval)); } return 0; } @@ -277,7 +281,7 @@ namespace Sass { Import* result = new (ctx.mem) Import(imp->pstate()); result->media_queries(imp->media_queries()); for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { - result->urls().push_back(imp->urls()[i]->perform(eval->with(env, backtrace))); + result->urls().push_back(imp->urls()[i]->perform(&eval)); } return result; } @@ -291,33 +295,33 @@ namespace Sass { Statement* Expand::operator()(Warning* w) { // eval handles this too, because warnings may occur in functions - w->perform(eval->with(env, backtrace)); + w->perform(&eval); return 0; } Statement* Expand::operator()(Error* e) { // eval handles this too, because errors may occur in functions - e->perform(eval->with(env, backtrace)); + e->perform(&eval); return 0; } Statement* Expand::operator()(Debug* d) { // eval handles this too, because warnings may occur in functions - d->perform(eval->with(env, backtrace)); + d->perform(&eval); return 0; } Statement* Expand::operator()(Comment* c) { // TODO: eval the text, once we're parsing/storing it as a String_Schema - return new (ctx.mem) Comment(c->pstate(), static_cast(c->text()->perform(eval->with(env, backtrace))), c->is_important()); + return new (ctx.mem) Comment(c->pstate(), static_cast(c->text()->perform(&eval)), c->is_important()); } Statement* Expand::operator()(If* i) { - if (*i->predicate()->perform(eval->with(env, backtrace))) { + if (*i->predicate()->perform(&eval)) { append_block(i->consequent()); } else { @@ -332,13 +336,13 @@ namespace Sass { Statement* Expand::operator()(For* f) { string variable(f->variable()); - Expression* low = f->lower_bound()->perform(eval->with(env, backtrace)); + Expression* low = f->lower_bound()->perform(&eval); if (low->concrete_type() != Expression::NUMBER) { - error("lower bound of `@for` directive must be numeric", low->pstate(), backtrace); + error("lower bound of `@for` directive must be numeric", low->pstate(), backtrace()); } - Expression* high = f->upper_bound()->perform(eval->with(env, backtrace)); + Expression* high = f->upper_bound()->perform(&eval); if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace); + error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace()); } Number* sass_start = static_cast(low); Number* sass_end = static_cast(high); @@ -347,11 +351,12 @@ namespace Sass { stringstream msg; msg << "Incompatible units: '" << sass_start->unit() << "' and '" << sass_end->unit() << "'."; - error(msg.str(), low->pstate(), backtrace); + error(msg.str(), low->pstate(), backtrace()); } double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment + Env* env = environment(); Number* it = new (env->mem) Number(low->pstate(), start, sass_end->unit()); AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0; env->set_local(variable, it); @@ -386,7 +391,7 @@ namespace Sass { Statement* Expand::operator()(Each* e) { vector variables(e->variables()); - Expression* expr = e->list()->perform(eval->with(env, backtrace)); + Expression* expr = e->list()->perform(&eval); List* list = 0; Map* map = 0; if (expr->concrete_type() == Expression::MAP) { @@ -400,6 +405,7 @@ namespace Sass { list = static_cast(expr); } // remember variables and then reset them + Env* env = environment(); vector old_vars(variables.size()); for (size_t i = 0, L = variables.size(); i < L; ++i) { old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0; @@ -409,8 +415,8 @@ namespace Sass { if (map) { for (auto key : map->keys()) { - Expression* k = key->perform(eval->with(env, backtrace)); - Expression* v = map->at(key)->perform(eval->with(env, backtrace)); + Expression* k = key->perform(&eval); + Expression* v = map->at(key)->perform(&eval); if (variables.size() == 1) { List* variable = new (ctx.mem) List(map->pstate(), 2, List::SPACE); @@ -436,7 +442,7 @@ namespace Sass { } for (size_t j = 0, K = variables.size(); j < K; ++j) { if (j < variable->length()) { - env->set_local(variables[j], (*variable)[j]->perform(eval->with(env, backtrace))); + env->set_local(variables[j], (*variable)[j]->perform(&eval)); } else { env->set_local(variables[j], new (ctx.mem) Null(expr->pstate())); @@ -457,7 +463,7 @@ namespace Sass { { Expression* pred = w->predicate(); Block* body = w->block(); - while (*pred->perform(eval->with(env, backtrace))) { + while (*pred->perform(&eval)) { append_block(body); } return 0; @@ -465,44 +471,51 @@ namespace Sass { Statement* Expand::operator()(Return* r) { - error("@return may only be used within a function", r->pstate(), backtrace); + error("@return may only be used within a function", r->pstate(), backtrace()); return 0; } Statement* Expand::operator()(Extension* e) { To_String to_string(&ctx); - Selector_List* extender = static_cast(selector_stack.back()); + Selector_List* extender = static_cast(selector()); if (!extender) return 0; - Contextualize_Eval* eval = contextualize_eval->with(0, env, backtrace); + selector_stack.push_back(0); + // extender->remove_parent_selectors(); + Selector_List* selector_list = static_cast(e->selector()); - Selector_List* contextualized = static_cast(selector_list->perform(eval)); - // ToDo: remove once feature proves stable! - // if (contextualized->length() != 1) { - // error("selector groups may not be extended", extendee->pstate(), backtrace); - // } + Selector_List* contextualized = static_cast(selector_list->perform(&eval)); + // contextualized->remove_parent_selectors(); for (auto complex_sel : contextualized->elements()) { Complex_Selector* c = complex_sel; if (!c->head() || c->tail()) { - error("nested selectors may not be extended", c->pstate(), backtrace); + error("nested selectors may not be extended", c->pstate(), backtrace()); } - Compound_Selector* compound_sel = c->head(); - compound_sel->is_optional(selector_list->is_optional()); - // // need to convert the compound selector into a by-value data structure - // vector target_vec; - // for (size_t i = 0, L = compound_sel->length(); i < L; ++i) - // { target_vec.push_back((*compound_sel)[i]->perform(&to_string)); } + Compound_Selector* placeholder = c->head(); + placeholder->is_optional(selector_list->is_optional()); for (size_t i = 0, L = extender->length(); i < L; ++i) { - // let's test this out - // cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl; - ctx.subset_map.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel)); + Complex_Selector* sel = (*extender)[i]; + if (!(sel->head() && sel->head()->length() > 0 && + dynamic_cast((*sel->head())[0]))) { + Compound_Selector* hh = new (ctx.mem) Compound_Selector((*extender)[i]->pstate()); + Complex_Selector* ssel = new (ctx.mem) Complex_Selector((*extender)[i]->pstate()); + *hh << new (ctx.mem) Parent_Selector((*extender)[i]->pstate()); + ssel->tail(sel); + ssel->head(hh); + sel = ssel; + } + if (c->has_line_feed()) sel->has_line_feed(true); + ctx.subset_map.put(placeholder->to_str_vec(), make_pair(sel, placeholder)); } } + selector_stack.pop_back(); + return 0; } Statement* Expand::operator()(Definition* d) { + Env* env = environment(); Definition* dd = new (ctx.mem) Definition(*d); env->local_frame()[d->name() + (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd; @@ -513,21 +526,21 @@ namespace Sass { Statement* Expand::operator()(Mixin_Call* c) { + Env* env = environment(); string full_name(c->name() + "[m]"); if (!env->has(full_name)) { - error("no mixin named " + c->name(), c->pstate(), backtrace); + error("no mixin named " + c->name(), c->pstate(), backtrace()); } Definition* def = static_cast((*env)[full_name]); Block* body = def->block(); Parameters* params = def->parameters(); - Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back(); Arguments* args = static_cast(c->arguments() - ->perform(eval->with(p, env, backtrace))); - Backtrace here(backtrace, c->pstate(), ", in mixin `" + c->name() + "`"); - backtrace = &here; - Env new_env; - new_env.link(def->environment()); + ->perform(&eval)); + Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`"); + backtrace_stack.push_back(&new_bt); + Env new_env(def->environment()); + env_stack.push_back(&new_env); if (c->block()) { // represent mixin content blocks as thunks/closures Definition* thunk = new (ctx.mem) Definition(c->pstate(), @@ -538,17 +551,16 @@ namespace Sass { thunk->environment(env); new_env.local_frame()["@content[m]"] = thunk; } - bind("mixin " + c->name(), params, args, ctx, &new_env, eval); - Env* old_env = env; - env = &new_env; + bind("mixin " + c->name(), params, args, ctx, &new_env, &eval); append_block(body); - env = old_env; - backtrace = here.parent; + backtrace_stack.pop_back(); + env_stack.pop_back(); return 0; } Statement* Expand::operator()(Content* c) { + Env* env = environment(); // convert @content directives into mixin calls to the underlying thunk if (!env->has("@content[m]")) return 0; Mixin_Call* call = new (ctx.mem) Mixin_Call(c->pstate(), @@ -560,17 +572,19 @@ namespace Sass { // produce an error if something is not implemented inline Statement* Expand::fallback_impl(AST_Node* n) { - error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace); - String_Constant* msg = new (ctx.mem) String_Constant(ParserState("[WARN]"), string("`Expand` doesn't handle ") + typeid(*n).name()); + string err = string("`Expand` doesn't handle ") + typeid(*n).name(); + String_Quoted* msg = new (ctx.mem) String_Quoted(ParserState("[WARN]"), err); + error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace()); return new (ctx.mem) Warning(ParserState("[WARN]"), msg); } + // process and add to last block on stack inline void Expand::append_block(Block* b) { - Block* current_block = block_stack.back(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* ith = (*b)[i]->perform(this); - if (ith) *current_block << ith; + if (ith) *block_stack.back() << ith; } } + } diff --git a/expand.hpp b/expand.hpp index 32290eedf5..5d5c5eded7 100644 --- a/expand.hpp +++ b/expand.hpp @@ -9,35 +9,39 @@ #include "eval.hpp" #include "operation.hpp" #include "environment.hpp" -#include "contextualize.hpp" namespace Sass { using namespace std; + class Listize; class Context; class Eval; - class Contextualize_Eval; typedef Environment Env; struct Backtrace; class Expand : public Operation_CRTP { + public: + + Env* environment(); + Context& context(); + Selector_List* selector(); + Backtrace* backtrace(); Context& ctx; - Eval* eval; - Contextualize_Eval* contextualize_eval; - Env* env; + Eval eval; + + // it's easier to work with vectors + vector env_stack; vector block_stack; vector property_stack; - vector selector_stack; - vector at_root_selector_stack; - bool in_at_root; + vector selector_stack; + vectorbacktrace_stack; bool in_keyframes; - Backtrace* backtrace; Statement* fallback_impl(AST_Node* n); public: - Expand(Context&, Eval*, Contextualize_Eval*, Env*, Backtrace*); + Expand(Context&, Env*, Backtrace*); virtual ~Expand() { } using Operation::operator(); diff --git a/extend.cpp b/extend.cpp index 48e163b136..e0213ea2fc 100644 --- a/extend.cpp +++ b/extend.cpp @@ -1,6 +1,9 @@ +#ifdef _MSC_VER +#pragma warning(disable : 4503) +#endif + #include "extend.hpp" #include "context.hpp" -#include "contextualize.hpp" #include "to_string.hpp" #include "backtrace.hpp" #include "paths.hpp" @@ -11,6 +14,7 @@ #include #include + /* NOTES: @@ -45,7 +49,7 @@ - wrap the contents of the print functions in DEBUG preprocesser conditionals so they will be optimized away in non-debug mode. - - consider making the extend* functions member functions to avoid passing around ctx and subsetMap map around. This has the + - consider making the extend* functions member functions to avoid passing around ctx and subset_map map around. This has the drawback that the implementation details of the operator are then exposed to the outside world, which is not ideal and can cause additional compile time dependencies. @@ -63,8 +67,6 @@ namespace Sass { typedef pair ExtensionPair; typedef vector SubsetMapEntries; - - #ifdef DEBUG // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp @@ -503,6 +505,7 @@ namespace Sass { DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex) Node tempResult = Node::createCollection(); + tempResult.got_line_feed = seqs1.got_line_feed; for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) { Node& seq1 = *seqs1Iter; @@ -1457,7 +1460,7 @@ namespace Sass { static Node extendComplexSelector( Complex_Selector* pComplexSelector, Context& ctx, - ExtensionSubsetMap& subsetMap, + ExtensionSubsetMap& subset_map, set seen); @@ -1485,17 +1488,17 @@ namespace Sass { static Node extendCompoundSelector( Compound_Selector* pSelector, Context& ctx, - ExtensionSubsetMap& subsetMap, + ExtensionSubsetMap& subset_map, set seen) { DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: ")) Node extendedSelectors = Node::createCollection(); + // extendedSelectors.got_line_feed = true; To_String to_string; - SubsetMapEntries entries = subsetMap.get_v(pSelector->to_str_vec()); - + SubsetMapEntries entries = subset_map.get_v(pSelector->to_str_vec()); typedef vector > > GroupedByToAResult; @@ -1503,7 +1506,6 @@ namespace Sass { GroupedByToAResult arr; group_by_to_a(entries, extPairKeyFunctor, arr); - typedef pair SelsNewSeqPair; typedef vector SelsNewSeqPairCollection; @@ -1517,7 +1519,7 @@ namespace Sass { Complex_Selector& seq = groupedPair.first; vector& group = groupedPair.second; -// DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) Compound_Selector* pSels = new (ctx.mem) Compound_Selector(pSelector->pstate()); @@ -1530,25 +1532,18 @@ namespace Sass { } } - - -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: ")) - + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: ")) Complex_Selector* pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge) Compound_Selector* pExtCompoundSelector = pSels; // All the simple selectors to be replaced from the current compound selector from all extensions - - // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL? Compound_Selector* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx); + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) - - - Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->base(); + Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->last()->head(); Compound_Selector* pUnifiedSelector = NULL; if (!pInnermostCompoundSelector) { @@ -1557,28 +1552,26 @@ namespace Sass { pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx); -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: ")) + + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: ")) + DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: ")) if (!pUnifiedSelector || pUnifiedSelector->length() == 0) { continue; } - - // TODO: implement the parent directive match (if necessary based on test failures) // next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none? - - - // TODO: This seems a little fishy to me. See if it causes any problems. From the ruby, we should be able to just // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered // out and aren't operated on. - Complex_Selector* pNewSelector = pExtComplexSelector->cloneFully(ctx); + Complex_Selector* pNewSelector = pExtComplexSelector->cloneFully(ctx); // ->first(); + Complex_Selector* pNewInnerMost = new (ctx.mem) Complex_Selector(pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL); + Complex_Selector::Combinator combinator = pNewSelector->clear_innermost(); pNewSelector->set_innermost(pNewInnerMost, combinator); @@ -1595,7 +1588,7 @@ namespace Sass { #endif - if (pSelector && pSelector->has_line_feed()) pNewSelector->has_line_feed(true); + // if (pSelector && pSelector->has_line_feed()) pNewInnerMost->has_line_feed(true); // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending. DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector, ctx)) @@ -1613,6 +1606,7 @@ namespace Sass { DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: ")) + if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);; holder.push_back(make_pair(pSels, pNewSelector)); } @@ -1635,8 +1629,7 @@ namespace Sass { DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx)) - - Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subsetMap, recurseSeen); + Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen); DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors) @@ -1663,7 +1656,7 @@ namespace Sass { static bool complexSelectorHasExtension( Complex_Selector* pComplexSelector, Context& ctx, - ExtensionSubsetMap& subsetMap) { + ExtensionSubsetMap& subset_map) { bool hasExtension = false; @@ -1673,7 +1666,7 @@ namespace Sass { Compound_Selector* pHead = pIter->head(); if (pHead) { - SubsetMapEntries entries = subsetMap.get_v(pHead->to_str_vec()); + SubsetMapEntries entries = subset_map.get_v(pHead->to_str_vec()); for (ExtensionPair ext : entries) { // check if both selectors have the same media block parent if (ext.first->media_block() == pComplexSelector->media_block()) continue; @@ -1712,7 +1705,7 @@ namespace Sass { string cwd(Sass::File::get_cwd()); string sel1(pComplexSelector->perform(&to_string)); Compound_Selector* pExtendSelector = 0; - for (auto i : subsetMap.values()) { + for (auto i : subset_map.values()) { if (i.first == pComplexSelector) { pExtendSelector = i.second; break; @@ -1746,11 +1739,9 @@ namespace Sass { static Node extendComplexSelector( Complex_Selector* pComplexSelector, Context& ctx, - ExtensionSubsetMap& subsetMap, + ExtensionSubsetMap& subset_map, set seen) { - pComplexSelector->tail()->has_line_feed(pComplexSelector->has_line_feed()); - Node complexSelector = complexSelectorToNode(pComplexSelector, ctx); DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) @@ -1775,8 +1766,8 @@ namespace Sass { Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head(); - Node extended = extendCompoundSelector(pCompoundSelector, ctx, subsetMap, seen); - + Node extended = extendCompoundSelector(pCompoundSelector, ctx, subset_map, seen); + if (sseqOrOp.got_line_feed) extended.got_line_feed = true; DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended) @@ -1796,6 +1787,7 @@ namespace Sass { } if (!isSuperselector) { + if (sseqOrOp.got_line_feed) pJustCurrentCompoundSelector->has_line_feed(sseqOrOp.got_line_feed); extended.collection()->push_front(complexSelectorToNode(pJustCurrentCompoundSelector, ctx)); } @@ -1823,6 +1815,7 @@ namespace Sass { for (NodeDeque::iterator pathsIter = paths.collection()->begin(), pathsEndIter = paths.collection()->end(); pathsIter != pathsEndIter; ++pathsIter) { Node& path = *pathsIter; Node weaved = weave(path, ctx); + weaved.got_line_feed = path.got_line_feed; weaves.collection()->push_back(weaved); } @@ -1853,7 +1846,7 @@ namespace Sass { /* This is the equivalent of ruby's CommaSequence.do_extend. */ - Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething) { + Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) { To_String to_string(&ctx); @@ -1869,7 +1862,7 @@ namespace Sass { // run through the extend code (which does a data model transformation), check if there is anything to extend before doing // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps // when debugging). - if (!complexSelectorHasExtension(pSelector, ctx, subsetMap)) { + if (!complexSelectorHasExtension(pSelector, ctx, subset_map)) { *pNewSelectors << pSelector; continue; } @@ -1877,8 +1870,8 @@ namespace Sass { extendedSomething = true; set seen; - Node extendedSelectors = extendComplexSelector(pSelector, ctx, subsetMap, seen); + Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen); if (!pSelector->has_placeholder()) { if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) { *pNewSelectors << pSelector; @@ -1888,7 +1881,7 @@ namespace Sass { for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { // When it is a replace, skip the first one, unless there is only one if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue; - + Node& childNode = *iterator; *pNewSelectors << nodeToComplexSelector(childNode, ctx); } @@ -1932,7 +1925,7 @@ namespace Sass { // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change. template - static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subsetMap) { + static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subset_map) { To_String to_string(&ctx); DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast(pObject->selector())->perform(&to_string)) @@ -1945,23 +1938,12 @@ namespace Sass { } bool extendedSomething = false; - Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, false, extendedSomething); + Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subset_map, false, extendedSomething); if (extendedSomething && pNewSelectorList) { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->perform(&to_string)) DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->perform(&to_string)) - - // re-parse in order to restructure expanded placeholder nodes correctly. - // - // TODO: I don't know if this is needed, but it was in the original C++ implementation, so I kept it. Try running the tests without re-parsing. - // this probably messes up source-maps - pObject->selector( - Parser::from_c_str( - (pNewSelectorList->perform(&to_string) + ";").c_str(), - ctx, - pNewSelectorList->pstate() - ).parse_selector_list() - ); + pObject->selector(pNewSelectorList); } else { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") } @@ -1983,30 +1965,24 @@ namespace Sass { void Extend::operator()(Ruleset* pRuleset) { extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map); - pRuleset->block()->perform(this); } void Extend::operator()(Supports_Block* pFeatureBlock) { - if (pFeatureBlock->selector()) { - extendObjectWithSelectorAndBlock(pFeatureBlock, ctx, subset_map); - } - pFeatureBlock->block()->perform(this); } void Extend::operator()(Media_Block* pMediaBlock) { - if (pMediaBlock->selector()) { - extendObjectWithSelectorAndBlock(pMediaBlock, ctx, subset_map); - } - pMediaBlock->block()->perform(this); } void Extend::operator()(At_Rule* a) { + // Selector_List* ls = dynamic_cast(a->selector()); + // selector_stack.push_back(ls); if (a->block()) a->block()->perform(this); + // exp.selector_stack.pop_back(); } } diff --git a/extend.hpp b/extend.hpp index 1f5a5bb4fd..03df5e4a96 100644 --- a/extend.hpp +++ b/extend.hpp @@ -27,7 +27,7 @@ namespace Sass { public: static Node subweave(Node& one, Node& two, Context& ctx); - static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething); + static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething); Extend(Context&, ExtensionSubsetMap&); virtual ~Extend() { } diff --git a/functions.cpp b/functions.cpp index 511600b9ce..b0380211b5 100644 --- a/functions.cpp +++ b/functions.cpp @@ -9,6 +9,7 @@ #include "extend.hpp" #include "eval.hpp" #include "util.hpp" +#include "expand.hpp" #include "utf8_string.hpp" #include "utf8.h" @@ -72,6 +73,26 @@ namespace Sass { namespace Functions { + inline void handle_utf8_error (const ParserState& pstate, Backtrace* backtrace) + { + try { + throw; + } + catch (utf8::invalid_code_point) { + string msg("utf8::invalid_code_point"); + error(msg, pstate, backtrace); + } + catch (utf8::not_enough_room) { + string msg("utf8::not_enough_room"); + error(msg, pstate, backtrace); + } + catch (utf8::invalid_utf8) { + string msg("utf8::invalid_utf8"); + error(msg, pstate, backtrace); + } + catch (...) { throw; } + } + template T* get_arg(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace) { @@ -451,7 +472,7 @@ namespace Sass { Number* amount = dynamic_cast(env["$amount"]); if (!amount) { To_String to_string(&ctx); - return new (ctx.mem) String_Constant(pstate, "saturate(" + env["$color"]->perform(&to_string) + ")"); + return new (ctx.mem) String_Quoted(pstate, "saturate(" + env["$color"]->perform(&to_string) + ")"); } ARGR("$amount", Number, 0, 100); @@ -512,7 +533,7 @@ namespace Sass { Number* amount = dynamic_cast(env["$color"]); if (amount) { To_String to_string(&ctx); - return new (ctx.mem) String_Constant(pstate, "grayscale(" + amount->perform(&to_string) + ")"); + return new (ctx.mem) String_Quoted(pstate, "grayscale(" + amount->perform(&to_string) + ")"); } Color* rgb_color = ARG("$color", Color); @@ -549,7 +570,7 @@ namespace Sass { Number* amount = dynamic_cast(env["$color"]); if (amount) { To_String to_string(&ctx); - return new (ctx.mem) String_Constant(pstate, "invert(" + amount->perform(&to_string) + ")"); + return new (ctx.mem) String_Quoted(pstate, "invert(" + amount->perform(&to_string) + ")"); } Color* rgb_color = ARG("$color", Color); @@ -569,14 +590,14 @@ namespace Sass { { String_Constant* ie_kwd = dynamic_cast(env["$color"]); if (ie_kwd) { - return new (ctx.mem) String_Constant(pstate, "alpha(" + ie_kwd->value() + ")"); + return new (ctx.mem) String_Quoted(pstate, "alpha(" + ie_kwd->value() + ")"); } // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$color"]); if (amount) { To_String to_string(&ctx); - return new (ctx.mem) String_Constant(pstate, "opacity(" + amount->perform(&to_string) + ")"); + return new (ctx.mem) String_Quoted(pstate, "opacity(" + amount->perform(&to_string) + ")"); } return new (ctx.mem) Number(pstate, ARG("$color", Color)->a()); @@ -787,7 +808,7 @@ namespace Sass { for (size_t i = 0, L = result.length(); i < L; ++i) { result[i] = std::toupper(result[i]); } - return new (ctx.mem) String_Constant(pstate, result); + return new (ctx.mem) String_Quoted(pstate, result); } /////////////////// @@ -802,7 +823,7 @@ namespace Sass { return new (ctx.mem) Null(pstate); } else if (String_Quoted* string_quoted = dynamic_cast(arg)) { - String_Constant* result = new (ctx.mem) String_Constant(pstate, string_quoted->value()); + String_Quoted* result = new (ctx.mem) String_Quoted(pstate, string_quoted->value()); // remember if the string was quoted (color tokens) result->sass_fix_1291(string_quoted->quote_mark() != 0); return result; @@ -824,7 +845,7 @@ namespace Sass { To_String to_string(&ctx); AST_Node* arg = env["$string"]; string str(quote(arg->perform(&to_string), String_Constant::double_quote())); - String_Constant* result = new (ctx.mem) String_Constant(pstate, str); + String_Quoted* result = new (ctx.mem) String_Quoted(pstate, str); result->is_delayed(true); return result; } @@ -839,19 +860,9 @@ namespace Sass { len = UTF_8::code_point_count(s->value(), 0, s->value().size()); } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, pstate, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, pstate, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, pstate, backtrace); - } - catch (...) { throw; } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, backtrace); } // return something even if we had an error (-1) return new (ctx.mem) Number(pstate, len); } @@ -896,20 +907,10 @@ namespace Sass { if (ss->quote_mark()) str = quote(str); } } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, pstate, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, pstate, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, pstate, backtrace); - } - catch (...) { throw; } - return new (ctx.mem) String_Constant(pstate, str); + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, backtrace); } + return new (ctx.mem) String_Quoted(pstate, str); } Signature str_index_sig = "str-index($string, $substring)"; @@ -930,19 +931,9 @@ namespace Sass { } index = UTF_8::code_point_count(str, 0, c_index) + 1; } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, pstate, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, pstate, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, pstate, backtrace); - } - catch (...) { throw; } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, backtrace); } // return something even if we had an error (-1) return new (ctx.mem) Number(pstate, index); } @@ -977,19 +968,9 @@ namespace Sass { if(ss->quote_mark()) newstr = quote(newstr); } } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, pstate, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, pstate, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, pstate, backtrace); - } - catch (...) { throw; } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, backtrace); } return new (ctx.mem) String_Quoted(pstate, newstr); } @@ -1010,7 +991,7 @@ namespace Sass { cpy->value(str); return cpy; } else { - return new (ctx.mem) String_Constant(pstate, str); + return new (ctx.mem) String_Quoted(pstate, str); } } @@ -1031,7 +1012,7 @@ namespace Sass { cpy->value(str); return cpy; } else { - return new (ctx.mem) String_Constant(pstate, str); + return new (ctx.mem) String_Quoted(pstate, str); } } @@ -1147,6 +1128,15 @@ namespace Sass { return new (ctx.mem) Number(pstate, map ? map->length() : 1); } + if (v->concrete_type() == Expression::SELECTOR) { + if (Compound_Selector* h = dynamic_cast(v)) { + return new (ctx.mem) Number(pstate, h->length()); + } else if (Selector_List* ls = dynamic_cast(v)) { + return new (ctx.mem) Number(pstate, ls->length()); + } else { + return new (ctx.mem) Number(pstate, 1); + } + } List* list = dynamic_cast(env["$list"]); return new (ctx.mem) Number(pstate, @@ -1313,7 +1303,7 @@ namespace Sass { l = new (ctx.mem) List(pstate, 1); *l << ARG("$list", Expression); } - return new (ctx.mem) String_Constant(pstate, + return new (ctx.mem) String_Quoted(pstate, l->separator() == List::COMMA ? "comma" : "space"); } @@ -1402,7 +1392,7 @@ namespace Sass { for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) { string name = string(((Argument*)(*arglist)[i])->name()); name = name.erase(0, 1); // sanitize name (remove dollar sign) - *result << make_pair(new (ctx.mem) String_Constant(pstate, name), + *result << make_pair(new (ctx.mem) String_Quoted(pstate, name), ((Argument*)(*arglist)[i])->value()); } return result; @@ -1420,7 +1410,7 @@ namespace Sass { To_String to_string(&ctx); string str(v->perform(&to_string)); } - return new (ctx.mem) String_Constant(pstate, ARG("$value", Expression)->type()); + return new (ctx.mem) String_Quoted(pstate, ARG("$value", Expression)->type()); } Signature unit_sig = "unit($number)"; @@ -1530,10 +1520,8 @@ namespace Sass { } } Function_Call* func = new (ctx.mem) Function_Call(pstate, name, args); - Contextualize contextualize(ctx, &d_env, backtrace); - Listize listize(ctx); - Eval eval(ctx, &contextualize, &listize, &d_env, backtrace); - return func->perform(&eval); + Expand expand(ctx, &d_env, backtrace); + return func->perform(&expand.eval); } @@ -1550,15 +1538,13 @@ namespace Sass { // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); } BUILT_IN(sass_if) { - Contextualize contextualize(ctx, &d_env, backtrace); - Listize listize(ctx); - Eval eval(ctx, &contextualize, &listize, &d_env, backtrace); - bool is_true = !ARG("$condition", Expression)->perform(&eval)->is_false(); + Expand expand(ctx, &d_env, backtrace); + bool is_true = !ARG("$condition", Expression)->perform(&expand.eval)->is_false(); if (is_true) { - return ARG("$if-true", Expression)->perform(&eval); + return ARG("$if-true", Expression)->perform(&expand.eval); } else { - return ARG("$if-false", Expression)->perform(&eval); + return ARG("$if-false", Expression)->perform(&expand.eval); } } @@ -1582,9 +1568,9 @@ namespace Sass { { Expression* v = ARG("$value", Expression); if (v->concrete_type() == Expression::NULL_VAL) { - return new (ctx.mem) String_Constant(pstate, "null"); + return new (ctx.mem) String_Quoted(pstate, "null"); } else if (v->concrete_type() == Expression::BOOLEAN && *v == 0) { - return new (ctx.mem) String_Constant(pstate, "false"); + return new (ctx.mem) String_Quoted(pstate, "false"); } else if (v->concrete_type() == Expression::STRING) { return v; } else { @@ -1597,7 +1583,7 @@ namespace Sass { string inspect = v->perform(&to_string); if (inspect.empty() && parentheses) inspect = "()"; ctx.output_style = old_style; - return new (ctx.mem) String_Constant(pstate, inspect); + return new (ctx.mem) String_Quoted(pstate, inspect); } @@ -1646,9 +1632,7 @@ namespace Sass { for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { Complex_Selector* parent = (*result)[i]->cloneFully(ctx); - Complex_Selector* selector = (*child)[j]; - parent->innermost()->tail(selector); - exploded.push_back(parent); + exploded.push_back((*child)[j]->parentize(parent, ctx)); } } @@ -1768,7 +1752,7 @@ namespace Sass { Simple_Selector* ss = (*sel)[i]; string ss_string = ss->perform(&to_string) ; - *l << new (ctx.mem) String_Constant(ss->pstate(), ss_string); + *l << new (ctx.mem) String_Quoted(ss->pstate(), ss_string); } return l; @@ -1843,7 +1827,7 @@ namespace Sass { uniform_real_distribution<> distributor(0, 4294967296); // 16^8 uint_fast32_t distributed = static_cast(distributor(rand)); ss << "u" << setfill('0') << setw(8) << std::hex << distributed; - return new (ctx.mem) String_Constant(pstate, ss.str()); + return new (ctx.mem) String_Quoted(pstate, ss.str()); } } diff --git a/inspect.cpp b/inspect.cpp index 203b57fd71..c48c265934 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -8,6 +8,7 @@ #include "ast.hpp" #include "inspect.hpp" #include "context.hpp" +#include "listize.hpp" #include "utf8/checked.h" namespace Sass { @@ -105,6 +106,10 @@ namespace Sass { at_rule->selector()->perform(this); in_wrapped = was_wrapped; } + if (at_rule->value()) { + append_mandatory_space(); + at_rule->value()->perform(this); + } if (at_rule->block()) { at_rule->block()->perform(this); } @@ -123,7 +128,14 @@ namespace Sass { append_indentation(); dec->property()->perform(this); append_colon_separator(); - dec->value()->perform(this); + + if (dec->value()->concrete_type() == Expression::SELECTOR) { + Listize listize(*ctx); + dec->value()->perform(&listize)->perform(this); + } else { + dec->value()->perform(this); + } + if (dec->is_important()) { append_optional_space(); append_string("!important"); @@ -746,17 +758,6 @@ namespace Sass { append_token("null", n); } - void Inspect::operator()(Parent_Selector* p) - { - if (p->selector()) { - p->selector()->perform(this); - append_delimiter(); - } - else { - append_string("&"); - } - } - // parameters and arguments void Inspect::operator()(Parameter* p) { @@ -821,10 +822,9 @@ namespace Sass { s->contents()->perform(this); } - void Inspect::operator()(Selector_Reference* ref) + void Inspect::operator()(Parent_Selector* p) { - if (ref->selector()) ref->selector()->perform(this); - else append_string("&"); + append_string("&"); } void Inspect::operator()(Selector_Placeholder* s) @@ -866,6 +866,7 @@ namespace Sass { { append_token(s->name(), s); if (s->expression()) { + append_string("("); s->expression()->perform(this); append_string(")"); } @@ -876,6 +877,7 @@ namespace Sass { bool was = in_wrapped; in_wrapped = true; append_token(s->name(), s); + append_string("("); s->selector()->perform(this); append_string(")"); in_wrapped = was; @@ -887,7 +889,9 @@ namespace Sass { (*s)[i]->perform(this); } if (s->has_line_break()) { - append_optional_linefeed(); + if (output_style() != COMPACT) { + append_optional_linefeed(); + } } } @@ -896,6 +900,14 @@ namespace Sass { Compound_Selector* head = c->head(); Complex_Selector* tail = c->tail(); Complex_Selector::Combinator comb = c->combinator(); + + if (c->has_line_feed()) { + if (!(c->has_parent_ref())) { + append_optional_linefeed(); + append_indentation(); + } + } + if (head && !head->is_empty_reference()) head->perform(this); bool is_empty = head && head->is_empty_reference(); bool is_tail = head && !head->is_empty_reference() && tail; @@ -927,6 +939,11 @@ namespace Sass { if (c->has_line_break()) append_optional_linefeed(); } if (tail) tail->perform(this); + if (!tail && c->has_line_break()) { + if (output_style() == COMPACT) { + append_mandatory_space(); + } + } } void Inspect::operator()(Selector_List* g) @@ -934,13 +951,10 @@ namespace Sass { if (g->empty()) return; for (size_t i = 0, L = g->length(); i < L; ++i) { if (!in_wrapped && i == 0) append_indentation(); + if ((*g)[i] == 0) continue; (*g)[i]->perform(this); if (i < L - 1) { append_comma_separator(); - if ((*g)[i]->has_line_feed()) { - append_optional_linefeed(); - append_indentation(); - } } } } diff --git a/inspect.hpp b/inspect.hpp index fccd3ed49d..9dc5c894ad 100644 --- a/inspect.hpp +++ b/inspect.hpp @@ -79,7 +79,6 @@ namespace Sass { virtual void operator()(Arguments*); // selectors virtual void operator()(Selector_Schema*); - virtual void operator()(Selector_Reference*); virtual void operator()(Selector_Placeholder*); virtual void operator()(Type_Selector*); virtual void operator()(Selector_Qualifier*); diff --git a/lexer.hpp b/lexer.hpp index 0bf7081848..8566a829ea 100644 --- a/lexer.hpp +++ b/lexer.hpp @@ -67,7 +67,6 @@ namespace Sass { const char* end_of_line(const char* src); // Assert end_of_file boundary (/\z/) - // This is a zero-width positive lookahead const char* end_of_file(const char* src); // const char* start_of_string(const char* src); @@ -151,28 +150,11 @@ namespace Sass { if ((rslt = mx(src))) return rslt; return 0; } - template + template const char* alternatives(const char* src) { const char* rslt; if ((rslt = mx1(src))) return rslt; - if ((rslt = mx2(src))) return rslt; - return 0; - } - template - const char* alternatives(const char* src) { - const char* rslt; - if ((rslt = mx1(src))) return rslt; - if ((rslt = mx2(src))) return rslt; - if ((rslt = mx3(src))) return rslt; - return 0; - } - template - const char* alternatives(const char* src) { - const char* rslt; - if ((rslt = mx1(src))) return rslt; - if ((rslt = mx2(src))) return rslt; - if ((rslt = mx3(src))) return rslt; - return alternatives(src); + return alternatives(src); } // Tries supplied matchers in order. @@ -184,28 +166,11 @@ namespace Sass { if (!(rslt = mx1(rslt))) return 0; return rslt; } - template - const char* sequence(const char* src) { - const char* rslt = src; - if (!(rslt = mx1(rslt))) return 0; - if (!(rslt = mx2(rslt))) return 0; - return rslt; - } - template - const char* sequence(const char* src) { - const char* rslt = src; - if (!(rslt = mx1(rslt))) return 0; - if (!(rslt = mx2(rslt))) return 0; - if (!(rslt = mx3(rslt))) return 0; - return rslt; - } - template + template const char* sequence(const char* src) { const char* rslt = src; if (!(rslt = mx1(rslt))) return 0; - if (!(rslt = mx2(rslt))) return 0; - if (!(rslt = mx3(rslt))) return 0; - return sequence(rslt); + return sequence(rslt); } diff --git a/listize.cpp b/listize.cpp index 3457d59e63..7967c09f2c 100644 --- a/listize.cpp +++ b/listize.cpp @@ -17,7 +17,7 @@ namespace Sass { { List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) { - // if (!(*sel)[i]) continue; + if (!(*sel)[i]) continue; *l << (*sel)[i]->perform(this); } return l; @@ -31,7 +31,7 @@ namespace Sass { Expression* e = (*sel)[i]->perform(this); if (e) str += e->perform(&to_string); } - return new (ctx.mem) String_Constant(sel->pstate(), str); + return new (ctx.mem) String_Quoted(sel->pstate(), str); } Expression* Listize::operator()(Complex_Selector* sel) @@ -48,13 +48,13 @@ namespace Sass { switch(sel->combinator()) { case Complex_Selector::PARENT_OF: - *l << new (ctx.mem) String_Constant(sel->pstate(), ">"); + *l << new (ctx.mem) String_Quoted(sel->pstate(), ">"); break; case Complex_Selector::ADJACENT_TO: - *l << new (ctx.mem) String_Constant(sel->pstate(), "+"); + *l << new (ctx.mem) String_Quoted(sel->pstate(), "+"); break; case Complex_Selector::PRECEDES: - *l << new (ctx.mem) String_Constant(sel->pstate(), "~"); + *l << new (ctx.mem) String_Quoted(sel->pstate(), "~"); break; case Complex_Selector::ANCESTOR_OF: break; @@ -72,7 +72,7 @@ namespace Sass { return l; } - Expression* Listize::operator()(Selector_Reference* sel) + Expression* Listize::operator()(Parent_Selector* sel) { return 0; } diff --git a/listize.hpp b/listize.hpp index 68f060c356..a77ce675d5 100644 --- a/listize.hpp +++ b/listize.hpp @@ -30,7 +30,7 @@ namespace Sass { Expression* operator()(Selector_List*); Expression* operator()(Complex_Selector*); Expression* operator()(Compound_Selector*); - Expression* operator()(Selector_Reference*); + Expression* operator()(Parent_Selector*); template Expression* fallback(U x) { return fallback_impl(x); } diff --git a/node.cpp b/node.cpp index c963f4cbb7..d16d02abae 100644 --- a/node.cpp +++ b/node.cpp @@ -19,7 +19,9 @@ namespace Sass { pStripped->tail(NULL); pStripped->combinator(Complex_Selector::ANCESTOR_OF); - return Node(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/); + Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/); + if (pSelector) n.got_line_feed = pSelector->has_line_feed(); + return n; } @@ -43,7 +45,7 @@ namespace Sass { 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(); */ } + { if (pSelector) got_line_feed = pSelector->has_line_feed(); } Node Node::clone(Context& ctx) const { @@ -55,7 +57,9 @@ namespace Sass { } } - return Node(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection); + Node n(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection); + n.got_line_feed = got_line_feed; + return n; } @@ -151,14 +155,14 @@ namespace Sass { } else if (node.isCollection()) { - os << "["; + 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 << (*iter); } os << "]"; @@ -175,18 +179,39 @@ namespace Sass { 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* 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(); + + if (pToConvert->head() == NULL || empty_parent_ref) { + } + // the first Complex_Selector may contain a dummy head pointer, skip it. - if (pToConvert->head() != NULL && !pToConvert->head()->is_empty_reference()) { + if (pToConvert->head() != NULL && !empty_parent_ref) { node.collection()->push_back(Node::createSelector(pToConvert, ctx)); + if (has_lf) node.collection()->back().got_line_feed = has_lf; + 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(); @@ -212,23 +237,30 @@ namespace Sass { string noPath(""); Position noPosition(-1, -1, -1); Complex_Selector* pFirst = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL); + Complex_Selector* 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()) { pCurrent->tail(child.selector()->clone(ctx)); // 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() + 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(new (ctx.mem) 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(); } } @@ -239,23 +271,21 @@ namespace Sass { // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass Compound_Selector* fakeHead = new (ctx.mem) Compound_Selector(ParserState("[NODE]"), 1); - Selector_Reference* selectorRef = new (ctx.mem) Selector_Reference(ParserState("[NODE]"), NULL); + Parent_Selector* selectorRef = new (ctx.mem) 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); - pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed); - return 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, Context& ctx) { - + + SourcesSet sel_set; Node result = Node::createCollection(); - - To_String to_string; - std::set< Complex_Selector*, Complex_Selector_Pointer_Compare > sel_set; - + // Add all selectors we don't already have, everything else just add it blindly for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) { Node& seqs1 = *seqsesIter; @@ -272,5 +302,4 @@ namespace Sass { return result; } - } diff --git a/node.hpp b/node.hpp index faaa95eea7..13cb22ef57 100644 --- a/node.hpp +++ b/node.hpp @@ -45,7 +45,7 @@ namespace Sass { NIL }; - TYPE type() const { return mType; } + TYPE type() const { return mType; } bool isCombinator() const { return mType == COMBINATOR; } bool isSelector() const { return mType == SELECTOR; } bool isCollection() const { return mType == COLLECTION; } diff --git a/output.cpp b/output.cpp index 691f4601d1..1a049d7d8f 100644 --- a/output.cpp +++ b/output.cpp @@ -126,8 +126,8 @@ namespace Sass { if (dec->value()->concrete_type() == Expression::STRING) { String_Constant* valConst = static_cast(dec->value()); string val(valConst->value()); - if (dynamic_cast(valConst)) { - if (!valConst->quote_mark() && val.empty()) { + if (auto qstr = dynamic_cast(valConst)) { + if (!qstr->quote_mark() && val.empty()) { bPrintExpression = false; } } @@ -282,35 +282,9 @@ namespace Sass { in_media_block = false; append_scope_opener(); - Selector* e = m->selector(); - if (e && b->has_non_hoistable()) { - // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable - e->perform(this); - append_scope_opener(); - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - stm->perform(this); - } - } - - append_scope_closer(); - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (stm->is_hoistable()) { - stm->perform(this); - } - } - } - else { - // JMA - not hoisted, just output in order - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - stm->perform(this); - if (i < L - 1) append_special_linefeed(); - } + for (size_t i = 0, L = b->length(); i < L; ++i) { + if ((*b)[i]) (*b)[i]->perform(this); + if (i < L - 1) append_special_linefeed(); } if (output_style() == NESTED) indentation -= m->tabs(); @@ -332,7 +306,7 @@ namespace Sass { s->perform(this); in_wrapped = false; } - else if (v) { + if (v) { append_mandatory_space(); v->perform(this); } diff --git a/parser.cpp b/parser.cpp index 2d6dcf737b..1c6531a885 100644 --- a/parser.cpp +++ b/parser.cpp @@ -9,6 +9,7 @@ #include "util.hpp" #include "prelexer.hpp" #include "sass_functions.h" +#include "error_handling.hpp" #include #include @@ -66,11 +67,10 @@ namespace Sass { return p; } + /* main entry point to parse root block */ Block* Parser::parse() { - Block* root = new (ctx.mem) Block(pstate); - block_stack.push_back(root); - root->is_root(true); + Block* root = new (ctx.mem) Block(pstate, 0, true); read_bom(); if (ctx.queue.size() == 1) { @@ -86,112 +86,203 @@ namespace Sass { } } - bool semicolon = false; - string error_message; - lex< optional_spaces >(); - Lookahead lookahead_result; + block_stack.push_back(root); + /* bool rv = */ parse_block_nodes(); + block_stack.pop_back(); + + // update for end position + root->update_pstate(pstate); + + if (position != end) { + css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); + } + + return root; + } + + + // convenience function for block parsing + // will create a new block ad-hoc for you + // this is the base block parsing function + Block* Parser::parse_css_block(bool is_root) + { + + // parse comments before block + // lex < optional_css_comments >(); + // lex mandatory opener or error out + if (!lex_css < exactly<'{'> >()) { + css_error("Invalid CSS", " after ", ": expected \"{\", was "); + } + // create new block and push to the selector stack + Block* block = new (ctx.mem) Block(pstate, 0, is_root); + block_stack.push_back(block); + + if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");; + + if (!lex_css < exactly<'}'> >()) { + css_error("Invalid CSS", " after ", ": expected \"}\", was "); + } + + // update for end position + block->update_pstate(pstate); + + // parse comments before block + // lex < optional_css_comments >(); + + block_stack.pop_back(); + + return block; + } + + // convenience function for block parsing + // will create a new block ad-hoc for you + // also updates the `in_at_root` flag + Block* Parser::parse_block(bool is_root) + { + LOCAL_FLAG(in_at_root, is_root); + return parse_css_block(is_root); + } + + // the main block parsing function + // parses stuff between `{` and `}` + bool Parser::parse_block_nodes() + { + + // loop until end of string while (position < end) { - parse_block_comments(root); - if (peek< kwd_import >()) { - Import* imp = parse_import(); - if (!imp->urls().empty()) (*root) << imp; - if (!imp->files().empty()) { - for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*root) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]); - } - } - semicolon = true; - error_message = "top-level @import directive must be terminated by ';'"; - } - else if (peek< kwd_mixin >() || peek< kwd_function >()) { - (*root) << parse_definition(); - } - else if (peek< variable >()) { - (*root) << parse_assignment(); - semicolon = true; - error_message = "top-level variable binding must be terminated by ';'"; - } + // parse comment blocks + parse_block_comments(); + lex < css_whitespace >(); + if (lex < exactly<';'> >()) continue; + if (peek < end_of_file >()) return true; + if (peek < exactly<'}'> >()) return true; - else if (peek< kwd_include_directive >() /* || peek< exactly<'+'> >() */) { - Mixin_Call* mixin_call = parse_include_directive(); - (*root) << mixin_call; - if (!mixin_call->block()) { - semicolon = true; - error_message = "top-level @include directive must be terminated by ';'"; - } - } - else if (peek< kwd_if_directive >()) { - (*root) << parse_if_directive(); - } - else if (peek< kwd_for_directive >()) { - (*root) << parse_for_directive(); - } - else if (peek< kwd_each_directive >()) { - (*root) << parse_each_directive(); - } - else if (peek< kwd_while_directive >()) { - (*root) << parse_while_directive(); - } - else if (peek< kwd_media >()) { - (*root) << parse_media_block(); - } - else if (peek< kwd_at_root >()) { - (*root) << parse_at_root_block(); - } - else if (peek< kwd_supports_directive >()) { - (*root) << parse_supports_directive(); - } - else if (peek< kwd_warn >()) { - (*root) << parse_warning(); - semicolon = true; - error_message = "top-level @warn directive must be terminated by ';'"; - } - else if (peek< kwd_err >()) { - (*root) << parse_error(); - semicolon = true; - error_message = "top-level @error directive must be terminated by ';'"; - } - else if (peek< kwd_dbg >()) { - (*root) << parse_debug(); - semicolon = true; - error_message = "top-level @debug directive must be terminated by ';'"; - } - // ignore the @charset directive for now - else if (lex< exactly< charset_kwd > >()) { - lex< quoted_string >(); - lex< one_plus< exactly<';'> > >(); - } - else if (peek< at_keyword >()) { - At_Rule* at_rule = parse_at_rule(); - (*root) << at_rule; - if (!at_rule->block()){ - semicolon = true; - error_message = "top-level directive must be terminated by ';'"; + if (parse_block_node()) continue; + + parse_block_comments(); + + if (lex_css < exactly<';'> >()) continue; + if (peek_css < end_of_file >()) return true; + if (peek_css < exactly<'}'> >()) return true; + + // illegal sass + return false; + } + // return success + return true; + } + + // parser for a single node in a block + // semicolons must be lexed beforehand + bool Parser::parse_block_node() { + + Block* block = block_stack.back(); + + while (lex< block_comment >()) { + bool is_important = lexed.begin[2] == '!'; + String* contents = parse_interpolated_chunk(lexed); + (*block) << new (ctx.mem) Comment(pstate, contents, is_important); + } + + // throw away white-space + // includes line comments + lex < css_whitespace >(); + + Lookahead lookahead_result; + + // also parse block comments + + // first parse everything that is allowed in functions + if (lex < variable >(true)) { (*block) << parse_assignment(); } + else if (lex < kwd_err >(true)) { (*block) << parse_error(); } + else if (lex < kwd_dbg >(true)) { (*block) << parse_debug(); } + else if (lex < kwd_warn >(true)) { (*block) << parse_warning(); } + else if (lex < kwd_if_directive >(true)) { (*block) << parse_if_directive(); } + else if (lex < kwd_for_directive >(true)) { (*block) << parse_for_directive(); } + else if (lex < kwd_each_directive >(true)) { (*block) << parse_each_directive(); } + else if (lex < kwd_while_directive >(true)) { (*block) << parse_while_directive(); } + else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); } + + // abort if we are in function context and have nothing parsed yet + else if (stack.back() == function_def) { + error("Functions can only contain variable declarations and control directives", pstate); + } + + // parse imports to process later + else if (lex < kwd_import >(true)) { + if (stack.back() == mixin_def || stack.back() == function_def) { + error("Import directives may not be used within control directives or mixins.", pstate); + } + Import* imp = parse_import(); + // if it is a url, we only add the statement + if (!imp->urls().empty()) (*block) << imp; + // if it is a file(s), we should process them + if (!imp->files().empty()) { + for (size_t i = 0, S = imp->files().size(); i < S; ++i) { + (*block) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]); } } - else if ((lookahead_result = lookahead_for_selector(position)).found) { - (*root) << parse_ruleset(lookahead_result); - } - else if (peek< exactly<';'> >()) { - lex< one_plus< exactly<';'> > >(); - } - else { - lex< css_whitespace >(); - if (position >= end) break; - error("invalid top-level expression", after_token); + } + + else if (lex < kwd_extend >(true)) { + if (block->is_root()) { + error("Extend directives may only be used within rules.", pstate); } - if (semicolon) { - if (!lex< one_plus< exactly<';'> > >() && peek_css< optional_css_whitespace >() != end) - { error(error_message, pstate); } - semicolon = false; + + Lookahead lookahead = lookahead_for_include(position); + if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was "); + Selector* target; + if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found); + else target = parse_selector_list(); + (*block) << new (ctx.mem) Extension(pstate, target); + } + + // selector may contain interpolations which need delayed evaluation + else if (!(lookahead_result = lookahead_for_selector(position)).error) + { (*block) << parse_ruleset(lookahead_result); } + + // parse multiple specific keyword directives + else if (lex < kwd_media >(true)) { (*block) << parse_media_block(); } + else if (lex < kwd_at_root >(true)) { (*block) << parse_at_root_block(); } + else if (lex < kwd_include_directive >(true)) { (*block) << parse_include_directive(); } + else if (lex < kwd_content_directive >(true)) { (*block) << parse_content_directive(); } + else if (lex < kwd_supports_directive >(true)) { (*block) << parse_supports_directive(); } + else if (lex < kwd_mixin >(true)) { (*block) << parse_definition(Definition::MIXIN); } + else if (lex < kwd_function >(true)) { (*block) << parse_definition(Definition::FUNCTION); } + + // ignore the @charset directive for now + else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } + + // generic at keyword (keep last) + else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); } + + else if (block->is_root()) { + lex< css_whitespace >(); + if (position >= end) return true; + css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was "); + } + // parse a declaration + else + { + // ToDo: how does it handle parse errors? + // maybe we are expected to parse something? + Declaration* decl = parse_declaration(); + decl->tabs(indentation); + (*block) << decl; + // maybe we have a "sub-block" + if (peek< exactly<'{'> >()) { + if (decl->is_indented()) ++ indentation; + // parse a propset that rides on the declaration's property + (*block) << new (ctx.mem) Propset(pstate, decl->property(), parse_block()); + if (decl->is_indented()) -- indentation; } - lex< optional_spaces >(); } - block_stack.pop_back(); - return root; + // something matched + return true; } + // EO parse_block_nodes void Parser::add_single_file (Import* imp, string import_path) { @@ -282,7 +373,6 @@ namespace Sass { Import* Parser::parse_import() { - lex< kwd_import >(); Import* imp = new (ctx.mem) Import(pstate); vector> to_import; bool first = true; @@ -303,7 +393,7 @@ namespace Sass { Expression* the_url = parse_string(); *args << new (ctx.mem) Argument(the_url->pstate(), the_url); } - else if (lex < uri_value >(position)) { // chunk seems to work too! + else if (lex < uri_value >(position != 0)) { // chunk seems to work too! String* the_url = parse_interpolated_chunk(lexed); *args << new (ctx.mem) Argument(the_url->pstate(), the_url); } @@ -341,11 +431,8 @@ namespace Sass { return imp; } - Definition* Parser::parse_definition() + Definition* Parser::parse_definition(Definition::Type which_type) { - Definition::Type which_type = Definition::MIXIN; - if (lex< kwd_mixin >()) which_type = Definition::MIXIN; - else if (lex< kwd_function >()) which_type = Definition::FUNCTION; string which_str(lexed); if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate); string name(Util::normalize_underscores(lexed)); @@ -353,7 +440,6 @@ namespace Sass { { error("Invalid function name \"" + name + "\".", pstate); } ParserState source_position_of_def = pstate; Parameters* params = parse_parameters(); - if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'", pstate); if (which_type == Definition::MIXIN) stack.push_back(mixin_def); else stack.push_back(function_def); Block* body = parse_block(); @@ -381,7 +467,7 @@ namespace Sass { Parameter* Parser::parse_parameter() { while (lex< alternatives < spaces, block_comment > >()); - lex< variable >(); + lex < variable >(); string name(Util::normalize_underscores(lexed)); ParserState pos = pstate; Expression* val = 0; @@ -399,21 +485,6 @@ namespace Sass { return p; } - Mixin_Call* Parser::parse_include_directive() - { - lex< kwd_include_directive >() /* || lex< exactly<'+'> >() */; - if (!lex< identifier >()) error("invalid name in @include directive", pstate); - ParserState source_position_of_call = pstate; - string name(Util::normalize_underscores(lexed)); - Arguments* args = parse_arguments(); - Block* content = 0; - if (peek< exactly<'{'> >()) { - content = parse_block(); - } - Mixin_Call* the_call = new (ctx.mem) Mixin_Call(source_position_of_call, name, args, content); - return the_call; - } - Arguments* Parser::parse_arguments(bool has_url) { string name(lexed); @@ -464,7 +535,6 @@ namespace Sass { Assignment* Parser::parse_assignment() { - lex< variable >(); string name(Util::normalize_underscores(lexed)); ParserState var_source_position = pstate; if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate); @@ -478,9 +548,9 @@ namespace Sass { val->is_delayed(false); bool is_default = false; bool is_global = false; - while (peek< default_flag >() || peek< global_flag >()) { - is_default = lex< default_flag >() || is_default; - is_global = lex< global_flag >() || is_global; + while (peek< alternatives < default_flag, global_flag > >()) { + if (lex< default_flag >()) is_default = true; + else if (lex< global_flag >()) is_global = true; } Assignment* var = new (ctx.mem) Assignment(var_source_position, name, val, is_default, is_global); return var; @@ -489,22 +559,16 @@ namespace Sass { // a ruleset connects a selector and a block Ruleset* Parser::parse_ruleset(Lookahead lookahead) { - Selector* sel; - if (lookahead.has_interpolants) { - sel = parse_selector_schema(lookahead.found); - } - else { - sel = parse_selector_list(); - } - bool old_in_at_root = in_at_root; - ParserState r_source_position = pstate; - lex < css_comments >(); - in_at_root = false; - if (!peek< exactly<'{'> >()) error("expected a '{' after the selector", pstate); - Block* block = parse_block(); - in_at_root = old_in_at_root; - old_in_at_root = false; - Ruleset* ruleset = new (ctx.mem) Ruleset(r_source_position, sel, block); + // create the connector object (add parts later) + Ruleset* ruleset = new (ctx.mem) Ruleset(pstate); + // parse selector static or as schema to be evaluated later + if (lookahead.parsable) ruleset->selector(parse_selector_list()); + else ruleset->selector(parse_selector_schema(lookahead.found)); + // then parse the inner block + ruleset->block(parse_block()); + // update for end position + ruleset->update_pstate(pstate); + // return AST Node return ruleset; } @@ -519,6 +583,9 @@ namespace Sass { // selector schema re-uses string schema implementation String_Schema* schema = new (ctx.mem) String_Schema(pstate); // the selector schema is pretty much just a wrapper for the string schema + Selector_Schema* selector_schema = new (ctx.mem) Selector_Schema(pstate, schema); + + // process until end while (i < end_of_selector) { // try to parse mutliple interpolants if (const char* p = find_first_in_interval< exactly >(i, end_of_selector)) { @@ -553,193 +620,242 @@ namespace Sass { // update position position = i; - Selector_Schema* selector_schema = new (ctx.mem) Selector_Schema(pstate, schema); - selector_schema->media_block(last_media_block); - selector_schema->last_block(block_stack.back()); - // update position + // update for end position + selector_schema->update_pstate(pstate); + + // return parsed result return selector_schema; } // EO parse_selector_schema - Selector_List* Parser::parse_selector_list() + void Parser::parse_charset_directive() + { + lex < + sequence < + quoted_string, + optional_spaces, + exactly <';'> + > + >(); + } + + // called after parsing `kwd_include_directive` + Mixin_Call* Parser::parse_include_directive() + { + // lex identifier into `lexed` var + lex_identifier(); // may error out + // normalize underscores to hyphens + string name(Util::normalize_underscores(lexed)); + // create the initial mixin call object + Mixin_Call* call = new (ctx.mem) Mixin_Call(pstate, name, 0, 0); + // parse mandatory arguments + call->arguments(parse_arguments()); + // parse optional block + if (peek < exactly <'{'> >()) { + call->block(parse_block()); + } + // return ast node + return call; + } + // EO parse_include_directive + + // parse a list of complex selectors + // this is the main entry point for most + Selector_List* Parser::parse_selector_list(bool in_root) { bool reloop = true; + bool had_linefeed = false; To_String to_string(&ctx); - lex< css_whitespace >(); Selector_List* group = new (ctx.mem) Selector_List(pstate); - group->media_block(last_media_block); - group->last_block(block_stack.back()); + do { reloop = false; - if (peek< alternatives < - exactly<'{'>, - exactly<'}'>, - exactly<')'>, - exactly<';'> - > >()) + + had_linefeed = had_linefeed || peek_newline(); + + if (peek_css< class_char < selector_list_delims > >()) break; // in case there are superfluous commas at the end - Complex_Selector* comb = parse_complex_selector(); - if (!comb->has_reference() && !in_at_root) { - ParserState sel_source_position = pstate; - Selector_Reference* ref = new (ctx.mem) Selector_Reference(sel_source_position); - Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(sel_source_position); - ref_wrap->media_block(last_media_block); - ref_wrap->last_block(block_stack.back()); - (*ref_wrap) << ref; - if (!comb->head()) { - comb->head(ref_wrap); - comb->has_reference(true); - } - else { - comb = new (ctx.mem) Complex_Selector(sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb); - comb->media_block(last_media_block); - comb->last_block(block_stack.back()); - comb->has_reference(true); - } - if (peek_newline()) ref_wrap->has_line_break(true); - } + + + // now parse the complex selector + Complex_Selector* sel = parse_complex_selector(in_root); + + if (!sel) return group; + + sel->has_line_feed(had_linefeed); + + had_linefeed = false; + while (peek_css< exactly<','> >()) { + lex< spaces >(); + lex< css_comments >(); // consume everything up and including the comma speparator - reloop = lex< sequence < optional_css_comments, exactly<','> > >() != 0; + reloop = lex< exactly<','> >() != 0; // remember line break (also between some commas) - if (peek_newline()) comb->has_line_feed(true); - if (comb->tail() && peek_newline()) comb->tail()->has_line_feed(true); - if (comb->tail() && comb->tail()->head() && peek_newline()) comb->tail()->head()->has_line_feed(true); + had_linefeed = had_linefeed || peek_newline(); // remember line break (also between some commas) } - (*group) << comb; + (*group) << sel; } while (reloop); - while (lex< kwd_optional >()) { + while (lex_css< kwd_optional >()) { group->is_optional(true); } + // update for end position + group->update_pstate(pstate); return group; } + // EO parse_selector_list - Complex_Selector* Parser::parse_complex_selector() + // a complex selector combines a compound selector with another + // complex selector, with one of four combinator operations. + // the compound selector (head) is optional, since the combinator + // can come first in the whole selector sequence (like `> DIV'). + Complex_Selector* Parser::parse_complex_selector(bool in_root) { - Position sel_source_position(-1); - Compound_Selector* lhs; - if (peek_css< alternatives < - exactly<'+'>, - exactly<'~'>, - exactly<'>'> - > >()) + + lex < block_comment >(); // parse the left hand side - { lhs = 0; } - else { + Compound_Selector* lhs = 0; + // special case if it starts with combinator ([+~>]) + if (!peek_css< class_char < selector_combinator_ops > >()) { + // parse the left hand side lhs = parse_compound_selector(); - sel_source_position = before_token; - lhs->has_line_break(peek_newline()); } - Complex_Selector::Combinator cmb; - if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO; - else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES; - else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF; - else cmb = Complex_Selector::ANCESTOR_OF; - bool cpx_lf = peek_newline(); + // check for end of file condition + if (peek < end_of_file >()) return 0; - Complex_Selector* rhs; - if (peek_css< alternatives < - exactly<','>, - exactly<')'>, - exactly<'{'>, - exactly<'}'>, - exactly<';'>, - kwd_optional - > >()) // parse combinator between lhs and rhs - { rhs = 0; } - else { - rhs = parse_complex_selector(); - sel_source_position = before_token; - } - if (!sel_source_position.line) sel_source_position = before_token; - Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, source, sel_source_position), cmb, lhs, rhs); - cpx->media_block(last_media_block); - cpx->last_block(block_stack.back()); - if (cpx_lf) cpx->has_line_break(cpx_lf); - return cpx; - } - + Complex_Selector::Combinator combinator; + if (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO; + else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES; + else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF; + else /* if (lex< zero >()) */ combinator = Complex_Selector::ANCESTOR_OF; + + if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0; + + // lex < block_comment >(); + // source position of a complex selector points to the combinator + // ToDo: make sure we update pstate for ancestor of (lex < zero >()); + Complex_Selector* sel = new (ctx.mem) Complex_Selector(pstate, combinator, lhs); + // has linfeed after combinator? + sel->has_line_break(peek_newline()); + // sel->has_line_feed(has_line_feed); + + // check if we got the abort condition (ToDo: optimize) + if (!peek_css< class_char < complex_selector_delims > >()) { + // parse next selector in sequence + sel->tail(parse_complex_selector()); + if (sel->tail()) { + // ToDo: move this logic below into tail setter + if (sel->tail()->has_reference()) sel->has_reference(true); + if (sel->tail()->has_placeholder()) sel->has_placeholder(true); + } + } + + // add a parent selector if we are not in a root + // also skip adding parent ref if we only have refs + if (!sel->has_reference() && !in_at_root && !in_root) { + // create the objects to wrap parent selector reference + Parent_Selector* parent = new (ctx.mem) Parent_Selector(pstate); + Compound_Selector* head = new (ctx.mem) Compound_Selector(pstate); + // add simple selector + (*head) << parent; + // selector may not have any head yet + if (!sel->head()) { sel->head(head); } + // otherwise we need to create a new complex selector and set the old one as its tail + else { sel = new (ctx.mem) Complex_Selector(pstate, Complex_Selector::ANCESTOR_OF, head, sel); } + // peek for linefeed and remember result on head + // if (peek_newline()) head->has_line_break(true); + } + + // complex selector + return sel; + } + // EO parse_complex_selector + + // parse one compound selector, which is basically + // a list of simple selectors (directly adjancent) + // lex them exactly (without skipping white-space) Compound_Selector* Parser::parse_compound_selector() { + // init an empty compound selector wrapper Compound_Selector* seq = new (ctx.mem) Compound_Selector(pstate); - seq->media_block(last_media_block); - seq->last_block(block_stack.back()); - bool sawsomething = false; - if (lex_css< exactly<'&'> >()) { + + // skip initial white-space + lex< css_whitespace >(); + + // parse list + while (true) + { // remove all block comments (don't skip white-space) - if (block_stack.back() && block_stack.back()->is_root()) { + lex< delimited_by< slash_star, star_slash, false > >(false); + // parse functional + if (peek < re_pseudo_selector >()) + { + (*seq) << parse_simple_selector(); + } + // parse parent selector + else if (lex< exactly<'&'> >(false)) + { // this produces a linefeed!? + seq->has_parent_reference(true); + (*seq) << new (ctx.mem) Parent_Selector(pstate); } - (*seq) << new (ctx.mem) Selector_Reference(pstate); - sawsomething = true; // parse type selector - if(peek< spaces >() || peek< alternatives < spaces, exactly<';'> > >()) { - return seq; + else if (lex< re_type_selector >(false)) + { + (*seq) << new (ctx.mem) Type_Selector(pstate, lexed); } - } - if (sawsomething && lex_css< sequence< negate< re_pseudo_selector >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) { // peek for abort conditions - (*seq) << new (ctx.mem) Type_Selector(pstate, unquote(lexed)); - } else if (lex_css< sequence< negate< re_pseudo_selector >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) { + else if (peek< spaces >()) break; + else if (peek< end_of_file >()) { break; } + else if (peek_css < class_char < selector_combinator_ops > >()) break; + else if (peek_css < class_char < complex_selector_delims > >()) break; // otherwise parse another simple selector - (*seq) << new (ctx.mem) Type_Selector(pstate, lexed); - sawsomething = true; - } - if (!sawsomething) { - // don't blindly do this if you saw a & or selector - (*seq) << parse_simple_selector(); + else { + Simple_Selector* sel = parse_simple_selector(); + if (!sel) return 0; + (*seq) << sel; + } } - while (!peek< spaces >(position) && - !(peek_css < alternatives < - exactly<'+'>, - exactly<'~'>, - exactly<'>'>, - exactly<','>, - exactly<')'>, - exactly<'{'>, - exactly<'}'>, - exactly<';'> - > >(position))) { - (*seq) << parse_simple_selector(); - } + if (seq) seq->has_line_break(peek_newline()); + + // EO while true return seq; + } + // EO parse_compound_selector Simple_Selector* Parser::parse_simple_selector() { lex < css_comments >(); if (lex< alternatives < id_name, class_name > >()) { - return new (ctx.mem) Selector_Qualifier(pstate, unquote(lexed)); + return new (ctx.mem) Selector_Qualifier(pstate, lexed); } else if (lex< quoted_string >()) { return new (ctx.mem) Type_Selector(pstate, unquote(lexed)); } - else if (lex< alternatives < number, kwd_sel_deep > >()) { + else if (lex< alternatives < variable, number, kwd_sel_deep > >()) { return new (ctx.mem) Type_Selector(pstate, lexed); } else if (peek< pseudo_not >()) { return parse_negated_selector(); } - else if (peek< exactly<':'> >(position) || peek< re_pseudo_selector >()) { + else if (peek< re_pseudo_selector >()) { return parse_pseudo_selector(); } - else if (peek< exactly<'['> >(position)) { + else if (peek< exactly<':'> >()) { + return parse_pseudo_selector(); + } + else if (lex < exactly<'['> >()) { return parse_attribute_selector(); } else if (lex< placeholder >()) { - Selector_Placeholder* sel = new (ctx.mem) Selector_Placeholder(pstate, unquote(lexed)); - sel->media_block(last_media_block); - sel->last_block(block_stack.back()); - return sel; - } - else { - error("invalid selector after " + lexed.to_string(), pstate); + return new (ctx.mem) Selector_Placeholder(pstate, lexed); } // failed return 0; @@ -754,67 +870,70 @@ namespace Sass { if (!lex< exactly<')'> >()) { error("negated selector is missing ')'", pstate); } + name.erase(name.size() - 1); return new (ctx.mem) Wrapped_Selector(nsource_position, name, negated); } + // a pseudo selector often starts with one or two colons + // it can contain more selectors inside parantheses Simple_Selector* Parser::parse_pseudo_selector() { - if (lex< sequence< pseudo_prefix, re_pseudo_selector > >() || lex< re_pseudo_selector >()) { + if (lex< sequence< + optional < pseudo_prefix >, + // we keep the space within the name, strange enough + // ToDo: refactor output to schedule the space for it + // or do we really want to keep the real white-space? + sequence< identifier, optional < block_comment >, exactly<'('> > + > >()) + { + string name(lexed); - String* expr = 0; + name.erase(name.size() - 1); ParserState p = pstate; - Selector* wrapped = 0; - if (lex< alternatives< even, odd > >()) { - expr = new (ctx.mem) String_Quoted(p, lexed); - } - else if (lex< binomial >(position)) { - expr = new (ctx.mem) String_Constant(p, lexed); - ((String_Constant*)expr)->can_compress_whitespace(true); - } - else if (peek< sequence< optional, - zero_plus, - exactly<'n'>, - optional_css_whitespace, - exactly<')'> > >()) { - lex< sequence< optional, - zero_plus, - exactly<'n'> > >(); - expr = new (ctx.mem) String_Quoted(p, lexed); - } - else if (lex< sequence< optional, one_plus < digit > > >()) { - expr = new (ctx.mem) String_Quoted(p, lexed); - } - else if (peek< sequence< identifier, optional_css_whitespace, exactly<')'> > >()) { - lex< identifier >(); - expr = new (ctx.mem) String_Quoted(p, lexed); - } - else if (lex< quoted_string >()) { - expr = new (ctx.mem) String_Quoted(p, lexed); - } - else if (peek< exactly<')'> >()) { - expr = new (ctx.mem) String_Constant(p, ""); - } - else { - wrapped = parse_selector_list(); + + // specially parse static stuff + // ToDo: really everything static? + if (peek_css < + sequence < + alternatives < + static_value, + binomial + >, + optional_css_whitespace, + exactly<')'> + > + >() + ) { + lex_css< alternatives < static_value, binomial > >(); + String_Constant* expr = new (ctx.mem) String_Constant(pstate, lexed); + if (expr && lex_css< exactly<')'> >()) { + expr->can_compress_whitespace(true); + return new (ctx.mem) Pseudo_Selector(p, name, expr); + } } - if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)", pstate); - if (wrapped) { - return new (ctx.mem) Wrapped_Selector(p, name, wrapped); + else if (Selector* wrapped = parse_selector_list()) { + if (wrapped && lex_css< exactly<')'> >()) { + return new (ctx.mem) Wrapped_Selector(p, name, wrapped); + } } - return new (ctx.mem) Pseudo_Selector(p, name, expr); + } - else if (lex < sequence< pseudo_prefix, identifier > >()) { - return new (ctx.mem) Pseudo_Selector(pstate, unquote(lexed)); + // EO if pseudo selector + + else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) { + return new (ctx.mem) Pseudo_Selector(pstate, lexed); } - else { - error("unrecognized pseudo-class or pseudo-element", pstate); + else if(lex < pseudo_prefix >()) { + css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was "); } + + css_error("Invalid CSS", " after ", ": expected \")\", was "); + // unreachable statement return 0; } Attribute_Selector* Parser::parse_attribute_selector() { - lex_css< exactly<'['> >(); ParserState p = pstate; if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate); string name(lexed); @@ -827,7 +946,7 @@ namespace Sass { String* value = 0; if (lex_css< identifier >()) { - value = new (ctx.mem) String_Constant(p, lexed); + value = new (ctx.mem) String_Quoted(p, lexed); } else if (lex_css< quoted_string >()) { value = parse_interpolated_chunk(lexed, true); // needed! @@ -841,8 +960,9 @@ namespace Sass { } /* parse block comment and add to block */ - void Parser::parse_block_comments(Block* block) + void Parser::parse_block_comments() { + Block* block = block_stack.back(); while (lex< block_comment >()) { bool is_important = lexed.begin[2] == '!'; String* contents = parse_interpolated_chunk(lexed); @@ -850,171 +970,9 @@ namespace Sass { } } - Block* Parser::parse_block() - { - lex< exactly<'{'> >(); - bool semicolon = false; - Lookahead lookahead_result; - Block* block = new (ctx.mem) Block(pstate); - block_stack.push_back(block); - lex< zero_plus < alternatives < space, line_comment > > >(); - // JMA - ensure that a block containing only block_comments is parsed - parse_block_comments(block); - - while (!lex< exactly<'}'> >()) { - parse_block_comments(block); - if (semicolon) { - if (!lex< one_plus< exactly<';'> > >()) { - error("non-terminal statement or declaration must end with ';'", pstate); - } - semicolon = false; - parse_block_comments(block); - if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break; - } - else if (peek< kwd_import >(position)) { - if (stack.back() == mixin_def || stack.back() == function_def) { - lex< kwd_import >(); // to adjust the before_token number - error("@import directives are not allowed inside mixins and functions", pstate); - } - Import* imp = parse_import(); - if (!imp->urls().empty()) (*block) << imp; - if (!imp->files().empty()) { - for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*block) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]); - } - } - semicolon = true; - } - else if (lex< variable >()) { - (*block) << parse_assignment(); - semicolon = true; - } - else if (lex< line_comment >()) { - // throw line comments away - } - else if (peek< kwd_if_directive >()) { - (*block) << parse_if_directive(); - } - else if (peek< kwd_for_directive >()) { - (*block) << parse_for_directive(); - } - else if (peek< kwd_each_directive >()) { - (*block) << parse_each_directive(); - } - else if (peek < kwd_while_directive >()) { - (*block) << parse_while_directive(); - } - else if (peek < kwd_return_directive >()) { - (*block) << parse_return_directive(); - semicolon = true; - } - else if (peek< kwd_warn >()) { - (*block) << parse_warning(); - semicolon = true; - } - else if (peek< kwd_err >()) { - (*block) << parse_error(); - semicolon = true; - } - else if (peek< kwd_dbg >()) { - (*block) << parse_debug(); - semicolon = true; - } - else if (stack.back() == function_def) { - error("only variable declarations and control directives are allowed inside functions", pstate); - } - else if (peek< kwd_mixin >() || peek< kwd_function >()) { - (*block) << parse_definition(); - } - else if (peek< kwd_include_directive >(position)) { - Mixin_Call* the_call = parse_include_directive(); - (*block) << the_call; - // don't need a semicolon after a content block - semicolon = (the_call->block()) ? false : true; - } - else if (lex< kwd_content >()) { - if (stack.back() != mixin_def) { - error("@content may only be used within a mixin", pstate); - } - (*block) << new (ctx.mem) Content(pstate); - semicolon = true; - } - /* - else if (peek< exactly<'+'> >()) { - (*block) << parse_mixin_call(); - semicolon = true; - } - */ - else if (lex< kwd_extend >()) { - Lookahead lookahead = lookahead_for_include(position); - if (!lookahead.found) error("invalid selector for @extend", pstate); - Selector* target; - if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found); - else target = parse_selector_list(); - (*block) << new (ctx.mem) Extension(pstate, target); - semicolon = true; - } - else if (peek< kwd_media >()) { - (*block) << parse_media_block(); - } - else if (peek< kwd_supports_directive >()) { - (*block) << parse_supports_directive(); - } - else if (peek< kwd_at_root >()) { - (*block) << parse_at_root_block(); - } - // ignore the @charset directive for now - else if (lex< exactly< charset_kwd > >()) { - lex< quoted_string >(); - lex< one_plus< exactly<';'> > >(); - } - else if (peek< at_keyword >()) { - At_Rule* at_rule = parse_at_rule(); - (*block) << at_rule; - if (!at_rule->block()) semicolon = true; - } - else if ((lookahead_result = lookahead_for_selector(position)).found) { - (*block) << parse_ruleset(lookahead_result); - }/* not used anymore - remove? - else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) { - (*block) << parse_propset(); - }*/ - else if (!peek< exactly<';'> >()) { - bool indent = ! peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position); - /* not used anymore - remove? - if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) { - (*block) << parse_propset(); - } - else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) { - (*block) << parse_propset(); - } - else */ { - Declaration* decl = parse_declaration(); - decl->tabs(indentation); - (*block) << decl; - if (peek< exactly<'{'> >()) { - // parse a propset that rides on the declaration's property - if (indent) indentation++; - Propset* ps = new (ctx.mem) Propset(pstate, decl->property(), parse_block()); - if (indent) indentation--; - (*block) << ps; - } - else { - // finish and let the semicolon get munched - semicolon = true; - } - } - } - else lex< one_plus< exactly<';'> > >(); - parse_block_comments(block); - } - block_stack.pop_back(); - return block; - } - Declaration* Parser::parse_declaration() { String* prop = 0; - if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) { + if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) { prop = parse_identifier_schema(); } else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) { @@ -1024,9 +982,11 @@ namespace Sass { else { error("invalid property name", pstate); } + bool is_indented = true; const string property(lexed); if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate); if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate); + if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty if (peek_css< static_value >()) { return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex()*/); } @@ -1049,7 +1009,9 @@ namespace Sass { } } - return new (ctx.mem) Declaration(prop->pstate(), prop, value/*, lex()*/); + auto decl = new (ctx.mem) Declaration(prop->pstate(), prop, value/*, lex()*/); + decl->is_indented(is_indented); + return decl; } } @@ -1071,8 +1033,8 @@ namespace Sass { Expression* Parser::parse_map() { - ParserState opstate = pstate; Expression* key = parse_list(); + Map* map = new (ctx.mem) Map(pstate, 1); if (String_Quoted* str = dynamic_cast(key)) { if (!str->quote_mark() && !str->is_delayed()) { if (ctx.names_to_colors.count(str->value())) { @@ -1092,7 +1054,6 @@ namespace Sass { Expression* value = parse_space_list(); - Map* map = new (ctx.mem) Map(opstate, 1); (*map) << make_pair(key, value); while (lex_css< exactly<','> >()) @@ -1128,49 +1089,66 @@ namespace Sass { return map; } + // parse list returns either a space separated list, + // a comma separated list or any bare expression found. + // so to speak: we unwrap items from lists if possible here! Expression* Parser::parse_list() { + // parse list is relly just an alias return parse_comma_list(); } + // will return singletons unwrapped Expression* Parser::parse_comma_list() { + // check if we have an empty list + // return the empty list as such if (peek_css< alternatives < // exactly<'!'>, - // exactly<':'>, exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, - exactly + exactly<':'>, + exactly, + default_flag, + global_flag > >(position)) { return new (ctx.mem) List(pstate, 0); } - Expression* list1 = parse_space_list(); + // now try to parse a space list - if (!peek_css< exactly<','> >(position)) return list1; + Expression* list = parse_space_list(); + // if it's a singleton, return it (don't wrap it) + if (!peek_css< exactly<','> >(position)) return list; + // if we got so far, we actually do have a comma list List* comma_list = new (ctx.mem) List(pstate, 2, List::COMMA); - (*comma_list) << list1; + // wrap the first expression + (*comma_list) << list; while (lex_css< exactly<','> >()) { + // check for abort condition if (peek_css< alternatives < - // exactly<'!'>, exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, exactly<':'>, - exactly + exactly, + default_flag, + global_flag > >(position) ) { break; } - Expression* list = parse_space_list(); - (*comma_list) << list; + // otherwise add another expression + (*comma_list) << parse_space_list(); } - + // return the list return comma_list; } + // EO parse_comma_list + // will return singletons unwrapped Expression* Parser::parse_space_list() { Expression* disj1 = parse_disjunction(); @@ -1205,42 +1183,52 @@ namespace Sass { global_flag > >(position)) && peek_css< optional_css_whitespace >() != end ) { + // the space is parsed implicitly? (*space_list) << parse_disjunction(); } - + // return the list return space_list; } + // EO parse_space_list + // parse logical OR operation Expression* Parser::parse_disjunction() { - Expression* conj1 = parse_conjunction(); // parse the left hand side conjunction - if (!peek_css< kwd_or >()) return conj1; - + Expression* conj = parse_conjunction(); + // parse multiple right hand sides vector operands; while (lex_css< kwd_or >()) operands.push_back(parse_conjunction()); - - return fold_operands(conj1, operands, Binary_Expression::OR); + // if it's a singleton, return it directly + if (operands.size() == 0) return conj; + // fold all operands into one binary expression + return fold_operands(conj, operands, Binary_Expression::OR); } + // EO parse_disjunction + // parse logical AND operation Expression* Parser::parse_conjunction() { - Expression* rel1 = parse_relation(); // parse the left hand side relation - if (!peek_css< kwd_and >()) return rel1; - + Expression* rel = parse_relation(); + // parse multiple right hand sides vector operands; while (lex_css< kwd_and >()) operands.push_back(parse_relation()); - - return fold_operands(rel1, operands, Binary_Expression::AND); + // if it's a singleton, return it directly + if (operands.size() == 0) return rel; + // fold all operands into one binary expression + return fold_operands(rel, operands, Binary_Expression::AND); } + // EO parse_conjunction + // parse comparison operations Expression* Parser::parse_relation() { - Expression* expr1 = parse_expression(); // parse the left hand side expression + Expression* lhs = parse_expression(); + // if it's a singleton, return it (don't wrap it) if (!(peek< alternatives < kwd_eq, kwd_neq, @@ -1249,8 +1237,8 @@ namespace Sass { kwd_lte, kwd_lt > >(position))) - { return expr1; } - + { return lhs; } + // parse the operator Binary_Expression::Type op = lex() ? Binary_Expression::EQ : lex() ? Binary_Expression::NEQ @@ -1258,22 +1246,30 @@ namespace Sass { : lex() ? Binary_Expression::LTE : lex() ? Binary_Expression::GT : lex() ? Binary_Expression::LT - : Binary_Expression::LT; // whatever - - Expression* expr2 = parse_expression(); - - return new (ctx.mem) Binary_Expression(expr1->pstate(), op, expr1, expr2); - } - + // we checked the possibilites on top of fn + : Binary_Expression::EQ; + // parse the right hand side expression + Expression* rhs = parse_expression(); + // return binary expression with a left and a right hand side + return new (ctx.mem) Binary_Expression(lhs->pstate(), op, lhs, rhs); + } + // parse_relation + + // parse expression valid for operations + // called from parse_relation + // called from parse_for_directive + // called from parse_media_expression + // parse addition and subtraction operations Expression* Parser::parse_expression() { - Expression* term1 = parse_operators(); + Expression* lhs = parse_operators(); // if it's a singleton, return it (don't wrap it) if (!(peek< exactly<'+'> >(position) || + // condition is a bit misterious, but some combinations should not be counted as operations (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) || peek< identifier >(position)) - { return term1; } + { return lhs; } vector operands; vector operators; @@ -1282,27 +1278,25 @@ namespace Sass { operands.push_back(parse_operators()); } - return fold_operands(term1, operands, operators); + if (operands.size() == 0) return lhs; + return fold_operands(lhs, operands, operators); } + // parse addition and subtraction operations Expression* Parser::parse_operators() { Expression* factor = parse_factor(); // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant - if (peek_css< exactly<'%'> >(position) && factor->concrete_type() == Expression::STRING) { + if (peek_css< exactly<'%'> >() && factor->concrete_type() == Expression::STRING) { String_Schema* ss = dynamic_cast(factor); if (ss && ss->has_interpolants()) return factor; } // if it's a singleton, return it (don't wrap it) - if (!peek< class_char< static_ops > >(position)) return factor; - return parse_operators(factor); - } - - Expression* Parser::parse_operators(Expression* factor) - { + if (!peek_css< class_char< static_ops > >()) return factor; // parse more factors and operators vector operands; // factors vector operators; // ops + // lex operations to apply to lhs while (lex_css< class_char< static_ops > >()) { switch(*lexed.begin) { case '*': operators.push_back(Binary_Expression::MUL); break; @@ -1315,12 +1309,19 @@ namespace Sass { // operands and operators to binary expression return fold_operands(factor, operands, operators); } + // EO parse_operators + + // called from parse_operators + // called from parse_value_schema Expression* Parser::parse_factor() { if (lex_css< exactly<'('> >()) { + // parse_map may return a list Expression* value = parse_map(); + // lex the expected closing parenthesis if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate); + // expression can be evaluated value->is_delayed(false); // make sure wrapped lists and division expressions are non-delayed within parentheses if (value->concrete_type() == Expression::LIST) { @@ -1345,22 +1346,22 @@ namespace Sass { peek< exactly< webkit_calc_kwd > >()) { return parse_calc_function(); } - else if (peek< functional_schema >()) { + else if (lex < functional_schema >()) { return parse_function_call_schema(); } - else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) { + else if (lex< identifier_schema >()) { return parse_identifier_schema(); } else if (peek< re_pseudo_selector >()) { return parse_function_call(); } - else if (lex< sequence< exactly<'+'>, optional_css_whitespace, negate< number > > >()) { + else if (lex< exactly<'+'> >()) { return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::PLUS, parse_factor()); } - else if (lex< sequence< exactly<'-'>, optional_css_whitespace, negate< number> > >()) { + else if (lex< exactly<'-'> >()) { return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_factor()); } - else if (lex< sequence< kwd_not, css_whitespace > >()) { + else if (lex< sequence< kwd_not > >()) { return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::NOT, parse_factor()); } else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) { @@ -1372,19 +1373,24 @@ namespace Sass { } } + // parse one value for a list Expression* Parser::parse_value() { lex< css_comments >(); if (lex< ampersand >()) { - return new (ctx.mem) Parent_Selector(pstate, parse_selector_list()); } + return new (ctx.mem) Parent_Selector(pstate); } if (lex< kwd_important >()) - { return new (ctx.mem) String_Constant(pstate, "!important"); } + { return new (ctx.mem) String_Quoted(pstate, "!important"); } if (const char* stop = peek< value_schema >()) { return parse_value_schema(stop); } + // string may be interpolated + if (lex< quoted_string >()) + { return parse_string(); } + if (lex< kwd_true >()) { return new (ctx.mem) Boolean(pstate, true); } @@ -1395,10 +1401,7 @@ namespace Sass { { return new (ctx.mem) Null(pstate); } if (lex< identifier >()) { - String_Constant* str = new (ctx.mem) String_Quoted(pstate, lexed); - // Dont' delay this string if it is a name color. Fixes #652. - str->is_delayed(ctx.names_to_colors.count(unquote(lexed)) == 0); - return str; + return new (ctx.mem) String_Constant(pstate, lexed); } if (lex< percentage >()) @@ -1412,12 +1415,12 @@ namespace Sass { if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >()) { return new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed); } + if (lex< sequence< static_component, one_plus< identifier > > >()) + { return new (ctx.mem) String_Quoted(pstate, lexed); } + if (lex< number >()) { return new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); } - if (peek< quoted_string >()) - { return parse_string(); } - if (lex< variable >()) { return new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); } @@ -1478,6 +1481,7 @@ namespace Sass { } ++ i; } + return schema; } @@ -1503,9 +1507,7 @@ namespace Sass { String* Parser::parse_string() { - lex< quoted_string >(); - Token token(lexed); - return parse_interpolated_chunk(token); + return parse_interpolated_chunk(Token(lexed)); } String* Parser::parse_ie_property() @@ -1516,10 +1518,7 @@ namespace Sass { // see if there any interpolants const char* p = find_first_in_interval< exactly >(str.begin, str.end); if (!p) { - String_Constant* str_node = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(str.begin, str.end))); - str_node->is_delayed(true); - str_node->quote_mark('*'); - return str_node; + return new (ctx.mem) String_Quoted(pstate, string(str.begin, str.end)); } String_Schema* schema = new (ctx.mem) String_Schema(pstate); @@ -1527,10 +1526,7 @@ namespace Sass { p = find_first_in_interval< exactly >(i, str.end); if (p) { if (i < p) { - String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, p))); // accumulate the preceding segment if it's nonempty - part->is_delayed(true); - part->quote_mark('*'); // avoid unquote in interpolation - (*schema) << part; + (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p)); // accumulate the preceding segment if it's nonempty } if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); @@ -1550,10 +1546,7 @@ namespace Sass { } else { // no interpolants left; add the last segment if nonempty if (i < str.end) { - String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, str.end))); - part->is_delayed(true); - part->quote_mark('*'); // avoid unquote in interpolation - (*schema) << part; + (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, str.end)); } break; } @@ -1568,10 +1561,10 @@ namespace Sass { *kwd_arg << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); } else { lex< alternatives< identifier_schema, identifier > >(); - *kwd_arg << new (ctx.mem) String_Constant(pstate, lexed); + *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed); } lex< exactly<'='> >(); - *kwd_arg << new (ctx.mem) String_Constant(pstate, lexed); + *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed); if (peek< variable >()) *kwd_arg << parse_list(); else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(pstate, Textual::NUMBER, Util::normalize_decimals(lexed)); else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); } @@ -1580,73 +1573,75 @@ namespace Sass { String_Schema* Parser::parse_value_schema(const char* stop) { + // initialize the string schema object to add tokens String_Schema* schema = new (ctx.mem) String_Schema(pstate); - size_t num_items = 0; + if (peek>()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } + + size_t num_items = 0; while (position < stop) { + // parse space between tokens if (lex< spaces >() && num_items) { - (*schema) << new (ctx.mem) String_Constant(pstate, " "); + (*schema) << new (ctx.mem) String_Quoted(pstate, " "); } - else if (lex< interpolant >()) { - Token insides(Token(lexed.begin + 2, lexed.end - 1)); - Expression* interp_node; - Parser p = Parser::from_token(insides, ctx, pstate); - if (!(interp_node = p.parse_static_expression())) { - interp_node = p.parse_list(); - interp_node->is_interpolant(true); + // lex an interpolant /#{...}/ + else if (lex< exactly < hash_lbrace > >()) { + // Try to lex static expression first + if (lex< re_static_expression >()) { + (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); + } else { + (*schema) << parse_list(); } - (*schema) << interp_node; + // ToDo: no error check here? + lex < exactly < rbrace > >(); } - else if (lex< exactly<'%'> >()) { - (*schema) << new (ctx.mem) String_Constant(pstate, lexed); + // lex some string constants + else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) { + (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); } - else if (lex< identifier >()) { + // lex a quoted string + else if (lex< quoted_string >()) { (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); } + // lex (normalized) variable + else if (lex< variable >()) { + string name(Util::normalize_underscores(lexed)); + (*schema) << new (ctx.mem) Variable(pstate, name); + } + // lex percentage value else if (lex< percentage >()) { (*schema) << new (ctx.mem) Textual(pstate, Textual::PERCENTAGE, lexed); } + // lex dimension value else if (lex< dimension >()) { (*schema) << new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed); } + // lex number value else if (lex< number >()) { - Expression* factor = new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); - if (peek< class_char< static_ops > >()) { - (*schema) << parse_operators(factor); - } else { - (*schema) << factor; - } + (*schema) << new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); } + // lex hex color value else if (lex< hex >()) { - (*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, unquote(lexed)); - } - else if (lex < exactly < '-' > >()) { - (*schema) << new (ctx.mem) String_Constant(pstate, lexed); - } - else if (lex< quoted_string >()) { - (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); - } - else if (lex< variable >()) { - (*schema) << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); + (*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, lexed); } + // lex a value in parentheses else if (peek< parenthese_scope >()) { (*schema) << parse_factor(); } else { - error("error parsing interpolated value", pstate); + return schema; } ++num_items; } return schema; } + // this parses interpolation outside other strings // means the result must not be quoted again later String* Parser::parse_identifier_schema() { - // first lex away whatever we have found - lex< sequence< optional< exactly<'*'> >, identifier_schema > >(); Token id(lexed); const char* i = id.begin; // see if there any interpolants @@ -1661,7 +1656,7 @@ namespace Sass { if (p) { if (i < p) { // accumulate the preceding segment if it's nonempty - (*schema) << new (ctx.mem) String_Constant(pstate, string(i, p)); + (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p)); } // we need to skip anything inside strings // create a new target in parser/prelexer @@ -1690,6 +1685,7 @@ namespace Sass { return schema; } + // calc functions should preserve arguments Function_Call* Parser::parse_calc_function() { lex< identifier >(); @@ -1700,7 +1696,10 @@ namespace Sass { const char* arg_beg = position; parse_list(); const char* arg_end = position; - lex< exactly<')'> >(); + lex< skip_over_scopes < + exactly < '(' >, + exactly < ')' > + > >(); Argument* arg = new (ctx.mem) Argument(arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end))); Arguments* args = new (ctx.mem) Arguments(arg_pos); @@ -1727,13 +1726,19 @@ namespace Sass { return the_call; } + Content* Parser::parse_content_directive() + { + if (stack.back() != mixin_def) { + error("@content may only be used within a mixin", pstate); + } + return new (ctx.mem) Content(pstate); + } + If* Parser::parse_if_directive(bool else_if) { - lex< kwd_if_directive >() || (else_if && lex< exactly >()); ParserState if_source_position = pstate; Expression* predicate = parse_list(); predicate->is_delayed(false); - if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if", pstate); Block* consequent = parse_block(); Block* alternative = 0; @@ -1742,21 +1747,15 @@ namespace Sass { (*alternative) << parse_if_directive(true); } else if (lex< kwd_else_directive >()) { - if (!peek< exactly<'{'> >()) { - error("expected '{' after @else", pstate); - } - else { - alternative = parse_block(); - } + alternative = parse_block(); } return new (ctx.mem) If(if_source_position, predicate, consequent, alternative); } For* Parser::parse_for_directive() { - lex< kwd_for_directive >(); ParserState for_source_position = pstate; - if (!lex< variable >()) error("@for directive requires an iteration variable", pstate); + lex_variable(); string var(Util::normalize_underscores(lexed)); if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate); Expression* lower_bound = parse_expression(); @@ -1767,17 +1766,41 @@ namespace Sass { else error("expected 'through' or 'to' keyword in @for directive", pstate); Expression* upper_bound = parse_expression(); upper_bound->is_delayed(false); - if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive", pstate); Block* body = parse_block(); return new (ctx.mem) For(for_source_position, var, lower_bound, upper_bound, body, inclusive); } + // helper to parse a var token + Token Parser::lex_variable() + { + // peek for dollar sign first + if (!peek< exactly <'$'> >()) { + css_error("Invalid CSS", " after ", ": expected \"$\", was "); + } + // we expect a simple identfier as the call name + if (!lex< sequence < exactly <'$'>, identifier > >()) { + lex< exactly <'$'> >(); // move pstate and position up + css_error("Invalid CSS", " after ", ": expected identifier, was "); + } + // return object + return token; + } + // helper to parse identifier + Token Parser::lex_identifier() + { + // we expect a simple identfier as the call name + if (!lex< identifier >()) { // ToDo: pstate wrong? + css_error("Invalid CSS", " after ", ": expected identifier, was "); + } + // return object + return token; + } + Each* Parser::parse_each_directive() { - lex < kwd_each_directive >(); ParserState each_source_position = pstate; - if (!lex< variable >()) error("@each directive requires an iteration variable", pstate); vector vars; + lex_variable(); vars.push_back(Util::normalize_underscores(lexed)); while (lex< exactly<','> >()) { if (!lex< variable >()) error("@each directive requires an iteration variable", pstate); @@ -1792,36 +1815,32 @@ namespace Sass { (*l)[i]->is_delayed(false); } } - if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive", pstate); Block* body = parse_block(); return new (ctx.mem) Each(each_source_position, vars, list, body); } + // called after parsing `kwd_while_directive` While* Parser::parse_while_directive() { - lex< kwd_while_directive >(); - ParserState while_source_position = pstate; + // create the initial while call object + While* call = new (ctx.mem) While(pstate, 0, 0); + // parse mandatory predicate Expression* predicate = parse_list(); predicate->is_delayed(false); - Block* body = parse_block(); - return new (ctx.mem) While(while_source_position, predicate, body); + call->predicate(predicate); + // parse mandatory block + call->block(parse_block()); + // return ast node + return call; } + // EO parse_while_directive Media_Block* Parser::parse_media_block() { - lex< kwd_media >(); - ParserState media_source_position = pstate; + Media_Block* media_block = new (ctx.mem) Media_Block(pstate, 0, 0); + media_block->media_queries(parse_media_queries()); - List* media_queries = parse_media_queries(); - - if (!peek< exactly<'{'> >()) { - error("expected '{' in media query", pstate); - } - Media_Block* media_block = new (ctx.mem) Media_Block(media_source_position, media_queries, 0); - Media_Block* prev_media_block = last_media_block; - last_media_block = media_block; - media_block->block(parse_block()); - last_media_block = prev_media_block; + media_block->block(parse_css_block()); return media_block; } @@ -1842,15 +1861,15 @@ namespace Sass { if (lex< exactly< not_kwd > >()) media_query->is_negated(true); else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true); - if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema()); + if (lex < identifier_schema >()) media_query->media_type(parse_identifier_schema()); else if (lex< identifier >()) media_query->media_type(parse_interpolated_chunk(lexed)); else (*media_query) << parse_media_expression(); while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression(); - if (peek< identifier_schema >()) { + if (lex < identifier_schema >()) { String_Schema* schema = new (ctx.mem) String_Schema(pstate); *schema << media_query->media_type(); - *schema << new (ctx.mem) String_Constant(pstate, " "); + *schema << new (ctx.mem) String_Quoted(pstate, " "); *schema << parse_identifier_schema(); media_query->media_type(schema); } @@ -1860,7 +1879,7 @@ namespace Sass { Media_Query_Expression* Parser::parse_media_expression() { - if (peek< identifier_schema >()) { + if (lex < identifier_schema >()) { String* ss = parse_identifier_schema(); return new (ctx.mem) Media_Query_Expression(pstate, ss, 0, true); } @@ -1882,117 +1901,147 @@ namespace Sass { return new (ctx.mem) Media_Query_Expression(feature->pstate(), feature, expression); } + // lexed after `kwd_supports_directive` + // these are very similar to media blocks Supports_Block* Parser::parse_supports_directive() { - lex< kwd_supports_directive >(); - ParserState supports_source_position = pstate; - - Supports_Query* queries = parse_supports_queries(); - - if (!peek< exactly<'{'> >()) { - error("expected '{' in feature query", pstate); - } - Block* block = parse_block(); - - return new (ctx.mem) Supports_Block(supports_source_position, queries, block); + // create the ast node object for the support queries + Supports_Block* query = new (ctx.mem) Supports_Block(pstate); + // now parse the support queries + query->queries(parse_supports_queries()); + // additional block is mandatory + // parse inner block + query->block(parse_block()); + // return ast node + return query; } + // parse multiple queries for supports blocks + // these are very similar to media queries Supports_Query* Parser::parse_supports_queries() { - Supports_Query* fq = new (ctx.mem) Supports_Query(pstate); + // lex optional comments + lex < css_whitespace >(); + // create wrapper object and root condition + Supports_Query* sq = new (ctx.mem) Supports_Query(pstate); Supports_Condition* cond = new (ctx.mem) Supports_Condition(pstate); + // first condition is the root cond->is_root(true); - while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position)) - (*cond) << parse_supports_query(); - (*fq) << cond; - - if (fq->empty()) error("expected @supports condition (e.g. (display: flexbox))", pstate); - - return fq; - } - - Supports_Condition* Parser::parse_supports_query() - { - if (peek< kwd_not >(position)) return parse_supports_negation(); - else if (peek< kwd_and >(position)) return parse_supports_conjunction(); - else if (peek< kwd_or >(position)) return parse_supports_disjunction(); - else if (peek< exactly<'('> >(position)) return parse_feature_query_in_parens(); - else return parse_supports_declaration(); - } - - Supports_Condition* Parser::parse_feature_query_in_parens() - { - Supports_Condition* cond = new (ctx.mem) Supports_Condition(pstate); - - if (!lex< exactly<'('> >()) error("@supports declaration expected '('", pstate); - while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position)) - (*cond) << parse_supports_query(); - if (!lex< exactly<')'> >()) error("unclosed parenthesis in @supports declaration", pstate); - - return (cond->length() == 1) ? (*cond)[0] : cond; - } + // loop until the abort condition + while (!peek < exactly <'{'> >()) + (*cond) << parse_supports_condition(); + // add condition + (*sq) << cond; + // at least one query is mandatory (ToDo: check for ruby sass compat) + if (sq->empty()) error("expected @supports condition (e.g. (display: flexbox))", pstate); + if (!peek_css < exactly <'{'> >()) error("expected \"{\" after @supports declaration", pstate); + // return ast node + return sq; + } + // EO parse_supports_queries Supports_Condition* Parser::parse_supports_negation() { - lex< kwd_not >(); - - Supports_Condition* cond = parse_supports_query(); + Supports_Condition* cond = 0; + cond = parse_supports_condition(); cond->operand(Supports_Condition::NOT); - return cond; } Supports_Condition* Parser::parse_supports_conjunction() { - lex< kwd_and >(); - - Supports_Condition* cond = parse_supports_query(); + Supports_Condition* cond = 0; + cond = parse_supports_condition(); cond->operand(Supports_Condition::AND); - return cond; } Supports_Condition* Parser::parse_supports_disjunction() { - lex< kwd_or >(); - - Supports_Condition* cond = parse_supports_query(); + Supports_Condition* cond = 0; + cond = parse_supports_condition(); cond->operand(Supports_Condition::OR); - return cond; } Supports_Condition* Parser::parse_supports_declaration() { + Supports_Condition* cond = 0; + // parse something declaration like Declaration* declaration = parse_declaration(); - Supports_Condition* cond = new (ctx.mem) Supports_Condition(declaration->pstate(), - 1, - declaration->property(), - declaration->value()); + if (!declaration) error("@supports condition expected declaration", pstate); + cond = new (ctx.mem) Supports_Condition(declaration->pstate(), + 1, + declaration->property(), + declaration->value()); + // ToDo: maybe we need an additional error condition? return cond; } + Supports_Condition* Parser::parse_supports_declaration_in_parens() + { + Supports_Condition* cond = 0; + // create the inner (parenthesis) condition + cond = new (ctx.mem) Supports_Condition(pstate); + // parse inner supports queries recursively + while (!peek < exactly <')'> >()) + (*cond) << parse_supports_condition(); + // at least one query is mandatory (ToDo: check for ruby sass compat) + if (cond->empty()) error("expected @supports condition (e.g. (display: flexbox))", pstate); + // the parenthesis closer is mandatory (ToDo: check for ruby sass compat) + if (!lex_css < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate); + // if we have just one query, we do not wrap it + return (cond->length() == 1) ? (*cond)[0] : cond; + } + + + // parse one query operation + // may encounter nested queries + Supports_Condition* Parser::parse_supports_condition() + { + // lex optional comments + lex < css_whitespace >(); + // parse `not` query operator + if (lex < kwd_not >()) { + return parse_supports_negation(); + } + // parse `and` query operator + else if (lex < kwd_and >()) { + return parse_supports_conjunction(); + } + // parse `or` query operator + else if (lex < kwd_or >()) { + return parse_supports_disjunction(); + } + // parse another list with queries + else if (lex < exactly <'('> >()) { + return parse_supports_declaration_in_parens(); + } + // or parse something declaration like + else { + return parse_supports_declaration(); + } + } + // EO parse_supports_condition + At_Root_Block* Parser::parse_at_root_block() { - lex(); ParserState at_source_position = pstate; Block* body = 0; At_Root_Expression* expr = 0; Lookahead lookahead_result; - in_at_root = true; - if (peek< exactly<'('> >()) { + LOCAL_FLAG(in_at_root, true); + if (lex< exactly<'('> >()) { expr = parse_at_root_expression(); - body = parse_block(); } - else if (peek< exactly<'{'> >()) { - body = parse_block(); + if (peek < exactly<'{'> >()) { + body = parse_block(true); } else if ((lookahead_result = lookahead_for_selector(position)).found) { Ruleset* r = parse_ruleset(lookahead_result); - body = new (ctx.mem) Block(r->pstate(), 1); + body = new (ctx.mem) Block(r->pstate(), 1, true); *body << r; } - in_at_root = false; At_Root_Block* at_root = new (ctx.mem) At_Root_Block(at_source_position, body); if (expr) at_root->expression(expr); return at_root; @@ -2000,7 +2049,6 @@ namespace Sass { At_Root_Expression* Parser::parse_at_root_expression() { - lex< exactly<'('> >(); if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate); if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) { @@ -2024,228 +2072,219 @@ namespace Sass { At_Rule* Parser::parse_at_rule() { - lex(); string kwd(lexed); - ParserState at_source_position = pstate; - Selector* sel = 0; - Expression* val = 0; + At_Rule* at_rule = new (ctx.mem) At_Rule(pstate, kwd); Lookahead lookahead = lookahead_for_include(position); if (lookahead.found) { if (lookahead.has_interpolants) { - sel = parse_selector_schema(lookahead.found); + at_rule->selector(parse_selector_schema(lookahead.found)); } else { - sel = parse_selector_list(); + at_rule->selector(parse_selector_list()); } } - else if (!(peek >() || peek >() || peek >())) { - val = parse_list(); + + lex < css_comments >(); + + if (lex < static_property >()) { + at_rule->value(parse_interpolated_chunk(Token(lexed))); + } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) { + at_rule->value(parse_list()); } - Block* body = 0; - if (peek< exactly<'{'> >()) body = parse_block(); - At_Rule* rule = new (ctx.mem) At_Rule(at_source_position, kwd, sel, body); - if (!sel) rule->value(val); - return rule; + + lex < css_comments >(); + + if (peek< exactly<'{'> >()) { + at_rule->block(parse_block()); + } + + return at_rule; } Warning* Parser::parse_warning() { - lex< kwd_warn >(); return new (ctx.mem) Warning(pstate, parse_list()); } Error* Parser::parse_error() { - lex< kwd_err >(); return new (ctx.mem) Error(pstate, parse_list()); } Debug* Parser::parse_debug() { - lex< kwd_dbg >(); return new (ctx.mem) Debug(pstate, parse_list()); } Return* Parser::parse_return_directive() { - lex< kwd_return_directive >(); return new (ctx.mem) Return(pstate, parse_list()); } Lookahead Parser::lookahead_for_selector(const char* start) { + // init result struct + Lookahead rv { 0 }; + // get start position const char* p = start ? start : position; - const char* q; - bool saw_stuff = false; - bool saw_interpolant = false; - - while ((q = peek< identifier >(p)) || - (q = peek< hyphens_and_identifier >(p)) || - (q = peek< hyphens_and_name >(p)) || - (q = peek< type_selector >(p)) || - (q = peek< id_name >(p)) || - (q = peek< class_name >(p)) || - (q = peek< sequence< pseudo_prefix, identifier > >(p)) || - (q = peek< percentage >(p)) || - (q = peek< variable >(p)) || - (q = peek< dimension >(p)) || - (q = peek< quoted_string >(p)) || - (q = peek< exactly<'*'> >(p)) || - (q = peek< exactly >(p)) || - (q = peek< exactly<'('> >(p)) || - (q = peek< exactly<')'> >(p)) || - (q = peek< exactly<'['> >(p)) || - (q = peek< exactly<']'> >(p)) || - (q = peek< exactly<'+'> >(p)) || - (q = peek< exactly<'~'> >(p)) || - (q = peek< exactly<'>'> >(p)) || - (q = peek< exactly<','> >(p)) || - (saw_stuff && (q = peek< exactly<'-'> >(p))) || - (q = peek< binomial >(p)) || - (q = peek< block_comment >(p)) || - (q = peek< sequence< optional, - zero_plus, - exactly<'n'> > >(p)) || - (q = peek< sequence< optional, - one_plus > >(p)) || - (q = peek< number >(p)) || - (q = peek< sequence< exactly<'&'>, - identifier_alnums > >(p)) || - (q = peek< exactly<'&'> >(p)) || - (q = peek< exactly<'%'> >(p)) || - (q = peek< alternatives >(p)) || - (q = peek< sequence< exactly<'.'>, interpolant > >(p)) || - (q = peek< sequence< exactly<'#'>, interpolant > >(p)) || - (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) || - (q = peek< sequence< pseudo_prefix, interpolant > >(p)) || - (q = peek< interpolant >(p))) { - saw_stuff = true; - p = q; - if (*(p - 1) == '}') saw_interpolant = true; - } - - Lookahead result; - result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0; - result.has_interpolants = saw_interpolant; - - return result; + // match in one big "regex" + rv.error = p; + if (const char* q = + peek < + one_plus < + alternatives < + // consume whitespace and comments + spaces, block_comment, line_comment, + // match `/deep/` selector (pass-trough) + // there is no functionality for it yet + exactly, + // match selector ops /[*&%,()\[\]]/ + class_char < selector_lookahead_ops >, + // match selector combinators /[>+~]/ + class_char < selector_combinator_ops >, + // match attribute compare operators + alternatives < + exact_match, class_match, dash_match, + prefix_match, suffix_match, substring_match + >, + // main selector match + sequence < + // allow namespace prefix + alternatives < + sequence < + exactly <'#'>, + // not for interpolation + negate < exactly <'{'> > + >, + // class match + exactly <'.'>, + // single or double colon + optional < pseudo_prefix > + >, + // accept hypens in token + optional < namespace_prefix >, + // accept hypens in token + one_plus < sequence < + // can start with hyphens + zero_plus < exactly<'-'> >, + // now the main token + alternatives < + kwd_optional, + quoted_string, + interpolant, + identifier, + percentage, + dimension, + variable, + alnum + > + > >, + // can also end with hyphens + zero_plus < exactly<'-'> > + > + > + > + >(p) + ) { + while (p < q) { + // did we have interpolations? + if (*p == '#' && *(p+1) == '{') { + rv.has_interpolants = true; + p = q; break; + } + ++ p; + } + // store anyway } + + + // ToDo: remove + rv.error = q; + rv.position = q; + // check expected opening bracket + // only after successfull matching + if (peek < exactly<'{'> >(q)) rv.found = q; + // else if (peek < exactly<';'> >(q)) rv.found = q; + // else if (peek < exactly<'}'> >(q)) rv.found = q; + if (rv.found || *p == 0) rv.error = 0; + } + + rv.parsable = ! rv.has_interpolants; + + // return result + return rv; + } + // EO lookahead_for_selector + // used in parse_block_nodes and parse_at_rule + // ToDo: actual usage is still not really clear to me? Lookahead Parser::lookahead_for_include(const char* start) { - const char* p = start ? start : position; - const char* q; - bool saw_interpolant = false; - bool saw_stuff = false; - - while ((q = peek< identifier >(p)) || - (q = peek< type_selector >(p)) || - (q = peek< id_name >(p)) || - (q = peek< class_name >(p)) || - (q = peek< sequence< pseudo_prefix, identifier > >(p)) || - (q = peek< percentage >(p)) || - (q = peek< dimension >(p)) || - (q = peek< quoted_string >(p)) || - (q = peek< exactly<'*'> >(p)) || - (q = peek< exactly<'('> >(p)) || - (q = peek< exactly<')'> >(p)) || - (q = peek< exactly<'['> >(p)) || - (q = peek< exactly<']'> >(p)) || - (q = peek< exactly<'+'> >(p)) || - (q = peek< exactly<'~'> >(p)) || - (q = peek< exactly<'>'> >(p)) || - (q = peek< exactly<','> >(p)) || - (saw_stuff && (q = peek< exactly<'-'> >(p))) || - (q = peek< binomial >(p)) || - (q = peek< block_comment >(p)) || - (q = peek< sequence< optional, - zero_plus, - exactly<'n'> > >(p)) || - (q = peek< sequence< optional, - one_plus > >(p)) || - (q = peek< number >(p)) || - (q = peek< sequence< exactly<'&'>, - identifier_alnums > >(p)) || - (q = peek< exactly<'&'> >(p)) || - (q = peek< exactly<'%'> >(p)) || - (q = peek< alternatives >(p)) || - (q = peek< sequence< exactly<'.'>, interpolant > >(p)) || - (q = peek< sequence< exactly<'#'>, interpolant > >(p)) || - (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) || - (q = peek< sequence< pseudo_prefix, interpolant > >(p)) || - (q = peek< interpolant >(p)) || - (q = peek< kwd_optional >(p))) { - p = q; - if (*(p - 1) == '}') saw_interpolant = true; - saw_stuff = true; - } - - Lookahead result; - result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0; - result.has_interpolants = saw_interpolant; - - return result; + // we actually just lookahead for a selector + Lookahead rv = lookahead_for_selector(start); + // but the "found" rules are different + if (const char* p = rv.position) { + // check for additional abort condition + if (peek < exactly<';'> >(p)) rv.found = p; + else if (peek < exactly<'}'> >(p)) rv.found = p; + } + // return result + return rv; } + // EO lookahead_for_include - + // look ahead for a token with interpolation in it + // we mostly use the result if there is an interpolation + // everything that passes here gets parsed as one schema + // meaning it will not be parsed as a space separated list Lookahead Parser::lookahead_for_value(const char* start) { + // init result struct + Lookahead rv { 0 }; + // get start position const char* p = start ? start : position; - const char* q; - bool saw_interpolant = false; - bool saw_stuff = false; - - while ((q = peek< identifier >(p)) || - (q = peek< percentage >(p)) || - (q = peek< dimension >(p)) || - (q = peek< quoted_string >(p)) || - (q = peek< variable >(p)) || - (q = peek< exactly<'*'> >(p)) || - (q = peek< exactly<'+'> >(p)) || - (q = peek< exactly<'~'> >(p)) || - (q = peek< exactly<'>'> >(p)) || - (q = peek< exactly<','> >(p)) || - (q = peek< sequence>(p)) || - (saw_stuff && (q = peek< exactly<'-'> >(p))) || - (q = peek< binomial >(p)) || - (q = peek< block_comment >(p)) || - (q = peek< sequence< optional, - zero_plus, - exactly<'n'> > >(p)) || - (q = peek< sequence< optional, - one_plus > >(p)) || - (q = peek< number >(p)) || - (q = peek< sequence< exactly<'&'>, - identifier_alnums > >(p)) || - (q = peek< exactly<'&'> >(p)) || - (q = peek< exactly<'%'> >(p)) || - (q = peek< sequence< exactly<'.'>, interpolant > >(p)) || - (q = peek< sequence< exactly<'#'>, interpolant > >(p)) || - (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) || - (q = peek< sequence< pseudo_prefix, interpolant > >(p)) || - (q = peek< interpolant >(p)) || - (q = peek< kwd_optional >(p))) { - p = q; - if (*(p - 1) == '}') saw_interpolant = true; - saw_stuff = true; - } - - Lookahead result; - result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0; - result.has_interpolants = saw_interpolant; - - return result; + // match in one big "regex" + if (const char* q = + peek < + one_plus < + alternatives < + // consume whitespace + block_comment, spaces, + // main tokens + interpolant, + identifier, + variable, + // issue #442 + sequence < + parenthese_scope, + interpolant + > + > + > + >(p) + ) { + while (p < q) { + // did we have interpolations? + if (*p == '#' && *(p+1) == '{') { + rv.has_interpolants = true; + p = q; break; + } + ++ p; + } + // store anyway + // ToDo: remove + rv.position = q; + // check expected opening bracket + // only after successfull matching + if (peek < exactly<'{'> >(q)) rv.found = q; + else if (peek < exactly<';'> >(q)) rv.found = q; + else if (peek < exactly<'}'> >(q)) rv.found = q; + } + + // return result + return rv; } + // EO lookahead_for_value void Parser::read_bom() { @@ -2358,7 +2397,8 @@ namespace Sass { int max_len = 14; const char* pos = peek < optional_spaces >(); bool ellipsis_left = false; - const char* pos_left(pos); + const char* pos_left(pos - 1); + if (pos_left < source) pos_left = source; while (*pos_left && pos_left > source) { if (pos - pos_left > max_len) { ellipsis_left = true; diff --git a/parser.hpp b/parser.hpp index 0a8303d47f..8be46d43b6 100644 --- a/parser.hpp +++ b/parser.hpp @@ -13,7 +13,9 @@ struct Lookahead { const char* found; + const char* error; const char* position; + bool parsable; bool has_interpolants; }; @@ -35,7 +37,6 @@ namespace Sass { Context& ctx; vector block_stack; vector stack; - Media_Block* last_media_block; const char* source; const char* position; const char* end; @@ -49,7 +50,7 @@ namespace Sass { bool in_at_root; Parser(Context& ctx, const ParserState& pstate) - : ParserState(pstate), ctx(ctx), block_stack(0), stack(0), last_media_block(0), + : ParserState(pstate), ctx(ctx), block_stack(0), stack(0), source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0) { in_at_root = false; stack.push_back(nothing); } @@ -204,7 +205,7 @@ namespace Sass { Block* parse(); Import* parse_import(); - Definition* parse_definition(); + Definition* parse_definition(Definition::Type which_type); Parameters* parse_parameters(); Parameter* parse_parameter(); Mixin_Call* parse_include_directive(); @@ -214,14 +215,18 @@ namespace Sass { // Propset* parse_propset(); Ruleset* parse_ruleset(Lookahead lookahead); Selector_Schema* parse_selector_schema(const char* end_of_selector); - Selector_List* parse_selector_list(); - Complex_Selector* parse_complex_selector(); + Selector_List* parse_selector_list(bool at_root = false); + Complex_Selector* parse_complex_selector(bool in_root = true); Compound_Selector* parse_compound_selector(); Simple_Selector* parse_simple_selector(); Wrapped_Selector* parse_negated_selector(); Simple_Selector* parse_pseudo_selector(); Attribute_Selector* parse_attribute_selector(); - Block* parse_block(); + Block* parse_block(bool is_root = false); + Block* parse_css_block(bool is_root = false); + bool parse_block_nodes(); + bool parse_block_node(); + bool parse_number_prefix(); Declaration* parse_declaration(); Expression* parse_map_value(); @@ -235,6 +240,7 @@ namespace Sass { Expression* parse_expression(); Expression* parse_operators(); Expression* parse_factor(); + Expression* parse_value2(); Expression* parse_value(); Function_Call* parse_calc_function(); Function_Call* parse_function_call(); @@ -242,11 +248,11 @@ namespace Sass { String* parse_interpolated_chunk(Token, bool constant = false); String* parse_string(); String_Constant* parse_static_expression(); + // String_Constant* parse_static_property(); String_Constant* parse_static_value(); String* parse_ie_property(); String* parse_ie_keyword_arg(); String_Schema* parse_value_schema(const char* stop); - Expression* parse_operators(Expression* factor); String* parse_identifier_schema(); // String_Schema* parse_url_schema(); If* parse_if_directive(bool else_if = false); @@ -254,18 +260,20 @@ namespace Sass { Each* parse_each_directive(); While* parse_while_directive(); Return* parse_return_directive(); + Content* parse_content_directive(); + void parse_charset_directive(); Media_Block* parse_media_block(); List* parse_media_queries(); Media_Query* parse_media_query(); Media_Query_Expression* parse_media_expression(); Supports_Block* parse_supports_directive(); Supports_Query* parse_supports_queries(); - Supports_Condition* parse_supports_query(); - Supports_Condition* parse_feature_query_in_parens(); + Supports_Condition* parse_supports_condition(); Supports_Condition* parse_supports_negation(); Supports_Condition* parse_supports_conjunction(); Supports_Condition* parse_supports_disjunction(); Supports_Condition* parse_supports_declaration(); + Supports_Condition* parse_supports_declaration_in_parens(); At_Root_Block* parse_at_root_block(); At_Root_Expression* parse_at_root_expression(); At_Rule* parse_at_rule(); @@ -273,7 +281,11 @@ namespace Sass { Error* parse_error(); Debug* parse_debug(); - void parse_block_comments(Block* block); + // these will throw errors + Token lex_variable(); + Token lex_identifier(); + + void parse_block_comments(); Lookahead lookahead_for_value(const char* start = 0); Lookahead lookahead_for_selector(const char* start = 0); diff --git a/prelexer.cpp b/prelexer.cpp index ca65af561b..fe00a40076 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -258,10 +258,14 @@ namespace Sass { return word(src); } - const char* kwd_content(const char* src) { + const char* kwd_content_directive(const char* src) { return word(src); } + const char* kwd_charset_directive(const char* src) { + return word(src); + } + const char* kwd_extend(const char* src) { return word(src); } @@ -339,10 +343,7 @@ namespace Sass { // Match CSS type selectors const char* namespace_prefix(const char* src) { return sequence< optional< alternatives< identifier, exactly<'*'> > >, - exactly<'|'> >(src); - } - const char* type_selector(const char* src) { - return sequence< optional, identifier>(src); + exactly<'|'>, negate> >(src); } const char* hyphens_and_identifier(const char* src) { return sequence< zero_plus< exactly< '-' > >, identifier >(src); @@ -389,13 +390,15 @@ namespace Sass { sign >(src); } const char* binomial(const char* src) { - return sequence< optional, - optional, - exactly<'n'>, - zero_plus < space >, - sign, - zero_plus < space >, - digits >(src); + return sequence < + optional < sign >, + optional < digits >, + exactly <'n'>, + zero_plus < sequence < + optional_css_whitespace, sign, + optional_css_whitespace, digits + > > + >(src); } const char* percentage(const char* src) { return sequence< number, exactly<'%'> >(src); @@ -504,8 +507,13 @@ namespace Sass { } // Match CSS function call openers. const char* functional_schema(const char* src) { - return sequence< identifier_schema, exactly<'('> >(src); + return sequence< identifier_schema, lookahead < exactly<'('> > >(src); } + + const char* re_nothing(const char* src) { + return src; + } + const char* re_pseudo_selector(const char* src) { return sequence< identifier, optional < block_comment >, exactly<'('> >(src); } @@ -748,6 +756,44 @@ namespace Sass { >(src); } + const char* static_property(const char* src) { + return + sequence < + zero_plus< + sequence < + optional_css_comments, + alternatives < + exactly<','>, + exactly<'('>, + exactly<')'>, + kwd_optional, + quoted_string, + interpolant, + identifier, + percentage, + dimension, + variable, + alnum, + sequence < + exactly <'\\'>, + any_char + > + > + > + >, + lookahead < + sequence < + optional_css_comments, + alternatives < + exactly <';'>, + exactly <'}'>, + end_of_file + > + > + > + >(src); + } + const char* static_value(const char* src) { return sequence< sequence< static_component, @@ -779,5 +825,18 @@ namespace Sass { >(src); } + const char* type_selector(const char* src) { + return sequence< optional, identifier>(src); + } + const char* re_type_selector(const char* src) { + return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src); + } + const char* re_type_selector2(const char* src) { + return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src); + } + const char* re_static_expression(const char* src) { + return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src); + } + } } diff --git a/prelexer.hpp b/prelexer.hpp index 333a5fc9b1..cc466f8b73 100644 --- a/prelexer.hpp +++ b/prelexer.hpp @@ -222,7 +222,8 @@ namespace Sass { const char* kwd_function(const char* src); const char* kwd_return_directive(const char* src); const char* kwd_include_directive(const char* src); - const char* kwd_content(const char* src); + const char* kwd_content_directive(const char* src); + const char* kwd_charset_directive(const char* src); const char* kwd_extend(const char* src); const char* kwd_if_directive(const char* src); @@ -239,12 +240,19 @@ namespace Sass { const char* kwd_while_directive(const char* src); + const char* re_nothing(const char* src); + const char* re_type_selector2(const char* src); + + const char* kwd_warn(const char* src); const char* kwd_err(const char* src); const char* kwd_dbg(const char* src); const char* kwd_null(const char* src); + const char* re_type_selector(const char* src); + const char* re_static_expression(const char* src); + // Match CSS type selectors const char* namespace_prefix(const char* src); const char* type_selector(const char* src); @@ -325,6 +333,7 @@ namespace Sass { const char* static_string(const char* src); const char* static_component(const char* src); + const char* static_property(const char* src); const char* static_value(const char* src); // Utility functions for finding and counting characters in a string. diff --git a/remove_placeholders.cpp b/remove_placeholders.cpp index fa8ce94580..a22d46566c 100644 --- a/remove_placeholders.cpp +++ b/remove_placeholders.cpp @@ -10,9 +10,13 @@ namespace Sass { : ctx(ctx) { } - template - void Remove_Placeholders::clean_selector_list(T r) { + void Remove_Placeholders::operator()(Block* b) { + for (size_t i = 0, L = b->length(); i < L; ++i) { + (*b)[i]->perform(this); + } + } + void Remove_Placeholders::operator()(Ruleset* r) { // Create a new selector group without placeholders Selector_List* sl = static_cast(r->selector()); @@ -20,7 +24,7 @@ namespace Sass { Selector_List* new_sl = new (ctx.mem) Selector_List(sl->pstate()); for (size_t i = 0, L = sl->length(); i < L; ++i) { - if (!(*sl)[i]->has_placeholder()) { + if (!(*sl)[i]->contains_placeholder()) { *new_sl << (*sl)[i]; } } @@ -33,25 +37,17 @@ namespace Sass { Block* b = r->block(); for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - stm->perform(this); + if ((*b)[i]) (*b)[i]->perform(this); } } - void Remove_Placeholders::operator()(Block* b) { + void Remove_Placeholders::operator()(Media_Block* m) { + Block* b = m->block(); for (size_t i = 0, L = b->length(); i < L; ++i) { - (*b)[i]->perform(this); + if ((*b)[i]) (*b)[i]->perform(this); } } - void Remove_Placeholders::operator()(Ruleset* r) { - clean_selector_list(r); - } - - void Remove_Placeholders::operator()(Media_Block* m) { - clean_selector_list(m); - } - void Remove_Placeholders::operator()(At_Rule* a) { if (a->block()) a->block()->perform(this); } diff --git a/remove_placeholders.hpp b/remove_placeholders.hpp index 5545dff9fb..df2b15afb6 100644 --- a/remove_placeholders.hpp +++ b/remove_placeholders.hpp @@ -31,9 +31,6 @@ namespace Sass { void operator()(Media_Block*); void operator()(At_Rule*); - template - void clean_selector_list(T r); - template void fallback(U x) { return fallback_impl(x); } }; diff --git a/sass.h b/sass.h index cb5eef2514..1f5e3e6752 100644 --- a/sass.h +++ b/sass.h @@ -1,10 +1,13 @@ #ifndef SASS_H #define SASS_H +// #define DEBUG 1 + #ifdef _MSC_VER -#define _SCL_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#define _CRT_NONSTDC_NO_DEPRECATE + #pragma warning(disable : 4503) + #define _SCL_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #define _CRT_NONSTDC_NO_DEPRECATE #endif #include @@ -24,10 +27,10 @@ /* You should define ADD_EXPORTS *only* when building the DLL. */ #ifdef ADD_EXPORTS #define ADDAPI __declspec(dllexport) - #define ADDCALL __cdecl + #define ADDCALL __cdecl #else #define ADDAPI - #define ADDCALL + #define ADDCALL #endif #else /* _WIN32 not defined. */ diff --git a/sass_util.cpp b/sass_util.cpp index fda191aeff..9e8abf3d6a 100644 --- a/sass_util.cpp +++ b/sass_util.cpp @@ -5,8 +5,8 @@ namespace Sass { /* - This is the equivalent of ruby's Sass::Util.paths. - + # This is the equivalent of ruby's Sass::Util.paths. + # # Return an array of all possible paths through the given arrays. # # @param arrs [NodeCollection>] @@ -23,7 +23,7 @@ namespace Sass { should be able to drop it into ruby 3.2.19 and get the same results from ruby sass. def paths(arrs) - // I changed the inject and maps to an iterative approach to make it easier to implement in C++ + // I changed the inject and maps to an iterative approach to make it easier to implement in C++ loopStart = [[]] for arr in arrs do @@ -36,7 +36,7 @@ namespace Sass { loopStart = permutations end end - */ + */ Node paths(const Node& arrs, Context& ctx) { To_String to_string(&ctx); @@ -61,6 +61,7 @@ namespace Sass { Node& path = *loopStartIter; Node newPermutation = Node::createCollection(); + newPermutation.got_line_feed = arr.got_line_feed; newPermutation.plus(path); newPermutation.collection()->push_back(e); @@ -75,7 +76,7 @@ namespace Sass { } - /* + /* This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten. Sass::Util.flatten requires the number of levels to flatten, while [].flatten doesn't and will flatten the entire array. This function @@ -114,14 +115,31 @@ namespace Sass { } Node flattened = Node::createCollection(); + if (arr.got_line_feed) flattened.got_line_feed = true; for (NodeDeque::iterator iter = arr.collection()->begin(), iterEnd = arr.collection()->end(); iter != iterEnd; iter++) { Node& e = *iter; + // e has the lf set if (e.isCollection()) { + + // e.collection().got_line_feed = e.got_line_feed; Node recurseFlattened = flatten(e, ctx, n - 1); - flattened.collection()->insert(flattened.collection()->end(), recurseFlattened.collection()->begin(), recurseFlattened.collection()->end()); + + if(e.got_line_feed) { + flattened.got_line_feed = e.got_line_feed; + recurseFlattened.got_line_feed = e.got_line_feed; + } + + for(auto i : (*recurseFlattened.collection())) { + if (recurseFlattened.got_line_feed) { + + i.got_line_feed = true; + } + flattened.collection()->push_back(i); + } + } else { flattened.collection()->push_back(e); } diff --git a/test/test_node.cpp b/test/test_node.cpp index 88ba1b2122..e265d8b57a 100644 --- a/test/test_node.cpp +++ b/test/test_node.cpp @@ -10,16 +10,16 @@ namespace Sass { - + Context ctx = Context::Data(); - + To_String to_string; - - + + const char* const ROUNDTRIP_TESTS[] = { NULL, - "~", - "CMPD", + "~", + "CMPD", "~ CMPD", "CMPD >", "> > CMPD", @@ -29,15 +29,15 @@ namespace Sass { "+ CMPD1 CMPD2 ~ CMPD3 + CMPD4 > CMPD5 > ~" }; - - + + static Complex_Selector* createComplexSelector(string src) { string temp(src); temp += ";"; return (*Parser::from_c_str(temp.c_str(), ctx, "", Position()).parse_selector_list())[0]; } - - + + void roundtripTest(const char* toTest) { // Create the initial selector @@ -46,51 +46,51 @@ namespace Sass { if (toTest) { pOrigSelector = createComplexSelector(toTest); } - + string expected(pOrigSelector ? pOrigSelector->perform(&to_string) : "NULL"); - - + + // Roundtrip the selector into a node and back - + Node node = complexSelectorToNode(pOrigSelector, ctx); - + stringstream nodeStringStream; nodeStringStream << node; string nodeString = nodeStringStream.str(); cout << "ASNODE: " << node << endl; - + Complex_Selector* pNewSelector = nodeToComplexSelector(node, ctx); - + // Show the result string result(pNewSelector ? pNewSelector->perform(&to_string) : "NULL"); - + cout << "SELECTOR: " << expected << endl; cout << "NEW SELECTOR: " << result << endl; - + // Test that they are equal using the equality operator - + assert( (!pOrigSelector && !pNewSelector ) || (pOrigSelector && pNewSelector) ); if (pOrigSelector) { - assert( *pOrigSelector == *pNewSelector ); + assert( *pOrigSelector == *pNewSelector ); } - + // Test that they are equal by comparing the string versions of the selectors assert(expected == result); - + } - int main() { + int main() { for (int index = 0; index < STATIC_ARRAY_SIZE(ROUNDTRIP_TESTS); index++) { const char* const toTest = ROUNDTRIP_TESTS[index]; cout << "\nINPUT STRING: " << (toTest ? toTest : "NULL") << endl; roundtripTest(toTest); } - + cout << "\nTesting Done.\n"; } diff --git a/test/test_superselector.cpp b/test/test_superselector.cpp index 8e25389a7b..b32967c5a8 100644 --- a/test/test_superselector.cpp +++ b/test/test_superselector.cpp @@ -13,7 +13,7 @@ Compound_Selector* compound_selector(string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } Complex_Selector* complex_selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_complex_selector(); } +{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_complex_selector(false); } void check_compound(string s1, string s2) { diff --git a/to_string.cpp b/to_string.cpp index fde1fe5d5d..2c7048737e 100644 --- a/to_string.cpp +++ b/to_string.cpp @@ -24,9 +24,13 @@ namespace Sass { return i.get_buffer(); } - inline string To_String::operator()(String_Constant* s) + inline string To_String::operator()(String_Schema* s) { - return s->value(); + string acc(""); + for (size_t i = 0, L = s->length(); i < L; ++i) { + acc += s->elements()[i]->perform(this); + } + return acc; } inline string To_String::operator()(String_Quoted* s) @@ -34,6 +38,11 @@ namespace Sass { return s->value(); } + inline string To_String::operator()(String_Constant* s) + { + return s->value(); + } + inline string To_String::operator()(Null* n) { return ""; } } diff --git a/to_string.hpp b/to_string.hpp index 38c2a118f2..a8f0af4cae 100644 --- a/to_string.hpp +++ b/to_string.hpp @@ -25,6 +25,8 @@ namespace Sass { virtual ~To_String(); string operator()(Null* n); + string operator()(String_Schema*); + string operator()(String_Quoted*); string operator()(String_Constant*); template diff --git a/utf8/core.h b/utf8/core.h index 693d388c07..f85081f8ff 100644 --- a/utf8/core.h +++ b/utf8/core.h @@ -116,15 +116,15 @@ namespace internal inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) { if (cp < 0x80) { - if (length != 1) + if (length != 1) return true; } else if (cp < 0x800) { - if (length != 2) + if (length != 2) return true; } else if (cp < 0x10000) { - if (length != 3) + if (length != 3) return true; } @@ -142,11 +142,11 @@ namespace internal if (!utf8::internal::is_trail(*it)) return INCOMPLETE_SEQUENCE; - + return UTF8_OK; } - #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} + #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} /// get_sequence_x functions decode utf-8 sequences of the length x template @@ -163,9 +163,9 @@ namespace internal template utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) { - if (it == end) + if (it == end) return NOT_ENOUGH_ROOM; - + code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) @@ -180,7 +180,7 @@ namespace internal { if (it == end) return NOT_ENOUGH_ROOM; - + code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) @@ -234,7 +234,7 @@ namespace internal // Get trail octets and calculate the code point utf_error err = UTF8_OK; switch (length) { - case 0: + case 0: return INVALID_LEAD; case 1: err = utf8::internal::get_sequence_1(it, end, cp); @@ -262,7 +262,7 @@ namespace internal else err = OVERLONG_SEQUENCE; } - else + else err = INVALID_CODE_POINT; } @@ -311,8 +311,8 @@ namespace internal ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) ); } - - //Deprecated in release 2.3 + + //Deprecated in release 2.3 template inline bool is_bom (octet_iterator it) { diff --git a/util.cpp b/util.cpp index e440facc90..a480319f12 100644 --- a/util.cpp +++ b/util.cpp @@ -1,4 +1,4 @@ -#include +#include "sass.h" #include "ast.hpp" #include "util.hpp" #include "lexer.hpp" @@ -6,6 +6,8 @@ #include "constants.hpp" #include "utf8/checked.h" +#include + namespace Sass { #define out_of_memory() do { \ @@ -470,6 +472,7 @@ namespace Sass { exactly <' '>, exactly <'\t'>, line_comment, + block_comment, delimited_by < slash_star, star_slash, @@ -588,13 +591,13 @@ namespace Sass { Block* b = f->block(); - bool hasSelectors = f->selector() && static_cast(f->selector())->length() > 0; +// bool hasSelectors = f->selector() && static_cast(f->selector())->length() > 0; bool hasDeclarations = false; bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (!stm->is_hoistable() && f->selector() != NULL && !hasSelectors) { + if (!stm->is_hoistable()) { // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0), // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with @@ -618,40 +621,19 @@ namespace Sass { return false; } - bool isPrintable(Media_Block* m, Output_Style style) { - if (m == NULL) { - return false; - } - + bool isPrintable(Media_Block* m, Output_Style style) + { + if (m == 0) return false; Block* b = m->block(); - - bool hasSelectors = m->selector() && static_cast(m->selector())->length() > 0; - - bool hasDeclarations = false; - bool hasPrintableChildBlocks = false; + if (b == 0) return false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (!stm->is_hoistable() && m->selector() != NULL && !hasSelectors) { - // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0), - // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector - // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with - // a declaration in it). - } - else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { - hasDeclarations = true; - } - else if (dynamic_cast(stm)) { - Block* pChildBlock = ((Has_Block*)stm)->block(); - if (isPrintable(pChildBlock, style)) { - hasPrintableChildBlocks = true; - } - } - - if (hasDeclarations || hasPrintableChildBlocks) { - return true; + if (typeid(*stm) == typeid(At_Rule)) return true; + if (typeid(*stm) == typeid(Declaration)) return true; + if (Has_Block* child = dynamic_cast(stm)) { + if (isPrintable(child->block(), style)) return true; } } - return false; } diff --git a/win/libsass.filters b/win/libsass.filters index 4e1838931d..c6851d7ab8 100644 --- a/win/libsass.filters +++ b/win/libsass.filters @@ -36,9 +36,6 @@ Source Files - - Source Files - Source Files @@ -179,9 +176,6 @@ Header Files - - Header Files - Header Files diff --git a/win/libsass.vcxproj b/win/libsass.vcxproj index a6cb1cb4bc..ea08df82f0 100644 --- a/win/libsass.vcxproj +++ b/win/libsass.vcxproj @@ -167,8 +167,6 @@ - - @@ -214,8 +212,6 @@ - - From 7514f0e042b8537711e099d39153831e30a68928 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 15 Jun 2015 01:57:40 +0200 Subject: [PATCH 05/10] Improve At_Rule parsing (with interpolations) Fixes https://github.com/sass/libsass/issues/1263 --- parser.cpp | 9 ++------- util.cpp | 4 +++- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/parser.cpp b/parser.cpp index 1c6531a885..00fa045d54 100644 --- a/parser.cpp +++ b/parser.cpp @@ -2075,13 +2075,8 @@ namespace Sass { string kwd(lexed); At_Rule* at_rule = new (ctx.mem) At_Rule(pstate, kwd); Lookahead lookahead = lookahead_for_include(position); - if (lookahead.found) { - if (lookahead.has_interpolants) { - at_rule->selector(parse_selector_schema(lookahead.found)); - } - else { - at_rule->selector(parse_selector_list()); - } + if (lookahead.found && !lookahead.has_interpolants) { + at_rule->selector(parse_selector_list()); } lex < css_comments >(); diff --git a/util.cpp b/util.cpp index a480319f12..f605f81d32 100644 --- a/util.cpp +++ b/util.cpp @@ -537,7 +537,9 @@ namespace Sass { bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { + if (dynamic_cast(stm)) { + return true; + } else if (dynamic_cast(stm)) { Block* pChildBlock = ((Has_Block*)stm)->block(); if (isPrintable(pChildBlock, style)) { hasPrintableChildBlocks = true; From 5312136634c336e9f0b21c4e40935c49c74b9cbb Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 7 Apr 2015 03:00:46 +0200 Subject: [PATCH 06/10] Implement static selector namespace handling Missing implementation for interpolated namespaces! --- ast.hpp | 44 ++++++++++++++++++++++++++++---------------- inspect.cpp | 8 ++++---- parser.cpp | 5 +++-- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/ast.hpp b/ast.hpp index 6156e799e6..1445dc3180 100644 --- a/ast.hpp +++ b/ast.hpp @@ -1729,10 +1729,27 @@ namespace Sass { // Abstract base class for simple selectors. //////////////////////////////////////////// class Simple_Selector : public Selector { + ADD_PROPERTY(string, ns); + ADD_PROPERTY(string, name) public: - Simple_Selector(ParserState pstate) - : Selector(pstate) - { } + Simple_Selector(ParserState pstate, string n = "") + : Selector(pstate), ns_(""), name_(n) + { + size_t pos = n.find('|'); + // found some namespace + if (pos != string::npos) { + ns_ = n.substr(0, pos); + name_ = n.substr(pos + 1); + } + } + virtual string ns_name() const + { + string name(""); + if (!ns_.empty()) + name += ns_ + "|"; + return name + name_; + } + virtual ~Simple_Selector() = 0; virtual Compound_Selector* unify_with(Compound_Selector*, Context&); virtual bool has_parent_ref() { return false; }; @@ -1745,6 +1762,7 @@ namespace Sass { inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); } bool operator<(const Simple_Selector& rhs) const; + ATTACH_OPERATIONS(); }; inline Simple_Selector::~Simple_Selector() { } @@ -1758,7 +1776,7 @@ namespace Sass { class Parent_Selector : public Simple_Selector { public: Parent_Selector(ParserState pstate) - : Simple_Selector(pstate) + : Simple_Selector(pstate, "&") { has_reference(true); } virtual bool has_parent_ref() { return true; }; virtual unsigned long specificity() @@ -1774,10 +1792,9 @@ namespace Sass { // Placeholder selectors (e.g., "%foo") for use in extend-only selectors. ///////////////////////////////////////////////////////////////////////// class Selector_Placeholder : public Simple_Selector { - ADD_PROPERTY(string, name) public: Selector_Placeholder(ParserState pstate, string n) - : Simple_Selector(pstate), name_(n) + : Simple_Selector(pstate, n) { has_placeholder(true); } // virtual Selector_Placeholder* find_placeholder(); ATTACH_OPERATIONS() @@ -1787,10 +1804,9 @@ namespace Sass { // Type selectors (and the universal selector) -- e.g., div, span, *. ///////////////////////////////////////////////////////////////////// class Type_Selector : public Simple_Selector { - ADD_PROPERTY(string, name) public: Type_Selector(ParserState pstate, string n) - : Simple_Selector(pstate), name_(n) + : Simple_Selector(pstate, n) { } virtual unsigned long specificity() { @@ -1806,10 +1822,9 @@ namespace Sass { // Selector qualifiers -- i.e., classes and ids. //////////////////////////////////////////////// class Selector_Qualifier : public Simple_Selector { - ADD_PROPERTY(string, name) public: Selector_Qualifier(ParserState pstate, string n) - : Simple_Selector(pstate), name_(n) + : Simple_Selector(pstate, n) { } virtual unsigned long specificity() { @@ -1825,12 +1840,11 @@ namespace Sass { // Attribute selectors -- e.g., [src*=".jpg"], etc. /////////////////////////////////////////////////// class Attribute_Selector : public Simple_Selector { - ADD_PROPERTY(string, name) ADD_PROPERTY(string, matcher) ADD_PROPERTY(String*, value) // might be interpolated public: Attribute_Selector(ParserState pstate, string n, string m, String* v) - : Simple_Selector(pstate), name_(n), matcher_(m), value_(v) + : Simple_Selector(pstate, n), matcher_(m), value_(v) { } virtual unsigned long specificity() { @@ -1855,11 +1869,10 @@ namespace Sass { } class Pseudo_Selector : public Simple_Selector { - ADD_PROPERTY(string, name) ADD_PROPERTY(String*, expression) public: Pseudo_Selector(ParserState pstate, string n, String* expr = 0) - : Simple_Selector(pstate), name_(n), expression_(expr) + : Simple_Selector(pstate, n), expression_(expr) { } // A pseudo-class always consists of a "colon" (:) followed by the name @@ -1897,11 +1910,10 @@ namespace Sass { // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir) ///////////////////////////////////////////////// class Wrapped_Selector : public Simple_Selector { - ADD_PROPERTY(string, name) ADD_PROPERTY(Selector*, selector) public: Wrapped_Selector(ParserState pstate, string n, Selector* sel) - : Simple_Selector(pstate), name_(n), selector_(sel) + : Simple_Selector(pstate, n), selector_(sel) { } virtual bool is_superselector_of(Wrapped_Selector* sub); // Selectors inside the negation pseudo-class are counted like any diff --git a/inspect.cpp b/inspect.cpp index c48c265934..90bd3bed81 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -837,12 +837,12 @@ namespace Sass { void Inspect::operator()(Type_Selector* s) { - append_token(s->name(), s); + append_token(s->ns_name(), s); } void Inspect::operator()(Selector_Qualifier* s) { - append_token(s->name(), s); + append_token(s->ns_name(), s); if (s->has_line_break()) append_optional_linefeed(); if (s->has_line_break()) append_indentation(); } @@ -851,7 +851,7 @@ namespace Sass { { append_string("["); add_open_mapping(s); - append_token(s->name(), s); + append_token(s->ns_name(), s); if (!s->matcher().empty()) { append_string(s->matcher()); if (s->value()) { @@ -864,7 +864,7 @@ namespace Sass { void Inspect::operator()(Pseudo_Selector* s) { - append_token(s->name(), s); + append_token(s->ns_name(), s); if (s->expression()) { append_string("("); s->expression()->perform(this); diff --git a/parser.cpp b/parser.cpp index 00fa045d54..80a52015c7 100644 --- a/parser.cpp +++ b/parser.cpp @@ -2145,6 +2145,8 @@ namespace Sass { // main selector match sequence < // allow namespace prefix + optional < namespace_prefix >, + // modifiers prefixes alternatives < sequence < exactly <'#'>, @@ -2157,14 +2159,13 @@ namespace Sass { optional < pseudo_prefix > >, // accept hypens in token - optional < namespace_prefix >, - // accept hypens in token one_plus < sequence < // can start with hyphens zero_plus < exactly<'-'> >, // now the main token alternatives < kwd_optional, + exactly <'*'>, quoted_string, interpolant, identifier, From 38d356e2b84abbd27afb8d9ea8b4e13f6fcff3e7 Mon Sep 17 00:00:00 2001 From: Eric Kimn Date: Tue, 30 Jun 2015 00:58:57 -0700 Subject: [PATCH 07/10] Fix for null selectors and respecting specificity. --- extend.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extend.cpp b/extend.cpp index e0213ea2fc..7c57ed61bf 100644 --- a/extend.cpp +++ b/extend.cpp @@ -477,7 +477,7 @@ namespace Sass { /* - IMPROVEMENT: We could probably work directly in the output trimmed deque. */ - static Node trim(Node& seqses, Context& ctx) { + static Node trim(Node& seqses, Context& ctx, bool isReplace) { // See the comments in the above ruby code before embarking on understanding this function. // Avoid poor performance in extreme cases. @@ -520,7 +520,7 @@ namespace Sass { // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely // a guess though. - unsigned long maxSpecificity = 0; + unsigned long maxSpecificity = isReplace ? pSeq1->specificity() : 0; SourcesSet sources = pSeq1->sources(); DEBUG_PRINTLN(TRIM, "TRIMASDF SEQ1: " << seq1) @@ -1461,7 +1461,7 @@ namespace Sass { Complex_Selector* pComplexSelector, Context& ctx, ExtensionSubsetMap& subset_map, - set seen); + set seen, bool isReplace); @@ -1489,7 +1489,7 @@ namespace Sass { Compound_Selector* pSelector, Context& ctx, ExtensionSubsetMap& subset_map, - set seen) { + set seen, bool isReplace) { DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: ")) @@ -1629,7 +1629,7 @@ namespace Sass { DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx)) - Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen); + Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen, isReplace); DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors) @@ -1740,7 +1740,7 @@ namespace Sass { Complex_Selector* pComplexSelector, Context& ctx, ExtensionSubsetMap& subset_map, - set seen) { + set seen, bool isReplace) { Node complexSelector = complexSelectorToNode(pComplexSelector, ctx); DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) @@ -1766,7 +1766,7 @@ namespace Sass { Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head(); - Node extended = extendCompoundSelector(pCompoundSelector, ctx, subset_map, seen); + Node extended = extendCompoundSelector(pCompoundSelector, ctx, subset_map, seen, isReplace); if (sseqOrOp.got_line_feed) extended.got_line_feed = true; DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended) @@ -1824,7 +1824,7 @@ namespace Sass { // Ruby Equivalent: trim - Node trimmed = trim(weaves, ctx); + Node trimmed = trim(weaves, ctx, isReplace); DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed) @@ -1871,7 +1871,7 @@ namespace Sass { set seen; - Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen); + Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen, isReplace); if (!pSelector->has_placeholder()) { if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) { *pNewSelectors << pSelector; From 4767390f2d1c0e494375dbbd0ffa2105b244bbad Mon Sep 17 00:00:00 2001 From: Eric Kimn Date: Tue, 30 Jun 2015 00:36:35 -0700 Subject: [PATCH 08/10] Fixing child selector repetition --- eval.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/eval.cpp b/eval.cpp index 923c37154a..eb1a8c60e4 100644 --- a/eval.cpp +++ b/eval.cpp @@ -1453,9 +1453,14 @@ namespace Sass { if (s->tail()) { Selector_List* tails = operator()(s->tail()); for (size_t m = 0, mL = tails->length(); m < mL; ++m) { - Complex_Selector* ss = new (ctx.mem) Complex_Selector(*s); - ss->tail((*tails)[m]); - *sl << ss; + Complex_Selector* tailm = (*tails)[m]; + if(head && head->is_superselector_of(tailm)) { + *sl << s; + } else { + Complex_Selector *ss = new(ctx.mem) Complex_Selector(*s); + ss->tail(tailm); + *sl << ss; + } } } else { From 3af051e6583a2d94cfdb6a51cb2f8dee3ebf0c22 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 13 Jul 2015 12:29:06 +0200 Subject: [PATCH 09/10] Fix parent selector bug in evaluation step --- ast.cpp | 51 +++++++++++++++++++++++++ ast.hpp | 4 ++ debugger.hpp | 1 - eval.cpp | 104 ++++++++++++++++++++++++++++++++------------------- parser.cpp | 4 +- 5 files changed, 124 insertions(+), 40 deletions(-) diff --git a/ast.cpp b/ast.cpp index ae2e1810a7..76e24f7c7a 100644 --- a/ast.cpp +++ b/ast.cpp @@ -572,9 +572,44 @@ namespace Sass { return ss; } + Complex_Selector* Complex_Selector::parentize(Context& ctx) + { + // create a new complex selector to return a processed copy + return this; + Complex_Selector* ss = new (ctx.mem) Complex_Selector(this->pstate()); + ss->has_line_feed(this->has_line_feed()); + ss->combinator(this->combinator()); + if (this->tail()) { + ss->tail(this->tail()->parentize(ctx)); + } + if (Compound_Selector* head = this->head()) { + // now add everything expect parent selectors to head + ss->head(new (ctx.mem) Compound_Selector(head->pstate())); + for (size_t i = 0, L = head->length(); i < L; ++i) { + if (!dynamic_cast((*head)[i])) { + *ss->head() << (*head)[i]; + } + } + // if (ss->head()->empty()) ss->head(0); + } + // return copy + return ss; + } + + Selector_List* Selector_List::parentize(Context& ctx) + { + Selector_List* ss = new (ctx.mem) Selector_List(pstate()); + for (size_t i = 0, L = length(); i < L; ++i) { + *ss << (*this)[i]->parentize(ctx); + } + // return selector + return ss; + } + Selector_List* Complex_Selector::parentize(Selector_List* ps, Context& ctx) { Selector_List* ss = new (ctx.mem) Selector_List(pstate()); + if (ps == 0) { *ss << this->parentize(ctx); return ss; } for (size_t i = 0, L = ps->length(); i < L; ++i) { *ss << this->parentize((*ps)[i], ctx); } @@ -584,10 +619,13 @@ namespace Sass { Complex_Selector* Complex_Selector::parentize(Complex_Selector* parent, Context& ctx) { + if (!parent) return parentize(ctx); Complex_Selector* pr = 0; Compound_Selector* head = this->head(); // create a new complex selector to return a processed copy Complex_Selector* ss = new (ctx.mem) Complex_Selector(pstate()); + ss->has_line_feed(has_line_feed()); + ss->has_line_break(has_line_break()); // Points to last complex selector // Moved when resolving parent refs @@ -680,7 +718,20 @@ namespace Sass { return cpy; } + Selector_List* Selector_List::clone(Context& ctx) const + { + Selector_List* cpy = new (ctx.mem) Selector_List(*this); + return cpy; + } + Selector_List* Selector_List::cloneFully(Context& ctx) const + { + Selector_List* cpy = new (ctx.mem) Selector_List(pstate()); + for (size_t i = 0, L = length(); i < L; ++i) { + *cpy << (*this)[i]->cloneFully(ctx); + } + return cpy; + } /* not used anymore - remove? Selector_Placeholder* Selector::find_placeholder() diff --git a/ast.hpp b/ast.hpp index 1445dc3180..1143cdf19c 100644 --- a/ast.hpp +++ b/ast.hpp @@ -2042,6 +2042,7 @@ namespace Sass { return s; }; size_t length(); + Complex_Selector* parentize(Context& ctx); Selector_List* parentize(Selector_List* parents, Context& ctx); Complex_Selector* parentize(Complex_Selector* parent, Context& ctx); virtual bool is_superselector_of(Compound_Selector* sub, string wrapping = ""); @@ -2136,6 +2137,7 @@ namespace Sass { // basically unwraps parsed selectors void remove_parent_selectors(); // virtual Selector_Placeholder* find_placeholder(); + Selector_List* parentize(Context& ctx); Selector_List* parentize(Selector_List* parents, Context& ctx); Selector_List* parentize(Complex_Selector* parent, Context& ctx); virtual bool is_superselector_of(Compound_Selector* sub, string wrapping = ""); @@ -2155,6 +2157,8 @@ namespace Sass { } return sum; } + Selector_List* clone(Context&) const; // does not clone Compound_Selector*s + Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s // vector members() { return elements_; } ATTACH_OPERATIONS() }; diff --git a/debugger.hpp b/debugger.hpp index 26d5bf9105..8a90d8641f 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -585,7 +585,6 @@ inline void debug_node(Node* node, string ind = "") case Complex_Selector::ADJACENT_TO: cerr << "{+} "; break; case Complex_Selector::PARENT_OF: cerr << "{>} "; break; case Complex_Selector::PRECEDES: cerr << "{~} "; break; - case Complex_Selector::REFERENCE: cerr << "{@} "; break; case Complex_Selector::ANCESTOR_OF: cerr << "{ } "; break; } cerr << endl; diff --git a/eval.cpp b/eval.cpp index eb1a8c60e4..6ce3c65f99 100644 --- a/eval.cpp +++ b/eval.cpp @@ -1399,55 +1399,83 @@ namespace Sass { Compound_Selector* head = s->head(); Complex_Selector::Combinator combinator = s->combinator(); Selector_List* sl = new (ctx.mem) Selector_List(s->pstate()); - if (head) { // check if we have a parent selector reference (expands to list) - if (head->length() > 0 && dynamic_cast((*head)[0])) { + if (head->length() > 1 && dynamic_cast((*head)[0])) { // do we have any parents to interpolate - Selector_List* pr = selector(); - if (pr && pr->length() > 0) { - for (size_t n = 0, nL = pr->length(); n < nL; ++n) { - if (tail) { - vector rv; - Selector_List* tails = operator()(tail); - for (size_t m = 0, mL = tails->length(); m < mL; ++m) { - Complex_Selector* ns = (*pr)[n]->cloneFully(ctx); - if (s->has_line_feed()) ns->has_line_feed(true); - Complex_Selector* tt = (*tails)[m]; - Complex_Selector* last = ns->last(); - if (combinator != Complex_Selector::ANCESTOR_OF) { - Complex_Selector* cp = 0; - cp = new (ctx.mem) Complex_Selector(s->pstate()); - cp->head(head); cp->tail(tt); - cp->combinator(combinator); - last->tail(cp); - } else { - last->tail(tt); - } - for (size_t i = 1, iL = head->length(); i < iL; ++i) { - // add simple selectors - *last->head() << (*head)[i]; - } - *sl << ns; - } - // EO foreach parentized tail - } else { - Complex_Selector* ns = (*pr)[n]->cloneFully(ctx); - Complex_Selector* last = ns->last(); - ns->combinator(combinator); - for (size_t i = 1, iL = head->length(); i < iL; ++i) { - // add simple selectors - *last->head() << (*head)[i]; + if (Selector_List* pr = selector()) { + // parent will be prefixed + Selector_List* ns = pr->cloneFully(ctx); + // the tail can be re-attached unchanged + for (size_t n = 0, nL = ns->length(); n < nL; ++n) { + Complex_Selector* lst_t = (*ns)[n]->last(); + Compound_Selector* lst_h = lst_t->head(); + for (size_t i = 1, L = head->length(); i < L; ++i) *lst_h << (*head)[i]; + lst_t->tail(tail); // now connect old tail back to new intermediate + lst_t->combinator(combinator); // and dont forget the combinator + // if (s->has_line_feed()) lst_t->has_line_feed(true); // and dont forget the combinator + } + return ns; + } + else { + Complex_Selector* cpy = s->cloneFully(ctx); + cpy->head(new (ctx.mem) Compound_Selector(head->pstate())); + for (size_t i = 1, L = head->length(); i < L; ++i) + *cpy->head() << (*head)[i]; + *sl << s; + return sl; + } + } + + // have a simple + if (head->length() == 1 && dynamic_cast((*head)[0])) { + // do we have any parents to interpolate + if (Selector_List* pr = selector()) { + // parent will be prefixed + Selector_List* ns = pr->cloneFully(ctx); + // the tail can be re-attached unchanged + for (size_t n = 0, nL = ns->length(); n < nL; ++n) { + Complex_Selector* lst = (*ns)[n]->last(); + lst->tail(tail); + if (combinator != Complex_Selector::ANCESTOR_OF) { + if (lst->combinator()!= Complex_Selector::ANCESTOR_OF) { + Complex_Selector* ins = s->clone(ctx); + ins->head(0); + ins->tail(tail); + lst->tail(ins); + } else { + lst->combinator(combinator); } - *sl << ns; } + if (s->has_line_feed()) lst->has_line_feed(true); + if (s->has_line_break()) lst->has_line_break(true); } - parentized = true; + return ns; + } + else { + Complex_Selector* ss = s->cloneFully(ctx); + // check if complex selector can be eliminated + if (s->combinator() == Complex_Selector::ANCESTOR_OF) + { + if (s->has_line_feed()) tail->has_line_feed(true); + if (s->has_line_break()) tail->has_line_break(true); + *sl << tail; + } + else + { + *sl << ss; + } + return sl; } } } + else + { + *sl << s; + return sl; + } if (parentized == false) { if (s->tail()) { diff --git a/parser.cpp b/parser.cpp index 80a52015c7..76d7ababdc 100644 --- a/parser.cpp +++ b/parser.cpp @@ -665,6 +665,7 @@ namespace Sass { { bool reloop = true; bool had_linefeed = false; + Complex_Selector* sel = 0; To_String to_string(&ctx); Selector_List* group = new (ctx.mem) Selector_List(pstate); @@ -678,7 +679,7 @@ namespace Sass { // now parse the complex selector - Complex_Selector* sel = parse_complex_selector(in_root); + sel = parse_complex_selector(in_root); if (!sel) return group; @@ -704,6 +705,7 @@ namespace Sass { } // update for end position group->update_pstate(pstate); + if (sel) sel->last()->has_line_break(false); return group; } // EO parse_selector_list From 6357bd64a1ea6e95993966152d0dd07936c8d8c8 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 13 Jul 2015 17:29:14 +0200 Subject: [PATCH 10/10] Additional fixes for further regressions --- ast.cpp | 10 +++++----- eval.cpp | 15 ++++++++++----- node.cpp | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ast.cpp b/ast.cpp index 76e24f7c7a..57f0aec59b 100644 --- a/ast.cpp +++ b/ast.cpp @@ -577,7 +577,7 @@ namespace Sass { // create a new complex selector to return a processed copy return this; Complex_Selector* ss = new (ctx.mem) Complex_Selector(this->pstate()); - ss->has_line_feed(this->has_line_feed()); + //ss->has_line_feed(this->has_line_feed()); ss->combinator(this->combinator()); if (this->tail()) { ss->tail(this->tail()->parentize(ctx)); @@ -624,7 +624,7 @@ namespace Sass { Compound_Selector* head = this->head(); // create a new complex selector to return a processed copy Complex_Selector* ss = new (ctx.mem) Complex_Selector(pstate()); - ss->has_line_feed(has_line_feed()); + // ss->has_line_feed(has_line_feed()); ss->has_line_break(has_line_break()); // Points to last complex selector @@ -749,9 +749,9 @@ namespace Sass { if ((*this)[i]->combinator() != Complex_Selector::ANCESTOR_OF) continue; if ((*this)[i]->head()->is_empty_reference()) { Complex_Selector* tail = (*this)[i]->tail(); - if ((*this)[i]->has_line_feed()) { - if (tail) tail->has_line_feed(true); - } + // if ((*this)[i]->has_line_feed()) { + // if (tail) tail->has_line_feed(true); + // } (*this)[i] = tail; } } diff --git a/eval.cpp b/eval.cpp index 6ce3c65f99..210b45ab60 100644 --- a/eval.cpp +++ b/eval.cpp @@ -1447,7 +1447,7 @@ namespace Sass { lst->combinator(combinator); } } - if (s->has_line_feed()) lst->has_line_feed(true); + if (s->has_line_feed()) (*ns)[n]->has_line_feed(true); if (s->has_line_break()) lst->has_line_break(true); } return ns; @@ -1473,7 +1473,12 @@ namespace Sass { } else { - *sl << s; + Selector_List* l = operator()(s->tail()); + for (size_t i = 0, L = l->length(); i < L; ++i) { + Complex_Selector* ss = s->clone(ctx); + ss->tail((*l)[i]); + *sl << ss; + } return sl; } @@ -1501,9 +1506,9 @@ namespace Sass { if (!(*sl)[i]->head()) continue; if ((*sl)[i]->combinator() != Complex_Selector::ANCESTOR_OF) continue; if ((*sl)[i]->head()->is_empty_reference()) { - if ((*sl)[i]->has_line_feed()) { - if ((*sl)[i]->tail()) (*sl)[i]->tail()->has_line_feed(true); - } + // if ((*sl)[i]->has_line_feed()) { + // if ((*sl)[i]->tail()) (*sl)[i]->tail()->has_line_feed(true); + // } (*sl)[i] = (*sl)[i]->tail(); } diff --git a/node.cpp b/node.cpp index d16d02abae..bad93b28db 100644 --- a/node.cpp +++ b/node.cpp @@ -249,7 +249,7 @@ namespace Sass { if (child.isSelector()) { pCurrent->tail(child.selector()->clone(ctx)); // 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() - if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed); + // if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed); pCurrent = pCurrent->tail(); } else if (child.isCombinator()) { pCurrent->combinator(child.combinator());