sassc 1.7.1 → 1.8.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/ext/libsass/.gitignore +10 -6
  4. data/ext/libsass/.travis.yml +4 -1
  5. data/ext/libsass/GNUmakefile.am +88 -0
  6. data/ext/libsass/Makefile +157 -76
  7. data/ext/libsass/Makefile.conf +47 -0
  8. data/ext/libsass/Readme.md +13 -14
  9. data/ext/libsass/appveyor.yml +25 -41
  10. data/ext/libsass/configure.ac +20 -7
  11. data/ext/libsass/contrib/plugin.cpp +1 -1
  12. data/ext/libsass/include/sass.h +15 -0
  13. data/ext/libsass/{sass.h → include/sass/base.h} +17 -9
  14. data/ext/libsass/{sass_context.h → include/sass/context.h} +3 -1
  15. data/ext/libsass/{sass_functions.h → include/sass/functions.h} +4 -4
  16. data/ext/libsass/{sass_interface.h → include/sass/interface.h} +5 -2
  17. data/ext/libsass/{sass_values.h → include/sass/values.h} +15 -1
  18. data/ext/libsass/{sass_version.h → include/sass/version.h} +0 -0
  19. data/ext/libsass/{sass_version.h.in → include/sass/version.h.in} +0 -0
  20. data/ext/libsass/{sass2scss.h → include/sass2scss.h} +6 -7
  21. data/ext/libsass/m4/m4-ax_cxx_compile_stdcxx_11.m4 +167 -0
  22. data/ext/libsass/script/ci-build-libsass +67 -23
  23. data/ext/libsass/src/GNUmakefile.am +54 -0
  24. data/ext/libsass/src/ast.cpp +2029 -0
  25. data/ext/libsass/{ast.hpp → src/ast.hpp} +832 -660
  26. data/ext/libsass/src/ast_def_macros.hpp +47 -0
  27. data/ext/libsass/src/ast_factory.hpp +93 -0
  28. data/ext/libsass/{ast_fwd_decl.hpp → src/ast_fwd_decl.hpp} +9 -4
  29. data/ext/libsass/{b64 → src/b64}/cencode.h +1 -1
  30. data/ext/libsass/{b64 → src/b64}/encode.h +0 -0
  31. data/ext/libsass/{backtrace.hpp → src/backtrace.hpp} +9 -10
  32. data/ext/libsass/{base64vlq.cpp → src/base64vlq.cpp} +2 -2
  33. data/ext/libsass/{base64vlq.hpp → src/base64vlq.hpp} +1 -2
  34. data/ext/libsass/{bind.cpp → src/bind.cpp} +96 -59
  35. data/ext/libsass/{bind.hpp → src/bind.hpp} +1 -1
  36. data/ext/libsass/src/c99func.c +54 -0
  37. data/ext/libsass/{cencode.c → src/cencode.c} +5 -5
  38. data/ext/libsass/src/color_maps.cpp +643 -0
  39. data/ext/libsass/src/color_maps.hpp +333 -0
  40. data/ext/libsass/{constants.cpp → src/constants.cpp} +10 -1
  41. data/ext/libsass/{constants.hpp → src/constants.hpp} +7 -0
  42. data/ext/libsass/{context.cpp → src/context.cpp} +152 -122
  43. data/ext/libsass/src/context.hpp +150 -0
  44. data/ext/libsass/{cssize.cpp → src/cssize.cpp} +123 -109
  45. data/ext/libsass/{cssize.hpp → src/cssize.hpp} +9 -13
  46. data/ext/libsass/{debug.hpp → src/debug.hpp} +9 -9
  47. data/ext/libsass/src/debugger.hpp +683 -0
  48. data/ext/libsass/{emitter.cpp → src/emitter.cpp} +13 -13
  49. data/ext/libsass/{emitter.hpp → src/emitter.hpp} +10 -11
  50. data/ext/libsass/src/environment.cpp +184 -0
  51. data/ext/libsass/src/environment.hpp +92 -0
  52. data/ext/libsass/src/error_handling.cpp +46 -0
  53. data/ext/libsass/src/error_handling.hpp +34 -0
  54. data/ext/libsass/src/eval.cpp +1462 -0
  55. data/ext/libsass/src/eval.hpp +107 -0
  56. data/ext/libsass/src/expand.cpp +653 -0
  57. data/ext/libsass/{expand.hpp → src/expand.hpp} +17 -16
  58. data/ext/libsass/{extend.cpp → src/extend.cpp} +198 -139
  59. data/ext/libsass/{extend.hpp → src/extend.hpp} +7 -8
  60. data/ext/libsass/{file.cpp → src/file.cpp} +103 -57
  61. data/ext/libsass/{file.hpp → src/file.hpp} +23 -14
  62. data/ext/libsass/{functions.cpp → src/functions.cpp} +642 -333
  63. data/ext/libsass/{functions.hpp → src/functions.hpp} +17 -4
  64. data/ext/libsass/{inspect.cpp → src/inspect.cpp} +147 -260
  65. data/ext/libsass/{inspect.hpp → src/inspect.hpp} +7 -7
  66. data/ext/libsass/{json.cpp → src/json.cpp} +33 -43
  67. data/ext/libsass/{json.hpp → src/json.hpp} +1 -1
  68. data/ext/libsass/{kwd_arg_macros.hpp → src/kwd_arg_macros.hpp} +0 -0
  69. data/ext/libsass/{lexer.cpp → src/lexer.cpp} +28 -0
  70. data/ext/libsass/{lexer.hpp → src/lexer.hpp} +25 -10
  71. data/ext/libsass/{listize.cpp → src/listize.cpp} +17 -13
  72. data/ext/libsass/{listize.hpp → src/listize.hpp} +0 -2
  73. data/ext/libsass/{mapping.hpp → src/mapping.hpp} +0 -0
  74. data/ext/libsass/src/memory_manager.cpp +76 -0
  75. data/ext/libsass/src/memory_manager.hpp +48 -0
  76. data/ext/libsass/{node.cpp → src/node.cpp} +89 -18
  77. data/ext/libsass/{node.hpp → src/node.hpp} +5 -6
  78. data/ext/libsass/{operation.hpp → src/operation.hpp} +18 -12
  79. data/ext/libsass/{output.cpp → src/output.cpp} +47 -55
  80. data/ext/libsass/{output.hpp → src/output.hpp} +5 -4
  81. data/ext/libsass/src/parser.cpp +2529 -0
  82. data/ext/libsass/{parser.hpp → src/parser.hpp} +84 -60
  83. data/ext/libsass/{paths.hpp → src/paths.hpp} +10 -13
  84. data/ext/libsass/{plugins.cpp → src/plugins.cpp} +14 -17
  85. data/ext/libsass/{plugins.hpp → src/plugins.hpp} +10 -11
  86. data/ext/libsass/{position.cpp → src/position.cpp} +5 -6
  87. data/ext/libsass/{position.hpp → src/position.hpp} +19 -22
  88. data/ext/libsass/{prelexer.cpp → src/prelexer.cpp} +401 -53
  89. data/ext/libsass/{prelexer.hpp → src/prelexer.hpp} +50 -10
  90. data/ext/libsass/{remove_placeholders.cpp → src/remove_placeholders.cpp} +12 -16
  91. data/ext/libsass/{remove_placeholders.hpp → src/remove_placeholders.hpp} +1 -7
  92. data/ext/libsass/{sass.cpp → src/sass.cpp} +3 -5
  93. data/ext/libsass/{sass2scss.cpp → src/sass2scss.cpp} +51 -46
  94. data/ext/libsass/{sass_context.cpp → src/sass_context.cpp} +114 -112
  95. data/ext/libsass/{sass_functions.cpp → src/sass_functions.cpp} +11 -18
  96. data/ext/libsass/{sass_interface.cpp → src/sass_interface.cpp} +44 -81
  97. data/ext/libsass/{sass_util.cpp → src/sass_util.cpp} +26 -8
  98. data/ext/libsass/{sass_util.hpp → src/sass_util.hpp} +14 -18
  99. data/ext/libsass/{sass_values.cpp → src/sass_values.cpp} +91 -20
  100. data/ext/libsass/{source_map.cpp → src/source_map.cpp} +13 -13
  101. data/ext/libsass/{source_map.hpp → src/source_map.hpp} +9 -9
  102. data/ext/libsass/{subset_map.hpp → src/subset_map.hpp} +29 -31
  103. data/ext/libsass/{support → src/support}/libsass.pc.in +0 -0
  104. data/ext/libsass/src/to_c.cpp +73 -0
  105. data/ext/libsass/src/to_c.hpp +41 -0
  106. data/ext/libsass/src/to_string.cpp +47 -0
  107. data/ext/libsass/{to_string.hpp → src/to_string.hpp} +9 -7
  108. data/ext/libsass/src/to_value.cpp +109 -0
  109. data/ext/libsass/src/to_value.hpp +50 -0
  110. data/ext/libsass/{units.cpp → src/units.cpp} +56 -51
  111. data/ext/libsass/{units.hpp → src/units.hpp} +8 -9
  112. data/ext/libsass/{utf8.h → src/utf8.h} +0 -0
  113. data/ext/libsass/{utf8 → src/utf8}/checked.h +0 -0
  114. data/ext/libsass/{utf8 → src/utf8}/core.h +12 -12
  115. data/ext/libsass/{utf8 → src/utf8}/unchecked.h +0 -0
  116. data/ext/libsass/{utf8_string.cpp → src/utf8_string.cpp} +0 -0
  117. data/ext/libsass/{utf8_string.hpp → src/utf8_string.hpp} +6 -6
  118. data/ext/libsass/{util.cpp → src/util.cpp} +144 -86
  119. data/ext/libsass/src/util.hpp +59 -0
  120. data/ext/libsass/src/values.cpp +137 -0
  121. data/ext/libsass/src/values.hpp +12 -0
  122. data/ext/libsass/test/test_node.cpp +33 -33
  123. data/ext/libsass/test/test_paths.cpp +5 -6
  124. data/ext/libsass/test/test_selector_difference.cpp +4 -5
  125. data/ext/libsass/test/test_specificity.cpp +4 -5
  126. data/ext/libsass/test/test_subset_map.cpp +91 -91
  127. data/ext/libsass/test/test_superselector.cpp +11 -11
  128. data/ext/libsass/test/test_unification.cpp +4 -4
  129. data/ext/libsass/win/libsass.targets +101 -0
  130. data/ext/libsass/win/libsass.vcxproj +45 -127
  131. data/ext/libsass/win/libsass.vcxproj.filters +303 -0
  132. data/lib/sassc/import_handler.rb +1 -1
  133. data/lib/sassc/native/native_functions_api.rb +3 -3
  134. data/lib/sassc/version.rb +1 -1
  135. data/test/custom_importer_test.rb +1 -4
  136. data/test/functions_test.rb +3 -2
  137. data/test/native_test.rb +4 -3
  138. metadata +117 -110
  139. data/ext/libsass/Makefile.am +0 -146
  140. data/ext/libsass/ast.cpp +0 -945
  141. data/ext/libsass/ast_def_macros.hpp +0 -21
  142. data/ext/libsass/ast_factory.hpp +0 -92
  143. data/ext/libsass/color_names.hpp +0 -327
  144. data/ext/libsass/context.hpp +0 -157
  145. data/ext/libsass/contextualize.cpp +0 -148
  146. data/ext/libsass/contextualize.hpp +0 -46
  147. data/ext/libsass/contextualize_eval.cpp +0 -93
  148. data/ext/libsass/contextualize_eval.hpp +0 -44
  149. data/ext/libsass/debugger.hpp +0 -558
  150. data/ext/libsass/environment.hpp +0 -163
  151. data/ext/libsass/error_handling.cpp +0 -35
  152. data/ext/libsass/error_handling.hpp +0 -32
  153. data/ext/libsass/eval.cpp +0 -1392
  154. data/ext/libsass/eval.hpp +0 -88
  155. data/ext/libsass/expand.cpp +0 -575
  156. data/ext/libsass/memory_manager.hpp +0 -57
  157. data/ext/libsass/parser.cpp +0 -2403
  158. data/ext/libsass/posix/getopt.c +0 -562
  159. data/ext/libsass/posix/getopt.h +0 -95
  160. data/ext/libsass/to_c.cpp +0 -61
  161. data/ext/libsass/to_c.hpp +0 -44
  162. data/ext/libsass/to_string.cpp +0 -34
  163. data/ext/libsass/util.hpp +0 -54
  164. data/ext/libsass/win/libsass.filters +0 -312
@@ -3,7 +3,6 @@
3
3
  #include "to_string.hpp"
4
4
 
