8 #include "position.hpp"
10 #include "position.hpp"
11 #include "prelexer.hpp"
18 bool has_interpolants;
23 class Parser : public ParserState {
26 enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules };
29 std::vector<Block_Obj> block_stack;
30 std::vector<Scope> stack;
31 Media_Block_Ptr last_media_block;
35 Position before_token;
43 Parser(Context& ctx, const ParserState& pstate)
44 : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(),
45 source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0)
46 { stack.push_back(Scope::Root); }
48 // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));
49 static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
50 static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
51 static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0);
52 // special static parsers to convert strings into certain selectors
53 static Selector_List_Obj parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);
57 // lex and peak uses the template parameter to branch on the action, which
58 // triggers clangs tautological comparison on the single-comparison
59 // branches. This is not a bug, just a merging of behaviour into
62 #pragma clang diagnostic push
63 #pragma clang diagnostic ignored "-Wtautological-compare"
68 // skip current token and next whitespace
69 // moves ParserState right before next token
70 void advanceToNextToken();
72 bool peek_newline(const char* start = 0);
74 // skip over spaces, tabs and line comments
75 template <Prelexer::prelexer mx>
76 const char* sneak(const char* start = 0)
78 using namespace Prelexer;
80 // maybe use optional start position from arguments?
81 const char* it_position = start ? start : position;
87 mx == css_whitespace ||
88 mx == optional_spaces ||
89 mx == optional_css_comments ||
90 mx == optional_css_whitespace
95 // skip over spaces, tabs and sass line comments
96 const char* pos = optional_css_whitespace(it_position);
97 // always return a valid position
98 return pos ? pos : it_position;
102 // peek will only skip over space, tabs and line comment
103 // return the position where the lexer match will occur
104 template <Prelexer::prelexer mx>
105 const char* match(const char* start = 0)
107 // match the given prelexer
111 // peek will only 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* peek(const char* start = 0)
117 // sneak up to the actual token we want to lex
118 // this should skip over white-space if desired
119 const char* it_before_token = sneak < mx >(start);
121 // match the given prelexer
122 const char* match = mx(it_before_token);
124 // check if match is in valid range
125 return match <= end ? match : 0;
129 // white-space handling is built into the lexer
130 // this way you do not need to parse it yourself
131 // some matchers don't accept certain white-space
132 // we do not support start arg, since we manipulate
133 // sourcemap offset and we modify the position pointer!
134 // lex will only skip over space, tabs and line comment
135 template <Prelexer::prelexer mx>
136 const char* lex(bool lazy = true, bool force = false)
139 if (*position == 0) return 0;
141 // position considered before lexed token
142 // we can skip whitespace or comments for
143 // lazy developers (but we need control)
144 const char* it_before_token = position;
146 // sneak up to the actual token we want to lex
147 // this should skip over white-space if desired
148 if (lazy) it_before_token = sneak < mx >(position);
150 // now call matcher to get position after token
151 const char* it_after_token = mx(it_before_token);
153 // check if match is in valid range
154 if (it_after_token > end) return 0;
156 // maybe we want to update the parser state anyway?
157 if (force == false) {
158 // assertion that we got a valid match
159 if (it_after_token == 0) return 0;
160 // assertion that we actually lexed something
161 if (it_after_token == it_before_token) return 0;
164 // create new lexed token object (holds the parse results)
165 lexed = Token(position, it_before_token, it_after_token);
167 // advance position (add whitespace before current token)
168 before_token = after_token.add(position, it_before_token);
170 // update after_token position for current token
171 after_token.add(it_before_token, it_after_token);
173 // ToDo: could probably do this incremetal on original object (API wants offset?)
174 pstate = ParserState(path, source, lexed, before_token, after_token - before_token);
176 // advance internal char iterator
177 return position = it_after_token;
181 // lex_css skips over space, tabs, line and block comment
182 // all block comments will be consumed and thrown away
183 // source-map position will point to token after the comment
184 template <Prelexer::prelexer mx>
185 const char* lex_css()
189 // store previous pointer
190 const char* oldpos = position;
191 Position bt = before_token;
192 Position at = after_token;
193 ParserState op = pstate;
194 // throw away comments
195 // update srcmap position
196 lex < Prelexer::css_comments >();
197 // now lex a new token
198 const char* pos = lex< mx >();
199 // maybe restore prev state
211 // all block comments will be skipped and thrown away
212 template <Prelexer::prelexer mx>
213 const char* peek_css(const char* start = 0)
215 // now peek a token (skip comments first)
216 return peek< mx >(peek < Prelexer::css_comments >(start));
221 #pragma clang diagnostic pop
225 void error(std::string msg, Position pos);
226 // generate message with given and expected sample
227 // text before and in the middle are configurable
228 void css_error(const std::string& msg,
229 const std::string& prefix = " after ",
230 const std::string& middle = ", was: ");
234 Import_Obj parse_import();
235 Definition_Obj parse_definition(Definition::Type which_type);
236 Parameters_Obj parse_parameters();
237 Parameter_Obj parse_parameter();
238 Mixin_Call_Obj parse_include_directive();
239 Arguments_Obj parse_arguments();
240 Argument_Obj parse_argument();
241 Assignment_Obj parse_assignment();
242 Ruleset_Obj parse_ruleset(Lookahead lookahead);
243 Selector_List_Obj parse_selector_list(bool chroot);
244 Complex_Selector_Obj parse_complex_selector(bool chroot);
245 Selector_Schema_Obj parse_selector_schema(const char* end_of_selector, bool chroot);
246 Compound_Selector_Obj parse_compound_selector();
247 Simple_Selector_Obj parse_simple_selector();
248 Wrapped_Selector_Obj parse_negated_selector();
249 Simple_Selector_Obj parse_pseudo_selector();
250 Attribute_Selector_Obj parse_attribute_selector();
251 Block_Obj parse_block(bool is_root = false);
252 Block_Obj parse_css_block(bool is_root = false);
253 bool parse_block_nodes(bool is_root = false);
254 bool parse_block_node(bool is_root = false);
256 bool parse_number_prefix();
257 Declaration_Obj parse_declaration();
258 Expression_Obj parse_map();
259 Expression_Obj parse_bracket_list();
260 Expression_Obj parse_list(bool delayed = false);
261 Expression_Obj parse_comma_list(bool delayed = false);
262 Expression_Obj parse_space_list();
263 Expression_Obj parse_disjunction();
264 Expression_Obj parse_conjunction();
265 Expression_Obj parse_relation();
266 Expression_Obj parse_expression();
267 Expression_Obj parse_operators();
268 Expression_Obj parse_factor();
269 Expression_Obj parse_value();
270 Function_Call_Obj parse_calc_function();
271 Function_Call_Obj parse_function_call();
272 Function_Call_Schema_Obj parse_function_call_schema();
273 String_Obj parse_url_function_string();
274 String_Obj parse_url_function_argument();
275 String_Obj parse_interpolated_chunk(Token, bool constant = false);
276 String_Obj parse_string();
277 String_Constant_Obj parse_static_value();
278 String_Obj parse_ie_property();
279 String_Obj parse_ie_keyword_arg();
280 String_Schema_Obj parse_value_schema(const char* stop);
281 String_Obj parse_identifier_schema();
282 If_Obj parse_if_directive(bool else_if = false);
283 For_Obj parse_for_directive();
284 Each_Obj parse_each_directive();
285 While_Obj parse_while_directive();
286 Return_Obj parse_return_directive();
287 Content_Obj parse_content_directive();
288 void parse_charset_directive();
289 Media_Block_Obj parse_media_block();
290 List_Obj parse_media_queries();
291 Media_Query_Obj parse_media_query();
292 Media_Query_Expression_Obj parse_media_expression();
293 Supports_Block_Obj parse_supports_directive();
294 Supports_Condition_Obj parse_supports_condition();
295 Supports_Condition_Obj parse_supports_negation();
296 Supports_Condition_Obj parse_supports_operator();
297 Supports_Condition_Obj parse_supports_interpolation();
298 Supports_Condition_Obj parse_supports_declaration();
299 Supports_Condition_Obj parse_supports_condition_in_parens();
300 At_Root_Block_Obj parse_at_root_block();
301 At_Root_Query_Obj parse_at_root_query();
302 String_Schema_Obj parse_almost_any_value();
303 Directive_Obj parse_special_directive();
304 Directive_Obj parse_prefixed_directive();
305 Directive_Obj parse_directive();
306 Warning_Obj parse_warning();
307 Error_Obj parse_error();
308 Debug_Obj parse_debug();
310 // be more like ruby sass
311 Expression_Obj lex_almost_any_value_token();
312 Expression_Obj lex_almost_any_value_chars();
313 Expression_Obj lex_interp_string();
314 Expression_Obj lex_interp_uri();
315 Expression_Obj lex_interpolation();
317 // these will throw errors
318 Token lex_variable();
319 Token lex_identifier();
321 void parse_block_comments();
323 Lookahead lookahead_for_value(const char* start = 0);
324 Lookahead lookahead_for_selector(const char* start = 0);
325 Lookahead lookahead_for_include(const char* start = 0);
327 Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, Operand op);
328 Expression_Obj fold_operands(Expression_Obj base, std::vector<Expression_Obj>& operands, std::vector<Operand>& ops, size_t i = 0);
330 void throw_syntax_error(std::string message, size_t ln = 0);
331 void throw_read_error(std::string message, size_t ln = 0);
334 template <Prelexer::prelexer open, Prelexer::prelexer close>
335 Expression_Obj lex_interp()
337 if (lex < open >(false)) {
338 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
339 // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";
340 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
341 if (position[0] == '#' && position[1] == '{') {
342 Expression_Obj itpl = lex_interpolation();
343 if (!itpl.isNull()) schema->append(itpl);
344 while (lex < close >(false)) {
345 // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";
346 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed));
347 if (position[0] == '#' && position[1] == '{') {
348 Expression_Obj itpl = lex_interpolation();
349 if (!itpl.isNull()) schema->append(itpl);
355 return SASS_MEMORY_NEW(String_Constant, pstate, lexed);
362 size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);