sassc 1.8.3 → 1.8.4

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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/ext/libsass/.editorconfig +1 -1
  4. data/ext/libsass/.gitignore +1 -0
  5. data/ext/libsass/LICENSE +1 -1
  6. data/ext/libsass/Makefile +20 -14
  7. data/ext/libsass/Makefile.conf +0 -1
  8. data/ext/libsass/Readme.md +3 -1
  9. data/ext/libsass/appveyor.yml +19 -11
  10. data/ext/libsass/docs/api-importer-example.md +2 -1235
  11. data/ext/libsass/docs/build-with-autotools.md +10 -0
  12. data/ext/libsass/docs/build-with-makefiles.md +18 -0
  13. data/ext/libsass/include/sass/base.h +4 -1
  14. data/ext/libsass/include/sass/values.h +2 -1
  15. data/ext/libsass/src/ast.cpp +279 -346
  16. data/ext/libsass/src/ast.hpp +234 -60
  17. data/ext/libsass/src/base64vlq.cpp +1 -0
  18. data/ext/libsass/src/bind.cpp +35 -45
  19. data/ext/libsass/src/bind.hpp +1 -0
  20. data/ext/libsass/src/color_maps.cpp +1 -0
  21. data/ext/libsass/src/constants.cpp +4 -1
  22. data/ext/libsass/src/constants.hpp +2 -1
  23. data/ext/libsass/src/context.cpp +41 -31
  24. data/ext/libsass/src/context.hpp +10 -10
  25. data/ext/libsass/src/cssize.cpp +7 -4
  26. data/ext/libsass/src/cssize.hpp +1 -3
  27. data/ext/libsass/src/debugger.hpp +73 -14
  28. data/ext/libsass/src/emitter.cpp +37 -25
  29. data/ext/libsass/src/emitter.hpp +10 -9
  30. data/ext/libsass/src/environment.cpp +16 -5
  31. data/ext/libsass/src/environment.hpp +5 -3
  32. data/ext/libsass/src/error_handling.cpp +91 -14
  33. data/ext/libsass/src/error_handling.hpp +105 -4
  34. data/ext/libsass/src/eval.cpp +519 -330
  35. data/ext/libsass/src/eval.hpp +12 -13
  36. data/ext/libsass/src/expand.cpp +92 -56
  37. data/ext/libsass/src/expand.hpp +5 -3
  38. data/ext/libsass/src/extend.cpp +60 -51
  39. data/ext/libsass/src/extend.hpp +1 -3
  40. data/ext/libsass/src/file.cpp +37 -27
  41. data/ext/libsass/src/functions.cpp +78 -62
  42. data/ext/libsass/src/functions.hpp +1 -0
  43. data/ext/libsass/src/inspect.cpp +293 -64
  44. data/ext/libsass/src/inspect.hpp +2 -0
  45. data/ext/libsass/src/lexer.cpp +1 -0
  46. data/ext/libsass/src/listize.cpp +14 -15
  47. data/ext/libsass/src/listize.hpp +3 -5
  48. data/ext/libsass/src/memory_manager.cpp +1 -0
  49. data/ext/libsass/src/node.cpp +2 -3
  50. data/ext/libsass/src/operation.hpp +70 -71
  51. data/ext/libsass/src/output.cpp +28 -32
  52. data/ext/libsass/src/output.hpp +1 -2
  53. data/ext/libsass/src/parser.cpp +402 -183
  54. data/ext/libsass/src/parser.hpp +19 -9
  55. data/ext/libsass/src/plugins.cpp +1 -0
  56. data/ext/libsass/src/position.cpp +1 -0
  57. data/ext/libsass/src/prelexer.cpp +134 -56
  58. data/ext/libsass/src/prelexer.hpp +51 -3
  59. data/ext/libsass/src/remove_placeholders.cpp +35 -9
  60. data/ext/libsass/src/remove_placeholders.hpp +4 -3
  61. data/ext/libsass/src/sass.cpp +1 -0
  62. data/ext/libsass/src/sass.hpp +129 -0
  63. data/ext/libsass/src/sass_context.cpp +31 -14
  64. data/ext/libsass/src/sass_context.hpp +2 -31
  65. data/ext/libsass/src/sass_functions.cpp +1 -0
  66. data/ext/libsass/src/sass_interface.cpp +5 -6
  67. data/ext/libsass/src/sass_util.cpp +1 -2
  68. data/ext/libsass/src/sass_util.hpp +5 -5
  69. data/ext/libsass/src/sass_values.cpp +13 -10
  70. data/ext/libsass/src/source_map.cpp +4 -3
  71. data/ext/libsass/src/source_map.hpp +2 -2
  72. data/ext/libsass/src/subset_map.hpp +0 -1
  73. data/ext/libsass/src/to_c.cpp +1 -0
  74. data/ext/libsass/src/to_c.hpp +1 -3
  75. data/ext/libsass/src/to_value.cpp +3 -5
  76. data/ext/libsass/src/to_value.hpp +1 -1
  77. data/ext/libsass/src/units.cpp +96 -59
  78. data/ext/libsass/src/units.hpp +10 -8
  79. data/ext/libsass/src/utf8_string.cpp +5 -0
  80. data/ext/libsass/src/util.cpp +23 -156
  81. data/ext/libsass/src/util.hpp +10 -14
  82. data/ext/libsass/src/values.cpp +1 -0
  83. data/ext/libsass/test/test_node.cpp +2 -6
  84. data/ext/libsass/test/test_selector_difference.cpp +1 -3
  85. data/ext/libsass/test/test_specificity.cpp +0 -2
  86. data/ext/libsass/test/test_superselector.cpp +0 -2
  87. data/ext/libsass/test/test_unification.cpp +1 -3
  88. data/ext/libsass/win/libsass.targets +18 -5
  89. data/ext/libsass/win/libsass.vcxproj +9 -7
  90. data/ext/libsass/win/libsass.vcxproj.filters +148 -106
  91. data/lib/sassc/version.rb +1 -1
  92. data/test/engine_test.rb +12 -0
  93. data/test/native_test.rb +1 -1
  94. metadata +3 -4
  95. data/ext/libsass/src/to_string.cpp +0 -48
  96. data/ext/libsass/src/to_string.hpp +0 -38
