diff --git a/Makefile.conf b/Makefile.conf index 2fa75560a4..8fb846a55f 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -1,33 +1,38 @@ # this is merely a common Makefile multiple implementers can use +# bigger files (in terms of compile time) tend to go to the top, +# so they don't end up as the last compile unit when compiling +# in parallel. But we also want to mix them a little too avoid +# heavy RAM usage peaks. Other than that the order is arbitrary. + SOURCES = \ ast.cpp \ - base64vlq.cpp \ - bind.cpp \ - check_nesting.cpp \ - color_maps.cpp \ - constants.cpp \ + node.cpp \ context.cpp \ - cssize.cpp \ - emitter.cpp \ + constants.cpp \ + functions.cpp \ + color_maps.cpp \ environment.cpp \ - error_handling.cpp \ - eval.cpp \ - expand.cpp \ - extend.cpp \ + bind.cpp \ file.cpp \ - functions.cpp \ - inspect.cpp \ + util.cpp \ json.cpp \ - lexer.cpp \ - listize.cpp \ - memory_manager.cpp \ - node.cpp \ - output.cpp \ - parser.cpp \ + units.cpp \ + values.cpp \ plugins.cpp \ position.cpp \ + lexer.cpp \ + parser.cpp \ prelexer.cpp \ + eval.cpp \ + expand.cpp \ + listize.cpp \ + cssize.cpp \ + extend.cpp \ + output.cpp \ + inspect.cpp \ + emitter.cpp \ + check_nesting.cpp \ remove_placeholders.cpp \ sass.cpp \ sass_util.cpp \ @@ -35,12 +40,12 @@ SOURCES = \ sass_context.cpp \ sass_functions.cpp \ sass2scss.cpp \ - source_map.cpp \ to_c.cpp \ to_value.cpp \ - units.cpp \ + source_map.cpp \ + error_handling.cpp \ + memory_manager.cpp \ utf8_string.cpp \ - values.cpp \ - util.cpp + base64vlq.cpp CSOURCES = cencode.c diff --git a/src/ast.cpp b/src/ast.cpp index 464a801bbe..d3c574b9d8 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -122,6 +122,11 @@ namespace Sass { pstate_.offset += pstate - pstate_ + pstate.offset; } + void AST_Node::set_pstate_offset(const Offset& offset) + { + pstate_.offset = offset; + } + inline bool is_ns_eq(const std::string& l, const std::string& r) { if (l.empty() && r.empty()) return true; @@ -1056,22 +1061,30 @@ namespace Sass { if (Class_Selector* sq = dynamic_cast(rh->last())) { Class_Selector* sqs = SASS_MEMORY_NEW(ctx.mem, Class_Selector, *sq); sqs->name(sqs->name() + (*h)[0]->name()); + sqs->pstate((*h)[0]->pstate()); (*rh)[rh->length()-1] = sqs; + rh->pstate(h->pstate()); for (i = 1; i < L; ++i) *rh << (*h)[i]; } else if (Id_Selector* sq = dynamic_cast(rh->last())) { Id_Selector* sqs = SASS_MEMORY_NEW(ctx.mem, Id_Selector, *sq); sqs->name(sqs->name() + (*h)[0]->name()); + sqs->pstate((*h)[0]->pstate()); (*rh)[rh->length()-1] = sqs; + rh->pstate(h->pstate()); for (i = 1; i < L; ++i) *rh << (*h)[i]; } else if (Element_Selector* ts = dynamic_cast(rh->last())) { Element_Selector* tss = SASS_MEMORY_NEW(ctx.mem, Element_Selector, *ts); tss->name(tss->name() + (*h)[0]->name()); + tss->pstate((*h)[0]->pstate()); (*rh)[rh->length()-1] = tss; + rh->pstate(h->pstate()); for (i = 1; i < L; ++i) *rh << (*h)[i]; } else if (Placeholder_Selector* ps = dynamic_cast(rh->last())) { Placeholder_Selector* pss = SASS_MEMORY_NEW(ctx.mem, Placeholder_Selector, *ps); pss->name(pss->name() + (*h)[0]->name()); + pss->pstate((*h)[0]->pstate()); (*rh)[rh->length()-1] = pss; + rh->pstate(h->pstate()); for (i = 1; i < L; ++i) *rh << (*h)[i]; } else { *last()->head_ += h; @@ -1150,8 +1163,19 @@ namespace Sass { Sequence_Selector* ss = this->clone(ctx); ss->tail(t ? t->clone(ctx) : 0); SimpleSequence_Selector* h = head_->clone(ctx); + // remove parent selector from sequence if (h->length()) h->erase(h->begin()); ss->head(h->length() ? h : 0); + // adjust for parent selector (1 char) + if (h->length()) { + ParserState state((*h)[0]->pstate()); + state.offset.column += 1; + state.column -= 1; + (*h)[0]->pstate(state); + } + // keep old parser state + s->pstate(pstate()); + // append new tail s->append(ctx, ss); *retval << s; } @@ -1171,10 +1195,21 @@ namespace Sass { } ss->tail(tail ? tail->clone(ctx) : 0); SimpleSequence_Selector* h = head_->clone(ctx); + // remove parent selector from sequence if (h->length()) h->erase(h->begin()); ss->head(h->length() ? h : 0); // \/ IMO ruby sass bug \/ ss->has_line_feed(false); + // adjust for parent selector (1 char) + if (h->length()) { + ParserState state((*h)[0]->pstate()); + state.offset.column += 1; + state.column -= 1; + (*h)[0]->pstate(state); + } + // keep old parser state + s->pstate(pstate()); + // append new tail s->append(ctx, ss); *retval << s; } @@ -1557,6 +1592,13 @@ namespace Sass { return result; } + SimpleSequence_Selector& SimpleSequence_Selector::operator<<(Simple_Selector* element) + { + Vectorized::operator<<(element); + pstate_.offset += element->pstate().offset; + return *this; + } + SimpleSequence_Selector* SimpleSequence_Selector::minus(SimpleSequence_Selector* rhs, Context& ctx) { SimpleSequence_Selector* result = SASS_MEMORY_NEW(ctx.mem, SimpleSequence_Selector, pstate()); diff --git a/src/ast.hpp b/src/ast.hpp index daf8242010..79d5205d53 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -101,6 +101,7 @@ namespace Sass { // virtual Block* block() { return 0; } public: void update_pstate(const ParserState& pstate); + void set_pstate_offset(const Offset& offset); public: Offset off() { return pstate(); } Position pos() { return pstate(); } @@ -235,7 +236,7 @@ namespace Sass { T& operator[](size_t i) { return elements_[i]; } virtual const T& at(size_t i) const { return elements_.at(i); } const T& operator[](size_t i) const { return elements_[i]; } - Vectorized& operator<<(T element) + virtual Vectorized& operator<<(T element) { if (!element) return *this; reset_hash(); @@ -2292,6 +2293,8 @@ namespace Sass { return false; }; + SimpleSequence_Selector& operator<<(Simple_Selector* element); + bool is_universal() const { return length() == 1 && (*this)[0]->is_universal(); diff --git a/src/eval.cpp b/src/eval.cpp index b808f13298..546ffd72e3 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -502,7 +502,7 @@ namespace Sass { // only the last item will be used to eval the binary expression if (String_Schema* s_l = dynamic_cast(b->left())) { if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) { - ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_l->pstate()); + ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, b->pstate()); Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(), b->op(), s_l->last(), b->right()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // unverified @@ -515,7 +515,7 @@ namespace Sass { } if (String_Schema* s_r = dynamic_cast(b->right())) { if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) { - ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_r->pstate()); + ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, b->pstate()); Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(), b->op(), b->left(), s_r->first()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // verified @@ -599,7 +599,7 @@ namespace Sass { std::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) { - lhs = SASS_MEMORY_NEW(ctx.mem, Textual, lhs->pstate(), Textual::DIMENSION, str->value()); + lhs = SASS_MEMORY_NEW(ctx.mem, Textual, b->pstate(), Textual::DIMENSION, str->value()); lhs = lhs->perform(this); } } @@ -607,7 +607,7 @@ namespace Sass { std::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::number >(start) != 0) { - rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value()); + rhs = SASS_MEMORY_NEW(ctx.mem, Textual, b->pstate(), Textual::DIMENSION, str->value()); rhs = rhs->perform(this); } } @@ -636,7 +636,7 @@ namespace Sass { str += b->separator(); if (b->op().ws_after) str += " "; str += v_r->to_string(ctx.c_options); - String_Constant* val = SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(), str); + String_Constant* val = SASS_MEMORY_NEW(ctx.mem, String_Constant, b->pstate(), str); val->is_interpolant(b->left()->has_interpolant()); return val; } @@ -1545,7 +1545,7 @@ namespace Sass { (sep != "/" || !rqstr || !rqstr->quote_mark()) */ ) { // create a new string that might be quoted on output (but do not unquote what we pass) - return SASS_MEMORY_NEW(mem, String_Quoted, lhs.pstate(), lstr + rstr, 0, false, true); + return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : lhs.pstate(), lstr + rstr, 0, false, true); } if (sep != "" && !delayed) { @@ -1558,7 +1558,7 @@ namespace Sass { if (rqstr && rqstr->quote_mark()) rstr = quote(rstr); } - return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), lstr + sep + rstr); + return SASS_MEMORY_NEW(mem, String_Constant, pstate ? *pstate : lhs.pstate(), lstr + sep + rstr); } Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate) diff --git a/src/json.cpp b/src/json.cpp index e9940f0d51..8f433f5d0b 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -402,7 +402,7 @@ char *json_encode_string(const char *str) try { emit_string(&sb, str); } - catch (std::exception &e) { + catch (std::exception) { sb_free(&sb); throw; } @@ -421,7 +421,7 @@ char *json_stringify(const JsonNode *node, const char *space) else emit_value(&sb, node); } - catch (std::exception &e) { + catch (std::exception) { sb_free(&sb); throw; } diff --git a/src/parser.cpp b/src/parser.cpp index 79a35c9753..818ac58d81 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -33,6 +33,8 @@ namespace Sass { Parser Parser::from_c_str(const char* beg, Context& ctx, ParserState pstate, const char* source) { + pstate.offset.column = 0; + pstate.offset.line = 0; Parser p(ctx, pstate); p.source = source ? source : beg; p.position = beg ? beg : p.source; @@ -45,6 +47,8 @@ namespace Sass { Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate, const char* source) { + pstate.offset.column = 0; + pstate.offset.line = 0; Parser p(ctx, pstate); p.source = source ? source : beg; p.position = beg ? beg : p.source; @@ -55,6 +59,14 @@ namespace Sass { return p; } + void Parser::advanceToNextToken() { + lex < css_comments >(false); + // advance to position + pstate += pstate.offset; + pstate.offset.column = 0; + pstate.offset.line = 0; + } + CommaSequence_Selector* Parser::parse_selector(const char* beg, Context& ctx, ParserState pstate, const char* source) { Parser p = Parser::from_c_str(beg, ctx, pstate, source); @@ -655,6 +667,9 @@ namespace Sass { String* reference = 0; lex < block_comment >(); + + Sequence_Selector* sel = SASS_MEMORY_NEW(ctx.mem, Sequence_Selector, pstate); + // parse the left hand side SimpleSequence_Selector* lhs = 0; // special case if it starts with combinator ([+~>]) @@ -683,9 +698,8 @@ namespace Sass { if (!lhs && combinator == Sequence_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 >()); - Sequence_Selector* sel = SASS_MEMORY_NEW(ctx.mem, Sequence_Selector, pstate, combinator, lhs); + sel->head(lhs); + sel->combinator(combinator); sel->media_block(last_media_block); if (combinator == Sequence_Selector::REFERENCE) sel->reference(reference); @@ -703,9 +717,9 @@ namespace Sass { // also skip adding parent ref if we only have refs if (!sel->has_parent_ref() && !in_at_root && !in_root) { // create the objects to wrap parent selector reference + SimpleSequence_Selector* head = SASS_MEMORY_NEW(ctx.mem, SimpleSequence_Selector, pstate); Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate, false); parent->media_block(last_media_block); - SimpleSequence_Selector* head = SASS_MEMORY_NEW(ctx.mem, SimpleSequence_Selector, pstate); head->media_block(last_media_block); // add simple selector (*head) << parent; @@ -720,6 +734,8 @@ namespace Sass { // if (peek_newline()) head->has_line_break(true); } + sel->update_pstate(pstate); + // complex selector return sel; } @@ -1173,6 +1189,8 @@ namespace Sass { // parse logical OR operation Expression* Parser::parse_disjunction() { + advanceToNextToken(); + ParserState state(pstate); // parse the left hand side conjunction Expression* conj = parse_conjunction(); // parse multiple right hand sides @@ -1182,13 +1200,18 @@ namespace Sass { // 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, { Sass_OP::OR }); + Expression* ex = fold_operands(conj, operands, { Sass_OP::OR }); + state.offset = pstate - state + pstate.offset; + ex->pstate(state); + return ex; } // EO parse_disjunction // parse logical AND operation Expression* Parser::parse_conjunction() { + advanceToNextToken(); + ParserState state(pstate); // parse the left hand side relation Expression* rel = parse_relation(); // parse multiple right hand sides @@ -1198,13 +1221,18 @@ namespace Sass { // 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, { Sass_OP::AND }); + Expression* ex = fold_operands(rel, operands, { Sass_OP::AND }); + state.offset = pstate - state + pstate.offset; + ex->pstate(state); + return ex; } // EO parse_conjunction // parse comparison operations Expression* Parser::parse_relation() { + advanceToNextToken(); + ParserState state(pstate); // parse the left hand side expression Expression* lhs = parse_expression(); std::vector operands; @@ -1242,7 +1270,10 @@ namespace Sass { // correctly set to zero. After folding we also unwrap // single nested items. So we cannot set delay on the // returned result here, as we have lost nestings ... - return fold_operands(lhs, operands, operators); + Expression* ex = fold_operands(lhs, operands, operators); + state.offset = pstate - state + pstate.offset; + ex->pstate(state); + return ex; } // parse_relation @@ -1253,6 +1284,8 @@ namespace Sass { // parse addition and subtraction operations Expression* Parser::parse_expression() { + advanceToNextToken(); + ParserState state(pstate); // parses multiple add and subtract operations // NOTE: make sure that identifiers starting with // NOTE: dashes do NOT count as subtract operation @@ -1285,12 +1318,17 @@ namespace Sass { } if (operands.size() == 0) return lhs; - return fold_operands(lhs, operands, operators); + Expression* ex = fold_operands(lhs, operands, operators); + state.offset = pstate - state + pstate.offset; + ex->pstate(state); + return ex; } // parse addition and subtraction operations Expression* Parser::parse_operators() { + advanceToNextToken(); + ParserState state(pstate); Expression* factor = parse_factor(); // if it's a singleton, return it (don't wrap it) std::vector operands; // factors @@ -1309,7 +1347,10 @@ namespace Sass { left_ws = peek < css_comments >(); } // operands and operators to binary expression - return fold_operands(factor, operands, operators); + Expression* ex = fold_operands(factor, operands, operators); + state.offset = pstate - state + pstate.offset; + ex->pstate(state); + return ex; } // EO parse_operators @@ -1527,6 +1568,9 @@ namespace Sass { { lex< static_value >(); Token str(lexed); + // static values always have trailing white- + // space and end delimiter (\s*[;]$) included + -- pstate.offset.column; --str.end; --position; @@ -1984,22 +2028,22 @@ namespace Sass { List* Parser::parse_media_queries() { + advanceToNextToken(); List* media_queries = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_COMMA); if (!peek_css < exactly <'{'> >()) (*media_queries) << parse_media_query(); while (lex_css < exactly <','> >()) (*media_queries) << parse_media_query(); + media_queries->update_pstate(pstate); return media_queries; } // Expression* Parser::parse_media_query() Media_Query* Parser::parse_media_query() { + advanceToNextToken(); Media_Query* media_query = SASS_MEMORY_NEW(ctx.mem, Media_Query, pstate); + if (lex < kwd_not >()) { media_query->is_negated(true); lex < css_comments >(false); } + else if (lex < kwd_only >()) { media_query->is_restricted(true); lex < css_comments >(false); } - lex < css_comments >(false); - if (lex < kwd_not >()) media_query->is_negated(true); - else if (lex < kwd_only >()) media_query->is_restricted(true); - - lex < css_comments >(false); 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(); @@ -2013,6 +2057,9 @@ namespace Sass { media_query->media_type(schema); } while (lex_css < kwd_and >()) (*media_query) << parse_media_expression(); + + media_query->update_pstate(pstate); + return media_query; } @@ -2620,7 +2667,7 @@ namespace Sass { Expression* Parser::fold_operands(Expression* base, std::vector& operands, Operand op) { for (size_t i = 0, S = operands.size(); i < S; ++i) { - base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]); + base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), op, base, operands[i]); } return base; } diff --git a/src/parser.hpp b/src/parser.hpp index 774248ecca..2d725c5db6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -66,6 +66,10 @@ namespace Sass { #endif + // skip current token and next whitespace + // moves ParserState right before next token + void advanceToNextToken(); + bool peek_newline(const char* start = 0); // skip over spaces, tabs and line comments