sassc 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ext/libsass/.gitignore +6 -0
- data/ext/libsass/.travis.yml +5 -1
- data/ext/libsass/Makefile +12 -3
- data/ext/libsass/Makefile.am +16 -28
- data/ext/libsass/Readme.md +1 -0
- data/ext/libsass/appveyor.yml +1 -2
- data/ext/libsass/ast.cpp +9 -0
- data/ext/libsass/ast.hpp +152 -55
- data/ext/libsass/ast_factory.hpp +2 -0
- data/ext/libsass/ast_fwd_decl.hpp +1 -0
- data/ext/libsass/backtrace.hpp +2 -2
- data/ext/libsass/bind.cpp +15 -13
- data/ext/libsass/configure.ac +17 -5
- data/ext/libsass/constants.cpp +22 -2
- data/ext/libsass/constants.hpp +21 -2
- data/ext/libsass/context.cpp +79 -57
- data/ext/libsass/context.hpp +23 -9
- data/ext/libsass/contextualize.cpp +2 -28
- data/ext/libsass/contextualize.hpp +6 -10
- data/ext/libsass/contextualize_eval.cpp +93 -0
- data/ext/libsass/contextualize_eval.hpp +44 -0
- data/ext/libsass/contrib/plugin.cpp +57 -0
- data/ext/libsass/cssize.cpp +3 -1
- data/ext/libsass/debugger.hpp +242 -83
- data/ext/libsass/emitter.cpp +1 -1
- data/ext/libsass/emitter.hpp +1 -1
- data/ext/libsass/environment.hpp +109 -25
- data/ext/libsass/error_handling.cpp +3 -3
- data/ext/libsass/error_handling.hpp +0 -1
- data/ext/libsass/eval.cpp +145 -61
- data/ext/libsass/eval.hpp +9 -1
- data/ext/libsass/expand.cpp +134 -60
- data/ext/libsass/expand.hpp +5 -2
- data/ext/libsass/extend.cpp +7 -5
- data/ext/libsass/file.cpp +176 -123
- data/ext/libsass/file.hpp +44 -7
- data/ext/libsass/functions.cpp +36 -17
- data/ext/libsass/functions.hpp +2 -2
- data/ext/libsass/inspect.cpp +23 -14
- data/ext/libsass/inspect.hpp +1 -0
- data/ext/libsass/json.cpp +132 -135
- data/ext/libsass/lexer.cpp +133 -0
- data/ext/libsass/lexer.hpp +239 -0
- data/ext/libsass/listize.cpp +83 -0
- data/ext/libsass/listize.hpp +41 -0
- data/ext/libsass/operation.hpp +2 -0
- data/ext/libsass/output.cpp +5 -6
- data/ext/libsass/parser.cpp +426 -388
- data/ext/libsass/parser.hpp +97 -109
- data/ext/libsass/plugins.cpp +15 -2
- data/ext/libsass/plugins.hpp +6 -4
- data/ext/libsass/position.cpp +52 -17
- data/ext/libsass/position.hpp +19 -17
- data/ext/libsass/prelexer.cpp +202 -235
- data/ext/libsass/prelexer.hpp +73 -333
- data/ext/libsass/sass.cpp +21 -11
- data/ext/libsass/sass.h +6 -6
- data/ext/libsass/sass_context.cpp +167 -81
- data/ext/libsass/sass_context.h +26 -6
- data/ext/libsass/sass_functions.cpp +49 -40
- data/ext/libsass/sass_functions.h +55 -43
- data/ext/libsass/sass_interface.cpp +9 -8
- data/ext/libsass/sass_interface.h +3 -3
- data/ext/libsass/sass_version.h +8 -0
- data/ext/libsass/sass_version.h.in +8 -0
- data/ext/libsass/script/ci-build-libsass +3 -3
- data/ext/libsass/script/ci-report-coverage +2 -1
- data/ext/libsass/source_map.cpp +2 -2
- data/ext/libsass/util.cpp +60 -11
- data/ext/libsass/util.hpp +6 -1
- data/ext/libsass/win/libsass.filters +12 -0
- data/ext/libsass/win/libsass.vcxproj +10 -0
- data/lib/sassc.rb +3 -1
- data/lib/sassc/cache_stores/base.rb +2 -0
- data/lib/sassc/dependency.rb +3 -1
- data/lib/sassc/engine.rb +31 -16
- data/lib/sassc/error.rb +3 -2
- data/lib/sassc/functions_handler.rb +54 -0
- data/lib/sassc/import_handler.rb +41 -0
- data/lib/sassc/importer.rb +4 -31
- data/lib/sassc/native.rb +1 -1
- data/lib/sassc/native/native_context_api.rb +3 -2
- data/lib/sassc/script.rb +0 -51
- data/lib/sassc/version.rb +1 -1
- data/sassc.gemspec +1 -0
- data/test/custom_importer_test.rb +72 -69
- data/test/engine_test.rb +53 -54
- data/test/functions_test.rb +40 -39
- data/test/native_test.rb +145 -149
- data/test/output_style_test.rb +98 -0
- data/test/test_helper.rb +21 -7
- 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
|
data/ext/libsass/operation.hpp
CHANGED
@@ -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); }
|
data/ext/libsass/output.cpp
CHANGED
@@ -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
|
-
|
340
|
+
in_wrapped = true;
|
342
341
|
s->perform(this);
|
343
|
-
|
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(
|
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 {
|
data/ext/libsass/parser.cpp
CHANGED
@@ -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
|
-
|
70
|
-
|
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<
|
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<
|
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<
|
109
|
+
else if (peek< kwd_if_directive >()) {
|
101
110
|
(*root) << parse_if_directive();
|
102
111
|
}
|
103
|
-
else if (peek<
|
112
|
+
else if (peek< kwd_for_directive >()) {
|
104
113
|
(*root) << parse_for_directive();
|
105
114
|
}
|
106
|
-
else if (peek<
|
115
|
+
else if (peek< kwd_each_directive >()) {
|
107
116
|
(*root) << parse_each_directive();
|
108
117
|
}
|
109
|
-
else if (peek<
|
118
|
+
else if (peek< kwd_while_directive >()) {
|
110
119
|
(*root) << parse_while_directive();
|
111
120
|
}
|
112
|
-
else if (peek<
|
121
|
+
else if (peek< kwd_media >()) {
|
113
122
|
(*root) << parse_media_block();
|
114
123
|
}
|
115
|
-
else if (peek<
|
124
|
+
else if (peek< kwd_at_root >()) {
|
116
125
|
(*root) << parse_at_root_block();
|
117
126
|
}
|
118
|
-
else if (peek<
|
127
|
+
else if (peek< kwd_supports >()) {
|
119
128
|
(*root) << parse_feature_block();
|
120
129
|
}
|
121
|
-
else if (peek<
|
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<
|
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<
|
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<
|
159
|
+
lex< css_whitespace >();
|
151
160
|
if (position >= end) break;
|
152
|
-
error("invalid top-level expression",
|
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<
|
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
|
-
|
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
|
-
|
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
|
-
|
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 (
|
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<
|
269
|
-
else if (lex<
|
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);
|
314
|
+
string name(lexed);
|
315
|
+
Position position = after_token;
|
289
316
|
Parameters* params = new (ctx.mem) Parameters(pstate);
|
290
|
-
if (
|
317
|
+
if (lex_css< exactly<'('> >()) {
|
291
318
|
// if there's anything there at all
|
292
|
-
if (!
|
319
|
+
if (!peek_css< exactly<')'> >()) {
|
293
320
|
do (*params) << parse_parameter();
|
294
|
-
while (
|
321
|
+
while (lex_css< exactly<','> >());
|
295
322
|
}
|
296
|
-
|
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<
|
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 (!
|
346
|
-
do (*args) << parse_argument();
|
347
|
-
while (
|
371
|
+
if (!peek_css< exactly<')'> >()) {
|
372
|
+
do (*args) << parse_argument(has_url);
|
373
|
+
while (lex_css< exactly<','> >());
|
348
374
|
}
|
349
|
-
|
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
|
-
|
360
|
-
if (
|
361
|
-
|
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
|
-
|
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 (
|
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
|
420
|
+
bool is_default = false;
|
393
421
|
bool is_global = false;
|
394
422
|
while (peek< default_flag >() || peek< global_flag >()) {
|
395
|
-
|
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,
|
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<
|
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<
|
487
|
-
|
488
|
-
|
489
|
-
|
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 (
|
542
|
+
while (peek_css< exactly<','> >())
|
512
543
|
{
|
513
544
|
// consume everything up and including the comma speparator
|
514
|
-
reloop = lex< sequence<
|
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 (
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
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 (
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
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 &&
|
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 (
|
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
|
-
!(
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
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
|
-
|
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 >()
|
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<
|
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
|
-
|
724
|
+
zero_plus<digit>,
|
687
725
|
exactly<'n'>,
|
688
|
-
|
726
|
+
optional_css_whitespace,
|
689
727
|
exactly<')'> > >()) {
|
690
728
|
lex< sequence< optional<sign>,
|
691
|
-
|
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>,
|
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,
|
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
|
-
|
767
|
+
lex_css< exactly<'['> >();
|
730
768
|
ParserState p = pstate;
|
731
|
-
if (!
|
769
|
+
if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
|
732
770
|
string name(lexed);
|
733
|
-
if (
|
734
|
-
if (!
|
735
|
-
|
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 (
|
779
|
+
if (lex_css< identifier >()) {
|
742
780
|
value = new (ctx.mem) String_Constant(p, lexed);
|
743
781
|
}
|
744
|
-
else if (
|
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 (!
|
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
|
-
|
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
|
-
|
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 (
|
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<
|
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<
|
845
|
+
else if (peek< kwd_if_directive >()) {
|
813
846
|
(*block) << parse_if_directive();
|
814
847
|
}
|
815
|
-
else if (peek<
|
848
|
+
else if (peek< kwd_for_directive >()) {
|
816
849
|
(*block) << parse_for_directive();
|
817
850
|
}
|
818
|
-
else if (peek<
|
851
|
+
else if (peek< kwd_each_directive >()) {
|
819
852
|
(*block) << parse_each_directive();
|
820
853
|
}
|
821
|
-
else if (peek <
|
854
|
+
else if (peek < kwd_while_directive >()) {
|
822
855
|
(*block) << parse_while_directive();
|
823
856
|
}
|
824
|
-
else if (lex <
|
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<
|
861
|
+
else if (peek< kwd_warn >()) {
|
829
862
|
(*block) << parse_warning();
|
830
863
|
semicolon = true;
|
831
864
|
}
|
832
|
-
else if (peek<
|
865
|
+
else if (peek< kwd_err >()) {
|
833
866
|
(*block) << parse_error();
|
834
867
|
semicolon = true;
|
835
868
|
}
|
836
|
-
else if (peek<
|
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<
|
876
|
+
else if (peek< kwd_mixin >() || peek< kwd_function >()) {
|
844
877
|
(*block) << parse_definition();
|
845
878
|
}
|
846
|
-
else if (peek<
|
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<
|
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<
|
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<
|
907
|
+
else if (peek< kwd_media >()) {
|
875
908
|
(*block) << parse_media_block();
|
876
909
|
}
|
877
|
-
else if (peek<
|
910
|
+
else if (peek< kwd_supports >()) {
|
878
911
|
(*block) << parse_feature_block();
|
879
912
|
}
|
880
|
-
else if (peek<
|
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
|
-
|
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
|
-
|
952
|
-
if (
|
953
|
-
if (
|
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
|
-
|
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 (
|
1025
|
+
while (lex_css< exactly<','> >())
|
994
1026
|
{
|
995
1027
|
// allow trailing commas - #495
|
996
|
-
if (
|
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 (
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
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 (!
|
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 (
|
1071
|
+
while (lex_css< exactly<','> >())
|
1038
1072
|
{
|
1039
|
-
if (
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
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 (
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
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 (!(
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
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 (!
|
1134
|
+
if (!peek_css< kwd_or >()) return conj1;
|
1096
1135
|
|
1097
1136
|
vector<Expression*> operands;
|
1098
|
-
while (
|
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 (!
|
1147
|
+
if (!peek_css< kwd_and >()) return rel1;
|
1109
1148
|
|
1110
1149
|
vector<Expression*> operands;
|
1111
|
-
while (
|
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<
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
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<
|
1131
|
-
: lex<
|
1132
|
-
: lex<
|
1133
|
-
: lex<
|
1134
|
-
: lex<
|
1135
|
-
: lex<
|
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<
|
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*
|
1166
|
-
|
1206
|
+
Expression* factor = parse_factor();
|
1167
1207
|
// Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
|
1168
|
-
if (
|
1169
|
-
String_Schema* ss = dynamic_cast<String_Schema*>(
|
1170
|
-
if (ss && ss->has_interpolants()) return
|
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 (!
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
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(
|
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 (
|
1232
|
+
if (lex_css< exactly<'('> >()) {
|
1195
1233
|
Expression* value = parse_map();
|
1196
|
-
if (!
|
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 >()
|
1265
|
+
else if (peek< functional >()) {
|
1228
1266
|
return parse_function_call();
|
1229
1267
|
}
|
1230
|
-
else if (lex< sequence< exactly<'+'>,
|
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<'-'>,
|
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<
|
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 <
|
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
|
-
|
1251
|
-
if (lex<
|
1252
|
-
|
1253
|
-
|
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<
|
1300
|
+
if (lex< kwd_true >())
|
1297
1301
|
{ return new (ctx.mem) Boolean(pstate, true); }
|
1298
1302
|
|
1299
|
-
if (lex<
|
1303
|
+
if (lex< kwd_false >())
|
1300
1304
|
{ return new (ctx.mem) Boolean(pstate, false); }
|
1301
1305
|
|
1302
|
-
if (lex<
|
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
|
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
|
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
|
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
|
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
|
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(
|
1608
|
-
|
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<
|
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
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
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<
|
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<
|
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<
|
1658
|
-
else if (lex<
|
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 <
|
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 (
|
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<
|
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<
|
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<
|
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<
|
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<
|
1809
|
-
else if (peek<
|
1810
|
-
else if (peek<
|
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<
|
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<
|
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<
|
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<
|
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<
|
1899
|
-
|
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<
|
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<
|
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<
|
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
|
-
|
1995
|
+
zero_plus<digit>,
|
1994
1996
|
exactly<'n'> > >(p)) ||
|
1995
1997
|
(q = peek< sequence< optional<sign>,
|
1996
|
-
|
1998
|
+
one_plus<digit> > >(p)) ||
|
1997
1999
|
(q = peek< number >(p)) ||
|
1998
2000
|
(q = peek< sequence< exactly<'&'>,
|
1999
|
-
|
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
|
-
|
2055
|
+
zero_plus<digit>,
|
2053
2056
|
exactly<'n'> > >(p)) ||
|
2054
2057
|
(q = peek< sequence< optional<sign>,
|
2055
|
-
|
2058
|
+
one_plus<digit> > >(p)) ||
|
2056
2059
|
(q = peek< number >(p)) ||
|
2057
2060
|
(q = peek< sequence< exactly<'&'>,
|
2058
|
-
|
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
|
}
|