@@ -23,8 +23,7 @@ namespace Sass {
23
23
  using Inspect::operator();
24
24
 
25
25
  public:
26
- // change to Emitter
27
- Output(Context* ctx);
26
+ Output(Sass_Output_Options& opt);
28
27
  virtual ~Output();
29
28
 
30
29
  protected:
@@ -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* str, Context& ctx, ParserState pstate)
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 = str;
26
- p.position = p.source;
27
- p.end = str + strlen(str);
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* src, Context& ctx, ParserState pstate)
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(src, ctx, pstate);
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
- while (lex< block_comment >()) {
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() == function_def) {
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
- if (stack.back() == mixin_def || stack.back() == function_def) {
213
- error("Import directives may not be used within control directives or mixins.", pstate);
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
- if (block->is_root()) {
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 (lex < uri_value >(false)) { // don't skip comments
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(mixin_def);
352
- else stack.push_back(function_def);
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) is_keyword = true;
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> >(i, end_of_selector)) {
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->has_reference() && !in_at_root && !in_root) {
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 (peek < re_pseudo_selector >())
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
- String* contents = parse_interpolated_chunk(lexed);
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
- Map* map = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);
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 (!peek< exactly<':'> >())
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) << std::make_pair(key, value);
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) << std::make_pair(key, value);
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
- if (!(peek< alternatives <
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
- { return lhs; }
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
- enum Sass_OP op
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 (!(peek< exactly<'+'> >(position) ||
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<Sass_OP> operators;
1213
- while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {
1214
- operators.push_back(lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB);
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<enum Sass_OP> operators; // ops
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
- value->is_delayed(false);
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
- Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());
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
- if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())
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
- error("error reading values after " + lexed.to_string(), pstate);
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> >(str.begin, str.end);
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> >(i, str.end);
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
- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1623
+ ex = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1552
1624
  } else {
1553
- (*schema) << parse_list();
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
- else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) {
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
- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");
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(position)) {
1569
- (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");
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> >(id.begin, id.end);
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> >(i, id.end);
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
- const char* p = position;
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
- lex< uri_prefix >();
1688
- std::string prefix = lexed;
1788
+ String* Parser::parse_url_function_argument()
1789
+ {
1790
+ const char* p = position;
1689
1791
 
1690
- lex< real_uri_value >(false);
1691
- std::string uri = lexed;
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 = peek< real_uri_suffix >(pp);
1803
+ position = pp;
1700
1804
  return parse_interpolated_chunk(Token(p, position));
1701
- } else {
1702
- lex< real_uri_suffix >();
1703
- std::string res = prefix + Util::rtrim(uri) + lexed.to_string();
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
- if (stack.back() != mixin_def) {
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 (lex< exactly<':'> >()) {
2023
+ if (lex_css< exactly<':'> >()) {
1902
2024
  expression = parse_list();
1903
2025
  }
1904
- if (!lex< exactly<')'> >()) {
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
- one_plus <
2117
- alternatives <
2118
- // consume whitespace and comments
2119
- spaces, block_comment, line_comment,
2120
- // match `/deep/` selector (pass-trough)
2121
- // there is no functionality for it yet
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
- // main selector match
2133
- sequence <
2134
- // allow namespace prefix
2135
- optional < namespace_schema >,
2136
- // modifiers prefixes
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
- sequence <
2139
- exactly <'#'>,
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
- // accept hypens in token
2149
- one_plus < sequence <
2150
- // can start with hyphens
2151
- zero_plus < exactly<'-'> >,
2152
- // now the main token
2287
+ // main selector match
2288
+ sequence <
2289
+ // allow namespace prefix
2290
+ optional < namespace_schema >,
2291
+ // modifiers prefixes
2153
2292
  alternatives <
2154
- kwd_optional,
2155
- exactly <'*'>,
2156
- quoted_string,
2157
- interpolant,
2158
- identifier,
2159
- percentage,
2160
- dimension,
2161
- variable,
2162
- alnum
2163
- >
2164
- > >,
2165
- // can also end with hyphens
2166
- zero_plus < exactly<'-'> >
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, enum Sass_OP op)
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<enum Sass_OP>& ops)
2366
- {
2367
- for (size_t i = 0, S = operands.size(); i < S; ++i) {
2368
- base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);
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 - 1);
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 ((!*last_pos || Prelexer::is_space(*last_pos)) && last_pos > source) -- last_pos;
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 + 1);
2398
- const char* end_left(last_pos + 1);
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 - pos_left >= max_len) {
2401
- ellipsis_left = true;
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 - 1;
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 <= end) {
2418
- if (end_right - pos_right > max_len) {
2419
- ellipsis_right = true;
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
- ++ end_right;
2641
+ utf8::next(end_right, end);
2425
2642
  }
2426
- if (end_right > end) end_right = end;
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
- if (ellipsis_left) left = ellipsis + left.substr(left.size() - 15);
2431
- if (ellipsis_right) right = right.substr(right.size() - 15) + ellipsis;
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
  }