sassc 1.8.3 → 1.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/ext/libsass/.editorconfig +1 -1
- data/ext/libsass/.gitignore +1 -0
- data/ext/libsass/LICENSE +1 -1
- data/ext/libsass/Makefile +20 -14
- data/ext/libsass/Makefile.conf +0 -1
- data/ext/libsass/Readme.md +3 -1
- data/ext/libsass/appveyor.yml +19 -11
- data/ext/libsass/docs/api-importer-example.md +2 -1235
- data/ext/libsass/docs/build-with-autotools.md +10 -0
- data/ext/libsass/docs/build-with-makefiles.md +18 -0
- data/ext/libsass/include/sass/base.h +4 -1
- data/ext/libsass/include/sass/values.h +2 -1
- data/ext/libsass/src/ast.cpp +279 -346
- data/ext/libsass/src/ast.hpp +234 -60
- data/ext/libsass/src/base64vlq.cpp +1 -0
- data/ext/libsass/src/bind.cpp +35 -45
- data/ext/libsass/src/bind.hpp +1 -0
- data/ext/libsass/src/color_maps.cpp +1 -0
- data/ext/libsass/src/constants.cpp +4 -1
- data/ext/libsass/src/constants.hpp +2 -1
- data/ext/libsass/src/context.cpp +41 -31
- data/ext/libsass/src/context.hpp +10 -10
- data/ext/libsass/src/cssize.cpp +7 -4
- data/ext/libsass/src/cssize.hpp +1 -3
- data/ext/libsass/src/debugger.hpp +73 -14
- data/ext/libsass/src/emitter.cpp +37 -25
- data/ext/libsass/src/emitter.hpp +10 -9
- data/ext/libsass/src/environment.cpp +16 -5
- data/ext/libsass/src/environment.hpp +5 -3
- data/ext/libsass/src/error_handling.cpp +91 -14
- data/ext/libsass/src/error_handling.hpp +105 -4
- data/ext/libsass/src/eval.cpp +519 -330
- data/ext/libsass/src/eval.hpp +12 -13
- data/ext/libsass/src/expand.cpp +92 -56
- data/ext/libsass/src/expand.hpp +5 -3
- data/ext/libsass/src/extend.cpp +60 -51
- data/ext/libsass/src/extend.hpp +1 -3
- data/ext/libsass/src/file.cpp +37 -27
- data/ext/libsass/src/functions.cpp +78 -62
- data/ext/libsass/src/functions.hpp +1 -0
- data/ext/libsass/src/inspect.cpp +293 -64
- data/ext/libsass/src/inspect.hpp +2 -0
- data/ext/libsass/src/lexer.cpp +1 -0
- data/ext/libsass/src/listize.cpp +14 -15
- data/ext/libsass/src/listize.hpp +3 -5
- data/ext/libsass/src/memory_manager.cpp +1 -0
- data/ext/libsass/src/node.cpp +2 -3
- data/ext/libsass/src/operation.hpp +70 -71
- data/ext/libsass/src/output.cpp +28 -32
- data/ext/libsass/src/output.hpp +1 -2
- data/ext/libsass/src/parser.cpp +402 -183
- data/ext/libsass/src/parser.hpp +19 -9
- data/ext/libsass/src/plugins.cpp +1 -0
- data/ext/libsass/src/position.cpp +1 -0
- data/ext/libsass/src/prelexer.cpp +134 -56
- data/ext/libsass/src/prelexer.hpp +51 -3
- data/ext/libsass/src/remove_placeholders.cpp +35 -9
- data/ext/libsass/src/remove_placeholders.hpp +4 -3
- data/ext/libsass/src/sass.cpp +1 -0
- data/ext/libsass/src/sass.hpp +129 -0
- data/ext/libsass/src/sass_context.cpp +31 -14
- data/ext/libsass/src/sass_context.hpp +2 -31
- data/ext/libsass/src/sass_functions.cpp +1 -0
- data/ext/libsass/src/sass_interface.cpp +5 -6
- data/ext/libsass/src/sass_util.cpp +1 -2
- data/ext/libsass/src/sass_util.hpp +5 -5
- data/ext/libsass/src/sass_values.cpp +13 -10
- data/ext/libsass/src/source_map.cpp +4 -3
- data/ext/libsass/src/source_map.hpp +2 -2
- data/ext/libsass/src/subset_map.hpp +0 -1
- data/ext/libsass/src/to_c.cpp +1 -0
- data/ext/libsass/src/to_c.hpp +1 -3
- data/ext/libsass/src/to_value.cpp +3 -5
- data/ext/libsass/src/to_value.hpp +1 -1
- data/ext/libsass/src/units.cpp +96 -59
- data/ext/libsass/src/units.hpp +10 -8
- data/ext/libsass/src/utf8_string.cpp +5 -0
- data/ext/libsass/src/util.cpp +23 -156
- data/ext/libsass/src/util.hpp +10 -14
- data/ext/libsass/src/values.cpp +1 -0
- data/ext/libsass/test/test_node.cpp +2 -6
- data/ext/libsass/test/test_selector_difference.cpp +1 -3
- data/ext/libsass/test/test_specificity.cpp +0 -2
- data/ext/libsass/test/test_superselector.cpp +0 -2
- data/ext/libsass/test/test_unification.cpp +1 -3
- data/ext/libsass/win/libsass.targets +18 -5
- data/ext/libsass/win/libsass.vcxproj +9 -7
- data/ext/libsass/win/libsass.vcxproj.filters +148 -106
- data/lib/sassc/version.rb +1 -1
- data/test/engine_test.rb +12 -0
- data/test/native_test.rb +1 -1
- metadata +3 -4
- data/ext/libsass/src/to_string.cpp +0 -48
- data/ext/libsass/src/to_string.hpp +0 -38
data/ext/libsass/src/output.hpp
CHANGED
data/ext/libsass/src/parser.cpp
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
#include "sass.hpp"
|
1
2
|
#include <cstdlib>
|
2
3
|
#include <iostream>
|
3
4
|
#include <vector>
|
4
5
|
#include "parser.hpp"
|
5
6
|
#include "file.hpp"
|
6
7
|
#include "inspect.hpp"
|
7
|
-
#include "to_string.hpp"
|
8
8
|
#include "constants.hpp"
|
9
9
|
#include "util.hpp"
|
10
10
|
#include "prelexer.hpp"
|
@@ -19,33 +19,33 @@ namespace Sass {
|
|
19
19
|
using namespace Constants;
|
20
20
|
using namespace Prelexer;
|
21
21
|
|
22
|
-
Parser Parser::from_c_str(const char*
|
22
|
+
Parser Parser::from_c_str(const char* beg, Context& ctx, ParserState pstate, const char* source)
|
23
23
|
{
|
24
24
|
Parser p(ctx, pstate);
|
25
|
-
p.source =
|
26
|
-
p.position = p.source;
|
27
|
-
p.end =
|
25
|
+
p.source = source ? source : beg;
|
26
|
+
p.position = beg ? beg : p.source;
|
27
|
+
p.end = p.position + strlen(p.position);
|
28
28
|
Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);
|
29
29
|
p.block_stack.push_back(root);
|
30
30
|
root->is_root(true);
|
31
31
|
return p;
|
32
32
|
}
|
33
33
|
|
34
|
-
Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate)
|
34
|
+
Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate, const char* source)
|
35
35
|
{
|
36
36
|
Parser p(ctx, pstate);
|
37
|
-
p.source = beg;
|
38
|
-
p.position = p.source;
|
39
|
-
p.end = end;
|
37
|
+
p.source = source ? source : beg;
|
38
|
+
p.position = beg ? beg : p.source;
|
39
|
+
p.end = end ? end : p.position + strlen(p.position);
|
40
40
|
Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);
|
41
41
|
p.block_stack.push_back(root);
|
42
42
|
root->is_root(true);
|
43
43
|
return p;
|
44
44
|
}
|
45
45
|
|
46
|
-
Selector_List* Parser::parse_selector(const char*
|
46
|
+
Selector_List* Parser::parse_selector(const char* beg, Context& ctx, ParserState pstate, const char* source)
|
47
47
|
{
|
48
|
-
Parser p = Parser::from_c_str(
|
48
|
+
Parser p = Parser::from_c_str(beg, ctx, pstate, source);
|
49
49
|
// ToDo: ruby sass errors on parent references
|
50
50
|
// ToDo: remap the source-map entries somehow
|
51
51
|
return p.parse_selector_list(false);
|
@@ -57,12 +57,12 @@ namespace Sass {
|
|
57
57
|
&& ! peek_css<exactly<'{'>>(start);
|
58
58
|
}
|
59
59
|
|
60
|
-
Parser Parser::from_token(Token t, Context& ctx, ParserState pstate)
|
60
|
+
Parser Parser::from_token(Token t, Context& ctx, ParserState pstate, const char* source)
|
61
61
|
{
|
62
62
|
Parser p(ctx, pstate);
|
63
|
-
p.source = t.begin;
|
64
|
-
p.position = p.source;
|
65
|
-
p.end = t.end;
|
63
|
+
p.source = source ? source : t.begin;
|
64
|
+
p.position = t.begin ? t.begin : p.source;
|
65
|
+
p.end = t.end ? t.end : p.position + strlen(p.position);
|
66
66
|
Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);
|
67
67
|
p.block_stack.push_back(root);
|
68
68
|
root->is_root(true);
|
@@ -177,11 +177,7 @@ namespace Sass {
|
|
177
177
|
|
178
178
|
Block* block = block_stack.back();
|
179
179
|
|
180
|
-
|
181
|
-
bool is_important = lexed.begin[2] == '!';
|
182
|
-
String* contents = parse_interpolated_chunk(lexed);
|
183
|
-
(*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);
|
184
|
-
}
|
180
|
+
parse_block_comments();
|
185
181
|
|
186
182
|
// throw away white-space
|
187
183
|
// includes line comments
|
@@ -203,14 +199,17 @@ namespace Sass {
|
|
203
199
|
else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); }
|
204
200
|
|
205
201
|
// abort if we are in function context and have nothing parsed yet
|
206
|
-
else if (stack.back() ==
|
202
|
+
else if (stack.back() == Scope::Function) {
|
207
203
|
error("Functions can only contain variable declarations and control directives", pstate);
|
208
204
|
}
|
209
205
|
|
210
206
|
// parse imports to process later
|
211
207
|
else if (lex < kwd_import >(true)) {
|
212
|
-
|
213
|
-
|
208
|
+
Scope parent = stack.empty() ? Scope::Rules : stack.back();
|
209
|
+
if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) {
|
210
|
+
if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20
|
211
|
+
error("Import directives may not be used within control directives or mixins.", pstate);
|
212
|
+
}
|
214
213
|
}
|
215
214
|
Import* imp = parse_import();
|
216
215
|
// if it is a url, we only add the statement
|
@@ -222,7 +221,8 @@ namespace Sass {
|
|
222
221
|
}
|
223
222
|
|
224
223
|
else if (lex < kwd_extend >(true)) {
|
225
|
-
|
224
|
+
Scope parent = stack.empty() ? Scope::Rules : stack.back();
|
225
|
+
if (parent == Scope::Root) {
|
226
226
|
error("Extend directives may only be used within rules.", pstate);
|
227
227
|
}
|
228
228
|
|
@@ -270,7 +270,9 @@ namespace Sass {
|
|
270
270
|
if (peek< exactly<'{'> >()) {
|
271
271
|
if (decl->is_indented()) ++ indentation;
|
272
272
|
// parse a propset that rides on the declaration's property
|
273
|
+
stack.push_back(Scope::Properties);
|
273
274
|
(*block) << SASS_MEMORY_NEW(ctx.mem, Propset, pstate, decl->property(), parse_block());
|
275
|
+
stack.pop_back();
|
274
276
|
if (decl->is_indented()) -- indentation;
|
275
277
|
}
|
276
278
|
}
|
@@ -298,12 +300,12 @@ namespace Sass {
|
|
298
300
|
else if (lex< uri_prefix >()) {
|
299
301
|
Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);
|
300
302
|
Function_Call* result = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", args);
|
303
|
+
|
301
304
|
if (lex< quoted_string >()) {
|
302
305
|
Expression* the_url = parse_string();
|
303
306
|
*args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);
|
304
307
|
}
|
305
|
-
else if (
|
306
|
-
String* the_url = parse_interpolated_chunk(lexed);
|
308
|
+
else if (String* the_url = parse_url_function_argument()) {
|
307
309
|
*args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);
|
308
310
|
}
|
309
311
|
else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {
|
@@ -341,6 +343,10 @@ namespace Sass {
|
|
341
343
|
|
342
344
|
Definition* Parser::parse_definition(Definition::Type which_type)
|
343
345
|
{
|
346
|
+
Scope parent = stack.empty() ? Scope::Rules : stack.back();
|
347
|
+
if (parent != Scope::Root && parent != Scope::Rules && parent != Scope::Function) {
|
348
|
+
error("Functions may not be defined within control directives or other mixins.", pstate);
|
349
|
+
}
|
344
350
|
std::string which_str(lexed);
|
345
351
|
if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);
|
346
352
|
std::string name(Util::normalize_underscores(lexed));
|
@@ -348,8 +354,8 @@ namespace Sass {
|
|
348
354
|
{ error("Invalid function name \"" + name + "\".", pstate); }
|
349
355
|
ParserState source_position_of_def = pstate;
|
350
356
|
Parameters* params = parse_parameters();
|
351
|
-
if (which_type == Definition::MIXIN) stack.push_back(
|
352
|
-
else stack.push_back(
|
357
|
+
if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin);
|
358
|
+
else stack.push_back(Scope::Function);
|
353
359
|
Block* body = parse_block();
|
354
360
|
stack.pop_back();
|
355
361
|
Definition* def = SASS_MEMORY_NEW(ctx.mem, Definition, source_position_of_def, name, params, body, which_type);
|
@@ -431,8 +437,11 @@ namespace Sass {
|
|
431
437
|
bool is_keyword = false;
|
432
438
|
Expression* val = parse_space_list();
|
433
439
|
val->is_delayed(false);
|
440
|
+
List* l = dynamic_cast<List*>(val);
|
434
441
|
if (lex_css< exactly< ellipsis > >()) {
|
435
|
-
if (val->concrete_type() == Expression::MAP
|
442
|
+
if (val->concrete_type() == Expression::MAP || (
|
443
|
+
(l != NULL && l->separator() == SASS_HASH)
|
444
|
+
)) is_keyword = true;
|
436
445
|
else is_arglist = true;
|
437
446
|
}
|
438
447
|
arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, val, "", is_arglist, is_keyword);
|
@@ -474,7 +483,9 @@ namespace Sass {
|
|
474
483
|
if (lookahead.parsable) ruleset->selector(parse_selector_list(is_root));
|
475
484
|
else ruleset->selector(parse_selector_schema(lookahead.found));
|
476
485
|
// then parse the inner block
|
486
|
+
stack.push_back(Scope::Rules);
|
477
487
|
ruleset->block(parse_block());
|
488
|
+
stack.pop_back();
|
478
489
|
// update for end position
|
479
490
|
ruleset->update_pstate(pstate);
|
480
491
|
// inherit is_root from parent block
|
@@ -501,7 +512,7 @@ namespace Sass {
|
|
501
512
|
// process until end
|
502
513
|
while (i < end_of_selector) {
|
503
514
|
// try to parse mutliple interpolants
|
504
|
-
if (const char* p = find_first_in_interval< exactly<hash_lbrace
|
515
|
+
if (const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, end_of_selector)) {
|
505
516
|
// accumulate the preceding segment if the position has advanced
|
506
517
|
if (i < p) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));
|
507
518
|
// check if the interpolation only contains white-space (error out)
|
@@ -514,6 +525,7 @@ namespace Sass {
|
|
514
525
|
Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();
|
515
526
|
// set status on the list expression
|
516
527
|
interpolant->is_interpolant(true);
|
528
|
+
// schema->has_interpolants(true);
|
517
529
|
// add to the string schema
|
518
530
|
(*schema) << interpolant;
|
519
531
|
// advance position
|
@@ -579,7 +591,6 @@ namespace Sass {
|
|
579
591
|
bool reloop = true;
|
580
592
|
bool had_linefeed = false;
|
581
593
|
Complex_Selector* sel = 0;
|
582
|
-
To_String to_string(&ctx);
|
583
594
|
Selector_List* group = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate);
|
584
595
|
group->media_block(last_media_block);
|
585
596
|
|
@@ -676,14 +687,14 @@ namespace Sass {
|
|
676
687
|
sel->tail(parse_complex_selector(true));
|
677
688
|
if (sel->tail()) {
|
678
689
|
// ToDo: move this logic below into tail setter
|
679
|
-
if (sel->tail()->has_reference()) sel->has_reference(true);
|
690
|
+
// if (sel->tail()->has_reference()) sel->has_reference(true);
|
680
691
|
if (sel->tail()->has_placeholder()) sel->has_placeholder(true);
|
681
692
|
}
|
682
693
|
}
|
683
694
|
|
684
695
|
// add a parent selector if we are not in a root
|
685
696
|
// also skip adding parent ref if we only have refs
|
686
|
-
if (!sel->
|
697
|
+
if (!sel->has_parent_ref() && !in_at_root && !in_root) {
|
687
698
|
// create the objects to wrap parent selector reference
|
688
699
|
Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);
|
689
700
|
parent->media_block(last_media_block);
|
@@ -725,7 +736,7 @@ namespace Sass {
|
|
725
736
|
// remove all block comments (don't skip white-space)
|
726
737
|
lex< delimited_by< slash_star, star_slash, false > >(false);
|
727
738
|
// parse functional
|
728
|
-
if (
|
739
|
+
if (match < re_pseudo_selector >())
|
729
740
|
{
|
730
741
|
(*seq) << parse_simple_selector();
|
731
742
|
}
|
@@ -735,6 +746,18 @@ namespace Sass {
|
|
735
746
|
// this produces a linefeed!?
|
736
747
|
seq->has_parent_reference(true);
|
737
748
|
(*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);
|
749
|
+
// parent selector only allowed at start
|
750
|
+
// upcoming sass may allow also trailing
|
751
|
+
if (seq->length() > 1) {
|
752
|
+
ParserState state(pstate);
|
753
|
+
Simple_Selector* cur = (*seq)[seq->length()-1];
|
754
|
+
Simple_Selector* prev = (*seq)[seq->length()-2];
|
755
|
+
std::string sel(prev->to_string({ NESTED, 5 }));
|
756
|
+
std::string found(cur->to_string({ NESTED, 5 }));
|
757
|
+
if (lex < identifier >()) { found += std::string(lexed); }
|
758
|
+
error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n"
|
759
|
+
"\"" + found + "\" may only be used at the beginning of a compound selector.", state);
|
760
|
+
}
|
738
761
|
}
|
739
762
|
// parse type selector
|
740
763
|
else if (lex< re_type_selector >(false))
|
@@ -901,7 +924,8 @@ namespace Sass {
|
|
901
924
|
Block* block = block_stack.back();
|
902
925
|
while (lex< block_comment >()) {
|
903
926
|
bool is_important = lexed.begin[2] == '!';
|
904
|
-
|
927
|
+
// flag on second param is to skip loosely over comments
|
928
|
+
String* contents = parse_interpolated_chunk(lexed, true);
|
905
929
|
(*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);
|
906
930
|
}
|
907
931
|
}
|
@@ -971,7 +995,7 @@ namespace Sass {
|
|
971
995
|
Expression* Parser::parse_map()
|
972
996
|
{
|
973
997
|
Expression* key = parse_list();
|
974
|
-
|
998
|
+
List* map = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_HASH);
|
975
999
|
if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {
|
976
1000
|
if (!str->quote_mark() && !str->is_delayed()) {
|
977
1001
|
if (const Color* col = name_to_color(str->value())) {
|
@@ -984,14 +1008,12 @@ namespace Sass {
|
|
984
1008
|
}
|
985
1009
|
|
986
1010
|
// it's not a map so return the lexed value as a list value
|
987
|
-
if (!
|
1011
|
+
if (!lex_css< exactly<':'> >())
|
988
1012
|
{ return key; }
|
989
1013
|
|
990
|
-
lex< exactly<':'> >();
|
991
|
-
|
992
1014
|
Expression* value = parse_space_list();
|
993
1015
|
|
994
|
-
(*map) <<
|
1016
|
+
(*map) << key << value;
|
995
1017
|
|
996
1018
|
while (lex_css< exactly<','> >())
|
997
1019
|
{
|
@@ -1016,7 +1038,7 @@ namespace Sass {
|
|
1016
1038
|
|
1017
1039
|
Expression* value = parse_space_list();
|
1018
1040
|
|
1019
|
-
(*map) <<
|
1041
|
+
(*map) << key << value;
|
1020
1042
|
}
|
1021
1043
|
|
1022
1044
|
ParserState ps = map->pstate();
|
@@ -1047,6 +1069,7 @@ namespace Sass {
|
|
1047
1069
|
exactly<'{'>,
|
1048
1070
|
exactly<')'>,
|
1049
1071
|
exactly<':'>,
|
1072
|
+
end_of_file,
|
1050
1073
|
exactly<ellipsis>,
|
1051
1074
|
default_flag,
|
1052
1075
|
global_flag
|
@@ -1072,6 +1095,7 @@ namespace Sass {
|
|
1072
1095
|
exactly<'{'>,
|
1073
1096
|
exactly<')'>,
|
1074
1097
|
exactly<':'>,
|
1098
|
+
end_of_file,
|
1075
1099
|
exactly<ellipsis>,
|
1076
1100
|
default_flag,
|
1077
1101
|
global_flag
|
@@ -1098,6 +1122,7 @@ namespace Sass {
|
|
1098
1122
|
exactly<')'>,
|
1099
1123
|
exactly<','>,
|
1100
1124
|
exactly<':'>,
|
1125
|
+
end_of_file,
|
1101
1126
|
exactly<ellipsis>,
|
1102
1127
|
default_flag,
|
1103
1128
|
global_flag
|
@@ -1115,6 +1140,7 @@ namespace Sass {
|
|
1115
1140
|
exactly<')'>,
|
1116
1141
|
exactly<','>,
|
1117
1142
|
exactly<':'>,
|
1143
|
+
end_of_file,
|
1118
1144
|
exactly<ellipsis>,
|
1119
1145
|
default_flag,
|
1120
1146
|
global_flag
|
@@ -1140,7 +1166,7 @@ namespace Sass {
|
|
1140
1166
|
// if it's a singleton, return it directly
|
1141
1167
|
if (operands.size() == 0) return conj;
|
1142
1168
|
// fold all operands into one binary expression
|
1143
|
-
return fold_operands(conj, operands, Sass_OP::OR);
|
1169
|
+
return fold_operands(conj, operands, { Sass_OP::OR });
|
1144
1170
|
}
|
1145
1171
|
// EO parse_disjunction
|
1146
1172
|
|
@@ -1156,7 +1182,7 @@ namespace Sass {
|
|
1156
1182
|
// if it's a singleton, return it directly
|
1157
1183
|
if (operands.size() == 0) return rel;
|
1158
1184
|
// fold all operands into one binary expression
|
1159
|
-
return fold_operands(rel, operands, Sass_OP::AND);
|
1185
|
+
return fold_operands(rel, operands, { Sass_OP::AND });
|
1160
1186
|
}
|
1161
1187
|
// EO parse_conjunction
|
1162
1188
|
|
@@ -1165,30 +1191,38 @@ namespace Sass {
|
|
1165
1191
|
{
|
1166
1192
|
// parse the left hand side expression
|
1167
1193
|
Expression* lhs = parse_expression();
|
1194
|
+
std::vector<Expression*> operands;
|
1195
|
+
std::vector<Operand> operators;
|
1168
1196
|
// if it's a singleton, return it (don't wrap it)
|
1169
|
-
|
1197
|
+
while (peek< alternatives <
|
1170
1198
|
kwd_eq,
|
1171
1199
|
kwd_neq,
|
1172
1200
|
kwd_gte,
|
1173
1201
|
kwd_gt,
|
1174
1202
|
kwd_lte,
|
1175
1203
|
kwd_lt
|
1176
|
-
> >(position))
|
1177
|
-
{
|
1204
|
+
> >(position))
|
1205
|
+
{
|
1206
|
+
// is directly adjancent to expression?
|
1207
|
+
bool left_ws = peek < css_comments >() != NULL;
|
1208
|
+
// parse the operator
|
1209
|
+
enum Sass_OP op
|
1210
|
+
= lex<kwd_eq>() ? Sass_OP::EQ
|
1211
|
+
: lex<kwd_neq>() ? Sass_OP::NEQ
|
1212
|
+
: lex<kwd_gte>() ? Sass_OP::GTE
|
1213
|
+
: lex<kwd_lte>() ? Sass_OP::LTE
|
1214
|
+
: lex<kwd_gt>() ? Sass_OP::GT
|
1215
|
+
: lex<kwd_lt>() ? Sass_OP::LT
|
1216
|
+
// we checked the possibilites on top of fn
|
1217
|
+
: Sass_OP::EQ;
|
1218
|
+
// is directly adjancent to expression?
|
1219
|
+
bool right_ws = peek < css_comments >() != NULL;
|
1220
|
+
operators.push_back({ op, left_ws, right_ws });
|
1221
|
+
operands.push_back(parse_expression());
|
1222
|
+
left_ws = peek < css_comments >() != NULL;
|
1223
|
+
}
|
1178
1224
|
// parse the operator
|
1179
|
-
|
1180
|
-
= lex<kwd_eq>() ? Sass_OP::EQ
|
1181
|
-
: lex<kwd_neq>() ? Sass_OP::NEQ
|
1182
|
-
: lex<kwd_gte>() ? Sass_OP::GTE
|
1183
|
-
: lex<kwd_lte>() ? Sass_OP::LTE
|
1184
|
-
: lex<kwd_gt>() ? Sass_OP::GT
|
1185
|
-
: lex<kwd_lt>() ? Sass_OP::LT
|
1186
|
-
// we checked the possibilites on top of fn
|
1187
|
-
: Sass_OP::EQ;
|
1188
|
-
// parse the right hand side expression
|
1189
|
-
Expression* rhs = parse_expression();
|
1190
|
-
// return binary expression with a left and a right hand side
|
1191
|
-
return SASS_MEMORY_NEW(ctx.mem, Binary_Expression, lhs->pstate(), op, lhs, rhs);
|
1225
|
+
return fold_operands(lhs, operands, operators);
|
1192
1226
|
}
|
1193
1227
|
// parse_relation
|
1194
1228
|
|
@@ -1199,20 +1233,37 @@ namespace Sass {
|
|
1199
1233
|
// parse addition and subtraction operations
|
1200
1234
|
Expression* Parser::parse_expression()
|
1201
1235
|
{
|
1236
|
+
// parses multiple add and subtract operations
|
1237
|
+
// NOTE: make sure that identifiers starting with
|
1238
|
+
// NOTE: dashes do NOT count as subtract operation
|
1202
1239
|
Expression* lhs = parse_operators();
|
1203
1240
|
// if it's a singleton, return it (don't wrap it)
|
1204
|
-
if (
|
1241
|
+
// if it's a singleton, return it (don't wrap it)
|
1242
|
+
if (!(peek_css< exactly<'+'> >(position) ||
|
1205
1243
|
// condition is a bit misterious, but some combinations should not be counted as operations
|
1206
1244
|
(peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) ||
|
1207
1245
|
(peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) ||
|
1208
|
-
peek< identifier >(position))
|
1246
|
+
peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position))
|
1209
1247
|
{ return lhs; }
|
1210
1248
|
|
1211
1249
|
std::vector<Expression*> operands;
|
1212
|
-
std::vector<
|
1213
|
-
|
1214
|
-
|
1250
|
+
std::vector<Operand> operators;
|
1251
|
+
bool left_ws = peek < css_comments >() != NULL;
|
1252
|
+
while (
|
1253
|
+
lex_css< exactly<'+'> >() ||
|
1254
|
+
|
1255
|
+
(
|
1256
|
+
! peek_css< sequence < zero_plus < exactly <'-' > >, identifier > >(position)
|
1257
|
+
&& lex_css< sequence< negate< digit >, exactly<'-'> > >()
|
1258
|
+
)
|
1259
|
+
|
1260
|
+
) {
|
1261
|
+
|
1262
|
+
|
1263
|
+
bool right_ws = peek < css_comments >() != NULL;
|
1264
|
+
operators.push_back({ lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB, left_ws, right_ws });
|
1215
1265
|
operands.push_back(parse_operators());
|
1266
|
+
left_ws = peek < css_comments >() != NULL;
|
1216
1267
|
}
|
1217
1268
|
|
1218
1269
|
if (operands.size() == 0) return lhs;
|
@@ -1223,25 +1274,21 @@ namespace Sass {
|
|
1223
1274
|
Expression* Parser::parse_operators()
|
1224
1275
|
{
|
1225
1276
|
Expression* factor = parse_factor();
|
1226
|
-
// Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
|
1227
|
-
if (peek_css< exactly<'%'> >() && factor->concrete_type() == Expression::STRING) {
|
1228
|
-
String_Schema* ss = dynamic_cast<String_Schema*>(factor);
|
1229
|
-
if (ss && ss->has_interpolants()) return factor;
|
1230
|
-
}
|
1231
1277
|
// if it's a singleton, return it (don't wrap it)
|
1232
|
-
if (!peek_css< class_char< static_ops > >()) return factor;
|
1233
|
-
// parse more factors and operators
|
1234
1278
|
std::vector<Expression*> operands; // factors
|
1235
|
-
std::vector<
|
1279
|
+
std::vector<Operand> operators; // ops
|
1236
1280
|
// lex operations to apply to lhs
|
1281
|
+
const char* left_ws = peek < css_comments >();
|
1237
1282
|
while (lex_css< class_char< static_ops > >()) {
|
1283
|
+
const char* right_ws = peek < css_comments >();
|
1238
1284
|
switch(*lexed.begin) {
|
1239
|
-
case '*': operators.push_back(Sass_OP::MUL); break;
|
1240
|
-
case '/': operators.push_back(Sass_OP::DIV); break;
|
1241
|
-
case '%': operators.push_back(Sass_OP::MOD); break;
|
1285
|
+
case '*': operators.push_back({ Sass_OP::MUL, left_ws != 0, right_ws != 0 }); break;
|
1286
|
+
case '/': operators.push_back({ Sass_OP::DIV, left_ws != 0, right_ws != 0 }); break;
|
1287
|
+
case '%': operators.push_back({ Sass_OP::MOD, left_ws != 0, right_ws != 0 }); break;
|
1242
1288
|
default: throw std::runtime_error("unknown static op parsed"); break;
|
1243
1289
|
}
|
1244
1290
|
operands.push_back(parse_factor());
|
1291
|
+
left_ws = peek < css_comments >();
|
1245
1292
|
}
|
1246
1293
|
// operands and operators to binary expression
|
1247
1294
|
return fold_operands(factor, operands, operators);
|
@@ -1260,18 +1307,21 @@ namespace Sass {
|
|
1260
1307
|
// lex the expected closing parenthesis
|
1261
1308
|
if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);
|
1262
1309
|
// expression can be evaluated
|
1263
|
-
|
1310
|
+
// make sure wrapped lists and division expressions are non-delayed within parentheses
|
1264
1311
|
// make sure wrapped lists and division expressions are non-delayed within parentheses
|
1265
1312
|
if (value->concrete_type() == Expression::LIST) {
|
1266
|
-
List* l = static_cast<List*>(value);
|
1267
|
-
if (!l->empty()) (*l)[0]->is_delayed(false);
|
1313
|
+
// List* l = static_cast<List*>(value);
|
1314
|
+
// if (!l->empty()) (*l)[0]->is_delayed(false);
|
1268
1315
|
} else if (typeid(*value) == typeid(Binary_Expression)) {
|
1269
1316
|
Binary_Expression* b = static_cast<Binary_Expression*>(value);
|
1270
|
-
|
1271
|
-
if (lhs && lhs->type() == Sass_OP::DIV) lhs->is_delayed(false);
|
1317
|
+
if (b && b->type() == Sass_OP::DIV) b->set_delayed(false);
|
1272
1318
|
}
|
1273
1319
|
return value;
|
1274
1320
|
}
|
1321
|
+
// string may be interpolated
|
1322
|
+
// if (lex< quoted_string >()) {
|
1323
|
+
// return parse_string();
|
1324
|
+
// }
|
1275
1325
|
else if (peek< ie_property >()) {
|
1276
1326
|
return parse_ie_property();
|
1277
1327
|
}
|
@@ -1332,6 +1382,17 @@ namespace Sass {
|
|
1332
1382
|
if (lex< kwd_important >())
|
1333
1383
|
{ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); }
|
1334
1384
|
|
1385
|
+
// parse `10%4px` into separated items and not a schema
|
1386
|
+
if (lex< sequence < percentage, lookahead < number > > >())
|
1387
|
+
{ return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); }
|
1388
|
+
|
1389
|
+
if (lex< sequence < number, lookahead< sequence < op, number > > > >())
|
1390
|
+
{ return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); }
|
1391
|
+
|
1392
|
+
// string may be interpolated
|
1393
|
+
if (lex< sequence < quoted_string, lookahead < exactly <'-'> > > >())
|
1394
|
+
{ return parse_string(); }
|
1395
|
+
|
1335
1396
|
if (const char* stop = peek< value_schema >())
|
1336
1397
|
{ return parse_value_schema(stop); }
|
1337
1398
|
|
@@ -1363,7 +1424,8 @@ namespace Sass {
|
|
1363
1424
|
{ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); }
|
1364
1425
|
|
1365
1426
|
// also handle the 10em- foo special case
|
1366
|
-
|
1427
|
+
// alternatives < exactly < '.' >, .. > -- `1.5em-.75em` is split into a list, not a binary expression
|
1428
|
+
if (lex< sequence< dimension, optional< sequence< exactly<'-'>, lookahead< alternatives < space > > > > > >())
|
1367
1429
|
{ return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); }
|
1368
1430
|
|
1369
1431
|
if (lex< sequence< static_component, one_plus< strict_identifier > > >())
|
@@ -1379,7 +1441,7 @@ namespace Sass {
|
|
1379
1441
|
if (lex< sequence< exactly<'%'>, optional< percentage > > >())
|
1380
1442
|
{ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }
|
1381
1443
|
|
1382
|
-
|
1444
|
+
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1383
1445
|
|
1384
1446
|
// unreachable statement
|
1385
1447
|
return 0;
|
@@ -1391,7 +1453,9 @@ namespace Sass {
|
|
1391
1453
|
{
|
1392
1454
|
const char* i = chunk.begin;
|
1393
1455
|
// see if there any interpolants
|
1394
|
-
const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end)
|
1456
|
+
const char* p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) :
|
1457
|
+
find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end);
|
1458
|
+
|
1395
1459
|
if (!p) {
|
1396
1460
|
String_Quoted* str_quoted = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(i, chunk.end));
|
1397
1461
|
if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');
|
@@ -1400,8 +1464,10 @@ namespace Sass {
|
|
1400
1464
|
}
|
1401
1465
|
|
1402
1466
|
String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
|
1467
|
+
schema->is_interpolant(true);
|
1403
1468
|
while (i < chunk.end) {
|
1404
|
-
p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end)
|
1469
|
+
p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) :
|
1470
|
+
find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end);
|
1405
1471
|
if (p) {
|
1406
1472
|
if (i < p) {
|
1407
1473
|
// accumulate the preceding segment if it's nonempty
|
@@ -1415,7 +1481,7 @@ namespace Sass {
|
|
1415
1481
|
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
|
1416
1482
|
if (j) { --j;
|
1417
1483
|
// parse the interpolant and accumulate it
|
1418
|
-
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
|
1484
|
+
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list();
|
1419
1485
|
interp_node->is_interpolant(true);
|
1420
1486
|
(*schema) << interp_node;
|
1421
1487
|
i = j;
|
@@ -1467,14 +1533,14 @@ namespace Sass {
|
|
1467
1533
|
Token str(lexed);
|
1468
1534
|
const char* i = str.begin;
|
1469
1535
|
// see if there any interpolants
|
1470
|
-
const char* p = find_first_in_interval< exactly<hash_lbrace
|
1536
|
+
const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(str.begin, str.end);
|
1471
1537
|
if (!p) {
|
1472
1538
|
return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(str.begin, str.end));
|
1473
1539
|
}
|
1474
1540
|
|
1475
1541
|
String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
|
1476
1542
|
while (i < str.end) {
|
1477
|
-
p = find_first_in_interval< exactly<hash_lbrace
|
1543
|
+
p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, str.end);
|
1478
1544
|
if (p) {
|
1479
1545
|
if (i < p) {
|
1480
1546
|
(*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // accumulate the preceding segment if it's nonempty
|
@@ -1485,7 +1551,7 @@ namespace Sass {
|
|
1485
1551
|
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
|
1486
1552
|
if (j) {
|
1487
1553
|
// parse the interpolant and accumulate it
|
1488
|
-
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
|
1554
|
+
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list();
|
1489
1555
|
interp_node->is_interpolant(true);
|
1490
1556
|
(*schema) << interp_node;
|
1491
1557
|
i = j;
|
@@ -1533,9 +1599,14 @@ namespace Sass {
|
|
1533
1599
|
|
1534
1600
|
const char* e = 0;
|
1535
1601
|
size_t num_items = 0;
|
1602
|
+
bool need_space = false;
|
1536
1603
|
while (position < stop) {
|
1537
1604
|
// parse space between tokens
|
1538
1605
|
if (lex< spaces >() && num_items) {
|
1606
|
+
need_space = true;
|
1607
|
+
}
|
1608
|
+
if (need_space) {
|
1609
|
+
need_space = false;
|
1539
1610
|
(*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");
|
1540
1611
|
}
|
1541
1612
|
if ((e = peek< re_functional >()) && e < stop) {
|
@@ -1547,27 +1618,35 @@ namespace Sass {
|
|
1547
1618
|
if (peek< exactly< rbrace > >()) {
|
1548
1619
|
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1549
1620
|
}
|
1621
|
+
Expression* ex = 0;
|
1550
1622
|
if (lex< re_static_expression >()) {
|
1551
|
-
|
1623
|
+
ex = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
|
1552
1624
|
} else {
|
1553
|
-
|
1625
|
+
ex = parse_list();
|
1554
1626
|
}
|
1627
|
+
ex->is_interpolant(true);
|
1628
|
+
(*schema) << ex;
|
1555
1629
|
// ToDo: no error check here?
|
1556
1630
|
lex < exactly < rbrace > >();
|
1557
1631
|
}
|
1558
|
-
// lex some string constants
|
1559
|
-
|
1632
|
+
// lex some string constants or other valid token
|
1633
|
+
// Note: [-+] chars are left over from ie. `#{3}+3`
|
1634
|
+
else if (lex< alternatives < exactly<'%'>, exactly < '-' >, exactly < '+' > > >()) {
|
1635
|
+
(*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
|
1636
|
+
}
|
1637
|
+
else if (lex< sequence < identifier > >()) {
|
1560
1638
|
(*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
|
1561
|
-
if (*position == '"' || *position == '\'') {
|
1562
|
-
|
1639
|
+
if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) {
|
1640
|
+
need_space = true;
|
1563
1641
|
}
|
1564
1642
|
}
|
1565
1643
|
// lex a quoted string
|
1566
1644
|
else if (lex< quoted_string >()) {
|
1567
1645
|
(*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed, '"');
|
1568
|
-
if (*position == '"' || *position == '\'' || alpha(
|
1569
|
-
|
1646
|
+
if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) {
|
1647
|
+
need_space = true;
|
1570
1648
|
}
|
1649
|
+
if (peek < exactly < '-' > >()) return schema;
|
1571
1650
|
}
|
1572
1651
|
// lex (normalized) variable
|
1573
1652
|
else if (lex< variable >()) {
|
@@ -1612,14 +1691,14 @@ namespace Sass {
|
|
1612
1691
|
Token id(lexed);
|
1613
1692
|
const char* i = id.begin;
|
1614
1693
|
// see if there any interpolants
|
1615
|
-
const char* p = find_first_in_interval< exactly<hash_lbrace
|
1694
|
+
const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(id.begin, id.end);
|
1616
1695
|
if (!p) {
|
1617
1696
|
return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(id.begin, id.end));
|
1618
1697
|
}
|
1619
1698
|
|
1620
1699
|
String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
|
1621
1700
|
while (i < id.end) {
|
1622
|
-
p = find_first_in_interval< exactly<hash_lbrace
|
1701
|
+
p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, id.end);
|
1623
1702
|
if (p) {
|
1624
1703
|
if (i < p) {
|
1625
1704
|
// accumulate the preceding segment if it's nonempty
|
@@ -1635,10 +1714,10 @@ namespace Sass {
|
|
1635
1714
|
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace
|
1636
1715
|
if (j) {
|
1637
1716
|
// parse the interpolant and accumulate it
|
1638
|
-
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
|
1717
|
+
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list();
|
1639
1718
|
interp_node->is_interpolant(true);
|
1640
1719
|
(*schema) << interp_node;
|
1641
|
-
schema->has_interpolants(true);
|
1720
|
+
// schema->has_interpolants(true);
|
1642
1721
|
i = j;
|
1643
1722
|
}
|
1644
1723
|
else {
|
@@ -1682,13 +1761,38 @@ namespace Sass {
|
|
1682
1761
|
|
1683
1762
|
String* Parser::parse_url_function_string()
|
1684
1763
|
{
|
1685
|
-
|
1764
|
+
std::string prefix("");
|
1765
|
+
if (lex< uri_prefix >()) {
|
1766
|
+
prefix = std::string(lexed);
|
1767
|
+
}
|
1768
|
+
|
1769
|
+
String* url_string = parse_url_function_argument();
|
1770
|
+
|
1771
|
+
std::string suffix("");
|
1772
|
+
if (lex< real_uri_suffix >()) {
|
1773
|
+
suffix = std::string(lexed);
|
1774
|
+
}
|
1775
|
+
|
1776
|
+
if (String_Schema* schema = dynamic_cast<String_Schema*>(url_string)) {
|
1777
|
+
String_Schema* res = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
|
1778
|
+
(*res) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, prefix);
|
1779
|
+
(*res) += schema;
|
1780
|
+
(*res) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, suffix);
|
1781
|
+
return res;
|
1782
|
+
} else {
|
1783
|
+
std::string res = prefix + url_string->to_string({ NESTED, 5 }) + suffix;
|
1784
|
+
return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res);
|
1785
|
+
}
|
1786
|
+
}
|
1686
1787
|
|
1687
|
-
|
1688
|
-
|
1788
|
+
String* Parser::parse_url_function_argument()
|
1789
|
+
{
|
1790
|
+
const char* p = position;
|
1689
1791
|
|
1690
|
-
|
1691
|
-
|
1792
|
+
std::string uri("");
|
1793
|
+
if (lex< real_uri_value >(false)) {
|
1794
|
+
uri = lexed.to_string();
|
1795
|
+
}
|
1692
1796
|
|
1693
1797
|
if (peek< exactly< hash_lbrace > >()) {
|
1694
1798
|
const char* pp = position;
|
@@ -1696,14 +1800,15 @@ namespace Sass {
|
|
1696
1800
|
while (peek< exactly< hash_lbrace > >(pp)) {
|
1697
1801
|
pp = sequence< interpolant, real_uri_value >(pp);
|
1698
1802
|
}
|
1699
|
-
position =
|
1803
|
+
position = pp;
|
1700
1804
|
return parse_interpolated_chunk(Token(p, position));
|
1701
|
-
}
|
1702
|
-
|
1703
|
-
std::string res =
|
1805
|
+
}
|
1806
|
+
else if (uri != "") {
|
1807
|
+
std::string res = Util::rtrim(uri);
|
1704
1808
|
return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res);
|
1705
1809
|
}
|
1706
1810
|
|
1811
|
+
return 0;
|
1707
1812
|
}
|
1708
1813
|
|
1709
1814
|
Function_Call* Parser::parse_function_call()
|
@@ -1727,7 +1832,14 @@ namespace Sass {
|
|
1727
1832
|
|
1728
1833
|
Content* Parser::parse_content_directive()
|
1729
1834
|
{
|
1730
|
-
|
1835
|
+
bool missing_mixin_parent = true;
|
1836
|
+
for (auto parent : stack) {
|
1837
|
+
if (parent == Scope::Mixin) {
|
1838
|
+
missing_mixin_parent = false;
|
1839
|
+
break;
|
1840
|
+
}
|
1841
|
+
}
|
1842
|
+
if (missing_mixin_parent) {
|
1731
1843
|
error("@content may only be used within a mixin", pstate);
|
1732
1844
|
}
|
1733
1845
|
return SASS_MEMORY_NEW(ctx.mem, Content, pstate);
|
@@ -1735,6 +1847,7 @@ namespace Sass {
|
|
1735
1847
|
|
1736
1848
|
If* Parser::parse_if_directive(bool else_if)
|
1737
1849
|
{
|
1850
|
+
stack.push_back(Scope::Control);
|
1738
1851
|
ParserState if_source_position = pstate;
|
1739
1852
|
Expression* predicate = parse_list();
|
1740
1853
|
predicate->is_delayed(false);
|
@@ -1750,11 +1863,13 @@ namespace Sass {
|
|
1750
1863
|
else if (lex_css< kwd_else_directive >()) {
|
1751
1864
|
alternative = parse_block();
|
1752
1865
|
}
|
1866
|
+
stack.pop_back();
|
1753
1867
|
return SASS_MEMORY_NEW(ctx.mem, If, if_source_position, predicate, block, alternative);
|
1754
1868
|
}
|
1755
1869
|
|
1756
1870
|
For* Parser::parse_for_directive()
|
1757
1871
|
{
|
1872
|
+
stack.push_back(Scope::Control);
|
1758
1873
|
ParserState for_source_position = pstate;
|
1759
1874
|
lex_variable();
|
1760
1875
|
std::string var(Util::normalize_underscores(lexed));
|
@@ -1768,6 +1883,7 @@ namespace Sass {
|
|
1768
1883
|
Expression* upper_bound = parse_expression();
|
1769
1884
|
upper_bound->is_delayed(false);
|
1770
1885
|
Block* body = parse_block();
|
1886
|
+
stack.pop_back();
|
1771
1887
|
return SASS_MEMORY_NEW(ctx.mem, For, for_source_position, var, lower_bound, upper_bound, body, inclusive);
|
1772
1888
|
}
|
1773
1889
|
|
@@ -1799,6 +1915,7 @@ namespace Sass {
|
|
1799
1915
|
|
1800
1916
|
Each* Parser::parse_each_directive()
|
1801
1917
|
{
|
1918
|
+
stack.push_back(Scope::Control);
|
1802
1919
|
ParserState each_source_position = pstate;
|
1803
1920
|
std::vector<std::string> vars;
|
1804
1921
|
lex_variable();
|
@@ -1817,12 +1934,14 @@ namespace Sass {
|
|
1817
1934
|
}
|
1818
1935
|
}
|
1819
1936
|
Block* body = parse_block();
|
1937
|
+
stack.pop_back();
|
1820
1938
|
return SASS_MEMORY_NEW(ctx.mem, Each, each_source_position, vars, list, body);
|
1821
1939
|
}
|
1822
1940
|
|
1823
1941
|
// called after parsing `kwd_while_directive`
|
1824
1942
|
While* Parser::parse_while_directive()
|
1825
1943
|
{
|
1944
|
+
stack.push_back(Scope::Control);
|
1826
1945
|
// create the initial while call object
|
1827
1946
|
While* call = SASS_MEMORY_NEW(ctx.mem, While, pstate, 0, 0);
|
1828
1947
|
// parse mandatory predicate
|
@@ -1832,12 +1951,15 @@ namespace Sass {
|
|
1832
1951
|
// parse mandatory block
|
1833
1952
|
call->block(parse_block());
|
1834
1953
|
// return ast node
|
1954
|
+
stack.pop_back();
|
1955
|
+
// return ast node
|
1835
1956
|
return call;
|
1836
1957
|
}
|
1837
1958
|
|
1838
1959
|
// EO parse_while_directive
|
1839
1960
|
Media_Block* Parser::parse_media_block()
|
1840
1961
|
{
|
1962
|
+
stack.push_back(Scope::Media);
|
1841
1963
|
Media_Block* media_block = SASS_MEMORY_NEW(ctx.mem, Media_Block, pstate, 0, 0);
|
1842
1964
|
media_block->media_queries(parse_media_queries());
|
1843
1965
|
|
@@ -1845,7 +1967,7 @@ namespace Sass {
|
|
1845
1967
|
last_media_block = media_block;
|
1846
1968
|
media_block->block(parse_css_block());
|
1847
1969
|
last_media_block = prev_media_block;
|
1848
|
-
|
1970
|
+
stack.pop_back();
|
1849
1971
|
return media_block;
|
1850
1972
|
}
|
1851
1973
|
|
@@ -1898,10 +2020,10 @@ namespace Sass {
|
|
1898
2020
|
}
|
1899
2021
|
feature = parse_expression();
|
1900
2022
|
Expression* expression = 0;
|
1901
|
-
if (
|
2023
|
+
if (lex_css< exactly<':'> >()) {
|
1902
2024
|
expression = parse_list();
|
1903
2025
|
}
|
1904
|
-
if (!
|
2026
|
+
if (!lex_css< exactly<')'> >()) {
|
1905
2027
|
error("unclosed parenthesis in media query expression", pstate);
|
1906
2028
|
}
|
1907
2029
|
return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, feature->pstate(), feature, expression);
|
@@ -2010,6 +2132,7 @@ namespace Sass {
|
|
2010
2132
|
Block* body = 0;
|
2011
2133
|
At_Root_Expression* expr = 0;
|
2012
2134
|
Lookahead lookahead_result;
|
2135
|
+
// stack.push_back(Scope::Root);
|
2013
2136
|
LOCAL_FLAG(in_at_root, true);
|
2014
2137
|
if (lex< exactly<'('> >()) {
|
2015
2138
|
expr = parse_at_root_expression();
|
@@ -2024,6 +2147,7 @@ namespace Sass {
|
|
2024
2147
|
}
|
2025
2148
|
At_Root_Block* at_root = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, at_source_position, body);
|
2026
2149
|
if (expr) at_root->expression(expr);
|
2150
|
+
// stack.pop_back();
|
2027
2151
|
return at_root;
|
2028
2152
|
}
|
2029
2153
|
|
@@ -2082,16 +2206,37 @@ namespace Sass {
|
|
2082
2206
|
|
2083
2207
|
Warning* Parser::parse_warning()
|
2084
2208
|
{
|
2209
|
+
if (stack.back() != Scope::Root &&
|
2210
|
+
stack.back() != Scope::Function &&
|
2211
|
+
stack.back() != Scope::Mixin &&
|
2212
|
+
stack.back() != Scope::Control &&
|
2213
|
+
stack.back() != Scope::Rules) {
|
2214
|
+
error("Illegal nesting: Only properties may be nested beneath properties.", pstate);
|
2215
|
+
}
|
2085
2216
|
return SASS_MEMORY_NEW(ctx.mem, Warning, pstate, parse_list());
|
2086
2217
|
}
|
2087
2218
|
|
2088
2219
|
Error* Parser::parse_error()
|
2089
2220
|
{
|
2221
|
+
if (stack.back() != Scope::Root &&
|
2222
|
+
stack.back() != Scope::Function &&
|
2223
|
+
stack.back() != Scope::Mixin &&
|
2224
|
+
stack.back() != Scope::Control &&
|
2225
|
+
stack.back() != Scope::Rules) {
|
2226
|
+
error("Illegal nesting: Only properties may be nested beneath properties.", pstate);
|
2227
|
+
}
|
2090
2228
|
return SASS_MEMORY_NEW(ctx.mem, Error, pstate, parse_list());
|
2091
2229
|
}
|
2092
2230
|
|
2093
2231
|
Debug* Parser::parse_debug()
|
2094
2232
|
{
|
2233
|
+
if (stack.back() != Scope::Root &&
|
2234
|
+
stack.back() != Scope::Function &&
|
2235
|
+
stack.back() != Scope::Mixin &&
|
2236
|
+
stack.back() != Scope::Control &&
|
2237
|
+
stack.back() != Scope::Rules) {
|
2238
|
+
error("Illegal nesting: Only properties may be nested beneath properties.", pstate);
|
2239
|
+
}
|
2095
2240
|
return SASS_MEMORY_NEW(ctx.mem, Debug, pstate, parse_list());
|
2096
2241
|
}
|
2097
2242
|
|
@@ -2113,57 +2258,69 @@ namespace Sass {
|
|
2113
2258
|
rv.error = p;
|
2114
2259
|
if (const char* q =
|
2115
2260
|
peek <
|
2116
|
-
|
2117
|
-
|
2118
|
-
|
2119
|
-
|
2120
|
-
|
2121
|
-
|
2122
|
-
schema_reference_combinator,
|
2123
|
-
// match selector ops /[*&%,()\[\]]/
|
2124
|
-
class_char < selector_lookahead_ops >,
|
2125
|
-
// match selector combinators /[>+~]/
|
2126
|
-
class_char < selector_combinator_ops >,
|
2127
|
-
// match attribute compare operators
|
2128
|
-
alternatives <
|
2129
|
-
exact_match, class_match, dash_match,
|
2130
|
-
prefix_match, suffix_match, substring_match
|
2261
|
+
alternatives <
|
2262
|
+
// partial bem selector
|
2263
|
+
sequence <
|
2264
|
+
ampersand,
|
2265
|
+
one_plus <
|
2266
|
+
exactly < '-' >
|
2131
2267
|
>,
|
2132
|
-
|
2133
|
-
|
2134
|
-
|
2135
|
-
|
2136
|
-
|
2268
|
+
word_boundary
|
2269
|
+
>,
|
2270
|
+
// main selector matching
|
2271
|
+
one_plus <
|
2272
|
+
alternatives <
|
2273
|
+
// consume whitespace and comments
|
2274
|
+
spaces, block_comment, line_comment,
|
2275
|
+
// match `/deep/` selector (pass-trough)
|
2276
|
+
// there is no functionality for it yet
|
2277
|
+
schema_reference_combinator,
|
2278
|
+
// match selector ops /[*&%,()\[\]]/
|
2279
|
+
class_char < selector_lookahead_ops >,
|
2280
|
+
// match selector combinators /[>+~]/
|
2281
|
+
class_char < selector_combinator_ops >,
|
2282
|
+
// match attribute compare operators
|
2137
2283
|
alternatives <
|
2138
|
-
|
2139
|
-
|
2140
|
-
// not for interpolation
|
2141
|
-
negate < exactly <'{'> >
|
2142
|
-
>,
|
2143
|
-
// class match
|
2144
|
-
exactly <'.'>,
|
2145
|
-
// single or double colon
|
2146
|
-
optional < pseudo_prefix >
|
2284
|
+
exact_match, class_match, dash_match,
|
2285
|
+
prefix_match, suffix_match, substring_match
|
2147
2286
|
>,
|
2148
|
-
//
|
2149
|
-
|
2150
|
-
//
|
2151
|
-
|
2152
|
-
//
|
2287
|
+
// main selector match
|
2288
|
+
sequence <
|
2289
|
+
// allow namespace prefix
|
2290
|
+
optional < namespace_schema >,
|
2291
|
+
// modifiers prefixes
|
2153
2292
|
alternatives <
|
2154
|
-
|
2155
|
-
|
2156
|
-
|
2157
|
-
|
2158
|
-
|
2159
|
-
|
2160
|
-
|
2161
|
-
|
2162
|
-
|
2163
|
-
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2293
|
+
sequence <
|
2294
|
+
exactly <'#'>,
|
2295
|
+
// not for interpolation
|
2296
|
+
negate < exactly <'{'> >
|
2297
|
+
>,
|
2298
|
+
// class match
|
2299
|
+
exactly <'.'>,
|
2300
|
+
// single or double colon
|
2301
|
+
optional < pseudo_prefix >
|
2302
|
+
>,
|
2303
|
+
// accept hypens in token
|
2304
|
+
one_plus < sequence <
|
2305
|
+
// can start with hyphens
|
2306
|
+
zero_plus < exactly<'-'> >,
|
2307
|
+
// now the main token
|
2308
|
+
alternatives <
|
2309
|
+
kwd_optional,
|
2310
|
+
exactly <'*'>,
|
2311
|
+
quoted_string,
|
2312
|
+
interpolant,
|
2313
|
+
identifier,
|
2314
|
+
variable,
|
2315
|
+
percentage,
|
2316
|
+
binomial,
|
2317
|
+
dimension,
|
2318
|
+
alnum
|
2319
|
+
>
|
2320
|
+
> >,
|
2321
|
+
// can also end with hyphens
|
2322
|
+
zero_plus < exactly<'-'> >
|
2323
|
+
>
|
2167
2324
|
>
|
2168
2325
|
>
|
2169
2326
|
>
|
@@ -2346,15 +2503,15 @@ namespace Sass {
|
|
2346
2503
|
}
|
2347
2504
|
|
2348
2505
|
|
2349
|
-
Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands,
|
2506
|
+
Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, Operand op)
|
2350
2507
|
{
|
2351
2508
|
for (size_t i = 0, S = operands.size(); i < S; ++i) {
|
2352
2509
|
base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]);
|
2353
2510
|
Binary_Expression* b = static_cast<Binary_Expression*>(base);
|
2354
|
-
if (op == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
2511
|
+
if (op.operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
2355
2512
|
base->is_delayed(true);
|
2356
2513
|
}
|
2357
|
-
else {
|
2514
|
+
else if (b && b->op().operand != Sass_OP::DIV) {
|
2358
2515
|
b->left()->is_delayed(false);
|
2359
2516
|
b->right()->is_delayed(false);
|
2360
2517
|
}
|
@@ -2362,18 +2519,62 @@ namespace Sass {
|
|
2362
2519
|
return base;
|
2363
2520
|
}
|
2364
2521
|
|
2365
|
-
Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<
|
2366
|
-
{
|
2367
|
-
|
2368
|
-
|
2522
|
+
Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<Operand>& ops, size_t i)
|
2523
|
+
{
|
2524
|
+
|
2525
|
+
if (String_Schema* schema = dynamic_cast<String_Schema*>(base)) {
|
2526
|
+
// return schema;
|
2527
|
+
if (schema->has_interpolants()) {
|
2528
|
+
if (i + 1 < operands.size() && (
|
2529
|
+
(ops[0].operand == Sass_OP::EQ)
|
2530
|
+
|| (ops[0].operand == Sass_OP::ADD)
|
2531
|
+
|| (ops[0].operand == Sass_OP::DIV)
|
2532
|
+
|| (ops[0].operand == Sass_OP::MUL)
|
2533
|
+
|| (ops[0].operand == Sass_OP::NEQ)
|
2534
|
+
|| (ops[0].operand == Sass_OP::LT)
|
2535
|
+
|| (ops[0].operand == Sass_OP::GT)
|
2536
|
+
|| (ops[0].operand == Sass_OP::LTE)
|
2537
|
+
|| (ops[0].operand == Sass_OP::GTE)
|
2538
|
+
)) {
|
2539
|
+
Expression* rhs = fold_operands(operands[0], operands, ops, 1);
|
2540
|
+
rhs = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[0], schema, rhs);
|
2541
|
+
rhs->set_delayed(false);
|
2542
|
+
rhs->is_delayed(true);
|
2543
|
+
return rhs;
|
2544
|
+
}
|
2545
|
+
// return schema;
|
2546
|
+
}
|
2547
|
+
}
|
2548
|
+
|
2549
|
+
for (size_t S = operands.size(); i < S; ++i) {
|
2550
|
+
if (String_Schema* schema = dynamic_cast<String_Schema*>(operands[i])) {
|
2551
|
+
if (schema->has_interpolants()) {
|
2552
|
+
if (i + 1 < S) {
|
2553
|
+
Expression* rhs = fold_operands(operands[i+1], operands, ops, i + 2);
|
2554
|
+
rhs = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], schema, rhs);
|
2555
|
+
base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, rhs);
|
2556
|
+
rhs->is_delayed(true);
|
2557
|
+
base->is_delayed(true);
|
2558
|
+
return base;
|
2559
|
+
}
|
2560
|
+
base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);
|
2561
|
+
if (ops[i].operand != Sass_OP::DIV) base->is_delayed(true);
|
2562
|
+
return base;
|
2563
|
+
} else {
|
2564
|
+
base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);
|
2565
|
+
}
|
2566
|
+
} else {
|
2567
|
+
base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);
|
2568
|
+
}
|
2369
2569
|
Binary_Expression* b = static_cast<Binary_Expression*>(base);
|
2370
|
-
if (ops[i] == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
2570
|
+
if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
2371
2571
|
base->is_delayed(true);
|
2372
2572
|
}
|
2373
|
-
else {
|
2573
|
+
else if (b) {
|
2374
2574
|
b->left()->is_delayed(false);
|
2375
2575
|
b->right()->is_delayed(false);
|
2376
2576
|
}
|
2577
|
+
|
2377
2578
|
}
|
2378
2579
|
return base;
|
2379
2580
|
}
|
@@ -2387,22 +2588,37 @@ namespace Sass {
|
|
2387
2588
|
void Parser::css_error(const std::string& msg, const std::string& prefix, const std::string& middle)
|
2388
2589
|
{
|
2389
2590
|
int max_len = 18;
|
2591
|
+
const char* end = this->end;
|
2592
|
+
while (*end != 0) ++ end;
|
2390
2593
|
const char* pos = peek < optional_spaces >();
|
2391
2594
|
|
2392
|
-
const char* last_pos(pos
|
2595
|
+
const char* last_pos(pos);
|
2596
|
+
if (last_pos > source) {
|
2597
|
+
utf8::prior(last_pos, source);
|
2598
|
+
}
|
2393
2599
|
// backup position to last significant char
|
2394
|
-
while (
|
2600
|
+
while (last_pos > source && last_pos < end) {
|
2601
|
+
if (!Prelexer::is_space(*last_pos)) break;
|
2602
|
+
utf8::prior(last_pos, source);
|
2603
|
+
}
|
2395
2604
|
|
2396
2605
|
bool ellipsis_left = false;
|
2397
|
-
const char* pos_left(last_pos
|
2398
|
-
const char* end_left(last_pos
|
2606
|
+
const char* pos_left(last_pos);
|
2607
|
+
const char* end_left(last_pos);
|
2608
|
+
|
2609
|
+
utf8::next(pos_left, end);
|
2610
|
+
utf8::next(end_left, end);
|
2399
2611
|
while (pos_left > source) {
|
2400
|
-
if (end_left
|
2401
|
-
|
2612
|
+
if (utf8::distance(pos_left, end_left) >= max_len) {
|
2613
|
+
utf8::prior(pos_left, source);
|
2614
|
+
ellipsis_left = *(pos_left) != '\n' &&
|
2615
|
+
*(pos_left) != '\r';
|
2616
|
+
utf8::next(pos_left, end);
|
2402
2617
|
break;
|
2403
2618
|
}
|
2404
2619
|
|
2405
|
-
const char* prev = pos_left
|
2620
|
+
const char* prev = pos_left;
|
2621
|
+
utf8::prior(prev, source);
|
2406
2622
|
if (*prev == '\r') break;
|
2407
2623
|
if (*prev == '\n') break;
|
2408
2624
|
pos_left = prev;
|
@@ -2414,21 +2630,24 @@ namespace Sass {
|
|
2414
2630
|
bool ellipsis_right = false;
|
2415
2631
|
const char* end_right(pos);
|
2416
2632
|
const char* pos_right(pos);
|
2417
|
-
while (end_right
|
2418
|
-
if (end_right
|
2419
|
-
|
2633
|
+
while (end_right < end) {
|
2634
|
+
if (utf8::distance(pos_right, end_right) > max_len) {
|
2635
|
+
ellipsis_left = *(pos_right) != '\n' &&
|
2636
|
+
*(pos_right) != '\r';
|
2420
2637
|
break;
|
2421
2638
|
}
|
2422
2639
|
if (*end_right == '\r') break;
|
2423
2640
|
if (*end_right == '\n') break;
|
2424
|
-
|
2641
|
+
utf8::next(end_right, end);
|
2425
2642
|
}
|
2426
|
-
if (end_right
|
2643
|
+
// if (*end_right == 0) end_right ++;
|
2427
2644
|
|
2428
2645
|
std::string left(pos_left, end_left);
|
2429
2646
|
std::string right(pos_right, end_right);
|
2430
|
-
|
2431
|
-
|
2647
|
+
size_t left_subpos = left.size() > 15 ? left.size() - 15 : 0;
|
2648
|
+
size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0;
|
2649
|
+
if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos);
|
2650
|
+
if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis;
|
2432
2651
|
// now pass new message to the more generic error function
|
2433
2652
|
error(msg + prefix + quote(left) + middle + quote(right), pstate);
|
2434
2653
|
}
|