precompiled-sassc 2.4.0

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 (214) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/cibuildgem.yaml +67 -0
  3. data/.gitignore +18 -0
  4. data/.gitmodules +3 -0
  5. data/.travis.yml +16 -0
  6. data/CHANGELOG.md +97 -0
  7. data/CODE_OF_CONDUCT.md +10 -0
  8. data/Gemfile +2 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +80 -0
  11. data/Rakefile +51 -0
  12. data/ext/depend +4 -0
  13. data/ext/extconf.rb +92 -0
  14. data/ext/libsass/VERSION +1 -0
  15. data/ext/libsass/contrib/plugin.cpp +60 -0
  16. data/ext/libsass/include/sass/base.h +97 -0
  17. data/ext/libsass/include/sass/context.h +174 -0
  18. data/ext/libsass/include/sass/functions.h +139 -0
  19. data/ext/libsass/include/sass/values.h +145 -0
  20. data/ext/libsass/include/sass/version.h +12 -0
  21. data/ext/libsass/include/sass.h +15 -0
  22. data/ext/libsass/include/sass2scss.h +120 -0
  23. data/ext/libsass/src/MurmurHash2.hpp +91 -0
  24. data/ext/libsass/src/ast.cpp +953 -0
  25. data/ext/libsass/src/ast.hpp +1064 -0
  26. data/ext/libsass/src/ast2c.cpp +80 -0
  27. data/ext/libsass/src/ast2c.hpp +39 -0
  28. data/ext/libsass/src/ast_def_macros.hpp +140 -0
  29. data/ext/libsass/src/ast_fwd_decl.cpp +31 -0
  30. data/ext/libsass/src/ast_fwd_decl.hpp +274 -0
  31. data/ext/libsass/src/ast_helpers.hpp +292 -0
  32. data/ext/libsass/src/ast_sel_cmp.cpp +396 -0
  33. data/ext/libsass/src/ast_sel_super.cpp +539 -0
  34. data/ext/libsass/src/ast_sel_unify.cpp +275 -0
  35. data/ext/libsass/src/ast_sel_weave.cpp +616 -0
  36. data/ext/libsass/src/ast_selectors.cpp +1043 -0
  37. data/ext/libsass/src/ast_selectors.hpp +522 -0
  38. data/ext/libsass/src/ast_supports.cpp +114 -0
  39. data/ext/libsass/src/ast_supports.hpp +121 -0
  40. data/ext/libsass/src/ast_values.cpp +1154 -0
  41. data/ext/libsass/src/ast_values.hpp +498 -0
  42. data/ext/libsass/src/b64/cencode.h +32 -0
  43. data/ext/libsass/src/b64/encode.h +79 -0
  44. data/ext/libsass/src/backtrace.cpp +50 -0
  45. data/ext/libsass/src/backtrace.hpp +29 -0
  46. data/ext/libsass/src/base64vlq.cpp +47 -0
  47. data/ext/libsass/src/base64vlq.hpp +30 -0
  48. data/ext/libsass/src/bind.cpp +312 -0
  49. data/ext/libsass/src/bind.hpp +15 -0
  50. data/ext/libsass/src/c2ast.cpp +64 -0
  51. data/ext/libsass/src/c2ast.hpp +14 -0
  52. data/ext/libsass/src/c99func.c +54 -0
  53. data/ext/libsass/src/cencode.c +106 -0
  54. data/ext/libsass/src/check_nesting.cpp +393 -0
  55. data/ext/libsass/src/check_nesting.hpp +70 -0
  56. data/ext/libsass/src/color_maps.cpp +652 -0
  57. data/ext/libsass/src/color_maps.hpp +323 -0
  58. data/ext/libsass/src/constants.cpp +199 -0
  59. data/ext/libsass/src/constants.hpp +200 -0
  60. data/ext/libsass/src/context.cpp +863 -0
  61. data/ext/libsass/src/context.hpp +140 -0
  62. data/ext/libsass/src/cssize.cpp +521 -0
  63. data/ext/libsass/src/cssize.hpp +71 -0
  64. data/ext/libsass/src/dart_helpers.hpp +199 -0
  65. data/ext/libsass/src/debug.hpp +43 -0
  66. data/ext/libsass/src/debugger.hpp +963 -0
  67. data/ext/libsass/src/emitter.cpp +297 -0
  68. data/ext/libsass/src/emitter.hpp +101 -0
  69. data/ext/libsass/src/environment.cpp +260 -0
  70. data/ext/libsass/src/environment.hpp +124 -0
  71. data/ext/libsass/src/error_handling.cpp +233 -0
  72. data/ext/libsass/src/error_handling.hpp +239 -0
  73. data/ext/libsass/src/eval.cpp +1543 -0
  74. data/ext/libsass/src/eval.hpp +110 -0
  75. data/ext/libsass/src/eval_selectors.cpp +75 -0
  76. data/ext/libsass/src/expand.cpp +875 -0
  77. data/ext/libsass/src/expand.hpp +98 -0
  78. data/ext/libsass/src/extender.cpp +1188 -0
  79. data/ext/libsass/src/extender.hpp +399 -0
  80. data/ext/libsass/src/extension.cpp +43 -0
  81. data/ext/libsass/src/extension.hpp +89 -0
  82. data/ext/libsass/src/file.cpp +531 -0
  83. data/ext/libsass/src/file.hpp +124 -0
  84. data/ext/libsass/src/fn_colors.cpp +596 -0
  85. data/ext/libsass/src/fn_colors.hpp +85 -0
  86. data/ext/libsass/src/fn_lists.cpp +285 -0
  87. data/ext/libsass/src/fn_lists.hpp +34 -0
  88. data/ext/libsass/src/fn_maps.cpp +94 -0
  89. data/ext/libsass/src/fn_maps.hpp +30 -0
  90. data/ext/libsass/src/fn_miscs.cpp +244 -0
  91. data/ext/libsass/src/fn_miscs.hpp +40 -0
  92. data/ext/libsass/src/fn_numbers.cpp +227 -0
  93. data/ext/libsass/src/fn_numbers.hpp +45 -0
  94. data/ext/libsass/src/fn_selectors.cpp +205 -0
  95. data/ext/libsass/src/fn_selectors.hpp +35 -0
  96. data/ext/libsass/src/fn_strings.cpp +268 -0
  97. data/ext/libsass/src/fn_strings.hpp +34 -0
  98. data/ext/libsass/src/fn_utils.cpp +158 -0
  99. data/ext/libsass/src/fn_utils.hpp +62 -0
  100. data/ext/libsass/src/inspect.cpp +1125 -0
  101. data/ext/libsass/src/inspect.hpp +101 -0
  102. data/ext/libsass/src/json.cpp +1436 -0
  103. data/ext/libsass/src/json.hpp +117 -0
  104. data/ext/libsass/src/kwd_arg_macros.hpp +28 -0
  105. data/ext/libsass/src/lexer.cpp +122 -0
  106. data/ext/libsass/src/lexer.hpp +304 -0
  107. data/ext/libsass/src/listize.cpp +70 -0
  108. data/ext/libsass/src/listize.hpp +37 -0
  109. data/ext/libsass/src/mapping.hpp +19 -0
  110. data/ext/libsass/src/memory/allocator.cpp +48 -0
  111. data/ext/libsass/src/memory/allocator.hpp +138 -0
  112. data/ext/libsass/src/memory/config.hpp +20 -0
  113. data/ext/libsass/src/memory/memory_pool.hpp +186 -0
  114. data/ext/libsass/src/memory/shared_ptr.cpp +33 -0
  115. data/ext/libsass/src/memory/shared_ptr.hpp +332 -0
  116. data/ext/libsass/src/memory.hpp +12 -0
  117. data/ext/libsass/src/operation.hpp +223 -0
  118. data/ext/libsass/src/operators.cpp +267 -0
  119. data/ext/libsass/src/operators.hpp +30 -0
  120. data/ext/libsass/src/ordered_map.hpp +112 -0
  121. data/ext/libsass/src/output.cpp +320 -0
  122. data/ext/libsass/src/output.hpp +47 -0
  123. data/ext/libsass/src/parser.cpp +2965 -0
  124. data/ext/libsass/src/parser.hpp +394 -0
  125. data/ext/libsass/src/parser_selectors.cpp +189 -0
  126. data/ext/libsass/src/permutate.hpp +164 -0
  127. data/ext/libsass/src/plugins.cpp +188 -0
  128. data/ext/libsass/src/plugins.hpp +57 -0
  129. data/ext/libsass/src/position.cpp +165 -0
  130. data/ext/libsass/src/position.hpp +147 -0
  131. data/ext/libsass/src/prelexer.cpp +1780 -0
  132. data/ext/libsass/src/prelexer.hpp +484 -0
  133. data/ext/libsass/src/remove_placeholders.cpp +86 -0
  134. data/ext/libsass/src/remove_placeholders.hpp +37 -0
  135. data/ext/libsass/src/sass.cpp +156 -0
  136. data/ext/libsass/src/sass.hpp +147 -0
  137. data/ext/libsass/src/sass2scss.cpp +895 -0
  138. data/ext/libsass/src/sass_context.cpp +741 -0
  139. data/ext/libsass/src/sass_context.hpp +129 -0
  140. data/ext/libsass/src/sass_functions.cpp +210 -0
  141. data/ext/libsass/src/sass_functions.hpp +50 -0
  142. data/ext/libsass/src/sass_values.cpp +362 -0
  143. data/ext/libsass/src/sass_values.hpp +82 -0
  144. data/ext/libsass/src/settings.hpp +19 -0
  145. data/ext/libsass/src/source.cpp +69 -0
  146. data/ext/libsass/src/source.hpp +95 -0
  147. data/ext/libsass/src/source_data.hpp +32 -0
  148. data/ext/libsass/src/source_map.cpp +202 -0
  149. data/ext/libsass/src/source_map.hpp +65 -0
  150. data/ext/libsass/src/stylesheet.cpp +22 -0
  151. data/ext/libsass/src/stylesheet.hpp +57 -0
  152. data/ext/libsass/src/to_value.cpp +114 -0
  153. data/ext/libsass/src/to_value.hpp +46 -0
  154. data/ext/libsass/src/units.cpp +507 -0
  155. data/ext/libsass/src/units.hpp +110 -0
  156. data/ext/libsass/src/utf8/checked.h +336 -0
  157. data/ext/libsass/src/utf8/core.h +332 -0
  158. data/ext/libsass/src/utf8/unchecked.h +235 -0
  159. data/ext/libsass/src/utf8.h +34 -0
  160. data/ext/libsass/src/utf8_string.cpp +104 -0
  161. data/ext/libsass/src/utf8_string.hpp +38 -0
  162. data/ext/libsass/src/util.cpp +723 -0
  163. data/ext/libsass/src/util.hpp +105 -0
  164. data/ext/libsass/src/util_string.cpp +125 -0
  165. data/ext/libsass/src/util_string.hpp +73 -0
  166. data/ext/libsass/src/values.cpp +140 -0
  167. data/ext/libsass/src/values.hpp +12 -0
  168. data/lib/sassc/dependency.rb +17 -0
  169. data/lib/sassc/engine.rb +141 -0
  170. data/lib/sassc/error.rb +37 -0
  171. data/lib/sassc/functions_handler.rb +73 -0
  172. data/lib/sassc/import_handler.rb +50 -0
  173. data/lib/sassc/importer.rb +31 -0
  174. data/lib/sassc/native/native_context_api.rb +147 -0
  175. data/lib/sassc/native/native_functions_api.rb +159 -0
  176. data/lib/sassc/native/sass2scss_api.rb +10 -0
  177. data/lib/sassc/native/sass_input_style.rb +13 -0
  178. data/lib/sassc/native/sass_output_style.rb +12 -0
  179. data/lib/sassc/native/sass_value.rb +97 -0
  180. data/lib/sassc/native/string_list.rb +10 -0
  181. data/lib/sassc/native.rb +65 -0
  182. data/lib/sassc/sass_2_scss.rb +9 -0
  183. data/lib/sassc/script/functions.rb +8 -0
  184. data/lib/sassc/script/value/bool.rb +32 -0
  185. data/lib/sassc/script/value/color.rb +95 -0
  186. data/lib/sassc/script/value/list.rb +136 -0
  187. data/lib/sassc/script/value/map.rb +69 -0
  188. data/lib/sassc/script/value/number.rb +389 -0
  189. data/lib/sassc/script/value/string.rb +96 -0
  190. data/lib/sassc/script/value.rb +137 -0
  191. data/lib/sassc/script/value_conversion/base.rb +13 -0
  192. data/lib/sassc/script/value_conversion/bool.rb +13 -0
  193. data/lib/sassc/script/value_conversion/color.rb +18 -0
  194. data/lib/sassc/script/value_conversion/list.rb +25 -0
  195. data/lib/sassc/script/value_conversion/map.rb +21 -0
  196. data/lib/sassc/script/value_conversion/number.rb +13 -0
  197. data/lib/sassc/script/value_conversion/string.rb +17 -0
  198. data/lib/sassc/script/value_conversion.rb +69 -0
  199. data/lib/sassc/script.rb +17 -0
  200. data/lib/sassc/util/normalized_map.rb +117 -0
  201. data/lib/sassc/util.rb +231 -0
  202. data/lib/sassc/version.rb +5 -0
  203. data/lib/sassc.rb +57 -0
  204. data/sassc.gemspec +69 -0
  205. data/test/custom_importer_test.rb +127 -0
  206. data/test/engine_test.rb +314 -0
  207. data/test/error_test.rb +29 -0
  208. data/test/fixtures/paths.scss +10 -0
  209. data/test/functions_test.rb +340 -0
  210. data/test/native_test.rb +213 -0
  211. data/test/output_style_test.rb +107 -0
  212. data/test/sass_2_scss_test.rb +14 -0
  213. data/test/test_helper.rb +45 -0
  214. metadata +391 -0
