Skip to content

Commit 5a037bb

Browse files
committed
Merge pull request #1010 from mgreter/bugfix/issue_674
Enable url function overloading
2 parents 70992f4 + fd1814c commit 5a037bb

File tree

8 files changed

+126
-95
lines changed

8 files changed

+126
-95
lines changed

constants.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ namespace Sass {
123123
extern const char sign_chars[] = "-+";
124124
extern const char hyphen[] = "-";
125125
extern const char ellipsis[] = "...";
126-
extern const char url_space_chars[] = " \t\r\n\f";
126+
// extern const char url_space_chars[] = " \t\r\n\f";
127127
extern const char escape_chars[] = " -~"; // need to include unicode spaces too
128128
// type names
129129
extern const char numeric_name[] = "numeric value";
@@ -137,6 +137,9 @@ namespace Sass {
137137
extern const char map_name[] = "map";
138138
extern const char arglist_name[] = "arglist";
139139

140+
// constants for uri parsing (RFC 3986 Appendix A.)
141+
extern const char uri_chars[] = ":/?!$%&#@[]{}'\"*+-._=";
142+
140143
// some specific constant character classes
141144
// they must be static to be useable by lexer
142145
extern const char static_ops[] = "*/%";

constants.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ namespace Sass {
124124
extern const char sign_chars[];
125125
extern const char hyphen[];
126126
extern const char ellipsis[];
127-
extern const char url_space_chars[];
127+
// extern const char url_space_chars[];
128128
extern const char escape_chars[];
129129

130130
// type names
@@ -139,6 +139,9 @@ namespace Sass {
139139
extern const char map_name[];
140140
extern const char arglist_name[];
141141

142+
// constants for uri parsing (RFC 3986 Appendix A.)
143+
extern const char uri_chars[];
144+
142145
// some specific constant character classes
143146
// they must be static to be useable by lexer
144147
extern const char static_ops[];

lexer.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ namespace Sass {
1111

1212
namespace Prelexer {
1313

14+
//####################################
15+
// BASIC CHARACTER MATCHERS
16+
//####################################
17+
18+
// Match standard control chars
19+
const char* kwd_at(const char* src) { return exactly<'@'>(src); }
20+
const char* kwd_dot(const char* src) { return exactly<'.'>(src); }
21+
const char* kwd_comma(const char* src) { return exactly<','>(src); };
22+
const char* kwd_colon(const char* src) { return exactly<':'>(src); };
23+
const char* kwd_star(const char* src) { return exactly<'*'>(src); };
24+
const char* kwd_plus(const char* src) { return exactly<'+'>(src); };
25+
const char* kwd_minus(const char* src) { return exactly<'-'>(src); };
26+
const char* kwd_slash(const char* src) { return exactly<'/'>(src); };
27+
1428
//####################################
1529
// implement some function that do exist in the standard
1630
// but those are locale aware which brought some trouble
@@ -69,7 +83,7 @@ namespace Sass {
6983
}
7084

7185
//####################################
72-
// BASIC CHARACTER MATCHERS
86+
// BASIC CLASS MATCHERS
7387
//####################################
7488

7589
// create matchers that advance the position

lexer.hpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ namespace Sass {
1010
// BASIC CHARACTER MATCHERS
1111
//####################################
1212

13+
// Match standard control chars
14+
const char* kwd_at(const char* src);
15+
const char* kwd_dot(const char* src);
16+
const char* kwd_comma(const char* src);
17+
const char* kwd_colon(const char* src);
18+
const char* kwd_star(const char* src);
19+
const char* kwd_plus(const char* src);
20+
const char* kwd_minus(const char* src);
21+
const char* kwd_slash(const char* src);
22+
23+
//####################################
24+
// BASIC CLASS MATCHERS
25+
//####################################
26+
1327
// These are locale independant
1428
const bool is_space(const char& src);
1529
const bool is_alpha(const char& src);
@@ -120,10 +134,10 @@ namespace Sass {
120134
// Aka. zero-width positive lookahead.
121135
// Regex equivalent: /(?=literal)/
122136
// just hangs around until we need it
123-
// template <prelexer mx>
124-
// const char* lookahead(const char* src) {
125-
// return mx(src) ? src : 0;
126-
// }
137+
template <prelexer mx>
138+
const char* lookahead(const char* src) {
139+
return mx(src) ? src : 0;
140+
}
127141

128142
// Tries supplied matchers in order.
129143
// Succeeds if one of them succeeds.

parser.cpp

Lines changed: 45 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,18 @@ namespace Sass {
266266
import_single_file(imp, lexed);
267267
}
268268
}
269-
else if (peek< uri_prefix >()) {
270-
imp->urls().push_back(parse_value());
269+
else if (lex< uri_prefix >()) {
270+
Arguments* args = new (ctx.mem) Arguments(pstate);
271+
Function_Call* result = new (ctx.mem) Function_Call(pstate, "url", args);
272+
if (lex < uri_value >()) { // chunk seems to work too!
273+
String* the_url = parse_interpolated_chunk(lexed);
274+
*args << new (ctx.mem) Argument(the_url->pstate(), the_url);
275+
}
276+
else {
277+
error("malformed URL", pstate);
278+
}
279+
if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);
280+
imp->urls().push_back(result);
271281
}
272282
else {
273283
if (first) error("@import directive requires a url or quoted path", pstate);
@@ -301,16 +311,16 @@ namespace Sass {
301311

302312
Parameters* Parser::parse_parameters()
303313
{
304-
string name(lexed); // for the error message
314+
string name(lexed);
315+
Position position = after_token;
305316
Parameters* params = new (ctx.mem) Parameters(pstate);
306-
if (lex< exactly<'('> >()) {
317+
if (lex_css< exactly<'('> >()) {
307318
// if there's anything there at all
308-
if (!peek< exactly<')'> >()) {
319+
if (!peek_css< exactly<')'> >()) {
309320
do (*params) << parse_parameter();
310321
while (lex_css< exactly<','> >());
311322
}
312-
while (lex< alternatives < spaces, block_comment > >()) {};
313-
if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, pstate);
323+
if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
314324
}
315325
return params;
316326
}
@@ -351,34 +361,36 @@ namespace Sass {
351361
return the_call;
352362
}
353363

354-
Arguments* Parser::parse_arguments()
364+
Arguments* Parser::parse_arguments(bool has_url)
355365
{
356366
string name(lexed);
367+
Position position = after_token;
357368
Arguments* args = new (ctx.mem) Arguments(pstate);
358-
359-
if (lex< exactly<'('> >()) {
369+
if (lex_css< exactly<'('> >()) {
360370
// if there's anything there at all
361-
if (!peek< exactly<')'> >()) {
362-
do (*args) << parse_argument();
371+
if (!peek_css< exactly<')'> >()) {
372+
do (*args) << parse_argument(has_url);
363373
while (lex_css< exactly<','> >());
364374
}
365-
while (lex< block_comment >());
366-
if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, pstate);
375+
if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
367376
}
368-
369377
return args;
370378
}
371379

372-
Argument* Parser::parse_argument()
380+
Argument* Parser::parse_argument(bool has_url)
373381
{
382+
374383
Argument* arg;
375-
while (lex< alternatives < spaces, block_comment > >());
376-
if (peek< sequence < variable, zero_plus < alternatives < spaces, line_comment, block_comment > >, exactly<':'> > >()) {
377-
lex< variable >();
384+
// some urls can look like line comments (parse literally - chunk would not work)
385+
if (has_url && lex< sequence < uri_value, lookahead < exactly<')'> > > >(false)) {
386+
String* the_url = parse_interpolated_chunk(lexed);
387+
arg = new (ctx.mem) Argument(the_url->pstate(), the_url);
388+
}
389+
else if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {
390+
lex_css< variable >();
378391
string name(Util::normalize_underscores(lexed));
379392
ParserState p = pstate;
380-
while (lex< alternatives < spaces, block_comment > >()) {};
381-
lex< exactly<':'> >();
393+
lex_css< exactly<':'> >();
382394
Expression* val = parse_space_list();
383395
val->is_delayed(false);
384396
arg = new (ctx.mem) Argument(p, val, name);
@@ -388,7 +400,7 @@ namespace Sass {
388400
bool is_keyword = false;
389401
Expression* val = parse_space_list();
390402
val->is_delayed(false);
391-
if (lex< exactly< ellipsis > >()) {
403+
if (lex_css< exactly< ellipsis > >()) {
392404
if (val->concrete_type() == Expression::MAP) is_keyword = true;
393405
else is_arglist = true;
394406
}
@@ -1118,10 +1130,10 @@ namespace Sass {
11181130
{
11191131
Expression* conj1 = parse_conjunction();
11201132
// if it's a singleton, return it directly; don't wrap it
1121-
if (!peek< sequence< kwd_or, negate< identifier > > >()) return conj1;
1133+
if (!peek_css< kwd_or >()) return conj1;
11221134

11231135
vector<Expression*> operands;
1124-
while (lex< sequence< kwd_or, negate< identifier > > >())
1136+
while (lex_css< kwd_or >())
11251137
operands.push_back(parse_conjunction());
11261138

11271139
return fold_operands(conj1, operands, Binary_Expression::OR);
@@ -1131,10 +1143,10 @@ namespace Sass {
11311143
{
11321144
Expression* rel1 = parse_relation();
11331145
// if it's a singleton, return it directly; don't wrap it
1134-
if (!peek< sequence< kwd_and, negate< identifier > > >()) return rel1;
1146+
if (!peek_css< kwd_and >()) return rel1;
11351147

11361148
vector<Expression*> operands;
1137-
while (lex< sequence< kwd_and, negate< identifier > > >())
1149+
while (lex_css< kwd_and >())
11381150
operands.push_back(parse_relation());
11391151

11401152
return fold_operands(rel1, operands, Binary_Expression::AND);
@@ -1249,7 +1261,7 @@ namespace Sass {
12491261
else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {
12501262
return parse_identifier_schema();
12511263
}
1252-
else if (peek< functional >() && !peek< uri_prefix >()) {
1264+
else if (peek< functional >()) {
12531265
return parse_function_call();
12541266
}
12551267
else if (lex< sequence< exactly<'+'>, optional_css_whitespace, negate< number > > >()) {
@@ -1272,45 +1284,7 @@ namespace Sass {
12721284

12731285
Expression* Parser::parse_value()
12741286
{
1275-
while (lex< block_comment >());
1276-
if (lex< uri_prefix >()) {
1277-
Arguments* args = new (ctx.mem) Arguments(pstate);
1278-
Function_Call* result = new (ctx.mem) Function_Call(pstate, "url", args);
1279-
const char* here = position;
1280-
Position here_p = before_token;
1281-
// Try to parse a SassScript expression. If it succeeds and we can munch
1282-
// a matching rparen, then that's our url. If we can't munch a matching
1283-
// rparen, or if the attempt to parse an expression fails, then try to
1284-
// munch a regular CSS url.
1285-
try {
1286-
// special case -- if there's a comment, treat it as part of a URL
1287-
lex<spaces>();
1288-
if (peek<line_comment>() || peek<block_comment_prefix>()) error("comment in URL", pstate); // doesn't really matter what we throw
1289-
Expression* expr = parse_list();
1290-
if (!lex< exactly<')'> >()) error("dangling expression in URL", pstate); // doesn't really matter what we throw
1291-
Argument* arg = new (ctx.mem) Argument(expr->pstate(), expr);
1292-
*args << arg;
1293-
return result;
1294-
}
1295-
catch (Sass_Error&) {
1296-
// back up so we can try again
1297-
position = here;
1298-
before_token = here_p;
1299-
}
1300-
catch (...) { throw; }
1301-
lex< spaces >();
1302-
if (lex< url >()) {
1303-
String* the_url = parse_interpolated_chunk(lexed);
1304-
Argument* arg = new (ctx.mem) Argument(the_url->pstate(), the_url);
1305-
*args << arg;
1306-
}
1307-
else {
1308-
error("malformed URL", pstate);
1309-
}
1310-
if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);
1311-
return result;
1312-
}
1313-
1287+
lex< css_comments >();
13141288
if (lex< ampersand >())
13151289
{
13161290
return new (ctx.mem) Parent_Selector(pstate, parse_selector_group()); }
@@ -1322,13 +1296,13 @@ namespace Sass {
13221296
if ((stop = peek< value_schema >()))
13231297
{ return parse_value_schema(stop); }
13241298

1325-
if (lex< sequence< kwd_true, negate< identifier > > >())
1299+
if (lex< kwd_true >())
13261300
{ return new (ctx.mem) Boolean(pstate, true); }
13271301

1328-
if (lex< sequence< kwd_false, negate< identifier > > >())
1302+
if (lex< kwd_false >())
13291303
{ return new (ctx.mem) Boolean(pstate, false); }
13301304

1331-
if (lex< sequence< kwd_null, negate< identifier > > >())
1305+
if (lex< kwd_null >())
13321306
{ return new (ctx.mem) Null(pstate); }
13331307

13341308
if (lex< identifier >()) {
@@ -1634,10 +1608,8 @@ namespace Sass {
16341608
{
16351609
lex< identifier >();
16361610
string name(lexed);
1637-
ParserState source_position_of_call = pstate;
1638-
1639-
Function_Call* the_call = new (ctx.mem) Function_Call(source_position_of_call, name, parse_arguments());
1640-
return the_call;
1611+
Arguments* args = parse_arguments(name == "url");
1612+
return new (ctx.mem) Function_Call(pstate, name, args);
16411613
}
16421614

16431615
Function_Call_Schema* Parser::parse_function_call_schema()

parser.hpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ namespace Sass {
8282
const char* it_position = start ? start : position;
8383

8484
// skip white-space?
85-
if (mx == url ||
86-
mx == spaces ||
85+
if (mx == spaces ||
8786
mx == no_spaces ||
8887
mx == css_comments ||
8988
mx == css_whitespace ||
@@ -123,12 +122,17 @@ namespace Sass {
123122
// sourcemap offset and we modify the position pointer!
124123
// lex will only skip over space, tabs and line comment
125124
template <prelexer mx>
126-
const char* lex()
125+
const char* lex(bool lazy = true)
127126
{
128127

128+
// position considered before lexed token
129+
// we can skip whitespace or comments for
130+
// lazy developers (but we need control)
131+
const char* it_before_token = position;
132+
129133
// sneak up to the actual token we want to lex
130134
// this should skip over white-space if desired
131-
const char* it_before_token = sneak < mx >(position);
135+
if (lazy) it_before_token = sneak < mx >(position);
132136

133137
// now call matcher to get position after token
134138
const char* it_after_token = mx(it_before_token);
@@ -196,8 +200,8 @@ namespace Sass {
196200
Parameters* parse_parameters();
197201
Parameter* parse_parameter();
198202
Mixin_Call* parse_mixin_call();
199-
Arguments* parse_arguments();
200-
Argument* parse_argument();
203+
Arguments* parse_arguments(bool has_url = false);
204+
Argument* parse_argument(bool has_url = false);
201205
Assignment* parse_assignment();
202206
// Propset* parse_propset();
203207
Ruleset* parse_ruleset(Selector_Lookahead lookahead);

0 commit comments

Comments
 (0)