sassc 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/libsass/.gitignore +6 -0
  4. data/ext/libsass/.travis.yml +5 -1
  5. data/ext/libsass/Makefile +12 -3
  6. data/ext/libsass/Makefile.am +16 -28
  7. data/ext/libsass/Readme.md +1 -0
  8. data/ext/libsass/appveyor.yml +1 -2
  9. data/ext/libsass/ast.cpp +9 -0
  10. data/ext/libsass/ast.hpp +152 -55
  11. data/ext/libsass/ast_factory.hpp +2 -0
  12. data/ext/libsass/ast_fwd_decl.hpp +1 -0
  13. data/ext/libsass/backtrace.hpp +2 -2
  14. data/ext/libsass/bind.cpp +15 -13
  15. data/ext/libsass/configure.ac +17 -5
  16. data/ext/libsass/constants.cpp +22 -2
  17. data/ext/libsass/constants.hpp +21 -2
  18. data/ext/libsass/context.cpp +79 -57
  19. data/ext/libsass/context.hpp +23 -9
  20. data/ext/libsass/contextualize.cpp +2 -28
  21. data/ext/libsass/contextualize.hpp +6 -10
  22. data/ext/libsass/contextualize_eval.cpp +93 -0
  23. data/ext/libsass/contextualize_eval.hpp +44 -0
  24. data/ext/libsass/contrib/plugin.cpp +57 -0
  25. data/ext/libsass/cssize.cpp +3 -1
  26. data/ext/libsass/debugger.hpp +242 -83
  27. data/ext/libsass/emitter.cpp +1 -1
  28. data/ext/libsass/emitter.hpp +1 -1
  29. data/ext/libsass/environment.hpp +109 -25
  30. data/ext/libsass/error_handling.cpp +3 -3
  31. data/ext/libsass/error_handling.hpp +0 -1
  32. data/ext/libsass/eval.cpp +145 -61
  33. data/ext/libsass/eval.hpp +9 -1
  34. data/ext/libsass/expand.cpp +134 -60
  35. data/ext/libsass/expand.hpp +5 -2
  36. data/ext/libsass/extend.cpp +7 -5
  37. data/ext/libsass/file.cpp +176 -123
  38. data/ext/libsass/file.hpp +44 -7
  39. data/ext/libsass/functions.cpp +36 -17
  40. data/ext/libsass/functions.hpp +2 -2
  41. data/ext/libsass/inspect.cpp +23 -14
  42. data/ext/libsass/inspect.hpp +1 -0
  43. data/ext/libsass/json.cpp +132 -135
  44. data/ext/libsass/lexer.cpp +133 -0
  45. data/ext/libsass/lexer.hpp +239 -0
  46. data/ext/libsass/listize.cpp +83 -0
  47. data/ext/libsass/listize.hpp +41 -0
  48. data/ext/libsass/operation.hpp +2 -0
  49. data/ext/libsass/output.cpp +5 -6
  50. data/ext/libsass/parser.cpp +426 -388
  51. data/ext/libsass/parser.hpp +97 -109
  52. data/ext/libsass/plugins.cpp +15 -2
  53. data/ext/libsass/plugins.hpp +6 -4
  54. data/ext/libsass/position.cpp +52 -17
  55. data/ext/libsass/position.hpp +19 -17
  56. data/ext/libsass/prelexer.cpp +202 -235
  57. data/ext/libsass/prelexer.hpp +73 -333
  58. data/ext/libsass/sass.cpp +21 -11
  59. data/ext/libsass/sass.h +6 -6
  60. data/ext/libsass/sass_context.cpp +167 -81
  61. data/ext/libsass/sass_context.h +26 -6
  62. data/ext/libsass/sass_functions.cpp +49 -40
  63. data/ext/libsass/sass_functions.h +55 -43
  64. data/ext/libsass/sass_interface.cpp +9 -8
  65. data/ext/libsass/sass_interface.h +3 -3
  66. data/ext/libsass/sass_version.h +8 -0
  67. data/ext/libsass/sass_version.h.in +8 -0
  68. data/ext/libsass/script/ci-build-libsass +3 -3
  69. data/ext/libsass/script/ci-report-coverage +2 -1
  70. data/ext/libsass/source_map.cpp +2 -2
  71. data/ext/libsass/util.cpp +60 -11
  72. data/ext/libsass/util.hpp +6 -1
  73. data/ext/libsass/win/libsass.filters +12 -0
  74. data/ext/libsass/win/libsass.vcxproj +10 -0
  75. data/lib/sassc.rb +3 -1
  76. data/lib/sassc/cache_stores/base.rb +2 -0
  77. data/lib/sassc/dependency.rb +3 -1
  78. data/lib/sassc/engine.rb +31 -16
  79. data/lib/sassc/error.rb +3 -2
  80. data/lib/sassc/functions_handler.rb +54 -0
  81. data/lib/sassc/import_handler.rb +41 -0
  82. data/lib/sassc/importer.rb +4 -31
  83. data/lib/sassc/native.rb +1 -1
  84. data/lib/sassc/native/native_context_api.rb +3 -2
  85. data/lib/sassc/script.rb +0 -51
  86. data/lib/sassc/version.rb +1 -1
  87. data/sassc.gemspec +1 -0
  88. data/test/custom_importer_test.rb +72 -69
  89. data/test/engine_test.rb +53 -54
  90. data/test/functions_test.rb +40 -39
  91. data/test/native_test.rb +145 -149
  92. data/test/output_style_test.rb +98 -0
  93. data/test/test_helper.rb +21 -7
  94. metadata +28 -2