@@ -0,0 +1,2965 @@
1
+ // sass.hpp must go before all system headers to get the
2
+ // __EXTENSIONS__ fix on Solaris.
3
+ #include "sass.hpp"
4
+
5
+ #include "parser.hpp"
6
+ #include "color_maps.hpp"
7
+ #include "util_string.hpp"
8
+
9
+ // Notes about delayed: some ast nodes can have delayed evaluation so
10
+ // they can preserve their original semantics if needed. This is most
11
+ // prominently exhibited by the division operation, since it is not
12
+ // only a valid operation, but also a valid css statement (i.e. for
13
+ // fonts, as in `16px/24px`). When parsing lists and expression we
14
+ // unwrap single items from lists and other operations. A nested list
15
+ // must not be delayed, only the items of the first level sometimes
16
+ // are delayed (as with argument lists). To achieve this we need to
17
+ // pass status to the list parser, so this can be set correctly.
18
+ // Another case with delayed values are colors. In compressed mode
19
+ // only processed values get compressed (other are left as written).
20
+
21
+
22
+ namespace Sass {
23
+ using namespace Constants;
24
+ using namespace Prelexer;
25
+
26
+
27
+ Parser::Parser(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent) :
28
+ SourceSpan(source),
29
+ ctx(ctx),
30
+ source(source),
31
+ begin(source->begin()),
32
+ position(source->begin()),
33
+ end(source->end()),
34
+ before_token(0, 0),
35
+ after_token(0, 0),
36
+ pstate(source->getSourceSpan()),
37
+ traces(traces),
38
+ indentation(0),
39
+ nestings(0),
40
+ allow_parent(allow_parent)
41
+ {
42
+ Block_Obj root = SASS_MEMORY_NEW(Block, pstate);
43
+ stack.push_back(Scope::Root);
44
+ block_stack.push_back(root);
45
+ root->is_root(true);
46
+ }
47
+
48
+ void Parser::advanceToNextToken() {
49
+ lex < css_comments >(false);
50
+ // advance to position
51
+ pstate.position += pstate.offset;
52
+ pstate.offset.column = 0;
53
+ pstate.offset.line = 0;
54
+ }
55
+
56
+ SelectorListObj Parser::parse_selector(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent)
57
+ {
58
+ Parser p(source, ctx, traces, allow_parent);
59
+ // ToDo: remap the source-map entries somehow
60
+ return p.parseSelectorList(false);
61
+ }
62
+
63
+ bool Parser::peek_newline(const char* start)
64
+ {
65
+ return peek_linefeed(start ? start : position)
66
+ && ! peek_css<exactly<'{'>>(start);
67
+ }
68
+
69
+ /* main entry point to parse root block */
70
+ Block_Obj Parser::parse()
71
+ {
72
+
73
+ // consume unicode BOM
74
+ read_bom();
75
+
76
+ // scan the input to find invalid utf8 sequences
77
+ const char* it = utf8::find_invalid(position, end);
78
+
79
+ // report invalid utf8
80
+ if (it != end) {
81
+ pstate.position += Offset::init(position, it);
82
+ traces.push_back(Backtrace(pstate));
83
+ throw Exception::InvalidSass(pstate, traces, "Invalid UTF-8 sequence");
84
+ }
85
+
86
+ // create a block AST node to hold children
87
+ Block_Obj root = SASS_MEMORY_NEW(Block, pstate, 0, true);
88
+
89
+ // check seems a bit esoteric but works
90
+ if (ctx.resources.size() == 1) {
91
+ // apply headers only on very first include
92
+ ctx.apply_custom_headers(root, getPath(), pstate);
93
+ }
94
+
95
+ // parse children nodes
96
+ block_stack.push_back(root);
97
+ parse_block_nodes(true);
98
+ block_stack.pop_back();
99
+
100
+ // update final position
101
+ root->update_pstate(pstate);
102
+
103
+ if (position != end) {
104
+ css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");
105
+ }
106
+
107
+ return root;
108
+ }
109
+
110
+
111
+ // convenience function for block parsing
112
+ // will create a new block ad-hoc for you
113
+ // this is the base block parsing function
114
+ Block_Obj Parser::parse_css_block(bool is_root)
115
+ {
116
+
117
+ // parse comments before block
118
+ // lex < optional_css_comments >();
119
+
120
+ // lex mandatory opener or error out
121
+ if (!lex_css < exactly<'{'> >()) {
122
+ css_error("Invalid CSS", " after ", ": expected \"{\", was ");
123
+ }
124
+ // create new block and push to the selector stack
125
+ Block_Obj block = SASS_MEMORY_NEW(Block, pstate, 0, is_root);
126
+ block_stack.push_back(block);
127
+
128
+ if (!parse_block_nodes(is_root)) css_error("Invalid CSS", " after ", ": expected \"}\", was ");
129
+
130
+ if (!lex_css < exactly<'}'> >()) {
131
+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");
132
+ }
133
+
134
+ // update for end position
135
+ // this seems to be done somewhere else
136
+ // but that fixed selector schema issue
137
+ // block->update_pstate(pstate);
138
+
139
+ // parse comments after block
140
+ // lex < optional_css_comments >();
141
+
142
+ block_stack.pop_back();
143
+
144
+ return block;
145
+ }
146
+
147
+ // convenience function for block parsing
148
+ // will create a new block ad-hoc for you
149
+ // also updates the `in_at_root` flag
150
+ Block_Obj Parser::parse_block(bool is_root)
151
+ {
152
+ return parse_css_block(is_root);
153
+ }
154
+
155
+ // the main block parsing function
156
+ // parses stuff between `{` and `}`
157
+ bool Parser::parse_block_nodes(bool is_root)
158
+ {
159
+
160
+ // loop until end of string
161
+ while (position < end) {
162
+
163
+ // we should be able to refactor this
164
+ parse_block_comments();
165
+ lex < css_whitespace >();
166
+
167
+ if (lex < exactly<';'> >()) continue;
168
+ if (peek < end_of_file >()) return true;
169
+ if (peek < exactly<'}'> >()) return true;
170
+
171
+ if (parse_block_node(is_root)) continue;
172
+
173
+ parse_block_comments();
174
+
175
+ if (lex_css < exactly<';'> >()) continue;
176
+ if (peek_css < end_of_file >()) return true;
177
+ if (peek_css < exactly<'}'> >()) return true;
178
+
179
+ // illegal sass
180
+ return false;
181
+ }
182
+ // return success
183
+ return true;
184
+ }
185
+
186
+ // parser for a single node in a block
187
+ // semicolons must be lexed beforehand
188
+ bool Parser::parse_block_node(bool is_root) {
189
+
190
+ Block_Obj block = block_stack.back();
191
+
192
+ parse_block_comments();
193
+
194
+ // throw away white-space
195
+ // includes line comments
196
+ lex < css_whitespace >();
197
+
198
+ Lookahead lookahead_result;
199
+
200
+ // also parse block comments
201
+
202
+ // first parse everything that is allowed in functions
203
+ if (lex < variable >(true)) { block->append(parse_assignment()); }
204
+ else if (lex < kwd_err >(true)) { block->append(parse_error()); }
205
+ else if (lex < kwd_dbg >(true)) { block->append(parse_debug()); }
206
+ else if (lex < kwd_warn >(true)) { block->append(parse_warning()); }
207
+ else if (lex < kwd_if_directive >(true)) { block->append(parse_if_directive()); }
208
+ else if (lex < kwd_for_directive >(true)) { block->append(parse_for_directive()); }
209
+ else if (lex < kwd_each_directive >(true)) { block->append(parse_each_directive()); }
210
+ else if (lex < kwd_while_directive >(true)) { block->append(parse_while_directive()); }
211
+ else if (lex < kwd_return_directive >(true)) { block->append(parse_return_directive()); }
212
+
213
+ // parse imports to process later
214
+ else if (lex < kwd_import >(true)) {
215
+ Scope parent = stack.empty() ? Scope::Rules : stack.back();
216
+ if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) {
217
+ if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20
218
+ error("Import directives may not be used within control directives or mixins.");
219
+ }
220
+ }
221
+ // this puts the parsed doc into sheets
222
+ // import stub will fetch this in expand
223
+ Import_Obj imp = parse_import();
224
+ // if it is a url, we only add the statement
225
+ if (!imp->urls().empty()) block->append(imp);
226
+ // process all resources now (add Import_Stub nodes)
227
+ for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
228
+ block->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));
229
+ }
230
+ }
231
+
232
+ else if (lex < kwd_extend >(true)) {
233
+ Lookahead lookahead = lookahead_for_include(position);
234
+ if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");
235
+ SelectorListObj target;
236
+ if (!lookahead.has_interpolants) {
237
+ LOCAL_FLAG(allow_parent, false);
238
+ auto selector = parseSelectorList(true);
239
+ auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector);
240
+ extender->isOptional(selector && selector->is_optional());
241
+ block->append(extender);
242
+ }
243
+ else {
244
+ LOCAL_FLAG(allow_parent, false);
245
+ auto selector = parse_selector_schema(lookahead.found, true);
246
+ auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector);
247
+ // A schema is not optional yet, check once it is evaluated
248
+ // extender->isOptional(selector && selector->is_optional());
249
+ block->append(extender);
250
+ }
251
+
252
+ }
253
+
254
+ // selector may contain interpolations which need delayed evaluation
255
+ else if (
256
+ !(lookahead_result = lookahead_for_selector(position)).error &&
257
+ !lookahead_result.is_custom_property
258
+ )
259
+ {
260
+ block->append(parse_ruleset(lookahead_result));
261
+ }
262
+
263
+ // parse multiple specific keyword directives
264
+ else if (lex < kwd_media >(true)) { block->append(parseMediaRule()); }
265
+ else if (lex < kwd_at_root >(true)) { block->append(parse_at_root_block()); }
266
+ else if (lex < kwd_include_directive >(true)) { block->append(parse_include_directive()); }
267
+ else if (lex < kwd_content_directive >(true)) { block->append(parse_content_directive()); }
268
+ else if (lex < kwd_supports_directive >(true)) { block->append(parse_supports_directive()); }
269
+ else if (lex < kwd_mixin >(true)) { block->append(parse_definition(Definition::MIXIN)); }
270
+ else if (lex < kwd_function >(true)) { block->append(parse_definition(Definition::FUNCTION)); }
271
+
272
+ // ignore the @charset directive for now
273
+ else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); }
274
+
275
+ else if (lex < exactly < else_kwd >>(true)) { error("Invalid CSS: @else must come after @if"); }
276
+
277
+ // generic at keyword (keep last)
278
+ else if (lex< at_keyword >(true)) { block->append(parse_directive()); }
279
+
280
+ else if (is_root && stack.back() != Scope::AtRoot /* && block->is_root() */) {
281
+ lex< css_whitespace >();
282
+ if (position >= end) return true;
283
+ css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was ");
284
+ }
285
+ // parse a declaration
286
+ else
287
+ {
288
+ // ToDo: how does it handle parse errors?
289
+ // maybe we are expected to parse something?
290
+ Declaration_Obj decl = parse_declaration();
291
+ decl->tabs(indentation);
292
+ block->append(decl);
293
+ // maybe we have a "sub-block"
294
+ if (peek< exactly<'{'> >()) {
295
+ if (decl->is_indented()) ++ indentation;
296
+ // parse a propset that rides on the declaration's property
297
+ stack.push_back(Scope::Properties);
298
+ decl->block(parse_block());
299
+ stack.pop_back();
300
+ if (decl->is_indented()) -- indentation;
301
+ }
302
+ }
303
+ // something matched
304
+ return true;
305
+ }
306
+ // EO parse_block_nodes
307
+
308
+ // parse imports inside the
309
+ Import_Obj Parser::parse_import()
310
+ {
311
+ Import_Obj imp = SASS_MEMORY_NEW(Import, pstate);
312
+ sass::vector<std::pair<sass::string,Function_Call_Obj>> to_import;
313
+ bool first = true;
314
+ do {
315
+ while (lex< block_comment >());
316
+ if (lex< quoted_string >()) {
317
+ to_import.push_back(std::pair<sass::string,Function_Call_Obj>(sass::string(lexed), {}));
318
+ }
319
+ else if (lex< uri_prefix >()) {
320
+ Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);
321
+ Function_Call_Obj result = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), args);
322
+
323
+ if (lex< quoted_string >()) {
324
+ ExpressionObj quoted_url = parse_string();
325
+ args->append(SASS_MEMORY_NEW(Argument, quoted_url->pstate(), quoted_url));
326
+ }
327
+ else if (String_Obj string_url = parse_url_function_argument()) {
328
+ args->append(SASS_MEMORY_NEW(Argument, string_url->pstate(), string_url));
329
+ }
330
+ else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {
331
+ ExpressionObj braced_url = parse_list(); // parse_interpolated_chunk(lexed);
332
+ args->append(SASS_MEMORY_NEW(Argument, braced_url->pstate(), braced_url));
333
+ }
334
+ else {
335
+ error("malformed URL");
336
+ }
337
+ if (!lex< exactly<')'> >()) error("URI is missing ')'");
338
+ to_import.push_back(std::pair<sass::string, Function_Call_Obj>("", result));
339
+ }
340
+ else {
341
+ if (first) error("@import directive requires a url or quoted path");
342
+ else error("expecting another url or quoted path in @import list");
343
+ }
344
+ first = false;
345
+ } while (lex_css< exactly<','> >());
346
+
347
+ if (!peek_css< alternatives< exactly<';'>, exactly<'}'>, end_of_file > >()) {
348
+ List_Obj import_queries = parse_media_queries();
349
+ imp->import_queries(import_queries);
350
+ }
351
+
352
+ for(auto location : to_import) {
353
+ if (location.second) {
354
+ imp->urls().push_back(location.second);
355
+ }
356
+ // check if custom importers want to take over the handling
357
+ else if (!ctx.call_importers(unquote(location.first), getPath(), pstate, imp)) {
358
+ // nobody wants it, so we do our import
359
+ ctx.import_url(imp, location.first, getPath());
360
+ }
361
+ }
362
+
363
+ return imp;
364
+ }
365
+
366
+ Definition_Obj Parser::parse_definition(Definition::Type which_type)
367
+ {
368
+ sass::string which_str(lexed);
369
+ if (!lex< identifier >()) error("invalid name in " + which_str + " definition");
370
+ sass::string name(Util::normalize_underscores(lexed));
371
+ if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))
372
+ { error("Invalid function name \"" + name + "\"."); }
373
+ SourceSpan source_position_of_def = pstate;
374
+ Parameters_Obj params = parse_parameters();
375
+ if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin);
376
+ else stack.push_back(Scope::Function);
377
+ Block_Obj body = parse_block();
378
+ stack.pop_back();
379
+ return SASS_MEMORY_NEW(Definition, source_position_of_def, name, params, body, which_type);
380
+ }
381
+
382
+ Parameters_Obj Parser::parse_parameters()
383
+ {
384
+ Parameters_Obj params = SASS_MEMORY_NEW(Parameters, pstate);
385
+ if (lex_css< exactly<'('> >()) {
386
+ // if there's anything there at all
387
+ if (!peek_css< exactly<')'> >()) {
388
+ do {
389
+ if (peek< exactly<')'> >()) break;
390
+ params->append(parse_parameter());
391
+ } while (lex_css< exactly<','> >());
392
+ }
393
+ if (!lex_css< exactly<')'> >()) {
394
+ css_error("Invalid CSS", " after ", ": expected \")\", was ");
395
+ }
396
+ }
397
+ return params;
398
+ }
399
+
400
+ Parameter_Obj Parser::parse_parameter()
401
+ {
402
+ if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) {
403
+ css_error("Invalid CSS", " after ", ": expected variable (e.g. $foo), was ");
404
+ }
405
+ while (lex< alternatives < spaces, block_comment > >());
406
+ lex < variable >();
407
+ sass::string name(Util::normalize_underscores(lexed));
408
+ SourceSpan pos = pstate;
409
+ ExpressionObj val;
410
+ bool is_rest = false;
411
+ while (lex< alternatives < spaces, block_comment > >());
412
+ if (lex< exactly<':'> >()) { // there's a default value
413
+ while (lex< block_comment >());
414
+ val = parse_space_list();
415
+ }
416
+ else if (lex< exactly< ellipsis > >()) {
417
+ is_rest = true;
418
+ }
419
+ return SASS_MEMORY_NEW(Parameter, pos, name, val, is_rest);
420
+ }
421
+
422
+ Arguments_Obj Parser::parse_arguments()
423
+ {
424
+ Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);
425
+ if (lex_css< exactly<'('> >()) {
426
+ // if there's anything there at all
427
+ if (!peek_css< exactly<')'> >()) {
428
+ do {
429
+ if (peek< exactly<')'> >()) break;
430
+ args->append(parse_argument());
431
+ } while (lex_css< exactly<','> >());
432
+ }
433
+ if (!lex_css< exactly<')'> >()) {
434
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
435
+ }
436
+ }
437
+ return args;
438
+ }
439
+
440
+ Argument_Obj Parser::parse_argument()
441
+ {
442
+ if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) {
443
+ css_error("Invalid CSS", " after ", ": expected \")\", was ");
444
+ }
445
+ if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) {
446
+ position += 2;
447
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
448
+ }
449
+
450
+ Argument_Obj arg;
451
+ if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {
452
+ lex_css< variable >();
453
+ sass::string name(Util::normalize_underscores(lexed));
454
+ SourceSpan p = pstate;
455
+ lex_css< exactly<':'> >();
456
+ ExpressionObj val = parse_space_list();
457
+ arg = SASS_MEMORY_NEW(Argument, p, val, name);
458
+ }
459
+ else {
460
+ bool is_arglist = false;
461
+ bool is_keyword = false;
462
+ ExpressionObj val = parse_space_list();
463
+ List* l = Cast<List>(val);
464
+ if (lex_css< exactly< ellipsis > >()) {
465
+ if (val->concrete_type() == Expression::MAP || (
466
+ (l != NULL && l->separator() == SASS_HASH)
467
+ )) is_keyword = true;
468
+ else is_arglist = true;
469
+ }
470
+ arg = SASS_MEMORY_NEW(Argument, pstate, val, "", is_arglist, is_keyword);
471
+ }
472
+ return arg;
473
+ }
474
+
475
+ Assignment_Obj Parser::parse_assignment()
476
+ {
477
+ sass::string name(Util::normalize_underscores(lexed));
478
+ SourceSpan var_source_position = pstate;
479
+ if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement");
480
+ if (peek_css< alternatives < exactly<';'>, end_of_file > >()) {
481
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
482
+ }
483
+ ExpressionObj val;
484
+ Lookahead lookahead = lookahead_for_value(position);
485
+ if (lookahead.has_interpolants && lookahead.found) {
486
+ val = parse_value_schema(lookahead.found);
487
+ } else {
488
+ val = parse_list();
489
+ }
490
+ bool is_default = false;
491
+ bool is_global = false;
492
+ while (peek< alternatives < default_flag, global_flag > >()) {
493
+ if (lex< default_flag >()) is_default = true;
494
+ else if (lex< global_flag >()) is_global = true;
495
+ }
496
+ return SASS_MEMORY_NEW(Assignment, var_source_position, name, val, is_default, is_global);
497
+ }
498
+
499
+ // a ruleset connects a selector and a block
500
+ StyleRuleObj Parser::parse_ruleset(Lookahead lookahead)
501
+ {
502
+ NESTING_GUARD(nestings);
503
+ // inherit is_root from parent block
504
+ Block_Obj parent = block_stack.back();
505
+ bool is_root = parent && parent->is_root();
506
+ // make sure to move up the the last position
507
+ lex < optional_css_whitespace >(false, true);
508
+ // create the connector object (add parts later)
509
+ StyleRuleObj ruleset = SASS_MEMORY_NEW(StyleRule, pstate);
510
+ // parse selector static or as schema to be evaluated later
511
+ if (lookahead.parsable) {
512
+ ruleset->selector(parseSelectorList(false));
513
+ }
514
+ else {
515
+ SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate);
516
+ auto sc = parse_selector_schema(lookahead.position, false);
517
+ ruleset->schema(sc);
518
+ ruleset->selector(list);
519
+ }
520
+ // then parse the inner block
521
+ stack.push_back(Scope::Rules);
522
+ ruleset->block(parse_block());
523
+ stack.pop_back();
524
+ // update for end position
525
+ ruleset->update_pstate(pstate);
526
+ ruleset->block()->update_pstate(pstate);
527
+ // need this info for sanity checks
528
+ ruleset->is_root(is_root);
529
+ // return AST Node
530
+ return ruleset;
531
+ }
532
+
533
+ // parse a selector schema that will be evaluated in the eval stage
534
+ // uses a string schema internally to do the actual schema handling
535
+ // in the eval stage we will be re-parse it into an actual selector
536
+ Selector_Schema_Obj Parser::parse_selector_schema(const char* end_of_selector, bool chroot)
537
+ {
538
+ NESTING_GUARD(nestings);
539
+ // move up to the start
540
+ lex< optional_spaces >();
541
+ const char* i = position;
542
+ // selector schema re-uses string schema implementation
543
+ String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate);
544
+ // the selector schema is pretty much just a wrapper for the string schema
545
+ Selector_Schema_Obj selector_schema = SASS_MEMORY_NEW(Selector_Schema, pstate, schema);
546
+ selector_schema->connect_parent(chroot == false);
547
+
548
+ // process until end
549
+ while (i < end_of_selector) {
550
+ // try to parse multiple interpolants
551
+ if (const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, end_of_selector)) {
552
+ // accumulate the preceding segment if the position has advanced
553
+ if (i < p) {
554
+ sass::string parsed(i, p);
555
+ String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed);
556
+ pstate.position += Offset(parsed);
557
+ str->update_pstate(pstate);
558
+ schema->append(str);
559
+ }
560
+
561
+ // skip over all nested inner interpolations up to our own delimiter
562
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);
563
+ // check if the interpolation never ends of only contains white-space (error out)
564
+ if (!j || peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) {
565
+ position = p+2;
566
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
567
+ }
568
+ // pass inner expression to the parser to resolve nested interpolations
569
+ LocalOption<const char*> partEnd(end, j);
570
+ LocalOption<const char*> partBeg(position, p + 2);
571
+ ExpressionObj interpolant = parse_list();
572
+ // set status on the list expression
573
+ interpolant->is_interpolant(true);
574
+ // schema->has_interpolants(true);
575
+ // add to the string schema
576
+ schema->append(interpolant);
577
+ // advance parser state
578
+ pstate.position.add(p+2, j);
579
+ // advance position
580
+ i = j;
581
+ }
582
+ // no more interpolants have been found
583
+ // add the last segment if there is one
584
+ else {
585
+ // make sure to add the last bits of the string up to the end (if any)
586
+ if (i < end_of_selector) {
587
+ sass::string parsed(i, end_of_selector);
588
+ String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed);
589
+ pstate.position += Offset(parsed);
590
+ str->update_pstate(pstate);
591
+ i = end_of_selector;
592
+ schema->append(str);
593
+ }
594
+ // exit loop
595
+ }
596
+ }
597
+ // EO until eos
598
+
599
+ // update position
600
+ position = i;
601
+
602
+ // update for end position
603
+ selector_schema->update_pstate(pstate);
604
+ schema->update_pstate(pstate);
605
+
606
+ after_token = before_token = pstate.position;
607
+
608
+ // return parsed result
609
+ return selector_schema.detach();
610
+ }
611
+ // EO parse_selector_schema
612
+
613
+ void Parser::parse_charset_directive()
614
+ {
615
+ lex <
616
+ sequence <
617
+ quoted_string,
618
+ optional_spaces,
619
+ exactly <';'>
620
+ >
621
+ >();
622
+ }
623
+
624
+ // called after parsing `kwd_include_directive`
625
+ Mixin_Call_Obj Parser::parse_include_directive()
626
+ {
627
+ // lex identifier into `lexed` var
628
+ lex_identifier(); // may error out
629
+ // normalize underscores to hyphens
630
+ sass::string name(Util::normalize_underscores(lexed));
631
+ // create the initial mixin call object
632
+ Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, Arguments_Obj{});
633
+ // parse mandatory arguments
634
+ call->arguments(parse_arguments());
635
+ // parse using and optional block parameters
636
+ bool has_parameters = lex< kwd_using >() != nullptr;
637
+
638
+ if (has_parameters) {
639
+ if (!peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \"(\", was ");
640
+ } else {
641
+ if (peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \";\", was ");
642
+ }
643
+
644
+ if (has_parameters) call->block_parameters(parse_parameters());
645
+
646
+ // parse optional block
647
+ if (peek < exactly <'{'> >()) {
648
+ call->block(parse_block());
649
+ }
650
+ else if (has_parameters) {
651
+ css_error("Invalid CSS", " after ", ": expected \"{\", was ");
652
+ }
653
+ // return ast node
654
+ return call.detach();
655
+ }
656
+ // EO parse_include_directive
657
+
658
+
659
+ SimpleSelectorObj Parser::parse_simple_selector()
660
+ {
661
+ lex < css_comments >(false);
662
+ if (lex< class_name >()) {
663
+ return SASS_MEMORY_NEW(ClassSelector, pstate, lexed);
664
+ }
665
+ else if (lex< id_name >()) {
666
+ return SASS_MEMORY_NEW(IDSelector, pstate, lexed);
667
+ }
668
+ else if (lex< alternatives < variable, number, static_reference_combinator > >()) {
669
+ return SASS_MEMORY_NEW(TypeSelector, pstate, lexed);
670
+ }
671
+ else if (peek< pseudo_not >()) {
672
+ return parse_negated_selector2();
673
+ }
674
+ else if (peek< re_pseudo_selector >()) {
675
+ return parse_pseudo_selector();
676
+ }
677
+ else if (peek< exactly<':'> >()) {
678
+ return parse_pseudo_selector();
679
+ }
680
+ else if (lex < exactly<'['> >()) {
681
+ return parse_attribute_selector();
682
+ }
683
+ else if (lex< placeholder >()) {
684
+ return SASS_MEMORY_NEW(PlaceholderSelector, pstate, lexed);
685
+ }
686
+ else {
687
+ css_error("Invalid CSS", " after ", ": expected selector, was ");
688
+ }
689
+ // failed
690
+ return {};
691
+ }
692
+
693
+ PseudoSelectorObj Parser::parse_negated_selector2()
694
+ {
695
+ lex< pseudo_not >();
696
+ sass::string name(lexed);
697
+ SourceSpan nsource_position = pstate;
698
+ SelectorListObj negated = parseSelectorList(true);
699
+ if (!lex< exactly<')'> >()) {
700
+ error("negated selector is missing ')'");
701
+ }
702
+ name.erase(name.size() - 1);
703
+
704
+ PseudoSelector* sel = SASS_MEMORY_NEW(PseudoSelector, nsource_position, name.substr(1));
705
+ sel->selector(negated);
706
+ return sel;
707
+ }
708
+
709
+ // Helper to clean binominal string
710
+ bool BothAreSpaces(char lhs, char rhs) { return isspace(lhs) && isspace(rhs); }
711
+
712
+ // a pseudo selector often starts with one or two colons
713
+ // it can contain more selectors inside parentheses
714
+ SimpleSelectorObj Parser::parse_pseudo_selector() {
715
+
716
+ // Lex one or two colon characters
717
+ if (lex<pseudo_prefix>()) {
718
+ sass::string colons(lexed);
719
+ // Check if it is a pseudo element
720
+ bool element = colons.size() == 2;
721
+
722
+ if (lex< sequence<
723
+ // we keep the space within the name, strange enough
724
+ // ToDo: refactor output to schedule the space for it
725
+ // or do we really want to keep the real white-space?
726
+ sequence< identifier, optional < block_comment >, exactly<'('> >
727
+ > >())
728
+ {
729
+
730
+ sass::string name(lexed);
731
+ name.erase(name.size() - 1);
732
+ SourceSpan p = pstate;
733
+
734
+ // specially parse nth-child pseudo selectors
735
+ if (lex_css < sequence < binomial, word_boundary >>()) {
736
+ sass::string parsed(lexed); // always compacting binominals (as dart-sass)
737
+ parsed.erase(std::unique(parsed.begin(), parsed.end(), BothAreSpaces), parsed.end());
738
+ String_Constant_Obj arg = SASS_MEMORY_NEW(String_Constant, pstate, parsed);
739
+ PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element);
740
+ if (lex < sequence < css_whitespace, insensitive < of_kwd >>>(false)) {
741
+ pseudo->selector(parseSelectorList(true));
742
+ }
743
+ pseudo->argument(arg);
744
+ if (lex_css< exactly<')'> >()) {
745
+ return pseudo;
746
+ }
747
+ }
748
+ else {
749
+ if (peek_css< exactly<')'>>() && Util::equalsLiteral("nth-", name.substr(0, 4))) {
750
+ css_error("Invalid CSS", " after ", ": expected An+B expression, was ");
751
+ }
752
+
753
+ sass::string unvendored = Util::unvendor(name);
754
+
755
+ if (unvendored == "not" || unvendored == "matches" || unvendored == "current" || unvendored == "any" || unvendored == "has" || unvendored == "host" || unvendored == "host-context" || unvendored == "slotted") {
756
+ if (SelectorListObj wrapped = parseSelectorList(true)) {
757
+ if (wrapped && lex_css< exactly<')'> >()) {
758
+ PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element);
759
+ pseudo->selector(wrapped);
760
+ return pseudo;
761
+ }
762
+ }
763
+ } else {
764
+ String_Schema_Obj arg = parse_css_variable_value();
765
+ PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element);
766
+ pseudo->argument(arg);
767
+
768
+ if (lex_css< exactly<')'> >()) {
769
+ return pseudo;
770
+ }
771
+ }
772
+ }
773
+
774
+ }
775
+ // EO if pseudo selector
776
+
777
+ else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {
778
+ return SASS_MEMORY_NEW(PseudoSelector, pstate, lexed, element);
779
+ }
780
+ else if (lex < pseudo_prefix >()) {
781
+ css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");
782
+ }
783
+
784
+ }
785
+ else {
786
+ lex < identifier >(); // needed for error message?
787
+ css_error("Invalid CSS", " after ", ": expected selector, was ");
788
+ }
789
+
790
+
791
+ css_error("Invalid CSS", " after ", ": expected \")\", was ");
792
+
793
+ // unreachable statement
794
+ return {};
795
+ }
796
+
797
+ const char* Parser::re_attr_sensitive_close(const char* src)
798
+ {
799
+ return alternatives < exactly<']'>, exactly<'/'> >(src);
800
+ }
801
+
802
+ const char* Parser::re_attr_insensitive_close(const char* src)
803
+ {
804
+ return sequence < insensitive<'i'>, re_attr_sensitive_close >(src);
805
+ }
806
+
807
+ AttributeSelectorObj Parser::parse_attribute_selector()
808
+ {
809
+ SourceSpan p = pstate;
810
+ if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector");
811
+ sass::string name(lexed);
812
+ if (lex_css< re_attr_sensitive_close >()) {
813
+ return SASS_MEMORY_NEW(AttributeSelector, p, name, "", String_Obj{});
814
+ }
815
+ else if (lex_css< re_attr_insensitive_close >()) {
816
+ char modifier = lexed.begin[0];
817
+ return SASS_MEMORY_NEW(AttributeSelector, p, name, "", String_Obj{}, modifier);
818
+ }
819
+ if (!lex_css< alternatives< exact_match, class_match, dash_match,
820
+ prefix_match, suffix_match, substring_match > >()) {
821
+ error("invalid operator in attribute selector for " + name);
822
+ }
823
+ sass::string matcher(lexed);
824
+
825
+ String_Obj value;
826
+ if (lex_css< identifier >()) {
827
+ value = SASS_MEMORY_NEW(String_Constant, p, lexed);
828
+ }
829
+ else if (lex_css< quoted_string >()) {
830
+ value = parse_interpolated_chunk(lexed, true); // needed!
831
+ }
832
+ else {
833
+ error("expected a string constant or identifier in attribute selector for " + name);
834
+ }
835
+
836
+ if (lex_css< re_attr_sensitive_close >()) {
837
+ return SASS_MEMORY_NEW(AttributeSelector, p, name, matcher, value, 0);
838
+ }
839
+ else if (lex_css< re_attr_insensitive_close >()) {
840
+ char modifier = lexed.begin[0];
841
+ return SASS_MEMORY_NEW(AttributeSelector, p, name, matcher, value, modifier);
842
+ }
843
+ error("unterminated attribute selector for " + name);
844
+ return {}; // to satisfy compilers (error must not return)
845
+ }
846
+
847
+ /* parse block comment and add to block */
848
+ void Parser::parse_block_comments(bool store)
849
+ {
850
+ Block_Obj block = block_stack.back();
851
+
852
+ while (lex< block_comment >()) {
853
+ bool is_important = lexed.begin[2] == '!';
854
+ // flag on second param is to skip loosely over comments
855
+ String_Obj contents = parse_interpolated_chunk(lexed, true, false);
856
+ if (store) block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important));
857
+ }
858
+ }
859
+
860
+ Declaration_Obj Parser::parse_declaration() {
861
+ String_Obj prop;
862
+ bool is_custom_property = false;
863
+ if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
864
+ const sass::string property(lexed);
865
+ is_custom_property = property.compare(0, 2, "--") == 0;
866
+ prop = parse_identifier_schema();
867
+ }
868
+ else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {
869
+ const sass::string property(lexed);
870
+ is_custom_property = property.compare(0, 2, "--") == 0;
871
+ prop = SASS_MEMORY_NEW(String_Constant, pstate, lexed);
872
+ }
873
+ else {
874
+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");
875
+ }
876
+ bool is_indented = true;
877
+ const sass::string property(lexed);
878
+ if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + escape_string(property) + "\" must be followed by a ':'");
879
+ if (!is_custom_property && match< sequence< optional_css_comments, exactly<';'> > >()) error("style declaration must contain a value");
880
+ if (match< sequence< optional_css_comments, exactly<'{'> > >()) is_indented = false; // don't indent if value is empty
881
+ if (is_custom_property) {
882
+ return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_css_variable_value(), false, true);
883
+ }
884
+ lex < css_comments >(false);
885
+ if (peek_css< static_value >()) {
886
+ return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_static_value()/*, lex<kwd_important>()*/);
887
+ }
888
+ else {
889
+ ExpressionObj value;
890
+ Lookahead lookahead = lookahead_for_value(position);
891
+ if (lookahead.found) {
892
+ if (lookahead.has_interpolants) {
893
+ value = parse_value_schema(lookahead.found);
894
+ } else {
895
+ value = parse_list(DELAYED);
896
+ }
897
+ }
898
+ else {
899
+ value = parse_list(DELAYED);
900
+ if (List* list = Cast<List>(value)) {
901
+ if (!list->is_bracketed() && list->length() == 0 && !peek< exactly <'{'> >()) {
902
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
903
+ }
904
+ }
905
+ }
906
+ lex < css_comments >(false);
907
+ Declaration_Obj decl = SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, value/*, lex<kwd_important>()*/);
908
+ decl->is_indented(is_indented);
909
+ decl->update_pstate(pstate);
910
+ return decl;
911
+ }
912
+ }
913
+
914
+ ExpressionObj Parser::parse_map()
915
+ {
916
+ NESTING_GUARD(nestings);
917
+ ExpressionObj key = parse_list();
918
+ List_Obj map = SASS_MEMORY_NEW(List, pstate, 0, SASS_HASH);
919
+
920
+ // it's not a map so return the lexed value as a list value
921
+ if (!lex_css< exactly<':'> >())
922
+ { return key; }
923
+
924
+ List_Obj l = Cast<List>(key);
925
+ if (l && l->separator() == SASS_COMMA) {
926
+ css_error("Invalid CSS", " after ", ": expected \")\", was ");
927
+ }
928
+
929
+ ExpressionObj value = parse_space_list();
930
+
931
+ map->append(key);
932
+ map->append(value);
933
+
934
+ while (lex_css< exactly<','> >())
935
+ {
936
+ // allow trailing commas - #495
937
+ if (peek_css< exactly<')'> >(position))
938
+ { break; }
939
+
940
+ key = parse_space_list();
941
+
942
+ if (!(lex< exactly<':'> >()))
943
+ { css_error("Invalid CSS", " after ", ": expected \":\", was "); }
944
+
945
+ value = parse_space_list();
946
+
947
+ map->append(key);
948
+ map->append(value);
949
+ }
950
+
951
+ SourceSpan ps = map->pstate();
952
+ ps.offset = pstate.position - ps.position + pstate.offset;
953
+ map->pstate(ps);
954
+
955
+ return map;
956
+ }
957
+
958
+ ExpressionObj Parser::parse_bracket_list()
959
+ {
960
+ NESTING_GUARD(nestings);
961
+ // check if we have an empty list
962
+ // return the empty list as such
963
+ if (peek_css< list_terminator >(position))
964
+ {
965
+ // return an empty list (nothing to delay)
966
+ return SASS_MEMORY_NEW(List, pstate, 0, SASS_SPACE, false, true);
967
+ }
968
+
969
+ bool has_paren = peek_css< exactly<'('> >() != NULL;
970
+
971
+ // now try to parse a space list
972
+ ExpressionObj list = parse_space_list();
973
+ // if it's a singleton, return it (don't wrap it)
974
+ if (!peek_css< exactly<','> >(position)) {
975
+ List_Obj l = Cast<List>(list);
976
+ if (!l || l->is_bracketed() || has_paren) {
977
+ List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 1, SASS_SPACE, false, true);
978
+ bracketed_list->append(list);
979
+ return bracketed_list;
980
+ }
981
+ l->is_bracketed(true);
982
+ return l;
983
+ }
984
+
985
+ // if we got so far, we actually do have a comma list
986
+ List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA, false, true);
987
+ // wrap the first expression
988
+ bracketed_list->append(list);
989
+
990
+ while (lex_css< exactly<','> >())
991
+ {
992
+ // check for abort condition
993
+ if (peek_css< list_terminator >(position)
994
+ ) { break; }
995
+ // otherwise add another expression
996
+ bracketed_list->append(parse_space_list());
997
+ }
998
+ // return the list
999
+ return bracketed_list;
1000
+ }
1001
+
1002
+ // parse list returns either a space separated list,
1003
+ // a comma separated list or any bare expression found.
1004
+ // so to speak: we unwrap items from lists if possible here!
1005
+ ExpressionObj Parser::parse_list(bool delayed)
1006
+ {
1007
+ NESTING_GUARD(nestings);
1008
+ return parse_comma_list(delayed);
1009
+ }
1010
+
1011
+ // will return singletons unwrapped
1012
+ ExpressionObj Parser::parse_comma_list(bool delayed)
1013
+ {
1014
+ NESTING_GUARD(nestings);
1015
+ // check if we have an empty list
1016
+ // return the empty list as such
1017
+ if (peek_css< list_terminator >(position))
1018
+ {
1019
+ // return an empty list (nothing to delay)
1020
+ return SASS_MEMORY_NEW(List, pstate, 0);
1021
+ }
1022
+
1023
+ // now try to parse a space list
1024
+ ExpressionObj list = parse_space_list();
1025
+ // if it's a singleton, return it (don't wrap it)
1026
+ if (!peek_css< exactly<','> >(position)) {
1027
+ // set_delay doesn't apply to list children
1028
+ // so this will only undelay single values
1029
+ if (!delayed) list->set_delayed(false);
1030
+ return list;
1031
+ }
1032
+
1033
+ // if we got so far, we actually do have a comma list
1034
+ List_Obj comma_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA);
1035
+ // wrap the first expression
1036
+ comma_list->append(list);
1037
+
1038
+ while (lex_css< exactly<','> >())
1039
+ {
1040
+ // check for abort condition
1041
+ if (peek_css< list_terminator >(position)
1042
+ ) { break; }
1043
+ // otherwise add another expression
1044
+ comma_list->append(parse_space_list());
1045
+ }
1046
+ // return the list
1047
+ return comma_list;
1048
+ }
1049
+ // EO parse_comma_list
1050
+
1051
+ // will return singletons unwrapped
1052
+ ExpressionObj Parser::parse_space_list()
1053
+ {
1054
+ NESTING_GUARD(nestings);
1055
+ ExpressionObj disj1 = parse_disjunction();
1056
+ // if it's a singleton, return it (don't wrap it)
1057
+ if (peek_css< space_list_terminator >(position)
1058
+ ) {
1059
+ return disj1; }
1060
+
1061
+ List_Obj space_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_SPACE);
1062
+ space_list->append(disj1);
1063
+
1064
+ while (
1065
+ !(peek_css< space_list_terminator >(position)) &&
1066
+ peek_css< optional_css_whitespace >() != end
1067
+ ) {
1068
+ // the space is parsed implicitly?
1069
+ space_list->append(parse_disjunction());
1070
+ }
1071
+ // return the list
1072
+ return space_list;
1073
+ }
1074
+ // EO parse_space_list
1075
+
1076
+ // parse logical OR operation
1077
+ ExpressionObj Parser::parse_disjunction()
1078
+ {
1079
+ NESTING_GUARD(nestings);
1080
+ advanceToNextToken();
1081
+ SourceSpan state(pstate);
1082
+ // parse the left hand side conjunction
1083
+ ExpressionObj conj = parse_conjunction();
1084
+ // parse multiple right hand sides
1085
+ sass::vector<ExpressionObj> operands;
1086
+ while (lex_css< kwd_or >())
1087
+ operands.push_back(parse_conjunction());
1088
+ // if it's a singleton, return it directly
1089
+ if (operands.size() == 0) return conj;
1090
+ // fold all operands into one binary expression
1091
+ ExpressionObj ex = fold_operands(conj, operands, { Sass_OP::OR });
1092
+ state.offset = pstate.position - state.position + pstate.offset;
1093
+ ex->pstate(state);
1094
+ return ex;
1095
+ }
1096
+ // EO parse_disjunction
1097
+
1098
+ // parse logical AND operation
1099
+ ExpressionObj Parser::parse_conjunction()
1100
+ {
1101
+ NESTING_GUARD(nestings);
1102
+ advanceToNextToken();
1103
+ SourceSpan state(pstate);
1104
+ // parse the left hand side relation
1105
+ ExpressionObj rel = parse_relation();
1106
+ // parse multiple right hand sides
1107
+ sass::vector<ExpressionObj> operands;
1108
+ while (lex_css< kwd_and >()) {
1109
+ operands.push_back(parse_relation());
1110
+ }
1111
+ // if it's a singleton, return it directly
1112
+ if (operands.size() == 0) return rel;
1113
+ // fold all operands into one binary expression
1114
+ ExpressionObj ex = fold_operands(rel, operands, { Sass_OP::AND });
1115
+ state.offset = pstate.position - state.position + pstate.offset;
1116
+ ex->pstate(state);
1117
+ return ex;
1118
+ }
1119
+ // EO parse_conjunction
1120
+
1121
+ // parse comparison operations
1122
+ ExpressionObj Parser::parse_relation()
1123
+ {
1124
+ NESTING_GUARD(nestings);
1125
+ advanceToNextToken();
1126
+ SourceSpan state(pstate);
1127
+ // parse the left hand side expression
1128
+ ExpressionObj lhs = parse_expression();
1129
+ sass::vector<ExpressionObj> operands;
1130
+ sass::vector<Operand> operators;
1131
+ // if it's a singleton, return it (don't wrap it)
1132
+ while (peek< alternatives <
1133
+ kwd_eq,
1134
+ kwd_neq,
1135
+ kwd_gte,
1136
+ kwd_gt,
1137
+ kwd_lte,
1138
+ kwd_lt
1139
+ > >(position))
1140
+ {
1141
+ // is directly adjancent to expression?
1142
+ bool left_ws = peek < css_comments >() != NULL;
1143
+ // parse the operator
1144
+ enum Sass_OP op
1145
+ = lex<kwd_eq>() ? Sass_OP::EQ
1146
+ : lex<kwd_neq>() ? Sass_OP::NEQ
1147
+ : lex<kwd_gte>() ? Sass_OP::GTE
1148
+ : lex<kwd_lte>() ? Sass_OP::LTE
1149
+ : lex<kwd_gt>() ? Sass_OP::GT
1150
+ : lex<kwd_lt>() ? Sass_OP::LT
1151
+ // we checked the possibilities on top of fn
1152
+ : Sass_OP::EQ;
1153
+ // is directly adjacent to expression?
1154
+ bool right_ws = peek < css_comments >() != NULL;
1155
+ operators.push_back({ op, left_ws, right_ws });
1156
+ operands.push_back(parse_expression());
1157
+ }
1158
+ // we are called recursively for list, so we first
1159
+ // fold inner binary expression which has delayed
1160
+ // correctly set to zero. After folding we also unwrap
1161
+ // single nested items. So we cannot set delay on the
1162
+ // returned result here, as we have lost nestings ...
1163
+ ExpressionObj ex = fold_operands(lhs, operands, operators);
1164
+ state.offset = pstate.position - state.position + pstate.offset;
1165
+ ex->pstate(state);
1166
+ return ex;
1167
+ }
1168
+ // parse_relation
1169
+
1170
+ // parse expression valid for operations
1171
+ // called from parse_relation
1172
+ // called from parse_for_directive
1173
+ // called from parse_media_expression
1174
+ // parse addition and subtraction operations
1175
+ ExpressionObj Parser::parse_expression()
1176
+ {
1177
+ NESTING_GUARD(nestings);
1178
+ advanceToNextToken();
1179
+ SourceSpan state(pstate);
1180
+ // parses multiple add and subtract operations
1181
+ // NOTE: make sure that identifiers starting with
1182
+ // NOTE: dashes do NOT count as subtract operation
1183
+ ExpressionObj lhs = parse_operators();
1184
+ // if it's a singleton, return it (don't wrap it)
1185
+ if (!(peek_css< exactly<'+'> >(position) ||
1186
+ // condition is a bit mysterious, but some combinations should not be counted as operations
1187
+ (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) ||
1188
+ (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) ||
1189
+ peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position))
1190
+ { return lhs; }
1191
+
1192
+ sass::vector<ExpressionObj> operands;
1193
+ sass::vector<Operand> operators;
1194
+ bool left_ws = peek < css_comments >() != NULL;
1195
+ while (
1196
+ lex_css< exactly<'+'> >() ||
1197
+
1198
+ (
1199
+ ! peek_css< sequence < zero_plus < exactly <'-' > >, identifier > >(position)
1200
+ && lex_css< sequence< negate< digit >, exactly<'-'> > >()
1201
+ )
1202
+
1203
+ ) {
1204
+
1205
+ bool right_ws = peek < css_comments >() != NULL;
1206
+ operators.push_back({ lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB, left_ws, right_ws });
1207
+ operands.push_back(parse_operators());
1208
+ left_ws = peek < css_comments >() != NULL;
1209
+ }
1210
+
1211
+ if (operands.size() == 0) return lhs;
1212
+ ExpressionObj ex = fold_operands(lhs, operands, operators);
1213
+ state.offset = pstate.position - state.position + pstate.offset;
1214
+ ex->pstate(state);
1215
+ return ex;
1216
+ }
1217
+
1218
+ // parse addition and subtraction operations
1219
+ ExpressionObj Parser::parse_operators()
1220
+ {
1221
+ NESTING_GUARD(nestings);
1222
+ advanceToNextToken();
1223
+ SourceSpan state(pstate);
1224
+ ExpressionObj factor = parse_factor();
1225
+ // if it's a singleton, return it (don't wrap it)
1226
+ sass::vector<ExpressionObj> operands; // factors
1227
+ sass::vector<Operand> operators; // ops
1228
+ // lex operations to apply to lhs
1229
+ const char* left_ws = peek < css_comments >();
1230
+ while (lex_css< class_char< static_ops > >()) {
1231
+ const char* right_ws = peek < css_comments >();
1232
+ switch(*lexed.begin) {
1233
+ case '*': operators.push_back({ Sass_OP::MUL, left_ws != 0, right_ws != 0 }); break;
1234
+ case '/': operators.push_back({ Sass_OP::DIV, left_ws != 0, right_ws != 0 }); break;
1235
+ case '%': operators.push_back({ Sass_OP::MOD, left_ws != 0, right_ws != 0 }); break;
1236
+ default: throw std::runtime_error("unknown static op parsed");
1237
+ }
1238
+ operands.push_back(parse_factor());
1239
+ left_ws = peek < css_comments >();
1240
+ }
1241
+ // operands and operators to binary expression
1242
+ ExpressionObj ex = fold_operands(factor, operands, operators);
1243
+ state.offset = pstate.position - state.position + pstate.offset;
1244
+ ex->pstate(state);
1245
+ return ex;
1246
+ }
1247
+ // EO parse_operators
1248
+
1249
+
1250
+ // called from parse_operators
1251
+ // called from parse_value_schema
1252
+ ExpressionObj Parser::parse_factor()
1253
+ {
1254
+ NESTING_GUARD(nestings);
1255
+ lex < css_comments >(false);
1256
+ if (lex_css< exactly<'('> >()) {
1257
+ // parse_map may return a list
1258
+ ExpressionObj value = parse_map();
1259
+ // lex the expected closing parenthesis
1260
+ if (!lex_css< exactly<')'> >()) error("unclosed parenthesis");
1261
+ // expression can be evaluated
1262
+ return value;
1263
+ }
1264
+ else if (lex_css< exactly<'['> >()) {
1265
+ // explicit bracketed
1266
+ ExpressionObj value = parse_bracket_list();
1267
+ // lex the expected closing square bracket
1268
+ if (!lex_css< exactly<']'> >()) error("unclosed squared bracket");
1269
+ return value;
1270
+ }
1271
+ // string may be interpolated
1272
+ // if (lex< quoted_string >()) {
1273
+ // return &parse_string();
1274
+ // }
1275
+ else if (peek< ie_property >()) {
1276
+ return parse_ie_property();
1277
+ }
1278
+ else if (peek< ie_keyword_arg >()) {
1279
+ return parse_ie_keyword_arg();
1280
+ }
1281
+ else if (peek< sequence < calc_fn_call, exactly <'('> > >()) {
1282
+ return parse_calc_function();
1283
+ }
1284
+ else if (lex < functional_schema >()) {
1285
+ return parse_function_call_schema();
1286
+ }
1287
+ else if (lex< identifier_schema >()) {
1288
+ String_Obj string = parse_identifier_schema();
1289
+ if (String_Schema* schema = Cast<String_Schema>(string)) {
1290
+ if (lex < exactly < '(' > >()) {
1291
+ schema->append(parse_list());
1292
+ lex < exactly < ')' > >();
1293
+ }
1294
+ }
1295
+ return string;
1296
+ }
1297
+ else if (peek< sequence< uri_prefix, W, real_uri_value > >()) {
1298
+ return parse_url_function_string();
1299
+ }
1300
+ else if (peek< re_functional >()) {
1301
+ return parse_function_call();
1302
+ }
1303
+ else if (lex< exactly<'+'> >()) {
1304
+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());
1305
+ if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());
1306
+ return ex;
1307
+ }
1308
+ else if (lex< exactly<'-'> >()) {
1309
+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());
1310
+ if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());
1311
+ return ex;
1312
+ }
1313
+ else if (lex< exactly<'/'> >()) {
1314
+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor());
1315
+ if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());
1316
+ return ex;
1317
+ }
1318
+ else if (lex< sequence< kwd_not > >()) {
1319
+ Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor());
1320
+ if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed());
1321
+ return ex;
1322
+ }
1323
+ else {
1324
+ return parse_value();
1325
+ }
1326
+ }
1327
+
1328
+ bool number_has_zero(const sass::string& parsed)
1329
+ {
1330
+ size_t L = parsed.length();
1331
+ return !( (L > 0 && parsed.substr(0, 1) == ".") ||
1332
+ (L > 1 && parsed.substr(0, 2) == "0.") ||
1333
+ (L > 1 && parsed.substr(0, 2) == "-.") ||
1334
+ (L > 2 && parsed.substr(0, 3) == "-0.") );
1335
+ }
1336
+
1337
+ Number* Parser::lexed_number(const SourceSpan& pstate, const sass::string& parsed)
1338
+ {
1339
+ Number* nr = SASS_MEMORY_NEW(Number,
1340
+ pstate,
1341
+ sass_strtod(parsed.c_str()),
1342
+ "",
1343
+ number_has_zero(parsed));
1344
+ nr->is_interpolant(false);
1345
+ nr->is_delayed(true);
1346
+ return nr;
1347
+ }
1348
+
1349
+ Number* Parser::lexed_percentage(const SourceSpan& pstate, const sass::string& parsed)
1350
+ {
1351
+ Number* nr = SASS_MEMORY_NEW(Number,
1352
+ pstate,
1353
+ sass_strtod(parsed.c_str()),
1354
+ "%",
1355
+ true);
1356
+ nr->is_interpolant(false);
1357
+ nr->is_delayed(true);
1358
+ return nr;
1359
+ }
1360
+
1361
+ Number* Parser::lexed_dimension(const SourceSpan& pstate, const sass::string& parsed)
1362
+ {
1363
+ size_t L = parsed.length();
1364
+ size_t num_pos = parsed.find_first_not_of(" \n\r\t");
1365
+ if (num_pos == sass::string::npos) num_pos = L;
1366
+ size_t unit_pos = parsed.find_first_not_of("-+0123456789.", num_pos);
1367
+ if (parsed[unit_pos] == 'e' && is_number(parsed[unit_pos+1]) ) {
1368
+ unit_pos = parsed.find_first_not_of("-+0123456789.", ++ unit_pos);
1369
+ }
1370
+ if (unit_pos == sass::string::npos) unit_pos = L;
1371
+ const sass::string& num = parsed.substr(num_pos, unit_pos - num_pos);
1372
+ Number* nr = SASS_MEMORY_NEW(Number,
1373
+ pstate,
1374
+ sass_strtod(num.c_str()),
1375
+ Token(number(parsed.c_str())),
1376
+ number_has_zero(parsed));
1377
+ nr->is_interpolant(false);
1378
+ nr->is_delayed(true);
1379
+ return nr;
1380
+ }
1381
+
1382
+ Value* Parser::lexed_hex_color(const SourceSpan& pstate, const sass::string& parsed)
1383
+ {
1384
+ Color_RGBA* color = NULL;
1385
+ if (parsed[0] != '#') {
1386
+ return SASS_MEMORY_NEW(String_Quoted, pstate, parsed);
1387
+ }
1388
+ // chop off the '#'
1389
+ sass::string hext(parsed.substr(1));
1390
+ if (parsed.length() == 4) {
1391
+ sass::string r(2, parsed[1]);
1392
+ sass::string g(2, parsed[2]);
1393
+ sass::string b(2, parsed[3]);
1394
+ color = SASS_MEMORY_NEW(Color_RGBA,
1395
+ pstate,
1396
+ static_cast<double>(strtol(r.c_str(), NULL, 16)),
1397
+ static_cast<double>(strtol(g.c_str(), NULL, 16)),
1398
+ static_cast<double>(strtol(b.c_str(), NULL, 16)),
1399
+ 1, // alpha channel
1400
+ parsed);
1401
+ }
1402
+ else if (parsed.length() == 5) {
1403
+ sass::string r(2, parsed[1]);
1404
+ sass::string g(2, parsed[2]);
1405
+ sass::string b(2, parsed[3]);
1406
+ sass::string a(2, parsed[4]);
1407
+ color = SASS_MEMORY_NEW(Color_RGBA,
1408
+ pstate,
1409
+ static_cast<double>(strtol(r.c_str(), NULL, 16)),
1410
+ static_cast<double>(strtol(g.c_str(), NULL, 16)),
1411
+ static_cast<double>(strtol(b.c_str(), NULL, 16)),
1412
+ static_cast<double>(strtol(a.c_str(), NULL, 16)) / 255,
1413
+ parsed);
1414
+ }
1415
+ else if (parsed.length() == 7) {
1416
+ sass::string r(parsed.substr(1,2));
1417
+ sass::string g(parsed.substr(3,2));
1418
+ sass::string b(parsed.substr(5,2));
1419
+ color = SASS_MEMORY_NEW(Color_RGBA,
1420
+ pstate,
1421
+ static_cast<double>(strtol(r.c_str(), NULL, 16)),
1422
+ static_cast<double>(strtol(g.c_str(), NULL, 16)),
1423
+ static_cast<double>(strtol(b.c_str(), NULL, 16)),
1424
+ 1, // alpha channel
1425
+ parsed);
1426
+ }
1427
+ else if (parsed.length() == 9) {
1428
+ sass::string r(parsed.substr(1,2));
1429
+ sass::string g(parsed.substr(3,2));
1430
+ sass::string b(parsed.substr(5,2));
1431
+ sass::string a(parsed.substr(7,2));
1432
+ color = SASS_MEMORY_NEW(Color_RGBA,
1433
+ pstate,
1434
+ static_cast<double>(strtol(r.c_str(), NULL, 16)),
1435
+ static_cast<double>(strtol(g.c_str(), NULL, 16)),
1436
+ static_cast<double>(strtol(b.c_str(), NULL, 16)),
1437
+ static_cast<double>(strtol(a.c_str(), NULL, 16)) / 255,
1438
+ parsed);
1439
+ }
1440
+ color->is_interpolant(false);
1441
+ color->is_delayed(false);
1442
+ return color;
1443
+ }
1444
+
1445
+ Value* Parser::color_or_string(const sass::string& lexed) const
1446
+ {
1447
+ if (auto color = name_to_color(lexed)) {
1448
+ auto c = SASS_MEMORY_NEW(Color_RGBA, color);
1449
+ c->is_delayed(true);
1450
+ c->pstate(pstate);
1451
+ c->disp(lexed);
1452
+ return c;
1453
+ } else {
1454
+ return SASS_MEMORY_NEW(String_Constant, pstate, lexed);
1455
+ }
1456
+ }
1457
+
1458
+ // parse one value for a list
1459
+ ExpressionObj Parser::parse_value()
1460
+ {
1461
+ lex< css_comments >(false);
1462
+ if (lex< ampersand >())
1463
+ {
1464
+ if (match< ampersand >()) {
1465
+ warning("In Sass, \"&&\" means two copies of the parent selector. You probably want to use \"and\" instead.", pstate);
1466
+ }
1467
+ return SASS_MEMORY_NEW(Parent_Reference, pstate); }
1468
+
1469
+ if (lex< kwd_important >())
1470
+ { return SASS_MEMORY_NEW(String_Constant, pstate, "!important"); }
1471
+
1472
+ // parse `10%4px` into separated items and not a schema
1473
+ if (lex< sequence < percentage, lookahead < number > > >())
1474
+ { return lexed_percentage(lexed); }
1475
+
1476
+ if (lex< sequence < number, lookahead< sequence < op, number > > > >())
1477
+ { return lexed_number(lexed); }
1478
+
1479
+ // string may be interpolated
1480
+ if (lex< sequence < quoted_string, lookahead < exactly <'-'> > > >())
1481
+ { return parse_string(); }
1482
+
1483
+ if (const char* stop = peek< value_schema >())
1484
+ { return parse_value_schema(stop); }
1485
+
1486
+ // string may be interpolated
1487
+ if (lex< quoted_string >())
1488
+ { return parse_string(); }
1489
+
1490
+ if (lex< kwd_true >())
1491
+ { return SASS_MEMORY_NEW(Boolean, pstate, true); }
1492
+
1493
+ if (lex< kwd_false >())
1494
+ { return SASS_MEMORY_NEW(Boolean, pstate, false); }
1495
+
1496
+ if (lex< kwd_null >())
1497
+ { return SASS_MEMORY_NEW(Null, pstate); }
1498
+
1499
+ if (lex< identifier >()) {
1500
+ return color_or_string(lexed);
1501
+ }
1502
+
1503
+ if (lex< percentage >())
1504
+ { return lexed_percentage(lexed); }
1505
+
1506
+ // match hex number first because 0x000 looks like a number followed by an identifier
1507
+ if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >())
1508
+ { return lexed_hex_color(lexed); }
1509
+
1510
+ if (lex< hexa >())
1511
+ { return lexed_hex_color(lexed); }
1512
+
1513
+ if (lex< sequence < exactly <'#'>, identifier > >())
1514
+ { return SASS_MEMORY_NEW(String_Quoted, pstate, lexed); }
1515
+
1516
+ // also handle the 10em- foo special case
1517
+ // alternatives < exactly < '.' >, .. > -- `1.5em-.75em` is split into a list, not a binary expression
1518
+ if (lex< sequence< dimension, optional< sequence< exactly<'-'>, lookahead< alternatives < space > > > > > >())
1519
+ { return lexed_dimension(lexed); }
1520
+
1521
+ if (lex< sequence< static_component, one_plus< strict_identifier > > >())
1522
+ { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); }
1523
+
1524
+ if (lex< number >())
1525
+ { return lexed_number(lexed); }
1526
+
1527
+ if (lex< variable >())
1528
+ { return SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed)); }
1529
+
1530
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1531
+
1532
+ // unreachable statement
1533
+ return {};
1534
+ }
1535
+
1536
+ // this parses interpolation inside other strings
1537
+ // means the result should later be quoted again
1538
+ String_Obj Parser::parse_interpolated_chunk(Token chunk, bool constant, bool css)
1539
+ {
1540
+ const char* i = chunk.begin;
1541
+ // see if there any interpolants
1542
+ const char* p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) :
1543
+ find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end);
1544
+
1545
+ if (!p) {
1546
+ String_Quoted* str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, sass::string(i, chunk.end), 0, false, false, true, css);
1547
+ if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');
1548
+ return str_quoted;
1549
+ }
1550
+
1551
+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate, 0, css);
1552
+ schema->is_interpolant(true);
1553
+ while (i < chunk.end) {
1554
+ p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) :
1555
+ find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end);
1556
+ if (p) {
1557
+ if (i < p) {
1558
+ // accumulate the preceding segment if it's nonempty
1559
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, p), css));
1560
+ }
1561
+ // we need to skip anything inside strings
1562
+ // create a new target in parser/prelexer
1563
+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
1564
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1565
+ }
1566
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
1567
+ if (j) { --j;
1568
+ // parse the interpolant and accumulate it
1569
+ LocalOption<const char*> partEnd(end, j);
1570
+ LocalOption<const char*> partBeg(position, p + 2);
1571
+ ExpressionObj interp_node = parse_list();
1572
+ interp_node->is_interpolant(true);
1573
+ schema->append(interp_node);
1574
+ i = j;
1575
+ }
1576
+ else {
1577
+ // throw an error if the interpolant is unterminated
1578
+ error("unterminated interpolant inside string constant " + chunk.to_string());
1579
+ }
1580
+ }
1581
+ else { // no interpolants left; add the last segment if nonempty
1582
+ // check if we need quotes here (was not sure after merge)
1583
+ if (i < chunk.end) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, chunk.end), css));
1584
+ break;
1585
+ }
1586
+ ++ i;
1587
+ }
1588
+
1589
+ return schema.detach();
1590
+ }
1591
+
1592
+ String_Schema_Obj Parser::parse_css_variable_value()
1593
+ {
1594
+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
1595
+ sass::vector<char> brackets;
1596
+ while (true) {
1597
+ if (
1598
+ (brackets.empty() && lex< css_variable_top_level_value >(false)) ||
1599
+ (!brackets.empty() && lex< css_variable_value >(false))
1600
+ ) {
1601
+ Token str(lexed);
1602
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str));
1603
+ } else if (ExpressionObj tok = lex_interpolation()) {
1604
+ if (String_Schema* s = Cast<String_Schema>(tok)) {
1605
+ if (s->empty()) break;
1606
+ schema->concat(s);
1607
+ } else {
1608
+ schema->append(tok);
1609
+ }
1610
+ } else if (lex< quoted_string >()) {
1611
+ ExpressionObj tok = parse_string();
1612
+ if (tok.isNull()) break;
1613
+ if (String_Schema* s = Cast<String_Schema>(tok)) {
1614
+ if (s->empty()) break;
1615
+ schema->concat(s);
1616
+ } else {
1617
+ schema->append(tok);
1618
+ }
1619
+ } else if (lex< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) {
1620
+ const char opening_bracket = *(position - 1);
1621
+ brackets.push_back(opening_bracket);
1622
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(1, opening_bracket)));
1623
+ } else if (const char *match = peek< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >()) {
1624
+ if (brackets.empty()) break;
1625
+ const char closing_bracket = *(match - 1);
1626
+ if (brackets.back() != Util::opening_bracket_for(closing_bracket)) {
1627
+ sass::string message = ": expected \"";
1628
+ message += Util::closing_bracket_for(brackets.back());
1629
+ message += "\", was ";
1630
+ css_error("Invalid CSS", " after ", message);
1631
+ }
1632
+ lex< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >();
1633
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(1, closing_bracket)));
1634
+ brackets.pop_back();
1635
+ } else {
1636
+ break;
1637
+ }
1638
+ }
1639
+
1640
+ if (!brackets.empty()) {
1641
+ sass::string message = ": expected \"";
1642
+ message += Util::closing_bracket_for(brackets.back());
1643
+ message += "\", was ";
1644
+ css_error("Invalid CSS", " after ", message);
1645
+ }
1646
+
1647
+ if (schema->empty()) error("Custom property values may not be empty.");
1648
+ return schema.detach();
1649
+ }
1650
+
1651
+ ValueObj Parser::parse_static_value()
1652
+ {
1653
+ lex< static_value >();
1654
+ Token str(lexed);
1655
+ // static values always have trailing white-
1656
+ // space and end delimiter (\s*[;]$) included
1657
+ --pstate.offset.column;
1658
+ --after_token.column;
1659
+ --str.end;
1660
+ --position;
1661
+
1662
+ return color_or_string(str.time_wspace());;
1663
+ }
1664
+
1665
+ String_Obj Parser::parse_string()
1666
+ {
1667
+ return parse_interpolated_chunk(Token(lexed));
1668
+ }
1669
+
1670
+ String_Obj Parser::parse_ie_property()
1671
+ {
1672
+ lex< ie_property >();
1673
+ Token str(lexed);
1674
+ const char* i = str.begin;
1675
+ // see if there any interpolants
1676
+ const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(str.begin, str.end);
1677
+ if (!p) {
1678
+ return SASS_MEMORY_NEW(String_Quoted, pstate, sass::string(str.begin, str.end));
1679
+ }
1680
+
1681
+ String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate);
1682
+ while (i < str.end) {
1683
+ p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, str.end);
1684
+ if (p) {
1685
+ if (i < p) {
1686
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, p))); // accumulate the preceding segment if it's nonempty
1687
+ }
1688
+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
1689
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1690
+ }
1691
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
1692
+ if (j) {
1693
+ // parse the interpolant and accumulate it
1694
+ LocalOption<const char*> partEnd(end, j);
1695
+ LocalOption<const char*> partBeg(position, p + 2);
1696
+ ExpressionObj interp_node = parse_list();
1697
+ interp_node->is_interpolant(true);
1698
+ schema->append(interp_node);
1699
+ i = j;
1700
+ }
1701
+ else {
1702
+ // throw an error if the interpolant is unterminated
1703
+ error("unterminated interpolant inside IE function " + str.to_string());
1704
+ }
1705
+ }
1706
+ else { // no interpolants left; add the last segment if nonempty
1707
+ if (i < str.end) {
1708
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, str.end)));
1709
+ }
1710
+ break;
1711
+ }
1712
+ }
1713
+ return schema;
1714
+ }
1715
+
1716
+ String_Obj Parser::parse_ie_keyword_arg()
1717
+ {
1718
+ String_Schema_Obj kwd_arg = SASS_MEMORY_NEW(String_Schema, pstate, 3);
1719
+ if (lex< variable >()) {
1720
+ kwd_arg->append(SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed)));
1721
+ } else {
1722
+ lex< alternatives< identifier_schema, identifier > >();
1723
+ kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
1724
+ }
1725
+ lex< exactly<'='> >();
1726
+ kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
1727
+ if (peek< variable >()) kwd_arg->append(parse_list());
1728
+ else if (lex< number >()) {
1729
+ sass::string parsed(lexed);
1730
+ Util::normalize_decimals(parsed);
1731
+ kwd_arg->append(lexed_number(parsed));
1732
+ }
1733
+ else if (peek < ie_keyword_arg_value >()) { kwd_arg->append(parse_list()); }
1734
+ return kwd_arg;
1735
+ }
1736
+
1737
+ String_Schema_Obj Parser::parse_value_schema(const char* stop)
1738
+ {
1739
+ // initialize the string schema object to add tokens
1740
+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
1741
+
1742
+ if (peek<exactly<'}'>>()) {
1743
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1744
+ }
1745
+
1746
+ const char* e;
1747
+ const char* ee = end;
1748
+ end = stop;
1749
+ size_t num_items = 0;
1750
+ bool need_space = false;
1751
+ while (position < stop) {
1752
+ // parse space between tokens
1753
+ if (lex< spaces >() && num_items) {
1754
+ need_space = true;
1755
+ }
1756
+ if (need_space) {
1757
+ need_space = false;
1758
+ // schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " "));
1759
+ }
1760
+ if ((e = peek< re_functional >()) && e < stop) {
1761
+ schema->append(parse_function_call());
1762
+ }
1763
+ // lex an interpolant /#{...}/
1764
+ else if (lex< exactly < hash_lbrace > >()) {
1765
+ // Try to lex static expression first
1766
+ if (peek< exactly< rbrace > >()) {
1767
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1768
+ }
1769
+ ExpressionObj ex;
1770
+ if (lex< re_static_expression >()) {
1771
+ ex = SASS_MEMORY_NEW(String_Constant, pstate, lexed);
1772
+ } else {
1773
+ ex = parse_list(true);
1774
+ }
1775
+ ex->is_interpolant(true);
1776
+ schema->append(ex);
1777
+ if (!lex < exactly < rbrace > >()) {
1778
+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");
1779
+ }
1780
+ }
1781
+ // lex some string constants or other valid token
1782
+ // Note: [-+] chars are left over from i.e. `#{3}+3`
1783
+ else if (lex< alternatives < exactly<'%'>, exactly < '-' >, exactly < '+' > > >()) {
1784
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
1785
+ }
1786
+ // lex a quoted string
1787
+ else if (lex< quoted_string >()) {
1788
+ // need_space = true;
1789
+ // if (schema->length()) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " "));
1790
+ // else need_space = true;
1791
+ schema->append(parse_string());
1792
+ if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) {
1793
+ // need_space = true;
1794
+ }
1795
+ if (peek < exactly < '-' > >()) break;
1796
+ }
1797
+ else if (lex< identifier >()) {
1798
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
1799
+ if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) {
1800
+ // need_space = true;
1801
+ }
1802
+ }
1803
+ // lex (normalized) variable
1804
+ else if (lex< variable >()) {
1805
+ sass::string name(Util::normalize_underscores(lexed));
1806
+ schema->append(SASS_MEMORY_NEW(Variable, pstate, name));
1807
+ }
1808
+ // lex percentage value
1809
+ else if (lex< percentage >()) {
1810
+ schema->append(lexed_percentage(lexed));
1811
+ }
1812
+ // lex dimension value
1813
+ else if (lex< dimension >()) {
1814
+ schema->append(lexed_dimension(lexed));
1815
+ }
1816
+ // lex number value
1817
+ else if (lex< number >()) {
1818
+ schema->append(lexed_number(lexed));
1819
+ }
1820
+ // lex hex color value
1821
+ else if (lex< sequence < hex, negate < exactly < '-' > > > >()) {
1822
+ schema->append(lexed_hex_color(lexed));
1823
+ }
1824
+ else if (lex< sequence < exactly <'#'>, identifier > >()) {
1825
+ schema->append(SASS_MEMORY_NEW(String_Quoted, pstate, lexed));
1826
+ }
1827
+ // lex a value in parentheses
1828
+ else if (peek< parenthese_scope >()) {
1829
+ schema->append(parse_factor());
1830
+ }
1831
+ else {
1832
+ break;
1833
+ }
1834
+ ++num_items;
1835
+ }
1836
+ if (position != stop) {
1837
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(position, stop)));
1838
+ position = stop;
1839
+ }
1840
+ end = ee;
1841
+ return schema;
1842
+ }
1843
+
1844
+ // this parses interpolation outside other strings
1845
+ // means the result must not be quoted again later
1846
+ String_Obj Parser::parse_identifier_schema()
1847
+ {
1848
+ Token id(lexed);
1849
+ const char* i = id.begin;
1850
+ // see if there any interpolants
1851
+ const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(id.begin, id.end);
1852
+ if (!p) {
1853
+ return SASS_MEMORY_NEW(String_Constant, pstate, sass::string(id.begin, id.end));
1854
+ }
1855
+
1856
+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
1857
+ while (i < id.end) {
1858
+ p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, id.end);
1859
+ if (p) {
1860
+ if (i < p) {
1861
+ // accumulate the preceding segment if it's nonempty
1862
+ const char* o = position; position = i;
1863
+ schema->append(parse_value_schema(p));
1864
+ position = o;
1865
+ }
1866
+ // we need to skip anything inside strings
1867
+ // create a new target in parser/prelexer
1868
+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p;
1869
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
1870
+ }
1871
+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace
1872
+ if (j) {
1873
+ // parse the interpolant and accumulate it
1874
+ LocalOption<const char*> partEnd(end, j);
1875
+ LocalOption<const char*> partBeg(position, p + 2);
1876
+ ExpressionObj interp_node = parse_list(DELAYED);
1877
+ interp_node->is_interpolant(true);
1878
+ schema->append(interp_node);
1879
+ // schema->has_interpolants(true);
1880
+ i = j;
1881
+ }
1882
+ else {
1883
+ // throw an error if the interpolant is unterminated
1884
+ error("unterminated interpolant inside interpolated identifier " + id.to_string());
1885
+ }
1886
+ }
1887
+ else { // no interpolants left; add the last segment if nonempty
1888
+ if (i < end) {
1889
+ const char* o = position; position = i;
1890
+ schema->append(parse_value_schema(id.end));
1891
+ position = o;
1892
+ }
1893
+ break;
1894
+ }
1895
+ }
1896
+ return schema ? schema.detach() : 0;
1897
+ }
1898
+
1899
+ // calc functions should preserve arguments
1900
+ Function_Call_Obj Parser::parse_calc_function()
1901
+ {
1902
+ lex< identifier >();
1903
+ sass::string name(lexed);
1904
+ SourceSpan call_pos = pstate;
1905
+ lex< exactly<'('> >();
1906
+ SourceSpan arg_pos = pstate;
1907
+ const char* arg_beg = position;
1908
+ parse_list();
1909
+ const char* arg_end = position;
1910
+ lex< skip_over_scopes <
1911
+ exactly < '(' >,
1912
+ exactly < ')' >
1913
+ > >();
1914
+
1915
+ Argument_Obj arg = SASS_MEMORY_NEW(Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));
1916
+ Arguments_Obj args = SASS_MEMORY_NEW(Arguments, arg_pos);
1917
+ args->append(arg);
1918
+ return SASS_MEMORY_NEW(Function_Call, call_pos, name, args);
1919
+ }
1920
+
1921
+ String_Obj Parser::parse_url_function_string()
1922
+ {
1923
+ sass::string prefix("");
1924
+ if (lex< uri_prefix >()) {
1925
+ prefix = sass::string(lexed);
1926
+ }
1927
+
1928
+ lex < optional_spaces >();
1929
+ String_Obj url_string = parse_url_function_argument();
1930
+
1931
+ sass::string suffix("");
1932
+ if (lex< real_uri_suffix >()) {
1933
+ suffix = sass::string(lexed);
1934
+ }
1935
+
1936
+ sass::string uri("");
1937
+ if (url_string) {
1938
+ uri = url_string->to_string({ NESTED, 5 });
1939
+ }
1940
+
1941
+ if (String_Schema* schema = Cast<String_Schema>(url_string)) {
1942
+ String_Schema_Obj res = SASS_MEMORY_NEW(String_Schema, pstate);
1943
+ res->append(SASS_MEMORY_NEW(String_Constant, pstate, prefix));
1944
+ res->append(schema);
1945
+ res->append(SASS_MEMORY_NEW(String_Constant, pstate, suffix));
1946
+ return res;
1947
+ } else {
1948
+ sass::string res = prefix + uri + suffix;
1949
+ return SASS_MEMORY_NEW(String_Constant, pstate, res);
1950
+ }
1951
+ }
1952
+
1953
+ String_Obj Parser::parse_url_function_argument()
1954
+ {
1955
+ const char* p = position;
1956
+
1957
+ sass::string uri("");
1958
+ if (lex< real_uri_value >(false)) {
1959
+ uri = lexed.to_string();
1960
+ }
1961
+
1962
+ if (peek< exactly< hash_lbrace > >()) {
1963
+ const char* pp = position;
1964
+ // TODO: error checking for unclosed interpolants
1965
+ while (pp && peek< exactly< hash_lbrace > >(pp)) {
1966
+ pp = sequence< interpolant, real_uri_value >(pp);
1967
+ }
1968
+ if (!pp) return {};
1969
+ position = pp;
1970
+ return parse_interpolated_chunk(Token(p, position));
1971
+ }
1972
+ else if (uri != "") {
1973
+ sass::string res = Util::rtrim(uri);
1974
+ return SASS_MEMORY_NEW(String_Constant, pstate, res);
1975
+ }
1976
+
1977
+ return {};
1978
+ }
1979
+
1980
+ Function_Call_Obj Parser::parse_function_call()
1981
+ {
1982
+ lex< identifier >();
1983
+ sass::string name(lexed);
1984
+
1985
+ if (Util::normalize_underscores(name) == "content-exists" && stack.back() != Scope::Mixin)
1986
+ { error("Cannot call content-exists() except within a mixin."); }
1987
+
1988
+ SourceSpan call_pos = pstate;
1989
+ Arguments_Obj args = parse_arguments();
1990
+ return SASS_MEMORY_NEW(Function_Call, call_pos, name, args);
1991
+ }
1992
+
1993
+ Function_Call_Obj Parser::parse_function_call_schema()
1994
+ {
1995
+ String_Obj name = parse_identifier_schema();
1996
+ SourceSpan source_position_of_call = pstate;
1997
+ Arguments_Obj args = parse_arguments();
1998
+
1999
+ return SASS_MEMORY_NEW(Function_Call, source_position_of_call, name, args);
2000
+ }
2001
+
2002
+ Content_Obj Parser::parse_content_directive()
2003
+ {
2004
+ SourceSpan call_pos = pstate;
2005
+ Arguments_Obj args = parse_arguments();
2006
+
2007
+ return SASS_MEMORY_NEW(Content, call_pos, args);
2008
+ }
2009
+
2010
+ If_Obj Parser::parse_if_directive(bool else_if)
2011
+ {
2012
+ stack.push_back(Scope::Control);
2013
+ SourceSpan if_source_position = pstate;
2014
+ bool root = block_stack.back()->is_root();
2015
+ ExpressionObj predicate = parse_list();
2016
+ Block_Obj block = parse_block(root);
2017
+ Block_Obj alternative;
2018
+
2019
+ // only throw away comment if we parse a case
2020
+ // we want all other comments to be parsed
2021
+ if (lex_css< elseif_directive >()) {
2022
+ alternative = SASS_MEMORY_NEW(Block, pstate);
2023
+ alternative->append(parse_if_directive(true));
2024
+ }
2025
+ else if (lex_css< kwd_else_directive >()) {
2026
+ alternative = parse_block(root);
2027
+ }
2028
+ stack.pop_back();
2029
+ return SASS_MEMORY_NEW(If, if_source_position, predicate, block, alternative);
2030
+ }
2031
+
2032
+ ForRuleObj Parser::parse_for_directive()
2033
+ {
2034
+ stack.push_back(Scope::Control);
2035
+ SourceSpan for_source_position = pstate;
2036
+ bool root = block_stack.back()->is_root();
2037
+ lex_variable();
2038
+ sass::string var(Util::normalize_underscores(lexed));
2039
+ if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive");
2040
+ ExpressionObj lower_bound = parse_expression();
2041
+ bool inclusive = false;
2042
+ if (lex< kwd_through >()) inclusive = true;
2043
+ else if (lex< kwd_to >()) inclusive = false;
2044
+ else error("expected 'through' or 'to' keyword in @for directive");
2045
+ ExpressionObj upper_bound = parse_expression();
2046
+ Block_Obj body = parse_block(root);
2047
+ stack.pop_back();
2048
+ return SASS_MEMORY_NEW(ForRule, for_source_position, var, lower_bound, upper_bound, body, inclusive);
2049
+ }
2050
+
2051
+ // helper to parse a var token
2052
+ Token Parser::lex_variable()
2053
+ {
2054
+ // peek for dollar sign first
2055
+ if (!peek< exactly <'$'> >()) {
2056
+ css_error("Invalid CSS", " after ", ": expected \"$\", was ");
2057
+ }
2058
+ // we expect a simple identifier as the call name
2059
+ if (!lex< sequence < exactly <'$'>, identifier > >()) {
2060
+ lex< exactly <'$'> >(); // move pstate and position up
2061
+ css_error("Invalid CSS", " after ", ": expected identifier, was ");
2062
+ }
2063
+ // return object
2064
+ return lexed;
2065
+ }
2066
+ // helper to parse identifier
2067
+ Token Parser::lex_identifier()
2068
+ {
2069
+ // we expect a simple identifier as the call name
2070
+ if (!lex< identifier >()) { // ToDo: pstate wrong?
2071
+ css_error("Invalid CSS", " after ", ": expected identifier, was ");
2072
+ }
2073
+ // return object
2074
+ return lexed;
2075
+ }
2076
+
2077
+ EachRuleObj Parser::parse_each_directive()
2078
+ {
2079
+ stack.push_back(Scope::Control);
2080
+ SourceSpan each_source_position = pstate;
2081
+ bool root = block_stack.back()->is_root();
2082
+ sass::vector<sass::string> vars;
2083
+ lex_variable();
2084
+ vars.push_back(Util::normalize_underscores(lexed));
2085
+ while (lex< exactly<','> >()) {
2086
+ if (!lex< variable >()) error("@each directive requires an iteration variable");
2087
+ vars.push_back(Util::normalize_underscores(lexed));
2088
+ }
2089
+ if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive");
2090
+ ExpressionObj list = parse_list();
2091
+ Block_Obj body = parse_block(root);
2092
+ stack.pop_back();
2093
+ return SASS_MEMORY_NEW(EachRule, each_source_position, vars, list, body);
2094
+ }
2095
+
2096
+ // called after parsing `kwd_while_directive`
2097
+ WhileRuleObj Parser::parse_while_directive()
2098
+ {
2099
+ stack.push_back(Scope::Control);
2100
+ bool root = block_stack.back()->is_root();
2101
+ // create the initial while call object
2102
+ WhileRuleObj call = SASS_MEMORY_NEW(WhileRule, pstate, ExpressionObj{}, Block_Obj{});
2103
+ // parse mandatory predicate
2104
+ ExpressionObj predicate = parse_list();
2105
+ List_Obj l = Cast<List>(predicate);
2106
+ if (!predicate || (l && !l->length())) {
2107
+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ", false);
2108
+ }
2109
+ call->predicate(predicate);
2110
+ // parse mandatory block
2111
+ call->block(parse_block(root));
2112
+ // return ast node
2113
+ stack.pop_back();
2114
+ // return ast node
2115
+ return call.detach();
2116
+ }
2117
+
2118
+
2119
+ sass::vector<CssMediaQuery_Obj> Parser::parseCssMediaQueries()
2120
+ {
2121
+ sass::vector<CssMediaQuery_Obj> result;
2122
+ do {
2123
+ if (auto query = parseCssMediaQuery()) {
2124
+ result.push_back(query);
2125
+ }
2126
+ } while (lex<exactly<','>>());
2127
+ return result;
2128
+ }
2129
+
2130
+ sass::string Parser::parseIdentifier()
2131
+ {
2132
+ if (lex < identifier >(false)) {
2133
+ return sass::string(lexed);
2134
+ }
2135
+ return sass::string();
2136
+ }
2137
+
2138
+ CssMediaQuery_Obj Parser::parseCssMediaQuery()
2139
+ {
2140
+ CssMediaQuery_Obj result = SASS_MEMORY_NEW(CssMediaQuery, pstate);
2141
+ lex<css_comments>(false);
2142
+
2143
+ // Check if any tokens are to parse
2144
+ if (!peek_css<exactly<'('>>()) {
2145
+
2146
+ sass::string token1(parseIdentifier());
2147
+ lex<css_comments>(false);
2148
+
2149
+ if (token1.empty()) {
2150
+ return {};
2151
+ }
2152
+
2153
+ sass::string token2(parseIdentifier());
2154
+ lex<css_comments>(false);
2155
+
2156
+ if (Util::equalsLiteral("and", token2)) {
2157
+ result->type(token1);
2158
+ }
2159
+ else {
2160
+ if (token2.empty()) {
2161
+ result->type(token1);
2162
+ }
2163
+ else {
2164
+ result->modifier(token1);
2165
+ result->type(token2);
2166
+ }
2167
+
2168
+ if (lex < kwd_and >()) {
2169
+ lex<css_comments>(false);
2170
+ }
2171
+ else {
2172
+ return result;
2173
+ }
2174
+
2175
+ }
2176
+
2177
+ }
2178
+
2179
+ sass::vector<sass::string> queries;
2180
+
2181
+ do {
2182
+ lex<css_comments>(false);
2183
+
2184
+ if (lex<exactly<'('>>()) {
2185
+ // In dart sass parser returns a pure string
2186
+ if (lex < skip_over_scopes < exactly < '(' >, exactly < ')' > > >()) {
2187
+ sass::string decl("(" + sass::string(lexed));
2188
+ queries.push_back(decl);
2189
+ }
2190
+ // Should be: parseDeclarationValue;
2191
+ if (!lex<exactly<')'>>()) {
2192
+ // Should we throw an error here?
2193
+ }
2194
+ }
2195
+ } while (lex < kwd_and >());
2196
+
2197
+ result->features(queries);
2198
+
2199
+ if (result->features().empty()) {
2200
+ if (result->type().empty()) {
2201
+ return {};
2202
+ }
2203
+ }
2204
+
2205
+ return result;
2206
+ }
2207
+
2208
+
2209
+ // EO parse_while_directive
2210
+ MediaRule_Obj Parser::parseMediaRule()
2211
+ {
2212
+ MediaRule_Obj rule = SASS_MEMORY_NEW(MediaRule, pstate);
2213
+ stack.push_back(Scope::Media);
2214
+ rule->schema(parse_media_queries());
2215
+ parse_block_comments(false);
2216
+ rule->block(parse_css_block());
2217
+ stack.pop_back();
2218
+ return rule;
2219
+ }
2220
+
2221
+ List_Obj Parser::parse_media_queries()
2222
+ {
2223
+ advanceToNextToken();
2224
+ List_Obj queries = SASS_MEMORY_NEW(List, pstate, 0, SASS_COMMA);
2225
+ if (!peek_css < exactly <'{'> >()) queries->append(parse_media_query());
2226
+ while (lex_css < exactly <','> >()) queries->append(parse_media_query());
2227
+ queries->update_pstate(pstate);
2228
+ return queries.detach();
2229
+ }
2230
+
2231
+ // Expression* Parser::parse_media_query()
2232
+ Media_Query_Obj Parser::parse_media_query()
2233
+ {
2234
+ advanceToNextToken();
2235
+ Media_Query_Obj media_query = SASS_MEMORY_NEW(Media_Query, pstate);
2236
+ if (lex < kwd_not >()) { media_query->is_negated(true); lex < css_comments >(false); }
2237
+ else if (lex < kwd_only >()) { media_query->is_restricted(true); lex < css_comments >(false); }
2238
+
2239
+ if (lex < identifier_schema >()) media_query->media_type(parse_identifier_schema());
2240
+ else if (lex < identifier >()) media_query->media_type(parse_interpolated_chunk(lexed));
2241
+ else media_query->append(parse_media_expression());
2242
+
2243
+ while (lex_css < kwd_and >()) media_query->append(parse_media_expression());
2244
+ if (lex < identifier_schema >()) {
2245
+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
2246
+ if (media_query->media_type()) {
2247
+ schema->append(media_query->media_type());
2248
+ schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " "));
2249
+ }
2250
+ schema->append(parse_identifier_schema());
2251
+ media_query->media_type(schema);
2252
+ }
2253
+ while (lex_css < kwd_and >()) media_query->append(parse_media_expression());
2254
+
2255
+ media_query->update_pstate(pstate);
2256
+
2257
+ return media_query;
2258
+ }
2259
+
2260
+ Media_Query_ExpressionObj Parser::parse_media_expression()
2261
+ {
2262
+ if (lex < identifier_schema >()) {
2263
+ String_Obj ss = parse_identifier_schema();
2264
+ return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, ExpressionObj{}, true);
2265
+ }
2266
+ if (!lex_css< exactly<'('> >()) {
2267
+ error("media query expression must begin with '('");
2268
+ }
2269
+ ExpressionObj feature;
2270
+ if (peek_css< exactly<')'> >()) {
2271
+ error("media feature required in media query expression");
2272
+ }
2273
+ feature = parse_expression();
2274
+ ExpressionObj expression;
2275
+ if (lex_css< exactly<':'> >()) {
2276
+ expression = parse_list(DELAYED);
2277
+ }
2278
+ if (!lex_css< exactly<')'> >()) {
2279
+ error("unclosed parenthesis in media query expression");
2280
+ }
2281
+ return SASS_MEMORY_NEW(Media_Query_Expression, feature->pstate(), feature, expression);
2282
+ }
2283
+
2284
+ // lexed after `kwd_supports_directive`
2285
+ // these are very similar to media blocks
2286
+ SupportsRuleObj Parser::parse_supports_directive()
2287
+ {
2288
+ SupportsConditionObj cond = parse_supports_condition(/*top_level=*/true);
2289
+ // create the ast node object for the support queries
2290
+ SupportsRuleObj query = SASS_MEMORY_NEW(SupportsRule, pstate, cond);
2291
+ // additional block is mandatory
2292
+ // parse inner block
2293
+ query->block(parse_block());
2294
+ // return ast node
2295
+ return query;
2296
+ }
2297
+
2298
+ // parse one query operation
2299
+ // may encounter nested queries
2300
+ SupportsConditionObj Parser::parse_supports_condition(bool top_level)
2301
+ {
2302
+ lex < css_whitespace >();
2303
+ SupportsConditionObj cond;
2304
+ if ((cond = parse_supports_negation())) return cond;
2305
+ if ((cond = parse_supports_operator(top_level))) return cond;
2306
+ if ((cond = parse_supports_interpolation())) return cond;
2307
+ return cond;
2308
+ }
2309
+
2310
+ SupportsConditionObj Parser::parse_supports_negation()
2311
+ {
2312
+ if (!lex < kwd_not >()) return {};
2313
+ SupportsConditionObj cond = parse_supports_condition_in_parens(/*parens_required=*/true);
2314
+ return SASS_MEMORY_NEW(SupportsNegation, pstate, cond);
2315
+ }
2316
+
2317
+ SupportsConditionObj Parser::parse_supports_operator(bool top_level)
2318
+ {
2319
+ SupportsConditionObj cond = parse_supports_condition_in_parens(/*parens_required=*/top_level);
2320
+ if (cond.isNull()) return {};
2321
+
2322
+ while (true) {
2323
+ SupportsOperation::Operand op = SupportsOperation::OR;
2324
+ if (lex < kwd_and >()) { op = SupportsOperation::AND; }
2325
+ else if(!lex < kwd_or >()) { break; }
2326
+
2327
+ lex < css_whitespace >();
2328
+ SupportsConditionObj right = parse_supports_condition_in_parens(/*parens_required=*/true);
2329
+
2330
+ // SupportsCondition* cc = SASS_MEMORY_NEW(SupportsCondition, *static_cast<SupportsCondition*>(cond));
2331
+ cond = SASS_MEMORY_NEW(SupportsOperation, pstate, cond, right, op);
2332
+ }
2333
+ return cond;
2334
+ }
2335
+
2336
+ SupportsConditionObj Parser::parse_supports_interpolation()
2337
+ {
2338
+ if (!lex < interpolant >()) return {};
2339
+
2340
+ String_Obj interp = parse_interpolated_chunk(lexed);
2341
+ if (!interp) return {};
2342
+
2343
+ return SASS_MEMORY_NEW(Supports_Interpolation, pstate, interp);
2344
+ }
2345
+
2346
+ // TODO: This needs some major work. Although feature conditions
2347
+ // look like declarations their semantics differ significantly
2348
+ SupportsConditionObj Parser::parse_supports_declaration()
2349
+ {
2350
+ SupportsCondition* cond;
2351
+ // parse something declaration like
2352
+ ExpressionObj feature = parse_expression();
2353
+ ExpressionObj expression;
2354
+ if (lex_css< exactly<':'> >()) {
2355
+ expression = parse_list(DELAYED);
2356
+ }
2357
+ if (!feature || !expression) error("@supports condition expected declaration");
2358
+ cond = SASS_MEMORY_NEW(SupportsDeclaration,
2359
+ feature->pstate(),
2360
+ feature,
2361
+ expression);
2362
+ // ToDo: maybe we need an additional error condition?
2363
+ return cond;
2364
+ }
2365
+
2366
+ SupportsConditionObj Parser::parse_supports_condition_in_parens(bool parens_required)
2367
+ {
2368
+ SupportsConditionObj interp = parse_supports_interpolation();
2369
+ if (interp != nullptr) return interp;
2370
+
2371
+ if (!lex < exactly <'('> >()) {
2372
+ if (parens_required) {
2373
+ css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", /*trim=*/false);
2374
+ } else {
2375
+ return {};
2376
+ }
2377
+ }
2378
+ lex < css_whitespace >();
2379
+
2380
+ SupportsConditionObj cond = parse_supports_condition(/*top_level=*/false);
2381
+ if (cond.isNull()) cond = parse_supports_declaration();
2382
+ if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");
2383
+
2384
+ lex < css_whitespace >();
2385
+ return cond;
2386
+ }
2387
+
2388
+ AtRootRuleObj Parser::parse_at_root_block()
2389
+ {
2390
+ stack.push_back(Scope::AtRoot);
2391
+ SourceSpan at_source_position = pstate;
2392
+ Block_Obj body;
2393
+ At_Root_Query_Obj expr;
2394
+ Lookahead lookahead_result;
2395
+ if (lex_css< exactly<'('> >()) {
2396
+ expr = parse_at_root_query();
2397
+ }
2398
+ if (peek_css < exactly<'{'> >()) {
2399
+ lex <optional_spaces>();
2400
+ body = parse_block(true);
2401
+ }
2402
+ else if ((lookahead_result = lookahead_for_selector(position)).found) {
2403
+ StyleRuleObj r = parse_ruleset(lookahead_result);
2404
+ body = SASS_MEMORY_NEW(Block, r->pstate(), 1, true);
2405
+ body->append(r);
2406
+ }
2407
+ AtRootRuleObj at_root = SASS_MEMORY_NEW(AtRootRule, at_source_position, body);
2408
+ if (!expr.isNull()) at_root->expression(expr);
2409
+ stack.pop_back();
2410
+ return at_root;
2411
+ }
2412
+
2413
+ At_Root_Query_Obj Parser::parse_at_root_query()
2414
+ {
2415
+ if (peek< exactly<')'> >()) error("at-root feature required in at-root expression");
2416
+
2417
+ if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {
2418
+ css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");
2419
+ }
2420
+
2421
+ ExpressionObj feature = parse_list();
2422
+ if (!lex_css< exactly<':'> >()) error("style declaration must contain a value");
2423
+ ExpressionObj expression = parse_list();
2424
+ List_Obj value = SASS_MEMORY_NEW(List, feature->pstate(), 1);
2425
+
2426
+ if (expression->concrete_type() == Expression::LIST) {
2427
+ value = Cast<List>(expression);
2428
+ }
2429
+ else value->append(expression);
2430
+
2431
+ At_Root_Query_Obj cond = SASS_MEMORY_NEW(At_Root_Query,
2432
+ value->pstate(),
2433
+ feature,
2434
+ value);
2435
+ if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression");
2436
+ return cond;
2437
+ }
2438
+
2439
+ AtRuleObj Parser::parse_directive()
2440
+ {
2441
+ AtRuleObj directive = SASS_MEMORY_NEW(AtRule, pstate, lexed);
2442
+ String_Schema_Obj val = parse_almost_any_value();
2443
+ // strip left and right if they are of type string
2444
+ directive->value(val);
2445
+ if (peek< exactly<'{'> >()) {
2446
+ directive->block(parse_block());
2447
+ }
2448
+ return directive;
2449
+ }
2450
+
2451
+ ExpressionObj Parser::lex_interpolation()
2452
+ {
2453
+ if (lex < interpolant >(true) != NULL) {
2454
+ return parse_interpolated_chunk(lexed, true);
2455
+ }
2456
+ return {};
2457
+ }
2458
+
2459
+ ExpressionObj Parser::lex_interp_uri()
2460
+ {
2461
+ // create a string schema by lexing optional interpolations
2462
+ return lex_interp< re_string_uri_open, re_string_uri_close >();
2463
+ }
2464
+
2465
+ ExpressionObj Parser::lex_interp_string()
2466
+ {
2467
+ ExpressionObj rv;
2468
+ if ((rv = lex_interp< re_string_double_open, re_string_double_close >())) return rv;
2469
+ if ((rv = lex_interp< re_string_single_open, re_string_single_close >())) return rv;
2470
+ return rv;
2471
+ }
2472
+
2473
+ ExpressionObj Parser::lex_almost_any_value_chars()
2474
+ {
2475
+ const char* match =
2476
+ lex <
2477
+ one_plus <
2478
+ alternatives <
2479
+ exactly <'>'>,
2480
+ sequence <
2481
+ exactly <'\\'>,
2482
+ any_char
2483
+ >,
2484
+ sequence <
2485
+ negate <
2486
+ sequence <
2487
+ exactly < url_kwd >,
2488
+ exactly <'('>
2489
+ >
2490
+ >,
2491
+ neg_class_char <
2492
+ almost_any_value_class
2493
+ >
2494
+ >,
2495
+ sequence <
2496
+ exactly <'/'>,
2497
+ negate <
2498
+ alternatives <
2499
+ exactly <'/'>,
2500
+ exactly <'*'>
2501
+ >
2502
+ >
2503
+ >,
2504
+ sequence <
2505
+ exactly <'\\'>,
2506
+ exactly <'#'>,
2507
+ negate <
2508
+ exactly <'{'>
2509
+ >
2510
+ >,
2511
+ sequence <
2512
+ exactly <'!'>,
2513
+ negate <
2514
+ alpha
2515
+ >
2516
+ >
2517
+ >
2518
+ >
2519
+ >(false);
2520
+ if (match) {
2521
+ return SASS_MEMORY_NEW(String_Constant, pstate, lexed);
2522
+ }
2523
+ return {};
2524
+ }
2525
+
2526
+ ExpressionObj Parser::lex_almost_any_value_token()
2527
+ {
2528
+ ExpressionObj rv;
2529
+ if (*position == 0) return {};
2530
+ if ((rv = lex_almost_any_value_chars())) return rv;
2531
+ // if ((rv = lex_block_comment())) return rv;
2532
+ // if ((rv = lex_single_line_comment())) return rv;
2533
+ if ((rv = lex_interp_string())) return rv;
2534
+ if ((rv = lex_interp_uri())) return rv;
2535
+ if ((rv = lex_interpolation())) return rv;
2536
+ if (lex< alternatives< hex, hex0 > >())
2537
+ { return lexed_hex_color(lexed); }
2538
+ return rv;
2539
+ }
2540
+
2541
+ String_Schema_Obj Parser::parse_almost_any_value()
2542
+ {
2543
+
2544
+ String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
2545
+ if (*position == 0) return {};
2546
+ lex < spaces >(false);
2547
+ ExpressionObj token = lex_almost_any_value_token();
2548
+ if (!token) return {};
2549
+ schema->append(token);
2550
+ if (*position == 0) {
2551
+ schema->rtrim();
2552
+ return schema.detach();
2553
+ }
2554
+
2555
+ while ((token = lex_almost_any_value_token())) {
2556
+ schema->append(token);
2557
+ }
2558
+
2559
+ lex < css_whitespace >();
2560
+
2561
+ schema->rtrim();
2562
+
2563
+ return schema.detach();
2564
+ }
2565
+
2566
+ WarningRuleObj Parser::parse_warning()
2567
+ {
2568
+ if (stack.back() != Scope::Root &&
2569
+ stack.back() != Scope::Function &&
2570
+ stack.back() != Scope::Mixin &&
2571
+ stack.back() != Scope::Control &&
2572
+ stack.back() != Scope::Rules) {
2573
+ error("Illegal nesting: Only properties may be nested beneath properties.");
2574
+ }
2575
+ return SASS_MEMORY_NEW(WarningRule, pstate, parse_list(DELAYED));
2576
+ }
2577
+
2578
+ ErrorRuleObj Parser::parse_error()
2579
+ {
2580
+ if (stack.back() != Scope::Root &&
2581
+ stack.back() != Scope::Function &&
2582
+ stack.back() != Scope::Mixin &&
2583
+ stack.back() != Scope::Control &&
2584
+ stack.back() != Scope::Rules) {
2585
+ error("Illegal nesting: Only properties may be nested beneath properties.");
2586
+ }
2587
+ return SASS_MEMORY_NEW(ErrorRule, pstate, parse_list(DELAYED));
2588
+ }
2589
+
2590
+ DebugRuleObj Parser::parse_debug()
2591
+ {
2592
+ if (stack.back() != Scope::Root &&
2593
+ stack.back() != Scope::Function &&
2594
+ stack.back() != Scope::Mixin &&
2595
+ stack.back() != Scope::Control &&
2596
+ stack.back() != Scope::Rules) {
2597
+ error("Illegal nesting: Only properties may be nested beneath properties.");
2598
+ }
2599
+ return SASS_MEMORY_NEW(DebugRule, pstate, parse_list(DELAYED));
2600
+ }
2601
+
2602
+ Return_Obj Parser::parse_return_directive()
2603
+ {
2604
+ // check that we do not have an empty list (ToDo: check if we got all cases)
2605
+ if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >())
2606
+ { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); }
2607
+ return SASS_MEMORY_NEW(Return, pstate, parse_list());
2608
+ }
2609
+
2610
+ Lookahead Parser::lookahead_for_selector(const char* start)
2611
+ {
2612
+ // init result struct
2613
+ Lookahead rv = Lookahead();
2614
+ // get start position
2615
+ const char* p = start ? start : position;
2616
+ // match in one big "regex"
2617
+ rv.error = p;
2618
+ if (const char* q =
2619
+ peek <
2620
+ re_selector_list
2621
+ >(p)
2622
+ ) {
2623
+ bool could_be_property = peek< sequence< exactly<'-'>, exactly<'-'> > >(p) != 0;
2624
+ bool could_be_escaped = false;
2625
+ while (p < q) {
2626
+ // did we have interpolations?
2627
+ if (*p == '#' && *(p+1) == '{') {
2628
+ rv.has_interpolants = true;
2629
+ p = q; break;
2630
+ }
2631
+ // A property that's ambiguous with a nested selector is interpreted as a
2632
+ // custom property.
2633
+ if (*p == ':' && !could_be_escaped) {
2634
+ rv.is_custom_property = could_be_property || p+1 == q || peek< space >(p+1);
2635
+ }
2636
+ could_be_escaped = *p == '\\';
2637
+ ++ p;
2638
+ }
2639
+ // store anyway }
2640
+
2641
+
2642
+ // ToDo: remove
2643
+ rv.error = q;
2644
+ rv.position = q;
2645
+ // check expected opening bracket
2646
+ // only after successful matching
2647
+ if (peek < exactly<'{'> >(q)) rv.found = q;
2648
+ // else if (peek < end_of_file >(q)) rv.found = q;
2649
+ else if (peek < exactly<'('> >(q)) rv.found = q;
2650
+ // else if (peek < exactly<';'> >(q)) rv.found = q;
2651
+ // else if (peek < exactly<'}'> >(q)) rv.found = q;
2652
+ if (rv.found || *p == 0) rv.error = 0;
2653
+ }
2654
+
2655
+ rv.parsable = ! rv.has_interpolants;
2656
+
2657
+ // return result
2658
+ return rv;
2659
+
2660
+ }
2661
+ // EO lookahead_for_selector
2662
+
2663
+ // used in parse_block_nodes and parse_special_directive
2664
+ // ToDo: actual usage is still not really clear to me?
2665
+ Lookahead Parser::lookahead_for_include(const char* start)
2666
+ {
2667
+ // we actually just lookahead for a selector
2668
+ Lookahead rv = lookahead_for_selector(start);
2669
+ // but the "found" rules are different
2670
+ if (const char* p = rv.position) {
2671
+ // check for additional abort condition
2672
+ if (peek < exactly<';'> >(p)) rv.found = p;
2673
+ else if (peek < exactly<'}'> >(p)) rv.found = p;
2674
+ }
2675
+ // return result
2676
+ return rv;
2677
+ }
2678
+ // EO lookahead_for_include
2679
+
2680
+ // look ahead for a token with interpolation in it
2681
+ // we mostly use the result if there is an interpolation
2682
+ // everything that passes here gets parsed as one schema
2683
+ // meaning it will not be parsed as a space separated list
2684
+ Lookahead Parser::lookahead_for_value(const char* start)
2685
+ {
2686
+ // init result struct
2687
+ Lookahead rv = Lookahead();
2688
+ // get start position
2689
+ const char* p = start ? start : position;
2690
+ // match in one big "regex"
2691
+ if (const char* q =
2692
+ peek <
2693
+ non_greedy <
2694
+ alternatives <
2695
+ // consume whitespace
2696
+ block_comment, // spaces,
2697
+ // main tokens
2698
+ sequence <
2699
+ interpolant,
2700
+ optional <
2701
+ quoted_string
2702
+ >
2703
+ >,
2704
+ identifier,
2705
+ variable,
2706
+ // issue #442
2707
+ sequence <
2708
+ parenthese_scope,
2709
+ interpolant,
2710
+ optional <
2711
+ quoted_string
2712
+ >
2713
+ >
2714
+ >,
2715
+ sequence <
2716
+ // optional_spaces,
2717
+ alternatives <
2718
+ // end_of_file,
2719
+ exactly<'{'>,
2720
+ exactly<'}'>,
2721
+ exactly<';'>
2722
+ >
2723
+ >
2724
+ >
2725
+ >(p)
2726
+ ) {
2727
+ if (p == q) return rv;
2728
+ while (p < q) {
2729
+ // did we have interpolations?
2730
+ if (*p == '#' && *(p+1) == '{') {
2731
+ rv.has_interpolants = true;
2732
+ p = q; break;
2733
+ }
2734
+ ++ p;
2735
+ }
2736
+ // store anyway
2737
+ // ToDo: remove
2738
+ rv.position = q;
2739
+ // check expected opening bracket
2740
+ // only after successful matching
2741
+ if (peek < exactly<'{'> >(q)) rv.found = q;
2742
+ else if (peek < exactly<';'> >(q)) rv.found = q;
2743
+ else if (peek < exactly<'}'> >(q)) rv.found = q;
2744
+ }
2745
+
2746
+ // return result
2747
+ return rv;
2748
+ }
2749
+ // EO lookahead_for_value
2750
+
2751
+ void Parser::read_bom()
2752
+ {
2753
+ size_t skip = 0;
2754
+ sass::string encoding;
2755
+ bool utf_8 = false;
2756
+ switch ((unsigned char)position[0]) {
2757
+ case 0xEF:
2758
+ skip = check_bom_chars(position, end, utf_8_bom, 3);
2759
+ encoding = "UTF-8";
2760
+ utf_8 = true;
2761
+ break;
2762
+ case 0xFE:
2763
+ skip = check_bom_chars(position, end, utf_16_bom_be, 2);
2764
+ encoding = "UTF-16 (big endian)";
2765
+ break;
2766
+ case 0xFF:
2767
+ skip = check_bom_chars(position, end, utf_16_bom_le, 2);
2768
+ skip += (skip ? check_bom_chars(position, end, utf_32_bom_le, 4) : 0);
2769
+ encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");
2770
+ break;
2771
+ case 0x00:
2772
+ skip = check_bom_chars(position, end, utf_32_bom_be, 4);
2773
+ encoding = "UTF-32 (big endian)";
2774
+ break;
2775
+ case 0x2B:
2776
+ skip = check_bom_chars(position, end, utf_7_bom_1, 4)
2777
+ | check_bom_chars(position, end, utf_7_bom_2, 4)
2778
+ | check_bom_chars(position, end, utf_7_bom_3, 4)
2779
+ | check_bom_chars(position, end, utf_7_bom_4, 4)
2780
+ | check_bom_chars(position, end, utf_7_bom_5, 5);
2781
+ encoding = "UTF-7";
2782
+ break;
2783
+ case 0xF7:
2784
+ skip = check_bom_chars(position, end, utf_1_bom, 3);
2785
+ encoding = "UTF-1";
2786
+ break;
2787
+ case 0xDD:
2788
+ skip = check_bom_chars(position, end, utf_ebcdic_bom, 4);
2789
+ encoding = "UTF-EBCDIC";
2790
+ break;
2791
+ case 0x0E:
2792
+ skip = check_bom_chars(position, end, scsu_bom, 3);
2793
+ encoding = "SCSU";
2794
+ break;
2795
+ case 0xFB:
2796
+ skip = check_bom_chars(position, end, bocu_1_bom, 3);
2797
+ encoding = "BOCU-1";
2798
+ break;
2799
+ case 0x84:
2800
+ skip = check_bom_chars(position, end, gb_18030_bom, 4);
2801
+ encoding = "GB-18030";
2802
+ break;
2803
+ default: break;
2804
+ }
2805
+ if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding);
2806
+ position += skip;
2807
+ }
2808
+
2809
+ size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)
2810
+ {
2811
+ size_t skip = 0;
2812
+ if (src + len > end) return 0;
2813
+ for (size_t i = 0; i < len; ++i, ++skip) {
2814
+ if ((unsigned char) src[i] != bom[i]) return 0;
2815
+ }
2816
+ return skip;
2817
+ }
2818
+
2819
+
2820
+ ExpressionObj Parser::fold_operands(ExpressionObj base, sass::vector<ExpressionObj>& operands, Operand op)
2821
+ {
2822
+ for (size_t i = 0, S = operands.size(); i < S; ++i) {
2823
+ base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), op, base, operands[i]);
2824
+ }
2825
+ return base;
2826
+ }
2827
+
2828
+ ExpressionObj Parser::fold_operands(ExpressionObj base, sass::vector<ExpressionObj>& operands, sass::vector<Operand>& ops, size_t i)
2829
+ {
2830
+ if (String_Schema* schema = Cast<String_Schema>(base)) {
2831
+ // return schema;
2832
+ if (schema->has_interpolants()) {
2833
+ if (i + 1 < operands.size() && (
2834
+ (ops[0].operand == Sass_OP::EQ)
2835
+ || (ops[0].operand == Sass_OP::ADD)
2836
+ || (ops[0].operand == Sass_OP::DIV)
2837
+ || (ops[0].operand == Sass_OP::MUL)
2838
+ || (ops[0].operand == Sass_OP::NEQ)
2839
+ || (ops[0].operand == Sass_OP::LT)
2840
+ || (ops[0].operand == Sass_OP::GT)
2841
+ || (ops[0].operand == Sass_OP::LTE)
2842
+ || (ops[0].operand == Sass_OP::GTE)
2843
+ )) {
2844
+ ExpressionObj rhs = fold_operands(operands[i], operands, ops, i + 1);
2845
+ rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[0], schema, rhs);
2846
+ return rhs;
2847
+ }
2848
+ // return schema;
2849
+ }
2850
+ }
2851
+
2852
+ if (operands.size() > Constants::MaxCallStack) {
2853
+ // XXX: this is never hit via spec tests
2854
+ sass::ostream stm;
2855
+ stm << "Stack depth exceeded max of " << Constants::MaxCallStack;
2856
+ error(stm.str());
2857
+ }
2858
+
2859
+ for (size_t S = operands.size(); i < S; ++i) {
2860
+ if (String_Schema* schema = Cast<String_Schema>(operands[i])) {
2861
+ if (schema->has_interpolants()) {
2862
+ if (i + 1 < S) {
2863
+ // this whole branch is never hit via spec tests
2864
+ ExpressionObj rhs = fold_operands(operands[i+1], operands, ops, i + 2);
2865
+ rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], schema, rhs);
2866
+ base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, rhs);
2867
+ return base;
2868
+ }
2869
+ base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]);
2870
+ return base;
2871
+ } else {
2872
+ base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]);
2873
+ }
2874
+ } else {
2875
+ base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]);
2876
+ }
2877
+ Binary_Expression* b = Cast<Binary_Expression>(base.ptr());
2878
+ if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
2879
+ base->is_delayed(true);
2880
+ }
2881
+ }
2882
+ // nested binary expression are never to be delayed
2883
+ if (Binary_Expression* b = Cast<Binary_Expression>(base)) {
2884
+ if (Cast<Binary_Expression>(b->left())) base->set_delayed(false);
2885
+ if (Cast<Binary_Expression>(b->right())) base->set_delayed(false);
2886
+ }
2887
+ return base;
2888
+ }
2889
+
2890
+ void Parser::error(sass::string msg)
2891
+ {
2892
+ traces.push_back(Backtrace(pstate));
2893
+ throw Exception::InvalidSass(pstate, traces, msg);
2894
+ }
2895
+
2896
+ // print a css parsing error with actual context information from parsed source
2897
+ void Parser::css_error(const sass::string& msg, const sass::string& prefix, const sass::string& middle, const bool trim)
2898
+ {
2899
+ int max_len = 18;
2900
+ const char* end = this->end;
2901
+ while (*end != 0) ++ end;
2902
+ const char* pos = peek < optional_spaces >();
2903
+ if (!pos) pos = position;
2904
+
2905
+ const char* last_pos(pos);
2906
+ if (last_pos > begin) {
2907
+ utf8::prior(last_pos, begin);
2908
+ }
2909
+ // backup position to last significant char
2910
+ while (trim && last_pos > begin&& last_pos < end) {
2911
+ if (!Util::ascii_isspace(static_cast<unsigned char>(*last_pos))) break;
2912
+ utf8::prior(last_pos, begin);
2913
+ }
2914
+
2915
+ bool ellipsis_left = false;
2916
+ const char* pos_left(last_pos);
2917
+ const char* end_left(last_pos);
2918
+
2919
+ if (*pos_left) utf8::next(pos_left, end);
2920
+ if (*end_left) utf8::next(end_left, end);
2921
+ while (pos_left > begin) {
2922
+ if (utf8::distance(pos_left, end_left) >= max_len) {
2923
+ utf8::prior(pos_left, begin);
2924
+ ellipsis_left = *(pos_left) != '\n' &&
2925
+ *(pos_left) != '\r';
2926
+ utf8::next(pos_left, end);
2927
+ break;
2928
+ }
2929
+
2930
+ const char* prev = pos_left;
2931
+ utf8::prior(prev, begin);
2932
+ if (*prev == '\r') break;
2933
+ if (*prev == '\n') break;
2934
+ pos_left = prev;
2935
+ }
2936
+ if (pos_left < begin) {
2937
+ pos_left = begin;
2938
+ }
2939
+
2940
+ bool ellipsis_right = false;
2941
+ const char* end_right(pos);
2942
+ const char* pos_right(pos);
2943
+ while (end_right < end) {
2944
+ if (utf8::distance(pos_right, end_right) > max_len) {
2945
+ ellipsis_left = *(pos_right) != '\n' &&
2946
+ *(pos_right) != '\r';
2947
+ break;
2948
+ }
2949
+ if (*end_right == '\r') break;
2950
+ if (*end_right == '\n') break;
2951
+ utf8::next(end_right, end);
2952
+ }
2953
+ // if (*end_right == 0) end_right ++;
2954
+
2955
+ sass::string left(pos_left, end_left);
2956
+ sass::string right(pos_right, end_right);
2957
+ size_t left_subpos = left.size() > 15 ? left.size() - 15 : 0;
2958
+ size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0;
2959
+ if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos);
2960
+ if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis;
2961
+ // now pass new message to the more generic error function
2962
+ error(msg + prefix + quote(left) + middle + quote(right));
2963
+ }
2964
+
2965
+ }