sassc 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
  }