@@ -0,0 +1,41 @@
1
+ #ifndef SASS_LISTIZE_H
2
+ #define SASS_LISTIZE_H
3
+
4
+ #include <vector>
5
+ #include <iostream>
6
+
7
+ #include "ast.hpp"
8
+ #include "context.hpp"
9
+ #include "operation.hpp"
10
+ #include "environment.hpp"
11
+
12
+ namespace Sass {
13
+ using namespace std;
14
+
15
+ typedef Environment<AST_Node*> Env;
16
+ struct Backtrace;
17
+
18
+ class Listize : public Operation_CRTP<Expression*, Listize> {
19
+
20
+ Context& ctx;
21
+
22
+ Expression* fallback_impl(AST_Node* n);
23
+
24
+ public:
25
+ Listize(Context&);
26
+ virtual ~Listize() { }
27
+
28
+ using Operation<Expression*>::operator();
29
+
30
+ Expression* operator()(Selector_List*);
31
+ Expression* operator()(Complex_Selector*);
32
+ Expression* operator()(Compound_Selector*);
33
+ Expression* operator()(Selector_Reference*);
34
+
35
+ template <typename U>
36
+ Expression* fallback(U x) { return fallback_impl(x); }
37
+ };
38
+
39
+ }
40
+
41
+ #endif
@@ -61,6 +61,7 @@ namespace Sass {
61
61
  virtual T operator()(Media_Query_Expression* x) = 0;
62
62
  virtual T operator()(At_Root_Expression* x) = 0;
63
63
  virtual T operator()(Null* x) = 0;
64
+ virtual T operator()(Parent_Selector* x) = 0;
64
65
  // parameters and arguments
65
66
  virtual T operator()(Parameter* x) = 0;
66
67
  virtual T operator()(Parameters* x) = 0;
@@ -135,6 +136,7 @@ namespace Sass {
135
136
  virtual T operator()(Media_Query_Expression* x) { return static_cast<D*>(this)->fallback(x); }
136
137
  virtual T operator()(At_Root_Expression* x) { return static_cast<D*>(this)->fallback(x); }
137
138
  virtual T operator()(Null* x) { return static_cast<D*>(this)->fallback(x); }
139
+ virtual T operator()(Parent_Selector* x) { return static_cast<D*>(this)->fallback(x); }
138
140
  // parameters and arguments
139
141
  virtual T operator()(Parameter* x) { return static_cast<D*>(this)->fallback(x); }
140
142
  virtual T operator()(Parameters* x) { return static_cast<D*>(this)->fallback(x); }
@@ -134,7 +134,7 @@ namespace Sass {
134
134
  String_Constant* valConst = static_cast<String_Constant*>(dec->value());
135
135
  string val(valConst->value());
136
136
  if (dynamic_cast<String_Quoted*>(valConst)) {
137
- if (val.empty()) {
137
+ if (!valConst->quote_mark() && val.empty()) {
138
138
  bPrintExpression = false;
139
139
  }
140
140
  }
@@ -172,11 +172,10 @@ namespace Sass {
172
172
 
173
173
  void Output::operator()(Keyframe_Rule* r)
174
174
  {
175
- String* v = r->rules();
176
175
  Block* b = r->block();
176
+ Selector* v = r->selector();
177
177
 
178
178
  if (v) {
179
- append_indentation();
180
179
  v->perform(this);
181
180
  }
182
181
 
@@ -338,9 +337,9 @@ namespace Sass {
338
337
  append_token(kwd, a);
339
338
  if (s) {
340
339
  append_mandatory_space();
341
- in_at_rule = true;
340
+ in_wrapped = true;
342
341
  s->perform(this);
343
- in_at_rule = false;
342
+ in_wrapped = false;
344
343
  }
345
344
  else if (v) {
346
345
  append_mandatory_space();
@@ -379,7 +378,7 @@ namespace Sass {
379
378
  void Output::operator()(String_Quoted* s)
380
379
  {
381
380
  if (s->quote_mark()) {
382
- append_token(quote((s->value()), s->quote_mark()), s);
381
+ append_token(quote(s->value(), s->quote_mark()), s);
383
382
  } else if (!in_comment) {
384
383
  append_token(string_to_output(s->value()), s);
385
384
  } else {
@@ -63,16 +63,25 @@ namespace Sass {
63
63
  block_stack.push_back(root);
64
64
  root->is_root(true);
65
65
  read_bom();
66
+
67
+ if (ctx.queue.size() == 1) {
68
+ Import* pre = new (ctx.mem) Import(pstate);
69
+ string load_path(ctx.queue[0].load_path);
70
+ do_import(load_path, pre, ctx.c_headers, false);
71
+ ctx.head_imports = ctx.queue.size() - 1;
72
+ if (!pre->urls().empty()) (*root) << pre;
73
+ if (!pre->files().empty()) {
74
+ for (size_t i = 0, S = pre->files().size(); i < S; ++i) {
75
+ (*root) << new (ctx.mem) Import_Stub(pstate, pre->files()[i]);
76
+ }
77
+ }
78
+ }
79
+
66
80
  lex< optional_spaces >();
67
81
  Selector_Lookahead lookahead_result;
68
82
  while (position < end) {
69
- if (lex< block_comment >()) {
70
- bool is_important = lexed.begin[2] == '!';
71
- String* contents = parse_interpolated_chunk(lexed);
72
- Comment* comment = new (ctx.mem) Comment(pstate, contents, is_important);
73
- (*root) << comment;
74
- }
75
- else if (peek< import >()) {
83
+ parse_block_comments(root);
84
+ if (peek< kwd_import >()) {
76
85
  Import* imp = parse_import();
77
86
  if (!imp->urls().empty()) (*root) << imp;
78
87
  if (!imp->files().empty()) {
@@ -82,7 +91,7 @@ namespace Sass {
82
91
  }
83
92
  if (!lex< one_plus< exactly<';'> > >()) error("top-level @import directive must be terminated by ';'", pstate);
84
93
  }
85
- else if (peek< mixin >() || peek< function >()) {
94
+ else if (peek< kwd_mixin >() || peek< kwd_function >()) {
86
95
  (*root) << parse_definition();
87
96
  }
88
97
  else if (peek< variable >()) {
@@ -92,41 +101,41 @@ namespace Sass {
92
101
  /*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
93
102
  (*root) << parse_propset();
94
103
  }*/
95
- else if (peek< include >() /* || peek< exactly<'+'> >() */) {
104
+ else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) {
96
105
  Mixin_Call* mixin_call = parse_mixin_call();
97
106
  (*root) << mixin_call;
98
107
  if (!mixin_call->block() && !lex< one_plus< exactly<';'> > >()) error("top-level @include directive must be terminated by ';'", pstate);
99
108
  }
100
- else if (peek< if_directive >()) {
109
+ else if (peek< kwd_if_directive >()) {
101
110
  (*root) << parse_if_directive();
102
111
  }
103
- else if (peek< for_directive >()) {
112
+ else if (peek< kwd_for_directive >()) {
104
113
  (*root) << parse_for_directive();
105
114
  }
106
- else if (peek< each_directive >()) {
115
+ else if (peek< kwd_each_directive >()) {
107
116
  (*root) << parse_each_directive();
108
117
  }
109
- else if (peek< while_directive >()) {
118
+ else if (peek< kwd_while_directive >()) {
110
119
  (*root) << parse_while_directive();
111
120
  }
112
- else if (peek< media >()) {
121
+ else if (peek< kwd_media >()) {
113
122
  (*root) << parse_media_block();
114
123
  }
115
- else if (peek< at_root >()) {
124
+ else if (peek< kwd_at_root >()) {
116
125
  (*root) << parse_at_root_block();
117
126
  }
118
- else if (peek< supports >()) {
127
+ else if (peek< kwd_supports >()) {
119
128
  (*root) << parse_feature_block();
120
129
  }
121
- else if (peek< warn >()) {
130
+ else if (peek< kwd_warn >()) {
122
131
  (*root) << parse_warning();
123
132
  if (!lex< one_plus< exactly<';'> > >()) error("top-level @warn directive must be terminated by ';'", pstate);
124
133
  }
125
- else if (peek< err >()) {
134
+ else if (peek< kwd_err >()) {
126
135
  (*root) << parse_error();
127
136
  if (!lex< one_plus< exactly<';'> > >()) error("top-level @error directive must be terminated by ';'", pstate);
128
137
  }
129
- else if (peek< dbg >()) {
138
+ else if (peek< kwd_dbg >()) {
130
139
  (*root) << parse_debug();
131
140
  if (!lex< one_plus< exactly<';'> > >()) error("top-level @debug directive must be terminated by ';'", pstate);
132
141
  }
@@ -147,9 +156,9 @@ namespace Sass {
147
156
  lex< one_plus< exactly<';'> > >();
148
157
  }
149
158
  else {
150
- lex< optional_spaces_and_comments >();
159
+ lex< css_whitespace >();
151
160
  if (position >= end) break;
152
- error("invalid top-level expression", pstate);
161
+ error("invalid top-level expression", after_token);
153
162
  }
154
163
  lex< optional_spaces >();
155
164
  }
@@ -183,90 +192,107 @@ namespace Sass {
183
192
 
184
193
  }
185
194
 
195
+ void Parser::import_single_file (Import* imp, string import_path) {
196
+
197
+ if (!unquote(import_path).substr(0, 7).compare("http://") ||
198
+ !unquote(import_path).substr(0, 8).compare("https://") ||
199
+ !unquote(import_path).substr(0, 2).compare("//"))
200
+ {
201
+ imp->urls().push_back(new (ctx.mem) String_Quoted(pstate, import_path));
202
+ }
203
+ else {
204
+ add_single_file(imp, import_path);
205
+ }
206
+
207
+ }
208
+
209
+ bool Parser::do_import(const string& import_path, Import* imp, vector<Sass_Importer_Entry> importers, bool only_one)
210
+ {
211
+ bool has_import = false;
212
+ string load_path = unquote(import_path);
213
+ for (auto importer : importers) {
214
+ // int priority = sass_importer_get_priority(importer);
215
+ Sass_Importer_Fn fn = sass_importer_get_function(importer);
216
+ if (Sass_Import_List includes =
217
+ fn(load_path.c_str(), importer, ctx.c_compiler)
218
+ ) {
219
+ Sass_Import_List list = includes;
220
+ while (*includes) {
221
+ Sass_Import_Entry include = *includes;
222
+ const char *file = sass_import_get_path(include);
223
+ char* source = sass_import_take_source(include);
224
+ size_t line = sass_import_get_error_line(include);
225
+ size_t column = sass_import_get_error_column(include);
226
+ const char* message = sass_import_get_error_message(include);
227
+ if (message) {
228
+ if (line == string::npos && column == string::npos) error(message, pstate);
229
+ else error(message, ParserState(message, source, Position(line, column)));
230
+ } else if (source) {
231
+ if (file) {
232
+ ctx.add_source(file, load_path, source);
233
+ imp->files().push_back(file);
234
+ } else {
235
+ ctx.add_source(load_path, load_path, source);
236
+ imp->files().push_back(load_path);
237
+ }
238
+ } else if(file) {
239
+ import_single_file(imp, file);
240
+ }
241
+ ++includes;
242
+ }
243
+ // deallocate returned memory
244
+ sass_delete_import_list(list);
245
+ // set success flag
246
+ has_import = true;
247
+ // break import chain
248
+ if (only_one) return true;
249
+ }
250
+ }
251
+ // return result
252
+ return has_import;
253
+ }
254
+
186
255
  Import* Parser::parse_import()
187
256
  {
188
- lex< import >();
257
+ lex< kwd_import >();
189
258
  Import* imp = new (ctx.mem) Import(pstate);
190
259
  bool first = true;
191
260
  do {
192
261
  while (lex< block_comment >());
193
262
  if (lex< quoted_string >()) {
194
- string import_path(lexed);
195
-
196
- // struct Sass_Options opt = sass_context_get_options(ctx)
197
- Sass_C_Import_Callback importer = ctx.importer;
198
- // custom importer
199
- if (importer) {
200
- Sass_Import* current = ctx.import_stack.back();
201
- Sass_C_Import_Fn fn = sass_import_get_function(importer);
202
- void* cookie = sass_import_get_cookie(importer);
203
- // create a new import entry
204
- string inc_path = unquote(import_path);
205
- struct Sass_Import** includes = fn(
206
- inc_path.c_str(),
207
- sass_import_get_path(current),
208
- cookie);
209
- if (includes) {
210
- struct Sass_Import** list = includes;
211
- while (*includes) {
212
- struct Sass_Import* include = *includes;
213
- const char *file = sass_import_get_path(include);
214
- char* source = sass_import_take_source(include);
215
- size_t line = sass_import_get_error_line(include);
216
- size_t column = sass_import_get_error_column(include);
217
- const char* message = sass_import_get_error_message(include);
218
- // char *srcmap = sass_import_take_srcmap(include);
219
- if (message) {
220
- if (line == string::npos && column == string::npos) error(message, pstate);
221
- else error(message, ParserState(message, Position(line, column)));
222
- } else if (source) {
223
- if (file) {
224
- ctx.add_source(file, inc_path, source);
225
- imp->files().push_back(file);
226
- } else {
227
- ctx.add_source(inc_path, inc_path, source);
228
- imp->files().push_back(inc_path);
229
- }
230
- } else if(file) {
231
- add_single_file(imp, file);
232
- }
233
- ++includes;
234
- }
235
- // deallocate returned memory
236
- sass_delete_import_list(list);
237
- // parse next import
238
- continue;
239
- }
240
- }
241
-
242
- if (!unquote(import_path).substr(0, 7).compare("http://") ||
243
- !unquote(import_path).substr(0, 8).compare("https://") ||
244
- !unquote(import_path).substr(0, 2).compare("//"))
263
+ if (!do_import(lexed, imp, ctx.c_importers, true))
245
264
  {
246
- imp->urls().push_back(new (ctx.mem) String_Quoted(pstate, import_path));
265
+ // push single file import
266
+ import_single_file(imp, lexed);
267
+ }
268
+ }
269
+ else if (lex< uri_prefix >()) {
270
+ Arguments* args = new (ctx.mem) Arguments(pstate);
271
+ Function_Call* result = new (ctx.mem) Function_Call(pstate, "url", args);
272
+ if (lex < uri_value >()) { // chunk seems to work too!
273
+ String* the_url = parse_interpolated_chunk(lexed);
274
+ *args << new (ctx.mem) Argument(the_url->pstate(), the_url);
247
275
  }
248
276
  else {
249
- add_single_file(imp, import_path);
277
+ error("malformed URL", pstate);
250
278
  }
251
-
252
- }
253
- else if (peek< uri_prefix >()) {
254
- imp->urls().push_back(parse_value());
279
+ if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);
280
+ imp->urls().push_back(result);
255
281
  }
256
282
  else {
257
283
  if (first) error("@import directive requires a url or quoted path", pstate);
258
284
  else error("expecting another url or quoted path in @import list", pstate);
259
285
  }
260
286
  first = false;
261
- } while (lex< exactly<','> >());
287
+ } while (lex_css< exactly<','> >());
262
288
  return imp;
263
289
  }
264
290
 
265
291
  Definition* Parser::parse_definition()
266
292
  {
267
293
  Definition::Type which_type = Definition::MIXIN;
268
- if (lex< mixin >()) which_type = Definition::MIXIN;
269
- else if (lex< function >()) which_type = Definition::FUNCTION;
294
+ if (lex< kwd_mixin >()) which_type = Definition::MIXIN;
295
+ else if (lex< kwd_function >()) which_type = Definition::FUNCTION;
270
296
  string which_str(lexed);
271
297
  if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);
272
298
  string name(Util::normalize_underscores(lexed));
@@ -279,22 +305,22 @@ namespace Sass {
279
305
  else stack.push_back(function_def);
280
306
  Block* body = parse_block();
281
307
  stack.pop_back();
282
- Definition* def = new (ctx.mem) Definition(source_position_of_def, name, params, body, which_type);
308
+ Definition* def = new (ctx.mem) Definition(source_position_of_def, name, params, body, &ctx, which_type);
283
309
  return def;
284
310
  }
285
311
 
286
312
  Parameters* Parser::parse_parameters()
287
313
  {
288
- string name(lexed); // for the error message
314
+ string name(lexed);
315
+ Position position = after_token;
289
316
  Parameters* params = new (ctx.mem) Parameters(pstate);
290
- if (lex< exactly<'('> >()) {
317
+ if (lex_css< exactly<'('> >()) {
291
318
  // if there's anything there at all
292
- if (!peek< exactly<')'> >()) {
319
+ if (!peek_css< exactly<')'> >()) {
293
320
  do (*params) << parse_parameter();
294
- while (lex< alternatives < spaces,block_comment, exactly<','> > >());
321
+ while (lex_css< exactly<','> >());
295
322
  }
296
- while (lex< alternatives < spaces, block_comment > >()) {};
297
- if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, pstate);
323
+ if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
298
324
  }
299
325
  return params;
300
326
  }
@@ -322,7 +348,7 @@ namespace Sass {
322
348
 
323
349
  Mixin_Call* Parser::parse_mixin_call()
324
350
  {
325
- lex< include >() /* || lex< exactly<'+'> >() */;
351
+ lex< kwd_include >() /* || lex< exactly<'+'> >() */;
326
352
  if (!lex< identifier >()) error("invalid name in @include directive", pstate);
327
353
  ParserState source_position_of_call = pstate;
328
354
  string name(Util::normalize_underscores(lexed));
@@ -335,34 +361,36 @@ namespace Sass {
335
361
  return the_call;
336
362
  }
337
363
 
338
- Arguments* Parser::parse_arguments()
364
+ Arguments* Parser::parse_arguments(bool has_url)
339
365
  {
340
366
  string name(lexed);
367
+ Position position = after_token;
341
368
  Arguments* args = new (ctx.mem) Arguments(pstate);
342
-
343
- if (lex< exactly<'('> >()) {
369
+ if (lex_css< exactly<'('> >()) {
344
370
  // if there's anything there at all
345
- if (!peek< exactly<')'> >()) {
346
- do (*args) << parse_argument();
347
- while (lex< alternatives < block_comment, exactly<','> > >());
371
+ if (!peek_css< exactly<')'> >()) {
372
+ do (*args) << parse_argument(has_url);
373
+ while (lex_css< exactly<','> >());
348
374
  }
349
- while (lex< block_comment >());
350
- if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, pstate);
375
+ if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
351
376
  }
352
-
353
377
  return args;
354
378
  }
355
379
 
356
- Argument* Parser::parse_argument()
380
+ Argument* Parser::parse_argument(bool has_url)
357
381
  {
382
+
358
383
  Argument* arg;
359
- while (lex< alternatives < spaces, block_comment > >());
360
- if (peek< sequence < variable, zero_plus < alternatives < spaces, line_comment, block_comment > >, exactly<':'> > >()) {
361
- lex< variable >();
384
+ // some urls can look like line comments (parse literally - chunk would not work)
385
+ if (has_url && lex< sequence < uri_value, lookahead < loosely<')'> > > >(false)) {
386
+ String* the_url = parse_interpolated_chunk(lexed);
387
+ arg = new (ctx.mem) Argument(the_url->pstate(), the_url);
388
+ }
389
+ else if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {
390
+ lex_css< variable >();
362
391
  string name(Util::normalize_underscores(lexed));
363
392
  ParserState p = pstate;
364
- while (lex< alternatives < spaces, block_comment > >()) {};
365
- lex< exactly<':'> >();
393
+ lex_css< exactly<':'> >();
366
394
  Expression* val = parse_space_list();
367
395
  val->is_delayed(false);
368
396
  arg = new (ctx.mem) Argument(p, val, name);
@@ -372,7 +400,7 @@ namespace Sass {
372
400
  bool is_keyword = false;
373
401
  Expression* val = parse_space_list();
374
402
  val->is_delayed(false);
375
- if (lex< exactly< ellipsis > >()) {
403
+ if (lex_css< exactly< ellipsis > >()) {
376
404
  if (val->concrete_type() == Expression::MAP) is_keyword = true;
377
405
  else is_arglist = true;
378
406
  }
@@ -389,13 +417,13 @@ namespace Sass {
389
417
  if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);
390
418
  Expression* val = parse_list();
391
419
  val->is_delayed(false);
392
- bool is_guarded = false;
420
+ bool is_default = false;
393
421
  bool is_global = false;
394
422
  while (peek< default_flag >() || peek< global_flag >()) {
395
- is_guarded = lex< default_flag >() || is_guarded;
423
+ is_default = lex< default_flag >() || is_default;
396
424
  is_global = lex< global_flag >() || is_global;
397
425
  }
398
- Assignment* var = new (ctx.mem) Assignment(var_source_position, name, val, is_guarded, is_global);
426
+ Assignment* var = new (ctx.mem) Assignment(var_source_position, name, val, is_default, is_global);
399
427
  return var;
400
428
  }
401
429
 
@@ -432,8 +460,9 @@ namespace Sass {
432
460
  sel = parse_selector_group();
433
461
  }
434
462
  bool old_in_at_root = in_at_root;
435
- in_at_root = false;
436
463
  ParserState r_source_position = pstate;
464
+ lex < css_comments >();
465
+ in_at_root = false;
437
466
  if (!peek< exactly<'{'> >()) error("expected a '{' after the selector", pstate);
438
467
  Block* block = parse_block();
439
468
  in_at_root = old_in_at_root;
@@ -477,16 +506,18 @@ namespace Sass {
477
506
  {
478
507
  bool reloop = true;
479
508
  To_String to_string(&ctx);
480
- lex< optional_spaces_and_comments >();
509
+ lex< css_whitespace >();
481
510
  Selector_List* group = new (ctx.mem) Selector_List(pstate);
482
511
  group->media_block(last_media_block);
483
512
  group->last_block(block_stack.back());
484
513
  do {
485
514
  reloop = false;
486
- if (peek< exactly<'{'> >() ||
487
- peek< exactly<'}'> >() ||
488
- peek< exactly<')'> >() ||
489
- peek< exactly<';'> >())
515
+ if (peek< alternatives <
516
+ exactly<'{'>,
517
+ exactly<'}'>,
518
+ exactly<')'>,
519
+ exactly<';'>
520
+ > >())
490
521
  break; // in case there are superfluous commas at the end
491
522
  Complex_Selector* comb = parse_selector_combination();
492
523
  if (!comb->has_reference() && !in_at_root) {
@@ -508,10 +539,10 @@ namespace Sass {
508
539
  }
509
540
  if (peek_newline()) ref_wrap->has_line_break(true);
510
541
  }
511
- while (peek< sequence< optional_spaces_and_comments, exactly<','> > >())
542
+ while (peek_css< exactly<','> >())
512
543
  {
513
544
  // consume everything up and including the comma speparator
514
- reloop = lex< sequence< optional_spaces_and_comments, exactly<','> > >() != 0;
545
+ reloop = lex< sequence < optional_css_comments, exactly<','> > >() != 0;
515
546
  // remember line break (also between some commas)
516
547
  if (peek_newline()) comb->has_line_feed(true);
517
548
  if (comb->tail() && peek_newline()) comb->tail()->has_line_feed(true);
@@ -529,15 +560,15 @@ namespace Sass {
529
560
 
530
561
  Complex_Selector* Parser::parse_selector_combination()
531
562
  {
532
- // lex< optional_spaces_and_comments >();
533
563
  Position sel_source_position(-1);
534
564
  Compound_Selector* lhs;
535
- if (peek< exactly<'+'> >() ||
536
- peek< exactly<'~'> >() ||
537
- peek< exactly<'>'> >()) {
538
- // no selector before the combinator
539
- lhs = 0;
540
- }
565
+ if (peek_css< alternatives <
566
+ exactly<'+'>,
567
+ exactly<'~'>,
568
+ exactly<'>'>
569
+ > >())
570
+ // no selector before the combinator
571
+ { lhs = 0; }
541
572
  else {
542
573
  lhs = parse_simple_selector_sequence();
543
574
  sel_source_position = before_token;
@@ -552,21 +583,22 @@ namespace Sass {
552
583
  bool cpx_lf = peek_newline();
553
584
 
554
585
  Complex_Selector* rhs;
555
- if (peek< exactly<','> >() ||
556
- peek< exactly<')'> >() ||
557
- peek< exactly<'{'> >() ||
558
- peek< exactly<'}'> >() ||
559
- peek< exactly<';'> >() ||
560
- peek< optional >()) {
561
- // no selector after the combinator
562
- rhs = 0;
563
- }
586
+ if (peek_css< alternatives <
587
+ exactly<','>,
588
+ exactly<')'>,
589
+ exactly<'{'>,
590
+ exactly<'}'>,
591
+ exactly<';'>,
592
+ optional
593
+ > >())
594
+ // no selector after the combinator
595
+ { rhs = 0; }
564
596
  else {
565
597
  rhs = parse_selector_combination();
566
598
  sel_source_position = before_token;
567
599
  }
568
600
  if (!sel_source_position.line) sel_source_position = before_token;
569
- Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, sel_source_position), cmb, lhs, rhs);
601
+ Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, source, sel_source_position), cmb, lhs, rhs);
570
602
  cpx->media_block(last_media_block);
571
603
  cpx->last_block(block_stack.back());
572
604
  if (cpx_lf) cpx->has_line_break(cpx_lf);
@@ -582,19 +614,19 @@ namespace Sass {
582
614
  if (lex< exactly<'&'> >()) {
583
615
  // check if we have a parent selector on the root level block
584
616
  if (block_stack.back() && block_stack.back()->is_root()) {
585
- error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate);
617
+ //error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate);
586
618
  }
587
619
  (*seq) << new (ctx.mem) Selector_Reference(pstate);
588
620
  sawsomething = true;
589
621
  // if you see a space after a &, then you're done
590
- if(peek< spaces >()) {
622
+ if(peek< spaces >() || peek< alternatives < spaces, exactly<';'> > >()) {
591
623
  return seq;
592
624
  }
593
625
  }
594
- if (sawsomething && lex< sequence< negate< functional >, alternatives< identifier_fragment, universal, quoted_string, dimension, percentage, number > > >()) {
626
+ if (sawsomething && lex_css< sequence< negate< functional >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) {
595
627
  // saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning
596
628
  (*seq) << new (ctx.mem) Type_Selector(pstate, unquote(lexed));
597
- } else if (lex< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) {
629
+ } else if (lex_css< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) {
598
630
  // if you see a type selector
599
631
  (*seq) << new (ctx.mem) Type_Selector(pstate, lexed);
600
632
  sawsomething = true;
@@ -605,14 +637,16 @@ namespace Sass {
605
637
  }
606
638
 
607
639
  while (!peek< spaces >(position) &&
608
- !(peek < exactly<'+'> >(position) ||
609
- peek < exactly<'~'> >(position) ||
610
- peek < exactly<'>'> >(position) ||
611
- peek < exactly<','> >(position) ||
612
- peek < exactly<')'> >(position) ||
613
- peek < exactly<'{'> >(position) ||
614
- peek < exactly<'}'> >(position) ||
615
- peek < exactly<';'> >(position))) {
640
+ !(peek_css < alternatives <
641
+ exactly<'+'>,
642
+ exactly<'~'>,
643
+ exactly<'>'>,
644
+ exactly<','>,
645
+ exactly<')'>,
646
+ exactly<'{'>,
647
+ exactly<'}'>,
648
+ exactly<';'>
649
+ > >(position))) {
616
650
  (*seq) << parse_simple_selector();
617
651
  }
618
652
  return seq;
@@ -620,12 +654,16 @@ namespace Sass {
620
654
 
621
655
  Simple_Selector* Parser::parse_simple_selector()
622
656
  {
623
- if (lex< id_name >() || lex< class_name >()) {
657
+ lex < css_comments >();
658
+ if (lex< alternatives < id_name, class_name > >()) {
624
659
  return new (ctx.mem) Selector_Qualifier(pstate, unquote(lexed));
625
660
  }
626
- else if (lex< quoted_string >() || lex< number >()) {
661
+ else if (lex< quoted_string >()) {
627
662
  return new (ctx.mem) Type_Selector(pstate, unquote(lexed));
628
663
  }
664
+ else if (lex< alternatives < number, kwd_sel_deep > >()) {
665
+ return new (ctx.mem) Type_Selector(pstate, lexed);
666
+ }
629
667
  else if (peek< pseudo_not >()) {
630
668
  return parse_negated_selector();
631
669
  }
@@ -675,7 +713,7 @@ namespace Sass {
675
713
  lex< sign >();
676
714
  String_Constant* op = new (ctx.mem) String_Quoted(p, lexed);
677
715
  // Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
678
- lex< digits >();
716
+ lex< one_plus < digit > >();
679
717
  String_Constant* constant = new (ctx.mem) String_Quoted(p, lexed);
680
718
  // expr = new (ctx.mem) Binary_Expression(p, op, var_coef, constant);
681
719
  String_Schema* schema = new (ctx.mem) String_Schema(p, 3);
@@ -683,19 +721,19 @@ namespace Sass {
683
721
  expr = schema;
684
722
  }
685
723
  else if (peek< sequence< optional<sign>,
686
- optional<digits>,
724
+ zero_plus<digit>,
687
725
  exactly<'n'>,
688
- optional_spaces_and_comments,
726
+ optional_css_whitespace,
689
727
  exactly<')'> > >()) {
690
728
  lex< sequence< optional<sign>,
691
- optional<digits>,
729
+ zero_plus<digit>,
692
730
  exactly<'n'> > >();
693
731
  expr = new (ctx.mem) String_Quoted(p, lexed);
694
732
  }
695
- else if (lex< sequence< optional<sign>, digits > >()) {
733
+ else if (lex< sequence< optional<sign>, one_plus < digit > > >()) {
696
734
  expr = new (ctx.mem) String_Quoted(p, lexed);
697
735
  }
698
- else if (peek< sequence< identifier, optional_spaces_and_comments, exactly<')'> > >()) {
736
+ else if (peek< sequence< identifier, optional_css_whitespace, exactly<')'> > >()) {
699
737
  lex< identifier >();
700
738
  expr = new (ctx.mem) String_Quoted(p, lexed);
701
739
  }
@@ -726,32 +764,42 @@ namespace Sass {
726
764
 
727
765
  Attribute_Selector* Parser::parse_attribute_selector()
728
766
  {
729
- lex< exactly<'['> >();
767
+ lex_css< exactly<'['> >();
730
768
  ParserState p = pstate;
731
- if (!lex< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
769
+ if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
732
770
  string name(lexed);
733
- if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0);
734
- if (!lex< alternatives< exact_match, class_match, dash_match,
735
- prefix_match, suffix_match, substring_match > >()) {
771
+ if (lex_css< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0);
772
+ if (!lex_css< alternatives< exact_match, class_match, dash_match,
773
+ prefix_match, suffix_match, substring_match > >()) {
736
774
  error("invalid operator in attribute selector for " + name, pstate);
737
775
  }
738
776
  string matcher(lexed);
739
777
 
740
778
  String* value = 0;
741
- if (lex< identifier >()) {
779
+ if (lex_css< identifier >()) {
742
780
  value = new (ctx.mem) String_Constant(p, lexed);
743
781
  }
744
- else if (lex< quoted_string >()) {
782
+ else if (lex_css< quoted_string >()) {
745
783
  value = parse_interpolated_chunk(lexed, true); // needed!
746
784
  }
747
785
  else {
748
786
  error("expected a string constant or identifier in attribute selector for " + name, pstate);
749
787
  }
750
788
 
751
- if (!lex< exactly<']'> >()) error("unterminated attribute selector for " + name, pstate);
789
+ if (!lex_css< exactly<']'> >()) error("unterminated attribute selector for " + name, pstate);
752
790
  return new (ctx.mem) Attribute_Selector(p, name, matcher, value);
753
791
  }
754
792
 
793
+ /* parse block comment and add to block */
794
+ void Parser::parse_block_comments(Block* block)
795
+ {
796
+ while (lex< block_comment >()) {
797
+ bool is_important = lexed.begin[2] == '!';
798
+ String* contents = parse_interpolated_chunk(lexed);
799
+ (*block) << new (ctx.mem) Comment(pstate, contents, is_important);
800
+ }
801
+ }
802
+
755
803
  Block* Parser::parse_block()
756
804
  {
757
805
  lex< exactly<'{'> >();
@@ -761,36 +809,21 @@ namespace Sass {
761
809
  block_stack.push_back(block);
762
810
  lex< zero_plus < alternatives < space, line_comment > > >();
763
811
  // JMA - ensure that a block containing only block_comments is parsed
764
- while (lex< block_comment >()) {
765
- bool is_important = lexed.begin[2] == '!';
766
- String* contents = parse_interpolated_chunk(lexed);
767
- Comment* comment = new (ctx.mem) Comment(pstate, contents, is_important);
768
- (*block) << comment;
769
- }
812
+ parse_block_comments(block);
770
813
 
771
814
  while (!lex< exactly<'}'> >()) {
815
+ parse_block_comments(block);
772
816
  if (semicolon) {
773
817
  if (!lex< one_plus< exactly<';'> > >()) {
774
818
  error("non-terminal statement or declaration must end with ';'", pstate);
775
819
  }
776
820
  semicolon = false;
777
- while (lex< block_comment >()) {
778
- bool is_important = lexed.begin[2] == '!';
779
- String* contents = parse_interpolated_chunk(lexed);
780
- Comment* comment = new (ctx.mem) Comment(pstate, contents, is_important);
781
- (*block) << comment;
782
- }
821
+ parse_block_comments(block);
783
822
  if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break;
784
823
  }
785
- if (lex< block_comment >()) {
786
- bool is_important = lexed.begin[2] == '!';
787
- String* contents = parse_interpolated_chunk(lexed);
788
- Comment* comment = new (ctx.mem) Comment(pstate, contents, is_important);
789
- (*block) << comment;
790
- }
791
- else if (peek< import >(position)) {
824
+ else if (peek< kwd_import >(position)) {
792
825
  if (stack.back() == mixin_def || stack.back() == function_def) {
793
- lex< import >(); // to adjust the before_token number
826
+ lex< kwd_import >(); // to adjust the before_token number
794
827
  error("@import directives are not allowed inside mixins and functions", pstate);
795
828
  }
796
829
  Import* imp = parse_import();
@@ -809,47 +842,47 @@ namespace Sass {
809
842
  else if (lex< line_comment >()) {
810
843
  // throw line comments away
811
844
  }
812
- else if (peek< if_directive >()) {
845
+ else if (peek< kwd_if_directive >()) {
813
846
  (*block) << parse_if_directive();
814
847
  }
815
- else if (peek< for_directive >()) {
848
+ else if (peek< kwd_for_directive >()) {
816
849
  (*block) << parse_for_directive();
817
850
  }
818
- else if (peek< each_directive >()) {
851
+ else if (peek< kwd_each_directive >()) {
819
852
  (*block) << parse_each_directive();
820
853
  }
821
- else if (peek < while_directive >()) {
854
+ else if (peek < kwd_while_directive >()) {
822
855
  (*block) << parse_while_directive();
823
856
  }
824
- else if (lex < return_directive >()) {
857
+ else if (lex < kwd_return_directive >()) {
825
858
  (*block) << new (ctx.mem) Return(pstate, parse_list());
826
859
  semicolon = true;
827
860
  }
828
- else if (peek< warn >()) {
861
+ else if (peek< kwd_warn >()) {
829
862
  (*block) << parse_warning();
830
863
  semicolon = true;
831
864
  }
832
- else if (peek< err >()) {
865
+ else if (peek< kwd_err >()) {
833
866
  (*block) << parse_error();
834
867
  semicolon = true;
835
868
  }
836
- else if (peek< dbg >()) {
869
+ else if (peek< kwd_dbg >()) {
837
870
  (*block) << parse_debug();
838
871
  semicolon = true;
839
872
  }
840
873
  else if (stack.back() == function_def) {
841
874
  error("only variable declarations and control directives are allowed inside functions", pstate);
842
875
  }
843
- else if (peek< mixin >() || peek< function >()) {
876
+ else if (peek< kwd_mixin >() || peek< kwd_function >()) {
844
877
  (*block) << parse_definition();
845
878
  }
846
- else if (peek< include >(position)) {
879
+ else if (peek< kwd_include >(position)) {
847
880
  Mixin_Call* the_call = parse_mixin_call();
848
881
  (*block) << the_call;
849
882
  // don't need a semicolon after a content block
850
883
  semicolon = (the_call->block()) ? false : true;
851
884
  }
852
- else if (lex< content >()) {
885
+ else if (lex< kwd_content >()) {
853
886
  if (stack.back() != mixin_def) {
854
887
  error("@content may only be used within a mixin", pstate);
855
888
  }
@@ -862,7 +895,7 @@ namespace Sass {
862
895
  semicolon = true;
863
896
  }
864
897
  */
865
- else if (lex< extend >()) {
898
+ else if (lex< kwd_extend >()) {
866
899
  Selector_Lookahead lookahead = lookahead_for_extension_target(position);
867
900
  if (!lookahead.found) error("invalid selector for @extend", pstate);
868
901
  Selector* target;
@@ -871,13 +904,13 @@ namespace Sass {
871
904
  (*block) << new (ctx.mem) Extension(pstate, target);
872
905
  semicolon = true;
873
906
  }
874
- else if (peek< media >()) {
907
+ else if (peek< kwd_media >()) {
875
908
  (*block) << parse_media_block();
876
909
  }
877
- else if (peek< supports >()) {
910
+ else if (peek< kwd_supports >()) {
878
911
  (*block) << parse_feature_block();
879
912
  }
880
- else if (peek< at_root >()) {
913
+ else if (peek< kwd_at_root >()) {
881
914
  (*block) << parse_at_root_block();
882
915
  }
883
916
  // ignore the @charset directive for now
@@ -923,12 +956,7 @@ namespace Sass {
923
956
  }
924
957
  }
925
958
  else lex< one_plus< exactly<';'> > >();
926
- while (lex< block_comment >()) {
927
- bool is_important = lexed.begin[2] == '!';
928
- String* contents = parse_interpolated_chunk(lexed);
929
- Comment* comment = new (ctx.mem) Comment(pstate, contents, is_important);
930
- (*block) << comment;
931
- }
959
+ parse_block_comments(block);
932
960
  }
933
961
  block_stack.pop_back();
934
962
  return block;
@@ -942,19 +970,23 @@ namespace Sass {
942
970
  else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
943
971
  prop = new (ctx.mem) String_Quoted(pstate, lexed);
944
972
  }
945
- else if (lex< custom_property_name >()) {
946
- prop = new (ctx.mem) String_Quoted(pstate, lexed);
947
- }
948
973
  else {
949
974
  error("invalid property name", pstate);
950
975
  }
951
- if (!lex< one_plus< exactly<':'> > >()) error("property \"" + string(lexed) + "\" must be followed by a ':'", pstate);
952
- if (peek< exactly<';'> >()) error("style declaration must contain a value", pstate);
953
- if (peek< static_value >()) {
976
+ const string property(lexed);
977
+ if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);
978
+ if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);
979
+ if (peek_css< static_value >()) {
954
980
  return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex<important>()*/);
955
981
  }
956
982
  else {
957
- return new (ctx.mem) Declaration(prop->pstate(), prop, parse_list()/*, lex<important>()*/);
983
+ Expression* list_ex = parse_list();
984
+ if (List* list = dynamic_cast<List*>(list_ex)) {
985
+ if (list->length() == 0 && !peek< exactly <'{'> >()) {
986
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
987
+ }
988
+ }
989
+ return new (ctx.mem) Declaration(prop->pstate(), prop, list_ex/*, lex<important>()*/);
958
990
  }
959
991
  }
960
992
 
@@ -990,10 +1022,10 @@ namespace Sass {
990
1022
  Map* map = new (ctx.mem) Map(pstate, 1);
991
1023
  (*map) << make_pair(key, value);
992
1024
 
993
- while (lex< exactly<','> >())
1025
+ while (lex_css< exactly<','> >())
994
1026
  {
995
1027
  // allow trailing commas - #495
996
- if (peek< exactly<')'> >(position))
1028
+ if (peek_css< exactly<')'> >(position))
997
1029
  { break; }
998
1030
 
999
1031
  Expression* key = parse_list();
@@ -1019,32 +1051,35 @@ namespace Sass {
1019
1051
 
1020
1052
  Expression* Parser::parse_comma_list()
1021
1053
  {
1022
- if (//peek< exactly<'!'> >(position) ||
1023
- peek< exactly<';'> >(position) ||
1024
- peek< exactly<'}'> >(position) ||
1025
- peek< exactly<'{'> >(position) ||
1026
- peek< exactly<')'> >(position) ||
1027
- //peek< exactly<':'> >(position) ||
1028
- peek< exactly<ellipsis> >(position))
1054
+ if (peek_css< alternatives <
1055
+ // exactly<'!'>,
1056
+ // exactly<':'>,
1057
+ exactly<';'>,
1058
+ exactly<'}'>,
1059
+ exactly<'{'>,
1060
+ exactly<')'>,
1061
+ exactly<ellipsis>
1062
+ > >(position))
1029
1063
  { return new (ctx.mem) List(pstate, 0); }
1030
1064
  Expression* list1 = parse_space_list();
1031
1065
  // if it's a singleton, return it directly; don't wrap it
1032
- if (!peek< exactly<','> >(position)) return list1;
1066
+ if (!peek_css< exactly<','> >(position)) return list1;
1033
1067
 
1034
1068
  List* comma_list = new (ctx.mem) List(pstate, 2, List::COMMA);
1035
1069
  (*comma_list) << list1;
1036
1070
 
1037
- while (lex< exactly<','> >())
1071
+ while (lex_css< exactly<','> >())
1038
1072
  {
1039
- if (//peek< exactly<'!'> >(position) ||
1040
- peek< exactly<';'> >(position) ||
1041
- peek< exactly<'}'> >(position) ||
1042
- peek< exactly<'{'> >(position) ||
1043
- peek< exactly<')'> >(position) ||
1044
- peek< exactly<':'> >(position) ||
1045
- peek< exactly<ellipsis> >(position)) {
1046
- break;
1047
- }
1073
+ if (peek_css< alternatives <
1074
+ // exactly<'!'>,
1075
+ exactly<';'>,
1076
+ exactly<'}'>,
1077
+ exactly<'{'>,
1078
+ exactly<')'>,
1079
+ exactly<':'>,
1080
+ exactly<ellipsis>
1081
+ > >(position)
1082
+ ) { break; }
1048
1083
  Expression* list = parse_space_list();
1049
1084
  (*comma_list) << list;
1050
1085
  }
@@ -1056,32 +1091,36 @@ namespace Sass {
1056
1091
  {
1057
1092
  Expression* disj1 = parse_disjunction();
1058
1093
  // if it's a singleton, return it directly; don't wrap it
1059
- if (//peek< exactly<'!'> >(position) ||
1060
- peek< exactly<';'> >(position) ||
1061
- peek< exactly<'}'> >(position) ||
1062
- peek< exactly<'{'> >(position) ||
1063
- peek< exactly<')'> >(position) ||
1064
- peek< exactly<','> >(position) ||
1065
- peek< exactly<':'> >(position) ||
1066
- peek< exactly<ellipsis> >(position) ||
1067
- peek< default_flag >(position) ||
1068
- peek< global_flag >(position))
1069
- { return disj1; }
1094
+ if (peek_css< alternatives <
1095
+ // exactly<'!'>,
1096
+ exactly<';'>,
1097
+ exactly<'}'>,
1098
+ exactly<'{'>,
1099
+ exactly<')'>,
1100
+ exactly<','>,
1101
+ exactly<':'>,
1102
+ exactly<ellipsis>,
1103
+ default_flag,
1104
+ global_flag
1105
+ > >(position)
1106
+ ) { return disj1; }
1070
1107
 
1071
1108
  List* space_list = new (ctx.mem) List(pstate, 2, List::SPACE);
1072
1109
  (*space_list) << disj1;
1073
1110
 
1074
- while (!(//peek< exactly<'!'> >(position) ||
1075
- peek< exactly<';'> >(position) ||
1076
- peek< exactly<'}'> >(position) ||
1077
- peek< exactly<'{'> >(position) ||
1078
- peek< exactly<')'> >(position) ||
1079
- peek< exactly<','> >(position) ||
1080
- peek< exactly<':'> >(position) ||
1081
- peek< exactly<ellipsis> >(position) ||
1082
- peek< default_flag >(position) ||
1083
- peek< global_flag >(position)))
1084
- {
1111
+ while (!(peek_css< alternatives <
1112
+ // exactly<'!'>,
1113
+ exactly<';'>,
1114
+ exactly<'}'>,
1115
+ exactly<'{'>,
1116
+ exactly<')'>,
1117
+ exactly<','>,
1118
+ exactly<':'>,
1119
+ exactly<ellipsis>,
1120
+ default_flag,
1121
+ global_flag
1122
+ > >(position))
1123
+ ) {
1085
1124
  (*space_list) << parse_disjunction();
1086
1125
  }
1087
1126
 
@@ -1092,10 +1131,10 @@ namespace Sass {
1092
1131
  {
1093
1132
  Expression* conj1 = parse_conjunction();
1094
1133
  // if it's a singleton, return it directly; don't wrap it
1095
- if (!peek< sequence< or_op, negate< identifier > > >()) return conj1;
1134
+ if (!peek_css< kwd_or >()) return conj1;
1096
1135
 
1097
1136
  vector<Expression*> operands;
1098
- while (lex< sequence< or_op, negate< identifier > > >())
1137
+ while (lex_css< kwd_or >())
1099
1138
  operands.push_back(parse_conjunction());
1100
1139
 
1101
1140
  return fold_operands(conj1, operands, Binary_Expression::OR);
@@ -1105,10 +1144,10 @@ namespace Sass {
1105
1144
  {
1106
1145
  Expression* rel1 = parse_relation();
1107
1146
  // if it's a singleton, return it directly; don't wrap it
1108
- if (!peek< sequence< and_op, negate< identifier > > >()) return rel1;
1147
+ if (!peek_css< kwd_and >()) return rel1;
1109
1148
 
1110
1149
  vector<Expression*> operands;
1111
- while (lex< sequence< and_op, negate< identifier > > >())
1150
+ while (lex_css< kwd_and >())
1112
1151
  operands.push_back(parse_relation());
1113
1152
 
1114
1153
  return fold_operands(rel1, operands, Binary_Expression::AND);
@@ -1118,21 +1157,23 @@ namespace Sass {
1118
1157
  {
1119
1158
  Expression* expr1 = parse_expression();
1120
1159
  // if it's a singleton, return it directly; don't wrap it
1121
- if (!(peek< eq_op >(position) ||
1122
- peek< neq_op >(position) ||
1123
- peek< gte_op >(position) ||
1124
- peek< gt_op >(position) ||
1125
- peek< lte_op >(position) ||
1126
- peek< lt_op >(position)))
1160
+ if (!(peek< alternatives <
1161
+ kwd_eq,
1162
+ kwd_neq,
1163
+ kwd_gte,
1164
+ kwd_gt,
1165
+ kwd_lte,
1166
+ kwd_lt
1167
+ > >(position)))
1127
1168
  { return expr1; }
1128
1169
 
1129
1170
  Binary_Expression::Type op
1130
- = lex<eq_op>() ? Binary_Expression::EQ
1131
- : lex<neq_op>() ? Binary_Expression::NEQ
1132
- : lex<gte_op>() ? Binary_Expression::GTE
1133
- : lex<lte_op>() ? Binary_Expression::LTE
1134
- : lex<gt_op>() ? Binary_Expression::GT
1135
- : lex<lt_op>() ? Binary_Expression::LT
1171
+ = lex<kwd_eq>() ? Binary_Expression::EQ
1172
+ : lex<kwd_neq>() ? Binary_Expression::NEQ
1173
+ : lex<kwd_gte>() ? Binary_Expression::GTE
1174
+ : lex<kwd_lte>() ? Binary_Expression::LTE
1175
+ : lex<kwd_gt>() ? Binary_Expression::GT
1176
+ : lex<kwd_lt>() ? Binary_Expression::LT
1136
1177
  : Binary_Expression::LT; // whatever
1137
1178
 
1138
1179
  Expression* expr2 = parse_expression();
@@ -1152,7 +1193,7 @@ namespace Sass {
1152
1193
 
1153
1194
  vector<Expression*> operands;
1154
1195
  vector<Binary_Expression::Type> operators;
1155
- while (lex< exactly<'+'> >() || lex< sequence< negate< digits >, exactly<'-'> > >()) {
1196
+ while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {
1156
1197
  operators.push_back(lexed.to_string() == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
1157
1198
  operands.push_back(parse_term());
1158
1199
  }
@@ -1162,38 +1203,35 @@ namespace Sass {
1162
1203
 
1163
1204
  Expression* Parser::parse_term()
1164
1205
  {
1165
- Expression* fact1 = parse_factor();
1166
-
1206
+ Expression* factor = parse_factor();
1167
1207
  // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
1168
- if (peek< exactly<'%'> >(position) && fact1->concrete_type() == Expression::STRING) {
1169
- String_Schema* ss = dynamic_cast<String_Schema*>(fact1);
1170
- if (ss && ss->has_interpolants()) return fact1;
1208
+ if (peek_css< exactly<'%'> >(position) && factor->concrete_type() == Expression::STRING) {
1209
+ String_Schema* ss = dynamic_cast<String_Schema*>(factor);
1210
+ if (ss && ss->has_interpolants()) return factor;
1171
1211
  }
1172
-
1173
1212
  // if it's a singleton, return it directly; don't wrap it
1174
- if (!(peek< exactly<'*'> >(position) ||
1175
- peek< exactly<'/'> >(position) ||
1176
- peek< exactly<'%'> >(position)))
1177
- { return fact1; }
1178
-
1179
- while (lex< block_comment >());
1180
- vector<Expression*> operands;
1181
- vector<Binary_Expression::Type> operators;
1182
- while (lex< exactly<'*'> >() || lex< exactly<'/'> >() || lex< exactly<'%'> >()) {
1183
- if (lexed.to_string() == "*") operators.push_back(Binary_Expression::MUL);
1184
- else if (lexed.to_string() == "/") operators.push_back(Binary_Expression::DIV);
1185
- else operators.push_back(Binary_Expression::MOD);
1213
+ if (!peek< class_char< static_ops > >(position)) return factor;
1214
+ // parse more factors and operators
1215
+ vector<Expression*> operands; // factors
1216
+ vector<Binary_Expression::Type> operators; // ops
1217
+ while (lex_css< class_char< static_ops > >()) {
1218
+ switch(*lexed.begin) {
1219
+ case '*': operators.push_back(Binary_Expression::MUL); break;
1220
+ case '/': operators.push_back(Binary_Expression::DIV); break;
1221
+ case '%': operators.push_back(Binary_Expression::MOD); break;
1222
+ default: throw runtime_error("unknown static op parsed"); break;
1223
+ }
1186
1224
  operands.push_back(parse_factor());
1187
1225
  }
1188
-
1189
- return fold_operands(fact1, operands, operators);
1226
+ // operands and operators to binary expression
1227
+ return fold_operands(factor, operands, operators);
1190
1228
  }
1191
1229
 
1192
1230
  Expression* Parser::parse_factor()
1193
1231
  {
1194
- if (lex< exactly<'('> >()) {
1232
+ if (lex_css< exactly<'('> >()) {
1195
1233
  Expression* value = parse_map();
1196
- if (!lex< exactly<')'> >()) error("unclosed parenthesis", pstate);
1234
+ if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);
1197
1235
  value->is_delayed(false);
1198
1236
  // make sure wrapped lists and division expressions are non-delayed within parentheses
1199
1237
  if (value->concrete_type() == Expression::LIST) {
@@ -1224,19 +1262,19 @@ namespace Sass {
1224
1262
  else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {
1225
1263
  return parse_identifier_schema();
1226
1264
  }
1227
- else if (peek< functional >() && !peek< uri_prefix >()) {
1265
+ else if (peek< functional >()) {
1228
1266
  return parse_function_call();
1229
1267
  }
1230
- else if (lex< sequence< exactly<'+'>, optional_spaces_and_comments, negate< number > > >()) {
1268
+ else if (lex< sequence< exactly<'+'>, optional_css_whitespace, negate< number > > >()) {
1231
1269
  return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::PLUS, parse_factor());
1232
1270
  }
1233
- else if (lex< sequence< exactly<'-'>, optional_spaces_and_comments, negate< number> > >()) {
1271
+ else if (lex< sequence< exactly<'-'>, optional_css_whitespace, negate< number> > >()) {
1234
1272
  return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_factor());
1235
1273
  }
1236
- else if (lex< sequence< not_op, spaces_and_comments > >()) {
1274
+ else if (lex< sequence< kwd_not, css_whitespace > >()) {
1237
1275
  return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::NOT, parse_factor());
1238
1276
  }
1239
- else if (peek < sequence < one_plus < alternatives < spaces_and_comments, exactly<'-'>, exactly<'+'> > >, number > >()) {
1277
+ else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {
1240
1278
  if (parse_number_prefix()) return parse_value(); // prefix is positive
1241
1279
  return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_value());
1242
1280
  }
@@ -1247,44 +1285,10 @@ namespace Sass {
1247
1285
 
1248
1286
  Expression* Parser::parse_value()
1249
1287
  {
1250
- while (lex< block_comment >());
1251
- if (lex< uri_prefix >()) {
1252
- Arguments* args = new (ctx.mem) Arguments(pstate);
1253
- Function_Call* result = new (ctx.mem) Function_Call(pstate, "url", args);
1254
- const char* here = position;
1255
- Position here_p = before_token;
1256
- // Try to parse a SassScript expression. If it succeeds and we can munch
1257
- // a matching rparen, then that's our url. If we can't munch a matching
1258
- // rparen, or if the attempt to parse an expression fails, then try to
1259
- // munch a regular CSS url.
1260
- try {
1261
- // special case -- if there's a comment, treat it as part of a URL
1262
- lex<spaces>();
1263
- if (peek<line_comment_prefix>() || peek<block_comment_prefix>()) error("comment in URL", pstate); // doesn't really matter what we throw
1264
- Expression* expr = parse_list();
1265
- if (!lex< exactly<')'> >()) error("dangling expression in URL", pstate); // doesn't really matter what we throw
1266
- Argument* arg = new (ctx.mem) Argument(expr->pstate(), expr);
1267
- *args << arg;
1268
- return result;
1269
- }
1270
- catch (Sass_Error&) {
1271
- // back up so we can try again
1272
- position = here;
1273
- before_token = here_p;
1274
- }
1275
- catch (...) { throw; }
1276
- lex< spaces >();
1277
- if (lex< url >()) {
1278
- String* the_url = parse_interpolated_chunk(lexed);
1279
- Argument* arg = new (ctx.mem) Argument(the_url->pstate(), the_url);
1280
- *args << arg;
1281
- }
1282
- else {
1283
- error("malformed URL", pstate);
1284
- }
1285
- if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);
1286
- return result;
1287
- }
1288
+ lex< css_comments >();
1289
+ if (lex< ampersand >())
1290
+ {
1291
+ return new (ctx.mem) Parent_Selector(pstate, parse_selector_group()); }
1288
1292
 
1289
1293
  if (lex< important >())
1290
1294
  { return new (ctx.mem) String_Constant(pstate, "!important"); }
@@ -1293,13 +1297,13 @@ namespace Sass {
1293
1297
  if ((stop = peek< value_schema >()))
1294
1298
  { return parse_value_schema(stop); }
1295
1299
 
1296
- if (lex< sequence< true_val, negate< identifier > > >())
1300
+ if (lex< kwd_true >())
1297
1301
  { return new (ctx.mem) Boolean(pstate, true); }
1298
1302
 
1299
- if (lex< sequence< false_val, negate< identifier > > >())
1303
+ if (lex< kwd_false >())
1300
1304
  { return new (ctx.mem) Boolean(pstate, false); }
1301
1305
 
1302
- if (lex< sequence< null, negate< identifier > > >())
1306
+ if (lex< kwd_null >())
1303
1307
  { return new (ctx.mem) Null(pstate); }
1304
1308
 
1305
1309
  if (lex< identifier >()) {
@@ -1366,7 +1370,7 @@ namespace Sass {
1366
1370
  const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
1367
1371
  if (j) { --j;
1368
1372
  // parse the interpolant and accumulate it
1369
- Expression* interp_node = Parser::from_token(Token(p+2, j, before_token), ctx, pstate).parse_list();
1373
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
1370
1374
  interp_node->is_interpolant(true);
1371
1375
  (*schema) << interp_node;
1372
1376
  i = j;
@@ -1427,7 +1431,7 @@ namespace Sass {
1427
1431
  const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
1428
1432
  if (j) {
1429
1433
  // parse the interpolant and accumulate it
1430
- Expression* interp_node = Parser::from_token(Token(p+2, j, before_token), ctx, pstate).parse_list();
1434
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
1431
1435
  interp_node->is_interpolant(true);
1432
1436
  (*schema) << interp_node;
1433
1437
  i = j;
@@ -1470,7 +1474,7 @@ namespace Sass {
1470
1474
  size_t num_items = 0;
1471
1475
  while (position < stop) {
1472
1476
  if (lex< interpolant >()) {
1473
- Token insides(Token(lexed.begin + 2, lexed.end - 1, before_token));
1477
+ Token insides(Token(lexed.begin + 2, lexed.end - 1));
1474
1478
  Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();
1475
1479
  interp_node->is_interpolant(true);
1476
1480
  (*schema) << interp_node;
@@ -1564,7 +1568,7 @@ namespace Sass {
1564
1568
  const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace
1565
1569
  if (j) {
1566
1570
  // parse the interpolant and accumulate it
1567
- Expression* interp_node = Parser::from_token(Token(p+2, j, before_token), ctx, pstate).parse_list();
1571
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
1568
1572
  interp_node->is_interpolant(true);
1569
1573
  (*schema) << interp_node;
1570
1574
  schema->has_interpolants(true);
@@ -1595,7 +1599,7 @@ namespace Sass {
1595
1599
  const char* arg_end = position;
1596
1600
  lex< exactly<')'> >();
1597
1601
 
1598
- Argument* arg = new (ctx.mem) Argument(arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end, before_token)));
1602
+ Argument* arg = new (ctx.mem) Argument(arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));
1599
1603
  Arguments* args = new (ctx.mem) Arguments(arg_pos);
1600
1604
  *args << arg;
1601
1605
  return new (ctx.mem) Function_Call(call_pos, name, args);
@@ -1604,11 +1608,9 @@ namespace Sass {
1604
1608
  Function_Call* Parser::parse_function_call()
1605
1609
  {
1606
1610
  lex< identifier >();
1607
- string name(Util::normalize_underscores(lexed));
1608
- ParserState source_position_of_call = pstate;
1609
-
1610
- Function_Call* the_call = new (ctx.mem) Function_Call(source_position_of_call, name, parse_arguments());
1611
- return the_call;
1611
+ string name(lexed);
1612
+ Arguments* args = parse_arguments(name == "url");
1613
+ return new (ctx.mem) Function_Call(pstate, name, args);
1612
1614
  }
1613
1615
 
1614
1616
  Function_Call_Schema* Parser::parse_function_call_schema()
@@ -1622,19 +1624,20 @@ namespace Sass {
1622
1624
 
1623
1625
  If* Parser::parse_if_directive(bool else_if)
1624
1626
  {
1625
- lex< if_directive >() || (else_if && lex< exactly<if_after_else_kwd> >());
1627
+ lex< kwd_if_directive >() || (else_if && lex< exactly<if_after_else_kwd> >());
1626
1628
  ParserState if_source_position = pstate;
1627
1629
  Expression* predicate = parse_list();
1628
1630
  predicate->is_delayed(false);
1629
1631
  if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if", pstate);
1630
1632
  Block* consequent = parse_block();
1631
1633
  Block* alternative = 0;
1632
- if (lex< else_directive >()) {
1633
- if (peek< exactly<if_after_else_kwd> >()) {
1634
- alternative = new (ctx.mem) Block(pstate);
1635
- (*alternative) << parse_if_directive(true);
1636
- }
1637
- else if (!peek< exactly<'{'> >()) {
1634
+
1635
+ if (lex< elseif_directive >()) {
1636
+ alternative = new (ctx.mem) Block(pstate);
1637
+ (*alternative) << parse_if_directive(true);
1638
+ }
1639
+ else if (lex< kwd_else_directive >()) {
1640
+ if (!peek< exactly<'{'> >()) {
1638
1641
  error("expected '{' after @else", pstate);
1639
1642
  }
1640
1643
  else {
@@ -1646,16 +1649,16 @@ namespace Sass {
1646
1649
 
1647
1650
  For* Parser::parse_for_directive()
1648
1651
  {
1649
- lex< for_directive >();
1652
+ lex< kwd_for_directive >();
1650
1653
  ParserState for_source_position = pstate;
1651
1654
  if (!lex< variable >()) error("@for directive requires an iteration variable", pstate);
1652
1655
  string var(Util::normalize_underscores(lexed));
1653
- if (!lex< from >()) error("expected 'from' keyword in @for directive", pstate);
1656
+ if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);
1654
1657
  Expression* lower_bound = parse_expression();
1655
1658
  lower_bound->is_delayed(false);
1656
1659
  bool inclusive = false;
1657
- if (lex< through >()) inclusive = true;
1658
- else if (lex< to >()) inclusive = false;
1660
+ if (lex< kwd_through >()) inclusive = true;
1661
+ else if (lex< kwd_to >()) inclusive = false;
1659
1662
  else error("expected 'through' or 'to' keyword in @for directive", pstate);
1660
1663
  Expression* upper_bound = parse_expression();
1661
1664
  upper_bound->is_delayed(false);
@@ -1666,16 +1669,16 @@ namespace Sass {
1666
1669
 
1667
1670
  Each* Parser::parse_each_directive()
1668
1671
  {
1669
- lex < each_directive >();
1672
+ lex < kwd_each_directive >();
1670
1673
  ParserState each_source_position = pstate;
1671
1674
  if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);
1672
1675
  vector<string> vars;
1673
1676
  vars.push_back(Util::normalize_underscores(lexed));
1674
- while (peek< exactly<','> >() && lex< exactly<','> >()) {
1677
+ while (lex< exactly<','> >()) {
1675
1678
  if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);
1676
1679
  vars.push_back(Util::normalize_underscores(lexed));
1677
1680
  }
1678
- if (!lex< in >()) error("expected 'in' keyword in @each directive", pstate);
1681
+ if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);
1679
1682
  Expression* list = parse_list();
1680
1683
  list->is_delayed(false);
1681
1684
  if (list->concrete_type() == Expression::LIST) {
@@ -1691,7 +1694,7 @@ namespace Sass {
1691
1694
 
1692
1695
  While* Parser::parse_while_directive()
1693
1696
  {
1694
- lex< while_directive >();
1697
+ lex< kwd_while_directive >();
1695
1698
  ParserState while_source_position = pstate;
1696
1699
  Expression* predicate = parse_list();
1697
1700
  predicate->is_delayed(false);
@@ -1701,7 +1704,7 @@ namespace Sass {
1701
1704
 
1702
1705
  Media_Block* Parser::parse_media_block()
1703
1706
  {
1704
- lex< media >();
1707
+ lex< kwd_media >();
1705
1708
  ParserState media_source_position = pstate;
1706
1709
 
1707
1710
  List* media_queries = parse_media_queries();
@@ -1776,7 +1779,7 @@ namespace Sass {
1776
1779
 
1777
1780
  Feature_Block* Parser::parse_feature_block()
1778
1781
  {
1779
- lex< supports >();
1782
+ lex< kwd_supports >();
1780
1783
  ParserState supports_source_position = pstate;
1781
1784
 
1782
1785
  Feature_Query* feature_queries = parse_feature_queries();
@@ -1805,9 +1808,9 @@ namespace Sass {
1805
1808
 
1806
1809
  Feature_Query_Condition* Parser::parse_feature_query()
1807
1810
  {
1808
- if (peek< not_op >(position)) return parse_supports_negation();
1809
- else if (peek< and_op >(position)) return parse_supports_conjunction();
1810
- else if (peek< or_op >(position)) return parse_supports_disjunction();
1811
+ if (peek< kwd_not >(position)) return parse_supports_negation();
1812
+ else if (peek< kwd_and >(position)) return parse_supports_conjunction();
1813
+ else if (peek< kwd_or >(position)) return parse_supports_disjunction();
1811
1814
  else if (peek< exactly<'('> >(position)) return parse_feature_query_in_parens();
1812
1815
  else return parse_supports_declaration();
1813
1816
  }
@@ -1826,7 +1829,7 @@ namespace Sass {
1826
1829
 
1827
1830
  Feature_Query_Condition* Parser::parse_supports_negation()
1828
1831
  {
1829
- lex< not_op >();
1832
+ lex< kwd_not >();
1830
1833
 
1831
1834
  Feature_Query_Condition* cond = parse_feature_query();
1832
1835
  cond->operand(Feature_Query_Condition::NOT);
@@ -1836,7 +1839,7 @@ namespace Sass {
1836
1839
 
1837
1840
  Feature_Query_Condition* Parser::parse_supports_conjunction()
1838
1841
  {
1839
- lex< and_op >();
1842
+ lex< kwd_and >();
1840
1843
 
1841
1844
  Feature_Query_Condition* cond = parse_feature_query();
1842
1845
  cond->operand(Feature_Query_Condition::AND);
@@ -1846,7 +1849,7 @@ namespace Sass {
1846
1849
 
1847
1850
  Feature_Query_Condition* Parser::parse_supports_disjunction()
1848
1851
  {
1849
- lex< or_op >();
1852
+ lex< kwd_or >();
1850
1853
 
1851
1854
  Feature_Query_Condition* cond = parse_feature_query();
1852
1855
  cond->operand(Feature_Query_Condition::OR);
@@ -1866,7 +1869,7 @@ namespace Sass {
1866
1869
 
1867
1870
  At_Root_Block* Parser::parse_at_root_block()
1868
1871
  {
1869
- lex<at_root>();
1872
+ lex<kwd_at_root>();
1870
1873
  ParserState at_source_position = pstate;
1871
1874
  Block* body = 0;
1872
1875
  At_Root_Expression* expr = 0;
@@ -1895,11 +1898,8 @@ namespace Sass {
1895
1898
  lex< exactly<'('> >();
1896
1899
  if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);
1897
1900
 
1898
- if (!peek< alternatives< with_directive, without_directive > >()) {
1899
- const char* i = position;
1900
- const char* p = peek< until<')'> >(i);
1901
- Token* t = new Token(i, p, Position(0, 0));
1902
- error("Invalid CSS after \"(\": expected \"with\" or \"without\", was \""+t->to_string()+"\"", pstate);
1901
+ if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {
1902
+ css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");
1903
1903
  }
1904
1904
 
1905
1905
  Declaration* declaration = parse_declaration();
@@ -1945,19 +1945,19 @@ namespace Sass {
1945
1945
 
1946
1946
  Warning* Parser::parse_warning()
1947
1947
  {
1948
- lex< warn >();
1948
+ lex< kwd_warn >();
1949
1949
  return new (ctx.mem) Warning(pstate, parse_list());
1950
1950
  }
1951
1951
 
1952
1952
  Error* Parser::parse_error()
1953
1953
  {
1954
- lex< err >();
1954
+ lex< kwd_err >();
1955
1955
  return new (ctx.mem) Error(pstate, parse_list());
1956
1956
  }
1957
1957
 
1958
1958
  Debug* Parser::parse_debug()
1959
1959
  {
1960
- lex< dbg >();
1960
+ lex< kwd_dbg >();
1961
1961
  return new (ctx.mem) Debug(pstate, parse_list());
1962
1962
  }
1963
1963
 
@@ -1977,8 +1977,9 @@ namespace Sass {
1977
1977
  (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
1978
1978
  (q = peek< percentage >(p)) ||
1979
1979
  (q = peek< dimension >(p)) ||
1980
- (q = peek< quoted_string >(p)) ||
1980
+ (q = peek< quoted_string >(p)) ||
1981
1981
  (q = peek< exactly<'*'> >(p)) ||
1982
+ (q = peek< exactly<sel_deep_kwd> >(p)) ||
1982
1983
  (q = peek< exactly<'('> >(p)) ||
1983
1984
  (q = peek< exactly<')'> >(p)) ||
1984
1985
  (q = peek< exactly<'['> >(p)) ||
@@ -1989,14 +1990,15 @@ namespace Sass {
1989
1990
  (q = peek< exactly<','> >(p)) ||
1990
1991
  (saw_stuff && (q = peek< exactly<'-'> >(p))) ||
1991
1992
  (q = peek< binomial >(p)) ||
1993
+ (q = peek< block_comment >(p)) ||
1992
1994
  (q = peek< sequence< optional<sign>,
1993
- optional<digits>,
1995
+ zero_plus<digit>,
1994
1996
  exactly<'n'> > >(p)) ||
1995
1997
  (q = peek< sequence< optional<sign>,
1996
- digits > >(p)) ||
1998
+ one_plus<digit> > >(p)) ||
1997
1999
  (q = peek< number >(p)) ||
1998
2000
  (q = peek< sequence< exactly<'&'>,
1999
- identifier_fragment > >(p)) ||
2001
+ identifier_alnums > >(p)) ||
2000
2002
  (q = peek< exactly<'&'> >(p)) ||
2001
2003
  (q = peek< exactly<'%'> >(p)) ||
2002
2004
  (q = peek< alternatives<exact_match,
@@ -2036,7 +2038,7 @@ namespace Sass {
2036
2038
  (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
2037
2039
  (q = peek< percentage >(p)) ||
2038
2040
  (q = peek< dimension >(p)) ||
2039
- (q = peek< quoted_string >(p)) ||
2041
+ (q = peek< quoted_string >(p)) ||
2040
2042
  (q = peek< exactly<'*'> >(p)) ||
2041
2043
  (q = peek< exactly<'('> >(p)) ||
2042
2044
  (q = peek< exactly<')'> >(p)) ||
@@ -2048,14 +2050,15 @@ namespace Sass {
2048
2050
  (q = peek< exactly<','> >(p)) ||
2049
2051
  (saw_stuff && (q = peek< exactly<'-'> >(p))) ||
2050
2052
  (q = peek< binomial >(p)) ||
2053
+ (q = peek< block_comment >(p)) ||
2051
2054
  (q = peek< sequence< optional<sign>,
2052
- optional<digits>,
2055
+ zero_plus<digit>,
2053
2056
  exactly<'n'> > >(p)) ||
2054
2057
  (q = peek< sequence< optional<sign>,
2055
- digits > >(p)) ||
2058
+ one_plus<digit> > >(p)) ||
2056
2059
  (q = peek< number >(p)) ||
2057
2060
  (q = peek< sequence< exactly<'&'>,
2058
- identifier_fragment > >(p)) ||
2061
+ identifier_alnums > >(p)) ||
2059
2062
  (q = peek< exactly<'&'> >(p)) ||
2060
2063
  (q = peek< exactly<'%'> >(p)) ||
2061
2064
  (q = peek< alternatives<exact_match,
@@ -2184,7 +2187,42 @@ namespace Sass {
2184
2187
 
2185
2188
  void Parser::error(string msg, Position pos)
2186
2189
  {
2187
- throw Sass_Error(Sass_Error::syntax, ParserState(path, pos.line ? pos : before_token, Offset(0, 0)), msg);
2190
+ throw Sass_Error(Sass_Error::syntax, ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);
2191
+ }
2192
+
2193
+ // print a css parsing error with actual context information from parsed source
2194
+ void Parser::css_error(const string& msg, const string& prefix, const string& middle)
2195
+ {
2196
+ int max_len = 14;
2197
+ const char* pos = peek < optional_spaces >();
2198
+ bool ellipsis_left = false;
2199
+ const char* pos_left(pos);
2200
+ while (*pos_left && pos_left >= source) {
2201
+ if (pos - pos_left > max_len) {
2202
+ ellipsis_left = true;
2203
+ break;
2204
+ }
2205
+ if (*pos_left == '\r') break;
2206
+ if (*pos_left == '\n') break;
2207
+ -- pos_left;
2208
+ }
2209
+ bool ellipsis_right = false;
2210
+ const char* pos_right(pos);
2211
+ while (*pos_right && pos_right <= end) {
2212
+ if (pos_right - pos > max_len) {
2213
+ ellipsis_right = true;
2214
+ break;
2215
+ }
2216
+ if (*pos_right == '\r') break;
2217
+ if (*pos_right == '\n') break;
2218
+ ++ pos_right;
2219
+ }
2220
+ string left(pos_left, pos);
2221
+ string right(pos, pos_right);
2222
+ if (ellipsis_left) left = ellipsis + left;
2223
+ if (ellipsis_right) right = right + ellipsis;
2224
+ // now pass new message to the more generic error function
2225
+ error(msg + prefix + quote(left) + middle + quote(right), pstate);
2188
2226
  }
2189
2227
 
2190
2228
  }