sassc 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/libsass/ast.cpp +124 -43
- data/ext/libsass/ast.hpp +15 -7
- data/ext/libsass/bind.cpp +57 -10
- data/ext/libsass/context.cpp +9 -7
- data/ext/libsass/contextualize.cpp +7 -0
- data/ext/libsass/contextualize_eval.cpp +1 -1
- data/ext/libsass/debugger.hpp +27 -70
- data/ext/libsass/emitter.cpp +5 -2
- data/ext/libsass/emitter.hpp +7 -1
- data/ext/libsass/eval.cpp +27 -13
- data/ext/libsass/expand.cpp +2 -2
- data/ext/libsass/functions.cpp +34 -9
- data/ext/libsass/functions.hpp +2 -0
- data/ext/libsass/inspect.cpp +111 -39
- data/ext/libsass/lexer.cpp +8 -8
- data/ext/libsass/lexer.hpp +8 -8
- data/ext/libsass/output.cpp +13 -5
- data/ext/libsass/parser.cpp +185 -46
- data/ext/libsass/parser.hpp +4 -0
- data/ext/libsass/prelexer.cpp +58 -7
- data/ext/libsass/prelexer.hpp +20 -2
- data/ext/libsass/sass_values.cpp +1 -1
- data/ext/libsass/sass_values.h +1 -1
- data/ext/libsass/to_string.cpp +3 -3
- data/ext/libsass/to_string.hpp +2 -1
- data/ext/libsass/units.cpp +134 -34
- data/ext/libsass/units.hpp +78 -4
- data/ext/libsass/util.cpp +8 -10
- data/ext/libsass/util.hpp +1 -1
- data/lib/sassc/engine.rb +7 -3
- data/lib/sassc/version.rb +1 -1
- data/test/native_test.rb +1 -1
- data/test/output_style_test.rb +7 -0
- metadata +2 -2
data/ext/libsass/functions.hpp
CHANGED
@@ -101,6 +101,7 @@ namespace Sass {
|
|
101
101
|
extern Signature keywords_sig;
|
102
102
|
extern Signature set_nth_sig;
|
103
103
|
extern Signature unique_id_sig;
|
104
|
+
extern Signature is_superselector_sig;
|
104
105
|
|
105
106
|
BUILT_IN(rgb);
|
106
107
|
BUILT_IN(rgba_4);
|
@@ -175,6 +176,7 @@ namespace Sass {
|
|
175
176
|
BUILT_IN(keywords);
|
176
177
|
BUILT_IN(set_nth);
|
177
178
|
BUILT_IN(unique_id);
|
179
|
+
BUILT_IN(is_superselector);
|
178
180
|
|
179
181
|
}
|
180
182
|
}
|
data/ext/libsass/inspect.cpp
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
#include "inspect.hpp"
|
2
|
-
#include "ast.hpp"
|
3
|
-
#include "context.hpp"
|
4
|
-
#include "utf8/checked.h"
|
5
1
|
#include <cmath>
|
6
2
|
#include <string>
|
7
3
|
#include <iostream>
|
@@ -9,6 +5,11 @@
|
|
9
5
|
#include <stdint.h>
|
10
6
|
#include <stdint.h>
|
11
7
|
|
8
|
+
#include "ast.hpp"
|
9
|
+
#include "inspect.hpp"
|
10
|
+
#include "context.hpp"
|
11
|
+
#include "utf8/checked.h"
|
12
|
+
|
12
13
|
namespace Sass {
|
13
14
|
using namespace std;
|
14
15
|
|
@@ -99,9 +100,10 @@ namespace Sass {
|
|
99
100
|
append_token(at_rule->keyword(), at_rule);
|
100
101
|
if (at_rule->selector()) {
|
101
102
|
append_mandatory_space();
|
103
|
+
bool was_wrapped = in_wrapped;
|
102
104
|
in_wrapped = true;
|
103
105
|
at_rule->selector()->perform(this);
|
104
|
-
in_wrapped =
|
106
|
+
in_wrapped = was_wrapped;
|
105
107
|
}
|
106
108
|
if (at_rule->block()) {
|
107
109
|
at_rule->block()->perform(this);
|
@@ -114,6 +116,7 @@ namespace Sass {
|
|
114
116
|
void Inspect::operator()(Declaration* dec)
|
115
117
|
{
|
116
118
|
if (dec->value()->concrete_type() == Expression::NULL_VAL) return;
|
119
|
+
bool was_decl = in_declaration;
|
117
120
|
in_declaration = true;
|
118
121
|
if (output_style() == NESTED)
|
119
122
|
indentation += dec->tabs();
|
@@ -128,7 +131,7 @@ namespace Sass {
|
|
128
131
|
append_delimiter();
|
129
132
|
if (output_style() == NESTED)
|
130
133
|
indentation -= dec->tabs();
|
131
|
-
in_declaration =
|
134
|
+
in_declaration = was_decl;
|
132
135
|
}
|
133
136
|
|
134
137
|
void Inspect::operator()(Assignment* assn)
|
@@ -346,8 +349,20 @@ namespace Sass {
|
|
346
349
|
else if (in_media_block && sep != " ") sep += " "; // verified
|
347
350
|
if (list->empty()) return;
|
348
351
|
bool items_output = false;
|
349
|
-
|
350
|
-
|
352
|
+
|
353
|
+
bool was_space_array = in_space_array;
|
354
|
+
bool was_comma_array = in_comma_array;
|
355
|
+
if (!in_declaration && (
|
356
|
+
(list->separator() == List::SPACE && in_space_array) ||
|
357
|
+
(list->separator() == List::COMMA && in_comma_array)
|
358
|
+
)) {
|
359
|
+
append_string("(");
|
360
|
+
}
|
361
|
+
|
362
|
+
if (list->separator() == List::SPACE) in_space_array = true;
|
363
|
+
else if (list->separator() == List::COMMA) in_comma_array = true;
|
364
|
+
|
365
|
+
for (size_t i = 0, L = list->size(); i < L; ++i) {
|
351
366
|
Expression* list_item = (*list)[i];
|
352
367
|
if (list_item->is_invisible()) {
|
353
368
|
continue;
|
@@ -360,7 +375,16 @@ namespace Sass {
|
|
360
375
|
list_item->perform(this);
|
361
376
|
items_output = true;
|
362
377
|
}
|
363
|
-
|
378
|
+
|
379
|
+
in_comma_array = was_comma_array;
|
380
|
+
in_space_array = was_space_array;
|
381
|
+
if (!in_declaration && (
|
382
|
+
(list->separator() == List::SPACE && in_space_array) ||
|
383
|
+
(list->separator() == List::COMMA && in_comma_array)
|
384
|
+
)) {
|
385
|
+
append_string(")");
|
386
|
+
}
|
387
|
+
|
364
388
|
}
|
365
389
|
|
366
390
|
void Inspect::operator()(Binary_Expression* expr)
|
@@ -416,43 +440,91 @@ namespace Sass {
|
|
416
440
|
|
417
441
|
void Inspect::operator()(Number* n)
|
418
442
|
{
|
443
|
+
|
444
|
+
string res;
|
445
|
+
|
446
|
+
// init stuff
|
419
447
|
n->normalize();
|
448
|
+
int precision = 5;
|
449
|
+
double value = n->value();
|
450
|
+
// get option from optional context
|
451
|
+
if (ctx) precision = ctx->precision;
|
452
|
+
|
453
|
+
// check if the fractional part of the value equals to zero
|
454
|
+
// neat trick from http://stackoverflow.com/a/1521682/1550314
|
455
|
+
// double int_part; bool is_int = modf(value, &int_part) == 0.0;
|
456
|
+
|
457
|
+
// this all cannot be done with one run only, since fixed
|
458
|
+
// output differs from normal output and regular output
|
459
|
+
// can contain scientific notation which we do not want!
|
460
|
+
|
461
|
+
// first sample
|
420
462
|
stringstream ss;
|
421
|
-
ss.precision(
|
422
|
-
ss <<
|
423
|
-
|
424
|
-
//
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
463
|
+
ss.precision(12);
|
464
|
+
ss << value;
|
465
|
+
|
466
|
+
// check if we got scientific notation in result
|
467
|
+
if (ss.str().find_first_of("e") != string::npos) {
|
468
|
+
ss.clear(); ss.str(string());
|
469
|
+
ss.precision(max(12, precision));
|
470
|
+
ss << fixed << value;
|
471
|
+
}
|
472
|
+
|
473
|
+
string tmp = ss.str();
|
474
|
+
size_t pos_point = tmp.find_first_of(".,");
|
475
|
+
size_t pos_fract = tmp.find_last_not_of("0");
|
476
|
+
bool is_int = pos_point == pos_fract ||
|
477
|
+
pos_point == string::npos;
|
478
|
+
|
479
|
+
// reset stream for another run
|
480
|
+
ss.clear(); ss.str(string());
|
481
|
+
|
482
|
+
// take a shortcut for integers
|
483
|
+
if (is_int)
|
484
|
+
{
|
485
|
+
ss.precision(0);
|
486
|
+
ss << fixed << value;
|
487
|
+
res = string(ss.str());
|
488
|
+
}
|
489
|
+
// process floats
|
490
|
+
else
|
491
|
+
{
|
492
|
+
// do we have have too much precision?
|
493
|
+
if (pos_fract < precision + pos_point)
|
494
|
+
{ precision = pos_fract - pos_point; }
|
495
|
+
// round value again
|
496
|
+
ss.precision(precision);
|
497
|
+
ss << fixed << value;
|
498
|
+
res = string(ss.str());
|
499
|
+
// maybe we truncated up to decimal point
|
500
|
+
size_t pos = res.find_last_not_of("0");
|
501
|
+
bool at_dec_point = res[pos] == '.' ||
|
502
|
+
res[pos] == ',';
|
503
|
+
// don't leave a blank point
|
504
|
+
if (at_dec_point) ++ pos;
|
505
|
+
res.resize (pos + 1);
|
506
|
+
}
|
507
|
+
|
508
|
+
// some final cosmetics
|
509
|
+
if (res == "-0.0") res.erase(0, 1);
|
510
|
+
else if (res == "-0") res.erase(0, 1);
|
511
|
+
|
512
|
+
// add unit now
|
513
|
+
res += n->unit();
|
514
|
+
|
515
|
+
// check for a valid unit here
|
516
|
+
// includes result for reporting
|
435
517
|
if (n->numerator_units().size() > 1 ||
|
436
518
|
n->denominator_units().size() > 0 ||
|
437
519
|
(n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != string::npos) ||
|
438
520
|
(n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != string::npos)
|
439
521
|
) {
|
440
|
-
error(
|
522
|
+
error(res + " isn't a valid CSS value.", n->pstate());
|
441
523
|
}
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
// remove the leading minus
|
447
|
-
if (d == "-0") d.erase(0, 1);
|
448
|
-
// use fractional output if we had
|
449
|
-
// a value before it got truncated
|
450
|
-
if (d == "0" && nonzero) d = "0.0";
|
451
|
-
// if the precision is 0 sass cast
|
452
|
-
// casts to a float with precision 1
|
453
|
-
if (ctx->precision == 0) d += ".0";
|
454
|
-
// append number and unit
|
455
|
-
append_token(d + n->unit(), n);
|
524
|
+
|
525
|
+
// output the final token
|
526
|
+
append_token(res, n);
|
527
|
+
|
456
528
|
}
|
457
529
|
|
458
530
|
// helper function for serializing colors
|
@@ -569,7 +641,7 @@ namespace Sass {
|
|
569
641
|
void Inspect::operator()(String_Quoted* s)
|
570
642
|
{
|
571
643
|
if (s->quote_mark()) {
|
572
|
-
append_token(quote(s->value(), s->quote_mark()), s);
|
644
|
+
append_token(quote(s->value(), s->quote_mark(), true), s);
|
573
645
|
} else {
|
574
646
|
append_token(s->value(), s);
|
575
647
|
}
|
data/ext/libsass/lexer.cpp
CHANGED
@@ -31,25 +31,25 @@ namespace Sass {
|
|
31
31
|
// this even seems to improve performance by quite a bit
|
32
32
|
//####################################
|
33
33
|
|
34
|
-
|
34
|
+
bool is_alpha(const char& chr)
|
35
35
|
{
|
36
36
|
return unsigned(chr - 'A') <= 'Z' - 'A' ||
|
37
37
|
unsigned(chr - 'a') <= 'z' - 'a';
|
38
38
|
}
|
39
39
|
|
40
|
-
|
40
|
+
bool is_space(const char& chr)
|
41
41
|
{
|
42
42
|
// adapted the technique from is_alpha
|
43
43
|
return chr == ' ' || unsigned(chr - '\t') <= '\r' - '\t';
|
44
44
|
}
|
45
45
|
|
46
|
-
|
46
|
+
bool is_digit(const char& chr)
|
47
47
|
{
|
48
48
|
// adapted the technique from is_alpha
|
49
49
|
return unsigned(chr - '0') <= '9' - '0';
|
50
50
|
}
|
51
51
|
|
52
|
-
|
52
|
+
bool is_xdigit(const char& chr)
|
53
53
|
{
|
54
54
|
// adapted the technique from is_alpha
|
55
55
|
return unsigned(chr - '0') <= '9' - '0' ||
|
@@ -57,26 +57,26 @@ namespace Sass {
|
|
57
57
|
unsigned(chr - 'A') <= 'F' - 'A';
|
58
58
|
}
|
59
59
|
|
60
|
-
|
60
|
+
bool is_punct(const char& chr)
|
61
61
|
{
|
62
62
|
// locale independent
|
63
63
|
return chr == '.';
|
64
64
|
}
|
65
65
|
|
66
|
-
|
66
|
+
bool is_alnum(const char& chr)
|
67
67
|
{
|
68
68
|
return is_alpha(chr) || is_digit(chr);
|
69
69
|
}
|
70
70
|
|
71
71
|
// check if char is outside ascii range
|
72
|
-
|
72
|
+
bool is_unicode(const char& chr)
|
73
73
|
{
|
74
74
|
// check for unicode range
|
75
75
|
return unsigned(chr) > 127;
|
76
76
|
}
|
77
77
|
|
78
78
|
// Match word character (look ahead)
|
79
|
-
|
79
|
+
bool is_character(const char& chr)
|
80
80
|
{
|
81
81
|
// valid alpha, numeric or unicode char (plus hyphen)
|
82
82
|
return is_alnum(chr) || is_unicode(chr) || chr == '-';
|
data/ext/libsass/lexer.hpp
CHANGED
@@ -25,14 +25,14 @@ namespace Sass {
|
|
25
25
|
//####################################
|
26
26
|
|
27
27
|
// These are locale independant
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
bool is_space(const char& src);
|
29
|
+
bool is_alpha(const char& src);
|
30
|
+
bool is_punct(const char& src);
|
31
|
+
bool is_digit(const char& src);
|
32
|
+
bool is_alnum(const char& src);
|
33
|
+
bool is_xdigit(const char& src);
|
34
|
+
bool is_unicode(const char& src);
|
35
|
+
bool is_character(const char& src);
|
36
36
|
|
37
37
|
// Match a single ctype predicate.
|
38
38
|
const char* space(const char* src);
|
data/ext/libsass/output.cpp
CHANGED
@@ -349,11 +349,13 @@ namespace Sass {
|
|
349
349
|
|
350
350
|
append_scope_opener();
|
351
351
|
|
352
|
+
bool format = kwd != "@font-face";;
|
353
|
+
|
352
354
|
for (size_t i = 0, L = b->length(); i < L; ++i) {
|
353
355
|
Statement* stm = (*b)[i];
|
354
356
|
if (!stm->is_hoistable()) {
|
355
357
|
stm->perform(this);
|
356
|
-
if (i < L - 1) append_special_linefeed();
|
358
|
+
if (i < L - 1 && format) append_special_linefeed();
|
357
359
|
}
|
358
360
|
}
|
359
361
|
|
@@ -361,7 +363,7 @@ namespace Sass {
|
|
361
363
|
Statement* stm = (*b)[i];
|
362
364
|
if (stm->is_hoistable()) {
|
363
365
|
stm->perform(this);
|
364
|
-
if (i < L - 1) append_special_linefeed();
|
366
|
+
if (i < L - 1 && format) append_special_linefeed();
|
365
367
|
}
|
366
368
|
}
|
367
369
|
|
@@ -383,10 +385,16 @@ namespace Sass {
|
|
383
385
|
{
|
384
386
|
if (String_Quoted* quoted = dynamic_cast<String_Quoted*>(s)) {
|
385
387
|
return Output::operator()(quoted);
|
386
|
-
} else if (!in_comment) {
|
387
|
-
append_token(string_to_output(s->value()), s);
|
388
388
|
} else {
|
389
|
-
|
389
|
+
string value(s->value());
|
390
|
+
if (s->can_compress_whitespace() && output_style() == COMPRESSED) {
|
391
|
+
value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
|
392
|
+
}
|
393
|
+
if (!in_comment) {
|
394
|
+
append_token(string_to_output(value), s);
|
395
|
+
} else {
|
396
|
+
append_token(value, s);
|
397
|
+
}
|
390
398
|
}
|
391
399
|
}
|
392
400
|
|
data/ext/libsass/parser.cpp
CHANGED
@@ -40,6 +40,14 @@ namespace Sass {
|
|
40
40
|
return p;
|
41
41
|
}
|
42
42
|
|
43
|
+
Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)
|
44
|
+
{
|
45
|
+
Parser p = Parser::from_c_str(src, ctx, pstate);
|
46
|
+
// ToDo: ruby sass errors on parent references
|
47
|
+
// ToDo: remap the source-map entries somehow
|
48
|
+
return p.parse_selector_group();
|
49
|
+
}
|
50
|
+
|
43
51
|
bool Parser::peek_newline(const char* start)
|
44
52
|
{
|
45
53
|
return peek_linefeed(start ? start : position);
|
@@ -269,10 +277,18 @@ namespace Sass {
|
|
269
277
|
else if (lex< uri_prefix >()) {
|
270
278
|
Arguments* args = new (ctx.mem) Arguments(pstate);
|
271
279
|
Function_Call* result = new (ctx.mem) Function_Call(pstate, "url", args);
|
272
|
-
if (lex
|
280
|
+
if (lex< quoted_string >()) {
|
281
|
+
Expression* the_url = parse_string();
|
282
|
+
*args << new (ctx.mem) Argument(the_url->pstate(), the_url);
|
283
|
+
}
|
284
|
+
else if (lex < uri_value >(position)) { // chunk seems to work too!
|
273
285
|
String* the_url = parse_interpolated_chunk(lexed);
|
274
286
|
*args << new (ctx.mem) Argument(the_url->pstate(), the_url);
|
275
287
|
}
|
288
|
+
else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {
|
289
|
+
Expression* the_url = parse_list(); // parse_interpolated_chunk(lexed);
|
290
|
+
*args << new (ctx.mem) Argument(the_url->pstate(), the_url);
|
291
|
+
}
|
276
292
|
else {
|
277
293
|
error("malformed URL", pstate);
|
278
294
|
}
|
@@ -415,7 +431,13 @@ namespace Sass {
|
|
415
431
|
string name(Util::normalize_underscores(lexed));
|
416
432
|
ParserState var_source_position = pstate;
|
417
433
|
if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);
|
418
|
-
Expression* val
|
434
|
+
Expression* val;
|
435
|
+
Selector_Lookahead lookahead = lookahead_for_value(position);
|
436
|
+
if (lookahead.has_interpolants && lookahead.found) {
|
437
|
+
val = parse_value_schema(lookahead.found);
|
438
|
+
} else {
|
439
|
+
val = parse_list();
|
440
|
+
}
|
419
441
|
val->is_delayed(false);
|
420
442
|
bool is_default = false;
|
421
443
|
bool is_global = false;
|
@@ -482,6 +504,9 @@ namespace Sass {
|
|
482
504
|
// accumulate the preceding segment if the position has advanced
|
483
505
|
if (i < p) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p));
|
484
506
|
// skip to the delimiter by skipping occurences in quoted strings
|
507
|
+
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
508
|
+
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
509
|
+
}
|
485
510
|
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);
|
486
511
|
Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();
|
487
512
|
interpolant->is_interpolant(true);
|
@@ -707,18 +732,9 @@ namespace Sass {
|
|
707
732
|
if (lex< alternatives< even, odd > >()) {
|
708
733
|
expr = new (ctx.mem) String_Quoted(p, lexed);
|
709
734
|
}
|
710
|
-
else if (
|
711
|
-
|
712
|
-
String_Constant*
|
713
|
-
lex< sign >();
|
714
|
-
String_Constant* op = new (ctx.mem) String_Quoted(p, lexed);
|
715
|
-
// Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
|
716
|
-
lex< one_plus < digit > >();
|
717
|
-
String_Constant* constant = new (ctx.mem) String_Quoted(p, lexed);
|
718
|
-
// expr = new (ctx.mem) Binary_Expression(p, op, var_coef, constant);
|
719
|
-
String_Schema* schema = new (ctx.mem) String_Schema(p, 3);
|
720
|
-
*schema << var_coef << op << constant;
|
721
|
-
expr = schema;
|
735
|
+
else if (lex< binomial >(position)) {
|
736
|
+
expr = new (ctx.mem) String_Constant(p, lexed);
|
737
|
+
((String_Constant*)expr)->can_compress_whitespace(true);
|
722
738
|
}
|
723
739
|
else if (peek< sequence< optional<sign>,
|
724
740
|
zero_plus<digit>,
|
@@ -980,13 +996,25 @@ namespace Sass {
|
|
980
996
|
return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex<important>()*/);
|
981
997
|
}
|
982
998
|
else {
|
983
|
-
Expression*
|
984
|
-
|
985
|
-
|
986
|
-
|
999
|
+
Expression* value;
|
1000
|
+
Selector_Lookahead lookahead = lookahead_for_value(position);
|
1001
|
+
if (lookahead.found) {
|
1002
|
+
if (lookahead.has_interpolants) {
|
1003
|
+
value = parse_value_schema(lookahead.found);
|
1004
|
+
} else {
|
1005
|
+
value = parse_list();
|
1006
|
+
}
|
1007
|
+
}
|
1008
|
+
else {
|
1009
|
+
value = parse_list();
|
1010
|
+
if (List* list = dynamic_cast<List*>(value)) {
|
1011
|
+
if (list->length() == 0 && !peek< exactly <'{'> >()) {
|
1012
|
+
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1013
|
+
}
|
987
1014
|
}
|
988
1015
|
}
|
989
|
-
|
1016
|
+
|
1017
|
+
return new (ctx.mem) Declaration(prop->pstate(), prop, value/*, lex<important>()*/);
|
990
1018
|
}
|
991
1019
|
}
|
992
1020
|
|
@@ -1008,8 +1036,18 @@ namespace Sass {
|
|
1008
1036
|
|
1009
1037
|
Expression* Parser::parse_map()
|
1010
1038
|
{
|
1011
|
-
|
1039
|
+
ParserState opstate = pstate;
|
1012
1040
|
Expression* key = parse_list();
|
1041
|
+
if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {
|
1042
|
+
if (!str->quote_mark() && !str->is_delayed()) {
|
1043
|
+
if (ctx.names_to_colors.count(str->value())) {
|
1044
|
+
Color* c = new (ctx.mem) Color(*ctx.names_to_colors[str->value()]);
|
1045
|
+
c->pstate(str->pstate());
|
1046
|
+
c->disp(str->value());
|
1047
|
+
key = c;
|
1048
|
+
}
|
1049
|
+
}
|
1050
|
+
}
|
1013
1051
|
|
1014
1052
|
// it's not a map so return the lexed value as a list value
|
1015
1053
|
if (!peek< exactly<':'> >())
|
@@ -1019,7 +1057,7 @@ namespace Sass {
|
|
1019
1057
|
|
1020
1058
|
Expression* value = parse_space_list();
|
1021
1059
|
|
1022
|
-
Map* map = new (ctx.mem) Map(
|
1060
|
+
Map* map = new (ctx.mem) Map(opstate, 1);
|
1023
1061
|
(*map) << make_pair(key, value);
|
1024
1062
|
|
1025
1063
|
while (lex_css< exactly<','> >())
|
@@ -1029,6 +1067,16 @@ namespace Sass {
|
|
1029
1067
|
{ break; }
|
1030
1068
|
|
1031
1069
|
Expression* key = parse_list();
|
1070
|
+
if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {
|
1071
|
+
if (!str->quote_mark() && !str->is_delayed()) {
|
1072
|
+
if (ctx.names_to_colors.count(str->value())) {
|
1073
|
+
Color* c = new (ctx.mem) Color(*ctx.names_to_colors[str->value()]);
|
1074
|
+
c->pstate(str->pstate());
|
1075
|
+
c->disp(str->value());
|
1076
|
+
key = c;
|
1077
|
+
}
|
1078
|
+
}
|
1079
|
+
}
|
1032
1080
|
|
1033
1081
|
if (!(lex< exactly<':'> >()))
|
1034
1082
|
{ error("invalid syntax", pstate); }
|
@@ -1038,8 +1086,15 @@ namespace Sass {
|
|
1038
1086
|
(*map) << make_pair(key, value);
|
1039
1087
|
}
|
1040
1088
|
|
1041
|
-
|
1042
|
-
|
1089
|
+
// Check was moved to eval step
|
1090
|
+
// if (map->has_duplicate_key()) {
|
1091
|
+
// To_String to_string(&ctx);
|
1092
|
+
// error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + ".", pstate);
|
1093
|
+
// }
|
1094
|
+
|
1095
|
+
ParserState ps = map->pstate();
|
1096
|
+
ps.offset = pstate - ps + pstate.offset;
|
1097
|
+
map->pstate(ps);
|
1043
1098
|
|
1044
1099
|
return map;
|
1045
1100
|
}
|
@@ -1211,6 +1266,11 @@ namespace Sass {
|
|
1211
1266
|
}
|
1212
1267
|
// if it's a singleton, return it directly; don't wrap it
|
1213
1268
|
if (!peek< class_char< static_ops > >(position)) return factor;
|
1269
|
+
return parse_operators(factor);
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
Expression* Parser::parse_operators(Expression* factor)
|
1273
|
+
{
|
1214
1274
|
// parse more factors and operators
|
1215
1275
|
vector<Expression*> operands; // factors
|
1216
1276
|
vector<Binary_Expression::Type> operators; // ops
|
@@ -1367,6 +1427,9 @@ namespace Sass {
|
|
1367
1427
|
}
|
1368
1428
|
// we need to skip anything inside strings
|
1369
1429
|
// create a new target in parser/prelexer
|
1430
|
+
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
1431
|
+
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1432
|
+
}
|
1370
1433
|
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
|
1371
1434
|
if (j) { --j;
|
1372
1435
|
// parse the interpolant and accumulate it
|
@@ -1415,28 +1478,26 @@ namespace Sass {
|
|
1415
1478
|
Token str(lexed);
|
1416
1479
|
const char* i = str.begin;
|
1417
1480
|
// see if there any interpolants
|
1418
|
-
const char* q;
|
1419
1481
|
const char* p = find_first_in_interval< exactly<hash_lbrace> >(str.begin, str.end);
|
1420
1482
|
if (!p) {
|
1421
1483
|
String_Constant* str_node = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(str.begin, str.end)));
|
1422
1484
|
str_node->is_delayed(true);
|
1485
|
+
str_node->quote_mark('*');
|
1423
1486
|
return str_node;
|
1424
1487
|
}
|
1425
1488
|
|
1426
1489
|
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1427
1490
|
while (i < str.end) {
|
1428
|
-
q = find_first_in_interval< alternatives< exactly<'"'>, exactly<'\''> > >(i, str.end);
|
1429
1491
|
p = find_first_in_interval< exactly<hash_lbrace> >(i, str.end);
|
1430
|
-
if (
|
1431
|
-
if (i < q) {
|
1432
|
-
(*schema) << new (ctx.mem) String_Constant(pstate, string(i, q)); // accumulate the preceding segment if it's nonempty
|
1433
|
-
}
|
1434
|
-
(*schema) << new (ctx.mem) String_Constant(pstate, string(q, q+1)); // capture the quote mark separately
|
1435
|
-
i = q+1;
|
1436
|
-
}
|
1437
|
-
else if (p) {
|
1492
|
+
if (p) {
|
1438
1493
|
if (i < p) {
|
1439
|
-
|
1494
|
+
String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, p))); // accumulate the preceding segment if it's nonempty
|
1495
|
+
part->is_delayed(true);
|
1496
|
+
part->quote_mark('*'); // avoid unquote in interpolation
|
1497
|
+
(*schema) << part;
|
1498
|
+
}
|
1499
|
+
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
1500
|
+
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1440
1501
|
}
|
1441
1502
|
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
|
1442
1503
|
if (j) {
|
@@ -1452,7 +1513,12 @@ namespace Sass {
|
|
1452
1513
|
}
|
1453
1514
|
}
|
1454
1515
|
else { // no interpolants left; add the last segment if nonempty
|
1455
|
-
if (i < str.end)
|
1516
|
+
if (i < str.end) {
|
1517
|
+
String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, str.end)));
|
1518
|
+
part->is_delayed(true);
|
1519
|
+
part->quote_mark('*'); // avoid unquote in interpolation
|
1520
|
+
(*schema) << part;
|
1521
|
+
}
|
1456
1522
|
break;
|
1457
1523
|
}
|
1458
1524
|
}
|
@@ -1466,15 +1532,13 @@ namespace Sass {
|
|
1466
1532
|
*kwd_arg << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));
|
1467
1533
|
} else {
|
1468
1534
|
lex< alternatives< identifier_schema, identifier > >();
|
1469
|
-
*kwd_arg << new (ctx.mem)
|
1535
|
+
*kwd_arg << new (ctx.mem) String_Constant(pstate, lexed);
|
1470
1536
|
}
|
1471
1537
|
lex< exactly<'='> >();
|
1472
|
-
*kwd_arg << new (ctx.mem)
|
1538
|
+
*kwd_arg << new (ctx.mem) String_Constant(pstate, lexed);
|
1473
1539
|
if (peek< variable >()) *kwd_arg << parse_list();
|
1474
1540
|
else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(pstate, Textual::NUMBER, Util::normalize_decimals(lexed));
|
1475
|
-
else if (
|
1476
|
-
*kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed);
|
1477
|
-
}
|
1541
|
+
else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); }
|
1478
1542
|
return kwd_arg;
|
1479
1543
|
}
|
1480
1544
|
|
@@ -1482,8 +1546,14 @@ namespace Sass {
|
|
1482
1546
|
{
|
1483
1547
|
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1484
1548
|
size_t num_items = 0;
|
1549
|
+
if (peek<exactly<'}'>>()) {
|
1550
|
+
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1551
|
+
}
|
1485
1552
|
while (position < stop) {
|
1486
|
-
if (lex<
|
1553
|
+
if (lex< spaces >() && num_items) {
|
1554
|
+
(*schema) << new (ctx.mem) String_Constant(pstate, " ");
|
1555
|
+
}
|
1556
|
+
else if (lex< interpolant >()) {
|
1487
1557
|
Token insides(Token(lexed.begin + 2, lexed.end - 1));
|
1488
1558
|
Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();
|
1489
1559
|
interp_node->is_interpolant(true);
|
@@ -1502,17 +1572,28 @@ namespace Sass {
|
|
1502
1572
|
(*schema) << new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed);
|
1503
1573
|
}
|
1504
1574
|
else if (lex< number >()) {
|
1505
|
-
|
1575
|
+
Expression* factor = new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed);
|
1576
|
+
if (peek< class_char< static_ops > >()) {
|
1577
|
+
(*schema) << parse_operators(factor);
|
1578
|
+
} else {
|
1579
|
+
(*schema) << factor;
|
1580
|
+
}
|
1506
1581
|
}
|
1507
1582
|
else if (lex< hex >()) {
|
1508
1583
|
(*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, unquote(lexed));
|
1509
1584
|
}
|
1585
|
+
else if (lex < exactly < '-' > >()) {
|
1586
|
+
(*schema) << new (ctx.mem) String_Constant(pstate, lexed);
|
1587
|
+
}
|
1510
1588
|
else if (lex< quoted_string >()) {
|
1511
1589
|
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
|
1512
1590
|
}
|
1513
1591
|
else if (lex< variable >()) {
|
1514
1592
|
(*schema) << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));
|
1515
1593
|
}
|
1594
|
+
else if (peek< parenthese_scope >()) {
|
1595
|
+
(*schema) << parse_factor();
|
1596
|
+
}
|
1516
1597
|
else {
|
1517
1598
|
error("error parsing interpolated value", pstate);
|
1518
1599
|
}
|
@@ -1575,6 +1656,9 @@ namespace Sass {
|
|
1575
1656
|
}
|
1576
1657
|
// we need to skip anything inside strings
|
1577
1658
|
// create a new target in parser/prelexer
|
1659
|
+
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
1660
|
+
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1661
|
+
}
|
1578
1662
|
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace
|
1579
1663
|
if (j) {
|
1580
1664
|
// parse the interpolant and accumulate it
|
@@ -1988,6 +2072,7 @@ namespace Sass {
|
|
1988
2072
|
(q = peek< class_name >(p)) ||
|
1989
2073
|
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
|
1990
2074
|
(q = peek< percentage >(p)) ||
|
2075
|
+
(q = peek< variable >(p)) ||
|
1991
2076
|
(q = peek< dimension >(p)) ||
|
1992
2077
|
(q = peek< quoted_string >(p)) ||
|
1993
2078
|
(q = peek< exactly<'*'> >(p)) ||
|
@@ -2050,6 +2135,7 @@ namespace Sass {
|
|
2050
2135
|
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
|
2051
2136
|
(q = peek< percentage >(p)) ||
|
2052
2137
|
(q = peek< dimension >(p)) ||
|
2138
|
+
(q = peek< variable >(p)) ||
|
2053
2139
|
(q = peek< quoted_string >(p)) ||
|
2054
2140
|
(q = peek< exactly<'*'> >(p)) ||
|
2055
2141
|
(q = peek< exactly<'('> >(p)) ||
|
@@ -2097,6 +2183,56 @@ namespace Sass {
|
|
2097
2183
|
return result;
|
2098
2184
|
}
|
2099
2185
|
|
2186
|
+
|
2187
|
+
Selector_Lookahead Parser::lookahead_for_value(const char* start)
|
2188
|
+
{
|
2189
|
+
const char* p = start ? start : position;
|
2190
|
+
const char* q;
|
2191
|
+
bool saw_interpolant = false;
|
2192
|
+
bool saw_stuff = false;
|
2193
|
+
|
2194
|
+
while ((q = peek< identifier >(p)) ||
|
2195
|
+
(q = peek< percentage >(p)) ||
|
2196
|
+
(q = peek< dimension >(p)) ||
|
2197
|
+
(q = peek< quoted_string >(p)) ||
|
2198
|
+
(q = peek< variable >(p)) ||
|
2199
|
+
(q = peek< exactly<'*'> >(p)) ||
|
2200
|
+
(q = peek< exactly<'+'> >(p)) ||
|
2201
|
+
(q = peek< exactly<'~'> >(p)) ||
|
2202
|
+
(q = peek< exactly<'>'> >(p)) ||
|
2203
|
+
(q = peek< exactly<','> >(p)) ||
|
2204
|
+
(q = peek< sequence<parenthese_scope, interpolant>>(p)) ||
|
2205
|
+
(saw_stuff && (q = peek< exactly<'-'> >(p))) ||
|
2206
|
+
(q = peek< binomial >(p)) ||
|
2207
|
+
(q = peek< block_comment >(p)) ||
|
2208
|
+
(q = peek< sequence< optional<sign>,
|
2209
|
+
zero_plus<digit>,
|
2210
|
+
exactly<'n'> > >(p)) ||
|
2211
|
+
(q = peek< sequence< optional<sign>,
|
2212
|
+
one_plus<digit> > >(p)) ||
|
2213
|
+
(q = peek< number >(p)) ||
|
2214
|
+
(q = peek< sequence< exactly<'&'>,
|
2215
|
+
identifier_alnums > >(p)) ||
|
2216
|
+
(q = peek< exactly<'&'> >(p)) ||
|
2217
|
+
(q = peek< exactly<'%'> >(p)) ||
|
2218
|
+
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
|
2219
|
+
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
|
2220
|
+
(q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
|
2221
|
+
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
|
2222
|
+
(q = peek< interpolant >(p)) ||
|
2223
|
+
(q = peek< optional >(p))) {
|
2224
|
+
p = q;
|
2225
|
+
if (*(p - 1) == '}') saw_interpolant = true;
|
2226
|
+
saw_stuff = true;
|
2227
|
+
}
|
2228
|
+
|
2229
|
+
Selector_Lookahead result;
|
2230
|
+
result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;
|
2231
|
+
result.has_interpolants = saw_interpolant;
|
2232
|
+
|
2233
|
+
return result;
|
2234
|
+
}
|
2235
|
+
|
2100
2236
|
void Parser::read_bom()
|
2101
2237
|
{
|
2102
2238
|
size_t skip = 0;
|
@@ -2209,14 +2345,16 @@ namespace Sass {
|
|
2209
2345
|
const char* pos = peek < optional_spaces >();
|
2210
2346
|
bool ellipsis_left = false;
|
2211
2347
|
const char* pos_left(pos);
|
2212
|
-
while (*pos_left && pos_left
|
2348
|
+
while (*pos_left && pos_left > source) {
|
2213
2349
|
if (pos - pos_left > max_len) {
|
2214
2350
|
ellipsis_left = true;
|
2215
2351
|
break;
|
2216
2352
|
}
|
2217
|
-
|
2218
|
-
if (*
|
2219
|
-
|
2353
|
+
const char* prev = pos_left - 1;
|
2354
|
+
if (*prev == '\r') break;
|
2355
|
+
if (*prev == '\n') break;
|
2356
|
+
if (*prev == 10) break;
|
2357
|
+
pos_left = prev;
|
2220
2358
|
}
|
2221
2359
|
bool ellipsis_right = false;
|
2222
2360
|
const char* pos_right(pos);
|
@@ -2227,6 +2365,7 @@ namespace Sass {
|
|
2227
2365
|
}
|
2228
2366
|
if (*pos_right == '\r') break;
|
2229
2367
|
if (*pos_right == '\n') break;
|
2368
|
+
if (*pos_left == 10) break;
|
2230
2369
|
++ pos_right;
|
2231
2370
|
}
|
2232
2371
|
string left(pos_left, pos);
|