5
5
  namespace Sass {
6
- using namespace std;
7
6
 
8
7
  Output::Output(Context* ctx)
9
8
  : Inspect(Emitter(ctx)),
@@ -18,11 +17,36 @@ namespace Sass {
18
17
  return n->perform(this);
19
18
  }
20
19
 
20
+ void Output::operator()(Number* n)
21
+ {
22
+ // use values to_string facility
23
+ To_String to_string(ctx);
24
+ std::string res = n->perform(&to_string);
25
+ // check for a valid unit here
26
+ // includes result for reporting
27
+ if (n->numerator_units().size() > 1 ||
28
+ n->denominator_units().size() > 0 ||
29
+ (n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != std::string::npos) ||
30
+ (n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != std::string::npos)
31
+ ) {
32
+ error(res + " isn't a valid CSS value.", n->pstate());
33
+ }
34
+ // output the final token
35
+ append_token(res, n);
36
+ }
37
+
21
38
  void Output::operator()(Import* imp)
22
39
  {
23
40
  top_nodes.push_back(imp);
24
41
  }
25
42
 
43
+ void Output::operator()(Map* m)
44
+ {
45
+ To_String to_string(ctx);
46
+ std::string dbg(m->perform(&to_string));
47
+ error(dbg + " isn't a valid CSS value.", m->pstate());
48
+ }
49
+
26
50
  OutputBuffer Output::get_buffer(void)
27
51
  {
28
52
 
@@ -68,7 +92,7 @@ namespace Sass {
68
92
  void Output::operator()(Comment* c)
69
93
  {
70
94
  To_String to_string(ctx);
71
- string txt = c->text()->perform(&to_string);
95
+ std::string txt = c->text()->perform(&to_string);
72
96
  // if (indentation && txt == "/**/") return;
73
97
  bool important = c->is_important();
74
98
  if (output_style() != COMPRESSED || important) {
@@ -109,7 +133,7 @@ namespace Sass {
109
133
  decls = true;
110
134
  if (output_style() == NESTED) indentation += r->tabs();
111
135
  if (ctx && ctx->source_comments) {
112
- stringstream ss;
136
+ std::stringstream ss;
113
137
  append_indentation();
114
138
  ss << "/* line " << r->pstate().line+1 << ", " << r->pstate().path << " */";
115
139
  append_string(ss.str());
@@ -125,9 +149,9 @@ namespace Sass {
125
149
  Declaration* dec = static_cast<Declaration*>(stm);
126
150
  if (dec->value()->concrete_type() == Expression::STRING) {
127
151
  String_Constant* valConst = static_cast<String_Constant*>(dec->value());
128
- string val(valConst->value());
129
- if (dynamic_cast<String_Quoted*>(valConst)) {
130
- if (!valConst->quote_mark() && val.empty()) {
152
+ std::string val(valConst->value());
153
+ if (auto qstr = dynamic_cast<String_Quoted*>(valConst)) {
154
+ if (!qstr->quote_mark() && val.empty()) {
131
155
  bPrintExpression = false;
132
156
  }
133
157
  }
@@ -196,12 +220,12 @@ namespace Sass {
196
220
  append_scope_closer();
197
221
  }
198
222
 
199
- void Output::operator()(Feature_Block* f)
223
+ void Output::operator()(Supports_Block* f)
200
224
  {
201
225
  if (f->is_invisible()) return;
202
226
 
203
- Feature_Query* q = f->feature_queries();
204
- Block* b = f->block();
227
+ Supports_Condition* c = f->condition();
228
+ Block* b = f->block();
205
229
 
206
230
  // Filter out feature blocks that aren't printable (process its children though)
207
231
  if (!Util::isPrintable(f, output_style())) {
@@ -218,13 +242,11 @@ namespace Sass {
218
242
  append_indentation();
219
243
  append_token("@supports", f);
220
244
  append_mandatory_space();
221
- q->perform(this);
245
+ c->perform(this);
222
246
  append_scope_opener();
223
247
 
224
- Selector* e = f->selector();
225
- if (e && b->has_non_hoistable()) {
248
+ if (b->has_non_hoistable()) {
226
249
  // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable
227
- e->perform(this);
228
250
  append_scope_opener();
229
251
 
230
252
  for (size_t i = 0, L = b->length(); i < L; ++i) {
@@ -284,35 +306,9 @@ namespace Sass {
284
306
  in_media_block = false;
285
307
  append_scope_opener();
286
308
 
287
- Selector* e = m->selector();
288
- if (e && b->has_non_hoistable()) {
289
- // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable
290
- e->perform(this);
291
- append_scope_opener();
292
-
293
- for (size_t i = 0, L = b->length(); i < L; ++i) {
294
- Statement* stm = (*b)[i];
295
- if (!stm->is_hoistable()) {
296
- stm->perform(this);
297
- }
298
- }
299
-
300
- append_scope_closer();
301
-
302
- for (size_t i = 0, L = b->length(); i < L; ++i) {
303
- Statement* stm = (*b)[i];
304
- if (stm->is_hoistable()) {
305
- stm->perform(this);
306
- }
307
- }
308
- }
309
- else {
310
- // JMA - not hoisted, just output in order
311
- for (size_t i = 0, L = b->length(); i < L; ++i) {
312
- Statement* stm = (*b)[i];
313
- stm->perform(this);
314
- if (i < L - 1) append_special_linefeed();
315
- }
309
+ for (size_t i = 0, L = b->length(); i < L; ++i) {
310
+ if ((*b)[i]) (*b)[i]->perform(this);
311
+ if (i < L - 1) append_special_linefeed();
316
312
  }
317
313
 
318
314
  if (output_style() == NESTED) indentation -= m->tabs();
@@ -321,7 +317,7 @@ namespace Sass {
321
317
 
322
318
  void Output::operator()(At_Rule* a)
323
319
  {
324
- string kwd = a->keyword();
320
+ std::string kwd = a->keyword();
325
321
  Selector* s = a->selector();
326
322
  Expression* v = a->value();
327
323
  Block* b = a->block();
@@ -334,7 +330,7 @@ namespace Sass {
334
330
  s->perform(this);
335
331
  in_wrapped = false;
336
332
  }
337
- else if (v) {
333
+ if (v) {
338
334
  append_mandatory_space();
339
335
  v->perform(this);
340
336
  }
@@ -383,18 +379,14 @@ namespace Sass {
383
379
 
384
380
  void Output::operator()(String_Constant* s)
385
381
  {
386
- if (String_Quoted* quoted = dynamic_cast<String_Quoted*>(s)) {
387
- return Output::operator()(quoted);
382
+ std::string value(s->value());
383
+ if (s->can_compress_whitespace() && output_style() == COMPRESSED) {
384
+ value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
385
+ }
386
+ if (!in_comment) {
387
+ append_token(string_to_output(value), s);
388
388
  } else {
389
- string value(s->value());
390
- if (s->can_compress_whitespace() && output_style() == COMPRESSED) {
391
- value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
392
- }
393
- if (!in_comment) {
394
- append_token(string_to_output(value), s);
395
- } else {
396
- append_token(value, s);
397
- }
389
+ append_token(value, s);
398
390
  }
399
391
  }
400
392
 
@@ -10,7 +10,6 @@
10
10
 
11
11
  namespace Sass {
12
12
  class Context;
13
- using namespace std;
14
13
 
15
14
  // Refactor to make it generic to find linefeed (look behind)
16
15
  inline bool ends_with(std::string const & value, std::string const & ending)
@@ -29,20 +28,22 @@ namespace Sass {
29
28
  virtual ~Output();
30
29
 
31
30
  protected:
32
- string charset;
33
- vector<AST_Node*> top_nodes;
31
+ std::string charset;
32
+ std::vector<AST_Node*> top_nodes;
34
33
 
35
34
  public:
36
35
  OutputBuffer get_buffer(void);
37
36
 
37
+ virtual void operator()(Map*);
38
38
  virtual void operator()(Ruleset*);
39
39
  // virtual void operator()(Propset*);
40
- virtual void operator()(Feature_Block*);
40
+ virtual void operator()(Supports_Block*);
41
41
  virtual void operator()(Media_Block*);
42
42
  virtual void operator()(At_Rule*);
43
43
  virtual void operator()(Keyframe_Rule*);
44
44
  virtual void operator()(Import*);
45
45
  virtual void operator()(Comment*);
46
+ virtual void operator()(Number*);
46
47
  virtual void operator()(String_Quoted*);
47
48
  virtual void operator()(String_Constant*);
48
49
 
@@ -0,0 +1,2529 @@
1
+ #include <cstdlib>
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include "parser.hpp"
5
+ #include "file.hpp"
6
+ #include "inspect.hpp"
7
+ #include "to_string.hpp"
8
+ #include "constants.hpp"
9
+ #include "util.hpp"
10
+ #include "prelexer.hpp"
11
+ #include "color_maps.hpp"
12
+ #include "sass/functions.h"
13
+ #include "error_handling.hpp"
14
+
15
+ #include <typeinfo>
16
+ #include <tuple>
17
+
18
+ namespace Sass {
19
+ using namespace Constants;
20
+ using namespace Prelexer;
21
+
22
+ Parser Parser::from_c_str(const char* str, Context& ctx, ParserState pstate)
23
+ {
24
+ Parser p(ctx, pstate);
25
+ p.source = str;
26
+ p.position = p.source;
27
+ p.end = str + strlen(str);
28
+ Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);
29
+ p.block_stack.push_back(root);
30
+ root->is_root(true);
31
+ return p;
32
+ }
33
+
34
+ Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate)
35
+ {
36
+ Parser p(ctx, pstate);
37
+ p.source = beg;
38
+ p.position = p.source;
39
+ p.end = end;
40
+ Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);
41
+ p.block_stack.push_back(root);
42
+ root->is_root(true);
43
+ return p;
44
+ }
45
+
46
+ Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)
47
+ {
48
+ Parser p = Parser::from_c_str(src, ctx, pstate);
49
+ // ToDo: ruby sass errors on parent references
50
+ // ToDo: remap the source-map entries somehow
51
+ return p.parse_selector_list(false);
52
+ }
53
+
54
+ bool Parser::peek_newline(const char* start)
55
+ {
56
+ return peek_linefeed(start ? start : position)
57
+ && ! peek_css<exactly<'{'>>(start);
58
+ }
59
+
60
+ Parser Parser::from_token(Token t, Context& ctx, ParserState pstate)
61
+ {
62
+ Parser p(ctx, pstate);
63
+ p.source = t.begin;
64
+ p.position = p.source;
65
+ p.end = t.end;
66
+ Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);
67
+ p.block_stack.push_back(root);
68
+ root->is_root(true);
69
+ return p;
70
+ }
71
+
72
+ /* main entry point to parse root block */
73
+ Block* Parser::parse()
74
+ {
75
+ bool is_root = false;
76
+ Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, true);
77
+ read_bom();
78
+
79
+ if (ctx.queue.size() == 1) {
80
+ is_root = true;
81
+ Import* pre = SASS_MEMORY_NEW(ctx.mem, Import, pstate);
82
+ std::string load_path(ctx.queue[0].load_path);
83
+ do_import(load_path, pre, ctx.c_headers, false);
84
+ ctx.head_imports = ctx.queue.size() - 1;
85
+ if (!pre->urls().empty()) (*root) << pre;
86
+ if (!pre->files().empty()) {
87
+ for (size_t i = 0, S = pre->files().size(); i < S; ++i) {
88
+ (*root) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, pre->files()[i]);
89
+ }
90
+ }
91
+ }
92
+
93
+ block_stack.push_back(root);
94
+ /* bool rv = */ parse_block_nodes(is_root);
95
+ block_stack.pop_back();
96
+
97
+ // update for end position
98
+ root->update_pstate(pstate);
99
+
100
+ if (position != end) {
101
+ css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");
102
+ }
103
+
104
+ return root;
105
+ }
106
+
107
+
108
+ // convenience function for block parsing
109
+ // will create a new block ad-hoc for you
110
+ // this is the base block parsing function
111
+ Block* Parser::parse_css_block(bool is_root)
112
+ {
113
+
114
+ // parse comments before block
115
+ // lex < optional_css_comments >();
116
+ // lex mandatory opener or error out
117
+ if (!lex_css < exactly<'{'> >()) {
118
+ css_error("Invalid CSS", " after ", ": expected \"{\", was ");
119
+ }
120
+ // create new block and push to the selector stack
121
+ Block* block = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, is_root);
122
+ block_stack.push_back(block);
123
+
124
+ if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");;
125
+
126
+ if (!lex_css < exactly<'}'> >()) {
127
+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");
128
+ }
129
+
130
+ // update for end position
131
+ block->update_pstate(pstate);
132
+
133
+ // parse comments before block
134
+ // lex < optional_css_comments >();
135
+
136
+ block_stack.pop_back();
137
+
138
+ return block;
139
+ }
140
+
141
+ // convenience function for block parsing
142
+ // will create a new block ad-hoc for you
143
+ // also updates the `in_at_root` flag
144
+ Block* Parser::parse_block(bool is_root)
145
+ {
146
+ LOCAL_FLAG(in_at_root, is_root);
147
+ return parse_css_block(is_root);
148
+ }
149
+
150
+ // the main block parsing function
151
+ // parses stuff between `{` and `}`
152
+ bool Parser::parse_block_nodes(bool is_root)
153
+ {
154
+
155
+ // loop until end of string
156
+ while (position < end) {
157
+
158
+ // we should be able to refactor this
159
+ parse_block_comments();
160
+ lex < css_whitespace >();
161
+
162
+ if (lex < exactly<';'> >()) continue;
163
+ if (peek < end_of_file >()) return true;
164
+ if (peek < exactly<'}'> >()) return true;
165
+
166
+ if (parse_block_node(is_root)) continue;
167
+
168
+ parse_block_comments();
169
+
170
+ if (lex_css < exactly<';'> >()) continue;
171
+ if (peek_css < end_of_file >()) return true;
172
+ if (peek_css < exactly<'}'> >()) return true;
173
+
174
+ // illegal sass
175
+ return false;
176
+ }
177
+ // return success
178
+ return true;
179
+ }
180
+
181
+ // parser for a single node in a block
182
+ // semicolons must be lexed beforehand
183
+ bool Parser::parse_block_node(bool is_root) {
184
+
185
+ Block* block = block_stack.back();
186
+
187
+ while (lex< block_comment >()) {
188
+ bool is_important = lexed.begin[2] == '!';
189
+ String* contents = parse_interpolated_chunk(lexed);
190
+ (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);
191
+ }
192
+
193
+ // throw away white-space
194
+ // includes line comments
195
+ lex < css_whitespace >();
196
+
197
+ Lookahead lookahead_result;
198
+
199
+ // also parse block comments
200
+
201
+ // first parse everything that is allowed in functions
202
+ if (lex < variable >(true)) { (*block) << parse_assignment(); }
203
+ else if (lex < kwd_err >(true)) { (*block) << parse_error(); }
204
+ else if (lex < kwd_dbg >(true)) { (*block) << parse_debug(); }
205
+ else if (lex < kwd_warn >(true)) { (*block) << parse_warning(); }
206
+ else if (lex < kwd_if_directive >(true)) { (*block) << parse_if_directive(); }
207
+ else if (lex < kwd_for_directive >(true)) { (*block) << parse_for_directive(); }
208
+ else if (lex < kwd_each_directive >(true)) { (*block) << parse_each_directive(); }
209
+ else if (lex < kwd_while_directive >(true)) { (*block) << parse_while_directive(); }
210
+ else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); }
211
+
212
+ // abort if we are in function context and have nothing parsed yet
213
+ else if (stack.back() == function_def) {
214
+ error("Functions can only contain variable declarations and control directives", pstate);
215
+ }
216
+
217
+ // parse imports to process later
218
+ else if (lex < kwd_import >(true)) {
219
+ if (stack.back() == mixin_def || stack.back() == function_def) {
220
+ error("Import directives may not be used within control directives or mixins.", pstate);
221
+ }
222
+ Import* imp = parse_import();
223
+ // if it is a url, we only add the statement
224
+ if (!imp->urls().empty()) (*block) << imp;
225
+ // if it is a file(s), we should process them
226
+ if (!imp->files().empty()) {
227
+ for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
228
+ (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->files()[i]);
229
+ }
230
+ }
231
+ }
232
+
233
+ else if (lex < kwd_extend >(true)) {
234
+ if (block->is_root()) {
235
+ error("Extend directives may only be used within rules.", pstate);
236
+ }
237
+
238
+ Lookahead lookahead = lookahead_for_include(position);
239
+ if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");
240
+ Selector* target;
241
+ if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);
242
+ else target = parse_selector_list(true);
243
+ (*block) << SASS_MEMORY_NEW(ctx.mem, Extension, pstate, target);
244
+ }
245
+
246
+ // selector may contain interpolations which need delayed evaluation
247
+ else if (!(lookahead_result = lookahead_for_selector(position)).error)
248
+ { (*block) << parse_ruleset(lookahead_result, is_root); }
249
+
250
+ // parse multiple specific keyword directives
251
+ else if (lex < kwd_media >(true)) { (*block) << parse_media_block(); }
252
+ else if (lex < kwd_at_root >(true)) { (*block) << parse_at_root_block(); }
253
+ else if (lex < kwd_include_directive >(true)) { (*block) << parse_include_directive(); }
254
+ else if (lex < kwd_content_directive >(true)) { (*block) << parse_content_directive(); }
255
+ else if (lex < kwd_supports_directive >(true)) { (*block) << parse_supports_directive(); }
256
+ else if (lex < kwd_mixin >(true)) { (*block) << parse_definition(Definition::MIXIN); }
257
+ else if (lex < kwd_function >(true)) { (*block) << parse_definition(Definition::FUNCTION); }
258
+
259
+ // ignore the @charset directive for now
260
+ else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); }
261
+
262
+ // generic at keyword (keep last)
263
+ else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); }
264
+
265
+ else if (block->is_root()) {
266
+ lex< css_whitespace >();
267
+ if (position >= end) return true;
268
+ css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was ");
269
+ }
270
+ // parse a declaration
271
+ else
272
+ {
273
+ // ToDo: how does it handle parse errors?
274
+ // maybe we are expected to parse something?
275
+ Declaration* decl = parse_declaration();
276
+ decl->tabs(indentation);
277
+ (*block) << decl;
278
+ // maybe we have a "sub-block"
279
+ if (peek< exactly<'{'> >()) {
280
+ if (decl->is_indented()) ++ indentation;
281
+ // parse a propset that rides on the declaration's property
282
+ (*block) << SASS_MEMORY_NEW(ctx.mem, Propset, pstate, decl->property(), parse_block());
283
+ if (decl->is_indented()) -- indentation;
284
+ }
285
+ }
286
+ // something matched
287
+ return true;
288
+ }
289
+ // EO parse_block_nodes
290
+
291
+ void Parser::add_single_file (Import* imp, std::string import_path) {
292
+
293
+ std::string extension;
294
+ std::string unquoted(unquote(import_path));
295
+ if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css
296
+ // a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end
297
+ extension = unquoted.substr(unquoted.length() - 4, 4);
298
+ }
299
+
300
+ if (extension == ".css") {
301
+ String_Constant* loc = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, unquote(import_path));
302
+ Argument* loc_arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, loc);
303
+ Arguments* loc_args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);
304
+ (*loc_args) << loc_arg;
305
+ Function_Call* new_url = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", loc_args);
306
+ imp->urls().push_back(new_url);
307
+ }
308
+ else {
309
+ std::string current_dir = File::dir_name(path);
310
+ std::string resolved(ctx.add_file(current_dir, unquoted, *this));
311
+ if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir, pstate);
312
+ imp->files().push_back(resolved);
313
+ }
314
+
315
+ }
316
+
317
+ void Parser::import_single_file (Import* imp, std::string import_path) {
318
+
319
+ if (imp->media_queries() ||
320
+ !unquote(import_path).substr(0, 7).compare("http://") ||
321
+ !unquote(import_path).substr(0, 8).compare("https://") ||
322
+ !unquote(import_path).substr(0, 2).compare("//"))
323
+ {
324
+ imp->urls().push_back(SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, import_path));
325
+ }
326
+ else {
327
+ add_single_file(imp, import_path);
328
+ }
329
+
330
+ }
331
+
332
+ bool Parser::do_import(const std::string& import_path, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one)
333
+ {
334
+ size_t i = 0;
335
+ bool has_import = false;
336
+ std::string load_path = unquote(import_path);
337
+ // std::cerr << "-- " << load_path << "\n";
338
+ for (Sass_Importer_Entry& importer : importers) {
339
+ // int priority = sass_importer_get_priority(importer);
340
+ Sass_Importer_Fn fn = sass_importer_get_function(importer);
341
+ if (Sass_Import_List includes =
342
+ fn(load_path.c_str(), importer, ctx.c_compiler)
343
+ ) {
344
+ Sass_Import_List list = includes;
345
+ while (*includes) { ++i;
346
+ std::string uniq_path = load_path;
347
+ if (!only_one && i) {
348
+ std::stringstream pathstrm;
349
+ pathstrm << uniq_path << ":" << i;
350
+ uniq_path = pathstrm.str();
351
+ }
352
+ Sass_Import_Entry include = *includes;
353
+ const char *abs_path = sass_import_get_abs_path(include);
354
+ char* source = sass_import_take_source(include);
355
+ size_t line = sass_import_get_error_line(include);
356
+ size_t column = sass_import_get_error_column(include);
357
+ const char* message = sass_import_get_error_message(include);
358
+ if (message) {
359
+ if (line == std::string::npos && column == std::string::npos) error(message, pstate);
360
+ else error(message, ParserState(message, source, Position(line, column)));
361
+ } else if (source) {
362
+ if (abs_path) {
363
+ ctx.add_source(uniq_path, abs_path, source);
364
+ imp->files().push_back(uniq_path);
365
+ size_t i = ctx.queue.size() - 1;
366
+ ctx.process_queue_entry(ctx.queue[i], i);
367
+ } else {
368
+ ctx.add_source(uniq_path, uniq_path, source);
369
+ imp->files().push_back(uniq_path);
370
+ size_t i = ctx.queue.size() - 1;
371
+ ctx.process_queue_entry(ctx.queue[i], i);
372
+ }
373
+ } else if(abs_path) {
374
+ import_single_file(imp, abs_path);
375
+ }
376
+ ++includes;
377
+ }
378
+ // deallocate returned memory
379
+ sass_delete_import_list(list);
380
+ // set success flag
381
+ has_import = true;
382
+ // break import chain
383
+ if (only_one) return true;
384
+ }
385
+ }
386
+ // return result
387
+ return has_import;
388
+ }
389
+
390
+ Import* Parser::parse_import()
391
+ {
392
+ Import* imp = SASS_MEMORY_NEW(ctx.mem, Import, pstate);
393
+ std::vector<std::pair<std::string,Function_Call*>> to_import;
394
+ bool first = true;
395
+ do {
396
+ while (lex< block_comment >());
397
+ if (lex< quoted_string >()) {
398
+ if (!do_import(lexed, imp, ctx.c_importers, true))
399
+ {
400
+ // push single file import
401
+ // import_single_file(imp, lexed);
402
+ to_import.push_back(std::pair<std::string,Function_Call*>(std::string(lexed), 0));
403
+ }
404
+ }
405
+ else if (lex< uri_prefix >()) {
406
+ Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);
407
+ Function_Call* result = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", args);
408
+ if (lex< quoted_string >()) {
409
+ Expression* the_url = parse_string();
410
+ *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);
411
+ }
412
+ else if (lex < uri_value >(false)) { // don't skip comments
413
+ String* the_url = parse_interpolated_chunk(lexed);
414
+ *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);
415
+ }
416
+ else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {
417
+ Expression* the_url = parse_list(); // parse_interpolated_chunk(lexed);
418
+ *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);
419
+ }
420
+ else {
421
+ error("malformed URL", pstate);
422
+ }
423
+ if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);
424
+ // imp->urls().push_back(result);
425
+ to_import.push_back(std::pair<std::string,Function_Call*>("", result));
426
+ }
427
+ else {
428
+ if (first) error("@import directive requires a url or quoted path", pstate);
429
+ else error("expecting another url or quoted path in @import list", pstate);
430
+ }
431
+ first = false;
432
+ } while (lex_css< exactly<','> >());
433
+
434
+ if (!peek_css<alternatives<exactly<';'>,end_of_file>>()) {
435
+ List* media_queries = parse_media_queries();
436
+ imp->media_queries(media_queries);
437
+ }
438
+
439
+ for(auto location : to_import) {
440
+ if (location.second) {
441
+ imp->urls().push_back(location.second);
442
+ } else {
443
+ import_single_file(imp, location.first);
444
+ }
445
+ }
446
+
447
+ return imp;
448
+ }
449
+
450
+ Definition* Parser::parse_definition(Definition::Type which_type)
451
+ {
452
+ std::string which_str(lexed);
453
+ if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);
454
+ std::string name(Util::normalize_underscores(lexed));
455
+ if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))
456
+ { error("Invalid function name \"" + name + "\".", pstate); }
457
+ ParserState source_position_of_def = pstate;
458
+ Parameters* params = parse_parameters();
459
+ if (which_type == Definition::MIXIN) stack.push_back(mixin_def);
460
+ else stack.push_back(function_def);
461
+ Block* body = parse_block();
462
+ stack.pop_back();
463
+ Definition* def = SASS_MEMORY_NEW(ctx.mem, Definition, source_position_of_def, name, params, body, which_type);
464
+ return def;
465
+ }
466
+
467
+ Parameters* Parser::parse_parameters()
468
+ {
469
+ std::string name(lexed);
470
+ Position position = after_token;
471
+ Parameters* params = SASS_MEMORY_NEW(ctx.mem, Parameters, pstate);
472
+ if (lex_css< exactly<'('> >()) {
473
+ // if there's anything there at all
474
+ if (!peek_css< exactly<')'> >()) {
475
+ do (*params) << parse_parameter();
476
+ while (lex_css< exactly<','> >());
477
+ }
478
+ if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
479
+ }
480
+ return params;
481
+ }
482
+
483
+ Parameter* Parser::parse_parameter()
484
+ {
485
+ while (lex< alternatives < spaces, block_comment > >());
486
+ lex < variable >();
487
+ std::string name(Util::normalize_underscores(lexed));
488
+ ParserState pos = pstate;
489
+ Expression* val = 0;
490
+ bool is_rest = false;
491
+ while (lex< alternatives < spaces, block_comment > >());
492
+ if (lex< exactly<':'> >()) { // there's a default value
493
+ while (lex< block_comment >());
494
+ val = parse_space_list();
495
+ val->is_delayed(false);
496
+ }
497
+ else if (lex< exactly< ellipsis > >()) {
498
+ is_rest = true;
499
+ }
500
+ Parameter* p = SASS_MEMORY_NEW(ctx.mem, Parameter, pos, name, val, is_rest);
501
+ return p;
502
+ }
503
+
504
+ Arguments* Parser::parse_arguments()
505
+ {
506
+ std::string name(lexed);
507
+ Position position = after_token;
508
+ Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);
509
+ if (lex_css< exactly<'('> >()) {
510
+ // if there's anything there at all
511
+ if (!peek_css< exactly<')'> >()) {
512
+ do (*args) << parse_argument();
513
+ while (lex_css< exactly<','> >());
514
+ }
515
+ if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
516
+ }
517
+ return args;
518
+ }
519
+
520
+ Argument* Parser::parse_argument()
521
+ {
522
+ if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) {
523
+ position += 2;
524
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
525
+ }
526
+
527
+ Argument* arg;
528
+ if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {
529
+ lex_css< variable >();
530
+ std::string name(Util::normalize_underscores(lexed));
531
+ ParserState p = pstate;
532
+ lex_css< exactly<':'> >();
533
+ Expression* val = parse_space_list();
534
+ val->is_delayed(false);
535
+ arg = SASS_MEMORY_NEW(ctx.mem, Argument, p, val, name);
536
+ }
537
+ else {
538
+ bool is_arglist = false;
539
+ bool is_keyword = false;
540
+ Expression* val = parse_space_list();
541
+ val->is_delayed(false);
542
+ if (lex_css< exactly< ellipsis > >()) {
543
+ if (val->concrete_type() == Expression::MAP) is_keyword = true;
544
+ else is_arglist = true;
545
+ }
546
+ arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, val, "", is_arglist, is_keyword);
547
+ }
548
+ return arg;
549
+ }
550
+
551
+ Assignment* Parser::parse_assignment()
552
+ {
553
+ std::string name(Util::normalize_underscores(lexed));
554
+ ParserState var_source_position = pstate;
555
+ if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);
556
+ Expression* val;
557
+ Lookahead lookahead = lookahead_for_value(position);
558
+ if (lookahead.has_interpolants && lookahead.found) {
559
+ val = parse_value_schema(lookahead.found);
560
+ } else {
561
+ val = parse_list();
562
+ }
563
+ val->is_delayed(false);
564
+ bool is_default = false;
565
+ bool is_global = false;
566
+ while (peek< alternatives < default_flag, global_flag > >()) {
567
+ if (lex< default_flag >()) is_default = true;
568
+ else if (lex< global_flag >()) is_global = true;
569
+ }
570
+ Assignment* var = SASS_MEMORY_NEW(ctx.mem, Assignment, var_source_position, name, val, is_default, is_global);
571
+ return var;
572
+ }
573
+
574
+ // a ruleset connects a selector and a block
575
+ Ruleset* Parser::parse_ruleset(Lookahead lookahead, bool is_root)
576
+ {
577
+ // make sure to move up the the last position
578
+ lex < optional_css_whitespace >(false, true);
579
+ // create the connector object (add parts later)
580
+ Ruleset* ruleset = SASS_MEMORY_NEW(ctx.mem, Ruleset, pstate);
581
+ // parse selector static or as schema to be evaluated later
582
+ if (lookahead.parsable) ruleset->selector(parse_selector_list(is_root));
583
+ else ruleset->selector(parse_selector_schema(lookahead.found));
584
+ // then parse the inner block
585
+ ruleset->block(parse_block());
586
+ // update for end position
587
+ ruleset->update_pstate(pstate);
588
+ // inherit is_root from parent block
589
+ // need this info for sanity checks
590
+ ruleset->is_root(is_root);
591
+ // return AST Node
592
+ return ruleset;
593
+ }
594
+
595
+ // parse a selector schema that will be evaluated in the eval stage
596
+ // uses a string schema internally to do the actual schema handling
597
+ // in the eval stage we will be re-parse it into an actual selector
598
+ Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)
599
+ {
600
+ // move up to the start
601
+ lex< optional_spaces >();
602
+ const char* i = position;
603
+ // selector schema re-uses string schema implementation
604
+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
605
+ // the selector schema is pretty much just a wrapper for the string schema
606
+ Selector_Schema* selector_schema = SASS_MEMORY_NEW(ctx.mem, Selector_Schema, pstate, schema);
607
+ selector_schema->media_block(last_media_block);
608
+
609
+ // process until end
610
+ while (i < end_of_selector) {
611
+ // try to parse mutliple interpolants
612
+ if (const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector)) {
613
+ // accumulate the preceding segment if the position has advanced
614
+ if (i < p) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));
615
+ // check if the interpolation only contains white-space (error out)
616
+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
617
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
618
+ }
619
+ // skip over all nested inner interpolations up to our own delimiter
620
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);
621
+ // pass inner expression to the parser to resolve nested interpolations
622
+ Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();
623
+ // set status on the list expression
624
+ interpolant->is_interpolant(true);
625
+ // add to the string schema
626
+ (*schema) << interpolant;
627
+ // advance position
628
+ i = j;
629
+ }
630
+ // no more interpolants have been found
631
+ // add the last segment if there is one
632
+ else {
633
+ // make sure to add the last bits of the string up to the end (if any)
634
+ if (i < end_of_selector) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, end_of_selector));
635
+ // exit loop
636
+ i = end_of_selector;
637
+ }
638
+ }
639
+ // EO until eos
640
+
641
+ // update position
642
+ position = i;
643
+
644
+ // update for end position
645
+ selector_schema->update_pstate(pstate);
646
+
647
+ // return parsed result
648
+ return selector_schema;
649
+ }
650
+ // EO parse_selector_schema
651
+
652
+ void Parser::parse_charset_directive()
653
+ {
654
+ lex <
655
+ sequence <
656
+ quoted_string,
657
+ optional_spaces,
658
+ exactly <';'>
659
+ >
660
+ >();
661
+ }
662
+
663
+ // called after parsing `kwd_include_directive`
664
+ Mixin_Call* Parser::parse_include_directive()
665
+ {
666
+ // lex identifier into `lexed` var
667
+ lex_identifier(); // may error out
668
+ // normalize underscores to hyphens
669
+ std::string name(Util::normalize_underscores(lexed));
670
+ // create the initial mixin call object
671
+ Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call, pstate, name, 0, 0);
672
+ // parse mandatory arguments
673
+ call->arguments(parse_arguments());
674
+ // parse optional block
675
+ if (peek < exactly <'{'> >()) {
676
+ call->block(parse_block());
677
+ }
678
+ // return ast node
679
+ return call;
680
+ }
681
+ // EO parse_include_directive
682
+
683
+ // parse a list of complex selectors
684
+ // this is the main entry point for most
685
+ Selector_List* Parser::parse_selector_list(bool in_root)
686
+ {
687
+ bool reloop = true;
688
+ bool had_linefeed = false;
689
+ Complex_Selector* sel = 0;
690
+ To_String to_string(&ctx);
691
+ Selector_List* group = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate);
692
+ group->media_block(last_media_block);
693
+
694
+ do {
695
+ reloop = false;
696
+
697
+ had_linefeed = had_linefeed || peek_newline();
698
+
699
+ if (peek_css< class_char < selector_list_delims > >())
700
+ break; // in case there are superfluous commas at the end
701
+
702
+
703
+ // now parse the complex selector
704
+ sel = parse_complex_selector(in_root);
705
+
706
+ if (!sel) return group;
707
+
708
+ sel->has_line_feed(had_linefeed);
709
+
710
+ had_linefeed = false;
711
+
712
+ while (peek_css< exactly<','> >())
713
+ {
714
+ lex< css_comments >(false);
715
+ // consume everything up and including the comma speparator
716
+ reloop = lex< exactly<','> >() != 0;
717
+ // remember line break (also between some commas)
718
+ had_linefeed = had_linefeed || peek_newline();
719
+ // remember line break (also between some commas)
720
+ }
721
+ (*group) << sel;
722
+ }
723
+ while (reloop);
724
+ while (lex_css< kwd_optional >()) {
725
+ group->is_optional(true);
726
+ }
727
+ // update for end position
728
+ group->update_pstate(pstate);
729
+ if (sel) sel->last()->has_line_break(false);
730
+ return group;
731
+ }
732
+ // EO parse_selector_list
733
+
734
+ // a complex selector combines a compound selector with another
735
+ // complex selector, with one of four combinator operations.
736
+ // the compound selector (head) is optional, since the combinator
737
+ // can come first in the whole selector sequence (like `> DIV').
738
+ Complex_Selector* Parser::parse_complex_selector(bool in_root)
739
+ {
740
+
741
+ String* reference = 0;
742
+ lex < block_comment >();
743
+ // parse the left hand side
744
+ Compound_Selector* lhs = 0;
745
+ // special case if it starts with combinator ([+~>])
746
+ if (!peek_css< class_char < selector_combinator_ops > >()) {
747
+ // parse the left hand side
748
+ lhs = parse_compound_selector();
749
+ }
750
+
751
+ // check for end of file condition
752
+ if (peek < end_of_file >()) return 0;
753
+
754
+ // parse combinator between lhs and rhs
755
+ Complex_Selector::Combinator combinator;
756
+ if (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO;
757
+ else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES;
758
+ else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF;
759
+ else if (lex< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) {
760
+ // comments are allowed, but not spaces?
761
+ combinator = Complex_Selector::REFERENCE;
762
+ if (!lex < re_reference_combinator >()) return 0;
763
+ reference = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
764
+ if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg?
765
+ }
766
+ else /* if (lex< zero >()) */ combinator = Complex_Selector::ANCESTOR_OF;
767
+
768
+ if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0;
769
+
770
+ // lex < block_comment >();
771
+ // source position of a complex selector points to the combinator
772
+ // ToDo: make sure we update pstate for ancestor of (lex < zero >());
773
+ Complex_Selector* sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, combinator, lhs);
774
+ sel->media_block(last_media_block);
775
+
776
+ if (combinator == Complex_Selector::REFERENCE) sel->reference(reference);
777
+ // has linfeed after combinator?
778
+ sel->has_line_break(peek_newline());
779
+ // sel->has_line_feed(has_line_feed);
780
+
781
+ // check if we got the abort condition (ToDo: optimize)
782
+ if (!peek_css< class_char < complex_selector_delims > >()) {
783
+ // parse next selector in sequence
784
+ sel->tail(parse_complex_selector(true));
785
+ if (sel->tail()) {
786
+ // ToDo: move this logic below into tail setter
787
+ if (sel->tail()->has_reference()) sel->has_reference(true);
788
+ if (sel->tail()->has_placeholder()) sel->has_placeholder(true);
789
+ }
790
+ }
791
+
792
+ // add a parent selector if we are not in a root
793
+ // also skip adding parent ref if we only have refs
794
+ if (!sel->has_reference() && !in_at_root && !in_root) {
795
+ // create the objects to wrap parent selector reference
796
+ Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);
797
+ parent->media_block(last_media_block);
798
+ Compound_Selector* head = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate);
799
+ head->media_block(last_media_block);
800
+ // add simple selector
801
+ (*head) << parent;
802
+ // selector may not have any head yet
803
+ if (!sel->head()) { sel->head(head); }
804
+ // otherwise we need to create a new complex selector and set the old one as its tail
805
+ else {
806
+ sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, Complex_Selector::ANCESTOR_OF, head, sel);
807
+ sel->media_block(last_media_block);
808
+ }
809
+ // peek for linefeed and remember result on head
810
+ // if (peek_newline()) head->has_line_break(true);
811
+ }
812
+
813
+ // complex selector
814
+ return sel;
815
+ }
816
+ // EO parse_complex_selector
817
+
818
+ // parse one compound selector, which is basically
819
+ // a list of simple selectors (directly adjancent)
820
+ // lex them exactly (without skipping white-space)
821
+ Compound_Selector* Parser::parse_compound_selector()
822
+ {
823
+ // init an empty compound selector wrapper
824
+ Compound_Selector* seq = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate);
825
+ seq->media_block(last_media_block);
826
+
827
+ // skip initial white-space
828
+ lex< css_whitespace >();
829
+
830
+ // parse list
831
+ while (true)
832
+ {
833
+ // remove all block comments (don't skip white-space)
834
+ lex< delimited_by< slash_star, star_slash, false > >(false);
835
+ // parse functional
836
+ if (peek < re_pseudo_selector >())
837
+ {
838
+ (*seq) << parse_simple_selector();
839
+ }
840
+ // parse parent selector
841
+ else if (lex< exactly<'&'> >(false))
842
+ {
843
+ // this produces a linefeed!?
844
+ seq->has_parent_reference(true);
845
+ (*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);
846
+ }
847
+ // parse type selector
848
+ else if (lex< re_type_selector >(false))
849
+ {
850
+ (*seq) << SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed);
851
+ }
852
+ // peek for abort conditions
853
+ else if (peek< spaces >()) break;
854
+ else if (peek< end_of_file >()) { break; }
855
+ else if (peek_css < class_char < selector_combinator_ops > >()) break;
856
+ else if (peek_css < class_char < complex_selector_delims > >()) break;
857
+ // otherwise parse another simple selector
858
+ else {
859
+ Simple_Selector* sel = parse_simple_selector();
860
+ if (!sel) return 0;
861
+ (*seq) << sel;
862
+ }
863
+ }
864
+
865
+ if (seq && !peek_css<exactly<'{'>>()) {
866
+ seq->has_line_break(peek_newline());
867
+ }
868
+
869
+ // EO while true
870
+ return seq;
871
+
872
+ }
873
+ // EO parse_compound_selector
874
+
875
+ Simple_Selector* Parser::parse_simple_selector()
876
+ {
877
+ lex < css_comments >(false);
878
+ if (lex< alternatives < id_name, class_name > >()) {
879
+ return SASS_MEMORY_NEW(ctx.mem, Selector_Qualifier, pstate, lexed);
880
+ }
881
+ else if (lex< quoted_string >()) {
882
+ return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, unquote(lexed));
883
+ }
884
+ else if (lex< alternatives < variable, number, static_reference_combinator > >()) {
885
+ return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed);
886
+ }
887
+ else if (peek< pseudo_not >()) {
888
+ return parse_negated_selector();
889
+ }
890
+ else if (peek< re_pseudo_selector >()) {
891
+ return parse_pseudo_selector();
892
+ }
893
+ else if (peek< exactly<':'> >()) {
894
+ return parse_pseudo_selector();
895
+ }
896
+ else if (lex < exactly<'['> >()) {
897
+ return parse_attribute_selector();
898
+ }
899
+ else if (lex< placeholder >()) {
900
+ Selector_Placeholder* sel = SASS_MEMORY_NEW(ctx.mem, Selector_Placeholder, pstate, lexed);
901
+ sel->media_block(last_media_block);
902
+ return sel;
903
+ }
904
+ // failed
905
+ return 0;
906
+ }
907
+
908
+ Wrapped_Selector* Parser::parse_negated_selector()
909
+ {
910
+ lex< pseudo_not >();
911
+ std::string name(lexed);
912
+ ParserState nsource_position = pstate;
913
+ Selector* negated = parse_selector_list(true);
914
+ if (!lex< exactly<')'> >()) {
915
+ error("negated selector is missing ')'", pstate);
916
+ }
917
+ name.erase(name.size() - 1);
918
+ return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, nsource_position, name, negated);
919
+ }
920
+
921
+ // a pseudo selector often starts with one or two colons
922
+ // it can contain more selectors inside parantheses
923
+ Simple_Selector* Parser::parse_pseudo_selector() {
924
+ if (lex< sequence<
925
+ optional < pseudo_prefix >,
926
+ // we keep the space within the name, strange enough
927
+ // ToDo: refactor output to schedule the space for it
928
+ // or do we really want to keep the real white-space?
929
+ sequence< identifier, optional < block_comment >, exactly<'('> >
930
+ > >())
931
+ {
932
+
933
+ std::string name(lexed);
934
+ name.erase(name.size() - 1);
935
+ ParserState p = pstate;
936
+
937
+ // specially parse static stuff
938
+ // ToDo: really everything static?
939
+ if (peek_css <
940
+ sequence <
941
+ alternatives <
942
+ static_value,
943
+ binomial
944
+ >,
945
+ optional_css_whitespace,
946
+ exactly<')'>
947
+ >
948
+ >()
949
+ ) {
950
+ lex_css< alternatives < static_value, binomial > >();
951
+ String_Constant* expr = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
952
+ if (expr && lex_css< exactly<')'> >()) {
953
+ expr->can_compress_whitespace(true);
954
+ return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, p, name, expr);
955
+ }
956
+ }
957
+ else if (Selector* wrapped = parse_selector_list(true)) {
958
+ if (wrapped && lex_css< exactly<')'> >()) {
959
+ return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, p, name, wrapped);
960
+ }
961
+ }
962
+
963
+ }
964
+ // EO if pseudo selector
965
+
966
+ else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {
967
+ return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, pstate, lexed);
968
+ }
969
+ else if(lex < pseudo_prefix >()) {
970
+ css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");
971
+ }
972
+
973
+ css_error("Invalid CSS", " after ", ": expected \")\", was ");
974
+
975
+ // unreachable statement
976
+ return 0;
977
+ }
978
+
979
+ Attribute_Selector* Parser::parse_attribute_selector()
980
+ {
981
+ ParserState p = pstate;
982
+ if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
983
+ std::string name(lexed);
984
+ if (lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, "", 0);
985
+ if (!lex_css< alternatives< exact_match, class_match, dash_match,
986
+ prefix_match, suffix_match, substring_match > >()) {
987
+ error("invalid operator in attribute selector for " + name, pstate);
988
+ }
989
+ std::string matcher(lexed);
990
+
991
+ String* value = 0;
992
+ if (lex_css< identifier >()) {
993
+ value = SASS_MEMORY_NEW(ctx.mem, String_Constant, p, lexed);
994
+ }
995
+ else if (lex_css< quoted_string >()) {
996
+ value = parse_interpolated_chunk(lexed, true); // needed!
997
+ }
998
+ else {
999
+ error("expected a string constant or identifier in attribute selector for " + name, pstate);
1000
+ }
1001
+
1002
+ if (!lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) error("unterminated attribute selector for " + name, pstate);
1003
+ return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, matcher, value);
1004
+ }
1005
+
1006
+ /* parse block comment and add to block */
1007
+ void Parser::parse_block_comments()
1008
+ {
1009
+ Block* block = block_stack.back();
1010
+ while (lex< block_comment >()) {
1011
+ bool is_important = lexed.begin[2] == '!';
1012
+ String* contents = parse_interpolated_chunk(lexed);
1013
+ (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);
1014
+ }
1015
+ }
1016
+
1017
+ Declaration* Parser::parse_declaration() {
1018
+ String* prop = 0;
1019
+ if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
1020
+ prop = parse_identifier_schema();
1021
+ }
1022
+ else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {
1023
+ prop = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1024
+ prop->is_delayed(true);
1025
+ }
1026
+ else {
1027
+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");
1028
+ }
1029
+ bool is_indented = true;
1030
+ const std::string property(lexed);
1031
+ if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);
1032
+ lex < css_comments >(false);
1033
+ if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);
1034
+ if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty
1035
+ if (peek_css< static_value >()) {
1036
+ return SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, parse_static_value()/*, lex<kwd_important>()*/);
1037
+ }
1038
+ else {
1039
+ Expression* value;
1040
+ Lookahead lookahead = lookahead_for_value(position);
1041
+ if (lookahead.found) {
1042
+ if (lookahead.has_interpolants) {
1043
+ value = parse_value_schema(lookahead.found);
1044
+ } else {
1045
+ value = parse_list();
1046
+ }
1047
+ }
1048
+ else {
1049
+ value = parse_list();
1050
+ if (List* list = dynamic_cast<List*>(value)) {
1051
+ if (list->length() == 0 && !peek< exactly <'{'> >()) {
1052
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1053
+ }
1054
+ }
1055
+ }
1056
+ lex < css_comments >(false);
1057
+ auto decl = SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, value/*, lex<kwd_important>()*/);
1058
+ decl->is_indented(is_indented);
1059
+ return decl;
1060
+ }
1061
+ }
1062
+
1063
+ // parse +/- and return false if negative
1064
+ bool Parser::parse_number_prefix()
1065
+ {
1066
+ bool positive = true;
1067
+ while(true) {
1068
+ if (lex < block_comment >()) continue;
1069
+ if (lex < number_prefix >()) continue;
1070
+ if (lex < exactly < '-' > >()) {
1071
+ positive = !positive;
1072
+ continue;
1073
+ }
1074
+ break;
1075
+ }
1076
+ return positive;
1077
+ }
1078
+
1079
+ Expression* Parser::parse_map()
1080
+ {
1081
+ Expression* key = parse_list();
1082
+ Map* map = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);
1083
+ if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {
1084
+ if (!str->quote_mark() && !str->is_delayed()) {
1085
+ if (const Color* col = name_to_color(str->value())) {
1086
+ Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);
1087
+ c->pstate(str->pstate());
1088
+ c->disp(str->value());
1089
+ key = c;
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ // it's not a map so return the lexed value as a list value
1095
+ if (!peek< exactly<':'> >())
1096
+ { return key; }
1097
+
1098
+ lex< exactly<':'> >();
1099
+
1100
+ Expression* value = parse_space_list();
1101
+
1102
+ (*map) << std::make_pair(key, value);
1103
+
1104
+ while (lex_css< exactly<','> >())
1105
+ {
1106
+ // allow trailing commas - #495
1107
+ if (peek_css< exactly<')'> >(position))
1108
+ { break; }
1109
+
1110
+ Expression* key = parse_list();
1111
+ if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {
1112
+ if (!str->quote_mark() && !str->is_delayed()) {
1113
+ if (const Color* col = name_to_color(str->value())) {
1114
+ Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);
1115
+ c->pstate(str->pstate());
1116
+ c->disp(str->value());
1117
+ key = c;
1118
+ }
1119
+ }
1120
+ }
1121
+
1122
+ if (!(lex< exactly<':'> >()))
1123
+ { error("invalid syntax", pstate); }
1124
+
1125
+ Expression* value = parse_space_list();
1126
+
1127
+ (*map) << std::make_pair(key, value);
1128
+ }
1129
+
1130
+ ParserState ps = map->pstate();
1131
+ ps.offset = pstate - ps + pstate.offset;
1132
+ map->pstate(ps);
1133
+
1134
+ return map;
1135
+ }
1136
+
1137
+ // parse list returns either a space separated list,
1138
+ // a comma separated list or any bare expression found.
1139
+ // so to speak: we unwrap items from lists if possible here!
1140
+ Expression* Parser::parse_list()
1141
+ {
1142
+ // parse list is relly just an alias
1143
+ return parse_comma_list();
1144
+ }
1145
+
1146
+ // will return singletons unwrapped
1147
+ Expression* Parser::parse_comma_list()
1148
+ {
1149
+ // check if we have an empty list
1150
+ // return the empty list as such
1151
+ if (peek_css< alternatives <
1152
+ // exactly<'!'>,
1153
+ exactly<';'>,
1154
+ exactly<'}'>,
1155
+ exactly<'{'>,
1156
+ exactly<')'>,
1157
+ exactly<':'>,
1158
+ exactly<ellipsis>,
1159
+ default_flag,
1160
+ global_flag
1161
+ > >(position))
1162
+ { return SASS_MEMORY_NEW(ctx.mem, List, pstate, 0); }
1163
+
1164
+ // now try to parse a space list
1165
+ Expression* list = parse_space_list();
1166
+ // if it's a singleton, return it (don't wrap it)
1167
+ if (!peek_css< exactly<','> >(position)) return list;
1168
+
1169
+ // if we got so far, we actually do have a comma list
1170
+ List* comma_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_COMMA);
1171
+ // wrap the first expression
1172
+ (*comma_list) << list;
1173
+
1174
+ while (lex_css< exactly<','> >())
1175
+ {
1176
+ // check for abort condition
1177
+ if (peek_css< alternatives <
1178
+ exactly<';'>,
1179
+ exactly<'}'>,
1180
+ exactly<'{'>,
1181
+ exactly<')'>,
1182
+ exactly<':'>,
1183
+ exactly<ellipsis>,
1184
+ default_flag,
1185
+ global_flag
1186
+ > >(position)
1187
+ ) { break; }
1188
+ // otherwise add another expression
1189
+ (*comma_list) << parse_space_list();
1190
+ }
1191
+ // return the list
1192
+ return comma_list;
1193
+ }
1194
+ // EO parse_comma_list
1195
+
1196
+ // will return singletons unwrapped
1197
+ Expression* Parser::parse_space_list()
1198
+ {
1199
+ Expression* disj1 = parse_disjunction();
1200
+ // if it's a singleton, return it (don't wrap it)
1201
+ if (peek_css< alternatives <
1202
+ // exactly<'!'>,
1203
+ exactly<';'>,
1204
+ exactly<'}'>,
1205
+ exactly<'{'>,
1206
+ exactly<')'>,
1207
+ exactly<','>,
1208
+ exactly<':'>,
1209
+ exactly<ellipsis>,
1210
+ default_flag,
1211
+ global_flag
1212
+ > >(position)
1213
+ ) { return disj1; }
1214
+
1215
+ List* space_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_SPACE);
1216
+ (*space_list) << disj1;
1217
+
1218
+ while (!(peek_css< alternatives <
1219
+ // exactly<'!'>,
1220
+ exactly<';'>,
1221
+ exactly<'}'>,
1222
+ exactly<'{'>,
1223
+ exactly<')'>,
1224
+ exactly<','>,
1225
+ exactly<':'>,
1226
+ exactly<ellipsis>,
1227
+ default_flag,
1228
+ global_flag
1229
+ > >(position)) && peek_css< optional_css_whitespace >() != end
1230
+ ) {
1231
+ // the space is parsed implicitly?
1232
+ (*space_list) << parse_disjunction();
1233
+ }
1234
+ // return the list
1235
+ return space_list;
1236
+ }
1237
+ // EO parse_space_list
1238
+
1239
+ // parse logical OR operation
1240
+ Expression* Parser::parse_disjunction()
1241
+ {
1242
+ // parse the left hand side conjunction
1243
+ Expression* conj = parse_conjunction();
1244
+ // parse multiple right hand sides
1245
+ std::vector<Expression*> operands;
1246
+ while (lex_css< kwd_or >())
1247
+ operands.push_back(parse_conjunction());
1248
+ // if it's a singleton, return it directly
1249
+ if (operands.size() == 0) return conj;
1250
+ // fold all operands into one binary expression
1251
+ return fold_operands(conj, operands, Sass_OP::OR);
1252
+ }
1253
+ // EO parse_disjunction
1254
+
1255
+ // parse logical AND operation
1256
+ Expression* Parser::parse_conjunction()
1257
+ {
1258
+ // parse the left hand side relation
1259
+ Expression* rel = parse_relation();
1260
+ // parse multiple right hand sides
1261
+ std::vector<Expression*> operands;
1262
+ while (lex_css< kwd_and >())
1263
+ operands.push_back(parse_relation());
1264
+ // if it's a singleton, return it directly
1265
+ if (operands.size() == 0) return rel;
1266
+ // fold all operands into one binary expression
1267
+ return fold_operands(rel, operands, Sass_OP::AND);
1268
+ }
1269
+ // EO parse_conjunction
1270
+
1271
+ // parse comparison operations
1272
+ Expression* Parser::parse_relation()
1273
+ {
1274
+ // parse the left hand side expression
1275
+ Expression* lhs = parse_expression();
1276
+ // if it's a singleton, return it (don't wrap it)
1277
+ if (!(peek< alternatives <
1278
+ kwd_eq,
1279
+ kwd_neq,
1280
+ kwd_gte,
1281
+ kwd_gt,
1282
+ kwd_lte,
1283
+ kwd_lt
1284
+ > >(position)))
1285
+ { return lhs; }
1286
+ // parse the operator
1287
+ enum Sass_OP op
1288
+ = lex<kwd_eq>() ? Sass_OP::EQ
1289
+ : lex<kwd_neq>() ? Sass_OP::NEQ
1290
+ : lex<kwd_gte>() ? Sass_OP::GTE
1291
+ : lex<kwd_lte>() ? Sass_OP::LTE
1292
+ : lex<kwd_gt>() ? Sass_OP::GT
1293
+ : lex<kwd_lt>() ? Sass_OP::LT
1294
+ // we checked the possibilites on top of fn
1295
+ : Sass_OP::EQ;
1296
+ // parse the right hand side expression
1297
+ Expression* rhs = parse_expression();
1298
+ // return binary expression with a left and a right hand side
1299
+ return SASS_MEMORY_NEW(ctx.mem, Binary_Expression, lhs->pstate(), op, lhs, rhs);
1300
+ }
1301
+ // parse_relation
1302
+
1303
+ // parse expression valid for operations
1304
+ // called from parse_relation
1305
+ // called from parse_for_directive
1306
+ // called from parse_media_expression
1307
+ // parse addition and subtraction operations
1308
+ Expression* Parser::parse_expression()
1309
+ {
1310
+ Expression* lhs = parse_operators();
1311
+ // if it's a singleton, return it (don't wrap it)
1312
+ if (!(peek< exactly<'+'> >(position) ||
1313
+ // condition is a bit misterious, but some combinations should not be counted as operations
1314
+ (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) ||
1315
+ (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) ||
1316
+ peek< identifier >(position))
1317
+ { return lhs; }
1318
+
1319
+ std::vector<Expression*> operands;
1320
+ std::vector<Sass_OP> operators;
1321
+ while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {
1322
+ operators.push_back(lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB);
1323
+ operands.push_back(parse_operators());
1324
+ }
1325
+
1326
+ if (operands.size() == 0) return lhs;
1327
+ return fold_operands(lhs, operands, operators);
1328
+ }
1329
+
1330
+ // parse addition and subtraction operations
1331
+ Expression* Parser::parse_operators()
1332
+ {
1333
+ Expression* factor = parse_factor();
1334
+ // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
1335
+ if (peek_css< exactly<'%'> >() && factor->concrete_type() == Expression::STRING) {
1336
+ String_Schema* ss = dynamic_cast<String_Schema*>(factor);
1337
+ if (ss && ss->has_interpolants()) return factor;
1338
+ }
1339
+ // if it's a singleton, return it (don't wrap it)
1340
+ if (!peek_css< class_char< static_ops > >()) return factor;
1341
+ // parse more factors and operators
1342
+ std::vector<Expression*> operands; // factors
1343
+ std::vector<enum Sass_OP> operators; // ops
1344
+ // lex operations to apply to lhs
1345
+ while (lex_css< class_char< static_ops > >()) {
1346
+ switch(*lexed.begin) {
1347
+ case '*': operators.push_back(Sass_OP::MUL); break;
1348
+ case '/': operators.push_back(Sass_OP::DIV); break;
1349
+ case '%': operators.push_back(Sass_OP::MOD); break;
1350
+ default: throw std::runtime_error("unknown static op parsed"); break;
1351
+ }
1352
+ operands.push_back(parse_factor());
1353
+ }
1354
+ // operands and operators to binary expression
1355
+ return fold_operands(factor, operands, operators);
1356
+ }
1357
+ // EO parse_operators
1358
+
1359
+
1360
+ // called from parse_operators
1361
+ // called from parse_value_schema
1362
+ Expression* Parser::parse_factor()
1363
+ {
1364
+ lex < css_comments >(false);
1365
+ if (lex_css< exactly<'('> >()) {
1366
+ // parse_map may return a list
1367
+ Expression* value = parse_map();
1368
+ // lex the expected closing parenthesis
1369
+ if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);
1370
+ // expression can be evaluated
1371
+ value->is_delayed(false);
1372
+ // make sure wrapped lists and division expressions are non-delayed within parentheses
1373
+ if (value->concrete_type() == Expression::LIST) {
1374
+ List* l = static_cast<List*>(value);
1375
+ if (!l->empty()) (*l)[0]->is_delayed(false);
1376
+ } else if (typeid(*value) == typeid(Binary_Expression)) {
1377
+ Binary_Expression* b = static_cast<Binary_Expression*>(value);
1378
+ Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());
1379
+ if (lhs && lhs->type() == Sass_OP::DIV) lhs->is_delayed(false);
1380
+ }
1381
+ return value;
1382
+ }
1383
+ else if (peek< ie_property >()) {
1384
+ return parse_ie_property();
1385
+ }
1386
+ else if (peek< ie_keyword_arg >()) {
1387
+ return parse_ie_keyword_arg();
1388
+ }
1389
+ else if (peek< exactly< calc_kwd > >() ||
1390
+ peek< exactly< moz_calc_kwd > >() ||
1391
+ peek< exactly< ms_calc_kwd > >() ||
1392
+ peek< exactly< webkit_calc_kwd > >()) {
1393
+ return parse_calc_function();
1394
+ }
1395
+ else if (lex < functional_schema >()) {
1396
+ return parse_function_call_schema();
1397
+ }
1398
+ else if (lex< identifier_schema >()) {
1399
+ String* string = parse_identifier_schema();
1400
+ if (String_Schema* schema = dynamic_cast<String_Schema*>(string)) {
1401
+ if (lex < exactly < '(' > >()) {
1402
+ *schema << parse_list();
1403
+ lex < exactly < ')' > >();
1404
+ }
1405
+ }
1406
+ return string;
1407
+ }
1408
+ else if (peek< sequence< uri_prefix, W, real_uri_value > >()) {
1409
+ return parse_url_function_string();
1410
+ }
1411
+ else if (peek< re_functional >()) {
1412
+ return parse_function_call();
1413
+ }
1414
+ else if (lex< exactly<'+'> >()) {
1415
+ return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());
1416
+ }
1417
+ else if (lex< exactly<'-'> >()) {
1418
+ return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());
1419
+ }
1420
+ else if (lex< sequence< kwd_not > >()) {
1421
+ return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());
1422
+ }
1423
+ else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {
1424
+ if (parse_number_prefix()) return parse_value(); // prefix is positive
1425
+ return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_value());
1426
+ }
1427
+ else {
1428
+ return parse_value();
1429
+ }
1430
+ }
1431
+
1432
+ // parse one value for a list
1433
+ Expression* Parser::parse_value()
1434
+ {
1435
+ lex< css_comments >(false);
1436
+ if (lex< ampersand >())
1437
+ {
1438
+ return SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); }
1439
+
1440
+ if (lex< kwd_important >())
1441
+ { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); }
1442
+
1443
+ if (const char* stop = peek< value_schema >())
1444
+ { return parse_value_schema(stop); }
1445
+
1446
+ // string may be interpolated
1447
+ if (lex< quoted_string >())
1448
+ { return parse_string(); }
1449
+
1450
+ if (lex< kwd_true >())
1451
+ { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); }
1452
+
1453
+ if (lex< kwd_false >())
1454
+ { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false); }
1455
+
1456
+ if (lex< kwd_null >())
1457
+ { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); }
1458
+
1459
+ if (lex< identifier >()) {
1460
+ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1461
+ }
1462
+
1463
+ if (lex< percentage >())
1464
+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); }
1465
+
1466
+ // match hex number first because 0x000 looks like a number followed by an indentifier
1467
+ if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >())
1468
+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed); }
1469
+
1470
+ if (lex< sequence < exactly <'#'>, identifier > >())
1471
+ { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); }
1472
+
1473
+ // also handle the 10em- foo special case
1474
+ if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())
1475
+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); }
1476
+
1477
+ if (lex< sequence< static_component, one_plus< strict_identifier > > >())
1478
+ { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }
1479
+
1480
+ if (lex< number >())
1481
+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); }
1482
+
1483
+ if (lex< variable >())
1484
+ { return SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed)); }
1485
+
1486
+ // Special case handling for `%` proceeding an interpolant.
1487
+ if (lex< sequence< exactly<'%'>, optional< percentage > > >())
1488
+ { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }
1489
+
1490
+ error("error reading values after " + lexed.to_string(), pstate);
1491
+
1492
+ // unreachable statement
1493
+ return 0;
1494
+ }
1495
+
1496
+ // this parses interpolation inside other strings
1497
+ // means the result should later be quoted again
1498
+ String* Parser::parse_interpolated_chunk(Token chunk, bool constant)
1499
+ {
1500
+ const char* i = chunk.begin;
1501
+ // see if there any interpolants
1502
+ const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);
1503
+ if (!p) {
1504
+ String_Quoted* str_quoted = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(i, chunk.end));
1505
+ if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');
1506
+ str_quoted->is_delayed(true);
1507
+ return str_quoted;
1508
+ }
1509
+
1510
+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
1511
+ while (i < chunk.end) {
1512
+ p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);
1513
+ if (p) {
1514
+ if (i < p) {
1515
+ // accumulate the preceding segment if it's nonempty
1516
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));
1517
+ }
1518
+ // we need to skip anything inside strings
1519
+ // create a new target in parser/prelexer
1520
+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
1521
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1522
+ }
1523
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
1524
+ if (j) { --j;
1525
+ // parse the interpolant and accumulate it
1526
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
1527
+ interp_node->is_interpolant(true);
1528
+ (*schema) << interp_node;
1529
+ i = j;
1530
+ }
1531
+ else {
1532
+ // throw an error if the interpolant is unterminated
1533
+ error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);
1534
+ }
1535
+ }
1536
+ else { // no interpolants left; add the last segment if nonempty
1537
+ // check if we need quotes here (was not sure after merge)
1538
+ if (i < chunk.end) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, chunk.end));
1539
+ break;
1540
+ }
1541
+ ++ i;
1542
+ }
1543
+
1544
+ return schema;
1545
+ }
1546
+
1547
+ String_Constant* Parser::parse_static_expression()
1548
+ {
1549
+ if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {
1550
+ return parse_static_value();
1551
+ }
1552
+ return 0;
1553
+ }
1554
+
1555
+ String_Constant* Parser::parse_static_value()
1556
+ {
1557
+ lex< static_value >();
1558
+ Token str(lexed);
1559
+ --str.end;
1560
+ --position;
1561
+
1562
+ String_Constant* str_node = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, str.time_wspace());
1563
+ str_node->is_delayed(true);
1564
+ return str_node;
1565
+ }
1566
+
1567
+ String* Parser::parse_string()
1568
+ {
1569
+ return parse_interpolated_chunk(Token(lexed));
1570
+ }
1571
+
1572
+ String* Parser::parse_ie_property()
1573
+ {
1574
+ lex< ie_property >();
1575
+ Token str(lexed);
1576
+ const char* i = str.begin;
1577
+ // see if there any interpolants
1578
+ const char* p = find_first_in_interval< exactly<hash_lbrace> >(str.begin, str.end);
1579
+ if (!p) {
1580
+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(str.begin, str.end));
1581
+ }
1582
+
1583
+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
1584
+ while (i < str.end) {
1585
+ p = find_first_in_interval< exactly<hash_lbrace> >(i, str.end);
1586
+ if (p) {
1587
+ if (i < p) {
1588
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // accumulate the preceding segment if it's nonempty
1589
+ }
1590
+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
1591
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1592
+ }
1593
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
1594
+ if (j) {
1595
+ // parse the interpolant and accumulate it
1596
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
1597
+ interp_node->is_interpolant(true);
1598
+ (*schema) << interp_node;
1599
+ i = j;
1600
+ }
1601
+ else {
1602
+ // throw an error if the interpolant is unterminated
1603
+ error("unterminated interpolant inside IE function " + str.to_string(), pstate);
1604
+ }
1605
+ }
1606
+ else { // no interpolants left; add the last segment if nonempty
1607
+ if (i < str.end) {
1608
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, str.end));
1609
+ }
1610
+ break;
1611
+ }
1612
+ }
1613
+ return schema;
1614
+ }
1615
+
1616
+ String* Parser::parse_ie_keyword_arg()
1617
+ {
1618
+ String_Schema* kwd_arg = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate, 3);
1619
+ if (lex< variable >()) {
1620
+ *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed));
1621
+ } else {
1622
+ lex< alternatives< identifier_schema, identifier > >();
1623
+ *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1624
+ }
1625
+ lex< exactly<'='> >();
1626
+ *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1627
+ if (peek< variable >()) *kwd_arg << parse_list();
1628
+ else if (lex< number >()) *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, Util::normalize_decimals(lexed));
1629
+ else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); }
1630
+ return kwd_arg;
1631
+ }
1632
+
1633
+ String_Schema* Parser::parse_value_schema(const char* stop)
1634
+ {
1635
+ // initialize the string schema object to add tokens
1636
+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
1637
+
1638
+ if (peek<exactly<'}'>>()) {
1639
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1640
+ }
1641
+
1642
+ const char* e = 0;
1643
+ size_t num_items = 0;
1644
+ while (position < stop) {
1645
+ // parse space between tokens
1646
+ if (lex< spaces >() && num_items) {
1647
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");
1648
+ }
1649
+ if ((e = peek< re_functional >()) && e < stop) {
1650
+ (*schema) << parse_function_call();
1651
+ }
1652
+ // lex an interpolant /#{...}/
1653
+ else if (lex< exactly < hash_lbrace > >()) {
1654
+ // Try to lex static expression first
1655
+ if (peek< exactly< rbrace > >()) {
1656
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1657
+ }
1658
+ if (lex< re_static_expression >()) {
1659
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1660
+ } else {
1661
+ (*schema) << parse_list();
1662
+ }
1663
+ // ToDo: no error check here?
1664
+ lex < exactly < rbrace > >();
1665
+ }
1666
+ // lex some string constants
1667
+ else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) {
1668
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
1669
+ if (*position == '"' || *position == '\'') {
1670
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");
1671
+ }
1672
+ }
1673
+ // lex a quoted string
1674
+ else if (lex< quoted_string >()) {
1675
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed, '"');
1676
+ if (*position == '"' || *position == '\'' || alpha(position)) {
1677
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");
1678
+ }
1679
+ }
1680
+ // lex (normalized) variable
1681
+ else if (lex< variable >()) {
1682
+ std::string name(Util::normalize_underscores(lexed));
1683
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, name);
1684
+ }
1685
+ // lex percentage value
1686
+ else if (lex< percentage >()) {
1687
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed);
1688
+ }
1689
+ // lex dimension value
1690
+ else if (lex< dimension >()) {
1691
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed);
1692
+ }
1693
+ // lex number value
1694
+ else if (lex< number >()) {
1695
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed);
1696
+ }
1697
+ // lex hex color value
1698
+ else if (lex< sequence < hex, negate < exactly < '-' > > > >()) {
1699
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed);
1700
+ }
1701
+ else if (lex< sequence < exactly <'#'>, identifier > >()) {
1702
+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed);
1703
+ }
1704
+ // lex a value in parentheses
1705
+ else if (peek< parenthese_scope >()) {
1706
+ (*schema) << parse_factor();
1707
+ }
1708
+ else {
1709
+ return schema;
1710
+ }
1711
+ ++num_items;
1712
+ }
1713
+ return schema;
1714
+ }
1715
+
1716
+ // this parses interpolation outside other strings
1717
+ // means the result must not be quoted again later
1718
+ String* Parser::parse_identifier_schema()
1719
+ {
1720
+ Token id(lexed);
1721
+ const char* i = id.begin;
1722
+ // see if there any interpolants
1723
+ const char* p = find_first_in_interval< exactly<hash_lbrace> >(id.begin, id.end);
1724
+ if (!p) {
1725
+ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(id.begin, id.end));
1726
+ }
1727
+
1728
+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
1729
+ while (i < id.end) {
1730
+ p = find_first_in_interval< exactly<hash_lbrace> >(i, id.end);
1731
+ if (p) {
1732
+ if (i < p) {
1733
+ // accumulate the preceding segment if it's nonempty
1734
+ const char* o = position; position = i;
1735
+ *schema << parse_value_schema(p);
1736
+ position = o;
1737
+ }
1738
+ // we need to skip anything inside strings
1739
+ // create a new target in parser/prelexer
1740
+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p;
1741
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1742
+ }
1743
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace
1744
+ if (j) {
1745
+ // parse the interpolant and accumulate it
1746
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
1747
+ interp_node->is_interpolant(true);
1748
+ (*schema) << interp_node;
1749
+ schema->has_interpolants(true);
1750
+ i = j;
1751
+ }
1752
+ else {
1753
+ // throw an error if the interpolant is unterminated
1754
+ error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate);
1755
+ }
1756
+ }
1757
+ else { // no interpolants left; add the last segment if nonempty
1758
+ if (i < end) {
1759
+ const char* o = position; position = i;
1760
+ *schema << parse_value_schema(id.end);
1761
+ position = o;
1762
+ }
1763
+ break;
1764
+ }
1765
+ }
1766
+ return schema;
1767
+ }
1768
+
1769
+ // calc functions should preserve arguments
1770
+ Function_Call* Parser::parse_calc_function()
1771
+ {
1772
+ lex< identifier >();
1773
+ std::string name(lexed);
1774
+ ParserState call_pos = pstate;
1775
+ lex< exactly<'('> >();
1776
+ ParserState arg_pos = pstate;
1777
+ const char* arg_beg = position;
1778
+ parse_list();
1779
+ const char* arg_end = position;
1780
+ lex< skip_over_scopes <
1781
+ exactly < '(' >,
1782
+ exactly < ')' >
1783
+ > >();
1784
+
1785
+ Argument* arg = SASS_MEMORY_NEW(ctx.mem, Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));
1786
+ Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, arg_pos);
1787
+ *args << arg;
1788
+ return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args);
1789
+ }
1790
+
1791
+ String* Parser::parse_url_function_string()
1792
+ {
1793
+ const char* p = position;
1794
+
1795
+ lex< uri_prefix >();
1796
+ std::string prefix = lexed;
1797
+
1798
+ lex< real_uri_value >(false);
1799
+ std::string uri = lexed;
1800
+
1801
+ if (peek< exactly< hash_lbrace > >()) {
1802
+ const char* pp = position;
1803
+ // TODO: error checking for unclosed interpolants
1804
+ while (peek< exactly< hash_lbrace > >(pp)) {
1805
+ pp = sequence< interpolant, real_uri_value >(pp);
1806
+ }
1807
+ position = peek< real_uri_suffix >(pp);
1808
+ return parse_interpolated_chunk(Token(p, position));
1809
+ } else {
1810
+ lex< real_uri_suffix >();
1811
+ std::string res = prefix + Util::rtrim(uri) + lexed.to_string();
1812
+ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res);
1813
+ }
1814
+
1815
+ }
1816
+
1817
+ Function_Call* Parser::parse_function_call()
1818
+ {
1819
+ lex< identifier >();
1820
+ std::string name(lexed);
1821
+
1822
+ ParserState call_pos = pstate;
1823
+ Arguments* args = parse_arguments();
1824
+ return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args);
1825
+ }
1826
+
1827
+ Function_Call_Schema* Parser::parse_function_call_schema()
1828
+ {
1829
+ String* name = parse_identifier_schema();
1830
+ ParserState source_position_of_call = pstate;
1831
+
1832
+ Function_Call_Schema* the_call = SASS_MEMORY_NEW(ctx.mem, Function_Call_Schema, source_position_of_call, name, parse_arguments());
1833
+ return the_call;
1834
+ }
1835
+
1836
+ Content* Parser::parse_content_directive()
1837
+ {
1838
+ if (stack.back() != mixin_def) {
1839
+ error("@content may only be used within a mixin", pstate);
1840
+ }
1841
+ return SASS_MEMORY_NEW(ctx.mem, Content, pstate);
1842
+ }
1843
+
1844
+ If* Parser::parse_if_directive(bool else_if)
1845
+ {
1846
+ ParserState if_source_position = pstate;
1847
+ Expression* predicate = parse_list();
1848
+ predicate->is_delayed(false);
1849
+ Block* block = parse_block();
1850
+ Block* alternative = 0;
1851
+
1852
+ if (lex< elseif_directive >()) {
1853
+ alternative = SASS_MEMORY_NEW(ctx.mem, Block, pstate);
1854
+ (*alternative) << parse_if_directive(true);
1855
+ }
1856
+ else if (lex< kwd_else_directive >()) {
1857
+ alternative = parse_block();
1858
+ }
1859
+ return SASS_MEMORY_NEW(ctx.mem, If, if_source_position, predicate, block, alternative);
1860
+ }
1861
+
1862
+ For* Parser::parse_for_directive()
1863
+ {
1864
+ ParserState for_source_position = pstate;
1865
+ lex_variable();
1866
+ std::string var(Util::normalize_underscores(lexed));
1867
+ if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);
1868
+ Expression* lower_bound = parse_expression();
1869
+ lower_bound->is_delayed(false);
1870
+ bool inclusive = false;
1871
+ if (lex< kwd_through >()) inclusive = true;
1872
+ else if (lex< kwd_to >()) inclusive = false;
1873
+ else error("expected 'through' or 'to' keyword in @for directive", pstate);
1874
+ Expression* upper_bound = parse_expression();
1875
+ upper_bound->is_delayed(false);
1876
+ Block* body = parse_block();
1877
+ return SASS_MEMORY_NEW(ctx.mem, For, for_source_position, var, lower_bound, upper_bound, body, inclusive);
1878
+ }
1879
+
1880
+ // helper to parse a var token
1881
+ Token Parser::lex_variable()
1882
+ {
1883
+ // peek for dollar sign first
1884
+ if (!peek< exactly <'$'> >()) {
1885
+ css_error("Invalid CSS", " after ", ": expected \"$\", was ");
1886
+ }
1887
+ // we expect a simple identfier as the call name
1888
+ if (!lex< sequence < exactly <'$'>, identifier > >()) {
1889
+ lex< exactly <'$'> >(); // move pstate and position up
1890
+ css_error("Invalid CSS", " after ", ": expected identifier, was ");
1891
+ }
1892
+ // return object
1893
+ return token;
1894
+ }
1895
+ // helper to parse identifier
1896
+ Token Parser::lex_identifier()
1897
+ {
1898
+ // we expect a simple identfier as the call name
1899
+ if (!lex< identifier >()) { // ToDo: pstate wrong?
1900
+ css_error("Invalid CSS", " after ", ": expected identifier, was ");
1901
+ }
1902
+ // return object
1903
+ return token;
1904
+ }
1905
+
1906
+ Each* Parser::parse_each_directive()
1907
+ {
1908
+ ParserState each_source_position = pstate;
1909
+ std::vector<std::string> vars;
1910
+ lex_variable();
1911
+ vars.push_back(Util::normalize_underscores(lexed));
1912
+ while (lex< exactly<','> >()) {
1913
+ if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);
1914
+ vars.push_back(Util::normalize_underscores(lexed));
1915
+ }
1916
+ if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);
1917
+ Expression* list = parse_list();
1918
+ list->is_delayed(false);
1919
+ if (list->concrete_type() == Expression::LIST) {
1920
+ List* l = static_cast<List*>(list);
1921
+ for (size_t i = 0, L = l->length(); i < L; ++i) {
1922
+ (*l)[i]->is_delayed(false);
1923
+ }
1924
+ }
1925
+ Block* body = parse_block();
1926
+ return SASS_MEMORY_NEW(ctx.mem, Each, each_source_position, vars, list, body);
1927
+ }
1928
+
1929
+ // called after parsing `kwd_while_directive`
1930
+ While* Parser::parse_while_directive()
1931
+ {
1932
+ // create the initial while call object
1933
+ While* call = SASS_MEMORY_NEW(ctx.mem, While, pstate, 0, 0);
1934
+ // parse mandatory predicate
1935
+ Expression* predicate = parse_list();
1936
+ predicate->is_delayed(false);
1937
+ call->predicate(predicate);
1938
+ // parse mandatory block
1939
+ call->block(parse_block());
1940
+ // return ast node
1941
+ return call;
1942
+ }
1943
+
1944
+ // EO parse_while_directive
1945
+ Media_Block* Parser::parse_media_block()
1946
+ {
1947
+ Media_Block* media_block = SASS_MEMORY_NEW(ctx.mem, Media_Block, pstate, 0, 0);
1948
+ media_block->media_queries(parse_media_queries());
1949
+
1950
+ Media_Block* prev_media_block = last_media_block;
1951
+ last_media_block = media_block;
1952
+ media_block->block(parse_css_block());
1953
+ last_media_block = prev_media_block;
1954
+
1955
+ return media_block;
1956
+ }
1957
+
1958
+ List* Parser::parse_media_queries()
1959
+ {
1960
+ List* media_queries = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_COMMA);
1961
+ if (!peek_css < exactly <'{'> >()) (*media_queries) << parse_media_query();
1962
+ while (lex_css < exactly <','> >()) (*media_queries) << parse_media_query();
1963
+ return media_queries;
1964
+ }
1965
+
1966
+ // Expression* Parser::parse_media_query()
1967
+ Media_Query* Parser::parse_media_query()
1968
+ {
1969
+ Media_Query* media_query = SASS_MEMORY_NEW(ctx.mem, Media_Query, pstate);
1970
+
1971
+ lex < css_comments >(false);
1972
+ if (lex < word < not_kwd > >()) media_query->is_negated(true);
1973
+ else if (lex < word < only_kwd > >()) media_query->is_restricted(true);
1974
+
1975
+ lex < css_comments >(false);
1976
+ if (lex < identifier_schema >()) media_query->media_type(parse_identifier_schema());
1977
+ else if (lex < identifier >()) media_query->media_type(parse_interpolated_chunk(lexed));
1978
+ else (*media_query) << parse_media_expression();
1979
+
1980
+ while (lex_css < word < and_kwd > >()) (*media_query) << parse_media_expression();
1981
+ if (lex < identifier_schema >()) {
1982
+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
1983
+ *schema << media_query->media_type();
1984
+ *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");
1985
+ *schema << parse_identifier_schema();
1986
+ media_query->media_type(schema);
1987
+ }
1988
+ while (lex_css < word < and_kwd > >()) (*media_query) << parse_media_expression();
1989
+ return media_query;
1990
+ }
1991
+
1992
+ Media_Query_Expression* Parser::parse_media_expression()
1993
+ {
1994
+ if (lex < identifier_schema >()) {
1995
+ String* ss = parse_identifier_schema();
1996
+ return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, pstate, ss, 0, true);
1997
+ }
1998
+ if (!lex_css< exactly<'('> >()) {
1999
+ error("media query expression must begin with '('", pstate);
2000
+ }
2001
+ Expression* feature = 0;
2002
+ if (peek_css< exactly<')'> >()) {
2003
+ error("media feature required in media query expression", pstate);
2004
+ }
2005
+ feature = parse_expression();
2006
+ Expression* expression = 0;
2007
+ if (lex< exactly<':'> >()) {
2008
+ expression = parse_list();
2009
+ }
2010
+ if (!lex< exactly<')'> >()) {
2011
+ error("unclosed parenthesis in media query expression", pstate);
2012
+ }
2013
+ return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, feature->pstate(), feature, expression);
2014
+ }
2015
+
2016
+ // lexed after `kwd_supports_directive`
2017
+ // these are very similar to media blocks
2018
+ Supports_Block* Parser::parse_supports_directive()
2019
+ {
2020
+ Supports_Condition* cond = parse_supports_condition();
2021
+ // create the ast node object for the support queries
2022
+ Supports_Block* query = SASS_MEMORY_NEW(ctx.mem, Supports_Block, pstate, cond);
2023
+ // additional block is mandatory
2024
+ // parse inner block
2025
+ query->block(parse_block());
2026
+ // return ast node
2027
+ return query;
2028
+ }
2029
+
2030
+ // parse one query operation
2031
+ // may encounter nested queries
2032
+ Supports_Condition* Parser::parse_supports_condition()
2033
+ {
2034
+ lex < css_whitespace >();
2035
+ Supports_Condition* cond = parse_supports_negation();
2036
+ if (!cond) cond = parse_supports_operator();
2037
+ if (!cond) cond = parse_supports_interpolation();
2038
+ return cond;
2039
+ }
2040
+
2041
+ Supports_Condition* Parser::parse_supports_negation()
2042
+ {
2043
+ if (!lex < kwd_not >()) return 0;
2044
+
2045
+ Supports_Condition* cond = parse_supports_condition_in_parens();
2046
+ return SASS_MEMORY_NEW(ctx.mem, Supports_Negation, pstate, cond);
2047
+ }
2048
+
2049
+ Supports_Condition* Parser::parse_supports_operator()
2050
+ {
2051
+ Supports_Condition* cond = parse_supports_condition_in_parens();
2052
+ if (!cond) return 0;
2053
+
2054
+ while (lex < kwd_and >() || lex < kwd_or >()) {
2055
+ Supports_Operator::Operand op = Supports_Operator::OR;
2056
+ if (lexed.to_string() == "and") op = Supports_Operator::AND;
2057
+
2058
+ lex < css_whitespace >();
2059
+ Supports_Condition* right = parse_supports_condition_in_parens();
2060
+
2061
+ // Supports_Condition* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Condition, *static_cast<Supports_Condition*>(cond));
2062
+ cond = SASS_MEMORY_NEW(ctx.mem, Supports_Operator, pstate, cond, right, op);
2063
+ }
2064
+ return cond;
2065
+ }
2066
+
2067
+ Supports_Condition* Parser::parse_supports_interpolation()
2068
+ {
2069
+ if (!lex < interpolant >()) return 0;
2070
+
2071
+ String* interp = parse_interpolated_chunk(lexed);
2072
+ if (!interp) return 0;
2073
+
2074
+ return SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation, pstate, interp);
2075
+ }
2076
+
2077
+ // TODO: This needs some major work. Although feature conditions
2078
+ // look like declarations their semantics differ siginificantly
2079
+ Supports_Condition* Parser::parse_supports_declaration()
2080
+ {
2081
+ Supports_Condition* cond = 0;
2082
+ // parse something declaration like
2083
+ Declaration* declaration = parse_declaration();
2084
+ if (!declaration) error("@supports condition expected declaration", pstate);
2085
+ cond = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration,
2086
+ declaration->pstate(),
2087
+ declaration->property(),
2088
+ declaration->value());
2089
+ // ToDo: maybe we need an additional error condition?
2090
+ return cond;
2091
+ }
2092
+
2093
+ Supports_Condition* Parser::parse_supports_condition_in_parens()
2094
+ {
2095
+ Supports_Condition* interp = parse_supports_interpolation();
2096
+ if (interp != 0) return interp;
2097
+
2098
+ if (!lex < exactly <'('> >()) return 0;
2099
+ lex < css_whitespace >();
2100
+
2101
+ Supports_Condition* cond = parse_supports_condition();
2102
+ if (cond != 0) {
2103
+ if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);
2104
+ } else {
2105
+ cond = parse_supports_declaration();
2106
+ if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);
2107
+ }
2108
+ lex < css_whitespace >();
2109
+ return cond;
2110
+ }
2111
+
2112
+ At_Root_Block* Parser::parse_at_root_block()
2113
+ {
2114
+ ParserState at_source_position = pstate;
2115
+ Block* body = 0;
2116
+ At_Root_Expression* expr = 0;
2117
+ Lookahead lookahead_result;
2118
+ LOCAL_FLAG(in_at_root, true);
2119
+ if (lex< exactly<'('> >()) {
2120
+ expr = parse_at_root_expression();
2121
+ }
2122
+ if (peek < exactly<'{'> >()) {
2123
+ body = parse_block(true);
2124
+ }
2125
+ else if ((lookahead_result = lookahead_for_selector(position)).found) {
2126
+ Ruleset* r = parse_ruleset(lookahead_result, false);
2127
+ body = SASS_MEMORY_NEW(ctx.mem, Block, r->pstate(), 1, true);
2128
+ *body << r;
2129
+ }
2130
+ At_Root_Block* at_root = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, at_source_position, body);
2131
+ if (expr) at_root->expression(expr);
2132
+ return at_root;
2133
+ }
2134
+
2135
+ At_Root_Expression* Parser::parse_at_root_expression()
2136
+ {
2137
+ if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);
2138
+
2139
+ if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {
2140
+ css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");
2141
+ }
2142
+
2143
+ Declaration* declaration = parse_declaration();
2144
+ List* value = SASS_MEMORY_NEW(ctx.mem, List, declaration->value()->pstate(), 1);
2145
+
2146
+ if (declaration->value()->concrete_type() == Expression::LIST) {
2147
+ value = static_cast<List*>(declaration->value());
2148
+ }
2149
+ else *value << declaration->value();
2150
+
2151
+ At_Root_Expression* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression,
2152
+ declaration->pstate(),
2153
+ declaration->property(),
2154
+ value);
2155
+ if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate);
2156
+ return cond;
2157
+ }
2158
+
2159
+ At_Rule* Parser::parse_at_rule()
2160
+ {
2161
+ std::string kwd(lexed);
2162
+ At_Rule* at_rule = SASS_MEMORY_NEW(ctx.mem, At_Rule, pstate, kwd);
2163
+ Lookahead lookahead = lookahead_for_include(position);
2164
+ if (lookahead.found && !lookahead.has_interpolants) {
2165
+ at_rule->selector(parse_selector_list(true));
2166
+ }
2167
+
2168
+ lex < css_comments >(false);
2169
+
2170
+ if (lex < static_property >()) {
2171
+ at_rule->value(parse_interpolated_chunk(Token(lexed)));
2172
+ } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {
2173
+ at_rule->value(parse_list());
2174
+ }
2175
+
2176
+ lex < css_comments >(false);
2177
+
2178
+ if (peek< exactly<'{'> >()) {
2179
+ at_rule->block(parse_block());
2180
+ }
2181
+
2182
+ return at_rule;
2183
+ }
2184
+
2185
+ Warning* Parser::parse_warning()
2186
+ {
2187
+ return SASS_MEMORY_NEW(ctx.mem, Warning, pstate, parse_list());
2188
+ }
2189
+
2190
+ Error* Parser::parse_error()
2191
+ {
2192
+ return SASS_MEMORY_NEW(ctx.mem, Error, pstate, parse_list());
2193
+ }
2194
+
2195
+ Debug* Parser::parse_debug()
2196
+ {
2197
+ return SASS_MEMORY_NEW(ctx.mem, Debug, pstate, parse_list());
2198
+ }
2199
+
2200
+ Return* Parser::parse_return_directive()
2201
+ {
2202
+ // check that we do not have an empty list (ToDo: check if we got all cases)
2203
+ if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >())
2204
+ { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); }
2205
+ return SASS_MEMORY_NEW(ctx.mem, Return, pstate, parse_list());
2206
+ }
2207
+
2208
+ Lookahead Parser::lookahead_for_selector(const char* start)
2209
+ {
2210
+ // init result struct
2211
+ Lookahead rv = Lookahead();
2212
+ // get start position
2213
+ const char* p = start ? start : position;
2214
+ // match in one big "regex"
2215
+ rv.error = p;
2216
+ if (const char* q =
2217
+ peek <
2218
+ one_plus <
2219
+ alternatives <
2220
+ // consume whitespace and comments
2221
+ spaces, block_comment, line_comment,
2222
+ // match `/deep/` selector (pass-trough)
2223
+ // there is no functionality for it yet
2224
+ schema_reference_combinator,
2225
+ // match selector ops /[*&%,()\[\]]/
2226
+ class_char < selector_lookahead_ops >,
2227
+ // match selector combinators /[>+~]/
2228
+ class_char < selector_combinator_ops >,
2229
+ // match attribute compare operators
2230
+ alternatives <
2231
+ exact_match, class_match, dash_match,
2232
+ prefix_match, suffix_match, substring_match
2233
+ >,
2234
+ // main selector match
2235
+ sequence <
2236
+ // allow namespace prefix
2237
+ optional < namespace_schema >,
2238
+ // modifiers prefixes
2239
+ alternatives <
2240
+ sequence <
2241
+ exactly <'#'>,
2242
+ // not for interpolation
2243
+ negate < exactly <'{'> >
2244
+ >,
2245
+ // class match
2246
+ exactly <'.'>,
2247
+ // single or double colon
2248
+ optional < pseudo_prefix >
2249
+ >,
2250
+ // accept hypens in token
2251
+ one_plus < sequence <
2252
+ // can start with hyphens
2253
+ zero_plus < exactly<'-'> >,
2254
+ // now the main token
2255
+ alternatives <
2256
+ kwd_optional,
2257
+ exactly <'*'>,
2258
+ quoted_string,
2259
+ interpolant,
2260
+ identifier,
2261
+ percentage,
2262
+ dimension,
2263
+ variable,
2264
+ alnum
2265
+ >
2266
+ > >,
2267
+ // can also end with hyphens
2268
+ zero_plus < exactly<'-'> >
2269
+ >
2270
+ >
2271
+ >
2272
+ >(p)
2273
+ ) {
2274
+ while (p < q) {
2275
+ // did we have interpolations?
2276
+ if (*p == '#' && *(p+1) == '{') {
2277
+ rv.has_interpolants = true;
2278
+ p = q; break;
2279
+ }
2280
+ ++ p;
2281
+ }
2282
+ // store anyway }
2283
+
2284
+
2285
+ // ToDo: remove
2286
+ rv.error = q;
2287
+ rv.position = q;
2288
+ // check expected opening bracket
2289
+ // only after successfull matching
2290
+ if (peek < exactly<'{'> >(q)) rv.found = q;
2291
+ // else if (peek < exactly<';'> >(q)) rv.found = q;
2292
+ // else if (peek < exactly<'}'> >(q)) rv.found = q;
2293
+ if (rv.found || *p == 0) rv.error = 0;
2294
+ }
2295
+
2296
+ rv.parsable = ! rv.has_interpolants;
2297
+
2298
+ // return result
2299
+ return rv;
2300
+
2301
+ }
2302
+ // EO lookahead_for_selector
2303
+
2304
+ // used in parse_block_nodes and parse_at_rule
2305
+ // ToDo: actual usage is still not really clear to me?
2306
+ Lookahead Parser::lookahead_for_include(const char* start)
2307
+ {
2308
+ // we actually just lookahead for a selector
2309
+ Lookahead rv = lookahead_for_selector(start);
2310
+ // but the "found" rules are different
2311
+ if (const char* p = rv.position) {
2312
+ // check for additional abort condition
2313
+ if (peek < exactly<';'> >(p)) rv.found = p;
2314
+ else if (peek < exactly<'}'> >(p)) rv.found = p;
2315
+ }
2316
+ // return result
2317
+ return rv;
2318
+ }
2319
+ // EO lookahead_for_include
2320
+
2321
+ // look ahead for a token with interpolation in it
2322
+ // we mostly use the result if there is an interpolation
2323
+ // everything that passes here gets parsed as one schema
2324
+ // meaning it will not be parsed as a space separated list
2325
+ Lookahead Parser::lookahead_for_value(const char* start)
2326
+ {
2327
+ // init result struct
2328
+ Lookahead rv = Lookahead();
2329
+ // get start position
2330
+ const char* p = start ? start : position;
2331
+ // match in one big "regex"
2332
+ if (const char* q =
2333
+ peek <
2334
+ one_plus <
2335
+ alternatives <
2336
+ // consume whitespace
2337
+ block_comment, spaces,
2338
+ // main tokens
2339
+ interpolant,
2340
+ identifier,
2341
+ variable,
2342
+ // issue #442
2343
+ sequence <
2344
+ parenthese_scope,
2345
+ interpolant
2346
+ >
2347
+ >
2348
+ >
2349
+ >(p)
2350
+ ) {
2351
+ while (p < q) {
2352
+ // did we have interpolations?
2353
+ if (*p == '#' && *(p+1) == '{') {
2354
+ rv.has_interpolants = true;
2355
+ p = q; break;
2356
+ }
2357
+ ++ p;
2358
+ }
2359
+ // store anyway
2360
+ // ToDo: remove
2361
+ rv.position = q;
2362
+ // check expected opening bracket
2363
+ // only after successfull matching
2364
+ if (peek < exactly<'{'> >(q)) rv.found = q;
2365
+ else if (peek < exactly<';'> >(q)) rv.found = q;
2366
+ else if (peek < exactly<'}'> >(q)) rv.found = q;
2367
+ }
2368
+
2369
+ // return result
2370
+ return rv;
2371
+ }
2372
+ // EO lookahead_for_value
2373
+
2374
+ void Parser::read_bom()
2375
+ {
2376
+ size_t skip = 0;
2377
+ std::string encoding;
2378
+ bool utf_8 = false;
2379
+ switch ((unsigned char) source[0]) {
2380
+ case 0xEF:
2381
+ skip = check_bom_chars(source, end, utf_8_bom, 3);
2382
+ encoding = "UTF-8";
2383
+ utf_8 = true;
2384
+ break;
2385
+ case 0xFE:
2386
+ skip = check_bom_chars(source, end, utf_16_bom_be, 2);
2387
+ encoding = "UTF-16 (big endian)";
2388
+ break;
2389
+ case 0xFF:
2390
+ skip = check_bom_chars(source, end, utf_16_bom_le, 2);
2391
+ skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);
2392
+ encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");
2393
+ break;
2394
+ case 0x00:
2395
+ skip = check_bom_chars(source, end, utf_32_bom_be, 4);
2396
+ encoding = "UTF-32 (big endian)";
2397
+ break;
2398
+ case 0x2B:
2399
+ skip = check_bom_chars(source, end, utf_7_bom_1, 4)
2400
+ | check_bom_chars(source, end, utf_7_bom_2, 4)
2401
+ | check_bom_chars(source, end, utf_7_bom_3, 4)
2402
+ | check_bom_chars(source, end, utf_7_bom_4, 4)
2403
+ | check_bom_chars(source, end, utf_7_bom_5, 5);
2404
+ encoding = "UTF-7";
2405
+ break;
2406
+ case 0xF7:
2407
+ skip = check_bom_chars(source, end, utf_1_bom, 3);
2408
+ encoding = "UTF-1";
2409
+ break;
2410
+ case 0xDD:
2411
+ skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);
2412
+ encoding = "UTF-EBCDIC";
2413
+ break;
2414
+ case 0x0E:
2415
+ skip = check_bom_chars(source, end, scsu_bom, 3);
2416
+ encoding = "SCSU";
2417
+ break;
2418
+ case 0xFB:
2419
+ skip = check_bom_chars(source, end, bocu_1_bom, 3);
2420
+ encoding = "BOCU-1";
2421
+ break;
2422
+ case 0x84:
2423
+ skip = check_bom_chars(source, end, gb_18030_bom, 4);
2424
+ encoding = "GB-18030";
2425
+ break;
2426
+ }
2427
+ if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate);
2428
+ position += skip;
2429
+ }
2430
+
2431
+ size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)
2432
+ {
2433
+ size_t skip = 0;
2434
+ if (src + len > end) return 0;
2435
+ for (size_t i = 0; i < len; ++i, ++skip) {
2436
+ if ((unsigned char) src[i] != bom[i]) return 0;
2437
+ }
2438
+ return skip;
2439
+ }
2440
+
2441
+
2442
+ Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, enum Sass_OP op)
2443
+ {
2444
+ for (size_t i = 0, S = operands.size(); i < S; ++i) {
2445
+ base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]);
2446
+ Binary_Expression* b = static_cast<Binary_Expression*>(base);
2447
+ if (op == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
2448
+ base->is_delayed(true);
2449
+ }
2450
+ else {
2451
+ b->left()->is_delayed(false);
2452
+ b->right()->is_delayed(false);
2453
+ }
2454
+ }
2455
+ return base;
2456
+ }
2457
+
2458
+ Expression* Parser::fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<enum Sass_OP>& ops)
2459
+ {
2460
+ for (size_t i = 0, S = operands.size(); i < S; ++i) {
2461
+ base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);
2462
+ Binary_Expression* b = static_cast<Binary_Expression*>(base);
2463
+ if (ops[i] == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
2464
+ base->is_delayed(true);
2465
+ }
2466
+ else {
2467
+ b->left()->is_delayed(false);
2468
+ b->right()->is_delayed(false);
2469
+ }
2470
+ }
2471
+ return base;
2472
+ }
2473
+
2474
+ void Parser::error(std::string msg, Position pos)
2475
+ {
2476
+ throw Error_Invalid(Error_Invalid::syntax, ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);
2477
+ }
2478
+
2479
+ // print a css parsing error with actual context information from parsed source
2480
+ void Parser::css_error(const std::string& msg, const std::string& prefix, const std::string& middle)
2481
+ {
2482
+ int max_len = 18;
2483
+ const char* pos = peek < optional_spaces >();
2484
+
2485
+ const char* last_pos(pos - 1);
2486
+ // backup position to last significant char
2487
+ while ((!*last_pos || Prelexer::is_space(*last_pos)) && last_pos > source) -- last_pos;
2488
+
2489
+ bool ellipsis_left = false;
2490
+ const char* pos_left(last_pos + 1);
2491
+ const char* end_left(last_pos + 1);
2492
+ while (pos_left > source) {
2493
+ if (end_left - pos_left >= max_len) {
2494
+ ellipsis_left = true;
2495
+ break;
2496
+ }
2497
+
2498
+ const char* prev = pos_left - 1;
2499
+ if (*prev == '\r') break;
2500
+ if (*prev == '\n') break;
2501
+ pos_left = prev;
2502
+ }
2503
+ if (pos_left < source) {
2504
+ pos_left = source;
2505
+ }
2506
+
2507
+ bool ellipsis_right = false;
2508
+ const char* end_right(pos);
2509
+ const char* pos_right(pos);
2510
+ while (end_right <= end) {
2511
+ if (end_right - pos_right > max_len) {
2512
+ ellipsis_right = true;
2513
+ break;
2514
+ }
2515
+ if (*end_right == '\r') break;
2516
+ if (*end_right == '\n') break;
2517
+ ++ end_right;
2518
+ }
2519
+ if (end_right > end) end_right = end;
2520
+
2521
+ std::string left(pos_left, end_left);
2522
+ std::string right(pos_right, end_right);
2523
+ if (ellipsis_left) left = ellipsis + left.substr(left.size() - 15);
2524
+ if (ellipsis_right) right = right.substr(right.size() - 15) + ellipsis;
2525
+ // now pass new message to the more generic error function
2526
+ error(msg + prefix + quote(left) + middle + quote(right), pstate);
2527
+ }
2528
+
2529
+ }