Skip to content

Commit 8dd7ecc

Browse files
committed
Improve block_comment parsing
Start to use `lex_css` and `peek_css` to indicate if you want to skip over `block_comments` or only "white-space". "white-space" in this case also includes `line_comments`! Fixes #941
1 parent b7ced5d commit 8dd7ecc

File tree

4 files changed

+54
-45
lines changed

4 files changed

+54
-45
lines changed

parser.cpp

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ namespace Sass {
253253
else error("expecting another url or quoted path in @import list", pstate);
254254
}
255255
first = false;
256-
} while (lex< exactly<','> >());
256+
} while (lex_css< exactly<','> >());
257257
return imp;
258258
}
259259

@@ -286,7 +286,7 @@ namespace Sass {
286286
// if there's anything there at all
287287
if (!peek< exactly<')'> >()) {
288288
do (*params) << parse_parameter();
289-
while (lex< alternatives < spaces,block_comment, exactly<','> > >());
289+
while (lex_css< exactly<','> >());
290290
}
291291
while (lex< alternatives < spaces, block_comment > >()) {};
292292
if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, pstate);
@@ -339,7 +339,7 @@ namespace Sass {
339339
// if there's anything there at all
340340
if (!peek< exactly<')'> >()) {
341341
do (*args) << parse_argument();
342-
while (lex< alternatives < block_comment, exactly<','> > >());
342+
while (lex_css< exactly<','> >());
343343
}
344344
while (lex< block_comment >());
345345
if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, pstate);
@@ -505,7 +505,7 @@ namespace Sass {
505505
}
506506
if (peek_newline()) ref_wrap->has_line_break(true);
507507
}
508-
while (peek< sequence < optional_css_whitespace, optional < block_comment >, exactly<','> > >())
508+
while (peek_css< exactly<','> >())
509509
{
510510
// consume everything up and including the comma speparator
511511
reloop = lex< sequence < optional_css_comments, exactly<','> > >() != 0;
@@ -528,7 +528,7 @@ namespace Sass {
528528
{
529529
Position sel_source_position(-1);
530530
Compound_Selector* lhs;
531-
if (peek< alternatives <
531+
if (peek_css< alternatives <
532532
exactly<'+'>,
533533
exactly<'~'>,
534534
exactly<'>'>
@@ -549,7 +549,7 @@ namespace Sass {
549549
bool cpx_lf = peek_newline();
550550

551551
Complex_Selector* rhs;
552-
if (peek< alternatives <
552+
if (peek_css< alternatives <
553553
exactly<','>,
554554
exactly<')'>,
555555
exactly<'{'>,
@@ -589,10 +589,10 @@ namespace Sass {
589589
return seq;
590590
}
591591
}
592-
if (sawsomething && lex< sequence< negate< functional >, alternatives< identifier_fragment, universal, quoted_string, dimension, percentage, number > > >()) {
592+
if (sawsomething && lex_css< sequence< negate< functional >, alternatives< identifier_fragment, universal, quoted_string, dimension, percentage, number > > >()) {
593593
// saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning
594594
(*seq) << new (ctx.mem) Type_Selector(pstate, unquote(lexed));
595-
} else if (lex< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) {
595+
} else if (lex_css< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) {
596596
// if you see a type selector
597597
(*seq) << new (ctx.mem) Type_Selector(pstate, lexed);
598598
sawsomething = true;
@@ -603,14 +603,15 @@ namespace Sass {
603603
}
604604

605605
while (!peek< spaces >(position) &&
606-
!(peek < alternatives < exactly<'+'>,
607-
exactly<'~'>,
608-
exactly<'>'>,
609-
exactly<','>,
610-
exactly<')'>,
611-
exactly<'{'>,
612-
exactly<'}'>,
613-
exactly<';'>
606+
!(peek_css < alternatives <
607+
exactly<'+'>,
608+
exactly<'~'>,
609+
exactly<'>'>,
610+
exactly<','>,
611+
exactly<')'>,
612+
exactly<'{'>,
613+
exactly<'}'>,
614+
exactly<';'>
614615
> >(position))) {
615616
(*seq) << parse_simple_selector();
616617
}
@@ -619,6 +620,7 @@ namespace Sass {
619620

620621
Simple_Selector* Parser::parse_simple_selector()
621622
{
623+
lex < css_comments >();
622624
if (lex< id_name >() || lex< class_name >()) {
623625
return new (ctx.mem) Selector_Qualifier(pstate, unquote(lexed));
624626
}
@@ -725,29 +727,29 @@ namespace Sass {
725727

726728
Attribute_Selector* Parser::parse_attribute_selector()
727729
{
728-
lex< exactly<'['> >();
730+
lex_css< exactly<'['> >();
729731
ParserState p = pstate;
730-
if (!lex< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
732+
if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
731733
string name(lexed);
732-
if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0);
733-
if (!lex< alternatives< exact_match, class_match, dash_match,
734-
prefix_match, suffix_match, substring_match > >()) {
734+
if (lex_css< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0);
735+
if (!lex_css< alternatives< exact_match, class_match, dash_match,
736+
prefix_match, suffix_match, substring_match > >()) {
735737
error("invalid operator in attribute selector for " + name, pstate);
736738
}
737739
string matcher(lexed);
738740

739741
String* value = 0;
740-
if (lex< identifier >()) {
742+
if (lex_css< identifier >()) {
741743
value = new (ctx.mem) String_Constant(p, lexed);
742744
}
743-
else if (lex< quoted_string >()) {
745+
else if (lex_css< quoted_string >()) {
744746
value = parse_interpolated_chunk(lexed, true); // needed!
745747
}
746748
else {
747749
error("expected a string constant or identifier in attribute selector for " + name, pstate);
748750
}
749751

750-
if (!lex< exactly<']'> >()) error("unterminated attribute selector for " + name, pstate);
752+
if (!lex_css< exactly<']'> >()) error("unterminated attribute selector for " + name, pstate);
751753
return new (ctx.mem) Attribute_Selector(p, name, matcher, value);
752754
}
753755

@@ -979,10 +981,10 @@ namespace Sass {
979981
Map* map = new (ctx.mem) Map(pstate, 1);
980982
(*map) << make_pair(key, value);
981983

982-
while (lex< exactly<','> >())
984+
while (lex_css< exactly<','> >())
983985
{
984986
// allow trailing commas - #495
985-
if (peek< exactly<')'> >(position))
987+
if (peek_css< exactly<')'> >(position))
986988
{ break; }
987989

988990
Expression* key = parse_list();

parser.hpp

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ namespace Sass {
7676
{
7777

7878
// maybe use optional start position from arguments?
79-
const char* it_before_token = start ? start : position;
79+
const char* it_position = start ? start : position;
8080

8181
// skip white-space?
8282
if (mx == url ||
@@ -88,11 +88,13 @@ namespace Sass {
8888
mx == optional_css_comments ||
8989
mx == optional_css_whitespace
9090
) {
91-
return it_before_token;
91+
return it_position;
9292
}
9393

9494
// skip over spaces, tabs and sass line comments
95-
return optional_css_whitespace(it_before_token);
95+
const char* pos = optional_css_whitespace(it_position);
96+
// always return a valid position
97+
return pos ? pos : it_position;
9698

9799
}
98100

@@ -112,33 +114,34 @@ namespace Sass {
112114
// white-space handling is built into the lexer
113115
// this way you do not need to parse it yourself
114116
// some matchers don't accept certain white-space
117+
// we do not support start arg, since we manipulate
118+
// sourcemap offset and we modify the position pointer!
115119
template <prelexer mx>
116120
const char* lex()
117121
{
118122

119-
// remeber interesting position
120-
const char* wspace_start = position;
121-
122123
// sneak up to the actual token we want to lex
123124
// this should skip over white-space if desired
124125
const char* it_before_token = sneak < mx >(position);
125126

126-
// advance position (add whitespace before current token)
127-
before_token = after_token.inc(position, it_before_token);
128-
129127
// now call matcher to get position after token
130128
const char* it_after_token = mx(it_before_token);
131129

132130
// assertion that we got a valid match
133131
if (it_after_token == 0) return 0;
132+
// assertion that we actually lexed something
133+
if (it_after_token == it_before_token) return 0;
134134

135135
// create new lexed token object (holds all parse result information)
136-
lexed = Token(wspace_start, it_before_token, it_after_token);
136+
lexed = Token(position, it_before_token, it_after_token);
137+
138+
// advance position (add whitespace before current token)
139+
before_token = after_token.add(position, it_before_token);
137140

138-
// update position of after_token (add token to before position)
139-
after_token = before_token.inc(it_before_token, it_after_token);
141+
// update after_token position for current token
142+
after_token.add(it_before_token, it_after_token);
140143

141-
// ToDo: could probably do this incremetal on original object
144+
// ToDo: could probably do this incremetal on original object (API wants offset?)
142145
pstate = ParserState(path, source, lexed, before_token, after_token - before_token);
143146

144147
// advance internal char iterator
@@ -151,17 +154,18 @@ namespace Sass {
151154
const char* lex_css()
152155
{
153156
// throw away comments
157+
// update srcmap position
154158
lex < css_comments >();
155159
// now lex a token
156160
return lex< mx >();
157161
}
158162

159163
// skips over css comments
160164
template <prelexer mx>
161-
const char* peek_css()
165+
const char* peek_css(const char* start = 0)
162166
{
163167
// now peek a token (skip comments first)
164-
return peek< mx >(peek < css_comments >());
168+
return peek< mx >(peek < css_comments >(start));
165169
}
166170

167171
#ifdef __clang__

position.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ namespace Sass {
3333
// increase offset by given string (mostly called by lexer)
3434
// increase line counter and count columns on the last line
3535
// ToDo: make the col count utf8 aware
36-
void Offset::add(const char* begin, const char* end)
36+
Offset Offset::add(const char* begin, const char* end)
3737
{
38+
if (end == 0) return *this;
3839
while (begin < end && *begin) {
3940
if (*begin == '\n') {
4041
++ line;
@@ -45,6 +46,7 @@ namespace Sass {
4546
}
4647
++begin;
4748
}
49+
return *this;
4850
}
4951

5052
// increase offset by given string (mostly called by lexer)
@@ -103,9 +105,10 @@ namespace Sass {
103105
ParserState::ParserState(string path, const char* src, Token token, Position position, Offset offset)
104106
: Position(position), path(path), src(src), offset(offset), token(token) { }
105107

106-
void Position::add(const char* begin, const char* end)
108+
Position Position::add(const char* begin, const char* end)
107109
{
108110
Offset::add(begin, end);
111+
return *this;
109112
}
110113

111114
Position Position::inc(const char* begin, const char* end) const

position.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Sass {
1919
Offset(const size_t line, const size_t column);
2020

2121
// return new position, incremented by the given string
22-
void add(const char* begin, const char* end);
22+
Offset add(const char* begin, const char* end);
2323
Offset inc(const char* begin, const char* end) const;
2424

2525
// init/create instance from const char substring
@@ -59,7 +59,7 @@ namespace Sass {
5959
const Position operator+ (const Offset &off) const;
6060
const Offset operator- (const Offset &off) const;
6161
// return new position, incremented by the given string
62-
void add(const char* begin, const char* end);
62+
Position add(const char* begin, const char* end);
6363
Position inc(const char* begin, const char* end) const;
6464

6565
public: // overload output stream operator

0 commit comments

Comments
 (0)