sassc 0.0.1

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 (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +24 -0
  8. data/Rakefile +21 -0
  9. data/ext/libsass/.editorconfig +15 -0
  10. data/ext/libsass/.gitattributes +2 -0
  11. data/ext/libsass/.gitignore +61 -0
  12. data/ext/libsass/.travis.yml +38 -0
  13. data/ext/libsass/COPYING +25 -0
  14. data/ext/libsass/INSTALL +1 -0
  15. data/ext/libsass/LICENSE +25 -0
  16. data/ext/libsass/Makefile +223 -0
  17. data/ext/libsass/Makefile.am +145 -0
  18. data/ext/libsass/Readme.md +93 -0
  19. data/ext/libsass/appveyor.yml +76 -0
  20. data/ext/libsass/ast.cpp +581 -0
  21. data/ext/libsass/ast.hpp +1949 -0
  22. data/ext/libsass/ast_def_macros.hpp +16 -0
  23. data/ext/libsass/ast_factory.hpp +87 -0
  24. data/ext/libsass/ast_fwd_decl.hpp +72 -0
  25. data/ext/libsass/b64/cencode.h +32 -0
  26. data/ext/libsass/b64/encode.h +77 -0
  27. data/ext/libsass/backtrace.hpp +81 -0
  28. data/ext/libsass/base64vlq.cpp +43 -0
  29. data/ext/libsass/base64vlq.hpp +28 -0
  30. data/ext/libsass/bind.cpp +187 -0
  31. data/ext/libsass/bind.hpp +18 -0
  32. data/ext/libsass/cencode.c +102 -0
  33. data/ext/libsass/color_names.hpp +324 -0
  34. data/ext/libsass/configure.ac +130 -0
  35. data/ext/libsass/constants.cpp +144 -0
  36. data/ext/libsass/constants.hpp +145 -0
  37. data/ext/libsass/context.cpp +507 -0
  38. data/ext/libsass/context.hpp +150 -0
  39. data/ext/libsass/contextualize.cpp +157 -0
  40. data/ext/libsass/contextualize.hpp +65 -0
  41. data/ext/libsass/copy_c_str.cpp +13 -0
  42. data/ext/libsass/copy_c_str.hpp +5 -0
  43. data/ext/libsass/debug.hpp +39 -0
  44. data/ext/libsass/environment.hpp +75 -0
  45. data/ext/libsass/error_handling.cpp +28 -0
  46. data/ext/libsass/error_handling.hpp +28 -0
  47. data/ext/libsass/eval.cpp +1149 -0
  48. data/ext/libsass/eval.hpp +80 -0
  49. data/ext/libsass/expand.cpp +430 -0
  50. data/ext/libsass/expand.hpp +77 -0
  51. data/ext/libsass/extconf.rb +6 -0
  52. data/ext/libsass/extend.cpp +1962 -0
  53. data/ext/libsass/extend.hpp +50 -0
  54. data/ext/libsass/file.cpp +291 -0
  55. data/ext/libsass/file.hpp +18 -0
  56. data/ext/libsass/functions.cpp +1565 -0
  57. data/ext/libsass/functions.hpp +187 -0
  58. data/ext/libsass/inspect.cpp +727 -0
  59. data/ext/libsass/inspect.hpp +108 -0
  60. data/ext/libsass/json.cpp +1411 -0
  61. data/ext/libsass/json.hpp +117 -0
  62. data/ext/libsass/kwd_arg_macros.hpp +23 -0
  63. data/ext/libsass/m4/.gitkeep +0 -0
  64. data/ext/libsass/mapping.hpp +17 -0
  65. data/ext/libsass/memory_manager.hpp +54 -0
  66. data/ext/libsass/node.cpp +251 -0
  67. data/ext/libsass/node.hpp +122 -0
  68. data/ext/libsass/operation.hpp +153 -0
  69. data/ext/libsass/output_compressed.cpp +401 -0
  70. data/ext/libsass/output_compressed.hpp +95 -0
  71. data/ext/libsass/output_nested.cpp +364 -0
  72. data/ext/libsass/output_nested.hpp +108 -0
  73. data/ext/libsass/parser.cpp +2016 -0
  74. data/ext/libsass/parser.hpp +264 -0
  75. data/ext/libsass/paths.hpp +69 -0
  76. data/ext/libsass/position.hpp +22 -0
  77. data/ext/libsass/posix/getopt.c +562 -0
  78. data/ext/libsass/posix/getopt.h +95 -0
  79. data/ext/libsass/prelexer.cpp +688 -0
  80. data/ext/libsass/prelexer.hpp +513 -0
  81. data/ext/libsass/remove_placeholders.cpp +59 -0
  82. data/ext/libsass/remove_placeholders.hpp +43 -0
  83. data/ext/libsass/res/resource.rc +35 -0
  84. data/ext/libsass/sass.cpp +33 -0
  85. data/ext/libsass/sass.h +60 -0
  86. data/ext/libsass/sass2scss.cpp +834 -0
  87. data/ext/libsass/sass2scss.h +110 -0
  88. data/ext/libsass/sass_context.cpp +709 -0
  89. data/ext/libsass/sass_context.h +120 -0
  90. data/ext/libsass/sass_functions.cpp +137 -0
  91. data/ext/libsass/sass_functions.h +90 -0
  92. data/ext/libsass/sass_interface.cpp +277 -0
  93. data/ext/libsass/sass_interface.h +97 -0
  94. data/ext/libsass/sass_util.cpp +136 -0
  95. data/ext/libsass/sass_util.hpp +259 -0
  96. data/ext/libsass/sass_values.cpp +337 -0
  97. data/ext/libsass/sass_values.h +124 -0
  98. data/ext/libsass/script/bootstrap +10 -0
  99. data/ext/libsass/script/branding +10 -0
  100. data/ext/libsass/script/ci-build-libsass +72 -0
  101. data/ext/libsass/script/ci-install-compiler +4 -0
  102. data/ext/libsass/script/ci-install-deps +19 -0
  103. data/ext/libsass/script/ci-report-coverage +25 -0
  104. data/ext/libsass/script/coveralls-debug +32 -0
  105. data/ext/libsass/script/spec +5 -0
  106. data/ext/libsass/script/tap-driver +652 -0
  107. data/ext/libsass/script/tap-runner +1 -0
  108. data/ext/libsass/source_map.cpp +133 -0
  109. data/ext/libsass/source_map.hpp +46 -0
  110. data/ext/libsass/subset_map.hpp +145 -0
  111. data/ext/libsass/support/libsass.pc.in +11 -0
  112. data/ext/libsass/test-driver +127 -0
  113. data/ext/libsass/test/test_node.cpp +98 -0
  114. data/ext/libsass/test/test_paths.cpp +29 -0
  115. data/ext/libsass/test/test_selector_difference.cpp +28 -0
  116. data/ext/libsass/test/test_specificity.cpp +28 -0
  117. data/ext/libsass/test/test_subset_map.cpp +472 -0
  118. data/ext/libsass/test/test_superselector.cpp +71 -0
  119. data/ext/libsass/test/test_unification.cpp +33 -0
  120. data/ext/libsass/to_c.cpp +61 -0
  121. data/ext/libsass/to_c.hpp +44 -0
  122. data/ext/libsass/to_string.cpp +29 -0
  123. data/ext/libsass/to_string.hpp +32 -0
  124. data/ext/libsass/token.hpp +32 -0
  125. data/ext/libsass/units.cpp +54 -0
  126. data/ext/libsass/units.hpp +10 -0
  127. data/ext/libsass/utf8.h +34 -0
  128. data/ext/libsass/utf8/checked.h +327 -0
  129. data/ext/libsass/utf8/core.h +329 -0
  130. data/ext/libsass/utf8/unchecked.h +228 -0
  131. data/ext/libsass/utf8_string.cpp +102 -0
  132. data/ext/libsass/utf8_string.hpp +36 -0
  133. data/ext/libsass/util.cpp +189 -0
  134. data/ext/libsass/util.hpp +26 -0
  135. data/ext/libsass/win/libsass.filters +291 -0
  136. data/ext/libsass/win/libsass.sln +28 -0
  137. data/ext/libsass/win/libsass.vcxproj +255 -0
  138. data/lib/sassc.rb +6 -0
  139. data/lib/sassc/engine.rb +13 -0
  140. data/lib/sassc/native.rb +44 -0
  141. data/lib/sassc/native/native_context_api.rb +140 -0
  142. data/lib/sassc/native/native_functions_api.rb +41 -0
  143. data/lib/sassc/native/sass_input_style.rb +11 -0
  144. data/lib/sassc/native/sass_output_style.rb +10 -0
  145. data/lib/sassc/native/sass_value.rb +95 -0
  146. data/lib/sassc/native/string_list.rb +8 -0
  147. data/lib/sassc/version.rb +3 -0
  148. data/sassc.gemspec +43 -0
  149. data/test/smoke_test.rb +171 -0
  150. data/test/test_helper.rb +4 -0
  151. metadata +281 -0
@@ -0,0 +1,108 @@
1
+ #include <string>
2
+
3
+ #ifndef SASS_OPERATION
4
+ #include "operation.hpp"
5
+ #endif
6
+
7
+ // #ifndef SASS_TO_STRING
8
+ // #include "to_string.hpp"
9
+ // #endif
10
+
11
+ namespace Sass {
12
+ using namespace std;
13
+ struct Context;
14
+
15
+ class Output_Nested : public Operation_CRTP<void, Output_Nested> {
16
+ // import all the class-specific methods and override as desired
17
+ using Operation_CRTP<void, Output_Nested>::operator();
18
+
19
+ string buffer;
20
+ string rendered_imports;
21
+ size_t indentation;
22
+ bool source_comments;
23
+ Context* ctx;
24
+ bool seen_utf8;
25
+ void indent();
26
+
27
+ void fallback_impl(AST_Node* n);
28
+
29
+ void append_to_buffer(const string& text);
30
+
31
+ public:
32
+
33
+ Output_Nested(bool source_comments = false, Context* ctx = 0);
34
+ virtual ~Output_Nested();
35
+
36
+ string get_buffer() {
37
+ if (!rendered_imports.empty() && !buffer.empty()) {
38
+ rendered_imports += "\n";
39
+ }
40
+ return (seen_utf8 ? "@charset \"UTF-8\";\n" : "")
41
+ + rendered_imports + buffer;
42
+ }
43
+
44
+ // statements
45
+ virtual void operator()(Block*);
46
+ virtual void operator()(Ruleset*);
47
+ // virtual void operator()(Propset*);
48
+ virtual void operator()(Feature_Block*);
49
+ virtual void operator()(Media_Block*);
50
+ virtual void operator()(At_Rule*);
51
+ // virtual void operator()(Declaration*);
52
+ // virtual void operator()(Assignment*);
53
+ virtual void operator()(Import*);
54
+ // virtual void operator()(Import_Stub*);
55
+ // virtual void operator()(Warning*);
56
+ // virtual void operator()(Error*);
57
+ // virtual void operator()(Debug*);
58
+ // virtual void operator()(Comment*);
59
+ // virtual void operator()(If*);
60
+ // virtual void operator()(For*);
61
+ // virtual void operator()(Each*);
62
+ // virtual void operator()(While*);
63
+ // virtual void operator()(Return*);
64
+ // virtual void operator()(Extension*);
65
+ // virtual void operator()(Definition*);
66
+ // virtual void operator()(Mixin_Call*);
67
+ // virtual void operator()(Content*);
68
+ // // expressions
69
+ // virtual void operator()(List*);
70
+ // virtual void operator()(Binary_Expression*);
71
+ // virtual void operator()(Unary_Expression*);
72
+ // virtual void operator()(Function_Call*);
73
+ // virtual void operator()(Function_Call_Schema*);
74
+ // virtual void operator()(Variable*);
75
+ // virtual void operator()(Textual*);
76
+ // virtual void operator()(Number*);
77
+ // virtual void operator()(Color*);
78
+ // virtual void operator()(Boolean*);
79
+ // virtual void operator()(String_Schema*);
80
+ // virtual void operator()(String_Constant* x);
81
+ // virtual void operator()(Media_Query*);
82
+ // virtual void operator()(Media_Query_Expression*);
83
+ // // parameters and arguments
84
+ // virtual void operator()(Parameter*);
85
+ // virtual void operator()(Parameters*);
86
+ // virtual void operator()(Argument*);
87
+ // virtual void operator()(Arguments*);
88
+ // // selectors
89
+ // virtual void operator()(Selector_Schema*);
90
+ // virtual void operator()(Selector_Reference*);
91
+ // virtual void operator()(Selector_Placeholder*);
92
+ // virtual void operator()(Type_Selector*);
93
+ // virtual void operator()(Selector_Qualifier*);
94
+ // virtual void operator()(Attribute_Selector*);
95
+ // virtual void operator()(Pseudo_Selector*);
96
+ // virtual void operator()(Wrapped_Selector*);
97
+ // virtual void operator()(Compound_Selector*);
98
+ // virtual void operator()(Complex_Selector*);
99
+ // virtual void operator()(Selector_List*);
100
+
101
+ template <typename U>
102
+ void fallback(U x) { fallback_impl(x); }
103
+ };
104
+
105
+ string unquote(const string&);
106
+ string quote(const string&, char);
107
+
108
+ }
@@ -0,0 +1,2016 @@
1
+ #include <cstdlib>
2
+ #include <iostream>
3
+ #include <vector>
4
+ #include "parser.hpp"
5
+ #include "file.hpp"
6
+ #include "inspect.hpp"
7
+ #include "to_string.hpp"
8
+ #include "constants.hpp"
9
+ #include "util.hpp"
10
+
11
+ #ifndef SASS_PRELEXER
12
+ #include "prelexer.hpp"
13
+ #endif
14
+
15
+ #include "sass_functions.h"
16
+
17
+ #include <typeinfo>
18
+
19
+ namespace Sass {
20
+ using namespace std;
21
+ using namespace Constants;
22
+
23
+ Parser Parser::from_c_str(const char* str, Context& ctx, string path, Position source_position)
24
+ {
25
+ Parser p(ctx, path, source_position);
26
+ p.source = str;
27
+ p.position = p.source;
28
+ p.end = str + strlen(str);
29
+ return p;
30
+ }
31
+
32
+ Parser Parser::from_token(Token t, Context& ctx, string path, Position source_position)
33
+ {
34
+ Parser p(ctx, path, source_position);
35
+ p.source = t.begin;
36
+ p.position = p.source;
37
+ p.end = t.end;
38
+ p.dequote = true;
39
+ return p;
40
+ }
41
+
42
+ Block* Parser::parse()
43
+ {
44
+ Block* root = new (ctx.mem) Block(path, source_position);
45
+ root->is_root(true);
46
+ read_bom();
47
+ lex< optional_spaces >();
48
+ Selector_Lookahead lookahead_result;
49
+ while (position < end) {
50
+ if (lex< block_comment >()) {
51
+ String* contents = parse_interpolated_chunk(lexed);
52
+ Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
53
+ (*root) << comment;
54
+ }
55
+ else if (peek< import >()) {
56
+ Import* imp = parse_import();
57
+ if (!imp->urls().empty()) (*root) << imp;
58
+ if (!imp->files().empty()) {
59
+ for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
60
+ (*root) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]);
61
+ }
62
+ }
63
+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @import directive must be terminated by ';'");
64
+ }
65
+ else if (peek< mixin >() || peek< function >()) {
66
+ (*root) << parse_definition();
67
+ }
68
+ else if (peek< variable >()) {
69
+ (*root) << parse_assignment();
70
+ if (!lex< one_plus< exactly<';'> > >()) error("top-level variable binding must be terminated by ';'");
71
+ }
72
+ else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
73
+ (*root) << parse_propset();
74
+ }
75
+ else if (peek< include >() /* || peek< exactly<'+'> >() */) {
76
+ Mixin_Call* mixin_call = parse_mixin_call();
77
+ (*root) << mixin_call;
78
+ if (!mixin_call->block() && !lex< one_plus< exactly<';'> > >()) error("top-level @include directive must be terminated by ';'");
79
+ }
80
+ else if (peek< if_directive >()) {
81
+ (*root) << parse_if_directive();
82
+ }
83
+ else if (peek< for_directive >()) {
84
+ (*root) << parse_for_directive();
85
+ }
86
+ else if (peek< each_directive >()) {
87
+ (*root) << parse_each_directive();
88
+ }
89
+ else if (peek< while_directive >()) {
90
+ (*root) << parse_while_directive();
91
+ }
92
+ else if (peek< media >()) {
93
+ (*root) << parse_media_block();
94
+ }
95
+ else if (peek< supports >()) {
96
+ (*root) << parse_feature_block();
97
+ }
98
+ else if (peek< warn >()) {
99
+ (*root) << parse_warning();
100
+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @warn directive must be terminated by ';'");
101
+ }
102
+ else if (peek< err >()) {
103
+ (*root) << parse_error();
104
+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @error directive must be terminated by ';'");
105
+ }
106
+ else if (peek< dbg >()) {
107
+ (*root) << parse_debug();
108
+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @debug directive must be terminated by ';'");
109
+ }
110
+ // ignore the @charset directive for now
111
+ else if (lex< exactly< charset_kwd > >()) {
112
+ lex< string_constant >();
113
+ lex< one_plus< exactly<';'> > >();
114
+ }
115
+ else if (peek< at_keyword >()) {
116
+ At_Rule* at_rule = parse_at_rule();
117
+ (*root) << at_rule;
118
+ if (!at_rule->block() && !lex< one_plus< exactly<';'> > >()) error("top-level directive must be terminated by ';'");
119
+ }
120
+ else if ((lookahead_result = lookahead_for_selector(position)).found) {
121
+ (*root) << parse_ruleset(lookahead_result);
122
+ }
123
+ else if (peek< exactly<';'> >()) {
124
+ lex< one_plus< exactly<';'> > >();
125
+ }
126
+ else {
127
+ lex< spaces_and_comments >();
128
+ if (position >= end) break;
129
+ error("invalid top-level expression");
130
+ }
131
+ lex< optional_spaces >();
132
+ }
133
+ return root;
134
+ }
135
+
136
+ void Parser::add_single_file (Import* imp, string import_path) {
137
+
138
+ string extension;
139
+ string unquoted(unquote(import_path));
140
+ if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css
141
+ // a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end
142
+ extension = unquoted.substr(unquoted.length() - 4, 4);
143
+ }
144
+
145
+ if (extension == ".css") {
146
+ String_Constant* loc = new (ctx.mem) String_Constant(path, source_position, import_path, true);
147
+ Argument* loc_arg = new (ctx.mem) Argument(path, source_position, loc);
148
+ Arguments* loc_args = new (ctx.mem) Arguments(path, source_position);
149
+ (*loc_args) << loc_arg;
150
+ Function_Call* new_url = new (ctx.mem) Function_Call(path, source_position, "url", loc_args);
151
+ imp->urls().push_back(new_url);
152
+ }
153
+ else {
154
+ string current_dir = File::dir_name(path);
155
+ string resolved(ctx.add_file(current_dir, unquoted));
156
+ if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir);
157
+ imp->files().push_back(resolved);
158
+ }
159
+
160
+ }
161
+
162
+ Import* Parser::parse_import()
163
+ {
164
+ lex< import >();
165
+ Import* imp = new (ctx.mem) Import(path, source_position);
166
+ bool first = true;
167
+ do {
168
+ if (lex< string_constant >()) {
169
+ string import_path(lexed);
170
+
171
+ // struct Sass_Options opt = sass_context_get_options(ctx)
172
+ Sass_C_Import_Callback importer = ctx.importer;
173
+ // custom importer
174
+ if (importer) {
175
+ Sass_Import* current = ctx.import_stack.back();
176
+ Sass_C_Import_Fn fn = sass_import_get_function(importer);
177
+ void* cookie = sass_import_get_cookie(importer);
178
+ // create a new import entry
179
+ string inc_path = unquote(import_path);
180
+ struct Sass_Import** includes = fn(
181
+ inc_path.c_str(),
182
+ sass_import_get_path(current),
183
+ cookie);
184
+ if (includes) {
185
+ struct Sass_Import** list = includes;
186
+ while (*includes) {
187
+ struct Sass_Import* include = *includes;
188
+ const char *file = sass_import_get_path(include);
189
+ char *source = sass_import_take_source(include);
190
+ // char *srcmap = sass_import_take_srcmap(include);
191
+ if (source) {
192
+ if (file) {
193
+ ctx.add_source(file, inc_path, source);
194
+ imp->files().push_back(file);
195
+ } else {
196
+ ctx.add_source(inc_path, inc_path, source);
197
+ imp->files().push_back(inc_path);
198
+ }
199
+ } else if(file) {
200
+ add_single_file(imp, file);
201
+ }
202
+ ++includes;
203
+ }
204
+ // deallocate returned memory
205
+ sass_delete_import_list(list);
206
+ // parse next import
207
+ continue;
208
+ }
209
+ }
210
+
211
+ add_single_file(imp, import_path);
212
+
213
+ }
214
+ else if (peek< uri_prefix >()) {
215
+ imp->urls().push_back(parse_value());
216
+ }
217
+ else {
218
+ if (first) error("@import directive requires a url or quoted path");
219
+ else error("expecting another url or quoted path in @import list");
220
+ }
221
+ first = false;
222
+ } while (lex< exactly<','> >());
223
+ return imp;
224
+ }
225
+
226
+ Definition* Parser::parse_definition()
227
+ {
228
+ Definition::Type which_type = Definition::MIXIN;
229
+ if (lex< mixin >()) which_type = Definition::MIXIN;
230
+ else if (lex< function >()) which_type = Definition::FUNCTION;
231
+ string which_str(lexed);
232
+ if (!lex< identifier >()) error("invalid name in " + which_str + " definition");
233
+ string name(Util::normalize_underscores(lexed));
234
+ if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))
235
+ { error("Invalid function name \"" + name + "\"."); }
236
+ Position source_position_of_def = source_position;
237
+ Parameters* params = parse_parameters();
238
+ if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'");
239
+ if (which_type == Definition::MIXIN) stack.push_back(mixin_def);
240
+ else stack.push_back(function_def);
241
+ Block* body = parse_block();
242
+ stack.pop_back();
243
+ Definition* def = new (ctx.mem) Definition(path, source_position_of_def, name, params, body, which_type);
244
+ return def;
245
+ }
246
+
247
+ Parameters* Parser::parse_parameters()
248
+ {
249
+ string name(lexed); // for the error message
250
+ Parameters* params = new (ctx.mem) Parameters(path, source_position);
251
+ if (lex< exactly<'('> >()) {
252
+ // if there's anything there at all
253
+ if (!peek< exactly<')'> >()) {
254
+ do (*params) << parse_parameter();
255
+ while (lex< exactly<','> >());
256
+ }
257
+ if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name);
258
+ }
259
+ return params;
260
+ }
261
+
262
+ Parameter* Parser::parse_parameter()
263
+ {
264
+ lex< variable >();
265
+ string name(Util::normalize_underscores(lexed));
266
+ Position pos = source_position;
267
+ Expression* val = 0;
268
+ bool is_rest = false;
269
+ if (lex< exactly<':'> >()) { // there's a default value
270
+ val = parse_space_list();
271
+ val->is_delayed(false);
272
+ }
273
+ else if (lex< exactly< ellipsis > >()) {
274
+ is_rest = true;
275
+ }
276
+ Parameter* p = new (ctx.mem) Parameter(path, pos, name, val, is_rest);
277
+ return p;
278
+ }
279
+
280
+ Mixin_Call* Parser::parse_mixin_call()
281
+ {
282
+ lex< include >() /* || lex< exactly<'+'> >() */;
283
+ if (!lex< identifier >()) error("invalid name in @include directive");
284
+ Position source_position_of_call = source_position;
285
+ string name(Util::normalize_underscores(lexed));
286
+ Arguments* args = parse_arguments();
287
+ Block* content = 0;
288
+ if (peek< exactly<'{'> >()) {
289
+ content = parse_block();
290
+ }
291
+ Mixin_Call* the_call = new (ctx.mem) Mixin_Call(path, source_position_of_call, name, args, content);
292
+ return the_call;
293
+ }
294
+
295
+ Arguments* Parser::parse_arguments()
296
+ {
297
+ string name(lexed);
298
+ Arguments* args = new (ctx.mem) Arguments(path, source_position);
299
+
300
+ if (lex< exactly<'('> >()) {
301
+ // if there's anything there at all
302
+ if (!peek< exactly<')'> >()) {
303
+ do (*args) << parse_argument();
304
+ while (lex< exactly<','> >());
305
+ }
306
+ if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name);
307
+ }
308
+
309
+ return args;
310
+ }
311
+
312
+ Argument* Parser::parse_argument()
313
+ {
314
+ Argument* arg;
315
+ if (peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
316
+ lex< variable >();
317
+ string name(Util::normalize_underscores(lexed));
318
+ Position p = source_position;
319
+ lex< exactly<':'> >();
320
+ Expression* val = parse_space_list();
321
+ val->is_delayed(false);
322
+ arg = new (ctx.mem) Argument(path, p, val, name);
323
+ }
324
+ else {
325
+ bool is_arglist = false;
326
+ bool is_keyword = false;
327
+ Expression* val = parse_space_list();
328
+ val->is_delayed(false);
329
+ if (lex< exactly< ellipsis > >()) {
330
+ if (val->concrete_type() == Expression::MAP) is_keyword = true;
331
+ else is_arglist = true;
332
+ }
333
+ arg = new (ctx.mem) Argument(path, source_position, val, "", is_arglist, is_keyword);
334
+ }
335
+ return arg;
336
+ }
337
+
338
+ Assignment* Parser::parse_assignment()
339
+ {
340
+ lex< variable >();
341
+ string name(Util::normalize_underscores(lexed));
342
+ Position var_source_position = source_position;
343
+ if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement");
344
+ Expression* val = parse_list();
345
+ val->is_delayed(false);
346
+ bool is_guarded = false;
347
+ bool is_global = false;
348
+ while (peek< default_flag >() || peek< global_flag >()) {
349
+ is_guarded = lex< default_flag >() || is_guarded;
350
+ is_global = lex< global_flag >() || is_global;
351
+ }
352
+ Assignment* var = new (ctx.mem) Assignment(path, var_source_position, name, val, is_guarded, is_global);
353
+ return var;
354
+ }
355
+
356
+ Propset* Parser::parse_propset()
357
+ {
358
+ String* property_segment;
359
+ if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
360
+ property_segment = parse_identifier_schema();
361
+ }
362
+ else {
363
+ lex< sequence< optional< exactly<'*'> >, identifier > >();
364
+ property_segment = new (ctx.mem) String_Constant(path, source_position, lexed);
365
+ }
366
+ Propset* propset = new (ctx.mem) Propset(path, source_position, property_segment);
367
+ lex< exactly<':'> >();
368
+
369
+ if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property");
370
+
371
+ propset->block(parse_block());
372
+
373
+ return propset;
374
+ }
375
+
376
+ Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead)
377
+ {
378
+ Selector* sel;
379
+ if (lookahead.has_interpolants) {
380
+ sel = parse_selector_schema(lookahead.found);
381
+ }
382
+ else {
383
+ sel = parse_selector_group();
384
+ }
385
+ Position r_source_position = source_position;
386
+ if (!peek< exactly<'{'> >()) error("expected a '{' after the selector");
387
+ Block* block = parse_block();
388
+ Ruleset* ruleset = new (ctx.mem) Ruleset(path, r_source_position, sel, block);
389
+ return ruleset;
390
+ }
391
+
392
+ Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)
393
+ {
394
+ lex< optional_spaces >();
395
+ const char* i = position;
396
+ const char* p;
397
+ String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
398
+
399
+ while (i < end_of_selector) {
400
+ p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector);
401
+ if (p) {
402
+ // accumulate the preceding segment if there is one
403
+ if (i < p) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p));
404
+ // find the end of the interpolant and parse it
405
+ const char* j = find_first_in_interval< exactly<rbrace> >(p, end_of_selector);
406
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
407
+ interp_node->is_interpolant(true);
408
+ (*schema) << interp_node;
409
+ i = j + 1;
410
+ }
411
+ else { // no interpolants left; add the last segment if there is one
412
+ if (i < end_of_selector) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, end_of_selector));
413
+ break;
414
+ }
415
+ }
416
+ position = end_of_selector;
417
+ return new (ctx.mem) Selector_Schema(path, source_position, schema);
418
+ }
419
+
420
+ Selector_List* Parser::parse_selector_group()
421
+ {
422
+ To_String to_string;
423
+ lex< spaces_and_comments >();
424
+ Selector_List* group = new (ctx.mem) Selector_List(path, source_position);
425
+ do {
426
+ if (peek< exactly<'{'> >() ||
427
+ peek< exactly<'}'> >() ||
428
+ peek< exactly<')'> >() ||
429
+ peek< exactly<';'> >())
430
+ break; // in case there are superfluous commas at the end
431
+ Complex_Selector* comb = parse_selector_combination();
432
+ if (!comb->has_reference()) {
433
+ Position sel_source_position = source_position;
434
+ Selector_Reference* ref = new (ctx.mem) Selector_Reference(path, sel_source_position);
435
+ Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(path, sel_source_position);
436
+ (*ref_wrap) << ref;
437
+ if (!comb->head()) {
438
+ comb->head(ref_wrap);
439
+ comb->has_reference(true);
440
+ }
441
+ else {
442
+ comb = new (ctx.mem) Complex_Selector(path, sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb);
443
+ comb->has_reference(true);
444
+ }
445
+ }
446
+ (*group) << comb;
447
+ }
448
+ while (lex< one_plus< sequence< spaces_and_comments, exactly<','> > > >());
449
+ while (lex< optional >()); // JMA - ignore optional flag if it follows the selector group
450
+ return group;
451
+ }
452
+
453
+ Complex_Selector* Parser::parse_selector_combination()
454
+ {
455
+ Position sel_source_position = Position();
456
+ Compound_Selector* lhs;
457
+ if (peek< exactly<'+'> >() ||
458
+ peek< exactly<'~'> >() ||
459
+ peek< exactly<'>'> >()) {
460
+ // no selector before the combinator
461
+ lhs = 0;
462
+ }
463
+ else {
464
+ lhs = parse_simple_selector_sequence();
465
+ sel_source_position = source_position;
466
+ }
467
+
468
+ Complex_Selector::Combinator cmb;
469
+ if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO;
470
+ else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES;
471
+ else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF;
472
+ else cmb = Complex_Selector::ANCESTOR_OF;
473
+
474
+ Complex_Selector* rhs;
475
+ if (peek< exactly<','> >() ||
476
+ peek< exactly<')'> >() ||
477
+ peek< exactly<'{'> >() ||
478
+ peek< exactly<'}'> >() ||
479
+ peek< exactly<';'> >() ||
480
+ peek< optional >()) {
481
+ // no selector after the combinator
482
+ rhs = 0;
483
+ }
484
+ else {
485
+ rhs = parse_selector_combination();
486
+ sel_source_position = source_position;
487
+ }
488
+ if (!sel_source_position.line) sel_source_position = source_position;
489
+ return new (ctx.mem) Complex_Selector(path, sel_source_position, cmb, lhs, rhs);
490
+ }
491
+
492
+ Compound_Selector* Parser::parse_simple_selector_sequence()
493
+ {
494
+ Compound_Selector* seq = new (ctx.mem) Compound_Selector(path, source_position);
495
+ bool sawsomething = false;
496
+ if (lex< exactly<'&'> >()) {
497
+ // if you see a &
498
+ (*seq) << new (ctx.mem) Selector_Reference(path, source_position);
499
+ sawsomething = true;
500
+ // if you see a space after a &, then you're done
501
+ if(lex< spaces >()) {
502
+ return seq;
503
+ }
504
+ }
505
+ if (sawsomething && lex< sequence< negate< functional >, alternatives< identifier_fragment, universal, string_constant, dimension, percentage, number > > >()) {
506
+ // saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning
507
+ (*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed);
508
+ } else if (lex< sequence< negate< functional >, alternatives< type_selector, universal, string_constant, dimension, percentage, number > > >()) {
509
+ // if you see a type selector
510
+ (*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed);
511
+ sawsomething = true;
512
+ }
513
+ if (!sawsomething) {
514
+ // don't blindly do this if you saw a & or selector
515
+ (*seq) << parse_simple_selector();
516
+ }
517
+
518
+ while (!peek< spaces >(position) &&
519
+ !(peek < exactly<'+'> >(position) ||
520
+ peek < exactly<'~'> >(position) ||
521
+ peek < exactly<'>'> >(position) ||
522
+ peek < exactly<','> >(position) ||
523
+ peek < exactly<')'> >(position) ||
524
+ peek < exactly<'{'> >(position) ||
525
+ peek < exactly<'}'> >(position) ||
526
+ peek < exactly<';'> >(position))) {
527
+ (*seq) << parse_simple_selector();
528
+ }
529
+ return seq;
530
+ }
531
+
532
+ Simple_Selector* Parser::parse_simple_selector()
533
+ {
534
+ if (lex< id_name >() || lex< class_name >()) {
535
+ return new (ctx.mem) Selector_Qualifier(path, source_position, lexed);
536
+ }
537
+ else if (lex< string_constant >() || lex< number >()) {
538
+ return new (ctx.mem) Type_Selector(path, source_position, lexed);
539
+ }
540
+ else if (peek< pseudo_not >()) {
541
+ return parse_negated_selector();
542
+ }
543
+ else if (peek< exactly<':'> >(position) || peek< functional >()) {
544
+ return parse_pseudo_selector();
545
+ }
546
+ else if (peek< exactly<'['> >(position)) {
547
+ return parse_attribute_selector();
548
+ }
549
+ else if (lex< placeholder >()) {
550
+ return new (ctx.mem) Selector_Placeholder(path, source_position, lexed);
551
+ }
552
+ else {
553
+ error("invalid selector after " + lexed.to_string());
554
+ }
555
+ // unreachable statement
556
+ return 0;
557
+ }
558
+
559
+ Wrapped_Selector* Parser::parse_negated_selector()
560
+ {
561
+ lex< pseudo_not >();
562
+ string name(lexed);
563
+ Position nsource_position = source_position;
564
+ Selector* negated = parse_selector_group();
565
+ if (!lex< exactly<')'> >()) {
566
+ error("negated selector is missing ')'");
567
+ }
568
+ return new (ctx.mem) Wrapped_Selector(path, nsource_position, name, negated);
569
+ }
570
+
571
+ Simple_Selector* Parser::parse_pseudo_selector() {
572
+ if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) {
573
+ string name(lexed);
574
+ String* expr = 0;
575
+ Position p = source_position;
576
+ Selector* wrapped = 0;
577
+ if (lex< alternatives< even, odd > >()) {
578
+ expr = new (ctx.mem) String_Constant(path, p, lexed);
579
+ }
580
+ else if (peek< binomial >(position)) {
581
+ lex< sequence< optional< coefficient >, exactly<'n'> > >();
582
+ String_Constant* var_coef = new (ctx.mem) String_Constant(path, p, lexed);
583
+ lex< sign >();
584
+ String_Constant* op = new (ctx.mem) String_Constant(path, p, lexed);
585
+ // Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
586
+ lex< digits >();
587
+ String_Constant* constant = new (ctx.mem) String_Constant(path, p, lexed);
588
+ // expr = new (ctx.mem) Binary_Expression(path, p, op, var_coef, constant);
589
+ String_Schema* schema = new (ctx.mem) String_Schema(path, p, 3);
590
+ *schema << var_coef << op << constant;
591
+ expr = schema;
592
+ }
593
+ else if (peek< sequence< optional<sign>,
594
+ optional<digits>,
595
+ exactly<'n'>,
596
+ spaces_and_comments,
597
+ exactly<')'> > >()) {
598
+ lex< sequence< optional<sign>,
599
+ optional<digits>,
600
+ exactly<'n'> > >();
601
+ expr = new (ctx.mem) String_Constant(path, p, lexed);
602
+ }
603
+ else if (lex< sequence< optional<sign>, digits > >()) {
604
+ expr = new (ctx.mem) String_Constant(path, p, lexed);
605
+ }
606
+ else if (peek< sequence< identifier, spaces_and_comments, exactly<')'> > >()) {
607
+ lex< identifier >();
608
+ expr = new (ctx.mem) String_Constant(path, p, lexed);
609
+ }
610
+ else if (lex< string_constant >()) {
611
+ expr = new (ctx.mem) String_Constant(path, p, lexed);
612
+ }
613
+ else if (peek< exactly<')'> >()) {
614
+ expr = new (ctx.mem) String_Constant(path, p, "");
615
+ }
616
+ else {
617
+ wrapped = parse_selector_group();
618
+ }
619
+ if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)");
620
+ if (wrapped) {
621
+ return new (ctx.mem) Wrapped_Selector(path, p, name, wrapped);
622
+ }
623
+ return new (ctx.mem) Pseudo_Selector(path, p, name, expr);
624
+ }
625
+ else if (lex < sequence< pseudo_prefix, identifier > >()) {
626
+ return new (ctx.mem) Pseudo_Selector(path, source_position, lexed);
627
+ }
628
+ else {
629
+ error("unrecognized pseudo-class or pseudo-element");
630
+ }
631
+ // unreachable statement
632
+ return 0;
633
+ }
634
+
635
+ Attribute_Selector* Parser::parse_attribute_selector()
636
+ {
637
+ lex< exactly<'['> >();
638
+ Position p = source_position;
639
+ if (!lex< attribute_name >()) error("invalid attribute name in attribute selector");
640
+ string name(lexed);
641
+ if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(path, p, name, "", 0);
642
+ if (!lex< alternatives< exact_match, class_match, dash_match,
643
+ prefix_match, suffix_match, substring_match > >()) {
644
+ error("invalid operator in attribute selector for " + name);
645
+ }
646
+ string matcher(lexed);
647
+
648
+ String* value = 0;
649
+ if (lex< identifier >()) {
650
+ value = new (ctx.mem) String_Constant(path, p, lexed, true);
651
+ }
652
+ else if (lex< string_constant >()) {
653
+ value = parse_interpolated_chunk(lexed);
654
+ }
655
+ else {
656
+ error("expected a string constant or identifier in attribute selector for " + name);
657
+ }
658
+
659
+ if (!lex< exactly<']'> >()) error("unterminated attribute selector for " + name);
660
+ return new (ctx.mem) Attribute_Selector(path, p, name, matcher, value);
661
+ }
662
+
663
+ Block* Parser::parse_block()
664
+ {
665
+ lex< exactly<'{'> >();
666
+ bool semicolon = false;
667
+ Selector_Lookahead lookahead_result;
668
+ Block* block = new (ctx.mem) Block(path, source_position);
669
+
670
+ // JMA - ensure that a block containing only block_comments is parsed
671
+ while (lex< block_comment >()) {
672
+ String* contents = parse_interpolated_chunk(lexed);
673
+ Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
674
+ (*block) << comment;
675
+ }
676
+
677
+ while (!lex< exactly<'}'> >()) {
678
+ if (semicolon) {
679
+ if (!lex< one_plus< exactly<';'> > >()) {
680
+ error("non-terminal statement or declaration must end with ';'");
681
+ }
682
+ semicolon = false;
683
+ while (lex< block_comment >()) {
684
+ String* contents = parse_interpolated_chunk(lexed);
685
+ Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
686
+ (*block) << comment;
687
+ }
688
+ if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break;
689
+ }
690
+ if (lex< block_comment >()) {
691
+ String* contents = parse_interpolated_chunk(lexed);
692
+ Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
693
+ (*block) << comment;
694
+ }
695
+ else if (peek< import >(position)) {
696
+ if (stack.back() == mixin_def || stack.back() == function_def) {
697
+ lex< import >(); // to adjust the source_position number
698
+ error("@import directives are not allowed inside mixins and functions");
699
+ }
700
+ Import* imp = parse_import();
701
+ if (!imp->urls().empty()) (*block) << imp;
702
+ if (!imp->files().empty()) {
703
+ for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
704
+ (*block) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]);
705
+ }
706
+ }
707
+ semicolon = true;
708
+ }
709
+ else if (lex< variable >()) {
710
+ (*block) << parse_assignment();
711
+ semicolon = true;
712
+ }
713
+ else if (peek< if_directive >()) {
714
+ (*block) << parse_if_directive();
715
+ }
716
+ else if (peek< for_directive >()) {
717
+ (*block) << parse_for_directive();
718
+ }
719
+ else if (peek< each_directive >()) {
720
+ (*block) << parse_each_directive();
721
+ }
722
+ else if (peek < while_directive >()) {
723
+ (*block) << parse_while_directive();
724
+ }
725
+ else if (lex < return_directive >()) {
726
+ (*block) << new (ctx.mem) Return(path, source_position, parse_list());
727
+ semicolon = true;
728
+ }
729
+ else if (peek< warn >()) {
730
+ (*block) << parse_warning();
731
+ semicolon = true;
732
+ }
733
+ else if (peek< err >()) {
734
+ (*block) << parse_error();
735
+ semicolon = true;
736
+ }
737
+ else if (peek< dbg >()) {
738
+ (*block) << parse_debug();
739
+ semicolon = true;
740
+ }
741
+ else if (stack.back() == function_def) {
742
+ error("only variable declarations and control directives are allowed inside functions");
743
+ }
744
+ else if (peek< mixin >() || peek< function >()) {
745
+ (*block) << parse_definition();
746
+ }
747
+ else if (peek< include >(position)) {
748
+ Mixin_Call* the_call = parse_mixin_call();
749
+ (*block) << the_call;
750
+ // don't need a semicolon after a content block
751
+ semicolon = (the_call->block()) ? false : true;
752
+ }
753
+ else if (lex< content >()) {
754
+ if (stack.back() != mixin_def) {
755
+ error("@content may only be used within a mixin");
756
+ }
757
+ (*block) << new (ctx.mem) Content(path, source_position);
758
+ semicolon = true;
759
+ }
760
+ /*
761
+ else if (peek< exactly<'+'> >()) {
762
+ (*block) << parse_mixin_call();
763
+ semicolon = true;
764
+ }
765
+ */
766
+ else if (lex< extend >()) {
767
+ Selector_Lookahead lookahead = lookahead_for_extension_target(position);
768
+ if (!lookahead.found) error("invalid selector for @extend");
769
+ Selector* target;
770
+ if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);
771
+ else target = parse_selector_group();
772
+ (*block) << new (ctx.mem) Extension(path, source_position, target);
773
+ semicolon = true;
774
+ }
775
+ else if (peek< media >()) {
776
+ (*block) << parse_media_block();
777
+ }
778
+ else if (peek< supports >()) {
779
+ (*block) << parse_feature_block();
780
+ }
781
+ // ignore the @charset directive for now
782
+ else if (lex< exactly< charset_kwd > >()) {
783
+ lex< string_constant >();
784
+ lex< one_plus< exactly<';'> > >();
785
+ }
786
+ else if (peek< at_keyword >()) {
787
+ At_Rule* at_rule = parse_at_rule();
788
+ (*block) << at_rule;
789
+ if (!at_rule->block()) semicolon = true;
790
+ }
791
+ else if ((lookahead_result = lookahead_for_selector(position)).found) {
792
+ (*block) << parse_ruleset(lookahead_result);
793
+ }
794
+ else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
795
+ (*block) << parse_propset();
796
+ }
797
+ else if (!peek< exactly<';'> >()) {
798
+ if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) {
799
+ (*block) << parse_propset();
800
+ }
801
+ else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) {
802
+ (*block) << parse_propset();
803
+ }
804
+ else {
805
+ Declaration* decl = parse_declaration();
806
+ (*block) << decl;
807
+ if (peek< exactly<'{'> >()) {
808
+ // parse a propset that rides on the declaration's property
809
+ Propset* ps = new (ctx.mem) Propset(path, source_position, decl->property(), parse_block());
810
+ (*block) << ps;
811
+ }
812
+ else {
813
+ // finish and let the semicolon get munched
814
+ semicolon = true;
815
+ }
816
+ }
817
+ }
818
+ else lex< one_plus< exactly<';'> > >();
819
+ while (lex< block_comment >()) {
820
+ String* contents = parse_interpolated_chunk(lexed);
821
+ Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
822
+ (*block) << comment;
823
+ }
824
+ }
825
+ return block;
826
+ }
827
+
828
+ Declaration* Parser::parse_declaration() {
829
+ String* prop = 0;
830
+ if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
831
+ prop = parse_identifier_schema();
832
+ }
833
+ else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
834
+ prop = new (ctx.mem) String_Constant(path, source_position, lexed);
835
+ }
836
+ else if (lex< custom_property_name >()) {
837
+ prop = new (ctx.mem) String_Constant(path, source_position, lexed);
838
+ }
839
+ else {
840
+ error("invalid property name");
841
+ }
842
+ if (!lex< one_plus< exactly<':'> > >()) error("property \"" + string(lexed) + "\" must be followed by a ':'");
843
+ if (peek< exactly<';'> >()) error("style declaration must contain a value");
844
+ if (peek< static_value >()) {
845
+ return new (ctx.mem) Declaration(path, prop->position(), prop, parse_static_value()/*, lex<important>()*/);
846
+ }
847
+ else {
848
+ return new (ctx.mem) Declaration(path, prop->position(), prop, parse_list()/*, lex<important>()*/);
849
+ }
850
+ }
851
+
852
+ Expression* Parser::parse_map()
853
+ {
854
+ To_String to_string;
855
+ Expression* key = parse_list();
856
+
857
+ // it's not a map so return the lexed value as a list value
858
+ if (!peek< exactly<':'> >())
859
+ { return key; }
860
+
861
+ lex< exactly<':'> >();
862
+
863
+ Expression* value = parse_space_list();
864
+
865
+ Map* map = new (ctx.mem) Map(path, source_position, 1);
866
+ (*map) << make_pair(key, value);
867
+
868
+ while (lex< exactly<','> >())
869
+ {
870
+ // allow trailing commas - #495
871
+ if (peek< exactly<')'> >(position))
872
+ { break; }
873
+
874
+ Expression* key = parse_list();
875
+
876
+ if (!(lex< exactly<':'> >()))
877
+ { error("invalid syntax"); }
878
+
879
+ Expression* value = parse_space_list();
880
+
881
+ (*map) << make_pair(key, value);
882
+ }
883
+
884
+ if (map->has_duplicate_key())
885
+ { error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + "."); }
886
+
887
+ return map;
888
+ }
889
+
890
+ Expression* Parser::parse_list()
891
+ {
892
+ return parse_comma_list();
893
+ }
894
+
895
+ Expression* Parser::parse_comma_list()
896
+ {
897
+ if (//peek< exactly<'!'> >(position) ||
898
+ peek< exactly<';'> >(position) ||
899
+ peek< exactly<'}'> >(position) ||
900
+ peek< exactly<'{'> >(position) ||
901
+ peek< exactly<')'> >(position) ||
902
+ //peek< exactly<':'> >(position) ||
903
+ peek< exactly<ellipsis> >(position))
904
+ { return new (ctx.mem) List(path, source_position, 0); }
905
+ Expression* list1 = parse_space_list();
906
+ // if it's a singleton, return it directly; don't wrap it
907
+ if (!peek< exactly<','> >(position)) return list1;
908
+
909
+ List* comma_list = new (ctx.mem) List(path, source_position, 2, List::COMMA);
910
+ (*comma_list) << list1;
911
+
912
+ while (lex< exactly<','> >())
913
+ {
914
+ if (//peek< exactly<'!'> >(position) ||
915
+ peek< exactly<';'> >(position) ||
916
+ peek< exactly<'}'> >(position) ||
917
+ peek< exactly<'{'> >(position) ||
918
+ peek< exactly<')'> >(position) ||
919
+ peek< exactly<':'> >(position) ||
920
+ peek< exactly<ellipsis> >(position)) {
921
+ break;
922
+ }
923
+ Expression* list = parse_space_list();
924
+ (*comma_list) << list;
925
+ }
926
+
927
+ return comma_list;
928
+ }
929
+
930
+ Expression* Parser::parse_space_list()
931
+ {
932
+ Expression* disj1 = parse_disjunction();
933
+ // if it's a singleton, return it directly; don't wrap it
934
+ if (//peek< exactly<'!'> >(position) ||
935
+ peek< exactly<';'> >(position) ||
936
+ peek< exactly<'}'> >(position) ||
937
+ peek< exactly<'{'> >(position) ||
938
+ peek< exactly<')'> >(position) ||
939
+ peek< exactly<','> >(position) ||
940
+ peek< exactly<':'> >(position) ||
941
+ peek< exactly<ellipsis> >(position) ||
942
+ peek< default_flag >(position) ||
943
+ peek< global_flag >(position))
944
+ { return disj1; }
945
+
946
+ List* space_list = new (ctx.mem) List(path, source_position, 2, List::SPACE);
947
+ (*space_list) << disj1;
948
+
949
+ while (!(//peek< exactly<'!'> >(position) ||
950
+ peek< exactly<';'> >(position) ||
951
+ peek< exactly<'}'> >(position) ||
952
+ peek< exactly<'{'> >(position) ||
953
+ peek< exactly<')'> >(position) ||
954
+ peek< exactly<','> >(position) ||
955
+ peek< exactly<':'> >(position) ||
956
+ peek< exactly<ellipsis> >(position) ||
957
+ peek< default_flag >(position) ||
958
+ peek< global_flag >(position)))
959
+ {
960
+ (*space_list) << parse_disjunction();
961
+ }
962
+
963
+ return space_list;
964
+ }
965
+
966
+ Expression* Parser::parse_disjunction()
967
+ {
968
+ Expression* conj1 = parse_conjunction();
969
+ // if it's a singleton, return it directly; don't wrap it
970
+ if (!peek< sequence< or_op, negate< identifier > > >()) return conj1;
971
+
972
+ vector<Expression*> operands;
973
+ while (lex< sequence< or_op, negate< identifier > > >())
974
+ operands.push_back(parse_conjunction());
975
+
976
+ return fold_operands(conj1, operands, Binary_Expression::OR);
977
+ }
978
+
979
+ Expression* Parser::parse_conjunction()
980
+ {
981
+ Expression* rel1 = parse_relation();
982
+ // if it's a singleton, return it directly; don't wrap it
983
+ if (!peek< sequence< and_op, negate< identifier > > >()) return rel1;
984
+
985
+ vector<Expression*> operands;
986
+ while (lex< sequence< and_op, negate< identifier > > >())
987
+ operands.push_back(parse_relation());
988
+
989
+ return fold_operands(rel1, operands, Binary_Expression::AND);
990
+ }
991
+
992
+ Expression* Parser::parse_relation()
993
+ {
994
+ Expression* expr1 = parse_expression();
995
+ // if it's a singleton, return it directly; don't wrap it
996
+ if (!(peek< eq_op >(position) ||
997
+ peek< neq_op >(position) ||
998
+ peek< gte_op >(position) ||
999
+ peek< gt_op >(position) ||
1000
+ peek< lte_op >(position) ||
1001
+ peek< lt_op >(position)))
1002
+ { return expr1; }
1003
+
1004
+ Binary_Expression::Type op
1005
+ = lex<eq_op>() ? Binary_Expression::EQ
1006
+ : lex<neq_op>() ? Binary_Expression::NEQ
1007
+ : lex<gte_op>() ? Binary_Expression::GTE
1008
+ : lex<lte_op>() ? Binary_Expression::LTE
1009
+ : lex<gt_op>() ? Binary_Expression::GT
1010
+ : lex<lt_op>() ? Binary_Expression::LT
1011
+ : Binary_Expression::LT; // whatever
1012
+
1013
+ Expression* expr2 = parse_expression();
1014
+
1015
+ return new (ctx.mem) Binary_Expression(path, expr1->position(), op, expr1, expr2);
1016
+ }
1017
+
1018
+ Expression* Parser::parse_expression()
1019
+ {
1020
+ Expression* term1 = parse_term();
1021
+ // if it's a singleton, return it directly; don't wrap it
1022
+ if (!(peek< exactly<'+'> >(position) ||
1023
+ peek< sequence< negate< number >, exactly<'-'> > >(position)) ||
1024
+ peek< identifier >(position))
1025
+ { return term1; }
1026
+
1027
+ vector<Expression*> operands;
1028
+ vector<Binary_Expression::Type> operators;
1029
+ while (lex< exactly<'+'> >() || lex< sequence< negate< number >, exactly<'-'> > >()) {
1030
+ operators.push_back(lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
1031
+ operands.push_back(parse_term());
1032
+ }
1033
+
1034
+ return fold_operands(term1, operands, operators);
1035
+ }
1036
+
1037
+ Expression* Parser::parse_term()
1038
+ {
1039
+ Expression* fact1 = parse_factor();
1040
+
1041
+ // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
1042
+ if (peek< exactly<'%'> >(position) && fact1->concrete_type() == Expression::STRING) {
1043
+ try {
1044
+ String_Schema* ss = dynamic_cast<String_Schema*>(fact1);
1045
+ if (ss->has_interpolants()) return fact1;
1046
+ }
1047
+ catch (bad_cast&) {}
1048
+ catch (...) { throw; }
1049
+ }
1050
+
1051
+ // if it's a singleton, return it directly; don't wrap it
1052
+ if (!(peek< exactly<'*'> >(position) ||
1053
+ peek< exactly<'/'> >(position) ||
1054
+ peek< exactly<'%'> >(position)))
1055
+ { return fact1; }
1056
+
1057
+ vector<Expression*> operands;
1058
+ vector<Binary_Expression::Type> operators;
1059
+ while (lex< exactly<'*'> >() || lex< exactly<'/'> >() || lex< exactly<'%'> >()) {
1060
+ if (lexed == "*") operators.push_back(Binary_Expression::MUL);
1061
+ else if (lexed == "/") operators.push_back(Binary_Expression::DIV);
1062
+ else operators.push_back(Binary_Expression::MOD);
1063
+ operands.push_back(parse_factor());
1064
+ }
1065
+
1066
+ return fold_operands(fact1, operands, operators);
1067
+ }
1068
+
1069
+ Expression* Parser::parse_factor()
1070
+ {
1071
+ if (lex< exactly<'('> >()) {
1072
+ Expression* value = parse_map();
1073
+ if (!lex< exactly<')'> >()) error("unclosed parenthesis");
1074
+ value->is_delayed(false);
1075
+ // make sure wrapped lists and division expressions are non-delayed within parentheses
1076
+ if (value->concrete_type() == Expression::LIST) {
1077
+ List* l = static_cast<List*>(value);
1078
+ if (!l->empty()) (*l)[0]->is_delayed(false);
1079
+ } else if (typeid(*value) == typeid(Binary_Expression)) {
1080
+ Binary_Expression* b = static_cast<Binary_Expression*>(value);
1081
+ Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());
1082
+ if (lhs && lhs->type() == Binary_Expression::DIV) lhs->is_delayed(false);
1083
+ }
1084
+ return value;
1085
+ }
1086
+ else if (peek< ie_property >()) {
1087
+ return parse_ie_property();
1088
+ }
1089
+ else if (peek< ie_keyword_arg >()) {
1090
+ return parse_ie_keyword_arg();
1091
+ }
1092
+ else if (peek< exactly< calc_kwd > >() ||
1093
+ peek< exactly< moz_calc_kwd > >() ||
1094
+ peek< exactly< webkit_calc_kwd > >()) {
1095
+ return parse_calc_function();
1096
+ }
1097
+ else if (peek< functional_schema >()) {
1098
+ return parse_function_call_schema();
1099
+ }
1100
+ else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {
1101
+ return parse_identifier_schema();
1102
+ }
1103
+ else if (peek< functional >() && !peek< uri_prefix >()) {
1104
+ return parse_function_call();
1105
+ }
1106
+ else if (lex< sequence< exactly<'+'>, spaces_and_comments, negate< number > > >()) {
1107
+ return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::PLUS, parse_factor());
1108
+ }
1109
+ else if (lex< sequence< exactly<'-'>, spaces_and_comments, negate< number> > >()) {
1110
+ return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::MINUS, parse_factor());
1111
+ }
1112
+ else if (lex< sequence< not_op, spaces_and_comments > >()) {
1113
+ return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::NOT, parse_factor());
1114
+ }
1115
+ else {
1116
+ return parse_value();
1117
+ }
1118
+ }
1119
+
1120
+ Expression* Parser::parse_value()
1121
+ {
1122
+ if (lex< uri_prefix >()) {
1123
+ Arguments* args = new (ctx.mem) Arguments(path, source_position);
1124
+ Function_Call* result = new (ctx.mem) Function_Call(path, source_position, "url", args);
1125
+ const char* here = position;
1126
+ Position here_p = source_position;
1127
+ // Try to parse a SassScript expression. If it succeeds and we can munch
1128
+ // a matching rparen, then that's our url. If we can't munch a matching
1129
+ // rparen, or if the attempt to parse an expression fails, then try to
1130
+ // munch a regular CSS url.
1131
+ try {
1132
+ // special case -- if there's a comment, treat it as part of a URL
1133
+ lex<spaces>();
1134
+ if (peek<line_comment_prefix>() || peek<block_comment_prefix>()) error("comment in URL"); // doesn't really matter what we throw
1135
+ Expression* expr = parse_list();
1136
+ if (!lex< exactly<')'> >()) error("dangling expression in URL"); // doesn't really matter what we throw
1137
+ Argument* arg = new (ctx.mem) Argument(path, expr->position(), expr);
1138
+ *args << arg;
1139
+ return result;
1140
+ }
1141
+ catch (Sass_Error&) {
1142
+ // back up so we can try again
1143
+ position = here;
1144
+ source_position = here_p;
1145
+ }
1146
+ catch (...) { throw; }
1147
+ lex< spaces >();
1148
+ if (lex< url >()) {
1149
+ String* the_url = parse_interpolated_chunk(lexed);
1150
+ Argument* arg = new (ctx.mem) Argument(path, the_url->position(), the_url);
1151
+ *args << arg;
1152
+ }
1153
+ else {
1154
+ error("malformed URL");
1155
+ }
1156
+ if (!lex< exactly<')'> >()) error("URI is missing ')'");
1157
+ return result;
1158
+ }
1159
+
1160
+ if (lex< important >())
1161
+ { return new (ctx.mem) String_Constant(path, source_position, "!important"); }
1162
+
1163
+ if (lex< value_schema >())
1164
+ { return Parser::from_token(lexed, ctx, path, source_position).parse_value_schema(); }
1165
+
1166
+ if (lex< sequence< true_val, negate< identifier > > >())
1167
+ { return new (ctx.mem) Boolean(path, source_position, true); }
1168
+
1169
+ if (lex< sequence< false_val, negate< identifier > > >())
1170
+ { return new (ctx.mem) Boolean(path, source_position, false); }
1171
+
1172
+ if (lex< sequence< null, negate< identifier > > >())
1173
+ { return new (ctx.mem) Null(path, source_position); }
1174
+
1175
+ if (lex< identifier >()) {
1176
+ String_Constant* str = new (ctx.mem) String_Constant(path, source_position, lexed);
1177
+ // Dont' delay this string if it is a name color. Fixes #652.
1178
+ str->is_delayed(ctx.names_to_colors.count(lexed) == 0);
1179
+ return str;
1180
+ }
1181
+
1182
+ if (lex< percentage >())
1183
+ { return new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed); }
1184
+
1185
+ if (lex< dimension >())
1186
+ { return new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed); }
1187
+
1188
+ if (lex< number >())
1189
+ { return new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed); }
1190
+
1191
+ if (lex< hex >())
1192
+ { return new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed); }
1193
+
1194
+ if (peek< string_constant >())
1195
+ { return parse_string(); }
1196
+
1197
+ if (lex< variable >())
1198
+ { return new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed)); }
1199
+
1200
+ // Special case handling for `%` proceeding an interpolant.
1201
+ if (lex< sequence< exactly<'%'>, optional< percentage > > >())
1202
+ { return new (ctx.mem) String_Constant(path, source_position, lexed); }
1203
+
1204
+ error("error reading values after " + lexed.to_string());
1205
+
1206
+ // unreachable statement
1207
+ return 0;
1208
+ }
1209
+
1210
+ String* Parser::parse_interpolated_chunk(Token chunk)
1211
+ {
1212
+ const char* i = chunk.begin;
1213
+ // see if there any interpolants
1214
+ const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(chunk.begin, chunk.end);
1215
+ if (!p) {
1216
+ String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, chunk, dequote);
1217
+ str_node->is_delayed(true);
1218
+ return str_node;
1219
+ }
1220
+
1221
+ String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
1222
+ schema->quote_mark(*chunk.begin);
1223
+ while (i < chunk.end) {
1224
+ p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, chunk.end);
1225
+ if (p) {
1226
+ if (i < p) {
1227
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
1228
+ }
1229
+ const char* j = find_first_in_interval< exactly<rbrace> >(p, chunk.end); // find the closing brace
1230
+ if (j) {
1231
+ // parse the interpolant and accumulate it
1232
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
1233
+ interp_node->is_interpolant(true);
1234
+ (*schema) << interp_node;
1235
+ i = j+1;
1236
+ }
1237
+ else {
1238
+ // throw an error if the interpolant is unterminated
1239
+ error("unterminated interpolant inside string constant " + chunk.to_string());
1240
+ }
1241
+ }
1242
+ else { // no interpolants left; add the last segment if nonempty
1243
+ if (i < chunk.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, chunk.end));
1244
+ break;
1245
+ }
1246
+ }
1247
+ return schema;
1248
+ }
1249
+
1250
+ String_Constant* Parser::parse_static_value()
1251
+ {
1252
+ lex< static_value >();
1253
+ Token str(lexed);
1254
+ --str.end;
1255
+ --position;
1256
+ String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
1257
+ str_node->is_delayed(true);
1258
+ return str_node;
1259
+ }
1260
+
1261
+ String* Parser::parse_string()
1262
+ {
1263
+ lex< string_constant >();
1264
+ Token str(lexed);
1265
+ return parse_interpolated_chunk(str);
1266
+ // const char* i = str.begin;
1267
+ // // see if there any interpolants
1268
+ // const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(str.begin, str.end);
1269
+ // if (!p) {
1270
+ // String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
1271
+ // str_node->is_delayed(true);
1272
+ // return str_node;
1273
+ // }
1274
+
1275
+ // String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
1276
+ // schema->quote_mark(*str.begin);
1277
+ // while (i < str.end) {
1278
+ // p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
1279
+ // if (p) {
1280
+ // if (i < p) {
1281
+ // (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
1282
+ // }
1283
+ // const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
1284
+ // if (j) {
1285
+ // // parse the interpolant and accumulate it
1286
+ // Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
1287
+ // interp_node->is_interpolant(true);
1288
+ // (*schema) << interp_node;
1289
+ // i = j+1;
1290
+ // }
1291
+ // else {
1292
+ // // throw an error if the interpolant is unterminated
1293
+ // error("unterminated interpolant inside string constant " + str.to_string());
1294
+ // }
1295
+ // }
1296
+ // else { // no interpolants left; add the last segment if nonempty
1297
+ // if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end));
1298
+ // break;
1299
+ // }
1300
+ // }
1301
+ // return schema;
1302
+ }
1303
+
1304
+ String* Parser::parse_ie_property()
1305
+ {
1306
+ lex< ie_property >();
1307
+ Token str(lexed);
1308
+ const char* i = str.begin;
1309
+ // see if there any interpolants
1310
+ const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(str.begin, str.end);
1311
+ if (!p) {
1312
+ String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
1313
+ str_node->is_delayed(true);
1314
+ return str_node;
1315
+ }
1316
+
1317
+ String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
1318
+ while (i < str.end) {
1319
+ p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
1320
+ if (p) {
1321
+ if (i < p) {
1322
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
1323
+ }
1324
+ const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
1325
+ if (j) {
1326
+ // parse the interpolant and accumulate it
1327
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
1328
+ interp_node->is_interpolant(true);
1329
+ (*schema) << interp_node;
1330
+ i = j+1;
1331
+ }
1332
+ else {
1333
+ // throw an error if the interpolant is unterminated
1334
+ error("unterminated interpolant inside IE function " + str.to_string());
1335
+ }
1336
+ }
1337
+ else { // no interpolants left; add the last segment if nonempty
1338
+ if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end));
1339
+ break;
1340
+ }
1341
+ }
1342
+ return schema;
1343
+ }
1344
+
1345
+ String* Parser::parse_ie_keyword_arg()
1346
+ {
1347
+ String_Schema* kwd_arg = new (ctx.mem) String_Schema(path, source_position, 3);
1348
+ if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed));
1349
+ else {
1350
+ lex< alternatives< identifier_schema, identifier > >();
1351
+ *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
1352
+ }
1353
+ lex< exactly<'='> >();
1354
+ *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
1355
+ if (peek< variable >()) *kwd_arg << parse_list();
1356
+ else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, Util::normalize_decimals(lexed));
1357
+ else {
1358
+ lex< alternatives< identifier_schema, identifier, number, hex > >();
1359
+ *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
1360
+ }
1361
+ return kwd_arg;
1362
+ }
1363
+
1364
+ String_Schema* Parser::parse_value_schema()
1365
+ {
1366
+ String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
1367
+ size_t num_items = 0;
1368
+ while (position < end) {
1369
+ if (lex< interpolant >()) {
1370
+ Token insides(Token(lexed.begin + 2, lexed.end - 1));
1371
+ Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
1372
+ interp_node->is_interpolant(true);
1373
+ (*schema) << interp_node;
1374
+ }
1375
+ else if (lex< exactly<'%'> >()) {
1376
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
1377
+ }
1378
+ else if (lex< identifier >()) {
1379
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
1380
+ }
1381
+ else if (lex< percentage >()) {
1382
+ (*schema) << new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed);
1383
+ }
1384
+ else if (lex< dimension >()) {
1385
+ (*schema) << new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed);
1386
+ }
1387
+ else if (lex< number >()) {
1388
+ (*schema) << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed);
1389
+ }
1390
+ else if (lex< hex >()) {
1391
+ (*schema) << new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed);
1392
+ }
1393
+ else if (lex< string_constant >()) {
1394
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
1395
+ if (!num_items) schema->quote_mark(*lexed.begin);
1396
+ }
1397
+ else if (lex< variable >()) {
1398
+ (*schema) << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed));
1399
+ }
1400
+ else {
1401
+ error("error parsing interpolated value");
1402
+ }
1403
+ ++num_items;
1404
+ }
1405
+ return schema;
1406
+ }
1407
+
1408
+ String_Schema* Parser::parse_url_schema()
1409
+ {
1410
+ String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
1411
+
1412
+ while (position < end) {
1413
+ if (position[0] == '/') {
1414
+ lexed = Token(position, position+1);
1415
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
1416
+ ++position;
1417
+ }
1418
+ else if (lex< interpolant >()) {
1419
+ Token insides(Token(lexed.begin + 2, lexed.end - 1));
1420
+ Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
1421
+ interp_node->is_interpolant(true);
1422
+ (*schema) << interp_node;
1423
+ }
1424
+ else if (lex< sequence< identifier, exactly<':'> > >()) {
1425
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
1426
+ }
1427
+ else if (lex< filename >()) {
1428
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
1429
+ }
1430
+ else {
1431
+ error("error parsing interpolated url");
1432
+ }
1433
+ }
1434
+ return schema;
1435
+ }
1436
+
1437
+ String* Parser::parse_identifier_schema()
1438
+ {
1439
+ lex< sequence< optional< exactly<'*'> >, identifier_schema > >();
1440
+ Token id(lexed);
1441
+ const char* i = id.begin;
1442
+ // see if there any interpolants
1443
+ const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(id.begin, id.end);
1444
+ if (!p) {
1445
+ return new (ctx.mem) String_Constant(path, source_position, id);
1446
+ }
1447
+
1448
+ String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
1449
+ while (i < id.end) {
1450
+ p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, id.end);
1451
+ if (p) {
1452
+ if (i < p) {
1453
+ (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
1454
+ }
1455
+ const char* j = find_first_in_interval< exactly<rbrace> >(p, id.end); // find the closing brace
1456
+ if (j) {
1457
+ // parse the interpolant and accumulate it
1458
+ Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
1459
+ interp_node->is_interpolant(true);
1460
+ (*schema) << interp_node;
1461
+ schema->has_interpolants(true);
1462
+ i = j+1;
1463
+ }
1464
+ else {
1465
+ // throw an error if the interpolant is unterminated
1466
+ error("unterminated interpolant inside interpolated identifier " + id.to_string());
1467
+ }
1468
+ }
1469
+ else { // no interpolants left; add the last segment if nonempty
1470
+ if (i < id.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, id.end));
1471
+ break;
1472
+ }
1473
+ }
1474
+ return schema;
1475
+ }
1476
+
1477
+ Function_Call* Parser::parse_calc_function()
1478
+ {
1479
+ lex< identifier >();
1480
+ string name(lexed);
1481
+ Position call_pos = source_position;
1482
+ lex< exactly<'('> >();
1483
+ Position arg_pos = source_position;
1484
+ const char* arg_beg = position;
1485
+ parse_list();
1486
+ const char* arg_end = position;
1487
+ lex< exactly<')'> >();
1488
+
1489
+ Argument* arg = new (ctx.mem) Argument(path, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));
1490
+ Arguments* args = new (ctx.mem) Arguments(path, arg_pos);
1491
+ *args << arg;
1492
+ return new (ctx.mem) Function_Call(path, call_pos, name, args);
1493
+ }
1494
+
1495
+ Function_Call* Parser::parse_function_call()
1496
+ {
1497
+ lex< identifier >();
1498
+ string name(Util::normalize_underscores(lexed));
1499
+ Position source_position_of_call = source_position;
1500
+
1501
+ Function_Call* the_call = new (ctx.mem) Function_Call(path, source_position_of_call, name, parse_arguments());
1502
+ return the_call;
1503
+ }
1504
+
1505
+ Function_Call_Schema* Parser::parse_function_call_schema()
1506
+ {
1507
+ String* name = parse_identifier_schema();
1508
+ Position source_position_of_call = source_position;
1509
+
1510
+ Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(path, source_position_of_call, name, parse_arguments());
1511
+ return the_call;
1512
+ }
1513
+
1514
+ If* Parser::parse_if_directive(bool else_if)
1515
+ {
1516
+ lex< if_directive >() || (else_if && lex< exactly<if_after_else_kwd> >());
1517
+ Position if_source_position = source_position;
1518
+ Expression* predicate = parse_list();
1519
+ predicate->is_delayed(false);
1520
+ if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if");
1521
+ Block* consequent = parse_block();
1522
+ Block* alternative = 0;
1523
+ if (lex< else_directive >()) {
1524
+ if (peek< exactly<if_after_else_kwd> >()) {
1525
+ alternative = new (ctx.mem) Block(path, source_position);
1526
+ (*alternative) << parse_if_directive(true);
1527
+ }
1528
+ else if (!peek< exactly<'{'> >()) {
1529
+ error("expected '{' after @else");
1530
+ }
1531
+ else {
1532
+ alternative = parse_block();
1533
+ }
1534
+ }
1535
+ return new (ctx.mem) If(path, if_source_position, predicate, consequent, alternative);
1536
+ }
1537
+
1538
+ For* Parser::parse_for_directive()
1539
+ {
1540
+ lex< for_directive >();
1541
+ Position for_source_position = source_position;
1542
+ if (!lex< variable >()) error("@for directive requires an iteration variable");
1543
+ string var(Util::normalize_underscores(lexed));
1544
+ if (!lex< from >()) error("expected 'from' keyword in @for directive");
1545
+ Expression* lower_bound = parse_expression();
1546
+ lower_bound->is_delayed(false);
1547
+ bool inclusive = false;
1548
+ if (lex< through >()) inclusive = true;
1549
+ else if (lex< to >()) inclusive = false;
1550
+ else error("expected 'through' or 'to' keyword in @for directive");
1551
+ Expression* upper_bound = parse_expression();
1552
+ upper_bound->is_delayed(false);
1553
+ if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive");
1554
+ Block* body = parse_block();
1555
+ return new (ctx.mem) For(path, for_source_position, var, lower_bound, upper_bound, body, inclusive);
1556
+ }
1557
+
1558
+ Each* Parser::parse_each_directive()
1559
+ {
1560
+ lex < each_directive >();
1561
+ Position each_source_position = source_position;
1562
+ if (!lex< variable >()) error("@each directive requires an iteration variable");
1563
+ vector<string> vars;
1564
+ vars.push_back(Util::normalize_underscores(lexed));
1565
+ while (peek< exactly<','> >() && lex< exactly<','> >()) {
1566
+ if (!lex< variable >()) error("@each directive requires an iteration variable");
1567
+ vars.push_back(Util::normalize_underscores(lexed));
1568
+ }
1569
+ if (!lex< in >()) error("expected 'in' keyword in @each directive");
1570
+ Expression* list = parse_list();
1571
+ list->is_delayed(false);
1572
+ if (list->concrete_type() == Expression::LIST) {
1573
+ List* l = static_cast<List*>(list);
1574
+ for (size_t i = 0, L = l->length(); i < L; ++i) {
1575
+ (*l)[i]->is_delayed(false);
1576
+ }
1577
+ }
1578
+ if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive");
1579
+ Block* body = parse_block();
1580
+ return new (ctx.mem) Each(path, each_source_position, vars, list, body);
1581
+ }
1582
+
1583
+ While* Parser::parse_while_directive()
1584
+ {
1585
+ lex< while_directive >();
1586
+ Position while_source_position = source_position;
1587
+ Expression* predicate = parse_list();
1588
+ predicate->is_delayed(false);
1589
+ Block* body = parse_block();
1590
+ return new (ctx.mem) While(path, while_source_position, predicate, body);
1591
+ }
1592
+
1593
+ Media_Block* Parser::parse_media_block()
1594
+ {
1595
+ lex< media >();
1596
+ Position media_source_position = source_position;
1597
+
1598
+ List* media_queries = parse_media_queries();
1599
+
1600
+ if (!peek< exactly<'{'> >()) {
1601
+ error("expected '{' in media query");
1602
+ }
1603
+ Block* block = parse_block();
1604
+
1605
+ return new (ctx.mem) Media_Block(path, media_source_position, media_queries, block);
1606
+ }
1607
+
1608
+ List* Parser::parse_media_queries()
1609
+ {
1610
+ List* media_queries = new (ctx.mem) List(path, source_position, 0, List::COMMA);
1611
+ if (!peek< exactly<'{'> >()) (*media_queries) << parse_media_query();
1612
+ while (lex< exactly<','> >()) (*media_queries) << parse_media_query();
1613
+ return media_queries;
1614
+ }
1615
+
1616
+ // Expression* Parser::parse_media_query()
1617
+ Media_Query* Parser::parse_media_query()
1618
+ {
1619
+ Media_Query* media_query = new (ctx.mem) Media_Query(path, source_position);
1620
+
1621
+ if (lex< exactly< not_kwd > >()) media_query->is_negated(true);
1622
+ else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true);
1623
+
1624
+ if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema());
1625
+ else if (lex< identifier >()) media_query->media_type(new (ctx.mem) String_Constant(path, source_position, lexed));
1626
+ else (*media_query) << parse_media_expression();
1627
+
1628
+ while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression();
1629
+
1630
+ return media_query;
1631
+ }
1632
+
1633
+ Media_Query_Expression* Parser::parse_media_expression()
1634
+ {
1635
+ if (peek< identifier_schema >()) {
1636
+ String* ss = parse_identifier_schema();
1637
+ return new (ctx.mem) Media_Query_Expression(path, source_position, ss, 0, true);
1638
+ }
1639
+ if (!lex< exactly<'('> >()) {
1640
+ error("media query expression must begin with '('");
1641
+ }
1642
+ Expression* feature = 0;
1643
+ if (peek< exactly<')'> >()) {
1644
+ error("media feature required in media query expression");
1645
+ }
1646
+ feature = parse_expression();
1647
+ Expression* expression = 0;
1648
+ if (lex< exactly<':'> >()) {
1649
+ expression = parse_list();
1650
+ }
1651
+ if (!lex< exactly<')'> >()) {
1652
+ error("unclosed parenthesis in media query expression");
1653
+ }
1654
+ return new (ctx.mem) Media_Query_Expression(path, feature->position(), feature, expression);
1655
+ }
1656
+
1657
+ Feature_Block* Parser::parse_feature_block()
1658
+ {
1659
+ lex< supports >();
1660
+ Position supports_source_position = source_position;
1661
+
1662
+ Feature_Query* feature_queries = parse_feature_queries();
1663
+
1664
+ if (!peek< exactly<'{'> >()) {
1665
+ error("expected '{' in feature query");
1666
+ }
1667
+ Block* block = parse_block();
1668
+
1669
+ return new (ctx.mem) Feature_Block(path, supports_source_position, feature_queries, block);
1670
+ }
1671
+
1672
+ Feature_Query* Parser::parse_feature_queries()
1673
+ {
1674
+ Feature_Query* fq = new (ctx.mem) Feature_Query(path, source_position);
1675
+ Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(path, source_position);
1676
+ cond->is_root(true);
1677
+ while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
1678
+ (*cond) << parse_feature_query();
1679
+ (*fq) << cond;
1680
+
1681
+ if (fq->empty()) error("expected @supports condition (e.g. (display: flexbox))");
1682
+
1683
+ return fq;
1684
+ }
1685
+
1686
+ Feature_Query_Condition* Parser::parse_feature_query()
1687
+ {
1688
+ if (peek< not_op >(position)) return parse_supports_negation();
1689
+ else if (peek< and_op >(position)) return parse_supports_conjunction();
1690
+ else if (peek< or_op >(position)) return parse_supports_disjunction();
1691
+ else if (peek< exactly<'('> >(position)) return parse_feature_query_in_parens();
1692
+ else return parse_supports_declaration();
1693
+ }
1694
+
1695
+ Feature_Query_Condition* Parser::parse_feature_query_in_parens()
1696
+ {
1697
+ Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(path, source_position);
1698
+
1699
+ if (!lex< exactly<'('> >()) error("@supports declaration expected '('");
1700
+ while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
1701
+ (*cond) << parse_feature_query();
1702
+ if (!lex< exactly<')'> >()) error("unclosed parenthesis in @supports declaration");
1703
+
1704
+ return (cond->length() == 1) ? (*cond)[0] : cond;
1705
+ }
1706
+
1707
+ Feature_Query_Condition* Parser::parse_supports_negation()
1708
+ {
1709
+ lex< not_op >();
1710
+
1711
+ Feature_Query_Condition* cond = parse_feature_query();
1712
+ cond->operand(Feature_Query_Condition::NOT);
1713
+
1714
+ return cond;
1715
+ }
1716
+
1717
+ Feature_Query_Condition* Parser::parse_supports_conjunction()
1718
+ {
1719
+ lex< and_op >();
1720
+
1721
+ Feature_Query_Condition* cond = parse_feature_query();
1722
+ cond->operand(Feature_Query_Condition::AND);
1723
+
1724
+ return cond;
1725
+ }
1726
+
1727
+ Feature_Query_Condition* Parser::parse_supports_disjunction()
1728
+ {
1729
+ lex< or_op >();
1730
+
1731
+ Feature_Query_Condition* cond = parse_feature_query();
1732
+ cond->operand(Feature_Query_Condition::OR);
1733
+
1734
+ return cond;
1735
+ }
1736
+
1737
+ Feature_Query_Condition* Parser::parse_supports_declaration()
1738
+ {
1739
+ Declaration* declaration = parse_declaration();
1740
+ Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(declaration->path(),
1741
+ declaration->position(),
1742
+ 1,
1743
+ declaration->property(),
1744
+ declaration->value());
1745
+ return cond;
1746
+ }
1747
+
1748
+ At_Rule* Parser::parse_at_rule()
1749
+ {
1750
+ lex<at_keyword>();
1751
+ string kwd(lexed);
1752
+ Position at_source_position = source_position;
1753
+ Selector* sel = 0;
1754
+ Expression* val = 0;
1755
+ Selector_Lookahead lookahead = lookahead_for_extension_target(position);
1756
+ if (lookahead.found) {
1757
+ if (lookahead.has_interpolants) {
1758
+ sel = parse_selector_schema(lookahead.found);
1759
+ }
1760
+ else {
1761
+ sel = parse_selector_group();
1762
+ }
1763
+ }
1764
+ else if (!(peek<exactly<'{'> >() || peek<exactly<'}'> >() || peek<exactly<';'> >())) {
1765
+ val = parse_list();
1766
+ }
1767
+ Block* body = 0;
1768
+ if (peek< exactly<'{'> >()) body = parse_block();
1769
+ At_Rule* rule = new (ctx.mem) At_Rule(path, at_source_position, kwd, sel, body);
1770
+ if (!sel) rule->value(val);
1771
+ return rule;
1772
+ }
1773
+
1774
+ Warning* Parser::parse_warning()
1775
+ {
1776
+ lex< warn >();
1777
+ return new (ctx.mem) Warning(path, source_position, parse_list());
1778
+ }
1779
+
1780
+ Error* Parser::parse_error()
1781
+ {
1782
+ lex< err >();
1783
+ return new (ctx.mem) Error(path, source_position, parse_list());
1784
+ }
1785
+
1786
+ Debug* Parser::parse_debug()
1787
+ {
1788
+ lex< dbg >();
1789
+ return new (ctx.mem) Debug(path, source_position, parse_list());
1790
+ }
1791
+
1792
+ Selector_Lookahead Parser::lookahead_for_selector(const char* start)
1793
+ {
1794
+ const char* p = start ? start : position;
1795
+ const char* q;
1796
+ bool saw_stuff = false;
1797
+ bool saw_interpolant = false;
1798
+
1799
+ while ((q = peek< identifier >(p)) ||
1800
+ (q = peek< hyphens_and_identifier >(p)) ||
1801
+ (q = peek< hyphens_and_name >(p)) ||
1802
+ (q = peek< type_selector >(p)) ||
1803
+ (q = peek< id_name >(p)) ||
1804
+ (q = peek< class_name >(p)) ||
1805
+ (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
1806
+ (q = peek< percentage >(p)) ||
1807
+ (q = peek< dimension >(p)) ||
1808
+ (q = peek< string_constant >(p)) ||
1809
+ (q = peek< exactly<'*'> >(p)) ||
1810
+ (q = peek< exactly<'('> >(p)) ||
1811
+ (q = peek< exactly<')'> >(p)) ||
1812
+ (q = peek< exactly<'['> >(p)) ||
1813
+ (q = peek< exactly<']'> >(p)) ||
1814
+ (q = peek< exactly<'+'> >(p)) ||
1815
+ (q = peek< exactly<'~'> >(p)) ||
1816
+ (q = peek< exactly<'>'> >(p)) ||
1817
+ (q = peek< exactly<','> >(p)) ||
1818
+ (q = peek< binomial >(p)) ||
1819
+ (q = peek< sequence< optional<sign>,
1820
+ optional<digits>,
1821
+ exactly<'n'> > >(p)) ||
1822
+ (q = peek< sequence< optional<sign>,
1823
+ digits > >(p)) ||
1824
+ (q = peek< number >(p)) ||
1825
+ (q = peek< sequence< exactly<'&'>,
1826
+ identifier_fragment > >(p)) ||
1827
+ (q = peek< exactly<'&'> >(p)) ||
1828
+ (q = peek< exactly<'%'> >(p)) ||
1829
+ (q = peek< alternatives<exact_match,
1830
+ class_match,
1831
+ dash_match,
1832
+ prefix_match,
1833
+ suffix_match,
1834
+ substring_match> >(p)) ||
1835
+ (q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
1836
+ (q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
1837
+ (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
1838
+ (q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
1839
+ (q = peek< interpolant >(p))) {
1840
+ saw_stuff = true;
1841
+ p = q;
1842
+ if (*(p - 1) == '}') saw_interpolant = true;
1843
+ }
1844
+
1845
+ Selector_Lookahead result;
1846
+ result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0;
1847
+ result.has_interpolants = saw_interpolant;
1848
+
1849
+ return result;
1850
+ }
1851
+
1852
+ Selector_Lookahead Parser::lookahead_for_extension_target(const char* start)
1853
+ {
1854
+ const char* p = start ? start : position;
1855
+ const char* q;
1856
+ bool saw_interpolant = false;
1857
+ bool saw_stuff = false;
1858
+
1859
+ while ((q = peek< identifier >(p)) ||
1860
+ (q = peek< type_selector >(p)) ||
1861
+ (q = peek< id_name >(p)) ||
1862
+ (q = peek< class_name >(p)) ||
1863
+ (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
1864
+ (q = peek< percentage >(p)) ||
1865
+ (q = peek< dimension >(p)) ||
1866
+ (q = peek< string_constant >(p)) ||
1867
+ (q = peek< exactly<'*'> >(p)) ||
1868
+ (q = peek< exactly<'('> >(p)) ||
1869
+ (q = peek< exactly<')'> >(p)) ||
1870
+ (q = peek< exactly<'['> >(p)) ||
1871
+ (q = peek< exactly<']'> >(p)) ||
1872
+ (q = peek< exactly<'+'> >(p)) ||
1873
+ (q = peek< exactly<'~'> >(p)) ||
1874
+ (q = peek< exactly<'>'> >(p)) ||
1875
+ (q = peek< exactly<','> >(p)) ||
1876
+ (q = peek< binomial >(p)) ||
1877
+ (q = peek< sequence< optional<sign>,
1878
+ optional<digits>,
1879
+ exactly<'n'> > >(p)) ||
1880
+ (q = peek< sequence< optional<sign>,
1881
+ digits > >(p)) ||
1882
+ (q = peek< number >(p)) ||
1883
+ (q = peek< sequence< exactly<'&'>,
1884
+ identifier_fragment > >(p)) ||
1885
+ (q = peek< exactly<'&'> >(p)) ||
1886
+ (q = peek< exactly<'%'> >(p)) ||
1887
+ (q = peek< alternatives<exact_match,
1888
+ class_match,
1889
+ dash_match,
1890
+ prefix_match,
1891
+ suffix_match,
1892
+ substring_match> >(p)) ||
1893
+ (q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
1894
+ (q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
1895
+ (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
1896
+ (q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
1897
+ (q = peek< interpolant >(p)) ||
1898
+ (q = peek< optional >(p))) {
1899
+ p = q;
1900
+ if (*(p - 1) == '}') saw_interpolant = true;
1901
+ saw_stuff = true;
1902
+ }
1903
+
1904
+ Selector_Lookahead result;
1905
+ result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;
1906
+ result.has_interpolants = saw_interpolant;
1907
+
1908
+ return result;
1909
+ }
1910
+
1911
+ void Parser::read_bom()
1912
+ {
1913
+ size_t skip = 0;
1914
+ string encoding;
1915
+ bool utf_8 = false;
1916
+ switch ((unsigned char) source[0]) {
1917
+ case 0xEF:
1918
+ skip = check_bom_chars(source, end, utf_8_bom, 3);
1919
+ encoding = "UTF-8";
1920
+ utf_8 = true;
1921
+ break;
1922
+ case 0xFE:
1923
+ skip = check_bom_chars(source, end, utf_16_bom_be, 2);
1924
+ encoding = "UTF-16 (big endian)";
1925
+ break;
1926
+ case 0xFF:
1927
+ skip = check_bom_chars(source, end, utf_16_bom_le, 2);
1928
+ skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);
1929
+ encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");
1930
+ break;
1931
+ case 0x00:
1932
+ skip = check_bom_chars(source, end, utf_32_bom_be, 4);
1933
+ encoding = "UTF-32 (big endian)";
1934
+ break;
1935
+ case 0x2B:
1936
+ skip = check_bom_chars(source, end, utf_7_bom_1, 4)
1937
+ | check_bom_chars(source, end, utf_7_bom_2, 4)
1938
+ | check_bom_chars(source, end, utf_7_bom_3, 4)
1939
+ | check_bom_chars(source, end, utf_7_bom_4, 4)
1940
+ | check_bom_chars(source, end, utf_7_bom_5, 5);
1941
+ encoding = "UTF-7";
1942
+ break;
1943
+ case 0xF7:
1944
+ skip = check_bom_chars(source, end, utf_1_bom, 3);
1945
+ encoding = "UTF-1";
1946
+ break;
1947
+ case 0xDD:
1948
+ skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);
1949
+ encoding = "UTF-EBCDIC";
1950
+ break;
1951
+ case 0x0E:
1952
+ skip = check_bom_chars(source, end, scsu_bom, 3);
1953
+ encoding = "SCSU";
1954
+ break;
1955
+ case 0xFB:
1956
+ skip = check_bom_chars(source, end, bocu_1_bom, 3);
1957
+ encoding = "BOCU-1";
1958
+ break;
1959
+ case 0x84:
1960
+ skip = check_bom_chars(source, end, gb_18030_bom, 4);
1961
+ encoding = "GB-18030";
1962
+ break;
1963
+ }
1964
+ if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding);
1965
+ position += skip;
1966
+ }
1967
+
1968
+ size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)
1969
+ {
1970
+ size_t skip = 0;
1971
+ if (src + len > end) return 0;
1972
+ for (size_t i = 0; i < len; ++i, ++skip) {
1973
+ if ((unsigned char) src[i] != bom[i]) return 0;
1974
+ }
1975
+ return skip;
1976
+ }
1977
+
1978
+
1979
+ Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, Binary_Expression::Type op)
1980
+ {
1981
+ for (size_t i = 0, S = operands.size(); i < S; ++i) {
1982
+ base = new (ctx.mem) Binary_Expression(path, source_position, op, base, operands[i]);
1983
+ Binary_Expression* b = static_cast<Binary_Expression*>(base);
1984
+ if (op == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
1985
+ base->is_delayed(true);
1986
+ }
1987
+ else {
1988
+ b->left()->is_delayed(false);
1989
+ b->right()->is_delayed(false);
1990
+ }
1991
+ }
1992
+ return base;
1993
+ }
1994
+
1995
+ Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, vector<Binary_Expression::Type>& ops)
1996
+ {
1997
+ for (size_t i = 0, S = operands.size(); i < S; ++i) {
1998
+ base = new (ctx.mem) Binary_Expression(path, base->position(), ops[i], base, operands[i]);
1999
+ Binary_Expression* b = static_cast<Binary_Expression*>(base);
2000
+ if (ops[i] == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
2001
+ base->is_delayed(true);
2002
+ }
2003
+ else {
2004
+ b->left()->is_delayed(false);
2005
+ b->right()->is_delayed(false);
2006
+ }
2007
+ }
2008
+ return base;
2009
+ }
2010
+
2011
+ void Parser::error(string msg, Position pos)
2012
+ {
2013
+ throw Sass_Error(Sass_Error::syntax, path, pos.line ? pos : source_position, msg);
2014
+ }
2015
+
2016
+ }