1#ifndef SASS_PARSER_H
2#define SASS_PARSER_H
3
4// sass.hpp must go before all system headers to get the
5// __EXTENSIONS__ fix on Solaris.
6#include "sass.hpp"
7
8#include <string>
9#include <vector>
10
11#include "ast.hpp"
12#include "position.hpp"
13#include "context.hpp"
14#include "position.hpp"
15#include "prelexer.hpp"
16#include "source.hpp"
17
18#ifndef MAX_NESTING
19// Note that this limit is not an exact science
20// it depends on various factors, which some are
21// not under our control (compile time or even OS
22// dependent settings on the available stack size)
23// It should fix most common segfault cases though.
24#define MAX_NESTING 512
25#endif
26
27struct Lookahead {
28 const char* found;
29 const char* error;
30 const char* position;
31 bool parsable;
32 bool has_interpolants;
33 bool is_custom_property;
34};
35
36namespace Sass {
37
38 class Parser : public SourceSpan {
39 public:
40
41 enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules, AtRoot };
42
43 Context& ctx;
44 sass::vector<Block_Obj> block_stack;
45 sass::vector<Scope> stack;
46 SourceDataObj source;
47 const char* begin;
48 const char* position;
49 const char* end;
50 Offset before_token;
51 Offset after_token;
52 SourceSpan pstate;
53 Backtraces traces;
54 size_t indentation;
55 size_t nestings;
56 bool allow_parent;
57 Token lexed;
58
59 Parser(SourceData* source, Context& ctx, Backtraces, bool allow_parent = true);
60
61 // special static parsers to convert strings into certain selectors
62 static SelectorListObj parse_selector(SourceData* source, Context& ctx, Backtraces, bool allow_parent = true);
63
64#ifdef __clang__
65
66 // lex and peak uses the template parameter to branch on the action, which
67 // triggers clangs tautological comparison on the single-comparison
68 // branches. This is not a bug, just a merging of behaviour into
69 // one function
70
71#pragma clang diagnostic push
72#pragma clang diagnostic ignored "-Wtautological-compare"
73
74#endif
75
76
77 // skip current token and next whitespace
78 // moves SourceSpan right before next token
79 void advanceToNextToken();
80
81 bool peek_newline(const char* start = 0);
82
83 // skip over spaces, tabs and line comments
84 template <Prelexer::prelexer mx>
85 const char* sneak(const char* start = 0)
86 {
87 using namespace Prelexer;
88
89 // maybe use optional start position from arguments?
90 const char* it_position = start ? start : position;
91
92 // skip white-space?
93 if (mx == spaces ||
94 mx == no_spaces ||
95 mx == css_comments ||
96 mx == css_whitespace ||
97 mx == optional_spaces ||
98 mx == optional_css_comments ||
99 mx == optional_css_whitespace
100 ) {
101 return it_position;
102 }
103
104 // skip over spaces, tabs and sass line comments
105 const char* pos = optional_css_whitespace(src: it_position);
106 // always return a valid position
107 return pos ? pos : it_position;
108
109 }
110
111 // match will not skip over space, tabs and line comment
112 // return the position where the lexer match will occur
113 template <Prelexer::prelexer mx>
114 const char* match(const char* start = 0)
115 {
116 // match the given prelexer
117 return mx(position);
118 }
119
120 // peek will only skip over space, tabs and line comment
121 // return the position where the lexer match will occur
122 template <Prelexer::prelexer mx>
123 const char* peek(const char* start = 0)
124 {
125
126 // sneak up to the actual token we want to lex
127 // this should skip over white-space if desired
128 const char* it_before_token = sneak < mx >(start);
129
130 // match the given prelexer
131 const char* match = mx(it_before_token);
132
133 // check if match is in valid range
134 return match <= end ? match : 0;
135
136 }
137
138 // white-space handling is built into the lexer
139 // this way you do not need to parse it yourself
140 // some matchers don't accept certain white-space
141 // we do not support start arg, since we manipulate
142 // sourcemap offset and we modify the position pointer!
143 // lex will only skip over space, tabs and line comment
144 template <Prelexer::prelexer mx>
145 const char* lex(bool lazy = true, bool force = false)
146 {
147
148 if (*position == 0) return 0;
149
150 // position considered before lexed token
151 // we can skip whitespace or comments for
152 // lazy developers (but we need control)
153 const char* it_before_token = position;
154
155 // sneak up to the actual token we want to lex
156 // this should skip over white-space if desired
157 if (lazy) it_before_token = sneak < mx >(position);
158
159 // now call matcher to get position after token
160 const char* it_after_token = mx(it_before_token);
161
162 // check if match is in valid range
163 if (it_after_token > end) return 0;
164
165 // maybe we want to update the parser state anyway?
166 if (force == false) {
167 // assertion that we got a valid match
168 if (it_after_token == 0) return 0;
169 // assertion that we actually lexed something
170 if (it_after_token == it_before_token) return 0;
171 }
172
173 // create new lexed token object (holds the parse results)
174 lexed = Token(position, it_before_token, it_after_token);
175
176 // advance position (add whitespace before current token)
177 before_token = after_token.add(begin: position, end: it_before_token);
178
179 // update after_token position for current token
180 after_token.add(begin: it_before_token, end: it_after_token);
181
182 // ToDo: could probably do this incremental on original object (API wants offset?)
183 pstate = SourceSpan(source, before_token, after_token - before_token);
184
185 // advance internal char iterator
186 return position = it_after_token;
187
188 }
189
190 // lex_css skips over space, tabs, line and block comment
191 // all block comments will be consumed and thrown away
192 // source-map position will point to token after the comment
193 template <Prelexer::prelexer mx>
194 const char* lex_css()
195 {
196 // copy old token
197 Token prev = lexed;
198 // store previous pointer
199 const char* oldpos = position;
200 Offset bt = before_token;
201 Offset at = after_token;
202 SourceSpan op = pstate;
203 // throw away comments
204 // update srcmap position
205 lex < Prelexer::css_comments >();
206 // now lex a new token
207 const char* pos = lex< mx >();
208 // maybe restore prev state
209 if (pos == 0) {
210 pstate = op;
211 lexed = prev;
212 position = oldpos;
213 after_token = at;
214 before_token = bt;
215 }
216 // return match
217 return pos;
218 }
219
220 // all block comments will be skipped and thrown away
221 template <Prelexer::prelexer mx>
222 const char* peek_css(const char* start = 0)
223 {
224 // now peek a token (skip comments first)
225 return peek< mx >(peek < Prelexer::css_comments >(start));
226 }
227
228#ifdef __clang__
229
230#pragma clang diagnostic pop
231
232#endif
233
234 void error(sass::string msg);
235 // generate message with given and expected sample
236 // text before and in the middle are configurable
237 void css_error(const sass::string& msg,
238 const sass::string& prefix = " after ",
239 const sass::string& middle = ", was: ",
240 const bool trim = true);
241 void read_bom();
242
243 Block_Obj parse();
244 Import_Obj parse_import();
245 Definition_Obj parse_definition(Definition::Type which_type);
246 Parameters_Obj parse_parameters();
247 Parameter_Obj parse_parameter();
248 Mixin_Call_Obj parse_include_directive();
249 Arguments_Obj parse_arguments();
250 Argument_Obj parse_argument();
251 Assignment_Obj parse_assignment();
252 StyleRuleObj parse_ruleset(Lookahead lookahead);
253 SelectorListObj parseSelectorList(bool chroot);
254 ComplexSelectorObj parseComplexSelector(bool chroot);
255 Selector_Schema_Obj parse_selector_schema(const char* end_of_selector, bool chroot);
256 CompoundSelectorObj parseCompoundSelector();
257 SimpleSelectorObj parse_simple_selector();
258 PseudoSelectorObj parse_negated_selector2();
259 Expression* parse_binominal();
260 SimpleSelectorObj parse_pseudo_selector();
261 AttributeSelectorObj parse_attribute_selector();
262 Block_Obj parse_block(bool is_root = false);
263 Block_Obj parse_css_block(bool is_root = false);
264 bool parse_block_nodes(bool is_root = false);
265 bool parse_block_node(bool is_root = false);
266
267 Declaration_Obj parse_declaration();
268 ExpressionObj parse_map();
269 ExpressionObj parse_bracket_list();
270 ExpressionObj parse_list(bool delayed = false);
271 ExpressionObj parse_comma_list(bool delayed = false);
272 ExpressionObj parse_space_list();
273 ExpressionObj parse_disjunction();
274 ExpressionObj parse_conjunction();
275 ExpressionObj parse_relation();
276 ExpressionObj parse_expression();
277 ExpressionObj parse_operators();
278 ExpressionObj parse_factor();
279 ExpressionObj parse_value();
280 Function_Call_Obj parse_calc_function();
281 Function_Call_Obj parse_function_call();
282 Function_Call_Obj parse_function_call_schema();
283 String_Obj parse_url_function_string();
284 String_Obj parse_url_function_argument();
285 String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true);
286 String_Obj parse_string();
287 ValueObj parse_static_value();
288 String_Schema_Obj parse_css_variable_value();
289 String_Obj parse_ie_property();
290 String_Obj parse_ie_keyword_arg();
291 String_Schema_Obj parse_value_schema(const char* stop);
292 String_Obj parse_identifier_schema();
293 If_Obj parse_if_directive(bool else_if = false);
294 ForRuleObj parse_for_directive();
295 EachRuleObj parse_each_directive();
296 WhileRuleObj parse_while_directive();
297 MediaRule_Obj parseMediaRule();
298 sass::vector<CssMediaQuery_Obj> parseCssMediaQueries();
299 sass::string parseIdentifier();
300 CssMediaQuery_Obj parseCssMediaQuery();
301 Return_Obj parse_return_directive();
302 Content_Obj parse_content_directive();
303 void parse_charset_directive();
304 List_Obj parse_media_queries();
305 Media_Query_Obj parse_media_query();
306 Media_Query_ExpressionObj parse_media_expression();
307 SupportsRuleObj parse_supports_directive();
308 SupportsConditionObj parse_supports_condition(bool top_level);
309 SupportsConditionObj parse_supports_negation();
310 SupportsConditionObj parse_supports_operator(bool top_level);
311 SupportsConditionObj parse_supports_interpolation();
312 SupportsConditionObj parse_supports_declaration();
313 SupportsConditionObj parse_supports_condition_in_parens(bool parens_required);
314 AtRootRuleObj parse_at_root_block();
315 At_Root_Query_Obj parse_at_root_query();
316 String_Schema_Obj parse_almost_any_value();
317 AtRuleObj parse_directive();
318 WarningRuleObj parse_warning();
319 ErrorRuleObj parse_error();
320 DebugRuleObj parse_debug();
321
322 Value* color_or_string(const sass::string& lexed) const;
323
324 // be more like ruby sass
325 ExpressionObj lex_almost_any_value_token();
326 ExpressionObj lex_almost_any_value_chars();
327 ExpressionObj lex_interp_string();
328 ExpressionObj lex_interp_uri();
329 ExpressionObj lex_interpolation();
330
331 // these will throw errors
332 Token lex_variable();
333 Token lex_identifier();
334
335 void parse_block_comments(bool store = true);
336
337 Lookahead lookahead_for_value(const char* start = 0);
338 Lookahead lookahead_for_selector(const char* start = 0);
339 Lookahead lookahead_for_include(const char* start = 0);
340
341 ExpressionObj fold_operands(ExpressionObj base, sass::vector<ExpressionObj>& operands, Operand op);
342 ExpressionObj fold_operands(ExpressionObj base, sass::vector<ExpressionObj>& operands, sass::vector<Operand>& ops, size_t i = 0);
343
344 void throw_syntax_error(sass::string message, size_t ln = 0);
345 void throw_read_error(sass::string message, size_t ln = 0);
346
347
348 template <Prelexer::prelexer open, Prelexer::prelexer close>
349 ExpressionObj lex_interp()
350 {
351 if (lex < open >(false)) {
352 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
353 // std::cerr << "LEX [[" << sass::string(lexed) << "]]\n";
354 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
355 if (position[0] == '#' && position[1] == '{') {
356 ExpressionObj itpl = lex_interpolation();
357 if (!itpl.isNull()) schema->append(element: itpl);
358 while (lex < close >(false)) {
359 // std::cerr << "LEX [[" << sass::string(lexed) << "]]\n";
360 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
361 if (position[0] == '#' && position[1] == '{') {
362 ExpressionObj itpl = lex_interpolation();
363 if (!itpl.isNull()) schema->append(element: itpl);
364 } else {
365 return schema;
366 }
367 }
368 } else {
369 return SASS_MEMORY_NEW(String_Constant, pstate, lexed);
370 }
371 }
372 return {};
373 }
374
375 public:
376 static Number* lexed_number(const SourceSpan& pstate, const sass::string& parsed);
377 static Number* lexed_dimension(const SourceSpan& pstate, const sass::string& parsed);
378 static Number* lexed_percentage(const SourceSpan& pstate, const sass::string& parsed);
379 static Value* lexed_hex_color(const SourceSpan& pstate, const sass::string& parsed);
380 private:
381 Number* lexed_number(const sass::string& parsed) { return lexed_number(pstate, parsed); };
382 Number* lexed_dimension(const sass::string& parsed) { return lexed_dimension(pstate, parsed); };
383 Number* lexed_percentage(const sass::string& parsed) { return lexed_percentage(pstate, parsed); };
384 Value* lexed_hex_color(const sass::string& parsed) { return lexed_hex_color(pstate, parsed); };
385
386 static const char* re_attr_sensitive_close(const char* src);
387 static const char* re_attr_insensitive_close(const char* src);
388
389 };
390
391 size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);
392}
393
394#endif
395

source code of gtk/subprojects/libsass/src/parser.hpp