From 8e80295068cbf24fbd038e56401ea5821968ca01 Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Fri, 21 Oct 2016 03:17:35 +0200 Subject: [PATCH] Fix ParserState for SourceMaps - Fixes parent selector mappings - Fixes media block/query mappings - Fixes range over binary expressions - Don't include semicolon for statics --- src/ast.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/ast.hpp | 5 ++++- src/parser.cpp | 45 +++++++++++++++++++++++++++++++++++---------- src/parser.hpp | 4 ++++ 4 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/ast.cpp b/src/ast.cpp index 464a801bbe..68d5fc349c 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,17 @@ 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) + ParserState nstate = (*h)[0]->pstate(); + nstate.offset.column += 1; + nstate.column -= 1; + (*h)[0]->pstate(nstate); + // keep old parser state + s->pstate(pstate()); + // append new tail s->append(ctx, ss); *retval << s; } @@ -1171,10 +1193,19 @@ 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) + ParserState nstate = (*h)[0]->pstate(); + nstate.offset.column += 1; + nstate.column -= 1; + (*h)[0]->pstate(nstate); + // keep old parser state + s->pstate(pstate()); + // append new tail s->append(ctx, ss); *retval << s; } @@ -1557,6 +1588,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 92c0e2f697..005ea6f63d 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(); @@ -2287,6 +2288,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/parser.cpp b/src/parser.cpp index 79a35c9753..01b1873f5c 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; } @@ -1182,6 +1198,7 @@ namespace Sass { // if it's a singleton, return it directly if (operands.size() == 0) return conj; // fold all operands into one binary expression + conj->set_pstate_offset(pstate - conj->pstate()); return fold_operands(conj, operands, { Sass_OP::OR }); } // EO parse_disjunction @@ -1198,6 +1215,7 @@ namespace Sass { // if it's a singleton, return it directly if (operands.size() == 0) return rel; // fold all operands into one binary expression + rel->set_pstate_offset(pstate - rel->pstate()); return fold_operands(rel, operands, { Sass_OP::AND }); } // EO parse_conjunction @@ -1242,6 +1260,7 @@ 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 ... + lhs->set_pstate_offset(pstate - lhs->pstate()); return fold_operands(lhs, operands, operators); } // parse_relation @@ -1527,6 +1546,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 +2006,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 +2035,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 +2645,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