From 97e69ca01f5aba5c39e07856373152e49bea3656 Mon Sep 17 00:00:00 2001 From: supermario Date: Mon, 1 Jun 2015 19:24:33 -0700 Subject: [PATCH 01/31] Added various various sass selector functions --- ast.cpp | 102 ++++++++++++++++++++ ast.hpp | 10 ++ context.cpp | 7 ++ extend.cpp | 13 ++- extend.hpp | 4 + functions.cpp | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++ functions.hpp | 15 +++ node.cpp | 28 ++++++ node.hpp | 1 + 9 files changed, 429 insertions(+), 5 deletions(-) diff --git a/ast.cpp b/ast.cpp index 7f7091e9fc..75b16c963e 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 bc933d15d5..fa79189c79 100644 --- a/ast.hpp +++ b/ast.hpp @@ -164,6 +164,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) @@ -2006,6 +2007,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 @@ -2074,6 +2078,8 @@ namespace Sass { typedef deque ComplexSelectorDeque; + typedef Subset_Map > ExtensionSubsetMap; + /////////////////////////////////// // Comma-separated selector groups. /////////////////////////////////// @@ -2092,6 +2098,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 5bef28f7fe..3905b3289a 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,38 @@ 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() @@ -1563,6 +1596,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) @@ -1577,6 +1800,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..153dcee14c 100644 --- a/node.cpp +++ b/node.cpp @@ -247,5 +247,33 @@ 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*, std::function< bool(Complex_Selector*, Complex_Selector*) > > sel_set([&] ( Complex_Selector* lhs, Complex_Selector* rhs ) { + bool result = lhs->perform(&to_string) < rhs->perform(&to_string); + return result; + } ); + + // 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 ef933a14799f54b0b446b55c55e78b8c05c2bacc Mon Sep 17 00:00:00 2001 From: supermario Date: Fri, 5 Jun 2015 10:45:03 -0700 Subject: [PATCH 02/31] Switched to existing Complex_Selector_Compare functor, instead of using std::function --- node.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/node.cpp b/node.cpp index 153dcee14c..c963f4cbb7 100644 --- a/node.cpp +++ b/node.cpp @@ -254,10 +254,7 @@ namespace Sass { Node result = Node::createCollection(); To_String to_string; - std::set< Complex_Selector*, std::function< bool(Complex_Selector*, Complex_Selector*) > > sel_set([&] ( Complex_Selector* lhs, Complex_Selector* rhs ) { - bool result = lhs->perform(&to_string) < rhs->perform(&to_string); - return result; - } ); + 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) { From c3b029bdfdb3e6c139821cfe701b13009405a53c Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sat, 6 Jun 2015 04:41:07 +0200 Subject: [PATCH 03/31] Improve and fix a few minor code issues Renamed some stuff to make it more distinguishable --- ast.cpp | 88 +++++------------- ast.hpp | 53 +++++------ 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 | 4 +- eval.cpp | 18 ++-- eval.hpp | 4 +- expand.cpp | 12 +-- expand.hpp | 2 +- extend.cpp | 5 +- extend.hpp | 7 +- functions.cpp | 181 ++++++++++++++++++-------------------- 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 | 96 ++++++++++---------- parser.hpp | 18 ++-- position.cpp | 6 +- position.hpp | 8 +- posix/getopt.c | 5 ++ prelexer.cpp | 10 +-- 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_specificity.cpp | 2 +- to_string.cpp | 2 +- util.cpp | 8 +- util.hpp | 2 +- 46 files changed, 359 insertions(+), 386 deletions(-) diff --git a/ast.cpp b/ast.cpp index 75b16c963e..d9608afd07 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 fa79189c79..f00d8a2c2c 100644 --- a/ast.hpp +++ b/ast.hpp @@ -184,6 +184,10 @@ namespace Sass { elements_.insert(elements_.begin(), element); return *this; } + size_t size() + { + return elements_.size(); + } vector& elements() { return elements_; } const vector& elements() const { return elements_; } vector& elements(vector& e) { elements_ = e; return elements_; } @@ -261,7 +265,7 @@ namespace Sass { RULESET, MEDIA, DIRECTIVE, - FEATURE, + SUPPORTS, ATROOT, BUBBLE, KEYFRAMERULE @@ -387,16 +391,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() @@ -649,7 +653,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: @@ -657,7 +660,6 @@ namespace Sass { string n, Parameters* params, Block* b, - Context* ctx, Type t) : Has_Block(pstate, b), name_(n), @@ -667,7 +669,6 @@ namespace Sass { native_function_(0), c_function_(0), cookie_(0), - ctx_(ctx), is_overload_stub_(false), signature_(0) { } @@ -676,7 +677,6 @@ namespace Sass { string n, Parameters* params, Native_Function func_ptr, - Context* ctx, bool overload_stub = false) : Has_Block(pstate, 0), name_(n), @@ -686,7 +686,6 @@ namespace Sass { native_function_(func_ptr), c_function_(0), cookie_(0), - ctx_(ctx), is_overload_stub_(overload_stub), signature_(sig) { } @@ -695,7 +694,6 @@ namespace Sass { string n, Parameters* params, Sass_Function_Entry c_func, - Context* ctx, bool whatever, bool whatever2) : Has_Block(pstate, 0), @@ -706,7 +704,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) { } @@ -1456,10 +1453,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() }; @@ -1467,7 +1464,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: @@ -1476,9 +1473,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() @@ -1551,7 +1548,7 @@ namespace Sass { { return expression()->exclude("rule"); } - if (s->statement_type() == Statement::FEATURE) + if (s->statement_type() == Statement::SUPPORTS) { return expression()->exclude("supports"); } @@ -1931,7 +1928,6 @@ namespace Sass { : Selector(pstate), Vectorized(s) { } - Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx); // virtual Selector_Placeholder* find_placeholder(); Simple_Selector* base() @@ -1991,9 +1987,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); @@ -2077,7 +2073,6 @@ namespace Sass { }; typedef deque ComplexSelectorDeque; - typedef Subset_Map > ExtensionSubsetMap; /////////////////////////////////// @@ -2101,7 +2096,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..823b57883c 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_Feature_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_Feature_Query_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 533fb24fbb..bdb0e182c3 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; } @@ -156,7 +151,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; } @@ -166,8 +161,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()); @@ -248,7 +243,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())); @@ -264,8 +259,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()); @@ -360,8 +355,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: @@ -423,10 +418,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 1389965393..19d1692dd2 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*); @@ -62,7 +61,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 0f36d1111c..fec863c72c 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 d8fe469630..6433ef2869 100644 --- a/environment.hpp +++ b/environment.hpp @@ -24,6 +24,8 @@ namespace Sass { public: Memory_Manager mem; Environment() : local_frame_(map()), parent_(0) { } + Environment(Environment* env) : local_frame_(map()), parent_(env) { } + Environment(Environment& env) : local_frame_(map()), parent_(&env) { } // link parent to create a stack void link(Environment& env) { parent_ = &env; } @@ -40,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 e874beb045..f691e7dfee 100644 --- a/eval.cpp +++ b/eval.cpp @@ -627,6 +627,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); @@ -924,10 +925,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()) { @@ -950,29 +948,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 5b18edd713..a03f7133ad 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; @@ -533,7 +533,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; @@ -557,6 +556,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 3905b3289a..563652225e 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,18 +138,18 @@ 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); 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__ + #ifdef __MINGW32__ uint64_t GetSeed() { HCRYPTPROV hp = 0; @@ -165,13 +163,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 @@ -1599,13 +1597,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) { @@ -1614,21 +1612,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] @@ -1638,32 +1636,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) { @@ -1672,21 +1668,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] @@ -1699,7 +1695,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 `"); @@ -1709,7 +1705,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() == "*") { @@ -1720,71 +1716,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)"; @@ -1800,37 +1826,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 9cfcfeffa7..f27c9e74cd 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); } @@ -649,7 +649,7 @@ namespace Sass { } } - void Inspect::operator()(Feature_Query* fq) + void Inspect::operator()(Supports_Query* fq) { size_t i = 0; (*fq)[i++]->perform(this); @@ -658,17 +658,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 36121a5f24..4c44e0ae86 100644 --- a/lexer.hpp +++ b/lexer.hpp @@ -107,7 +107,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..6364f66e24 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 87157af6c5..1045a2a975 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 a9d3f0e93d..d98a004d0d 100644 --- a/parser.cpp +++ b/parser.cpp @@ -45,7 +45,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) @@ -113,7 +113,7 @@ namespace Sass { /*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) { (*root) << parse_propset(); }*/ - else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) { + else if (peek< kwd_include_directive >() /* || peek< exactly<'+'> >() */) { Mixin_Call* mixin_call = parse_mixin_call(); (*root) << mixin_call; if (!mixin_call->block()) { @@ -139,7 +139,7 @@ namespace Sass { else if (peek< kwd_at_root >()) { (*root) << parse_at_root_block(); } - else if (peek< kwd_supports >()) { + else if (peek< kwd_supports_directive >()) { (*root) << parse_feature_block(); } else if (peek< kwd_warn >()) { @@ -339,7 +339,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; } @@ -382,7 +382,7 @@ namespace Sass { Mixin_Call* Parser::parse_mixin_call() { - 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)); @@ -497,7 +497,7 @@ namespace Sass { 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; @@ -521,7 +521,7 @@ namespace Sass { if (const char* p = find_first_in_interval< exactly >(i, end_of_selector)) { // accumulate the preceding segment if the position has advanced if (i < p) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p)); - // skip to the delimiter by skipping occurences in quoted strings + // check if the interpolation only contains white-space (error out) if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } @@ -545,7 +545,7 @@ namespace Sass { return selector_schema; } - Selector_List* Parser::parse_selector_group() + Selector_List* Parser::parse_selector_list() { bool reloop = true; To_String to_string(&ctx); @@ -595,7 +595,7 @@ namespace Sass { (*group) << comb; } while (reloop); - while (lex< optional >()) { + while (lex< kwd_optional >()) { group->is_optional(true); } return group; @@ -632,7 +632,7 @@ namespace Sass { exactly<'{'>, exactly<'}'>, exactly<';'>, - optional + kwd_optional > >()) // no selector after the combinator { rhs = 0; } @@ -666,10 +666,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 > > >()) { // saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning (*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 > > >()) { // if you see a type selector (*seq) << new (ctx.mem) Type_Selector(pstate, lexed); sawsomething = true; @@ -710,7 +710,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)) { @@ -734,7 +734,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); } @@ -742,7 +742,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; @@ -778,7 +778,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) { @@ -910,7 +910,7 @@ namespace Sass { else if (peek< kwd_mixin >() || peek< kwd_function >()) { (*block) << parse_definition(); } - else if (peek< kwd_include >(position)) { + else if (peek< kwd_include_directive >(position)) { Mixin_Call* the_call = parse_mixin_call(); (*block) << the_call; // don't need a semicolon after a content block @@ -934,14 +934,14 @@ namespace Sass { 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 >()) { + else if (peek< kwd_supports_directive >()) { (*block) << parse_feature_block(); } else if (peek< kwd_at_root >()) { @@ -1012,7 +1012,7 @@ namespace Sass { 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< static_value >()) { - return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex()*/); + return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex()*/); } else { Expression* value; @@ -1033,7 +1033,7 @@ namespace Sass { } } - return new (ctx.mem) Declaration(prop->pstate(), prop, value/*, lex()*/); + return new (ctx.mem) Declaration(prop->pstate(), prop, value/*, lex()*/); } } @@ -1335,7 +1335,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 > > >()) { @@ -1361,9 +1361,9 @@ 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; @@ -1898,25 +1898,25 @@ namespace Sass { return new (ctx.mem) Media_Query_Expression(feature->pstate(), feature, expression); } - Feature_Block* Parser::parse_feature_block() + Supports_Block* Parser::parse_feature_block() { - lex< kwd_supports >(); + lex< kwd_supports_directive >(); ParserState supports_source_position = pstate; - Feature_Query* feature_queries = parse_feature_queries(); + Supports_Query* queries = parse_feature_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_feature_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(); @@ -1927,7 +1927,7 @@ namespace Sass { return fq; } - Feature_Query_Condition* Parser::parse_feature_query() + Supports_Condition* Parser::parse_feature_query() { if (peek< kwd_not >(position)) return parse_supports_negation(); else if (peek< kwd_and >(position)) return parse_supports_conjunction(); @@ -1936,9 +1936,9 @@ 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)) @@ -1948,40 +1948,40 @@ namespace Sass { 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_feature_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_feature_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_feature_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()); @@ -2051,7 +2051,7 @@ namespace Sass { sel = parse_selector_schema(lookahead.found); } else { - sel = parse_selector_group(); + sel = parse_selector_list(); } } else if (!(peek >() || peek >() || peek >())) { @@ -2194,7 +2194,7 @@ 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; @@ -2244,7 +2244,7 @@ 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; diff --git a/parser.hpp b/parser.hpp index d5cd3123fe..c5cb8e4238 100644 --- a/parser.hpp +++ b/parser.hpp @@ -214,7 +214,7 @@ namespace Sass { // Propset* parse_propset(); Ruleset* parse_ruleset(Selector_Lookahead lookahead); Selector_Schema* parse_selector_schema(const char* end_of_selector); - Selector_List* parse_selector_group(); + Selector_List* parse_selector_list(); Complex_Selector* parse_selector_combination(); Compound_Selector* parse_simple_selector_sequence(); Simple_Selector* parse_simple_selector(); @@ -257,14 +257,14 @@ namespace Sass { 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_feature_block(); + Supports_Query* parse_feature_queries(); + Supports_Condition* parse_feature_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(); 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 72065333bc..fe477d084a 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -240,7 +240,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 +256,7 @@ namespace Sass { return word(src); } - const char* kwd_include(const char* src) { + const char* kwd_include_directive(const char* src) { return word(src); } @@ -476,13 +476,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); @@ -507,7 +507,7 @@ namespace Sass { const char* functional_schema(const char* src) { return sequence< identifier_schema, exactly<'('> >(src); } - const char* functional(const char* src) { + const char* re_pseudo_selector(const char* src) { return sequence< identifier, exactly<'('> >(src); } // Match the CSS negation pseudo-class. 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_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/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 831a4b7cd9..1bf68a2dba 100644 --- a/util.cpp +++ b/util.cpp @@ -1,4 +1,4 @@ -#include +#include #include "ast.hpp" #include "util.hpp" #include "prelexer.hpp" @@ -565,7 +565,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; } @@ -658,8 +658,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 64f72f3ec2d463c52302dfb4a47cafe3738fef5b Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 7 Jun 2015 21:57:11 +0200 Subject: [PATCH 04/31] Performance improvements --- eval.cpp | 14 ++++++-------- output.cpp | 4 +--- parser.cpp | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/eval.cpp b/eval.cpp index f691e7dfee..b2738725a1 100644 --- a/eval.cpp +++ b/eval.cpp @@ -830,12 +830,7 @@ namespace Sass { Expression* Eval::operator()(Number* n) { - n->normalize(); - // behave according to as ruby sass (add leading zero) - return new (ctx.mem) Number(n->pstate(), - n->value(), - n->unit(), - true); + return n; } Expression* Eval::operator()(Boolean* b) @@ -1107,8 +1102,11 @@ namespace Sass { } break; case Expression::NUMBER: { - return *static_cast(lhs) == - *static_cast(rhs); + Number* l = static_cast(lhs); + Number* r = static_cast(rhs); + return (l->value() == r->value()) && + (l->numerator_units() == r->numerator_units()) && + (l->denominator_units() == r->denominator_units()); } break; case Expression::COLOR: { diff --git a/output.cpp b/output.cpp index 1045a2a975..1589d5e6e9 100644 --- a/output.cpp +++ b/output.cpp @@ -221,10 +221,8 @@ namespace Sass { q->perform(this); append_scope_opener(); - Selector* e = f->selector(); - if (e && b->has_non_hoistable()) { + if (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) { diff --git a/parser.cpp b/parser.cpp index d98a004d0d..4fb8c918e1 100644 --- a/parser.cpp +++ b/parser.cpp @@ -511,11 +511,17 @@ namespace Sass { return ruleset; } + // parse a selector schema that will be evaluated in the eval stage + // uses a string schema internally to do the actual schema handling + // in the eval stage we will be re-parse it into an actual selector Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector) { + // move up to the start lex< optional_spaces >(); const char* i = position; + // selector schema re-uses string schema implementation String_Schema* schema = new (ctx.mem) String_Schema(pstate); + // 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)) { @@ -525,25 +531,34 @@ namespace Sass { if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } + // skip over all nested inner interpolations up to our own delimiter const char* j = skip_over_scopes< exactly, exactly >(p + 2, end_of_selector); + // pass inner expression to the parser to resolve nested interpolations Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list(); + // set status on the list expression interpolant->is_interpolant(true); + // add to the string schema (*schema) << interpolant; + // advance position i = j; } // no more interpolants have been found // add the last segment if there is one 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; } } + // EO until eos position = end_of_selector; 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 return selector_schema; } + // EO parse_selector_schema Selector_List* Parser::parse_selector_list() { From dd2cc25cd9de217d119550202d556ed6f252bacd Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 7 Jun 2015 22:14:41 +0200 Subject: [PATCH 05/31] Rename some stuff in the parser --- parser.cpp | 92 +++++++++++++++++-------------- parser.hpp | 29 +++++----- prelexer.cpp | 8 +-- test/test_selector_difference.cpp | 2 +- test/test_superselector.cpp | 4 +- test/test_unification.cpp | 2 +- util.cpp | 25 +++++++-- 7 files changed, 92 insertions(+), 70 deletions(-) diff --git a/parser.cpp b/parser.cpp index 4fb8c918e1..a668b9e6f0 100644 --- a/parser.cpp +++ b/parser.cpp @@ -88,7 +88,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,7 +114,7 @@ namespace Sass { (*root) << parse_propset(); }*/ else if (peek< kwd_include_directive >() /* || peek< exactly<'+'> >() */) { - Mixin_Call* mixin_call = parse_mixin_call(); + Mixin_Call* mixin_call = parse_include_directive(); (*root) << mixin_call; if (!mixin_call->block()) { semicolon = true; @@ -140,7 +140,7 @@ namespace Sass { (*root) << parse_at_root_block(); } else if (peek< kwd_supports_directive >()) { - (*root) << parse_feature_block(); + (*root) << parse_supports_directive(); } else if (peek< kwd_warn >()) { (*root) << parse_warning(); @@ -380,7 +380,7 @@ namespace Sass { return p; } - Mixin_Call* Parser::parse_mixin_call() + Mixin_Call* Parser::parse_include_directive() { lex< kwd_include_directive >() /* || lex< exactly<'+'> >() */; if (!lex< identifier >()) error("invalid name in @include directive", pstate); @@ -450,7 +450,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 { @@ -490,7 +490,7 @@ namespace Sass { return propset; } */ - Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead) + Ruleset* Parser::parse_ruleset(Lookahead lookahead) { Selector* sel; if (lookahead.has_interpolants) { @@ -547,11 +547,15 @@ 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()); @@ -577,7 +581,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); @@ -616,7 +620,7 @@ namespace Sass { return group; } - Complex_Selector* Parser::parse_selector_combination() + Complex_Selector* Parser::parse_complex_selector() { Position sel_source_position(-1); Compound_Selector* lhs; @@ -628,7 +632,7 @@ namespace Sass { // no selector before the combinator { lhs = 0; } else { - lhs = parse_simple_selector_sequence(); + lhs = parse_compound_selector(); sel_source_position = before_token; lhs->has_line_break(peek_newline()); } @@ -652,7 +656,7 @@ namespace Sass { // no selector after the combinator { 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; @@ -663,7 +667,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); @@ -853,7 +857,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 > > >(); @@ -903,8 +907,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 >()) { @@ -926,7 +930,7 @@ namespace Sass { (*block) << parse_definition(); } else if (peek< kwd_include_directive >(position)) { - Mixin_Call* the_call = parse_mixin_call(); + 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; @@ -940,12 +944,12 @@ namespace Sass { } /* else if (peek< exactly<'+'> >()) { - (*block) << parse_mixin_call(); + (*block) << parse_include_directive(); semicolon = true; } */ 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); @@ -957,7 +961,7 @@ namespace Sass { (*block) << parse_media_block(); } else if (peek< kwd_supports_directive >()) { - (*block) << parse_feature_block(); + (*block) << parse_supports_directive(); } else if (peek< kwd_at_root >()) { (*block) << parse_at_root_block(); @@ -1031,7 +1035,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); @@ -1266,7 +1270,7 @@ namespace Sass { Expression* Parser::parse_expression() { - Expression* term1 = parse_term(); + Expression* term1 = parse_operators(); // if it's a singleton, return it directly; don't wrap it if (!(peek< exactly<'+'> >(position) || (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || @@ -1278,13 +1282,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 @@ -1913,12 +1917,12 @@ namespace Sass { return new (ctx.mem) Media_Query_Expression(feature->pstate(), feature, expression); } - Supports_Block* Parser::parse_feature_block() + Supports_Block* Parser::parse_supports_directive() { lex< kwd_supports_directive >(); ParserState supports_source_position = pstate; - Supports_Query* queries = parse_feature_queries(); + Supports_Query* queries = parse_supports_queries(); if (!peek< exactly<'{'> >()) { error("expected '{' in feature query", pstate); @@ -1928,13 +1932,13 @@ namespace Sass { return new (ctx.mem) Supports_Block(supports_source_position, queries, block); } - Supports_Query* Parser::parse_feature_queries() + Supports_Query* Parser::parse_supports_queries() { 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); @@ -1942,7 +1946,7 @@ namespace Sass { return fq; } - Supports_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(); @@ -1957,7 +1961,7 @@ namespace Sass { 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; @@ -1967,7 +1971,7 @@ namespace Sass { { lex< kwd_not >(); - Supports_Condition* cond = parse_feature_query(); + Supports_Condition* cond = parse_supports_query(); cond->operand(Supports_Condition::NOT); return cond; @@ -1977,7 +1981,7 @@ namespace Sass { { lex< kwd_and >(); - Supports_Condition* cond = parse_feature_query(); + Supports_Condition* cond = parse_supports_query(); cond->operand(Supports_Condition::AND); return cond; @@ -1987,7 +1991,7 @@ namespace Sass { { lex< kwd_or >(); - Supports_Condition* cond = parse_feature_query(); + Supports_Condition* cond = parse_supports_query(); cond->operand(Supports_Condition::OR); return cond; @@ -2009,7 +2013,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(); @@ -2060,7 +2064,7 @@ 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); @@ -2097,7 +2101,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; @@ -2154,14 +2164,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; @@ -2215,7 +2225,7 @@ namespace Sass { 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; @@ -2223,7 +2233,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; @@ -2265,7 +2275,7 @@ namespace Sass { 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 c5cb8e4238..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_list(); - Complex_Selector* parse_selector_combination(); - Compound_Selector* parse_simple_selector_sequence(); + 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,13 +253,14 @@ 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(); - Supports_Block* parse_feature_block(); - Supports_Query* parse_feature_queries(); - Supports_Condition* parse_feature_query(); + 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(); @@ -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/prelexer.cpp b/prelexer.cpp index fe477d084a..9b903df386 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 @@ -508,7 +506,7 @@ namespace Sass { return sequence< identifier_schema, exactly<'('> >(src); } const char* re_pseudo_selector(const char* src) { - return sequence< identifier, exactly<'('> >(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/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_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/util.cpp b/util.cpp index 1bf68a2dba..a3e93a7b84 100644 --- a/util.cpp +++ b/util.cpp @@ -1,7 +1,9 @@ #include #include "ast.hpp" #include "util.hpp" +#include "lexer.hpp" #include "prelexer.hpp" +#include "constants.hpp" #include "utf8/checked.h" namespace Sass { @@ -457,12 +459,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 { From 7c6a26c23e3db40c8c86201db0ebb40052d896e8 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 7 Jun 2015 22:49:49 +0200 Subject: [PATCH 06/31] Performance improvements --- ast.hpp | 6 ++--- cssize.cpp | 6 ++++- cssize.hpp | 1 + environment.hpp | 18 +++++++-------- lexer.hpp | 59 ++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 69 insertions(+), 21 deletions(-) diff --git a/ast.hpp b/ast.hpp index f00d8a2c2c..eb4cf416b4 100644 --- a/ast.hpp +++ b/ast.hpp @@ -1683,7 +1683,7 @@ namespace Sass { ///////////////////////////////////////// // Abstract base class for CSS selectors. ///////////////////////////////////////// - class Selector : public AST_Node { + class Selector : public Expression { ADD_PROPERTY(bool, has_reference) ADD_PROPERTY(bool, has_placeholder) // line break before list separator @@ -1697,14 +1697,14 @@ namespace Sass { ADD_PROPERTY(Media_Block*, media_block) public: Selector(ParserState pstate, bool r = false, bool h = false) - : AST_Node(pstate), + : Expression(pstate), has_reference_(r), has_placeholder_(h), has_line_feed_(false), has_line_break_(false), is_optional_(false), media_block_(0) - { } + { concrete_type(SELECTOR); } virtual ~Selector() = 0; // virtual Selector_Placeholder* find_placeholder(); virtual unsigned long specificity() { diff --git a/cssize.cpp b/cssize.cpp index bdb0e182c3..2b01857930 100644 --- a/cssize.cpp +++ b/cssize.cpp @@ -131,6 +131,11 @@ namespace Sass { return rules; } + Statement* Cssize::operator()(Null* m) + { + return 0; + } + Statement* Cssize::operator()(Media_Block* m) { if (parent()->statement_type() == Statement::RULESET) @@ -238,7 +243,6 @@ namespace Sass { At_Root_Block* mm = new (ctx.mem) At_Root_Block(m->pstate(), wrapper_block, m->expression()); - Bubble* bubble = new (ctx.mem) Bubble(mm->pstate(), mm); return bubble; } diff --git a/cssize.hpp b/cssize.hpp index 19d1692dd2..f6829c88bf 100644 --- a/cssize.hpp +++ b/cssize.hpp @@ -55,6 +55,7 @@ namespace Sass { // Statement* operator()(Definition*); // Statement* operator()(Mixin_Call*); // Statement* operator()(Content*); + Statement* operator()(Null*); Statement* parent(); vector> slice_by_bubble(Statement*); diff --git a/environment.hpp b/environment.hpp index 6433ef2869..2f3d797f0c 100644 --- a/environment.hpp +++ b/environment.hpp @@ -1,9 +1,9 @@ #ifndef SASS_ENVIRONMENT_H #define SASS_ENVIRONMENT_H -#include #include #include +#include #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp" @@ -11,21 +11,21 @@ namespace Sass { using std::string; - using std::map; + using std::unordered_map; using std::cerr; using std::endl; template class Environment { // TODO: test with unordered_map - map local_frame_; + unordered_map local_frame_; ADD_PROPERTY(Environment*, parent) public: Memory_Manager mem; - Environment() : local_frame_(map()), parent_(0) { } - Environment(Environment* env) : local_frame_(map()), parent_(env) { } - Environment(Environment& env) : local_frame_(map()), parent_(&env) { } + Environment() : local_frame_(unordered_map()), parent_(0) { } + Environment(Environment* env) : local_frame_(unordered_map()), parent_(env) { } + Environment(Environment& env) : local_frame_(unordered_map()), parent_(&env) { } // link parent to create a stack void link(Environment& env) { parent_ = &env; } @@ -49,12 +49,12 @@ namespace Sass { // scope operates on the current frame - map& local_frame() { + unordered_map& local_frame() { return local_frame_; } bool has_local(const string& key) const - { return local_frame_.count(key); } + { return local_frame_.find(key) != local_frame_.end(); } T& get_local(const string& key) { return local_frame_[key]; } @@ -149,7 +149,7 @@ namespace Sass { #ifdef DEBUG void print() { - for (typename map::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) { + for (typename unordered_map::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) { cerr << i->first << endl; } if (parent_) { diff --git a/lexer.hpp b/lexer.hpp index 4c44e0ae86..f697de9f1e 100644 --- a/lexer.hpp +++ b/lexer.hpp @@ -142,26 +142,69 @@ namespace Sass { // Tries supplied matchers in order. // Succeeds if one of them succeeds. // Regex equivalent: /(?:FOO|BAR)/ - template + template const char* alternatives(const char* src) { const char* rslt; - for (prelexer mx : { mxs... }) { - if ((rslt = mx(src))) return rslt; - } + if ((rslt = mx(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; + 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); + } // Tries supplied matchers in order. // Succeeds if all of them succeeds. // Regex equivalent: /(?:FOO)(?:BAR)/ - template + template const char* sequence(const char* src) { const char* rslt = src; - for (prelexer mx : { mxs... }) { - if (!(rslt = mx(rslt))) return 0; - } + 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 + 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); + } + // Match a pattern or not. Always succeeds. // Regex equivalent: /(?:literal)?/ From 457fd6f8ad2bf6cdca1de094dfcaa3a40d327f81 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 7 Jun 2015 23:47:33 +0200 Subject: [PATCH 07/31] Preparation for parser refactoring --- ast.hpp | 33 ++++--- eval.cpp | 1 - parser.cpp | 267 +++++++++++++++++++++++++++++++++++++++-------------- parser.hpp | 1 + 4 files changed, 215 insertions(+), 87 deletions(-) diff --git a/ast.hpp b/ast.hpp index eb4cf416b4..46d75c250e 100644 --- a/ast.hpp +++ b/ast.hpp @@ -1746,9 +1746,13 @@ namespace Sass { }; inline Simple_Selector::~Simple_Selector() { } - ///////////////////////////////////// - // Parent references (i.e., the "&"). - ///////////////////////////////////// + + ////////////////////////////////// + // 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 Selector_Reference : public Simple_Selector { ADD_PROPERTY(Selector*, selector) public: @@ -1760,6 +1764,8 @@ namespace Sass { if (!selector()) return 0; return selector()->specificity(); } + string type() { return "selector"; } + static string type_name() { return "selector"; } ATTACH_OPERATIONS() }; @@ -1940,9 +1946,9 @@ namespace Sass { return (*this)[0]; 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); + // virtual bool is_superselector_of(Complex_Selector* sub); + // virtual bool is_superselector_of(Selector_List* sub); virtual unsigned long specificity() { int sum = 0; @@ -1999,13 +2005,11 @@ namespace Sass { Complex_Selector* context(Context&); Complex_Selector* innermost(); size_t length(); - 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); + virtual bool is_superselector_of(Complex_Selector* sub); + virtual 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,13 +2094,12 @@ namespace Sass { : Selector(pstate), Vectorized(s), wspace_(0) { } // 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); + virtual bool is_superselector_of(Compound_Selector* sub); + virtual bool is_superselector_of(Complex_Selector* sub); + virtual 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/eval.cpp b/eval.cpp index b2738725a1..bc8282fb7e 100644 --- a/eval.cpp +++ b/eval.cpp @@ -629,7 +629,6 @@ namespace Sass { // 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()); diff --git a/parser.cpp b/parser.cpp index a668b9e6f0..7051a7b306 100644 --- a/parser.cpp +++ b/parser.cpp @@ -189,6 +189,7 @@ namespace Sass { lex< optional_spaces >(); } block_stack.pop_back(); + // something matched return root; } @@ -299,7 +300,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); } @@ -362,7 +363,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; @@ -380,21 +381,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); @@ -521,6 +507,13 @@ namespace Sass { const char* i = position; // 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); + + // set some options from parsing context + selector_schema->media_block(last_media_block); + selector_schema->last_block(block_stack.back()); + // process until end while (i < end_of_selector) { // try to parse mutliple interpolants @@ -556,14 +549,35 @@ 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()); // return parsed result return selector_schema; } // EO parse_selector_schema + // called after parsing `kwd_include_directive` + Mixin_Call* Parser::parse_include_directive() + { + lex< kwd_include_directive >() /* || lex< exactly<'+'> >() */; + // lex identifier into `lexed` var + if (!lex< identifier >()) error("invalid name in @include directive", pstate); + ParserState source_position_of_call = pstate; + // normalize underscores to hyphens + string name(Util::normalize_underscores(lexed)); + // parse mandatory arguments + Arguments* args = parse_arguments(); + Block* content = 0; + // parse optional block + if (peek< exactly<'{'> >()) { + content = parse_block(); + } + Mixin_Call* call = new (ctx.mem) Mixin_Call(source_position_of_call, name, args, content); + // 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 reloop = true; @@ -619,11 +633,18 @@ namespace Sass { } return group; } + // EO parse_selector_list + // 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() { Position sel_source_position(-1); - Compound_Selector* lhs; + // parse the left hand side + Compound_Selector* lhs = 0; + // special case if it starts with combinator ([+~>]) if (peek_css< alternatives < exactly<'+'>, exactly<'~'>, @@ -632,19 +653,24 @@ namespace Sass { // no selector before the combinator { lhs = 0; } else { + // 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; + // parse combinator between lhs and rhs + 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; bool cpx_lf = peek_newline(); + // source position of a complex selector points to the combinator + // ToDo: make sure we update pstate for ancestor of (lex < zero >()); Complex_Selector* rhs; + // check if we got the abort condition (ToDo: optimize) if (peek_css< alternatives < exactly<','>, exactly<')'>, @@ -660,16 +686,24 @@ namespace Sass { 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); + Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, source, sel_source_position), combinator, lhs, rhs); cpx->media_block(last_media_block); cpx->last_block(block_stack.back()); if (cpx_lf) cpx->has_line_break(cpx_lf); + + // complex selector return cpx; } + // 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); + // set some options from parsing context seq->media_block(last_media_block); seq->last_block(block_stack.back()); bool sawsomething = false; @@ -711,8 +745,12 @@ namespace Sass { > >(position))) { (*seq) << parse_simple_selector(); } + + // EO while true return seq; + } + // EO parse_compound_selector Simple_Selector* Parser::parse_simple_selector() { @@ -729,10 +767,13 @@ namespace Sass { 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<':'> >()) { return parse_pseudo_selector(); } - else if (peek< exactly<'['> >(position)) { + else if (peek< exactly<'['> >()) { return parse_attribute_selector(); } else if (lex< placeholder >()) { @@ -760,6 +801,8 @@ namespace Sass { 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 >()) { string name(lexed); @@ -853,7 +896,17 @@ namespace Sass { } } + // convenience function for block parsing + // will create a new block ad-hoc for you Block* Parser::parse_block() + { + // create new block and pass it to actual parse function + return parse_block(new (ctx.mem) Block(pstate, 0)); + } + + // the main block parsing function + // parses stuff between `{` and `}` + Block* Parser::parse_block(Block* root) { lex< exactly<'{'> >(); bool semicolon = false; @@ -1012,6 +1065,8 @@ namespace Sass { parse_block_comments(block); } block_stack.pop_back(); + // return passed pointer + // used for syntax sugar return block; } @@ -1074,8 +1129,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())) { @@ -1095,7 +1150,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<','> >()) @@ -1131,13 +1185,20 @@ 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<':'>, @@ -1148,15 +1209,20 @@ namespace Sass { exactly > >(position)) { return new (ctx.mem) List(pstate, 0); } - Expression* list1 = parse_space_list(); - // if it's a singleton, return it directly; don't wrap it - if (!peek_css< exactly<','> >(position)) return list1; + // now try to parse a space list + 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<';'>, @@ -1167,17 +1233,19 @@ namespace Sass { exactly > >(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(); - // if it's a singleton, return it directly; don't wrap it + // if it's a singleton, return it (don't wrap it) if (peek_css< alternatives < // exactly<'!'>, exactly<';'>, @@ -1208,42 +1276,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(); - // if it's a singleton, return it directly; don't wrap it - if (!peek_css< kwd_or >()) return conj1; - + // parse the left hand side conjunction + 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(); - // if it's a singleton, return it directly; don't wrap it - if (!peek_css< kwd_and >()) return rel1; - + // parse the left hand side relation + 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(); - // if it's a singleton, return it directly; don't wrap it + // 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, @@ -1252,8 +1330,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 @@ -1261,22 +1339,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(); - // if it's a singleton, return it directly; don't wrap it + 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; @@ -1285,19 +1371,20 @@ namespace Sass { operands.push_back(parse_operators()); } - return fold_operands(term1, operands, operators); + 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 directly; don't wrap it - if (!peek< class_char< static_ops > >(position)) return factor; + // if it's a singleton, return it (don't wrap it) + if (!peek< class_char< static_ops > >()) return factor; return parse_operators(factor); } @@ -1306,6 +1393,7 @@ namespace Sass { // 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; @@ -1318,12 +1406,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) { @@ -1375,6 +1470,7 @@ namespace Sass { } } + // parse one value for a list Expression* Parser::parse_value() { lex< css_comments >(); @@ -1584,6 +1680,7 @@ 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>()) { @@ -1603,18 +1700,22 @@ namespace Sass { } (*schema) << interp_node; } + // lex some string constants else if (lex< exactly<'%'> >()) { (*schema) << new (ctx.mem) String_Constant(pstate, lexed); } else if (lex< identifier >()) { (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); } + // 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 > >()) { @@ -1623,6 +1724,7 @@ namespace Sass { (*schema) << factor; } } + // lex hex color value else if (lex< hex >()) { (*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, unquote(lexed)); } @@ -1632,9 +1734,11 @@ namespace Sass { else if (lex< quoted_string >()) { (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); } + // lex (normalized) variable else if (lex< variable >()) { (*schema) << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); } + // lex a value in parentheses else if (peek< parenthese_scope >()) { (*schema) << parse_factor(); } @@ -1725,6 +1829,7 @@ namespace Sass { return schema; } + // calc functions should preserve arguments Function_Call* Parser::parse_calc_function() { lex< identifier >(); @@ -1832,16 +1937,19 @@ namespace Sass { 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; + // 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); } + // EO parse_while_directive Media_Block* Parser::parse_media_block() { lex< kwd_media >(); @@ -1917,6 +2025,8 @@ 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 >(); @@ -1932,6 +2042,8 @@ namespace Sass { return new (ctx.mem) Supports_Block(supports_source_position, queries, block); } + // 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); @@ -1945,7 +2057,10 @@ namespace Sass { return fq; } + // EO parse_supports_queries + // parse one query operation + // may encounter nested queries Supports_Condition* Parser::parse_supports_query() { if (peek< kwd_not >(position)) return parse_supports_negation(); @@ -2170,7 +2285,10 @@ namespace Sass { return result; } + // 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; @@ -2229,10 +2347,15 @@ namespace Sass { result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0; result.has_interpolants = saw_interpolant; + // return result return result; } + // 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) { const char* p = start ? start : position; @@ -2279,8 +2402,10 @@ namespace Sass { result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0; result.has_interpolants = saw_interpolant; + // return result return result; } + // EO lookahead_for_value void Parser::read_bom() { diff --git a/parser.hpp b/parser.hpp index 0a8303d47f..a5606e8460 100644 --- a/parser.hpp +++ b/parser.hpp @@ -222,6 +222,7 @@ namespace Sass { Simple_Selector* parse_pseudo_selector(); Attribute_Selector* parse_attribute_selector(); Block* parse_block(); + Block* parse_block(Block* root); bool parse_number_prefix(); Declaration* parse_declaration(); Expression* parse_map_value(); From 7a30de02e8d41e76fb0af3e56fc2c6200a5faec0 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 8 Jun 2015 01:38:48 +0200 Subject: [PATCH 08/31] Parser refactoring --- ast.cpp | 3 +- constants.cpp | 7 ++ constants.hpp | 5 + parser.cpp | 323 ++++++++++++++++++++++---------------------------- parser.hpp | 5 - 5 files changed, 154 insertions(+), 189 deletions(-) diff --git a/ast.cpp b/ast.cpp index d9608afd07..60380bc47c 100644 --- a/ast.cpp +++ b/ast.cpp @@ -211,7 +211,8 @@ namespace Sass { { 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 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/parser.cpp b/parser.cpp index 7051a7b306..d183df4c97 100644 --- a/parser.cpp +++ b/parser.cpp @@ -91,7 +91,7 @@ namespace Sass { Lookahead lookahead_result; while (position < end) { parse_block_comments(root); - if (peek< kwd_import >()) { + if (lex < kwd_import >()) { Import* imp = parse_import(); if (!imp->urls().empty()) (*root) << imp; if (!imp->files().empty()) { @@ -105,7 +105,7 @@ namespace Sass { else if (peek< kwd_mixin >() || peek< kwd_function >()) { (*root) << parse_definition(); } - else if (peek< variable >()) { + else if (lex < variable >()) { (*root) << parse_assignment(); semicolon = true; error_message = "top-level variable binding must be terminated by ';'"; @@ -113,7 +113,7 @@ namespace Sass { /*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) { (*root) << parse_propset(); }*/ - else if (peek< kwd_include_directive >() /* || peek< exactly<'+'> >() */) { + else if (lex < kwd_include_directive >() /* || peek< exactly<'+'> >() */) { Mixin_Call* mixin_call = parse_include_directive(); (*root) << mixin_call; if (!mixin_call->block()) { @@ -124,35 +124,35 @@ namespace Sass { else if (peek< kwd_if_directive >()) { (*root) << parse_if_directive(); } - else if (peek< kwd_for_directive >()) { + else if (lex < kwd_for_directive >()) { (*root) << parse_for_directive(); } - else if (peek< kwd_each_directive >()) { + else if (lex < kwd_each_directive >()) { (*root) << parse_each_directive(); } - else if (peek< kwd_while_directive >()) { + else if (lex < kwd_while_directive >()) { (*root) << parse_while_directive(); } - else if (peek< kwd_media >()) { + else if (lex < kwd_media >()) { (*root) << parse_media_block(); } - else if (peek< kwd_at_root >()) { + else if (lex < kwd_at_root >()) { (*root) << parse_at_root_block(); } - else if (peek< kwd_supports_directive >()) { + else if (lex < kwd_supports_directive >()) { (*root) << parse_supports_directive(); } - else if (peek< kwd_warn >()) { + else if (lex < kwd_warn >()) { (*root) << parse_warning(); semicolon = true; error_message = "top-level @warn directive must be terminated by ';'"; } - else if (peek< kwd_err >()) { + else if (lex < kwd_err >()) { (*root) << parse_error(); semicolon = true; error_message = "top-level @error directive must be terminated by ';'"; } - else if (peek< kwd_dbg >()) { + else if (lex < kwd_dbg >()) { (*root) << parse_debug(); semicolon = true; error_message = "top-level @debug directive must be terminated by ';'"; @@ -162,7 +162,7 @@ namespace Sass { lex< quoted_string >(); lex< one_plus< exactly<';'> > >(); } - else if (peek< at_keyword >()) { + else if (lex< at_keyword >()) { At_Rule* at_rule = parse_at_rule(); (*root) << at_rule; if (!at_rule->block()){ @@ -281,7 +281,6 @@ namespace Sass { Import* Parser::parse_import() { - lex< kwd_import >(); Import* imp = new (ctx.mem) Import(pstate); bool first = true; do { @@ -431,7 +430,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); @@ -557,20 +555,18 @@ namespace Sass { // called after parsing `kwd_include_directive` Mixin_Call* Parser::parse_include_directive() { - lex< kwd_include_directive >() /* || lex< exactly<'+'> >() */; // lex identifier into `lexed` var if (!lex< identifier >()) error("invalid name in @include directive", pstate); - ParserState source_position_of_call = pstate; // 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 - Arguments* args = parse_arguments(); - Block* content = 0; + call->arguments(parse_arguments()); // parse optional block if (peek< exactly<'{'> >()) { - content = parse_block(); + call->block(parse_block()); } - Mixin_Call* call = new (ctx.mem) Mixin_Call(source_position_of_call, name, args, content); // return ast node return call; } @@ -588,12 +584,7 @@ namespace Sass { group->last_block(block_stack.back()); do { reloop = false; - if (peek< alternatives < - exactly<'{'>, - exactly<'}'>, - exactly<')'>, - exactly<';'> - > >()) + 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) { @@ -628,7 +619,7 @@ namespace Sass { (*group) << comb; } while (reloop); - while (lex< kwd_optional >()) { + while (lex_css< kwd_optional >()) { group->is_optional(true); } return group; @@ -645,14 +636,7 @@ namespace Sass { // parse the left hand side Compound_Selector* lhs = 0; // special case if it starts with combinator ([+~>]) - if (peek_css< alternatives < - exactly<'+'>, - exactly<'~'>, - exactly<'>'> - > >()) - // no selector before the combinator - { lhs = 0; } - else { + if (!peek_css< class_char < selector_combinator_ops > >()) { // parse the left hand side lhs = parse_compound_selector(); sel_source_position = before_token; @@ -671,14 +655,7 @@ namespace Sass { // ToDo: make sure we update pstate for ancestor of (lex < zero >()); Complex_Selector* rhs; // check if we got the abort condition (ToDo: optimize) - if (peek_css< alternatives < - exactly<','>, - exactly<')'>, - exactly<'{'>, - exactly<'}'>, - exactly<';'>, - kwd_optional - > >()) + if (peek_css< class_char < complex_selector_delims > >()) // no selector after the combinator { rhs = 0; } else { @@ -756,7 +733,7 @@ namespace Sass { { 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)); @@ -773,11 +750,11 @@ namespace Sass { else if (peek< exactly<':'> >()) { return parse_pseudo_selector(); } - else if (peek< exactly<'['> >()) { + else if (lex < exactly<'['> >()) { return parse_attribute_selector(); } else if (lex< placeholder >()) { - Selector_Placeholder* sel = new (ctx.mem) Selector_Placeholder(pstate, unquote(lexed)); + Selector_Placeholder* sel = new (ctx.mem) Selector_Placeholder(pstate, lexed); sel->media_block(last_media_block); sel->last_block(block_stack.back()); return sel; @@ -860,7 +837,6 @@ namespace Sass { 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); @@ -927,9 +903,8 @@ namespace Sass { parse_block_comments(block); if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break; } - else if (peek< kwd_import >(position)) { + else if (lex < 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(); @@ -941,7 +916,7 @@ namespace Sass { } semicolon = true; } - else if (lex< variable >()) { + else if (lex < variable >()) { (*block) << parse_assignment(); semicolon = true; } @@ -951,28 +926,28 @@ namespace Sass { else if (peek< kwd_if_directive >()) { (*block) << parse_if_directive(); } - else if (peek< kwd_for_directive >()) { + else if (lex < kwd_for_directive >()) { (*block) << parse_for_directive(); } - else if (peek< kwd_each_directive >()) { + else if (lex < kwd_each_directive >()) { (*block) << parse_each_directive(); } - else if (peek < kwd_while_directive >()) { + else if (lex < kwd_while_directive >()) { (*block) << parse_while_directive(); } - else if (peek < kwd_return_directive >()) { + else if (lex < kwd_return_directive >()) { (*block) << parse_return_directive(); semicolon = true; } - else if (peek< kwd_warn >()) { + else if (lex < kwd_warn >()) { (*block) << parse_warning(); semicolon = true; } - else if (peek< kwd_err >()) { + else if (lex < kwd_err >()) { (*block) << parse_error(); semicolon = true; } - else if (peek< kwd_dbg >()) { + else if (lex < kwd_dbg >()) { (*block) << parse_debug(); semicolon = true; } @@ -982,7 +957,7 @@ namespace Sass { else if (peek< kwd_mixin >() || peek< kwd_function >()) { (*block) << parse_definition(); } - else if (peek< kwd_include_directive >(position)) { + else if (lex < kwd_include_directive >(position)) { Mixin_Call* the_call = parse_include_directive(); (*block) << the_call; // don't need a semicolon after a content block @@ -1010,13 +985,13 @@ namespace Sass { (*block) << new (ctx.mem) Extension(pstate, target); semicolon = true; } - else if (peek< kwd_media >()) { + else if (lex < kwd_media >()) { (*block) << parse_media_block(); } - else if (peek< kwd_supports_directive >()) { + else if (lex < kwd_supports_directive >()) { (*block) << parse_supports_directive(); } - else if (peek< kwd_at_root >()) { + else if (lex < kwd_at_root >()) { (*block) << parse_at_root_block(); } // ignore the @charset directive for now @@ -1024,7 +999,7 @@ namespace Sass { lex< quoted_string >(); lex< one_plus< exactly<';'> > >(); } - else if (peek< at_keyword >()) { + else if (lex< at_keyword >()) { At_Rule* at_rule = parse_at_rule(); (*block) << at_rule; if (!at_rule->block()) semicolon = true; @@ -1201,12 +1176,14 @@ namespace Sass { // 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); } @@ -1224,13 +1201,14 @@ namespace Sass { { // check for abort condition if (peek_css< alternatives < - // exactly<'!'>, exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, exactly<':'>, - exactly + exactly, + default_flag, + global_flag > >(position) ) { break; } // otherwise add another expression @@ -1371,6 +1349,7 @@ namespace Sass { operands.push_back(parse_operators()); } + if (operands.size() == 0) return lhs; return fold_operands(lhs, operands, operators); } @@ -1384,7 +1363,7 @@ namespace Sass { if (ss && ss->has_interpolants()) return factor; } // if it's a singleton, return it (don't wrap it) - if (!peek< class_char< static_ops > >()) return factor; + if (!peek_css< class_char< static_ops > >()) return factor; return parse_operators(factor); } @@ -1485,6 +1464,10 @@ namespace Sass { if ((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); } @@ -1495,10 +1478,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 >()) @@ -1515,9 +1495,6 @@ namespace Sass { 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)); } @@ -1603,7 +1580,6 @@ namespace Sass { String* Parser::parse_string() { - lex< quoted_string >(); Token token(lexed); return parse_interpolated_chunk(token); } @@ -1682,14 +1658,18 @@ namespace Sass { { // 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, " "); } + // lex an interpolant /#{...}/ else if (lex< interpolant >()) { Token insides(Token(lexed.begin + 2, lexed.end - 1)); Expression* interp_node; @@ -1701,12 +1681,18 @@ namespace Sass { (*schema) << interp_node; } // lex some string constants - else if (lex< exactly<'%'> >()) { + else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) { (*schema) << new (ctx.mem) String_Constant(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); @@ -1717,26 +1703,11 @@ namespace Sass { } // 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); - } - // lex (normalized) variable - 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 >()) { @@ -1894,7 +1865,6 @@ namespace Sass { 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); string var(Util::normalize_underscores(lexed)); @@ -1914,7 +1884,6 @@ namespace Sass { 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; @@ -1940,19 +1909,21 @@ namespace Sass { // 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 optional 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; List* media_queries = parse_media_queries(); @@ -2029,33 +2000,41 @@ namespace Sass { // 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(); - + // 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 if (!peek< exactly<'{'> >()) { error("expected '{' in feature query", pstate); } - Block* block = parse_block(); - - return new (ctx.mem) Supports_Block(supports_source_position, queries, block); + // 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); + // loop until the abort condition 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; + // 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 @@ -2063,78 +2042,62 @@ namespace Sass { // may encounter nested queries 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; - } - - Supports_Condition* Parser::parse_supports_negation() - { - lex< kwd_not >(); - - Supports_Condition* cond = parse_supports_query(); - cond->operand(Supports_Condition::NOT); - - return cond; - } - - Supports_Condition* Parser::parse_supports_conjunction() - { - lex< kwd_and >(); - - Supports_Condition* cond = parse_supports_query(); - cond->operand(Supports_Condition::AND); - - return cond; - } - - Supports_Condition* Parser::parse_supports_disjunction() - { - lex< kwd_or >(); - - Supports_Condition* cond = parse_supports_query(); - cond->operand(Supports_Condition::OR); - - return cond; - } - - Supports_Condition* Parser::parse_supports_declaration() - { - Declaration* declaration = parse_declaration(); - Supports_Condition* cond = new (ctx.mem) Supports_Condition(declaration->pstate(), - 1, - declaration->property(), - declaration->value()); + Supports_Condition* cond = 0; + // lex optional comments + lex < css_whitespace >(); + // parse `not` query operator + if (lex < kwd_not >(position)) { + cond = parse_supports_query(); + cond->operand(Supports_Condition::NOT); + } + // parse `and` query operator + else if (lex < kwd_and >(position)) { + cond = parse_supports_query(); + cond->operand(Supports_Condition::AND); + } + // parse `or` query operator + else if (lex < kwd_or >(position)) { + cond = parse_supports_query(); + cond->operand(Supports_Condition::OR); + } + // parse another list with queries + else if (lex < exactly <'('> >()) { + // create the inner (parenthesis) condition + cond = new (ctx.mem) Supports_Condition(pstate); + // parse inner supports queries recursively + while (!peek < exactly <')'> >()) + (*cond) << parse_supports_query(); + // 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 + cond = (cond->length() == 1) ? (*cond)[0] : cond; + } + else { + // or parse something declaration like + Declaration* declaration = parse_declaration(); + cond = new (ctx.mem) Supports_Condition(declaration->pstate(), + 1, + declaration->property(), + declaration->value()); + // ToDo: maybe we need an addition error condition? + } return cond; } + // EO parse_supports_query 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<'('> >()) { + if (lex< exactly<'('> >()) { expr = parse_at_root_expression(); - body = parse_block(); } - else if (peek< exactly<'{'> >()) { + if (peek< exactly<'{'> >()) { body = parse_block(); } else if ((lookahead_result = lookahead_for_selector(position)).found) { @@ -2150,7 +2113,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 > >()) { @@ -2174,7 +2136,6 @@ namespace Sass { At_Rule* Parser::parse_at_rule() { - lex(); string kwd(lexed); ParserState at_source_position = pstate; Selector* sel = 0; @@ -2200,25 +2161,21 @@ namespace Sass { 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()); } diff --git a/parser.hpp b/parser.hpp index a5606e8460..df169f1221 100644 --- a/parser.hpp +++ b/parser.hpp @@ -262,11 +262,6 @@ namespace Sass { 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(); From 91bdfa5abafebcdd238c61fe238cdaaba91c6e03 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 8 Jun 2015 03:05:51 +0200 Subject: [PATCH 09/31] Performance improvements --- ast.cpp | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/ast.cpp b/ast.cpp index 60380bc47c..3450f9c03e 100644 --- a/ast.cpp +++ b/ast.cpp @@ -740,17 +740,17 @@ namespace Sass { string Number::unit() const { - stringstream u; + string u; for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) { - if (i) u << '*'; - u << numerator_units_[i]; + if (i) u += '*'; + u += numerator_units_[i]; } - if (!denominator_units_.empty()) u << '/'; + if (!denominator_units_.empty()) u += '/'; for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) { - if (i) u << '*'; - u << denominator_units_[i]; + if (i) u += '*'; + u += denominator_units_[i]; } - return u.str(); + return u; } bool Number::is_unitless() @@ -941,17 +941,11 @@ namespace Sass { bool Number::operator== (Expression* rhs) const { - try - { - Number l(pstate_, value_, unit()); - Number& r = dynamic_cast(*rhs); - l.normalize(find_convertible_unit()); - r.normalize(find_convertible_unit()); - return l.unit() == r.unit() && - l.value() == r.value(); + if (Number* r = static_cast(rhs)) { + return (value() == r->value()) && + (numerator_units_ == r->numerator_units_) && + (denominator_units_ == r->denominator_units_); } - catch (std::bad_cast&) {} - catch (...) { throw; } return false; } From c9471e11bbe1918f3fba734da31dc31bcb1f3fc0 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 8 Jun 2015 03:20:52 +0200 Subject: [PATCH 10/31] Next parser refactoring iteration [WIP] --- Makefile | 2 - Makefile.am | 2 - ast.cpp | 394 ++++++++--- ast.hpp | 118 ++-- ast_def_macros.hpp | 25 + ast_fwd_decl.hpp | 1 - bind.cpp | 13 +- context.cpp | 15 +- contextualize.cpp | 182 ++--- contextualize.hpp | 47 +- contextualize_eval.cpp | 88 --- contextualize_eval.hpp | 37 - cssize.cpp | 2 +- debugger.hpp | 103 ++- emitter.cpp | 1 + emitter.hpp | 1 + error_handling.cpp | 3 + error_handling.hpp | 5 + eval.cpp | 409 ++++++++---- eval.hpp | 49 +- expand.cpp | 337 +++++----- expand.hpp | 26 +- extend.cpp | 158 +++-- extend.hpp | 2 +- functions.cpp | 30 +- inspect.cpp | 50 +- inspect.hpp | 1 - lexer.cpp | 9 + lexer.hpp | 2 +- listize.cpp | 4 +- listize.hpp | 2 +- node.cpp | 57 +- output.cpp | 32 +- parser.cpp | 1259 +++++++++++++++-------------------- parser.hpp | 26 +- prelexer.cpp | 41 +- prelexer.hpp | 7 + remove_placeholders.cpp | 26 +- remove_placeholders.hpp | 3 - sass.h | 6 +- sass_util.cpp | 20 +- test/test_node.cpp | 42 +- test/test_superselector.cpp | 2 +- util.cpp | 42 +- win/libsass.filters | 6 - win/libsass.vcxproj | 4 - 46 files changed, 1939 insertions(+), 1752 deletions(-) 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 3450f9c03e..6b86231d1c 100644 --- a/ast.cpp +++ b/ast.cpp @@ -2,6 +2,7 @@ #include "context.hpp" #include "node.hpp" #include "extend.hpp" +#include "debugger.hpp" #include "to_string.hpp" #include #include @@ -20,6 +21,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,7 +219,59 @@ 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); + } + cerr << "INVALID INTERNAL STATE\n"; + } else { + cerr << "INVALID INTERNAL STATE\n"; + } + return false; + } + + bool Simple_Selector::is_superselector_of(Compound_Selector* sub) + { + cerr << "doda\n"; + return false; + } + + bool Pseudo_Selector::is_superselector_of(Compound_Selector* compound) + { + cerr << "is_sup pseudo Compound_Selector\n"; + return false; + } + bool Pseudo_Selector::is_superselector_of(Complex_Selector* complex) + { + cerr << "is_sup pseudo Complex_Selector\n"; + return false; + } + bool Pseudo_Selector::is_superselector_of(Selector_List* list) + { + cerr << "is_sup pseudo Selector_List\n"; + return false; + } + + bool Compound_Selector::is_superselector_of(Selector_List* rhs, string wrapped) + { +//cerr << "DAHJKA\n"; + 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; @@ -238,49 +302,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 { + cerr << "do not\n"; + } + } + 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 { @@ -309,18 +446,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 @@ -341,12 +466,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; @@ -354,10 +479,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(); @@ -365,7 +490,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) @@ -373,16 +498,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(); } @@ -424,43 +552,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 @@ -468,22 +559,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->size(); 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; @@ -493,7 +655,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(); } @@ -544,10 +706,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; @@ -557,40 +736,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) { @@ -637,7 +816,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; } @@ -671,6 +850,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 46d75c250e..b037e99e27 100644 --- a/ast.hpp +++ b/ast.hpp @@ -293,6 +293,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) @@ -306,7 +307,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() @@ -331,9 +332,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 @@ -372,13 +374,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; } @@ -396,10 +397,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; } @@ -453,10 +453,11 @@ namespace Sass { ADD_PROPERTY(String*, property) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_important) + ADD_PROPERTY(bool, surfer); 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), surfer_(false) { } ATTACH_OPERATIONS() }; @@ -1664,22 +1665,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. ///////////////////////////////////////// @@ -1693,7 +1678,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) @@ -1719,9 +1703,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() }; @@ -1736,9 +1721,14 @@ 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); + // virtual bool is_superselector_of(Complex_Selector* sub); + // virtual bool is_superselector_of(Selector_List* sub); + bool operator==(const Simple_Selector& rhs) const; inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); } @@ -1753,16 +1743,15 @@ namespace Sass { // parent selectors can occur in selectors but also // inside strings in declarations (Compound_Selector). // only one simple parent selector means the first case. - class Selector_Reference : public Simple_Selector { - ADD_PROPERTY(Selector*, selector) + class Parent_Selector : public Simple_Selector { public: - Selector_Reference(ParserState pstate, Selector* r = 0) - : Simple_Selector(pstate), selector_(r) + 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"; } @@ -1869,6 +1858,10 @@ namespace Sass { && ! is_pseudo_class_element(name_); } + virtual bool is_superselector_of(Compound_Selector* compound); + virtual bool is_superselector_of(Complex_Selector* complex); + virtual bool is_superselector_of(Selector_List* list); + // A pseudo-element is made of two colons (::) followed by the name. // The `::` notation is introduced by the current document in order to // establish a discrimination between pseudo-classes and pseudo-elements. @@ -1902,6 +1895,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() @@ -1923,6 +1917,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) { @@ -1932,23 +1927,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 = size(); 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; } - virtual bool is_superselector_of(Compound_Selector* sub); - // virtual bool is_superselector_of(Complex_Selector* sub); - // virtual 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; @@ -1959,8 +1964,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 @@ -1992,6 +1996,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, @@ -2001,13 +2010,23 @@ 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()->size() == 1 && dynamic_cast((*s->head())[0])) { + s = s->tail(); + } + return s; + }; size_t length(); - virtual bool is_superselector_of(Compound_Selector* sub); - virtual bool is_superselector_of(Complex_Selector* sub); - virtual 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(); @@ -2093,10 +2112,15 @@ 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(); - virtual bool is_superselector_of(Compound_Selector* sub); - virtual bool is_superselector_of(Complex_Selector* sub); - virtual 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&); 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/bind.cpp b/bind.cpp index 207681fa65..5e3a0609f2 100644 --- a/bind.cpp +++ b/bind.cpp @@ -12,6 +12,7 @@ namespace Sass { void bind(string callee, Parameters* ps, Arguments* as, Context& ctx, Env* env, Eval* eval) { + Listize listize(ctx); map param_map; // Set up a map to ensure named arguments refer to actual parameters. Also @@ -29,6 +30,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()) { @@ -227,15 +230,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/context.cpp b/context.cpp index 4b7a3cef19..eb6f0dfd96 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" @@ -35,6 +33,7 @@ #include "sass2scss.h" #include "prelexer.hpp" #include "emitter.hpp" +#include "debugger.hpp" namespace Sass { using namespace Constants; @@ -340,30 +339,30 @@ 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 +//debug_ast(root, "parsed: "); root = root->perform(&expand)->block(); +//debug_ast(root, "expand: "); // merge and bubble certain rules root = root->perform(&cssize)->block(); +// debug_ast(root, "cssize: "); // should we extend something? if (!subset_map.empty()) { // create crtp visitor object Extend extend(*this, subset_map); // extend tree nodes root->perform(&extend); +//debug_ast(root, "extend: "); } // clean up by removing empty placeholders // ToDo: maybe we can do this somewhere else? Remove_Placeholders remove_placeholders(*this); root->perform(&remove_placeholders); - +//debug_ast(root, "cleaned: "); // return processed tree return root; } diff --git a/contextualize.cpp b/contextualize.cpp index fca78c41cd..24fa462fa1 100644 --- a/contextualize.cpp +++ b/contextualize.cpp @@ -1,148 +1,86 @@ #include "contextualize.hpp" -#include "ast.hpp" -#include "eval.hpp" -#include "backtrace.hpp" -#include "to_string.hpp" -#include "parser.hpp" -namespace Sass { +#include +#include - Contextualize::Contextualize(Context& ctx, Env* env, Backtrace* bt, Selector* placeholder, Selector* extender) - : ctx(ctx), env(env), backtrace(bt), parent(0), placeholder(placeholder), extender(extender) - { } +#include "listize.hpp" +#include "to_string.hpp" +#include "context.hpp" +#include "backtrace.hpp" +#include "error_handling.hpp" - Contextualize::~Contextualize() { } +namespace Sass { - Selector* Contextualize::fallback_impl(AST_Node* n) - { return parent; } + Listize2::Listize2(Context& ctx) + : ctx(ctx) + { } - Contextualize* Contextualize::with(Selector* s, Env* e, Backtrace* bt, Selector* p, Selector* ex) + Expression* Listize2::operator()(Selector_List* sel) { - 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; - } + 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 ss->length() ? ss : 0; + return l; } - Selector* Contextualize::operator()(Complex_Selector* s) + Expression* Listize2::operator()(Compound_Selector* sel) { - 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; + To_String to_string; + string str; + for (size_t i = 0, L = sel->length(); i < L; ++i) { + Expression* e = (*sel)[i]->perform(this); + if (e) str += e->perform(&to_string); } + return new (ctx.mem) String_Constant(sel->pstate(), str); } - Selector* Contextualize::operator()(Compound_Selector* s) + Expression* Listize2::operator()(Complex_Selector* sel) { - To_String to_string(&ctx); - if (placeholder && extender && s->perform(&to_string) == placeholder->perform(&to_string)) { - return extender; + List* l = new (ctx.mem) List(sel->pstate(), 2); + + Compound_Selector* head = sel->head(); + if (head && !head->is_empty_reference()) + { + Expression* hh = head->perform(this); + if (hh) *l << hh; } - 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; + + switch(sel->combinator()) + { + case Complex_Selector::PARENT_OF: + *l << new (ctx.mem) String_Constant(sel->pstate(), ">"); + break; + case Complex_Selector::ADJACENT_TO: + *l << new (ctx.mem) String_Constant(sel->pstate(), "+"); + break; + case Complex_Selector::PRECEDES: + *l << new (ctx.mem) String_Constant(sel->pstate(), "~"); + break; + case Complex_Selector::ANCESTOR_OF: + break; } - 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; + Complex_Selector* tail = sel->tail(); + if (tail) + { + Expression* tt = tail->perform(this); + if (tt && tt->concrete_type() == Expression::LIST) + { *l += static_cast(tt); } + else if (tt) *l << static_cast(tt); + } + if (l->length() == 0) return 0; + return l; } - 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) + Expression* Listize2::operator()(Parent_Selector* sel) { - To_String to_string(&ctx); - if (placeholder && extender && p->perform(&to_string) == placeholder->perform(&to_string)) { - return extender; - } - else { - return p; - } + return 0; } - Selector* Contextualize::operator()(Selector_Reference* s) + Expression* Listize2::fallback_impl(AST_Node* n) { - if (!parent) return 0; - Selector_Reference* ss = new (ctx.mem) Selector_Reference(*s); - ss->selector(parent); - return ss; + return static_cast(n); } } diff --git a/contextualize.hpp b/contextualize.hpp index 7efccd1c08..ca7925dd09 100644 --- a/contextualize.hpp +++ b/contextualize.hpp @@ -1,46 +1,41 @@ #ifndef SASS_CONTEXTUALIZE_H #define SASS_CONTEXTUALIZE_H +#include +#include + +#include "ast.hpp" #include "context.hpp" #include "operation.hpp" #include "environment.hpp" -#include "ast_fwd_decl.hpp" namespace Sass { - struct Backtrace; + using namespace std; typedef Environment Env; + struct Backtrace; + + class Listize2 : public Operation_CRTP { - class Contextualize : public Operation_CRTP { + Context& ctx; + Expression* fallback_impl(AST_Node* n); 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*); + Listize2(Context&); + virtual ~Listize2() { } + + using Operation::operator(); + + Expression* operator()(Selector_List*); + Expression* operator()(Complex_Selector*); + Expression* operator()(Compound_Selector*); + Expression* operator()(Parent_Selector*); template - Selector* fallback(U x) { return fallback_impl(x); } + Expression* fallback(U x) { return fallback_impl(x); } }; + } #endif diff --git a/contextualize_eval.cpp b/contextualize_eval.cpp index 483bd8a252..6a4e1bb0f5 100644 --- a/contextualize_eval.cpp +++ b/contextualize_eval.cpp @@ -1,93 +1,5 @@ #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 index 738b4d606d..00a75def50 100644 --- a/contextualize_eval.hpp +++ b/contextualize_eval.hpp @@ -1,44 +1,7 @@ #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..cf942a70fc 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()); diff --git a/debugger.hpp b/debugger.hpp index fec863c72c..64ed315ac0 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -3,6 +3,7 @@ #include #include +#include "node.hpp" #include "ast_fwd_decl.hpp" using namespace std; @@ -64,8 +65,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 +80,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 +159,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 +174,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 +217,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); @@ -360,18 +354,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 + ">"); + if (ruleset->block()) for(auto i : ruleset->block()->elements()) { debug_ast(i, ind + "|", env); } } 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); @@ -555,4 +550,52 @@ 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::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/emitter.hpp b/emitter.hpp index 2ee538a24e..3cbb36eb4d 100644 --- a/emitter.hpp +++ b/emitter.hpp @@ -73,6 +73,7 @@ namespace Sass { void append_indentation(); void append_optional_space(void); void append_mandatory_space(void); + void append_forced_space(void); void append_special_linefeed(void); void append_optional_linefeed(void); void append_mandatory_linefeed(void); diff --git a/error_handling.cpp b/error_handling.cpp index 522994829b..af2afb3027 100644 --- a/error_handling.cpp +++ b/error_handling.cpp @@ -8,6 +8,9 @@ namespace Sass { : type(type), pstate(pstate), message(message) { } + Css_Error::Css_Error() + { } + void warn(string msg, ParserState pstate) { cerr << "Warning: " << msg<< endl; diff --git a/error_handling.hpp b/error_handling.hpp index 6580b79067..dd91fc7600 100644 --- a/error_handling.hpp +++ b/error_handling.hpp @@ -10,6 +10,11 @@ namespace Sass { struct Backtrace; + struct Css_Error { + Css_Error(); + + }; + struct Sass_Error { enum Type { read, write, syntax, evaluation }; diff --git a/eval.cpp b/eval.cpp index bc8282fb7e..3833dcc3fa 100644 --- a/eval.cpp +++ b/eval.cpp @@ -12,11 +12,18 @@ #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" +#include "contextualize.hpp" +#include "debugger.hpp" namespace Sass { using namespace std; @@ -25,31 +32,39 @@ namespace Sass { inline double sub(double x, double y) { return x - y; } 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 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte - add, sub, mul, div, fmod + 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() { - contextualize = contextualize->with(0, e, bt); - env = e; - backtrace = bt; - return this; + return ctx; } - Eval* Eval::with(Selector* c, Env* e, Backtrace* bt, Selector* p, Selector* ex) // for setting the env before eval'ing an expression + Env* Eval::environment() { - contextualize = contextualize->with(c, e, bt, p, ex); - env = e; - backtrace = bt; - return this; + return exp.environment(); + } + + Selector_List* Eval::selector() + { + return exp.selector(); + } + + Backtrace* Eval::backtrace() + { + return exp.backtrace(); } Expression* Eval::operator()(Block* b) @@ -64,6 +79,7 @@ namespace Sass { Expression* Eval::operator()(Assignment* a) { + Env* env = exp.environment(); string var(a->variable()); if (a->is_global()) { if (a->is_default()) { @@ -124,14 +140,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 @@ -154,11 +174,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); @@ -197,6 +218,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) { @@ -287,6 +309,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]")) { @@ -308,7 +331,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; @@ -319,6 +342,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]")) { @@ -348,6 +372,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]")) { @@ -465,8 +490,19 @@ 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 @@ -543,10 +579,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]"); @@ -555,77 +591,55 @@ 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_Constant(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; + Env fn_env(def->environment()); + exp.env_stack.push_back(&fn_env); - // if it's user-defined, eval the body - if (body) { - - 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) { @@ -639,62 +653,32 @@ 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; - } - - // backtrace = here.parent; - // env = old_env; - // link back to function definition // only do this for custom functions @@ -705,6 +689,7 @@ namespace Sass { result->is_delayed(result->concrete_type() == Expression::STRING); result = result->perform(this); } while (result->concrete_type() == Expression::NONE); + exp.env_stack.pop_back(); return result; } @@ -722,6 +707,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; @@ -758,6 +744,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; @@ -850,6 +839,7 @@ 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()) { @@ -864,6 +854,13 @@ namespace Sass { string str = str_constant->value(); if (!str_constant->quote_mark()) str = unquote(str); return evacuate_escapes(str); + } 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 (Selector_List* sel = dynamic_cast(s)) { + To_String to_string(&ctx); + return evacuate_quotes(sel->perform(this)->perform(&to_string)); } else if (String_Schema* str_schema = dynamic_cast(s)) { string res = ""; for(auto i : str_schema->elements()) @@ -899,9 +896,6 @@ namespace Sass { } 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); - return evacuate_quotes(interpolation(ex)); } else if (Unary_Expression* var = dynamic_cast(s)) { Expression* ex = var->perform(this); return evacuate_quotes(interpolation(ex)); @@ -1068,19 +1062,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); @@ -1384,4 +1365,168 @@ 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->size(); 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]->size() > 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->size() > 0 && dynamic_cast((*head)[0])) { + // do we have any parents to interpolate + Selector_List* pr = selector(); + if (pr && pr->size() > 0) { + for (size_t n = 0, nL = pr->size(); n < nL; ++n) { + if (tail) { + vector rv; + Selector_List* tails = operator()(tail); + for (size_t m = 0, mL = tails->size(); 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->size(); 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->size(); 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->size(); 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->size(); 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(); + } + + } + +// (*sl)[i] = (*sl)[i]->first(); + + // sl->remove_parent_selectors(); +To_String to_string; +// cerr << "returned [" << sl->perform(&to_string) << "]" << endl; + 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()(Selector_Placeholder* p) + { + To_String to_string(&ctx); +// if (placeholder && extender && p->perform(&to_string) == placeholder->perform(&to_string)) { +// return extender; +// } +// else { + return p; +// } + } + + Expression* Eval::operator()(Parent_Selector* p) + { + Selector_List* pr = selector(); + exp.selector_stack.pop_back(); + auto ss = pr ? pr->perform(this) : 0; + exp.selector_stack.push_back(pr); + // all selectors must be listized +// if (dynamic_cast(pr)) +// ss = ss->perform(&listize); + return ss; + } + } diff --git a/eval.hpp b/eval.hpp index cabcf712a2..bcea1cd2df 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 @@ -69,7 +65,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; }; + // actual evaluated selectors + Expression* operator()(Selector_Placeholder* s); + 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 a03f7133ad..c13f50a2cc 100644 --- a/expand.cpp +++ b/expand.cpp @@ -1,104 +1,114 @@ +#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" +#include "debugger.hpp" #include "parser.hpp" 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,11 +117,9 @@ 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() @@ -122,13 +130,13 @@ namespace Sass { *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 +147,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 (as) as = static_cast(as->perform(&eval)); + else if (av) av = av->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; 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 +218,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 +244,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 +260,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; } @@ -276,7 +281,7 @@ namespace Sass { { Import* result = new (ctx.mem) Import(imp->pstate()); 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; } @@ -290,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 { @@ -331,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); @@ -346,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); @@ -385,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) { @@ -399,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; @@ -408,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); @@ -435,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())); @@ -456,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; @@ -464,44 +471,73 @@ 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; } +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"; +} + + 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(); +//cerr << "extend\n"; +//debug_ast(extender, "extender: "); +//debug_ast(selector_list, "selector_list: "); +//debug_ast(contextualized, "contextualized: "); 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); - } - 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)); } +// if (!c->head() || c->tail()) { +// error("nested selectors may not be extended", c->pstate(), backtrace()); +// } + 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()->size() > 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; + } +//debug_ast(sel, "sel: "); + if (c->has_line_feed()) sel->has_line_feed(true); + // sel = dynamic_cast(sel->perform(&eval)); +// cerr << "REGISTERING EXTENSION REQUEST: " << endl; +// debug_ast(sel); +// debug_ast(placeholder); + ctx.subset_map.put(placeholder->to_str_vec(), make_pair(sel, placeholder)); } } + selector_stack.pop_back(); + +//debug_extenstion_map(&ctx.subset_map, "ctx.subset_map: "); + 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; @@ -512,21 +548,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(), @@ -537,17 +573,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(), @@ -559,17 +594,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_Constant* msg = new (ctx.mem) String_Constant(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..8af5a2ba50 100644 --- a/expand.hpp +++ b/expand.hpp @@ -9,35 +9,41 @@ #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_at_root; 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..30c1633193 100644 --- a/extend.cpp +++ b/extend.cpp @@ -1,16 +1,21 @@ +#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" #include "parser.hpp" #include "node.hpp" #include "sass_util.hpp" +#include "debugger.hpp" #include "debug.hpp" #include #include + /* NOTES: @@ -45,7 +50,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,6 +68,25 @@ namespace Sass { typedef pair ExtensionPair; typedef vector SubsetMapEntries; +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"; +} #ifdef DEBUG @@ -503,6 +527,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 +1482,7 @@ namespace Sass { static Node extendComplexSelector( Complex_Selector* pComplexSelector, Context& ctx, - ExtensionSubsetMap& subsetMap, + ExtensionSubsetMap& subset_map, set seen); @@ -1485,17 +1510,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: ")) - +// debug_ast(pSelector, "extendCompoundSelector:"); 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,6 +1528,7 @@ namespace Sass { GroupedByToAResult arr; group_by_to_a(entries, extPairKeyFunctor, arr); +// debug_subset_entries(&entries); typedef pair SelsNewSeqPair; typedef vector SelsNewSeqPairCollection; @@ -1517,7 +1543,8 @@ namespace Sass { Complex_Selector& seq = groupedPair.first; vector& group = groupedPair.second; -// DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) +// debug_ast(&seq); + DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) Compound_Selector* pSels = new (ctx.mem) Compound_Selector(pSelector->pstate()); @@ -1530,25 +1557,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,30 +1577,34 @@ 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(); + +// debug_ast(pNewSelector); +// debug_ast(pNewSelector); Complex_Selector* pNewInnerMost = new (ctx.mem) Complex_Selector(pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL); +// debug_ast(pNewInnerMost, "= "); Complex_Selector::Combinator combinator = pNewSelector->clear_innermost(); pNewSelector->set_innermost(pNewInnerMost, combinator); +// pNewSelector->head(0); + +// pNewSelector = pNewSelector; + //debug_ast(pNewSelector); #ifdef DEBUG SourcesSet debugSet; @@ -1595,7 +1619,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,7 +1637,9 @@ 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);; +//debug_ast(pNewSelector, " haqweqweqwe: "); holder.push_back(make_pair(pSels, pNewSelector)); } @@ -1635,8 +1661,9 @@ namespace Sass { DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx)) - - Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subsetMap, recurseSeen); +//debug_ast(pNewSelector, " rec: "); + Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen); +//debug_node(&recurseExtendedSelectors, " reced: "); DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors) @@ -1663,7 +1690,7 @@ namespace Sass { static bool complexSelectorHasExtension( Complex_Selector* pComplexSelector, Context& ctx, - ExtensionSubsetMap& subsetMap) { + ExtensionSubsetMap& subset_map) { bool hasExtension = false; @@ -1673,7 +1700,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 +1739,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,12 +1773,12 @@ 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()); - + //debug_ast(pComplexSelector, "hwa: "); Node complexSelector = complexSelectorToNode(pComplexSelector, ctx); + //debug_node(&complexSelector, "now: "); DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) Node extendedNotExpanded = Node::createCollection(); @@ -1775,8 +1802,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 +1823,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 +1851,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 +1882,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 +1898,8 @@ 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)) { +//debug_ast(pSelector, "test1: "); *pNewSelectors << pSelector; continue; } @@ -1877,10 +1907,12 @@ namespace Sass { extendedSomething = true; set seen; - Node extendedSelectors = extendComplexSelector(pSelector, ctx, subsetMap, seen); + Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen); +//debug_node(&extendedSelectors, "doubled?: "); if (!pSelector->has_placeholder()) { if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) { +//debug_ast(pSelector, "test2: "); *pNewSelectors << pSelector; } } @@ -1888,8 +1920,9 @@ 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; +//debug_node(&childNode, "test2: "); *pNewSelectors << nodeToComplexSelector(childNode, ctx); } } @@ -1932,7 +1965,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,7 +1978,9 @@ namespace Sass { } bool extendedSomething = false; - Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, false, extendedSomething); +//debug_extenstion_map(&subset_map, "++"); + Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subset_map, false, extendedSomething); +//debug_ast(pNewSelectorList, "=="); if (extendedSomething && pNewSelectorList) { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->perform(&to_string)) @@ -1955,13 +1990,10 @@ namespace Sass { // // 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); + + pObject->selector(pNewSelectorList); + } else { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") } @@ -1982,6 +2014,18 @@ namespace Sass { void Extend::operator()(Ruleset* pRuleset) { + if (Selector_List* sl = dynamic_cast(pRuleset->selector())) { + Selector_List* ssl = new (ctx.mem) Selector_List(sl->pstate()); + for (size_t i = 0, iL = sl->size(); i < iL; ++i) { + Complex_Selector* item = (*sl)[i]; + Compound_Selector* hh = new (ctx.mem) Compound_Selector(sl->pstate()); + Complex_Selector* ss = new (ctx.mem) Complex_Selector(sl->pstate()); + *hh << new (ctx.mem) Parent_Selector(sl->pstate()); + ss->head(hh); ss->tail(item); *ssl << ss; + } + pRuleset->selector(ssl); + } +// debug_ast(pRuleset); extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map); pRuleset->block()->perform(this); @@ -1989,18 +2033,14 @@ namespace Sass { 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); - } + // if (pMediaBlock->selector()) { + // extendObjectWithSelectorAndBlock(pMediaBlock, ctx, subset_map); + // } pMediaBlock->block()->perform(this); } 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 563652225e..cf64036f08 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" @@ -1135,6 +1136,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->size()); + } else if (Selector_List* ls = dynamic_cast(v)) { + return new (ctx.mem) Number(pstate, ls->size()); + } else { + return new (ctx.mem) Number(pstate, 1); + } + } List* list = dynamic_cast(env["$list"]); return new (ctx.mem) Number(pstate, @@ -1521,10 +1531,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); } @@ -1541,15 +1549,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); } } @@ -1637,9 +1643,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)); } } diff --git a/inspect.cpp b/inspect.cpp index f27c9e74cd..0d62cc6d89 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 { @@ -123,7 +124,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"); @@ -741,17 +749,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) { @@ -816,10 +813,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) @@ -861,6 +857,7 @@ namespace Sass { { append_token(s->name(), s); if (s->expression()) { + append_string("("); s->expression()->perform(this); append_string(")"); } @@ -871,6 +868,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; @@ -882,7 +880,9 @@ namespace Sass { (*s)[i]->perform(this); } if (s->has_line_break()) { - append_optional_linefeed(); + if (output_style() != COMPACT) { + append_optional_linefeed(); + } } } @@ -891,6 +891,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; @@ -922,6 +930,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) @@ -929,13 +942,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.cpp b/lexer.cpp index fdac244489..9e6beb09c6 100644 --- a/lexer.cpp +++ b/lexer.cpp @@ -129,5 +129,14 @@ namespace Sass { return *src == 0 || *src == '\n' || *src == '\r' ? src : 0; } + // Assert end_of_file boundary (/\z/) + // This is a zero-width positive lookahead + const char* end_of_file(const char* src) + { + // end of file or unix linefeed return here + return *src == 0 ? src : 0; + } + + } } diff --git a/lexer.hpp b/lexer.hpp index f697de9f1e..c71afb6e44 100644 --- a/lexer.hpp +++ b/lexer.hpp @@ -65,7 +65,7 @@ namespace Sass { // Assert string boundaries (/\Z|\z|\A/) // There are zero-width positive lookaheads const char* end_of_line(const char* src); - // const char* end_of_string(const char* src); + const char* end_of_file(const char* src); // const char* start_of_string(const char* src); // Type definition for prelexer functions diff --git a/listize.cpp b/listize.cpp index 3457d59e63..8c0e669c64 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; @@ -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..5d97a10a0e 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; } @@ -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/output.cpp b/output.cpp index 1589d5e6e9..58b06a2a40 100644 --- a/output.cpp +++ b/output.cpp @@ -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(); diff --git a/parser.cpp b/parser.cpp index d183df4c97..d8ea61c99c 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 @@ -67,9 +68,7 @@ namespace Sass { 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) { @@ -85,113 +84,194 @@ namespace Sass { } } - bool semicolon = false; - string error_message; - lex< optional_spaces >(); - Lookahead lookahead_result; + block_stack.push_back(root); + parse_block_nodes(); + block_stack.pop_back(); + 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< 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); + parse_block_nodes(); + 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 (lex < 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 (lex < variable >()) { - (*root) << parse_assignment(); - semicolon = true; - error_message = "top-level variable binding must be terminated by ';'"; - } - /*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) { - (*root) << parse_propset(); - }*/ - else if (lex < 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 (lex < kwd_for_directive >()) { - (*root) << parse_for_directive(); - } - else if (lex < kwd_each_directive >()) { - (*root) << parse_each_directive(); - } - else if (lex < kwd_while_directive >()) { - (*root) << parse_while_directive(); - } - else if (lex < kwd_media >()) { - (*root) << parse_media_block(); - } - else if (lex < kwd_at_root >()) { - (*root) << parse_at_root_block(); - } - else if (lex < kwd_supports_directive >()) { - (*root) << parse_supports_directive(); - } - else if (lex < kwd_warn >()) { - (*root) << parse_warning(); - semicolon = true; - error_message = "top-level @warn directive must be terminated by ';'"; - } - else if (lex < kwd_err >()) { - (*root) << parse_error(); - semicolon = true; - error_message = "top-level @error directive must be terminated by ';'"; - } - else if (lex < 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 (lex< 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 ';'"; + // parse comment blocks + parse_block_comments(); + // check scope closing condition + if (lex< exactly<'}'> >()) break; + // delegate rest to the block parser + else if (!parse_block_node()) return false; + } + // return success + return true; + } + + // main parser for anything between `{` and `}` + bool Parser::parse_block_node() { + + // throw away white-space + // includes line comments + lex < css_whitespace >(); + + Lookahead lookahead_result; + + Block* block = block_stack.back(); + + if (!block->is_root() && lex < kwd_extend >(true)) { + 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); + } + else if (lex < kwd_import >(true)) { + if (stack.back() == mixin_def || stack.back() == function_def) { + 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]); } } - else if ((lookahead_result = lookahead_for_selector(position)).found) { - (*root) << parse_ruleset(lookahead_result); - } - else if (peek< exactly<';'> >()) { - lex< one_plus< exactly<';'> > >(); + } + // consume any superfluous semicolons + else if (lex < one_plus < exactly <';'> > >(true)) {} + + else if (lex < kwd_content >(true)) { + if (stack.back() == mixin_def) *block << new (ctx.mem) Content(pstate); + else error("@content may only be used within a mixin", pstate); + } + else if (!(lookahead_result = lookahead_for_selector(position)).error) { + (*block) << parse_ruleset(lookahead_result); + } + 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_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(); + } + else if (lex < kwd_supports_directive >(true)) { + (*block) << parse_supports_directive(); + } + else if (lex < variable >(true)) { + (*block) << parse_assignment(); + } + else if (lex < kwd_warn >(true)) { + (*block) << parse_warning(); + } + else if (lex < kwd_err >(true)) { + (*block) << parse_error(); + } + else if (lex < kwd_dbg >(true)) { + (*block) << parse_debug(); + } + 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< exactly< charset_kwd > >(true)) { + lex < + sequence < + quoted_string, + optional_spaces, + exactly <';'> + > + >(); + } + // generic at keyword (keep last) + else if (lex< at_keyword >(true)) { + (*block) << parse_at_rule(); + } + + else if (!block->is_root() && !peek< exactly<';'> >()) { + + Declaration* decl = parse_declaration(); + decl->tabs(indentation); + (*block) << decl; + if (peek< exactly<'{'> >()) { + // parse a propset that rides on the declaration's property + if (!decl->surfer()) indentation++; + Propset* ps = new (ctx.mem) Propset(pstate, decl->property(), parse_block()); + if (!decl->surfer()) indentation--; + (*block) << ps; } else { - lex< css_whitespace >(); - if (position >= end) break; - error("invalid top-level expression", after_token); - } - if (semicolon) { - if (!lex< one_plus< exactly<';'> > >() && peek_css< optional_css_whitespace >() != end) - { error(error_message, pstate); } - semicolon = false; + // cerr << "NO BLOCK\n"; + // finish and let the semicolon get munched } - lex< optional_spaces >(); } - block_stack.pop_back(); + else if (!block->is_root() && stack.back() == function_def) { + error("only variable declarations and control directives are allowed inside functions", pstate); + } + else if (block->is_root()) { + lex< css_whitespace >(); + if (position >= end) return true; + css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); + } + // nothing matched + else { + + // cerr << "parse decl\n"; + return false; } // something matched - return root; + return true; } + // EO parse_block_nodes void Parser::add_single_file (Import* imp, string import_path) { @@ -322,11 +402,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)); @@ -334,7 +411,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(); @@ -451,47 +527,17 @@ namespace Sass { return var; } - /* not used anymore - remove? - Propset* Parser::parse_propset() - { - String* property_segment; - if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) { - property_segment = parse_identifier_schema(); - } - else { - lex< sequence< optional< exactly<'*'> >, identifier > >(); - property_segment = new (ctx.mem) String_Quoted(pstate, lexed); - } - Propset* propset = new (ctx.mem) Propset(pstate, property_segment); - lex< exactly<':'> >(); - - if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property", pstate); - - propset->block(parse_block()); - - propset->tabs(indentation); - - return propset; - } */ - + // 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()); + // return AST Node return ruleset; } @@ -508,10 +554,6 @@ namespace Sass { // the selector schema is pretty much just a wrapper for the string schema Selector_Schema* selector_schema = new (ctx.mem) Selector_Schema(pstate, schema); - // set some options from parsing context - selector_schema->media_block(last_media_block); - selector_schema->last_block(block_stack.back()); - // process until end while (i < end_of_selector) { // try to parse mutliple interpolants @@ -556,7 +598,7 @@ namespace Sass { Mixin_Call* Parser::parse_include_directive() { // lex identifier into `lexed` var - if (!lex< identifier >()) error("invalid name in @include directive", pstate); + lex_identifier(); // may error out // normalize underscores to hyphens string name(Util::normalize_underscores(lexed)); // create the initial mixin call object @@ -564,7 +606,7 @@ namespace Sass { // parse mandatory arguments call->arguments(parse_arguments()); // parse optional block - if (peek< exactly<'{'> >()) { + if (peek < exactly <'{'> >()) { call->block(parse_block()); } // return ast node @@ -574,49 +616,42 @@ namespace Sass { // parse a list of complex selectors // this is the main entry point for most - Selector_List* Parser::parse_selector_list() + 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; + + 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_css< kwd_optional >()) { @@ -630,46 +665,64 @@ namespace Sass { // 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() + Complex_Selector* Parser::parse_complex_selector(bool in_root) { - Position sel_source_position(-1); + + lex < block_comment >(); // parse the left hand side 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()); } + // check for end of file condition + if (peek < end_of_file >()) return 0; + // parse combinator between lhs and rhs 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; - bool cpx_lf = peek_newline(); + // 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* rhs; + 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 > >()) - // no selector after the combinator - { rhs = 0; } - else { - rhs = parse_complex_selector(); - sel_source_position = before_token; + if (!peek_css< class_char < complex_selector_delims > >()) { + // parse next selector in sequence + sel->tail(parse_complex_selector()); + // ToDo: move the 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); } - if (!sel_source_position.line) sel_source_position = before_token; - Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, source, sel_source_position), combinator, lhs, rhs); - cpx->media_block(last_media_block); - cpx->last_block(block_stack.back()); - if (cpx_lf) cpx->has_line_break(cpx_lf); // complex selector - return cpx; + return sel; } // EO parse_complex_selector @@ -680,47 +733,39 @@ namespace Sass { { // init an empty compound selector wrapper Compound_Selector* seq = new (ctx.mem) Compound_Selector(pstate); - // set some options from parsing context - seq->media_block(last_media_block); - seq->last_block(block_stack.back()); - bool sawsomething = false; - if (lex_css< exactly<'&'> >()) { - // check if we have a parent selector on the root level block - if (block_stack.back() && block_stack.back()->is_root()) { - //error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate); + + // skip initial white-space + lex< css_whitespace >(); + + // parse list + while (true) + { + // remove all block comments (don't skip white-space) + lex< delimited_by< slash_star, star_slash, false > >(false); + // parse functional + if (peek < re_pseudo_selector >()) + { + (*seq) << parse_simple_selector(); } - (*seq) << new (ctx.mem) Selector_Reference(pstate); - sawsomething = true; - // if you see a space after a &, then you're done - if(peek< spaces >() || peek< alternatives < spaces, exactly<';'> > >()) { - return seq; + // parse parent selector + else if (lex< exactly<'&'> >(false)) + { + // this produces a linefeed!? + seq->has_parent_reference(true); + (*seq) << new (ctx.mem) Parent_Selector(pstate); } - } - if (sawsomething && lex_css< sequence< negate< re_pseudo_selector >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) { - // saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning - (*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 > > >()) { - // if you see a type 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(); - } - - while (!peek< spaces >(position) && - !(peek_css < alternatives < - exactly<'+'>, - exactly<'~'>, - exactly<'>'>, - exactly<','>, - exactly<')'>, - exactly<'{'>, - exactly<'}'>, - exactly<';'> - > >(position))) { - (*seq) << parse_simple_selector(); + // parse type selector + else if (lex< re_type_selector >(false)) + { + (*seq) << new (ctx.mem) Type_Selector(pstate, lexed); + } + // peek for abort conditions + else if (peek< spaces >()) break; + else if (peek< exactly <0> >()) { cerr << "EO\n"; 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 + else (*seq) << parse_simple_selector(); } // EO while true @@ -738,7 +783,7 @@ namespace Sass { 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 >()) { @@ -754,15 +799,9 @@ namespace Sass { return parse_attribute_selector(); } else if (lex< placeholder >()) { - Selector_Placeholder* sel = new (ctx.mem) Selector_Placeholder(pstate, lexed); - sel->media_block(last_media_block); - sel->last_block(block_stack.back()); - return sel; + return new (ctx.mem) Selector_Placeholder(pstate, lexed); } - else { - error("invalid selector after " + lexed.to_string(), pstate); - } - // unreachable statement + // failed return 0; } @@ -775,62 +814,64 @@ 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; } @@ -863,8 +904,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); @@ -872,182 +914,9 @@ namespace Sass { } } - // convenience function for block parsing - // will create a new block ad-hoc for you - Block* Parser::parse_block() - { - // create new block and pass it to actual parse function - return parse_block(new (ctx.mem) Block(pstate, 0)); - } - - // the main block parsing function - // parses stuff between `{` and `}` - Block* Parser::parse_block(Block* root) - { - 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 (lex < kwd_import >(position)) { - if (stack.back() == mixin_def || stack.back() == function_def) { - 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 (lex < kwd_for_directive >()) { - (*block) << parse_for_directive(); - } - else if (lex < kwd_each_directive >()) { - (*block) << parse_each_directive(); - } - else if (lex < kwd_while_directive >()) { - (*block) << parse_while_directive(); - } - else if (lex < kwd_return_directive >()) { - (*block) << parse_return_directive(); - semicolon = true; - } - else if (lex < kwd_warn >()) { - (*block) << parse_warning(); - semicolon = true; - } - else if (lex < kwd_err >()) { - (*block) << parse_error(); - semicolon = true; - } - else if (lex < 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 (lex < 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_include_directive(); - 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 (lex < kwd_media >()) { - (*block) << parse_media_block(); - } - else if (lex < kwd_supports_directive >()) { - (*block) << parse_supports_directive(); - } - else if (lex < 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 (lex< 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 passed pointer - // used for syntax sugar - 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 > >()) { @@ -1057,9 +926,11 @@ namespace Sass { else { error("invalid property name", pstate); } + bool surfer = false; 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<'{'> >()) surfer = true; // surf with the indentation if (peek_css< static_value >()) { return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex()*/); } @@ -1082,7 +953,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->surfer(surfer); + return decl; } } @@ -1364,11 +1237,6 @@ namespace Sass { } // if it's a singleton, return it (don't wrap it) if (!peek_css< class_char< static_ops > >()) return factor; - return parse_operators(factor); - } - - Expression* Parser::parse_operators(Expression* factor) - { // parse more factors and operators vector operands; // factors vector operators; // ops @@ -1422,22 +1290,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 > >()) { @@ -1455,7 +1323,7 @@ namespace Sass { 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"); } @@ -1670,15 +1538,15 @@ namespace Sass { (*schema) << new (ctx.mem) String_Constant(pstate, " "); } // lex an interpolant /#{...}/ - 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); + else if (lex< exactly < hash_lbrace > >()) { + // Try to lex static expression first + if (lex< re_static_expression >()) { + (*schema) << new (ctx.mem) String_Constant(pstate, lexed); + } else { + (*schema) << parse_list(); } - (*schema) << interp_node; + // ToDo: no error check here? + lex < exactly < rbrace > >(); } // lex some string constants else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) { @@ -1714,49 +1582,17 @@ namespace Sass { (*schema) << parse_factor(); } else { - error("error parsing interpolated value", pstate); + return schema; } ++num_items; } return schema; } - /* not used anymore - remove? - String_Schema* Parser::parse_url_schema() - { - String_Schema* schema = new (ctx.mem) String_Schema(pstate); - - while (position < end) { - if (position[0] == '/') { - lexed = Token(position, position+1, before_token); - (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); - ++position; - } - else if (lex< interpolant >()) { - Token insides(Token(lexed.begin + 2, lexed.end - 1, before_token)); - Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list(); - interp_node->is_interpolant(true); - (*schema) << interp_node; - } - else if (lex< sequence< identifier, exactly<':'> > >()) { - (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); - } - else if (lex< filename >()) { - (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); - } - else { - error("error parsing interpolated url", pstate); - } - } - 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 @@ -1811,7 +1647,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); @@ -1840,11 +1679,9 @@ namespace Sass { 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; @@ -1853,12 +1690,7 @@ 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); } @@ -1866,7 +1698,7 @@ namespace Sass { For* Parser::parse_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(); @@ -1877,16 +1709,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() { 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); @@ -1901,7 +1758,6 @@ 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); } @@ -1915,7 +1771,7 @@ namespace Sass { Expression* predicate = parse_list(); predicate->is_delayed(false); call->predicate(predicate); - // parse optional block + // parse mandatory block call->block(parse_block()); // return ast node return call; @@ -1924,18 +1780,10 @@ namespace Sass { // EO parse_while_directive Media_Block* Parser::parse_media_block() { - ParserState media_source_position = pstate; - - List* media_queries = parse_media_queries(); + Media_Block* media_block = new (ctx.mem) Media_Block(pstate, 0, 0); + media_block->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; } @@ -1956,12 +1804,12 @@ 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, " "); @@ -1974,7 +1822,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); } @@ -2005,9 +1853,6 @@ namespace Sass { // now parse the support queries query->queries(parse_supports_queries()); // additional block is mandatory - if (!peek< exactly<'{'> >()) { - error("expected '{' in feature query", pstate); - } // parse inner block query->block(parse_block()); // return ast node @@ -2026,7 +1871,7 @@ namespace Sass { // first condition is the root cond->is_root(true); // loop until the abort condition - while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position)) + while (!peek < exactly <'{'> >()) (*cond) << parse_supports_query(); // add condition (*sq) << cond; @@ -2093,19 +1938,20 @@ namespace Sass { Block* body = 0; At_Root_Expression* expr = 0; Lookahead lookahead_result; - in_at_root = true; + LOCAL_FLAG(in_at_root, true); if (lex< exactly<'('> >()) { expr = parse_at_root_expression(); } - 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; + } else { + cerr << "NO BLOCK\n"; } - 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; @@ -2149,11 +1995,16 @@ namespace Sass { sel = parse_selector_list(); } } - else if (!(peek >() || peek >() || peek >())) { + else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) { val = parse_list(); } Block* body = 0; - if (peek< exactly<'{'> >()) body = parse_block(); + if (peek< exactly<'{'> >()) { + body = parse_block(); + } + // ToDo: is the block really optional + // else cerr << "NO AT RULE BLOCK\n"; + At_Rule* rule = new (ctx.mem) At_Rule(at_source_position, kwd, sel, body); if (!sel) rule->value(val); return rule; @@ -2181,66 +2032,96 @@ namespace Sass { 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 < + // modifiers prefixes + alternatives < + sequence < + exactly <'#'>, + // not for interpolation + negate < exactly <'{'> > + >, + // class match + exactly <'.'>, + // single or double colon + optional < pseudo_prefix > + >, + // can be namespaced + 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 @@ -2248,64 +2129,16 @@ namespace Sass { // 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; - + // 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 result; + return rv; } // EO lookahead_for_include @@ -2315,52 +2148,50 @@ namespace Sass { // 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; + // 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 result; + return rv; } // EO lookahead_for_value diff --git a/parser.hpp b/parser.hpp index df169f1221..936e7a5035 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,15 +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(Block* root); + 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(); @@ -236,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(); @@ -247,7 +252,6 @@ namespace Sass { 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); @@ -269,7 +273,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 9b903df386..eb1b3e85bb 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -339,10 +339,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 +386,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); @@ -503,8 +502,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); } @@ -778,5 +782,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..9b111a3a3b 100644 --- a/prelexer.hpp +++ b/prelexer.hpp @@ -239,12 +239,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); 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..9352e61519 100644 --- a/sass.h +++ b/sass.h @@ -1,6 +1,8 @@ #ifndef SASS_H #define SASS_H +// #define DEBUG 1 + #ifdef _MSC_VER #define _SCL_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS @@ -24,10 +26,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..6c70b8643f 100644 --- a/sass_util.cpp +++ b/sass_util.cpp @@ -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); @@ -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..33599e949e 100644 --- a/test/test_node.cpp +++ b/test/test_node.cpp @@ -10,12 +10,12 @@ namespace Sass { - + Context ctx = Context::Data(); - + To_String to_string; - - + + const char* const ROUNDTRIP_TESTS[] = { NULL, "~", @@ -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,41 +46,41 @@ 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 ); } - + // Test that they are equal by comparing the string versions of the selectors assert(expected == result); - + } @@ -90,7 +90,7 @@ namespace Sass { 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/util.cpp b/util.cpp index a3e93a7b84..6702ab8985 100644 --- a/util.cpp +++ b/util.cpp @@ -467,6 +467,7 @@ namespace Sass { exactly <' '>, exactly <'\t'>, line_comment, + block_comment, delimited_by < slash_star, star_slash, @@ -585,13 +586,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 @@ -615,40 +616,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 2f42c8658890d7ec86905fabec808b1c1c4cd7c6 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 12 Jun 2015 13:30:50 +0200 Subject: [PATCH 11/31] Fix custom at rules value handling --- ast.hpp | 5 +++-- bind.cpp | 7 +++++++ debugger.hpp | 2 +- expand.cpp | 8 ++++---- inspect.cpp | 4 ++++ output.cpp | 2 +- parser.cpp | 54 +++++++++++++++++++++++++++------------------------- parser.hpp | 1 + prelexer.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++ prelexer.hpp | 1 + 10 files changed, 98 insertions(+), 34 deletions(-) diff --git a/ast.hpp b/ast.hpp index b037e99e27..5ceea76695 100644 --- a/ast.hpp +++ b/ast.hpp @@ -169,6 +169,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); @@ -415,8 +416,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() { diff --git a/bind.cpp b/bind.cpp index 5e3a0609f2..ca8e00dd8a 100644 --- a/bind.cpp +++ b/bind.cpp @@ -15,6 +15,13 @@ namespace Sass { 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('*'); + } + } + // 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) { diff --git a/debugger.hpp b/debugger.hpp index 64ed315ac0..29ee8b5bc5 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -315,8 +315,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); diff --git a/expand.cpp b/expand.cpp index c13f50a2cc..11577a49a4 100644 --- a/expand.cpp +++ b/expand.cpp @@ -190,15 +190,15 @@ namespace Sass { Selector* as = a->selector(); Expression* av = a->value(); selector_stack.push_back(0); - if (as) as = static_cast(as->perform(&eval)); - else if (av) av = av->perform(&eval); + 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); + bb, + av); return aa; } diff --git a/inspect.cpp b/inspect.cpp index 0d62cc6d89..f7af321bcd 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -106,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); } diff --git a/output.cpp b/output.cpp index 58b06a2a40..89312c0246 100644 --- a/output.cpp +++ b/output.cpp @@ -306,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 d8ea61c99c..18def7dd1d 100644 --- a/parser.cpp +++ b/parser.cpp @@ -251,7 +251,6 @@ namespace Sass { (*block) << ps; } else { - // cerr << "NO BLOCK\n"; // finish and let the semicolon get munched } } @@ -264,10 +263,7 @@ namespace Sass { css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); } // nothing matched - else { - - // cerr << "parse decl\n"; - return false; } + else { return false; } // something matched return true; } @@ -675,7 +671,6 @@ namespace Sass { if (!peek_css< class_char < selector_combinator_ops > >()) { // parse the left hand side lhs = parse_compound_selector(); - lhs->has_line_break(peek_newline()); } // check for end of file condition @@ -688,6 +683,8 @@ namespace Sass { 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 >()); @@ -761,13 +758,19 @@ namespace Sass { } // peek for abort conditions else if (peek< spaces >()) break; - else if (peek< exactly <0> >()) { cerr << "EO\n"; 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 - else (*seq) << parse_simple_selector(); + else { + Simple_Selector* sel = parse_simple_selector(); + if (!sel) return 0; + (*seq) << sel; + } } + if (seq) seq->has_line_break(peek_newline()); + // EO while true return seq; @@ -1423,6 +1426,7 @@ namespace Sass { } ++ i; } + return schema; } @@ -1448,8 +1452,7 @@ namespace Sass { String* Parser::parse_string() { - Token token(lexed); - return parse_interpolated_chunk(token); + return parse_interpolated_chunk(Token(lexed)); } String* Parser::parse_ie_property() @@ -1949,8 +1952,6 @@ namespace Sass { Ruleset* r = parse_ruleset(lookahead_result); body = new (ctx.mem) Block(r->pstate(), 1, true); *body << r; - } else { - cerr << "NO BLOCK\n"; } At_Root_Block* at_root = new (ctx.mem) At_Root_Block(at_source_position, body); if (expr) at_root->expression(expr); @@ -1983,31 +1984,32 @@ namespace Sass { At_Rule* Parser::parse_at_rule() { 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 < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) { - 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; + + lex < css_comments >(); + if (peek< exactly<'{'> >()) { - body = parse_block(); + at_rule->block(parse_block()); } - // ToDo: is the block really optional - // else cerr << "NO AT RULE BLOCK\n"; - At_Rule* rule = new (ctx.mem) At_Rule(at_source_position, kwd, sel, body); - if (!sel) rule->value(val); - return rule; + return at_rule; } Warning* Parser::parse_warning() diff --git a/parser.hpp b/parser.hpp index 936e7a5035..139dfa810c 100644 --- a/parser.hpp +++ b/parser.hpp @@ -248,6 +248,7 @@ 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(); diff --git a/prelexer.cpp b/prelexer.cpp index eb1b3e85bb..2d3e89550f 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -751,6 +751,54 @@ namespace Sass { >(src); } + const char* static_property(const char* src) { + return + sequence < + zero_plus< + sequence < + zero_plus < + alternatives < + spaces, + line_comment, + block_comment + > + >, + alternatives < + exactly<','>, + exactly<'('>, + exactly<')'>, + kwd_optional, + quoted_string, + interpolant, + identifier, + percentage, + dimension, + variable, + alnum, + sequence < + exactly <'\\'>, + any_char + > + > + > + >, + lookahead < + sequence < + zero_plus < + alternatives < + spaces + > + >, + alternatives < + exactly <';'>, + exactly <'}'>, + exactly <'\0'> + > + > + > + >(src); + } + const char* static_value(const char* src) { return sequence< sequence< static_component, diff --git a/prelexer.hpp b/prelexer.hpp index 9b111a3a3b..ef1984c918 100644 --- a/prelexer.hpp +++ b/prelexer.hpp @@ -332,6 +332,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. From e1b7713b0ced75461a30fbc4ae0e7afa79af564d Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 12 Jun 2015 17:47:07 +0200 Subject: [PATCH 12/31] Improve function call return value handling Fixes https://github.com/sass/libsass/issues/1269 --- eval.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/eval.cpp b/eval.cpp index 3833dcc3fa..5285f75528 100644 --- a/eval.cpp +++ b/eval.cpp @@ -685,10 +685,8 @@ namespace Sass { if (result->pstate().file == string::npos) result->pstate(c->pstate()); - do { - result->is_delayed(result->concrete_type() == Expression::STRING); - result = result->perform(this); - } while (result->concrete_type() == Expression::NONE); + result->is_delayed(result->concrete_type() == Expression::STRING); + if (!result->is_delayed()) result = result->perform(this); exp.env_stack.pop_back(); return result; } From 2189e4ad1ad0512e2a74cafa4ebeb651f5f27f81 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 12 Jun 2015 18:23:55 +0200 Subject: [PATCH 13/31] Fix segfault caused by static cast and wrong type --- bind.cpp | 4 +--- parser.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bind.cpp b/bind.cpp index ca8e00dd8a..339b935937 100644 --- a/bind.cpp +++ b/bind.cpp @@ -152,9 +152,7 @@ namespace Sass { break; } // otherwise move one of the rest args into the param, converting to argument if necessary - if (arglist->is_arglist()) { - a = static_cast((*arglist)[0]); - } else { + if (!(a = dynamic_cast((*arglist)[0]))) { Expression* a_to_convert = (*arglist)[0]; a = new (ctx.mem) Argument(a_to_convert->pstate(), a_to_convert, diff --git a/parser.cpp b/parser.cpp index 18def7dd1d..969743f658 100644 --- a/parser.cpp +++ b/parser.cpp @@ -697,9 +697,11 @@ namespace Sass { if (!peek_css< class_char < complex_selector_delims > >()) { // parse next selector in sequence sel->tail(parse_complex_selector()); - // ToDo: move the logic below into tail setter - if (sel->tail()->has_reference()) sel->has_reference(true); - if (sel->tail()->has_placeholder()) sel->has_placeholder(true); + 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 From ed5411fa91e4340633c1a4ef698b2e17d63e0842 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 12 Jun 2015 19:52:51 +0200 Subject: [PATCH 14/31] Fix MSVC compilation and some remaining warnings --- lexer.hpp | 42 ++++-------------------------------------- parser.cpp | 6 +++--- prelexer.cpp | 16 +++------------- sass.h | 7 ++++--- util.cpp | 4 +++- 5 files changed, 17 insertions(+), 58 deletions(-) diff --git a/lexer.hpp b/lexer.hpp index c71afb6e44..159f6f3195 100644 --- a/lexer.hpp +++ b/lexer.hpp @@ -148,28 +148,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. @@ -181,28 +164,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/parser.cpp b/parser.cpp index 969743f658..2d732cc244 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1896,17 +1896,17 @@ namespace Sass { // lex optional comments lex < css_whitespace >(); // parse `not` query operator - if (lex < kwd_not >(position)) { + if (lex < kwd_not >()) { cond = parse_supports_query(); cond->operand(Supports_Condition::NOT); } // parse `and` query operator - else if (lex < kwd_and >(position)) { + else if (lex < kwd_and >()) { cond = parse_supports_query(); cond->operand(Supports_Condition::AND); } // parse `or` query operator - else if (lex < kwd_or >(position)) { + else if (lex < kwd_or >()) { cond = parse_supports_query(); cond->operand(Supports_Condition::OR); } diff --git a/prelexer.cpp b/prelexer.cpp index 2d3e89550f..88aba15a90 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -756,13 +756,7 @@ namespace Sass { sequence < zero_plus< sequence < - zero_plus < - alternatives < - spaces, - line_comment, - block_comment - > - >, + optional_css_comments, alternatives < exactly<','>, exactly<'('>, @@ -784,15 +778,11 @@ namespace Sass { >, lookahead < sequence < - zero_plus < - alternatives < - spaces - > - >, + optional_css_comments, alternatives < exactly <';'>, exactly <'}'>, - exactly <'\0'> + end_of_file > > > diff --git a/sass.h b/sass.h index 9352e61519..1f5e3e6752 100644 --- a/sass.h +++ b/sass.h @@ -4,9 +4,10 @@ // #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 diff --git a/util.cpp b/util.cpp index 6702ab8985..7ef26044b1 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 { \ From 7a9f9ed39a7f2b9ed6649ab25240f48fbba317a1 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 12 Jun 2015 21:01:54 +0200 Subject: [PATCH 15/31] Clean up some code left overs --- ast.cpp | 4 +- b64/cencode.h | 2 +- context.cpp | 6 --- contextualize.cpp | 86 ------------------------------------------ contextualize.hpp | 41 -------------------- contextualize_eval.cpp | 5 --- contextualize_eval.hpp | 7 ---- debug.hpp | 18 ++++----- debugger.hpp | 22 +++++++++++ emitter.hpp | 1 - error_handling.cpp | 3 -- error_handling.hpp | 5 --- eval.cpp | 25 +----------- eval.hpp | 2 +- expand.cpp | 29 ++------------ expand.hpp | 2 - extend.cpp | 62 +++--------------------------- node.cpp | 4 +- node.hpp | 2 +- parser.cpp | 12 +++--- prelexer.cpp | 2 +- sass_util.cpp | 10 ++--- test/test_node.cpp | 8 ++-- utf8/core.h | 24 ++++++------ 24 files changed, 75 insertions(+), 307 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/ast.cpp b/ast.cpp index 6b86231d1c..80dc49cb2e 100644 --- a/ast.cpp +++ b/ast.cpp @@ -2,7 +2,6 @@ #include "context.hpp" #include "node.hpp" #include "extend.hpp" -#include "debugger.hpp" #include "to_string.hpp" #include #include @@ -258,7 +257,6 @@ namespace Sass { bool Compound_Selector::is_superselector_of(Selector_List* rhs, string wrapped) { -//cerr << "DAHJKA\n"; for (Complex_Selector* item : rhs->elements()) { if (is_superselector_of(item, wrapped)) return true; } @@ -325,7 +323,7 @@ namespace Sass { if (Selector_List* not_list = dynamic_cast(wrapped->selector())) { if (not_list->is_superselector_of(rhs, wrapped->name())) return false; } else { - cerr << "do not\n"; + throw runtime_error("wrapped not selector is not a list"); } } if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { 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/context.cpp b/context.cpp index eb6f0dfd96..6348d778cd 100644 --- a/context.cpp +++ b/context.cpp @@ -33,7 +33,6 @@ #include "sass2scss.h" #include "prelexer.hpp" #include "emitter.hpp" -#include "debugger.hpp" namespace Sass { using namespace Constants; @@ -343,26 +342,21 @@ namespace Sass { Expand expand(*this, &global, &backtrace); Cssize cssize(*this, &backtrace); // expand and eval the tree -//debug_ast(root, "parsed: "); root = root->perform(&expand)->block(); -//debug_ast(root, "expand: "); // merge and bubble certain rules root = root->perform(&cssize)->block(); -// debug_ast(root, "cssize: "); // should we extend something? if (!subset_map.empty()) { // create crtp visitor object Extend extend(*this, subset_map); // extend tree nodes root->perform(&extend); -//debug_ast(root, "extend: "); } // clean up by removing empty placeholders // ToDo: maybe we can do this somewhere else? Remove_Placeholders remove_placeholders(*this); root->perform(&remove_placeholders); -//debug_ast(root, "cleaned: "); // return processed tree return root; } diff --git a/contextualize.cpp b/contextualize.cpp deleted file mode 100644 index 24fa462fa1..0000000000 --- a/contextualize.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "contextualize.hpp" - -#include -#include - -#include "listize.hpp" -#include "to_string.hpp" -#include "context.hpp" -#include "backtrace.hpp" -#include "error_handling.hpp" - -namespace Sass { - - Listize2::Listize2(Context& ctx) - : ctx(ctx) - { } - - Expression* Listize2::operator()(Selector_List* sel) - { - 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; - } - - Expression* Listize2::operator()(Compound_Selector* sel) - { - To_String to_string; - string str; - for (size_t i = 0, L = sel->length(); i < L; ++i) { - Expression* e = (*sel)[i]->perform(this); - if (e) str += e->perform(&to_string); - } - return new (ctx.mem) String_Constant(sel->pstate(), str); - } - - Expression* Listize2::operator()(Complex_Selector* sel) - { - List* l = new (ctx.mem) List(sel->pstate(), 2); - - Compound_Selector* head = sel->head(); - if (head && !head->is_empty_reference()) - { - Expression* hh = head->perform(this); - if (hh) *l << hh; - } - - switch(sel->combinator()) - { - case Complex_Selector::PARENT_OF: - *l << new (ctx.mem) String_Constant(sel->pstate(), ">"); - break; - case Complex_Selector::ADJACENT_TO: - *l << new (ctx.mem) String_Constant(sel->pstate(), "+"); - break; - case Complex_Selector::PRECEDES: - *l << new (ctx.mem) String_Constant(sel->pstate(), "~"); - break; - case Complex_Selector::ANCESTOR_OF: - break; - } - - Complex_Selector* tail = sel->tail(); - if (tail) - { - Expression* tt = tail->perform(this); - if (tt && tt->concrete_type() == Expression::LIST) - { *l += static_cast(tt); } - else if (tt) *l << static_cast(tt); - } - if (l->length() == 0) return 0; - return l; - } - - Expression* Listize2::operator()(Parent_Selector* sel) - { - return 0; - } - - Expression* Listize2::fallback_impl(AST_Node* n) - { - return static_cast(n); - } -} diff --git a/contextualize.hpp b/contextualize.hpp deleted file mode 100644 index ca7925dd09..0000000000 --- a/contextualize.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef SASS_CONTEXTUALIZE_H -#define SASS_CONTEXTUALIZE_H - -#include -#include - -#include "ast.hpp" -#include "context.hpp" -#include "operation.hpp" -#include "environment.hpp" - -namespace Sass { - using namespace std; - - typedef Environment Env; - struct Backtrace; - - class Listize2 : public Operation_CRTP { - - Context& ctx; - - Expression* fallback_impl(AST_Node* n); - - public: - Listize2(Context&); - virtual ~Listize2() { } - - using Operation::operator(); - - Expression* operator()(Selector_List*); - Expression* operator()(Complex_Selector*); - Expression* operator()(Compound_Selector*); - Expression* operator()(Parent_Selector*); - - template - Expression* fallback(U x) { return fallback_impl(x); } - }; - -} - -#endif diff --git a/contextualize_eval.cpp b/contextualize_eval.cpp deleted file mode 100644 index 6a4e1bb0f5..0000000000 --- a/contextualize_eval.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "contextualize_eval.hpp" - -namespace Sass { - -} diff --git a/contextualize_eval.hpp b/contextualize_eval.hpp deleted file mode 100644 index 00a75def50..0000000000 --- a/contextualize_eval.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef SASS_CONTEXTUALIZE_EVAL_H -#define SASS_CONTEXTUALIZE_EVAL_H - -namespace Sass { -} - -#endif 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 29ee8b5bc5..0fc344347a 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -9,6 +9,28 @@ 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; diff --git a/emitter.hpp b/emitter.hpp index 3cbb36eb4d..2ee538a24e 100644 --- a/emitter.hpp +++ b/emitter.hpp @@ -73,7 +73,6 @@ namespace Sass { void append_indentation(); void append_optional_space(void); void append_mandatory_space(void); - void append_forced_space(void); void append_special_linefeed(void); void append_optional_linefeed(void); void append_mandatory_linefeed(void); diff --git a/error_handling.cpp b/error_handling.cpp index af2afb3027..522994829b 100644 --- a/error_handling.cpp +++ b/error_handling.cpp @@ -8,9 +8,6 @@ namespace Sass { : type(type), pstate(pstate), message(message) { } - Css_Error::Css_Error() - { } - void warn(string msg, ParserState pstate) { cerr << "Warning: " << msg<< endl; diff --git a/error_handling.hpp b/error_handling.hpp index dd91fc7600..6580b79067 100644 --- a/error_handling.hpp +++ b/error_handling.hpp @@ -10,11 +10,6 @@ namespace Sass { struct Backtrace; - struct Css_Error { - Css_Error(); - - }; - struct Sass_Error { enum Type { read, write, syntax, evaluation }; diff --git a/eval.cpp b/eval.cpp index 5285f75528..60d7f978fe 100644 --- a/eval.cpp +++ b/eval.cpp @@ -22,8 +22,6 @@ #include "prelexer.hpp" #include "parser.hpp" #include "expand.hpp" -#include "contextualize.hpp" -#include "debugger.hpp" namespace Sass { using namespace std; @@ -1478,11 +1476,6 @@ namespace Sass { } -// (*sl)[i] = (*sl)[i]->first(); - - // sl->remove_parent_selectors(); -To_String to_string; -// cerr << "returned [" << sl->perform(&to_string) << "]" << endl; return sl; } @@ -1504,27 +1497,13 @@ To_String to_string; return operator()(p.parse_selector_list(exp.block_stack.back()->is_root())); } - Expression* Eval::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; -// } - } - Expression* Eval::operator()(Parent_Selector* p) { Selector_List* pr = selector(); exp.selector_stack.pop_back(); - auto ss = pr ? pr->perform(this) : 0; + if (pr) pr = operator()(pr); exp.selector_stack.push_back(pr); - // all selectors must be listized -// if (dynamic_cast(pr)) -// ss = ss->perform(&listize); - return ss; + return pr; } } diff --git a/eval.hpp b/eval.hpp index bcea1cd2df..d837e72e21 100644 --- a/eval.hpp +++ b/eval.hpp @@ -75,8 +75,8 @@ namespace Sass { 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 - Expression* operator()(Selector_Placeholder* s); Selector_List* operator()(Selector_Schema*); Expression* operator()(Parent_Selector*); diff --git a/expand.cpp b/expand.cpp index 11577a49a4..8e7521c311 100644 --- a/expand.cpp +++ b/expand.cpp @@ -11,7 +11,6 @@ #include "to_string.hpp" #include "backtrace.hpp" #include "context.hpp" -#include "debugger.hpp" #include "parser.hpp" namespace Sass { @@ -475,17 +474,6 @@ namespace Sass { return 0; } -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"; -} - - Statement* Expand::operator()(Extension* e) { To_String to_string(&ctx); @@ -497,15 +485,11 @@ inline void debug_extenstion_map(Sass::ExtensionSubsetMap* map, string ind = "") Selector_List* selector_list = static_cast(e->selector()); Selector_List* contextualized = static_cast(selector_list->perform(&eval)); // contextualized->remove_parent_selectors(); -//cerr << "extend\n"; -//debug_ast(extender, "extender: "); -//debug_ast(selector_list, "selector_list: "); -//debug_ast(contextualized, "contextualized: "); 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()); -// } + if (!c->head() || c->tail()) { + error("nested selectors may not be extended", c->pstate(), backtrace()); + } Compound_Selector* placeholder = c->head(); placeholder->is_optional(selector_list->is_optional()); for (size_t i = 0, L = extender->length(); i < L; ++i) { @@ -519,19 +503,12 @@ inline void debug_extenstion_map(Sass::ExtensionSubsetMap* map, string ind = "") ssel->head(hh); sel = ssel; } -//debug_ast(sel, "sel: "); if (c->has_line_feed()) sel->has_line_feed(true); - // sel = dynamic_cast(sel->perform(&eval)); -// cerr << "REGISTERING EXTENSION REQUEST: " << endl; -// debug_ast(sel); -// debug_ast(placeholder); ctx.subset_map.put(placeholder->to_str_vec(), make_pair(sel, placeholder)); } } selector_stack.pop_back(); -//debug_extenstion_map(&ctx.subset_map, "ctx.subset_map: "); - return 0; } diff --git a/expand.hpp b/expand.hpp index 8af5a2ba50..5d5c5eded7 100644 --- a/expand.hpp +++ b/expand.hpp @@ -36,8 +36,6 @@ namespace Sass { vector property_stack; vector selector_stack; vectorbacktrace_stack; - - // bool in_at_root; bool in_keyframes; Statement* fallback_impl(AST_Node* n); diff --git a/extend.cpp b/extend.cpp index 30c1633193..5b5778da91 100644 --- a/extend.cpp +++ b/extend.cpp @@ -10,7 +10,6 @@ #include "parser.hpp" #include "node.hpp" #include "sass_util.hpp" -#include "debugger.hpp" #include "debug.hpp" #include #include @@ -68,27 +67,6 @@ namespace Sass { typedef pair ExtensionPair; typedef vector SubsetMapEntries; -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"; -} - - #ifdef DEBUG // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp @@ -1514,7 +1492,7 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") set seen) { DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: ")) -// debug_ast(pSelector, "extendCompoundSelector:"); + Node extendedSelectors = Node::createCollection(); // extendedSelectors.got_line_feed = true; @@ -1528,8 +1506,6 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") GroupedByToAResult arr; group_by_to_a(entries, extPairKeyFunctor, arr); -// debug_subset_entries(&entries); - typedef pair SelsNewSeqPair; typedef vector SelsNewSeqPairCollection; @@ -1543,7 +1519,6 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") Complex_Selector& seq = groupedPair.first; vector& group = groupedPair.second; -// debug_ast(&seq); DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) @@ -1595,16 +1570,10 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") // out and aren't operated on. Complex_Selector* pNewSelector = pExtComplexSelector->cloneFully(ctx); // ->first(); -// debug_ast(pNewSelector); -// debug_ast(pNewSelector); Complex_Selector* pNewInnerMost = new (ctx.mem) Complex_Selector(pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL); -// debug_ast(pNewInnerMost, "= "); + Complex_Selector::Combinator combinator = pNewSelector->clear_innermost(); pNewSelector->set_innermost(pNewInnerMost, combinator); -// pNewSelector->head(0); - -// pNewSelector = pNewSelector; - //debug_ast(pNewSelector); #ifdef DEBUG SourcesSet debugSet; @@ -1639,7 +1608,6 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);; -//debug_ast(pNewSelector, " haqweqweqwe: "); holder.push_back(make_pair(pSels, pNewSelector)); } @@ -1661,9 +1629,7 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx)) -//debug_ast(pNewSelector, " rec: "); Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen); -//debug_node(&recurseExtendedSelectors, " reced: "); DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors) @@ -1776,9 +1742,7 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") ExtensionSubsetMap& subset_map, set seen) { - //debug_ast(pComplexSelector, "hwa: "); Node complexSelector = complexSelectorToNode(pComplexSelector, ctx); - //debug_node(&complexSelector, "now: "); DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) Node extendedNotExpanded = Node::createCollection(); @@ -1899,7 +1863,6 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") // 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, subset_map)) { -//debug_ast(pSelector, "test1: "); *pNewSelectors << pSelector; continue; } @@ -1909,10 +1872,8 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") set seen; Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen); -//debug_node(&extendedSelectors, "doubled?: "); if (!pSelector->has_placeholder()) { if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) { -//debug_ast(pSelector, "test2: "); *pNewSelectors << pSelector; } } @@ -1922,7 +1883,6 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue; Node& childNode = *iterator; -//debug_node(&childNode, "test2: "); *pNewSelectors << nodeToComplexSelector(childNode, ctx); } } @@ -1978,22 +1938,12 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") } bool extendedSomething = false; -//debug_extenstion_map(&subset_map, "++"); Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subset_map, false, extendedSomething); -//debug_ast(pNewSelectorList, "=="); 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(pNewSelectorList); - pObject->selector(pNewSelectorList); - } else { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") } @@ -2025,7 +1975,6 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") } pRuleset->selector(ssl); } -// debug_ast(pRuleset); extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map); pRuleset->block()->perform(this); @@ -2038,15 +1987,14 @@ inline void debug_subset_entries(SubsetMapEntries* entries, string ind = "") 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/node.cpp b/node.cpp index 5d97a10a0e..d16d02abae 100644 --- a/node.cpp +++ b/node.cpp @@ -155,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 << "]"; 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/parser.cpp b/parser.cpp index 2d732cc244..db38c2a1ed 100644 --- a/parser.cpp +++ b/parser.cpp @@ -515,9 +515,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; @@ -1896,17 +1896,17 @@ namespace Sass { // lex optional comments lex < css_whitespace >(); // parse `not` query operator - if (lex < kwd_not >()) { + if (lex < kwd_not >()) { cond = parse_supports_query(); cond->operand(Supports_Condition::NOT); } // parse `and` query operator - else if (lex < kwd_and >()) { + else if (lex < kwd_and >()) { cond = parse_supports_query(); cond->operand(Supports_Condition::AND); } // parse `or` query operator - else if (lex < kwd_or >()) { + else if (lex < kwd_or >()) { cond = parse_supports_query(); cond->operand(Supports_Condition::OR); } diff --git a/prelexer.cpp b/prelexer.cpp index 88aba15a90..fb2465f735 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -756,7 +756,7 @@ namespace Sass { sequence < zero_plus< sequence < - optional_css_comments, + optional_css_comments, alternatives < exactly<','>, exactly<'('>, diff --git a/sass_util.cpp b/sass_util.cpp index 6c70b8643f..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); @@ -76,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 diff --git a/test/test_node.cpp b/test/test_node.cpp index 33599e949e..e265d8b57a 100644 --- a/test/test_node.cpp +++ b/test/test_node.cpp @@ -18,8 +18,8 @@ namespace Sass { const char* const ROUNDTRIP_TESTS[] = { NULL, - "~", - "CMPD", + "~", + "CMPD", "~ CMPD", "CMPD >", "> > CMPD", @@ -73,7 +73,7 @@ namespace Sass { assert( (!pOrigSelector && !pNewSelector ) || (pOrigSelector && pNewSelector) ); if (pOrigSelector) { - assert( *pOrigSelector == *pNewSelector ); + assert( *pOrigSelector == *pNewSelector ); } @@ -84,7 +84,7 @@ namespace Sass { } - 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; 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) { From c4439545e271beff342d2b55c34e33c4e4dfac0b Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sat, 13 Jun 2015 17:54:45 +0200 Subject: [PATCH 16/31] Fix parsing edge case with numbers and trailing escapes Fixes https://github.com/sass/libsass/issues/1219 --- parser.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parser.cpp b/parser.cpp index db38c2a1ed..98cdbf8b64 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1333,8 +1333,7 @@ namespace Sass { 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); } // string may be interpolated @@ -1365,6 +1364,9 @@ 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_Constant(pstate, lexed); } + if (lex< number >()) { return new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); } From 226144f62cef6bf5752c10b92a6d60e087bbb074 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 15 Jun 2015 00:23:19 +0200 Subject: [PATCH 17/31] Improve url parsing to allow interpolants Fixes https://github.com/sass/libsass/issues/1273 --- prelexer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/prelexer.cpp b/prelexer.cpp index fb2465f735..5735b82ec7 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -445,6 +445,7 @@ namespace Sass { zero_plus < alternatives < alnum, + interpolant, exactly <'/'>, class_char < uri_chars > > From 28855bd22471b6589e1c74262191ad537a3904b9 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 15 Jun 2015 01:57:40 +0200 Subject: [PATCH 18/31] Improve At_Rule parsing 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 98cdbf8b64..3eb5d69c47 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1990,13 +1990,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 7ef26044b1..eea910e273 100644 --- a/util.cpp +++ b/util.cpp @@ -534,7 +534,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 8bed09fefd5f3f546f61469f86513e0ab173cdad Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 15 Jun 2015 02:23:41 +0200 Subject: [PATCH 19/31] Fix unquote handling for non string types Feature is deprecated in ruby sass. We may should print a warning to inform the user. Fixes https://github.com/sass/libsass/issues/1258 --- functions.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/functions.cpp b/functions.cpp index cf64036f08..b5dd5ceb99 100644 --- a/functions.cpp +++ b/functions.cpp @@ -803,6 +803,9 @@ namespace Sass { if (dynamic_cast(arg)) { return new (ctx.mem) Null(pstate); } + else if (List* list = dynamic_cast(arg)) { + return list; + } else if (String_Quoted* string_quoted = dynamic_cast(arg)) { String_Constant* result = new (ctx.mem) String_Constant(pstate, string_quoted->value()); // remember if the string was quoted (color tokens) @@ -810,7 +813,7 @@ namespace Sass { return result; } To_String to_string(&ctx); - return new (ctx.mem) String_Constant(pstate, unquote(string(arg->perform(&to_string)))); + return new (ctx.mem) String_Constant(pstate, string(arg->perform(&to_string))); } Signature quote_sig = "quote($string)"; From 683dbd2be4a72a0251958cdbf9a548621aa3fffa Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 15 Jun 2015 02:44:50 +0200 Subject: [PATCH 20/31] Fix string output normalization for linefeeds Fixes https://github.com/sass/libsass/issues/1230 --- util.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util.cpp b/util.cpp index eea910e273..f605f81d32 100644 --- a/util.cpp +++ b/util.cpp @@ -220,11 +220,14 @@ namespace Sass { string string_to_output(const string& str) { string out(""); + bool lf = false; for (auto i : str) { if (i == 10) { out += ' '; - } else { + lf = true; + } else if (!(lf && isspace(i))) { out += i; + lf = false; } } return out; From 439b6f609315edd5b5da339f0a630e61e2985bd8 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 15 Jun 2015 20:04:12 +0200 Subject: [PATCH 21/31] Cleaning up some minor code parts --- ast.hpp | 6 +++- extend.cpp | 12 -------- functions.cpp | 84 ++++++++++++++++++++------------------------------- 3 files changed, 37 insertions(+), 65 deletions(-) diff --git a/ast.hpp b/ast.hpp index 5ceea76695..0f4f9fa887 100644 --- a/ast.hpp +++ b/ast.hpp @@ -2128,8 +2128,12 @@ namespace Sass { 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/extend.cpp b/extend.cpp index 5b5778da91..e0213ea2fc 100644 --- a/extend.cpp +++ b/extend.cpp @@ -1964,19 +1964,7 @@ namespace Sass { void Extend::operator()(Ruleset* pRuleset) { - if (Selector_List* sl = dynamic_cast(pRuleset->selector())) { - Selector_List* ssl = new (ctx.mem) Selector_List(sl->pstate()); - for (size_t i = 0, iL = sl->size(); i < iL; ++i) { - Complex_Selector* item = (*sl)[i]; - Compound_Selector* hh = new (ctx.mem) Compound_Selector(sl->pstate()); - Complex_Selector* ss = new (ctx.mem) Complex_Selector(sl->pstate()); - *hh << new (ctx.mem) Parent_Selector(sl->pstate()); - ss->head(hh); ss->tail(item); *ssl << ss; - } - pRuleset->selector(ssl); - } extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map); - pRuleset->block()->perform(this); } diff --git a/functions.cpp b/functions.cpp index b5dd5ceb99..6d6d4f28d1 100644 --- a/functions.cpp +++ b/functions.cpp @@ -73,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) { @@ -837,19 +857,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); } @@ -894,19 +904,9 @@ 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; } + // handle any invalid utf8 errors + // other errors will be re-thrown + catch (...) { handle_utf8_error(pstate, backtrace); } return new (ctx.mem) String_Constant(pstate, str); } @@ -928,19 +928,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); } @@ -975,19 +965,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); } From 907800ec47ca47f4bf4be823c3b779c3a7371f65 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Sun, 5 Apr 2015 04:30:30 +0200 Subject: [PATCH 22/31] Implement static media_queries for imports Missing implementation for interpolated media queries! --- ast.hpp | 7 +++++-- expand.cpp | 1 + inspect.cpp | 8 ++++++++ parser.cpp | 25 ++++++++++++++++++++++--- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/ast.hpp b/ast.hpp index 0f4f9fa887..1c9d4bd306 100644 --- a/ast.hpp +++ b/ast.hpp @@ -488,12 +488,15 @@ namespace Sass { class Import : public Statement { vector files_; vector urls_; + ADD_PROPERTY(List*, media_queries); public: Import(ParserState pstate) : Statement(pstate), - files_(vector()), urls_(vector()) + files_(vector()), + urls_(vector()), + media_queries_(0) { } - vector& files() { return files_; } + vector& files() { return files_; } vector& urls() { return urls_; } ATTACH_OPERATIONS() }; diff --git a/expand.cpp b/expand.cpp index 8e7521c311..b0baa56418 100644 --- a/expand.cpp +++ b/expand.cpp @@ -279,6 +279,7 @@ namespace Sass { Statement* Expand::operator()(Import* imp) { 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)); } diff --git a/inspect.cpp b/inspect.cpp index f7af321bcd..106bb7fcdd 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -169,6 +169,10 @@ namespace Sass { } import->urls().front()->perform(this); + if (import->media_queries()) { + append_mandatory_space(); + import->media_queries()->perform(this); + } append_delimiter(); for (size_t i = 1, S = import->urls().size(); i < S; ++i) { append_mandatory_linefeed(); @@ -180,6 +184,10 @@ namespace Sass { } import->urls()[i]->perform(this); + if (import->media_queries()) { + append_mandatory_space(); + import->media_queries()->perform(this); + } append_delimiter(); } } diff --git a/parser.cpp b/parser.cpp index 3eb5d69c47..3c05e92cf2 100644 --- a/parser.cpp +++ b/parser.cpp @@ -12,6 +12,7 @@ #include "error_handling.hpp" #include +#include namespace Sass { using namespace std; @@ -297,7 +298,8 @@ namespace Sass { void Parser::import_single_file (Import* imp, string import_path) { - if (!unquote(import_path).substr(0, 7).compare("http://") || + if (imp->media_queries() || + !unquote(import_path).substr(0, 7).compare("http://") || !unquote(import_path).substr(0, 8).compare("https://") || !unquote(import_path).substr(0, 2).compare("//")) { @@ -358,6 +360,7 @@ namespace Sass { Import* Parser::parse_import() { Import* imp = new (ctx.mem) Import(pstate); + vector> to_import; bool first = true; do { while (lex< block_comment >()); @@ -365,7 +368,8 @@ namespace Sass { if (!do_import(lexed, imp, ctx.c_importers, true)) { // push single file import - import_single_file(imp, lexed); + // import_single_file(imp, lexed); + to_import.push_back(pair(string(lexed), 0)); } } else if (lex< uri_prefix >()) { @@ -387,7 +391,8 @@ namespace Sass { error("malformed URL", pstate); } if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate); - imp->urls().push_back(result); + // imp->urls().push_back(result); + to_import.push_back(pair("", result)); } else { if (first) error("@import directive requires a url or quoted path", pstate); @@ -395,6 +400,20 @@ namespace Sass { } first = false; } while (lex_css< exactly<','> >()); + + if (!peek_css,end_of_file>>()) { + List* media_queries = parse_media_queries(); + imp->media_queries(media_queries); + } + + for(auto location : to_import) { + if (location.second) { + imp->urls().push_back(location.second); + } else { + import_single_file(imp, location.first); + } + } + return imp; } From 906e39951c1c417cd857368c35314c123a48d13e Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 7 Apr 2015 03:00:46 +0200 Subject: [PATCH 23/31] Implement static selector namespace handling Missing implementation for interpolated namespaces! --- ast.hpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++++---- inspect.cpp | 8 +++---- parser.cpp | 5 ++-- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/ast.hpp b/ast.hpp index 1c9d4bd306..dbcb2c7d67 100644 --- a/ast.hpp +++ b/ast.hpp @@ -1719,6 +1719,7 @@ namespace Sass { // Abstract base class for simple selectors. //////////////////////////////////////////// class Simple_Selector : public Selector { + ADD_PROPERTY(string, ns); public: Simple_Selector(ParserState pstate) : Selector(pstate) @@ -1737,6 +1738,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() { } @@ -1783,7 +1785,21 @@ namespace Sass { public: Type_Selector(ParserState pstate, string n) : Simple_Selector(pstate), 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 unsigned long specificity() { // ToDo: What is the specificity of the star selector? @@ -1802,7 +1818,21 @@ namespace Sass { public: Selector_Qualifier(ParserState pstate, string n) : Simple_Selector(pstate), 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 unsigned long specificity() { if (name()[0] == '#') return Constants::Specificity_ID; @@ -1823,7 +1853,21 @@ namespace Sass { public: Attribute_Selector(ParserState pstate, string n, string m, String* v) : Simple_Selector(pstate), name_(n), matcher_(m), value_(v) - { } + { + 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 unsigned long specificity() { return Constants::Specificity_Attr; @@ -1852,7 +1896,21 @@ namespace Sass { public: Pseudo_Selector(ParserState pstate, string n, String* expr = 0) : Simple_Selector(pstate), name_(n), expression_(expr) - { } + { + 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_; + } // A pseudo-class always consists of a "colon" (:) followed by the name // of the pseudo-class and optionally by a value between parentheses. diff --git a/inspect.cpp b/inspect.cpp index 106bb7fcdd..90068ba5ad 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -840,12 +840,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(); } @@ -854,7 +854,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()) { @@ -867,7 +867,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 3c05e92cf2..2fb1c8f9b9 100644 --- a/parser.cpp +++ b/parser.cpp @@ -2078,6 +2078,8 @@ namespace Sass { >, // main selector match sequence < + // allow namespace prefix + optional < namespace_prefix >, // modifiers prefixes alternatives < sequence < @@ -2090,8 +2092,6 @@ namespace Sass { // single or double colon optional < pseudo_prefix > >, - // can be namespaced - optional < namespace_prefix >, // accept hypens in token one_plus < sequence < // can start with hyphens @@ -2099,6 +2099,7 @@ namespace Sass { // now the main token alternatives < kwd_optional, + exactly <'*'>, quoted_string, interpolant, identifier, From cd87ee362894b3b0dc413df031cb738134660558 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Mon, 15 Jun 2015 22:24:26 +0200 Subject: [PATCH 24/31] Implement initial parts for reference combinators --- ast.hpp | 5 +++-- debugger.hpp | 2 ++ eval.cpp | 4 ++++ extend.cpp | 1 + inspect.cpp | 7 +++++++ listize.cpp | 6 ++++++ node.cpp | 1 + parser.cpp | 18 ++++++++++++++++-- 8 files changed, 40 insertions(+), 4 deletions(-) diff --git a/ast.hpp b/ast.hpp index dbcb2c7d67..49efe532bb 100644 --- a/ast.hpp +++ b/ast.hpp @@ -2052,11 +2052,12 @@ namespace Sass { //////////////////////////////////////////////////////////////////////////// class Complex_Selector : public Selector { public: - enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO }; + enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE }; private: ADD_PROPERTY(Combinator, combinator) ADD_PROPERTY(Compound_Selector*, head) ADD_PROPERTY(Complex_Selector*, tail) + ADD_PROPERTY(String*, reference); public: bool contains_placeholder() { if (head() && head()->contains_placeholder()) return true; @@ -2067,7 +2068,7 @@ namespace Sass { Combinator c = ANCESTOR_OF, Compound_Selector* h = 0, Complex_Selector* t = 0) - : Selector(pstate), combinator_(c), head_(h), tail_(t) + : Selector(pstate), combinator_(c), head_(h), tail_(t), reference_(0) { if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true); diff --git a/debugger.hpp b/debugger.hpp index 0fc344347a..e9e6462e1d 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -123,7 +123,9 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0) case Complex_Selector::PRECEDES: del = "~"; break; case Complex_Selector::ADJACENT_TO: del = "+"; break; case Complex_Selector::ANCESTOR_OF: del = " "; break; + case Complex_Selector::REFERENCE: del = "//"; break; } + // if (del = "/") del += selector->reference()->perform(&to_string) + "/"; cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << endl; debug_ast(selector->head(), ind + " " /* + "[" + del + "]" */, env); if (selector->tail()) { diff --git a/eval.cpp b/eval.cpp index 60d7f978fe..6ede753b41 100644 --- a/eval.cpp +++ b/eval.cpp @@ -1397,8 +1397,10 @@ namespace Sass { bool parentized = false; Complex_Selector* tail = s->tail(); Compound_Selector* head = s->head(); + String* reference = s->reference(); Complex_Selector::Combinator combinator = s->combinator(); Selector_List* sl = new (ctx.mem) Selector_List(s->pstate()); + if (reference) reference = (String*) reference->perform(this); if (head) { // check if we have a parent selector reference (expands to list) @@ -1420,6 +1422,7 @@ namespace Sass { cp = new (ctx.mem) Complex_Selector(s->pstate()); cp->head(head); cp->tail(tt); cp->combinator(combinator); + cp->reference(reference); last->tail(cp); } else { last->tail(tt); @@ -1435,6 +1438,7 @@ namespace Sass { Complex_Selector* ns = (*pr)[n]->cloneFully(ctx); Complex_Selector* last = ns->last(); ns->combinator(combinator); + ns->reference(reference); for (size_t i = 1, iL = head->size(); i < iL; ++i) { // add simple selectors *last->head() << (*head)[i]; diff --git a/extend.cpp b/extend.cpp index e0213ea2fc..3f99f3cb02 100644 --- a/extend.cpp +++ b/extend.cpp @@ -76,6 +76,7 @@ namespace Sass { case Complex_Selector::PARENT_OF: os << "\">\""; break; case Complex_Selector::PRECEDES: os << "\"~\""; break; case Complex_Selector::ADJACENT_TO: os << "\"+\""; break; + case Complex_Selector::REFERENCE: os << "\"/\""; break; } return os; diff --git a/inspect.cpp b/inspect.cpp index 90068ba5ad..b59ed12b45 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -930,6 +930,13 @@ namespace Sass { append_string("+"); append_optional_space(); break; + case Complex_Selector::REFERENCE: + append_mandatory_space(); + append_string("/"); + c->reference()->perform(this); + append_string("/"); + append_mandatory_space(); + break; case Complex_Selector::PRECEDES: if (is_empty) append_optional_space(); else append_mandatory_space(); diff --git a/listize.cpp b/listize.cpp index 8c0e669c64..8cfae231ed 100644 --- a/listize.cpp +++ b/listize.cpp @@ -45,6 +45,9 @@ namespace Sass { if (hh) *l << hh; } + To_String to_string; + string reference = ! sel->reference() ? "" + : sel->reference()->perform(&to_string); switch(sel->combinator()) { case Complex_Selector::PARENT_OF: @@ -53,6 +56,9 @@ namespace Sass { case Complex_Selector::ADJACENT_TO: *l << new (ctx.mem) String_Constant(sel->pstate(), "+"); break; + case Complex_Selector::REFERENCE: + *l << new (ctx.mem) String_Constant(sel->pstate(), "/" + reference + "/"); + break; case Complex_Selector::PRECEDES: *l << new (ctx.mem) String_Constant(sel->pstate(), "~"); break; diff --git a/node.cpp b/node.cpp index d16d02abae..309c84d29f 100644 --- a/node.cpp +++ b/node.cpp @@ -142,6 +142,7 @@ namespace Sass { case Complex_Selector::PARENT_OF: os << "\">\""; break; case Complex_Selector::PRECEDES: os << "\"~\""; break; case Complex_Selector::ADJACENT_TO: os << "\"+\""; break; + case Complex_Selector::REFERENCE: os << "\"/\""; break; } } else if (node.isNil()) { diff --git a/parser.cpp b/parser.cpp index 2fb1c8f9b9..28f0853b0f 100644 --- a/parser.cpp +++ b/parser.cpp @@ -683,6 +683,7 @@ namespace Sass { Complex_Selector* Parser::parse_complex_selector(bool in_root) { + String* reference = 0; lex < block_comment >(); // parse the left hand side Compound_Selector* lhs = 0; @@ -700,6 +701,13 @@ namespace Sass { 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< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) { + // comments are allowed, but not spaces? + combinator = Complex_Selector::REFERENCE; + if (!lex < identifier >()) return 0; // ToDo: error msg? + reference = new (ctx.mem) String_Constant(pstate, lexed); + if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg? + } else /* if (lex< zero >()) */ combinator = Complex_Selector::ANCESTOR_OF; if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0; @@ -708,6 +716,7 @@ namespace Sass { // 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); + if (combinator == Complex_Selector::REFERENCE) sel->reference(reference); // has linfeed after combinator? sel->has_line_break(peek_newline()); // sel->has_line_feed(has_line_feed); @@ -905,7 +914,7 @@ namespace Sass { ParserState p = pstate; if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate); string name(lexed); - if (lex_css< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0); + if (lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0); if (!lex_css< alternatives< exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match > >()) { error("invalid operator in attribute selector for " + name, pstate); @@ -923,7 +932,7 @@ namespace Sass { error("expected a string constant or identifier in attribute selector for " + name, pstate); } - if (!lex_css< exactly<']'> >()) error("unterminated attribute selector for " + name, pstate); + if (!lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) error("unterminated attribute selector for " + name, pstate); return new (ctx.mem) Attribute_Selector(p, name, matcher, value); } @@ -2067,6 +2076,11 @@ namespace Sass { // match `/deep/` selector (pass-trough) // there is no functionality for it yet exactly, + sequence < + exactly <'/'>, + identifier, + exactly <'/'> + >, // match selector ops /[*&%,()\[\]]/ class_char < selector_lookahead_ops >, // match selector combinators /[>+~]/ From bcffad21489ba3f7c19a9c9796ada5183c890fb2 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Thu, 18 Jun 2015 02:23:58 +0200 Subject: [PATCH 25/31] Fix type-of function reporting color for strings Fixes https://github.com/sass/libsass/issues/1281 --- functions.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/functions.cpp b/functions.cpp index 6d6d4f28d1..1dbb1196b7 100644 --- a/functions.cpp +++ b/functions.cpp @@ -166,7 +166,6 @@ namespace Sass { 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; } @@ -1400,9 +1399,6 @@ namespace Sass { if (v->concrete_type() == Expression::STRING) { To_String to_string(&ctx); string str(v->perform(&to_string)); - if (ctx.names_to_colors.count(str)) { - return new (ctx.mem) String_Constant(pstate, "color"); - } } return new (ctx.mem) String_Constant(pstate, ARG("$value", Expression)->type()); } From 93cf5a1c9a6fd0c71b741e2e283c99f5aa0cbd1f Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 23 Jun 2015 19:13:09 +0200 Subject: [PATCH 26/31] Fix hashed index calculation for lists Fixes https://github.com/sass/libsass/issues/1283 --- ast.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast.hpp b/ast.hpp index 49efe532bb..ac14312fa3 100644 --- a/ast.hpp +++ b/ast.hpp @@ -771,7 +771,7 @@ namespace Sass { hash_ = std::hash()(separator() == COMMA ? "comma" : "space"); for (size_t i = 0, L = length(); i < L; ++i) - hash_ ^= (elements()[i])->hash(); + hash_ ^= (elements()[i])->hash() + (i + 1); return hash_; } From ec7d378501aa371b8cfddea52ac520e8a3670bb2 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 23 Jun 2015 19:46:35 +0200 Subject: [PATCH 27/31] Improve deprecation handling in unquote function Fixes https://github.com/sass/libsass/issues/1291 --- error_handling.cpp | 9 +++++++++ error_handling.hpp | 3 +++ functions.cpp | 14 +++++++++----- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/error_handling.cpp b/error_handling.cpp index 522994829b..29c2e1f153 100644 --- a/error_handling.cpp +++ b/error_handling.cpp @@ -20,6 +20,15 @@ namespace Sass { warn(msg, pstate); } + void deprecated(string msg, ParserState pstate) + { + string cwd(Sass::File::get_cwd()); + cerr << "DEPRECATION WARNING: " << msg << endl; + cerr << "will be an error in future versions of Sass." << endl; + string rel_path(Sass::File::resolve_relative_path(pstate.path, cwd, cwd)); + cerr << " on line " << pstate.line+1 << " of " << rel_path << endl; + } + void error(string msg, ParserState pstate) { throw Sass_Error(Sass_Error::syntax, pstate, msg); diff --git a/error_handling.hpp b/error_handling.hpp index 6580b79067..1c743ab0e4 100644 --- a/error_handling.hpp +++ b/error_handling.hpp @@ -24,6 +24,9 @@ namespace Sass { void warn(string msg, ParserState pstate); void warn(string msg, ParserState pstate, Backtrace* bt); + void deprecated(string msg, ParserState pstate); + // void deprecated(string msg, ParserState pstate, Backtrace* bt); + void error(string msg, ParserState pstate); void error(string msg, ParserState pstate, Backtrace* bt); diff --git a/functions.cpp b/functions.cpp index 1dbb1196b7..efefd9f968 100644 --- a/functions.cpp +++ b/functions.cpp @@ -822,17 +822,21 @@ namespace Sass { if (dynamic_cast(arg)) { return new (ctx.mem) Null(pstate); } - else if (List* list = dynamic_cast(arg)) { - return list; - } else if (String_Quoted* string_quoted = dynamic_cast(arg)) { String_Constant* result = new (ctx.mem) String_Constant(pstate, string_quoted->value()); // remember if the string was quoted (color tokens) result->sass_fix_1291(string_quoted->quote_mark() != 0); return result; } - To_String to_string(&ctx); - return new (ctx.mem) String_Constant(pstate, string(arg->perform(&to_string))); + else if (dynamic_cast(arg)) { + return (Expression*) arg; + } + else { + To_String to_string(&ctx); + string val(arg->perform(&to_string)); + deprecated("Passing " + val + ", a non-string value, to unquote()", pstate); + return (Expression*) arg; + } } Signature quote_sig = "quote($string)"; From 36f9c2aa66c33578906f9dc32057386a98469ae5 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 23 Jun 2015 21:24:15 +0200 Subject: [PATCH 28/31] Handle quoted string in case conversion functions Fixes https://github.com/sass/libsass/issues/1279 --- functions.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/functions.cpp b/functions.cpp index efefd9f968..478c10b1ab 100644 --- a/functions.cpp +++ b/functions.cpp @@ -987,9 +987,12 @@ namespace Sass { } if (String_Quoted* ss = dynamic_cast(s)) { - str = ss->quote_mark() ? quote(str) : str; + String_Quoted* cpy = new (ctx.mem) String_Quoted(*ss); + cpy->value(str); + return cpy; + } else { + return new (ctx.mem) String_Constant(pstate, str); } - return new (ctx.mem) String_Constant(pstate, str); } Signature to_lower_case_sig = "to-lower-case($string)"; @@ -1005,9 +1008,12 @@ namespace Sass { } if (String_Quoted* ss = dynamic_cast(s)) { - str = ss->quote_mark() ? quote(str, '"') : str; + String_Quoted* cpy = new (ctx.mem) String_Quoted(*ss); + cpy->value(str); + return cpy; + } else { + return new (ctx.mem) String_Constant(pstate, str); } - return new (ctx.mem) String_Constant(pstate, str); } /////////////////// From f1938e5e95ba9a2a092419ce51d6016ef8523b10 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 23 Jun 2015 22:52:03 +0200 Subject: [PATCH 29/31] Add default CRTP visitor for String_Quoted --- inspect.cpp | 3 --- operation.hpp | 2 ++ output.cpp | 18 +++++++----------- to_c.cpp | 3 +++ to_c.hpp | 1 + to_string.cpp | 5 +++++ to_string.hpp | 1 + 7 files changed, 19 insertions(+), 14 deletions(-) diff --git a/inspect.cpp b/inspect.cpp index b59ed12b45..0205b6aa96 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -654,9 +654,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 6364f66e24..e7b1c35b66 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 89312c0246..a0ddd1bc8a 100644 --- a/output.cpp +++ b/output.cpp @@ -355,18 +355,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 b03bb4a3fb..5b5da8337b 100644 --- a/to_c.cpp +++ b/to_c.cpp @@ -21,6 +21,9 @@ namespace Sass { Sass_Value* To_C::operator()(String_Constant* s) { return sass_make_string(s->value().c_str()); } + Sass_Value* To_C::operator()(String_Quoted* s) + { return sass_make_qstring(s->value().c_str()); } + Sass_Value* To_C::operator()(List* l) { Sass_Value* v = sass_make_list(l->length(), l->separator() == List::COMMA ? SASS_COMMA : SASS_SPACE); diff --git a/to_c.hpp b/to_c.hpp index 409c90c06c..8aef0c8b24 100644 --- a/to_c.hpp +++ b/to_c.hpp @@ -29,6 +29,7 @@ namespace Sass { Sass_Value* operator()(Number*); Sass_Value* operator()(Color*); Sass_Value* operator()(String_Constant*); + Sass_Value* operator()(String_Quoted*); Sass_Value* operator()(List*); Sass_Value* operator()(Map*); Sass_Value* operator()(Null*); 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 ""; } } diff --git a/to_string.hpp b/to_string.hpp index 38c2a118f2..cd9da31ab0 100644 --- a/to_string.hpp +++ b/to_string.hpp @@ -25,6 +25,7 @@ namespace Sass { virtual ~To_String(); string operator()(Null* n); + string operator()(String_Quoted*); string operator()(String_Constant*); template From 60d26948e2569ffa3f8e53393622f1dcea09de0a Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Tue, 23 Jun 2015 23:25:31 +0200 Subject: [PATCH 30/31] Next WIP parser refactoring iteration Re-armed the error handling and slightly cleaned up main block parsing function. Includes hash improvement for list as keys --- ast.hpp | 14 ++- bind.cpp | 5 +- cssize.cpp | 4 +- debugger.hpp | 1 + eval.cpp | 66 ++++++------ eval.hpp | 2 + expand.cpp | 4 +- functions.cpp | 38 +++---- listize.cpp | 10 +- output.cpp | 4 +- parser.cpp | 279 +++++++++++++++++++++++++++----------------------- parser.hpp | 2 + prelexer.cpp | 6 +- prelexer.hpp | 3 +- to_string.cpp | 13 ++- to_string.hpp | 1 + 16 files changed, 248 insertions(+), 204 deletions(-) diff --git a/ast.hpp b/ast.hpp index ac14312fa3..e8c5e99ac8 100644 --- a/ast.hpp +++ b/ast.hpp @@ -53,6 +53,13 @@ namespace Sass { using namespace std; + template + void hash_combine (std::size_t& seed, const T& val) + { + seed ^= std::hash()(val) + 0x9e3779b9 + + (seed<<6) + (seed>>2); + } + ////////////////////////////////////////////////////////// // Abstract base class for all abstract syntax tree nodes. ////////////////////////////////////////////////////////// @@ -454,11 +461,11 @@ namespace Sass { ADD_PROPERTY(String*, property) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_important) - ADD_PROPERTY(bool, surfer); + 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), surfer_(false) + : Statement(pstate), property_(prop), value_(val), is_important_(i), is_indented_(false) { } ATTACH_OPERATIONS() }; @@ -769,9 +776,8 @@ 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_ ^= (elements()[i])->hash() + (i + 1); + hash_combine(hash_, (elements()[i])->hash()); return hash_; } diff --git a/bind.cpp b/bind.cpp index 339b935937..7a73c1e9ea 100644 --- a/bind.cpp +++ b/bind.cpp @@ -18,7 +18,10 @@ namespace Sass { 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('*'); + if (str->quote_mark()) { + str->quote_mark('*'); + str->is_delayed(true); + } } } diff --git a/cssize.cpp b/cssize.cpp index cf942a70fc..45159e9fb7 100644 --- a/cssize.cpp +++ b/cssize.cpp @@ -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/debugger.hpp b/debugger.hpp index e9e6462e1d..3cf1c51759 100644 --- a/debugger.hpp +++ b/debugger.hpp @@ -586,6 +586,7 @@ 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 6ede753b41..dbe2b80491 100644 --- a/eval.cpp +++ b/eval.cpp @@ -455,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); @@ -532,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); @@ -564,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; } @@ -597,7 +598,7 @@ namespace Sass { c->name(), args); To_String to_string(&ctx); - return new (ctx.mem) String_Constant(c->pstate(), + return new (ctx.mem) String_Quoted(c->pstate(), lit->perform(&to_string)); } else { // call generic function @@ -643,7 +644,7 @@ namespace Sass { 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; @@ -718,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) { @@ -784,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 '#' @@ -839,7 +836,7 @@ namespace Sass { 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())); } @@ -847,17 +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 (Selector_List* sel = dynamic_cast(s)) { - To_String to_string(&ctx); - return evacuate_quotes(sel->perform(this)->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)); @@ -885,22 +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); + } else if (dynamic_cast(s)) { + Expression* ex = s->perform(this); return evacuate_quotes(interpolation(ex)); - } else if (Function_Call* 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); - 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)); } } @@ -923,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()); @@ -932,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(), @@ -993,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(), @@ -1154,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()); @@ -1224,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); @@ -1330,7 +1326,7 @@ namespace Sass { e = new (ctx.mem) Color(pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); } break; case SASS_STRING: { - e = new (ctx.mem) String_Constant(pstate, sass_string_get_value(v)); + e = new (ctx.mem) String_Quoted(pstate, sass_string_get_value(v)); } break; case SASS_LIST: { List* l = new (ctx.mem) List(pstate, sass_list_get_length(v), sass_list_get_separator(v) == SASS_COMMA ? List::COMMA : List::SPACE); diff --git a/eval.hpp b/eval.hpp index d837e72e21..146f1d7126 100644 --- a/eval.hpp +++ b/eval.hpp @@ -55,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*); diff --git a/expand.cpp b/expand.cpp index b0baa56418..df3beafb0b 100644 --- a/expand.cpp +++ b/expand.cpp @@ -122,7 +122,7 @@ namespace Sass { 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 { @@ -573,7 +573,7 @@ namespace Sass { inline Statement* Expand::fallback_impl(AST_Node* n) { string err = string("`Expand` doesn't handle ") + typeid(*n).name(); - String_Constant* msg = new (ctx.mem) String_Constant(ParserState("[WARN]"), err); + 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); } diff --git a/functions.cpp b/functions.cpp index 478c10b1ab..bebbadcca7 100644 --- a/functions.cpp +++ b/functions.cpp @@ -472,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); @@ -533,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); @@ -570,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); @@ -590,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()); @@ -808,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); } /////////////////// @@ -823,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; @@ -845,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; } @@ -910,7 +910,7 @@ namespace Sass { // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, backtrace); } - return new (ctx.mem) String_Constant(pstate, str); + return new (ctx.mem) String_Quoted(pstate, str); } Signature str_index_sig = "str-index($string, $substring)"; @@ -991,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); } } @@ -1012,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); } } @@ -1303,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"); } @@ -1392,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; @@ -1410,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)"; @@ -1568,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 { @@ -1583,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); } @@ -1752,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; @@ -1827,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/listize.cpp b/listize.cpp index 8cfae231ed..3fc909de63 100644 --- a/listize.cpp +++ b/listize.cpp @@ -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) @@ -51,16 +51,16 @@ 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::REFERENCE: - *l << new (ctx.mem) String_Constant(sel->pstate(), "/" + reference + "/"); + *l << new (ctx.mem) String_Quoted(sel->pstate(), "/" + reference + "/"); 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; diff --git a/output.cpp b/output.cpp index a0ddd1bc8a..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; } } diff --git a/parser.cpp b/parser.cpp index 28f0853b0f..b85face2a3 100644 --- a/parser.cpp +++ b/parser.cpp @@ -67,6 +67,7 @@ namespace Sass { return p; } + /* main entry point to parse root block */ Block* Parser::parse() { Block* root = new (ctx.mem) Block(pstate, 0, true); @@ -86,8 +87,13 @@ namespace Sass { } block_stack.push_back(root); - parse_block_nodes(); + /* bool rv = */ parse_block_nodes(); block_stack.pop_back(); + + if (position != end) { + css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); + } + return root; } @@ -97,17 +103,28 @@ namespace Sass { // this is the base block parsing function Block* Parser::parse_css_block(bool is_root) { + // parse comments before block - lex < optional_css_comments >(); + // lex < optional_css_comments >(); // lex mandatory opener or error out - if (!lex< exactly<'{'> >()) { + 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); - parse_block_nodes(); + + if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");; + + if (!lex_css < exactly<'}'> >()) { + css_error("Invalid CSS", " after ", ": expected \"}\", was "); + } + + // parse comments before block + // lex < optional_css_comments >(); + block_stack.pop_back(); + return block; } @@ -124,147 +141,138 @@ namespace Sass { // parses stuff between `{` and `}` bool Parser::parse_block_nodes() { + // loop until end of string while (position < end) { - // parse comment blocks + + // we should be able to refactor this + parse_block_comments(); + lex < css_whitespace >(); + + if (lex < exactly<';'> >()) continue; + if (peek < end_of_file >()) return true; + if (peek < exactly<'}'> >()) return true; + + if (parse_block_node()) continue; + parse_block_comments(); - // check scope closing condition - if (lex< exactly<'}'> >()) break; - // delegate rest to the block parser - else if (!parse_block_node()) return false; + + 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; } - // main parser for anything between `{` and `}` + // 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; - Block* block = block_stack.back(); + // also parse block comments - if (!block->is_root() && lex < kwd_extend >(true)) { - 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); + // 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 are not allowed inside mixins and functions", pstate); + 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]); } } } - // consume any superfluous semicolons - else if (lex < one_plus < exactly <';'> > >(true)) {} - else if (lex < kwd_content >(true)) { - if (stack.back() == mixin_def) *block << new (ctx.mem) Content(pstate); - else error("@content may only be used within a mixin", pstate); - } - else if (!(lookahead_result = lookahead_for_selector(position)).error) { - (*block) << parse_ruleset(lookahead_result); - } - 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_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(); - } - else if (lex < kwd_supports_directive >(true)) { - (*block) << parse_supports_directive(); - } - else if (lex < variable >(true)) { - (*block) << parse_assignment(); - } - else if (lex < kwd_warn >(true)) { - (*block) << parse_warning(); - } - else if (lex < kwd_err >(true)) { - (*block) << parse_error(); - } - else if (lex < kwd_dbg >(true)) { - (*block) << parse_debug(); - } - else if (lex < kwd_mixin >(true)) { - (*block) << parse_definition(Definition::MIXIN); - } - else if (lex < kwd_function >(true)) { - (*block) << parse_definition(Definition::FUNCTION); + else if (lex < kwd_extend >(true)) { + if (block->is_root()) { + error("Extend directives may only be used within rules.", pstate); + } + + 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< exactly< charset_kwd > >(true)) { - lex < - sequence < - quoted_string, - optional_spaces, - exactly <';'> - > - >(); - } - // generic at keyword (keep last) - else if (lex< at_keyword >(true)) { - (*block) << parse_at_rule(); - } + else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } - else if (!block->is_root() && !peek< exactly<';'> >()) { + // 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 - if (!decl->surfer()) indentation++; - Propset* ps = new (ctx.mem) Propset(pstate, decl->property(), parse_block()); - if (!decl->surfer()) indentation--; - (*block) << ps; - } - else { - // finish and let the semicolon get munched + (*block) << new (ctx.mem) Propset(pstate, decl->property(), parse_block()); + if (decl->is_indented()) -- indentation; } } - else if (!block->is_root() && stack.back() == function_def) { - error("only variable declarations and control directives are allowed inside functions", pstate); - } - else if (block->is_root()) { - lex< css_whitespace >(); - if (position >= end) return true; - css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); - } - // nothing matched - else { return false; } // something matched return true; } @@ -609,6 +617,17 @@ namespace Sass { } // EO parse_selector_schema + void Parser::parse_charset_directive() + { + lex < + sequence < + quoted_string, + optional_spaces, + exactly <';'> + > + >(); + } + // called after parsing `kwd_include_directive` Mixin_Call* Parser::parse_include_directive() { @@ -705,7 +724,7 @@ namespace Sass { // comments are allowed, but not spaces? combinator = Complex_Selector::REFERENCE; if (!lex < identifier >()) return 0; // ToDo: error msg? - reference = new (ctx.mem) String_Constant(pstate, lexed); + reference = new (ctx.mem) String_Quoted(pstate, lexed); if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg? } else /* if (lex< zero >()) */ combinator = Complex_Selector::ANCESTOR_OF; @@ -923,7 +942,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! @@ -959,11 +978,11 @@ namespace Sass { else { error("invalid property name", pstate); } - bool surfer = false; + 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<'{'> >()) surfer = true; // surf with the indentation + 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()*/); } @@ -987,7 +1006,7 @@ namespace Sass { } auto decl = new (ctx.mem) Declaration(prop->pstate(), prop, value/*, lex()*/); - decl->surfer(surfer); + decl->is_indented(is_indented); return decl; } } @@ -1359,7 +1378,7 @@ namespace Sass { 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); } @@ -1393,7 +1412,7 @@ namespace Sass { { return new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed); } if (lex< sequence< static_component, one_plus< identifier > > >()) - { return new (ctx.mem) String_Constant(pstate, lexed); } + { return new (ctx.mem) String_Quoted(pstate, lexed); } if (lex< number >()) { return new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); } @@ -1495,10 +1514,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); @@ -1506,10 +1522,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 "); @@ -1529,10 +1542,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; } @@ -1547,10 +1557,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(); } @@ -1570,13 +1580,13 @@ namespace Sass { 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, " "); } // 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_Constant(pstate, lexed); + (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); } else { (*schema) << parse_list(); } @@ -1585,7 +1595,7 @@ namespace Sass { } // lex some string constants else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) { - (*schema) << new (ctx.mem) String_Constant(pstate, lexed); + (*schema) << new (ctx.mem) String_Quoted(pstate, lexed); } // lex a quoted string else if (lex< quoted_string >()) { @@ -1642,7 +1652,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 @@ -1712,6 +1722,14 @@ 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) { ParserState if_source_position = pstate; @@ -1847,7 +1865,7 @@ namespace Sass { 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); } @@ -2341,7 +2359,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 139dfa810c..dbb9c7ad64 100644 --- a/parser.hpp +++ b/parser.hpp @@ -260,6 +260,8 @@ 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(); diff --git a/prelexer.cpp b/prelexer.cpp index 5735b82ec7..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); } diff --git a/prelexer.hpp b/prelexer.hpp index ef1984c918..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); diff --git a/to_string.cpp b/to_string.cpp index fde1fe5d5d..11b0329b13 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->size(); 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 cd9da31ab0..a8f0af4cae 100644 --- a/to_string.hpp +++ b/to_string.hpp @@ -25,6 +25,7 @@ namespace Sass { virtual ~To_String(); string operator()(Null* n); + string operator()(String_Schema*); string operator()(String_Quoted*); string operator()(String_Constant*); From 789c41b05fa83639043c183e69e54f35ea1ab088 Mon Sep 17 00:00:00 2001 From: Eric Kimn Date: Tue, 30 Jun 2015 00:58:57 -0700 Subject: [PATCH 31/31] Fix for null selectors and respecting specificity. --- expand.cpp | 10 +++++++++- extend.cpp | 18 +++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/expand.cpp b/expand.cpp index df3beafb0b..f97817e729 100644 --- a/expand.cpp +++ b/expand.cpp @@ -12,6 +12,7 @@ #include "backtrace.hpp" #include "context.hpp" #include "parser.hpp" +#include "debug.hpp" namespace Sass { @@ -96,8 +97,10 @@ namespace Sass { return k; } + //DEBUG_PRINTLN(ALL, "Expand Ruleset: Before eval") Expression* ex = r->selector()->perform(&eval); Selector_List* sel = dynamic_cast(ex); + //DEBUG_PRINTLN(ALL, "Expand Ruleset: " << sel->mCachedSelector()) if (sel == 0) throw runtime_error("Expanded null selector"); selector_stack.push_back(sel); @@ -271,7 +274,12 @@ namespace Sass { } } else { - env->set_lexical(var, a->value()->perform(&eval)); + //DEBUG_PRINTLN(ALL, "Assign: " << var.data()); + auto val = a->value()->perform(&eval); + if(val == NULL) { + val = new (ctx.mem) Null(a->pstate()); + } + env->set_lexical(var, val); } return 0; } diff --git a/extend.cpp b/extend.cpp index 3f99f3cb02..dcdbff5050 100644 --- a/extend.cpp +++ b/extend.cpp @@ -478,7 +478,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. @@ -521,7 +521,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) @@ -1462,7 +1462,7 @@ namespace Sass { Complex_Selector* pComplexSelector, Context& ctx, ExtensionSubsetMap& subset_map, - set seen); + set seen, bool isReplace); @@ -1490,7 +1490,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: ")) @@ -1630,7 +1630,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) @@ -1741,7 +1741,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) @@ -1767,7 +1767,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) @@ -1825,7 +1825,7 @@ namespace Sass { // Ruby Equivalent: trim - Node trimmed = trim(weaves, ctx); + Node trimmed = trim(weaves, ctx, isReplace); DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed) @@ -1872,7 +1872,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;