sassc 1.8.3 → 1.8.4

Sign up to get free protection for your applications and to get access to all the features.
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
  }