sassc 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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);
|