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