1 /*
2 * This file is part of LibCSS.
3 * Licensed under the MIT License,
4 * http://www.opensource.org/licenses/mit-license.php
5 * Copyright 2008 John-Mark Bell <jmb@netsurf-browser.org>
6 */
7
8 #include <assert.h>
9 #include <ctype.h>
10 #include <stdbool.h>
11
12 #include <libwapcaplet/libwapcaplet.h>
13
14 #include <parserutils/input/inputstream.h>
15 #include <parserutils/utils/stack.h>
16 #include <parserutils/utils/vector.h>
17
18 #include "charset/detect.h"
19 #include "lex/lex.h"
20 #include "parse/parse.h"
21 #include "utils/parserutilserror.h"
22 #include "utils/utils.h"
23
24 #undef DEBUG_STACK
25 #undef DEBUG_EVENTS
26
27 #ifndef NDEBUG
28 #include <stdio.h>
29 extern void parserutils_stack_dump(parserutils_stack *stack, const char *prefix,
30 void (*printer)(void *item));
31 extern void parserutils_vector_dump(parserutils_vector *vector,
32 const char *prefix, void (*printer)(void *item));
33 #ifdef DEBUG_STACK
34 static void printer(void *item);
35 #endif
36 #ifdef DEBUG_EVENTS
37 static void tprinter(void *token);
38 #endif
39 #endif
40
41 /**
42 * Major state numbers
43 */
44 enum {
45 sStart = 0,
46 sStylesheet = 1,
47 sStatement = 2,
48 sRuleset = 3,
49 sRulesetEnd = 4,
50 sAtRule = 5,
51 sAtRuleEnd = 6,
52 sBlock = 7,
53 sBlockContent = 8,
54 sSelector = 9,
55 sDeclaration = 10,
56 sDeclList = 11,
57 sDeclListEnd = 12,
58 sProperty = 13,
59 sValue0 = 14,
60 sValue1 = 15,
61 sValue = 16,
62 sAny0 = 17,
63 sAny1 = 18,
64 sAny = 19,
65 sMalformedDecl = 20,
66 sMalformedSelector = 21,
67 sMalformedAtRule = 22,
68 sInlineStyle = 23,
69 sISBody0 = 24,
70 sISBody = 25,
71 sMediaQuery = 26,
72 };
73
74 /**
75 * Representation of a parser state
76 */
77 typedef struct parser_state
78 {
79 unsigned int state : 16,
80 substate : 16;
81 } parser_state;
82
83 /**
84 * CSS parser object
85 */
86 struct css_parser
87 {
88 parserutils_inputstream *stream; /**< The inputstream */
89 css_lexer *lexer; /**< The lexer to use */
90
91 bool quirks; /**< Whether to enable parsing quirks */
92
93 #define STACK_CHUNK 32
94 parserutils_stack *states; /**< Stack of states */
95
96 parserutils_vector *tokens; /**< Vector of pending tokens */
97
98 const css_token *pushback; /**< Push back buffer */
99
100 bool parseError; /**< A parse error has occurred */
101 parserutils_stack *open_items; /**< Stack of open brackets */
102
103 bool last_was_ws; /**< Last token was whitespace */
104
105 css_parser_event_handler event; /**< Client's event handler */
106 void *event_pw; /**< Client data for event handler */
107 };
108
109 static css_error css__parser_create_internal(const char *charset,
110 css_charset_source cs_source, parser_state initial,
111 css_parser **parser);
112
113 static css_error transition(css_parser *parser, parser_state to,
114 parser_state subsequent);
115 static css_error transitionNoRet(css_parser *parser, parser_state to);
116 static css_error done(css_parser *parser);
117 static css_error expect(css_parser *parser, css_token_type type);
118 static css_error getToken(css_parser *parser, const css_token **token);
119 static css_error pushBack(css_parser *parser, const css_token *token);
120 static css_error eatWS(css_parser *parser);
121
122 static css_error parseStart(css_parser *parser);
123 static css_error parseStylesheet(css_parser *parser);
124 static css_error parseStatement(css_parser *parser);
125 static css_error parseRuleset(css_parser *parser);
126 static css_error parseRulesetEnd(css_parser *parser);
127 static css_error parseAtRule(css_parser *parser);
128 static css_error parseAtRuleEnd(css_parser *parser);
129 static css_error parseBlock(css_parser *parser);
130 static css_error parseBlockContent(css_parser *parser);
131 static css_error parseSelector(css_parser *parser);
132 static css_error parseDeclaration(css_parser *parser);
133 static css_error parseDeclList(css_parser *parser);
134 static css_error parseDeclListEnd(css_parser *parser);
135 static css_error parseProperty(css_parser *parser);
136 static css_error parseValue0(css_parser *parser);
137 static css_error parseValue1(css_parser *parser);
138 static css_error parseValue(css_parser *parser);
139 static css_error parseAny0(css_parser *parser);
140 static css_error parseAny1(css_parser *parser);
141 static css_error parseAny(css_parser *parser);
142 static css_error parseMalformedDeclaration(css_parser *parser);
143 static css_error parseMalformedSelector(css_parser *parser);
144 static css_error parseMalformedAtRule(css_parser *parser);
145 static css_error parseInlineStyle(css_parser *parser);
146 static css_error parseISBody0(css_parser *parser);
147 static css_error parseISBody(css_parser *parser);
148 static css_error parseMediaQuery(css_parser *parser);
149
150 static void discard_tokens(css_parser *parser);
151
152 /**
153 * Dispatch table for parsing, indexed by major state number
154 */
155 static css_error (*parseFuncs[])(css_parser *parser) = {
156 parseStart,
157 parseStylesheet,
158 parseStatement,
159 parseRuleset,
160 parseRulesetEnd,
161 parseAtRule,
162 parseAtRuleEnd,
163 parseBlock,
164 parseBlockContent,
165 parseSelector,
166 parseDeclaration,
167 parseDeclList,
168 parseDeclListEnd,
169 parseProperty,
170 parseValue0,
171 parseValue1,
172 parseValue,
173 parseAny0,
174 parseAny1,
175 parseAny,
176 parseMalformedDeclaration,
177 parseMalformedSelector,
178 parseMalformedAtRule,
179 parseInlineStyle,
180 parseISBody0,
181 parseISBody,
182 parseMediaQuery,
183 };
184
185 /**
186 * Create a CSS parser
187 *
188 * \param charset Charset of data, if known, or NULL
189 * \param cs_source Source of charset information, or CSS_CHARSET_DEFAULT
190 * \param parser Pointer to location to receive parser instance
191 * \return CSS_OK on success,
192 * CSS_BADPARM on bad parameters,
193 * CSS_NOMEM on memory exhaustion
194 */
css__parser_create(const char * charset,css_charset_source cs_source,css_parser ** parser)195 css_error css__parser_create(const char *charset, css_charset_source cs_source,
196 css_parser **parser)
197 {
198 parser_state initial = { sStart, 0 };
199
200 return css__parser_create_internal(charset, cs_source,
201 initial, parser);
202 }
203
204 /**
205 * Create a CSS parser for an inline style
206 *
207 * \param charset Charset of data, if known, or NULL
208 * \param cs_source Source of charset information, or CSS_CHARSET_DEFAULT
209 * \param parser Pointer to location to receive parser instance
210 * \return CSS_OK on success,
211 * CSS_BADPARM on bad parameters,
212 * CSS_NOMEM on memory exhaustion
213 */
css__parser_create_for_inline_style(const char * charset,css_charset_source cs_source,css_parser ** parser)214 css_error css__parser_create_for_inline_style(const char *charset,
215 css_charset_source cs_source, css_parser **parser)
216 {
217 parser_state initial = { sInlineStyle, 0 };
218
219 return css__parser_create_internal(charset, cs_source,
220 initial, parser);
221 }
222
223 /**
224 * Create a CSS parser for a media query
225 *
226 * \param charset Charset of data, if known, or NULL
227 * \param cs_source Source of charset information, or CSS_CHARSET_DEFAULT
228 * \param parser Pointer to location to receive parser instance
229 * \return CSS_OK on success,
230 * CSS_BADPARM on bad parameters,
231 * CSS_NOMEM on memory exhaustion
232 */
css__parser_create_for_media_query(const char * charset,css_charset_source cs_source,css_parser ** parser)233 css_error css__parser_create_for_media_query(const char *charset,
234 css_charset_source cs_source, css_parser **parser)
235 {
236 parser_state initial = { sMediaQuery, 0 };
237
238 return css__parser_create_internal(charset, cs_source,
239 initial, parser);
240 }
241
242 /**
243 * Destroy a CSS parser
244 *
245 * \param parser The parser instance to destroy
246 * \return CSS_OK on success, appropriate error otherwise
247 */
css__parser_destroy(css_parser * parser)248 css_error css__parser_destroy(css_parser *parser)
249 {
250 if (parser == NULL)
251 return CSS_BADPARM;
252
253 parserutils_stack_destroy(parser->open_items);
254
255 parserutils_vector_destroy(parser->tokens);
256
257 parserutils_stack_destroy(parser->states);
258
259 css__lexer_destroy(parser->lexer);
260
261 parserutils_inputstream_destroy(parser->stream);
262
263 free(parser);
264
265 return CSS_OK;
266 }
267
268 /**
269 * Configure a CSS parser
270 *
271 * \param parser The parser instance to configure
272 * \param type The option to configure
273 * \param params Option-specific data
274 * \return CSS_OK on success, appropriate error otherwise
275 */
css__parser_setopt(css_parser * parser,css_parser_opttype type,css_parser_optparams * params)276 css_error css__parser_setopt(css_parser *parser, css_parser_opttype type,
277 css_parser_optparams *params)
278 {
279 if (parser == NULL || params == NULL)
280 return CSS_BADPARM;
281
282 switch (type) {
283 case CSS_PARSER_QUIRKS:
284 parser->quirks = params->quirks;
285 break;
286 case CSS_PARSER_EVENT_HANDLER:
287 parser->event = params->event_handler.handler;
288 parser->event_pw = params->event_handler.pw;
289 break;
290 }
291
292 return CSS_OK;
293 }
294
295 /**
296 * Parse a chunk of data using a CSS parser
297 *
298 * \param parser The parser to use
299 * \param data Pointer to the chunk to parse
300 * \param len Length of chunk
301 * \return CSS_OK on success, appropriate error otherwise
302 */
css__parser_parse_chunk(css_parser * parser,const uint8_t * data,size_t len)303 css_error css__parser_parse_chunk(css_parser *parser, const uint8_t *data,
304 size_t len)
305 {
306 parserutils_error perror;
307 parser_state *state;
308 css_error error = CSS_OK;
309
310 if (parser == NULL || data == NULL)
311 return CSS_BADPARM;
312
313 perror = parserutils_inputstream_append(parser->stream, data, len);
314 if (perror != PARSERUTILS_OK)
315 return css_error_from_parserutils_error(perror);
316
317 do {
318 state = parserutils_stack_get_current(parser->states);
319 if (state == NULL)
320 break;
321
322 error = parseFuncs[state->state](parser);
323 } while (error == CSS_OK);
324
325 return error;
326 }
327
328 /**
329 * Inform a CSS parser that all data has been received.
330 *
331 * \param parser The parser to inform
332 * \return CSS_OK on success, appropriate error otherwise
333 */
css__parser_completed(css_parser * parser)334 css_error css__parser_completed(css_parser *parser)
335 {
336 parserutils_error perror;
337 parser_state *state;
338 css_error error = CSS_OK;
339
340 if (parser == NULL)
341 return CSS_BADPARM;
342
343 /* Send EOF to input stream */
344 perror = parserutils_inputstream_append(parser->stream, NULL, 0);
345 if (perror != PARSERUTILS_OK)
346 return css_error_from_parserutils_error(perror);
347
348 /* Flush through any remaining data */
349 do {
350 state = parserutils_stack_get_current(parser->states);
351 if (state == NULL)
352 break;
353
354 error = parseFuncs[state->state](parser);
355 } while (error == CSS_OK);
356
357 return error;
358 }
359
360 /**
361 * Retrieve document charset information from a CSS parser
362 *
363 * \param parser The parser instance
364 * \param source Pointer to location to receive charset source
365 * \return Pointer to charset name (constant; do not free)
366 */
css__parser_read_charset(css_parser * parser,css_charset_source * source)367 const char *css__parser_read_charset(css_parser *parser,
368 css_charset_source *source)
369 {
370 const char *charset;
371 uint32_t src;
372
373 if (parser == NULL || source == NULL)
374 return NULL;
375
376 charset = parserutils_inputstream_read_charset(parser->stream, &src);
377
378 *source = (css_charset_source) src;
379
380 return charset;
381 }
382
383 /**
384 * Quirks permitted when parsing
385 *
386 * \param parser Parser to query
387 * \return True if quirks permitted, false otherwise
388 */
css__parser_quirks_permitted(css_parser * parser)389 bool css__parser_quirks_permitted(css_parser *parser)
390 {
391 return parser->quirks;
392 }
393
394 /******************************************************************************
395 * Parser creation helper *
396 ******************************************************************************/
397
398 /**
399 * Create a CSS parser (internal)
400 *
401 * \param charset Charset of data, if known, or NULL
402 * \param cs_source Source of charset information, or CSS_CHARSET_DEFAULT
403 * \param initial The required initial state of the parser
404 * \param parser Pointer to location to receive parser instance
405 * \return CSS_OK on success,
406 * CSS_BADPARM on bad parameters,
407 * CSS_NOMEM on memory exhaustion
408 */
css__parser_create_internal(const char * charset,css_charset_source cs_source,parser_state initial,css_parser ** parser)409 css_error css__parser_create_internal(const char *charset,
410 css_charset_source cs_source, parser_state initial,
411 css_parser **parser)
412 {
413 css_parser *p;
414 parserutils_error perror;
415 css_error error;
416
417 if (parser == NULL)
418 return CSS_BADPARM;
419
420 p = malloc(sizeof(css_parser));
421 if (p == NULL)
422 return CSS_NOMEM;
423
424 perror = parserutils_inputstream_create(charset, cs_source,
425 css__charset_extract, &p->stream);
426 if (perror != PARSERUTILS_OK) {
427 free(p);
428 return css_error_from_parserutils_error(perror);
429 }
430
431 error = css__lexer_create(p->stream, &p->lexer);
432 if (error != CSS_OK) {
433 parserutils_inputstream_destroy(p->stream);
434 free(p);
435 return error;
436 }
437
438 perror = parserutils_stack_create(sizeof(parser_state),
439 STACK_CHUNK, &p->states);
440 if (perror != PARSERUTILS_OK) {
441 css__lexer_destroy(p->lexer);
442 parserutils_inputstream_destroy(p->stream);
443 free(p);
444 return css_error_from_parserutils_error(perror);
445 }
446
447 perror = parserutils_vector_create(sizeof(css_token),
448 STACK_CHUNK, &p->tokens);
449 if (perror != PARSERUTILS_OK) {
450 parserutils_stack_destroy(p->states);
451 css__lexer_destroy(p->lexer);
452 parserutils_inputstream_destroy(p->stream);
453 free(p);
454 return css_error_from_parserutils_error(perror);
455 }
456
457 perror = parserutils_stack_create(sizeof(char),
458 STACK_CHUNK, &p->open_items);
459 if (perror != PARSERUTILS_OK) {
460 parserutils_vector_destroy(p->tokens);
461 parserutils_stack_destroy(p->states);
462 css__lexer_destroy(p->lexer);
463 parserutils_inputstream_destroy(p->stream);
464 free(p);
465 return css_error_from_parserutils_error(perror);
466 }
467
468 perror = parserutils_stack_push(p->states, (void *) &initial);
469 if (perror != PARSERUTILS_OK) {
470 parserutils_stack_destroy(p->open_items);
471 parserutils_vector_destroy(p->tokens);
472 parserutils_stack_destroy(p->states);
473 css__lexer_destroy(p->lexer);
474 parserutils_inputstream_destroy(p->stream);
475 free(p);
476 return css_error_from_parserutils_error(perror);
477 }
478
479 p->quirks = false;
480 p->pushback = NULL;
481 p->parseError = false;
482 p->event = NULL;
483 p->last_was_ws = false;
484 p->event_pw = NULL;
485
486 *parser = p;
487
488 return CSS_OK;
489 }
490
491 /******************************************************************************
492 * Helper functions *
493 ******************************************************************************/
494
495 /**
496 * Transition to a new state, ensuring return to the one specified
497 *
498 * \param parser The parser instance
499 * \param to Destination state
500 * \param subsequent The state to return to
501 * \return CSS_OK on success, appropriate error otherwise
502 */
transition(css_parser * parser,parser_state to,parser_state subsequent)503 css_error transition(css_parser *parser, parser_state to,
504 parser_state subsequent)
505 {
506 parser_state *state = parserutils_stack_get_current(parser->states);
507 parser_state current = *state;
508 parserutils_error perror;
509
510 /* Replace current state on the stack with the subsequent one */
511 *state = subsequent;
512
513 /* Push next state on the stack */
514 perror = parserutils_stack_push(parser->states, (void *) &to);
515 if (perror != PARSERUTILS_OK) {
516 *state = current;
517 return css_error_from_parserutils_error(perror);
518 }
519
520 #if !defined(NDEBUG) && defined(DEBUG_STACK)
521 parserutils_stack_dump(parser->states, __func__, printer);
522 #endif
523
524 /* Clear the error flag */
525 parser->parseError = false;
526
527 return CSS_OK;
528 }
529
530 /**
531 * Transition to a new state, returning to previous state on stack
532 *
533 * \param parser The parser instance
534 * \param to Destination state
535 * \return CSS_OK on success, appropriate error otherwise
536 */
transitionNoRet(css_parser * parser,parser_state to)537 css_error transitionNoRet(css_parser *parser, parser_state to)
538 {
539 parser_state *state = parserutils_stack_get_current(parser->states);
540
541 /* Replace current state on the stack with destination */
542 *state = to;
543
544 #if !defined(NDEBUG) && defined(DEBUG_STACK)
545 parserutils_stack_dump(parser->states, __func__, printer);
546 #endif
547
548 /* Clear the error flag */
549 parser->parseError = false;
550
551 return CSS_OK;
552 }
553
554 /**
555 * Return to previous state on the stack
556 *
557 * \param parser The parser instance
558 * \return CSS_OK on success, appropriate error otherwise
559 */
done(css_parser * parser)560 css_error done(css_parser *parser)
561 {
562 parserutils_error perror;
563
564 /* Pop current state from stack */
565 perror = parserutils_stack_pop(parser->states, NULL);
566 if (perror != PARSERUTILS_OK)
567 return css_error_from_parserutils_error(perror);
568
569 #if !defined(NDEBUG) && defined(DEBUG_STACK)
570 parserutils_stack_dump(parser->states, __func__, printer);
571 #endif
572
573 return CSS_OK;
574 }
575
576 /**
577 * Assert that the expected token is next in the input
578 *
579 * \param parser The parser instance
580 * \param type The expected token type
581 * \return CSS_OK on success, appropriate error otherwise
582 */
expect(css_parser * parser,css_token_type type)583 css_error expect(css_parser *parser, css_token_type type)
584 {
585 const css_token *token;
586 css_error error;
587
588 error = getToken(parser, &token);
589 if (error != CSS_OK)
590 return error;
591
592 if (token->type != type) {
593 error = pushBack(parser, token);
594 if (error != CSS_OK)
595 return error;
596 return CSS_INVALID;
597 }
598
599 return CSS_OK;
600 }
601
602 /**
603 * Retrieve the next token in the input
604 *
605 * \param parser The parser instance
606 * \param token Pointer to location to receive token
607 * \return CSS_OK on success, appropriate error otherwise
608 */
getToken(css_parser * parser,const css_token ** token)609 css_error getToken(css_parser *parser, const css_token **token)
610 {
611 parserutils_error perror;
612 lwc_error lerror;
613 css_error error;
614
615 /* Use pushback, if it exists */
616 if (parser->pushback != NULL) {
617 *token = parser->pushback;
618 parser->pushback = NULL;
619 } else {
620 /* Otherwise, ask the lexer */
621 css_token *t;
622
623 error = css__lexer_get_token(parser->lexer, &t);
624 if (error != CSS_OK)
625 return error;
626
627 /* If the last token read was whitespace, keep reading
628 * tokens until we encounter one that isn't whitespace */
629 while (parser->last_was_ws && t->type == CSS_TOKEN_S) {
630 error = css__lexer_get_token(parser->lexer, &t);
631 if (error != CSS_OK)
632 return error;
633 }
634
635 /* We need only intern for the following token types:
636 *
637 * CSS_TOKEN_IDENT, CSS_TOKEN_ATKEYWORD, CSS_TOKEN_STRING,
638 * CSS_TOKEN_INVALID_STRING, CSS_TOKEN_HASH, CSS_TOKEN_URI,
639 * CSS_TOKEN_UNICODE_RANGE?, CSS_TOKEN_FUNCTION, CSS_TOKEN_CHAR,
640 * CSS_TOKEN_NUMBER, CSS_TOKEN_PERCENTAGE, CSS_TOKEN_DIMENSION
641 *
642 * These token types all appear before CSS_TOKEN_LAST_INTERN.
643 * All other token types appear after this magic value.
644 */
645
646 if (t->type < CSS_TOKEN_LAST_INTERN && t->data.data != NULL) {
647 /* Insert token text into the dictionary */
648 lerror = lwc_intern_string((char *)t->data.data,
649 t->data.len, &t->idata);
650 if (lerror != lwc_error_ok)
651 return css_error_from_lwc_error(lerror);
652 } else {
653 t->idata = NULL;
654 }
655
656 *token = t;
657 }
658
659 /* Append token to vector */
660 perror = parserutils_vector_append(parser->tokens,
661 (css_token *) (*token));
662 if (perror != PARSERUTILS_OK)
663 return css_error_from_parserutils_error(perror);
664
665 parser->last_was_ws = ((*token)->type == CSS_TOKEN_S);
666
667 return CSS_OK;
668 }
669
670 /**
671 * Push a token back on the input
672 *
673 * \param parser The parser instance
674 * \param token The token to push back
675 * \return CSS_OK on success.
676 */
pushBack(css_parser * parser,const css_token * token)677 css_error pushBack(css_parser *parser, const css_token *token)
678 {
679 parserutils_error perror;
680
681 /* The pushback buffer depth is 1 token. Assert this. */
682 assert(parser->pushback == NULL);
683
684 perror = parserutils_vector_remove_last(parser->tokens);
685 if (perror != PARSERUTILS_OK)
686 return css_error_from_parserutils_error(perror);
687
688 parser->pushback = token;
689
690 return CSS_OK;
691 }
692
693 /**
694 * Eat whitespace tokens
695 *
696 * \param parser The parser instance
697 * \return CSS_OK on success, appropriate error otherwise
698 */
eatWS(css_parser * parser)699 css_error eatWS(css_parser *parser)
700 {
701 const css_token *token;
702 css_error error;
703
704 error = getToken(parser, &token);
705 if (error != CSS_OK)
706 return error;
707
708 if (token->type != CSS_TOKEN_S) {
709 error = pushBack(parser, token);
710 if (error != CSS_OK)
711 return error;
712 }
713
714 return CSS_OK;
715 }
716
717 /******************************************************************************
718 * Parser stages *
719 ******************************************************************************/
720
parseStart(css_parser * parser)721 css_error parseStart(css_parser *parser)
722 {
723 enum { Initial = 0, AfterWS = 1, AfterStylesheet = 2 };
724 parser_state *state = parserutils_stack_get_current(parser->states);
725 css_error error = CSS_OK;
726
727 /* start -> ws stylesheet EOF */
728
729 switch (state->substate) {
730 case Initial:
731 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
732 printf("Begin stylesheet\n");
733 #endif
734 if (parser->event != NULL) {
735 parser->event(CSS_PARSER_START_STYLESHEET, NULL,
736 parser->event_pw);
737 }
738
739 error = eatWS(parser);
740 if (error != CSS_OK)
741 return error;
742 state->substate = AfterWS;
743 /* Fall through */
744 case AfterWS:
745 {
746 parser_state to = { sStylesheet, Initial };
747 parser_state subsequent = { sStart, AfterStylesheet };
748
749 return transition(parser, to, subsequent);
750 }
751 case AfterStylesheet:
752 error = expect(parser, CSS_TOKEN_EOF);
753 if (error != CSS_OK)
754 return error;
755
756 /* Flag completion, just in case */
757 }
758
759 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
760 parserutils_vector_dump(parser->tokens, __func__, tprinter);
761 printf("End stylesheet\n");
762 #endif
763 if (parser->event != NULL) {
764 parser->event(CSS_PARSER_END_STYLESHEET, NULL,
765 parser->event_pw);
766 }
767
768 discard_tokens(parser);
769
770 return done(parser);
771 }
772
parseStylesheet(css_parser * parser)773 css_error parseStylesheet(css_parser *parser)
774 {
775 enum { Initial = 0, WS = 1 };
776 parser_state *state = parserutils_stack_get_current(parser->states);
777 const css_token *token;
778 css_error error;
779
780 /* stylesheet -> CDO ws stylesheet
781 * -> CDC ws stylesheet
782 * -> statement ws stylesheet
783 * ->
784 */
785
786 while (1) {
787 switch (state->substate) {
788 case Initial:
789 error = getToken(parser, &token);
790 if (error != CSS_OK)
791 return error;
792
793 switch (token->type) {
794 case CSS_TOKEN_EOF:
795 error = pushBack(parser, token);
796 if (error != CSS_OK)
797 return error;
798
799 discard_tokens(parser);
800
801 return done(parser);
802 case CSS_TOKEN_CDO:
803 case CSS_TOKEN_CDC:
804 break;
805 default:
806 {
807 parser_state to = { sStatement, Initial };
808 parser_state subsequent = { sStylesheet, WS };
809
810 error = pushBack(parser, token);
811 if (error != CSS_OK)
812 return error;
813
814 return transition(parser, to, subsequent);
815 }
816 }
817
818 state->substate = WS;
819 /* Fall through */
820 case WS:
821 error = eatWS(parser);
822 if (error != CSS_OK)
823 return error;
824
825 state->substate = Initial;
826 }
827 }
828 }
829
parseStatement(css_parser * parser)830 css_error parseStatement(css_parser *parser)
831 {
832 enum { Initial = 0 };
833 const css_token *token;
834 parser_state to = { sRuleset, Initial };
835 css_error error;
836
837 /* statement -> ruleset
838 * at-rule
839 */
840
841 error = getToken(parser, &token);
842 if (error != CSS_OK)
843 return error;
844
845 if (token->type == CSS_TOKEN_ATKEYWORD)
846 to.state = sAtRule;
847
848 error = pushBack(parser, token);
849 if (error != CSS_OK)
850 return error;
851
852 return transitionNoRet(parser, to);
853 }
854
parseRuleset(css_parser * parser)855 css_error parseRuleset(css_parser *parser)
856 {
857 enum { Initial = 0, Brace = 1, WS = 2 };
858 parser_state *state = parserutils_stack_get_current(parser->states);
859 parser_state to = { sRulesetEnd, Initial };
860 const css_token *token;
861 css_error error;
862
863 /* ruleset -> selector '{' ws ruleset-end
864 * -> '{' ws ruleset-end
865 */
866
867 switch (state->substate) {
868 case Initial:
869 discard_tokens(parser);
870
871 error = getToken(parser, &token);
872 if (error != CSS_OK)
873 return error;
874
875 /* The grammar's ambiguous here -- selectors may start with a
876 * brace. We're going to assume that that won't happen,
877 * however. */
878 if (token->type == CSS_TOKEN_CHAR &&
879 lwc_string_length(token->idata) == 1 &&
880 lwc_string_data(token->idata)[0] == '{') {
881 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
882 printf("Begin ruleset\n");
883 #endif
884 if (parser->event != NULL) {
885 if (parser->event(CSS_PARSER_START_RULESET,
886 NULL, parser->event_pw) ==
887 CSS_INVALID) {
888 parser_state to =
889 { sMalformedSelector, Initial };
890
891 return transitionNoRet(parser, to);
892 }
893 }
894
895 state->substate = WS;
896 goto ws;
897 } else {
898 parser_state to = { sSelector, Initial };
899 parser_state subsequent = { sRuleset, Brace };
900
901 error = pushBack(parser, token);
902 if (error != CSS_OK)
903 return error;
904
905 return transition(parser, to, subsequent);
906 }
907 break;
908 case Brace:
909 if (parser->parseError == true) {
910 parser_state to = { sMalformedSelector, Initial };
911
912 return transitionNoRet(parser, to);
913 }
914
915 error = getToken(parser, &token);
916 if (error != CSS_OK)
917 return error;
918
919 if (token->type != CSS_TOKEN_CHAR ||
920 lwc_string_length(token->idata) != 1 ||
921 lwc_string_data(token->idata)[0] != '{') {
922 /* FOLLOW(selector) contains only '{', but we may
923 * also have seen EOF, which is a parse error. */
924 error = pushBack(parser, token);
925 if (error != CSS_OK)
926 return error;
927
928 parser->parseError = true;
929 return done(parser);
930 }
931
932 /* We don't want to emit the brace, so push it back */
933 error = pushBack(parser, token);
934 if (error != CSS_OK)
935 return error;
936
937 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
938 printf("Begin ruleset\n");
939 parserutils_vector_dump(parser->tokens, __func__, tprinter);
940 #endif
941 if (parser->parseError == false && parser->event != NULL) {
942 if (parser->event(CSS_PARSER_START_RULESET,
943 parser->tokens, parser->event_pw) ==
944 CSS_INVALID)
945 parser->parseError = true;
946 }
947
948 /* Re-read the brace */
949 error = getToken(parser, &token);
950 if (error != CSS_OK)
951 return error;
952
953 state->substate = WS;
954 /* Fall through */
955 case WS:
956 ws:
957 error = eatWS(parser);
958 if (error != CSS_OK)
959 return error;
960
961 break;
962 }
963
964 return transitionNoRet(parser, to);
965 }
966
parseRulesetEnd(css_parser * parser)967 css_error parseRulesetEnd(css_parser *parser)
968 {
969 enum { Initial = 0, DeclList = 1, Brace = 2, WS = 3 };
970 parser_state *state = parserutils_stack_get_current(parser->states);
971 const css_token *token;
972 css_error error;
973
974 /* ruleset-end -> declaration decl-list '}' ws
975 * -> decl-list '}' ws
976 */
977
978 switch (state->substate) {
979 case Initial:
980 error = getToken(parser, &token);
981 if (error != CSS_OK)
982 return error;
983
984 error = pushBack(parser, token);
985 if (error != CSS_OK)
986 return error;
987
988 if (token->type == CSS_TOKEN_EOF)
989 return done(parser);
990
991 /* If this can't possibly be the start of a decl-list, then
992 * attempt to parse a declaration. This will catch any invalid
993 * input at this point and read to the start of the next
994 * declaration. FIRST(decl-list) = (';', '}') */
995 if (token->type != CSS_TOKEN_CHAR ||
996 lwc_string_length(token->idata) != 1 ||
997 (lwc_string_data(token->idata)[0] != '}' &&
998 lwc_string_data(token->idata)[0] != ';')) {
999 parser_state to = { sDeclaration, Initial };
1000 parser_state subsequent = { sRulesetEnd, DeclList };
1001
1002 return transition(parser, to, subsequent);
1003 }
1004
1005 state->substate = DeclList;
1006 /* Fall through */
1007 case DeclList:
1008 {
1009 parser_state to = { sDeclList, Initial };
1010 parser_state subsequent = { sRulesetEnd, Brace };
1011
1012 return transition(parser, to, subsequent);
1013 }
1014 case Brace:
1015 error = getToken(parser, &token);
1016 if (error != CSS_OK)
1017 return error;
1018
1019 if (token->type == CSS_TOKEN_EOF) {
1020 error = pushBack(parser, token);
1021 if (error != CSS_OK)
1022 return error;
1023
1024 return done(parser);
1025 }
1026
1027 if (token->type != CSS_TOKEN_CHAR ||
1028 lwc_string_length(token->idata) != 1 ||
1029 lwc_string_data(token->idata)[0] != '}') {
1030 /* This should never happen, as FOLLOW(decl-list)
1031 * contains only '}' */
1032 assert(0 && "Expected }");
1033 }
1034
1035 state->substate = WS;
1036 /* Fall through */
1037 case WS:
1038 error = eatWS(parser);
1039 if (error != CSS_OK)
1040 return error;
1041
1042 break;
1043 }
1044
1045 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1046 printf("End ruleset\n");
1047 #endif
1048 if (parser->event != NULL) {
1049 parser->event(CSS_PARSER_END_RULESET, NULL, parser->event_pw);
1050 }
1051
1052 return done(parser);
1053 }
1054
parseAtRule(css_parser * parser)1055 css_error parseAtRule(css_parser *parser)
1056 {
1057 enum { Initial = 0, WS = 1, Any = 2, AfterAny = 3 };
1058 parser_state *state = parserutils_stack_get_current(parser->states);
1059 parser_state to = { sAtRuleEnd, Initial };
1060 const css_token *token;
1061 css_error error;
1062
1063 /* at-rule -> ATKEYWORD ws any0 at-rule-end */
1064
1065 switch (state->substate) {
1066 case Initial:
1067 discard_tokens(parser);
1068
1069 error = getToken(parser, &token);
1070 if (error != CSS_OK)
1071 return error;
1072
1073 assert(token->type == CSS_TOKEN_ATKEYWORD);
1074
1075 state->substate = WS;
1076 /* Fall through */
1077 case WS:
1078 error = eatWS(parser);
1079 if (error != CSS_OK)
1080 return error;
1081
1082 state->substate = Any;
1083 /* Fall through */
1084 case Any:
1085 {
1086 parser_state to = { sAny0, Initial };
1087 parser_state subsequent = { sAtRule, AfterAny };
1088
1089 return transition(parser, to, subsequent);
1090 }
1091 case AfterAny:
1092 if (parser->parseError) {
1093 parser_state to = { sMalformedAtRule, Initial };
1094
1095 return transitionNoRet(parser, to);
1096 }
1097
1098 error = getToken(parser, &token);
1099 if (error != CSS_OK)
1100 return error;
1101
1102 /* Grammar ambiguity: any0 can be followed by '{',';',')',']'.
1103 * at-rule can only be followed by '{' and ';'. */
1104 if (token->type == CSS_TOKEN_CHAR &&
1105 lwc_string_length(token->idata) == 1) {
1106 if (lwc_string_data(token->idata)[0] == ')' ||
1107 lwc_string_data(
1108 token->idata)[0] == ']') {
1109 parser_state to = { sAny0, Initial };
1110 parser_state subsequent = { sAtRule, AfterAny };
1111
1112 return transition(parser, to, subsequent);
1113 }
1114 }
1115
1116 error = pushBack(parser, token);
1117 if (error != CSS_OK)
1118 return error;
1119
1120 break;
1121 }
1122
1123 return transitionNoRet(parser, to);
1124 }
1125
parseAtRuleEnd(css_parser * parser)1126 css_error parseAtRuleEnd(css_parser *parser)
1127 {
1128 enum { Initial = 0, WS = 1, AfterBlock = 2 };
1129 parser_state *state = parserutils_stack_get_current(parser->states);
1130 const css_token *token;
1131 css_error error;
1132
1133 /* at-rule-end -> block
1134 * -> ';' ws
1135 */
1136
1137 switch (state->substate) {
1138 case Initial:
1139 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1140 printf("Begin at-rule\n");
1141 parserutils_vector_dump(parser->tokens, __func__, tprinter);
1142 #endif
1143 if (parser->event != NULL) {
1144 if (parser->event(CSS_PARSER_START_ATRULE,
1145 parser->tokens, parser->event_pw) ==
1146 CSS_INVALID) {
1147 parser_state to = { sMalformedAtRule, Initial };
1148
1149 return transitionNoRet(parser, to);
1150 }
1151 }
1152
1153 error = getToken(parser, &token);
1154 if (error != CSS_OK)
1155 return error;
1156
1157 if (token->type == CSS_TOKEN_EOF) {
1158 error = pushBack(parser, token);
1159 if (error != CSS_OK)
1160 return error;
1161
1162 return done(parser);
1163 }
1164
1165 if (token->type != CSS_TOKEN_CHAR ||
1166 lwc_string_length(token->idata) != 1) {
1167 /* Should never happen FOLLOW(at-rule) == '{', ';'*/
1168 assert(0 && "Expected { or ;");
1169 }
1170
1171 if (lwc_string_data(token->idata)[0] == '{') {
1172 parser_state to = { sBlock, Initial };
1173 parser_state subsequent = { sAtRuleEnd, AfterBlock };
1174
1175 error = pushBack(parser, token);
1176 if (error != CSS_OK)
1177 return error;
1178
1179 return transition(parser, to, subsequent);
1180 } else if (lwc_string_data(token->idata)[0] != ';') {
1181 /* Again, should never happen */
1182 assert(0 && "Expected ;");
1183 }
1184
1185 state->substate = WS;
1186 /* Fall through */
1187 case WS:
1188 error = eatWS(parser);
1189 if (error != CSS_OK)
1190 return error;
1191
1192 break;
1193 case AfterBlock:
1194 break;
1195 }
1196
1197 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1198 printf("End at-rule\n");
1199 #endif
1200 if (parser->event != NULL) {
1201 parser->event(CSS_PARSER_END_ATRULE, NULL, parser->event_pw);
1202 }
1203
1204 return done(parser);
1205 }
1206
parseBlock(css_parser * parser)1207 css_error parseBlock(css_parser *parser)
1208 {
1209 enum { Initial = 0, WS = 1, Content = 2, Brace = 3, WS2 = 4 };
1210 parser_state *state = parserutils_stack_get_current(parser->states);
1211 const css_token *token;
1212 css_error error;
1213
1214 /* block -> '{' ws block-content '}' ws */
1215
1216 switch (state->substate) {
1217 case Initial:
1218 error = getToken(parser, &token);
1219 if (error != CSS_OK)
1220 return error;
1221
1222 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1223 printf("Begin block\n");
1224 #endif
1225 if (parser->event != NULL) {
1226 parser->event(CSS_PARSER_START_BLOCK, NULL,
1227 parser->event_pw);
1228 }
1229
1230 if (token->type != CSS_TOKEN_CHAR ||
1231 lwc_string_length(token->idata) != 1 ||
1232 lwc_string_data(token->idata)[0] != '{') {
1233 /* This should never happen, as FIRST(block) == '{' */
1234 assert(0 && "Expected {");
1235 }
1236
1237 discard_tokens(parser);
1238
1239 state->substate = WS;
1240 /* Fall through */
1241 case WS:
1242 error = eatWS(parser);
1243 if (error != CSS_OK)
1244 return error;
1245
1246 state->substate = Content;
1247 /* Fall through */
1248 case Content:
1249 {
1250 parser_state to = { sBlockContent, Initial };
1251 parser_state subsequent = { sBlock, Brace };
1252
1253 return transition(parser, to, subsequent);
1254 }
1255 case Brace:
1256 error = getToken(parser, &token);
1257 if (error != CSS_OK)
1258 return error;
1259
1260 if (token->type == CSS_TOKEN_EOF) {
1261 error = pushBack(parser, token);
1262 if (error != CSS_OK)
1263 return error;
1264
1265 return done(parser);
1266 }
1267
1268 if (token->type != CSS_TOKEN_CHAR ||
1269 lwc_string_length(token->idata) != 1 ||
1270 lwc_string_data(token->idata)[0] != '}') {
1271 /* This should never happen, as
1272 * FOLLOW(block-content) == '}' */
1273 assert(0 && "Expected }");
1274 }
1275
1276 state->substate = WS2;
1277 /* Fall through */
1278 case WS2:
1279 error = eatWS(parser);
1280 if (error != CSS_OK)
1281 return error;
1282
1283 break;
1284 }
1285
1286 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1287 printf("End block\n");
1288 #endif
1289 if (parser->event != NULL) {
1290 parser->event(CSS_PARSER_END_BLOCK, NULL, parser->event_pw);
1291 }
1292
1293 discard_tokens(parser);
1294
1295 return done(parser);
1296 }
1297
parseBlockContent(css_parser * parser)1298 css_error parseBlockContent(css_parser *parser)
1299 {
1300 enum { Initial = 0, WS = 1 };
1301 parser_state *state = parserutils_stack_get_current(parser->states);
1302 const css_token *token;
1303 css_error error;
1304
1305 /* block-content -> any block-content
1306 * -> block block-content
1307 * -> ATKEYWORD ws block-content
1308 * -> ';' ws block-content
1309 * ->
1310 */
1311
1312 while (1) {
1313 switch (state->substate) {
1314 case Initial:
1315 error = getToken(parser, &token);
1316 if (error != CSS_OK)
1317 return error;
1318
1319 if (token->type == CSS_TOKEN_ATKEYWORD) {
1320 state->substate = WS;
1321 } else if (token->type == CSS_TOKEN_CHAR) {
1322 if (lwc_string_length(token->idata) == 1 &&
1323 lwc_string_data(
1324 token->idata)[0] == '{') {
1325 /* Grammar ambiguity. Assume block */
1326 parser_state to = { sBlock, Initial };
1327 parser_state subsequent =
1328 { sBlockContent, Initial };
1329
1330 error = pushBack(parser, token);
1331 if (error != CSS_OK)
1332 return error;
1333
1334 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1335 parserutils_vector_dump(parser->tokens,
1336 __func__, tprinter);
1337 #endif
1338 if (parser->event != NULL) {
1339 parser->event(
1340 CSS_PARSER_BLOCK_CONTENT,
1341 parser->tokens,
1342 parser->event_pw);
1343 }
1344
1345 discard_tokens(parser);
1346
1347 return transition(parser, to,
1348 subsequent);
1349 } else if (lwc_string_length(
1350 token->idata) == 1 &&
1351 lwc_string_data(
1352 token->idata)[0] == ';') {
1353 /* Grammar ambiguity. Assume semi */
1354 error = pushBack(parser, token);
1355 if (error != CSS_OK)
1356 return error;
1357
1358 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1359 parserutils_vector_dump(parser->tokens,
1360 __func__, tprinter);
1361 #endif
1362 if (parser->event != NULL) {
1363 parser->event(
1364 CSS_PARSER_BLOCK_CONTENT,
1365 parser->tokens,
1366 parser->event_pw);
1367 }
1368
1369 error = getToken(parser, &token);
1370 if (error != CSS_OK)
1371 return error;
1372
1373 discard_tokens(parser);
1374
1375 state->substate = WS;
1376 } else if (lwc_string_length(
1377 token->idata) == 1 &&
1378 lwc_string_data(
1379 token->idata)[0] == '}') {
1380 /* Grammar ambiguity. Assume end */
1381 error = pushBack(parser, token);
1382 if (error != CSS_OK)
1383 return error;
1384
1385 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1386 parserutils_vector_dump(parser->tokens,
1387 __func__, tprinter);
1388 #endif
1389 if (parser->event != NULL) {
1390 parser->event(
1391 CSS_PARSER_END_BLOCK_CONTENT,
1392 parser->tokens,
1393 parser->event_pw);
1394 }
1395
1396 discard_tokens(parser);
1397
1398 return done(parser);
1399 }
1400 } else if (token->type == CSS_TOKEN_EOF) {
1401 error = pushBack(parser, token);
1402 if (error != CSS_OK)
1403 return error;
1404
1405 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1406 parserutils_vector_dump(parser->tokens,
1407 __func__, tprinter);
1408 #endif
1409 if (parser->event != NULL) {
1410 parser->event(CSS_PARSER_BLOCK_CONTENT,
1411 parser->tokens,
1412 parser->event_pw);
1413 }
1414
1415 discard_tokens(parser);
1416
1417 return done(parser);
1418 }
1419
1420 if (state->substate == Initial) {
1421 parser_state to = { sAny, Initial };
1422 parser_state subsequent =
1423 { sBlockContent, Initial };
1424
1425 error = pushBack(parser, token);
1426 if (error != CSS_OK)
1427 return error;
1428
1429 return transition(parser, to, subsequent);
1430 }
1431
1432 break;
1433 case WS:
1434 error = eatWS(parser);
1435 if (error != CSS_OK)
1436 return error;
1437
1438 state->substate = Initial;
1439 }
1440 }
1441
1442 return done(parser);
1443 }
1444
parseSelector(css_parser * parser)1445 css_error parseSelector(css_parser *parser)
1446 {
1447 enum { Initial = 0, AfterAny1 = 1 };
1448 parser_state *state = parserutils_stack_get_current(parser->states);
1449
1450 /* selector -> any1 */
1451
1452 switch (state->substate) {
1453 case Initial:
1454 {
1455 parser_state to = { sAny1, Initial };
1456 parser_state subsequent = { sSelector, AfterAny1 };
1457
1458 discard_tokens(parser);
1459
1460 return transition(parser, to, subsequent);
1461 }
1462 case AfterAny1:
1463 break;
1464 }
1465
1466 return done(parser);
1467 }
1468
parseDeclaration(css_parser * parser)1469 css_error parseDeclaration(css_parser *parser)
1470 {
1471 enum { Initial = 0, Colon = 1, WS = 2, AfterValue1 = 3 };
1472 parser_state *state = parserutils_stack_get_current(parser->states);
1473 const css_token *token;
1474 css_error error;
1475
1476 /* declaration -> property ':' ws value1 */
1477
1478 switch (state->substate) {
1479 case Initial:
1480 {
1481 parser_state to = { sProperty, Initial };
1482 parser_state subsequent = { sDeclaration, Colon };
1483
1484 discard_tokens(parser);
1485
1486 return transition(parser, to, subsequent);
1487 }
1488 case Colon:
1489 if (parser->parseError) {
1490 parser_state to = { sMalformedDecl, Initial };
1491
1492 parser->parseError = false;
1493
1494 return transitionNoRet(parser, to);
1495 }
1496
1497 error = getToken(parser, &token);
1498 if (error != CSS_OK)
1499 return error;
1500
1501 if (token->type == CSS_TOKEN_EOF) {
1502 error = pushBack(parser, token);
1503 if (error != CSS_OK)
1504 return error;
1505
1506 return done(parser);
1507 }
1508
1509 if (token->type != CSS_TOKEN_CHAR ||
1510 lwc_string_length(token->idata) != 1 ||
1511 lwc_string_data(token->idata)[0] != ':') {
1512 /* parse error -- expected : */
1513 parser_state to = { sMalformedDecl, Initial };
1514
1515 error = pushBack(parser, token);
1516 if (error != CSS_OK)
1517 return error;
1518
1519 return transitionNoRet(parser, to);
1520 }
1521
1522 state->substate = WS;
1523 /* Fall through */
1524 case WS:
1525 {
1526 parser_state to = { sValue1, Initial };
1527 parser_state subsequent = { sDeclaration, AfterValue1 };
1528
1529 error = eatWS(parser);
1530 if (error != CSS_OK)
1531 return error;
1532
1533 return transition(parser, to, subsequent);
1534 }
1535 case AfterValue1:
1536 if (parser->parseError) {
1537 parser_state to = { sMalformedDecl, Initial };
1538
1539 parser->parseError = false;
1540
1541 return transitionNoRet(parser, to);
1542 }
1543
1544 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1545 parserutils_vector_dump(parser->tokens, __func__, tprinter);
1546 #endif
1547 if (parser->event != NULL) {
1548 parser->event(CSS_PARSER_DECLARATION, parser->tokens,
1549 parser->event_pw);
1550 }
1551 break;
1552 }
1553
1554 return done(parser);
1555 }
1556
parseDeclList(css_parser * parser)1557 css_error parseDeclList(css_parser *parser)
1558 {
1559 enum { Initial = 0, WS = 1 };
1560 parser_state *state = parserutils_stack_get_current(parser->states);
1561 parser_state to = { sDeclListEnd, Initial };
1562 const css_token *token;
1563 css_error error;
1564
1565 /* decl-list -> ';' ws decl-list-end
1566 * ->
1567 */
1568
1569 switch (state->substate) {
1570 case Initial:
1571 error = getToken(parser, &token);
1572 if (error != CSS_OK)
1573 return error;
1574
1575 if (token->type == CSS_TOKEN_EOF) {
1576 error = pushBack(parser, token);
1577 if (error != CSS_OK)
1578 return error;
1579
1580 return done(parser);
1581 }
1582
1583 if (token->type != CSS_TOKEN_CHAR ||
1584 lwc_string_length(token->idata) != 1 ||
1585 (lwc_string_data(token->idata)[0] != '}' &&
1586 lwc_string_data(token->idata)[0] != ';')) {
1587 /* Should never happen */
1588 assert(0 && "Expected ; or }");
1589 }
1590
1591 if (lwc_string_data(token->idata)[0] == '}') {
1592 error = pushBack(parser, token);
1593 if (error != CSS_OK)
1594 return error;
1595
1596 return done(parser);
1597 } else {
1598 /* ; */
1599 state->substate = WS;
1600 }
1601
1602 /* Fall through */
1603 case WS:
1604 error = eatWS(parser);
1605 if (error != CSS_OK)
1606 return error;
1607
1608 break;
1609 }
1610
1611 return transitionNoRet(parser, to);
1612 }
1613
parseDeclListEnd(css_parser * parser)1614 css_error parseDeclListEnd(css_parser *parser)
1615 {
1616 enum { Initial = 0, AfterDeclaration = 1 };
1617 parser_state *state = parserutils_stack_get_current(parser->states);
1618 parser_state to = { sDeclList, Initial };
1619 const css_token *token;
1620 css_error error;
1621
1622 /* decl-list-end -> declaration decl-list
1623 * -> decl-list
1624 */
1625
1626 switch (state->substate) {
1627 case Initial:
1628 error = getToken(parser, &token);
1629 if (error != CSS_OK)
1630 return error;
1631
1632 if (token->type != CSS_TOKEN_CHAR ||
1633 lwc_string_length(token->idata) != 1 ||
1634 (lwc_string_data(token->idata)[0] != ';' &&
1635 lwc_string_data(token->idata)[0] != '}')) {
1636 parser_state to = { sDeclaration, Initial };
1637 parser_state subsequent =
1638 { sDeclListEnd, AfterDeclaration };
1639
1640 error = pushBack(parser, token);
1641 if (error != CSS_OK)
1642 return error;
1643
1644 return transition(parser, to, subsequent);
1645 } else {
1646 error = pushBack(parser, token);
1647 if (error != CSS_OK)
1648 return error;
1649 }
1650
1651 state->substate = AfterDeclaration;
1652 /* Fall through */
1653 case AfterDeclaration:
1654 break;
1655 }
1656
1657 return transitionNoRet(parser, to);
1658 }
1659
parseProperty(css_parser * parser)1660 css_error parseProperty(css_parser *parser)
1661 {
1662 enum { Initial = 0, WS = 1 };
1663 parser_state *state = parserutils_stack_get_current(parser->states);
1664 const css_token *token;
1665 css_error error;
1666
1667 /* property -> IDENT ws */
1668
1669 switch (state->substate) {
1670 case Initial:
1671 error = getToken(parser, &token);
1672 if (error != CSS_OK)
1673 return error;
1674
1675 if (token->type == CSS_TOKEN_EOF) {
1676 error = pushBack(parser, token);
1677 if (error != CSS_OK)
1678 return error;
1679
1680 return done(parser);
1681 }
1682
1683 if (token->type != CSS_TOKEN_IDENT) {
1684 /* parse error */
1685 parser->parseError = true;
1686
1687 return done(parser);
1688 }
1689
1690 state->substate = WS;
1691 /* Fall through */
1692 case WS:
1693 error = eatWS(parser);
1694 if (error != CSS_OK)
1695 return error;
1696
1697 break;
1698 }
1699
1700 return done(parser);
1701 }
1702
parseValue1(css_parser * parser)1703 css_error parseValue1(css_parser *parser)
1704 {
1705 enum { Initial = 0, AfterValue = 1 };
1706 parser_state *state = parserutils_stack_get_current(parser->states);
1707 parser_state to = { sValue0, Initial };
1708 const css_token *token;
1709 css_error error;
1710
1711 /* value1 -> value value0 */
1712
1713 switch (state->substate) {
1714 case Initial:
1715 {
1716 parser_state to = { sValue, Initial };
1717 parser_state subsequent = { sValue1, AfterValue };
1718
1719 error = getToken(parser, &token);
1720 if (error != CSS_OK)
1721 return error;
1722
1723 error = pushBack(parser, token);
1724 if (error != CSS_OK)
1725 return error;
1726
1727 /* Grammar ambiguity -- assume ';' or '}' mark end */
1728 if (token->type == CSS_TOKEN_CHAR &&
1729 lwc_string_length(token->idata) == 1 &&
1730 (lwc_string_data(token->idata)[0] == ';' ||
1731 lwc_string_data(token->idata)[0] == '}')) {
1732 /* Parse error */
1733 parser->parseError = true;
1734
1735 return done(parser);
1736 }
1737
1738 return transition(parser, to, subsequent);
1739 }
1740 case AfterValue:
1741 if (parser->parseError)
1742 return done(parser);
1743 break;
1744 }
1745
1746 return transitionNoRet(parser, to);
1747 }
1748
parseValue0(css_parser * parser)1749 css_error parseValue0(css_parser *parser)
1750 {
1751 enum { Initial = 0, AfterValue = 1 };
1752 parser_state *state = parserutils_stack_get_current(parser->states);
1753 const css_token *token;
1754 css_error error;
1755
1756 /* value0 -> value value0
1757 * ->
1758 */
1759
1760 while (1) {
1761 switch (state->substate) {
1762 case Initial:
1763 {
1764 parser_state to = { sValue, Initial };
1765 parser_state subsequent = { sValue0, AfterValue };
1766
1767 error = getToken(parser, &token);
1768 if (error != CSS_OK)
1769 return error;
1770
1771 error = pushBack(parser, token);
1772 if (error != CSS_OK)
1773 return error;
1774
1775 if (token->type == CSS_TOKEN_EOF)
1776 return done(parser);
1777
1778 /* Grammar ambiguity -- assume ';' or '}' mark end */
1779 if (token->type == CSS_TOKEN_CHAR &&
1780 lwc_string_length(
1781 token->idata) == 1 &&
1782 (lwc_string_data(
1783 token->idata)[0] == ';' ||
1784 lwc_string_data(
1785 token->idata)[0] == '}')) {
1786 return done(parser);
1787 }
1788
1789 return transition(parser, to, subsequent);
1790 }
1791 case AfterValue:
1792 if (parser->parseError)
1793 return done(parser);
1794
1795 state->substate = Initial;
1796
1797 break;
1798 }
1799 }
1800
1801 return done(parser);
1802 }
1803
parseValue(css_parser * parser)1804 css_error parseValue(css_parser *parser)
1805 {
1806 enum { Initial = 0, WS = 1 };
1807 parser_state *state = parserutils_stack_get_current(parser->states);
1808 const css_token *token;
1809 css_error error;
1810
1811 /* value -> any
1812 * -> block
1813 * -> ATKEYWORD ws
1814 */
1815
1816 switch (state->substate) {
1817 case Initial:
1818 error = getToken(parser, &token);
1819 if (error != CSS_OK)
1820 return error;
1821
1822 if (token->type == CSS_TOKEN_ATKEYWORD) {
1823 state->substate = WS;
1824 } else if (token->type == CSS_TOKEN_CHAR &&
1825 lwc_string_length(token->idata) == 1 &&
1826 lwc_string_data(token->idata)[0] == '{') {
1827 /* Grammar ambiguity. Assume block. */
1828 parser_state to = { sBlock, Initial };
1829
1830 error = pushBack(parser, token);
1831 if (error != CSS_OK)
1832 return error;
1833
1834 return transitionNoRet(parser, to);
1835 } else {
1836 parser_state to = { sAny, Initial };
1837
1838 error = pushBack(parser, token);
1839 if (error != CSS_OK)
1840 return error;
1841
1842 return transitionNoRet(parser, to);
1843 }
1844
1845 /* Fall through */
1846 case WS:
1847 error = eatWS(parser);
1848 if (error != CSS_OK)
1849 return error;
1850
1851 break;
1852 }
1853
1854 return done(parser);
1855 }
1856
parseAny0(css_parser * parser)1857 css_error parseAny0(css_parser *parser)
1858 {
1859 enum { Initial = 0, AfterAny = 1 };
1860 parser_state *state = parserutils_stack_get_current(parser->states);
1861 const css_token *token;
1862 css_error error;
1863
1864 /* any0 -> any any0
1865 * ->
1866 */
1867
1868 while (1) {
1869 switch (state->substate) {
1870 case Initial:
1871 {
1872 parser_state to = { sAny, Initial };
1873 parser_state subsequent = { sAny0, AfterAny };
1874
1875 error = getToken(parser, &token);
1876 if (error != CSS_OK)
1877 return error;
1878
1879 error = pushBack(parser, token);
1880 if (error != CSS_OK)
1881 return error;
1882
1883 if (token->type == CSS_TOKEN_EOF)
1884 return done(parser);
1885
1886 /* Grammar ambiguity:
1887 * assume '{', ';', ')', ']' mark end */
1888 if (token->type == CSS_TOKEN_CHAR &&
1889 lwc_string_length(
1890 token->idata) == 1 &&
1891 (lwc_string_data(
1892 token->idata)[0] == '{' ||
1893 lwc_string_data(
1894 token->idata)[0] == ';' ||
1895 lwc_string_data(
1896 token->idata)[0] == ')' ||
1897 lwc_string_data(
1898 token->idata)[0] == ']')) {
1899 return done(parser);
1900 }
1901
1902 return transition(parser, to, subsequent);
1903 }
1904 case AfterAny:
1905 if (parser->parseError)
1906 return done(parser);
1907
1908 state->substate = Initial;
1909
1910 break;
1911 }
1912 }
1913
1914 return done(parser);
1915 }
1916
parseAny1(css_parser * parser)1917 css_error parseAny1(css_parser *parser)
1918 {
1919 enum { Initial = 0, AfterAny = 1, AfterAny0 = 2 };
1920 parser_state *state = parserutils_stack_get_current(parser->states);
1921 const css_token *token;
1922 css_error error;
1923
1924 /* any1 -> any any0 */
1925
1926 switch (state->substate) {
1927 case Initial:
1928 {
1929 parser_state to = { sAny, Initial };
1930 parser_state subsequent = { sAny1, AfterAny };
1931
1932 return transition(parser, to, subsequent);
1933 }
1934 case AfterAny:
1935 {
1936 parser_state to = { sAny0, Initial };
1937 parser_state subsequent = { sAny1, AfterAny0 };
1938
1939 if (parser->parseError)
1940 return done(parser);
1941
1942 return transition(parser, to, subsequent);
1943 }
1944 case AfterAny0:
1945 if (parser->parseError)
1946 return done(parser);
1947
1948 error = getToken(parser, &token);
1949 if (error != CSS_OK)
1950 return error;
1951
1952 error = pushBack(parser, token);
1953 if (error != CSS_OK)
1954 return error;
1955
1956 if (token->type == CSS_TOKEN_EOF)
1957 return done(parser);
1958
1959 /* Grammar ambiguity: any0 can be followed by
1960 * '{', ';', ')', ']'. any1 can only be followed by '{'. */
1961 if (token->type == CSS_TOKEN_CHAR &&
1962 lwc_string_length(token->idata) == 1) {
1963 if (lwc_string_data(token->idata)[0] == ';' ||
1964 lwc_string_data(
1965 token->idata)[0] == ')' ||
1966 lwc_string_data(
1967 token->idata)[0] == ']') {
1968 parser_state to = { sAny, Initial };
1969 parser_state subsequent = { sAny1, AfterAny };
1970
1971 return transition(parser, to, subsequent);
1972 } else if (lwc_string_data(token->idata)[0] != '{') {
1973 /* parse error */
1974 parser->parseError = true;
1975 }
1976 } else {
1977 /* parse error */
1978 parser->parseError = true;
1979 }
1980 }
1981
1982 return done(parser);
1983 }
1984
parseAny(css_parser * parser)1985 css_error parseAny(css_parser *parser)
1986 {
1987 enum { Initial = 0, WS = 1, AfterAny0 = 2, WS2 = 3 };
1988 parser_state *state = parserutils_stack_get_current(parser->states);
1989 const css_token *token;
1990 css_error error;
1991
1992 /* any -> IDENT ws
1993 * -> NUMBER ws
1994 * -> PERCENTAGE ws
1995 * -> DIMENSION ws
1996 * -> STRING ws
1997 * -> CHAR ws
1998 * -> URI ws
1999 * -> HASH ws
2000 * -> UNICODE-RANGE ws
2001 * -> INCLUDES ws
2002 * -> DASHMATCH ws
2003 * -> PREFIXMATCH ws
2004 * -> SUFFIXMATCH ws
2005 * -> SUBSTRINGMATCH ws
2006 * -> FUNCTION ws any0 ')' ws
2007 * -> '(' ws any0 ')' ws
2008 * -> '[' ws any0 ']' ws
2009 */
2010
2011 switch (state->substate) {
2012 case Initial:
2013 error = getToken(parser, &token);
2014 if (error != CSS_OK)
2015 return error;
2016
2017 if (token->type != CSS_TOKEN_IDENT &&
2018 token->type != CSS_TOKEN_NUMBER &&
2019 token->type != CSS_TOKEN_PERCENTAGE &&
2020 token->type != CSS_TOKEN_DIMENSION &&
2021 token->type != CSS_TOKEN_STRING &&
2022 token->type != CSS_TOKEN_CHAR &&
2023 token->type != CSS_TOKEN_URI &&
2024 token->type != CSS_TOKEN_HASH &&
2025 token->type != CSS_TOKEN_UNICODE_RANGE &&
2026 token->type != CSS_TOKEN_INCLUDES &&
2027 token->type != CSS_TOKEN_DASHMATCH &&
2028 token->type != CSS_TOKEN_PREFIXMATCH &&
2029 token->type != CSS_TOKEN_SUFFIXMATCH &&
2030 token->type != CSS_TOKEN_SUBSTRINGMATCH &&
2031 token->type != CSS_TOKEN_FUNCTION) {
2032 /* parse error */
2033 parser->parseError = true;
2034
2035 return done(parser);
2036 }
2037
2038 if (token->type == CSS_TOKEN_FUNCTION) {
2039 parserutils_stack_push(parser->open_items, &")"[0]);
2040 state->substate = WS;
2041 } else if (token->type == CSS_TOKEN_CHAR &&
2042 lwc_string_length(token->idata) == 1 &&
2043 (lwc_string_data(token->idata)[0] == '(' ||
2044 lwc_string_data(token->idata)[0] == '[')) {
2045 parserutils_stack_push(parser->open_items,
2046 &(lwc_string_data(
2047 token->idata)[0] == '(' ? ")" : "]")[0]);
2048 state->substate = WS;
2049 } else {
2050 state->substate = WS2;
2051 }
2052 /* Fall through */
2053 case WS:
2054 case WS2:
2055 ws2:
2056 {
2057 parser_state to = { sAny0, Initial };
2058 parser_state subsequent = { sAny, AfterAny0 };
2059
2060 error = eatWS(parser);
2061 if (error != CSS_OK)
2062 return error;
2063
2064 if (state->substate == WS2)
2065 break;
2066
2067 return transition(parser, to, subsequent);
2068 }
2069 case AfterAny0:
2070 {
2071 parser_state to = { sAny0, Initial };
2072 parser_state subsequent = { sAny, AfterAny0 };
2073
2074 error = getToken(parser, &token);
2075 if (error != CSS_OK)
2076 return error;
2077
2078 if (token->type == CSS_TOKEN_EOF) {
2079 error = pushBack(parser, token);
2080 if (error != CSS_OK)
2081 return error;
2082
2083 /* parse error */
2084 parser->parseError = true;
2085
2086 return done(parser);
2087 }
2088
2089 /* Match correct close bracket (grammar ambiguity) */
2090 if (token->type == CSS_TOKEN_CHAR &&
2091 lwc_string_length(token->idata) == 1 &&
2092 lwc_string_data(token->idata)[0] ==
2093 ((uint8_t *) parserutils_stack_get_current(
2094 parser->open_items))[0]) {
2095 parserutils_stack_pop(parser->open_items, NULL);
2096 state->substate = WS2;
2097 goto ws2;
2098 }
2099
2100 return transition(parser, to, subsequent);
2101 }
2102 }
2103
2104 return done(parser);
2105 }
2106
parseMalformedDeclaration(css_parser * parser)2107 css_error parseMalformedDeclaration(css_parser *parser)
2108 {
2109 enum { Initial = 0, Go = 1 };
2110 parser_state *state = parserutils_stack_get_current(parser->states);
2111 const css_token *token = NULL;
2112 css_error error;
2113
2114 /* Malformed declaration: read everything up to the next ; or }
2115 * We must ensure that pairs of {}, (), [], are balanced */
2116
2117 switch (state->substate) {
2118 case Initial:
2119 {
2120 /* Clear stack of open items */
2121 while (parserutils_stack_pop(parser->open_items, NULL) ==
2122 PARSERUTILS_OK)
2123 ;
2124
2125 state->substate = Go;
2126 }
2127 /* Fall through */
2128 case Go:
2129 while (1) {
2130 char want;
2131 char *match;
2132 const char *data;
2133 size_t len;
2134
2135 error = getToken(parser, &token);
2136 if (error != CSS_OK)
2137 return error;
2138
2139 if (token->type == CSS_TOKEN_EOF)
2140 break;
2141
2142 if (token->type != CSS_TOKEN_CHAR)
2143 continue;
2144
2145 len = lwc_string_length(token->idata);
2146 data = lwc_string_data(token->idata);
2147
2148 if (len != 1 || (data[0] != '{' && data[0] != '}' &&
2149 data[0] != '[' && data[0] != ']' &&
2150 data[0] != '(' && data[0] != ')' &&
2151 data[0] != ';'))
2152 continue;
2153
2154 match = parserutils_stack_get_current(
2155 parser->open_items);
2156
2157 /* If the stack is empty, then we're done if we've got
2158 * either a ';' or '}' */
2159 if (match == NULL) {
2160 if (data[0] == ';' || data[0] == '}')
2161 break;
2162 }
2163
2164 /* Regardless, if we've got a semicolon, ignore it */
2165 if (data[0] == ';')
2166 continue;
2167
2168 /* Get corresponding start tokens for end tokens */
2169 switch (data[0]) {
2170 case '}':
2171 want = '{';
2172 break;
2173 case ']':
2174 want = '[';
2175 break;
2176 case ')':
2177 want = '(';
2178 break;
2179 default:
2180 want = 0;
2181 break;
2182 }
2183
2184 /* Either pop off the stack, if we've matched the
2185 * current item, or push the start token on */
2186 if (match != NULL && *match == want) {
2187 parserutils_stack_pop(
2188 parser->open_items, NULL);
2189 } else if (want == 0) {
2190 parserutils_stack_push(parser->open_items,
2191 &data[0]);
2192 }
2193 }
2194 }
2195
2196 /* Push the last token (';', '}' or EOF) back */
2197 error = pushBack(parser, token);
2198 if (error != CSS_OK)
2199 return error;
2200
2201 /* Discard the tokens we've read */
2202 discard_tokens(parser);
2203
2204 return done(parser);
2205 }
2206
parseMalformedSelector(css_parser * parser)2207 css_error parseMalformedSelector(css_parser *parser)
2208 {
2209 enum { Initial = 0, Go = 1 };
2210 parser_state *state = parserutils_stack_get_current(parser->states);
2211 const css_token *token;
2212 css_error error;
2213
2214 /* Malformed selector: discard the entirety of the next block,
2215 * ensuring we correctly match pairs of {}, [], and (). */
2216
2217 switch (state->substate) {
2218 case Initial:
2219 /* Clear the stack of open items */
2220 while (parserutils_stack_pop(parser->open_items, NULL) ==
2221 PARSERUTILS_OK)
2222 ;
2223
2224 state->substate = Go;
2225 /* Fall through */
2226 case Go:
2227 while (1) {
2228 char want;
2229 char *match;
2230 const char *data;
2231 size_t len;
2232
2233 error = getToken(parser, &token);
2234 if (error != CSS_OK)
2235 return error;
2236
2237 if (token->type == CSS_TOKEN_EOF)
2238 break;
2239
2240 if (token->type != CSS_TOKEN_CHAR)
2241 continue;
2242
2243 len = lwc_string_length(token->idata);
2244 data = lwc_string_data(token->idata);
2245
2246 if (len != 1 || (data[0] != '{' && data[0] != '}' &&
2247 data[0] != '[' && data[0] != ']' &&
2248 data[0] != '(' && data[0] != ')'))
2249 continue;
2250
2251 match = parserutils_stack_get_current(
2252 parser->open_items);
2253
2254 /* Get corresponding start tokens for end tokens */
2255 switch (data[0]) {
2256 case '}':
2257 want = '{';
2258 break;
2259 case ']':
2260 want = '[';
2261 break;
2262 case ')':
2263 want = '(';
2264 break;
2265 default:
2266 want = 0;
2267 break;
2268 }
2269
2270 /* Either pop off the stack, if we've matched the
2271 * current item, or push the start token on */
2272 if (match != NULL && *match == want) {
2273 parserutils_stack_pop(
2274 parser->open_items, NULL);
2275 } else if (want == 0) {
2276 parserutils_stack_push(parser->open_items,
2277 &data[0]);
2278 }
2279
2280 /* If we encountered a '}', there was data on the stack
2281 * before, and the stack's now empty, then we've popped
2282 * a '{' off the stack. That means we've found the end
2283 * of the block, so we're done here. */
2284 if (want == '{' && match != NULL &&
2285 parserutils_stack_get_current(
2286 parser->open_items) == NULL)
2287 break;
2288 }
2289 }
2290
2291 /* Consume any trailing whitespace after the ruleset */
2292 error = eatWS(parser);
2293 if (error != CSS_OK)
2294 return error;
2295
2296 /* Discard the tokens we've read */
2297 discard_tokens(parser);
2298
2299 return done(parser);
2300 }
2301
parseMalformedAtRule(css_parser * parser)2302 css_error parseMalformedAtRule(css_parser *parser)
2303 {
2304 enum { Initial = 0, Go = 1 };
2305 parser_state *state = parserutils_stack_get_current(parser->states);
2306 const css_token *token = NULL;
2307 css_error error;
2308
2309 /* Malformed at-rule: read everything up to the next ; or the next
2310 * block, whichever is first.
2311 * We must ensure that pairs of {}, (), [], are balanced */
2312
2313 switch (state->substate) {
2314 case Initial:
2315 {
2316 /* Clear stack of open items */
2317 while (parserutils_stack_pop(parser->open_items, NULL) ==
2318 PARSERUTILS_OK)
2319 ;
2320
2321 state->substate = Go;
2322 }
2323 /* Fall through */
2324 case Go:
2325 while (1) {
2326 char want;
2327 char *match;
2328 const char *data;
2329 size_t len;
2330
2331 error = getToken(parser, &token);
2332 if (error != CSS_OK)
2333 return error;
2334
2335 if (token->type == CSS_TOKEN_EOF)
2336 break;
2337
2338 if (token->type != CSS_TOKEN_CHAR)
2339 continue;
2340
2341 len = lwc_string_length(token->idata);
2342 data = lwc_string_data(token->idata);
2343
2344 if (len != 1 || (data[0] != '{' && data[0] != '}' &&
2345 data[0] != '[' && data[0] != ']' &&
2346 data[0] != '(' && data[0] != ')' &&
2347 data[0] != ';'))
2348 continue;
2349
2350 match = parserutils_stack_get_current(
2351 parser->open_items);
2352
2353 /* If we have a semicolon, then we're either done or
2354 * need to ignore it */
2355 if (data[0] == ';') {
2356 if (match == NULL)
2357 break;
2358 else
2359 continue;
2360 }
2361
2362 /* Get corresponding start tokens for end tokens */
2363 switch (data[0]) {
2364 case '}':
2365 want = '{';
2366 break;
2367 case ']':
2368 want = '[';
2369 break;
2370 case ')':
2371 want = '(';
2372 break;
2373 default:
2374 want = 0;
2375 break;
2376 }
2377
2378 /* Either pop off the stack, if we've matched the
2379 * current item, or push the start token on */
2380 if (match != NULL && *match == want) {
2381 parserutils_stack_pop(
2382 parser->open_items, NULL);
2383 } else if (want == 0) {
2384 parserutils_stack_push(parser->open_items,
2385 &data[0]);
2386 }
2387
2388 /* If we encountered a '}', there was data on the stack
2389 * before, and the stack's now empty, then we've popped
2390 * a '{' off the stack. That means we've found the end
2391 * of the block, so we're done here. */
2392 if (want == '{' && match != NULL &&
2393 parserutils_stack_get_current(
2394 parser->open_items) == NULL)
2395 break;
2396 }
2397 }
2398
2399 /* Consume any trailing whitespace after the at-rule */
2400 error = eatWS(parser);
2401 if (error != CSS_OK)
2402 return error;
2403
2404 /* Discard the tokens we've read */
2405 discard_tokens(parser);
2406
2407 return done(parser);
2408 }
2409
parseInlineStyle(css_parser * parser)2410 css_error parseInlineStyle(css_parser *parser)
2411 {
2412 enum { Initial = 0, WS = 1, AfterISBody0 = 2 };
2413 parser_state *state = parserutils_stack_get_current(parser->states);
2414 css_error error;
2415
2416 /* inline-style = ws is-body0 */
2417
2418 switch (state->substate) {
2419 case Initial:
2420 /* Emit fake events such that the language parser knows
2421 * no different from a normal parse. */
2422
2423 if (parser->event != NULL) {
2424 /* 1) begin stylesheet */
2425 parser->event(CSS_PARSER_START_STYLESHEET, NULL,
2426 parser->event_pw);
2427
2428 /* 2) begin ruleset */
2429 parser->event(CSS_PARSER_START_RULESET, NULL,
2430 parser->event_pw);
2431 }
2432
2433 /* Fall through */
2434 case WS:
2435 {
2436 parser_state to = { sISBody0, Initial };
2437 parser_state subsequent = { sInlineStyle, AfterISBody0 };
2438
2439 state->substate = WS;
2440
2441 /* Consume leading whitespace */
2442 error = eatWS(parser);
2443 if (error != CSS_OK)
2444 return error;
2445
2446 return transition(parser, to, subsequent);
2447 }
2448 case AfterISBody0:
2449 /* Clean up any remaining tokens */
2450 discard_tokens(parser);
2451
2452 /* Emit remaining fake events to end the parse */
2453 if (parser->event != NULL) {
2454 /* 1) end ruleset */
2455 parser->event(CSS_PARSER_END_RULESET, NULL,
2456 parser->event_pw);
2457
2458 /* 2) end stylesheet */
2459 parser->event(CSS_PARSER_END_STYLESHEET, NULL,
2460 parser->event_pw);
2461 }
2462
2463 break;
2464 }
2465
2466 return done(parser);
2467 }
2468
parseISBody0(css_parser * parser)2469 css_error parseISBody0(css_parser *parser)
2470 {
2471 enum { Initial = 0, AfterISBody = 1 };
2472 parser_state *state = parserutils_stack_get_current(parser->states);
2473 const css_token *token;
2474 css_error error;
2475
2476 /* is-body0 -> is-body is-body0
2477 * ->
2478 */
2479
2480 while (1) {
2481 switch (state->substate) {
2482 case Initial:
2483 {
2484 parser_state to = { sISBody, Initial };
2485 parser_state subsequent = { sISBody0, AfterISBody };
2486
2487 error = getToken(parser, &token);
2488 if (error != CSS_OK)
2489 return error;
2490
2491 error = pushBack(parser, token);
2492 if (error != CSS_OK)
2493 return error;
2494
2495 /* EOF is the only way out */
2496 if (token->type == CSS_TOKEN_EOF)
2497 return done(parser);
2498
2499 return transition(parser, to, subsequent);
2500 }
2501 case AfterISBody:
2502 /* Unless there's a parse error */
2503 if (parser->parseError)
2504 return done(parser);
2505
2506 state->substate = Initial;
2507
2508 break;
2509 }
2510 }
2511
2512 return done(parser);
2513 }
2514
parseISBody(css_parser * parser)2515 css_error parseISBody(css_parser *parser)
2516 {
2517 enum { Initial = 0, DeclList = 1, Brace = 2, WS = 3 };
2518 parser_state *state = parserutils_stack_get_current(parser->states);
2519 const css_token *token;
2520 css_error error;
2521
2522 /* is-body -> declaration decl-list '}' ws
2523 * -> decl-list '}' ws
2524 *
2525 * Note that this looks suspiciously similar to ruleset-end.
2526 * The difference here, however, is that we never end the ruleset.
2527 */
2528
2529 switch (state->substate) {
2530 case Initial:
2531 error = getToken(parser, &token);
2532 if (error != CSS_OK)
2533 return error;
2534
2535 error = pushBack(parser, token);
2536 if (error != CSS_OK)
2537 return error;
2538
2539 /* If this can't possibly be the start of a decl-list, then
2540 * attempt to parse a declaration. This will catch any invalid
2541 * input at this point and read to the start of the next
2542 * declaration. FIRST(decl-list) = (';', '}') */
2543 if (token->type != CSS_TOKEN_CHAR ||
2544 lwc_string_length(token->idata) != 1 ||
2545 (lwc_string_data(token->idata)[0] != '}' &&
2546 lwc_string_data(token->idata)[0] != ';')) {
2547 parser_state to = { sDeclaration, Initial };
2548 parser_state subsequent = { sISBody, DeclList };
2549
2550 return transition(parser, to, subsequent);
2551 }
2552
2553 state->substate = DeclList;
2554 /* Fall through */
2555 case DeclList:
2556 {
2557 parser_state to = { sDeclList, Initial };
2558 parser_state subsequent = { sISBody, Brace };
2559
2560 return transition(parser, to, subsequent);
2561 }
2562 case Brace:
2563 error = getToken(parser, &token);
2564 if (error != CSS_OK)
2565 return error;
2566
2567 if (token->type == CSS_TOKEN_EOF) {
2568 error = pushBack(parser, token);
2569 if (error != CSS_OK)
2570 return error;
2571
2572 return done(parser);
2573 }
2574
2575 if (token->type != CSS_TOKEN_CHAR ||
2576 lwc_string_length(token->idata) != 1 ||
2577 lwc_string_data(token->idata)[0] != '}') {
2578 /* This should never happen, as FOLLOW(decl-list)
2579 * contains only '}' */
2580 assert(0 && "Expected }");
2581 }
2582
2583 state->substate = WS;
2584 /* Fall through */
2585 case WS:
2586 error = eatWS(parser);
2587 if (error != CSS_OK)
2588 return error;
2589
2590 break;
2591 }
2592
2593 return done(parser);
2594 }
2595
parseMediaQuery(css_parser * parser)2596 css_error parseMediaQuery(css_parser *parser)
2597 {
2598 enum { Initial = 0, AfterAtRule = 1 };
2599 parser_state *state = parserutils_stack_get_current(parser->states);
2600
2601 /* media-query = at-rule */
2602
2603 switch (state->substate) {
2604 case Initial:
2605 {
2606 parser_state to = { sAtRule, Initial };
2607 parser_state subsequent = { sMediaQuery, AfterAtRule };
2608
2609 return transition(parser, to, subsequent);
2610 }
2611 case AfterAtRule:
2612 /* Clean up any remaining tokens */
2613 discard_tokens(parser);
2614 break;
2615 }
2616
2617 return done(parser);
2618 }
2619
2620 /**
2621 * Discard the contents of the token vector
2622 *
2623 * \param parser The parser whose tokens we are cleaning up.
2624 */
discard_tokens(css_parser * parser)2625 void discard_tokens(css_parser *parser)
2626 {
2627 int32_t ctx = 0;
2628 const css_token *tok;
2629
2630 while ((tok = parserutils_vector_iterate(
2631 parser->tokens, &ctx)) != NULL) {
2632 if (tok->idata != NULL) {
2633 lwc_string_unref(tok->idata);
2634 }
2635 }
2636
2637 parserutils_vector_clear(parser->tokens);
2638 }
2639
2640 #ifndef NDEBUG
2641 #ifdef DEBUG_STACK
printer(void * item)2642 static void printer(void *item)
2643 {
2644 parser_state *s = item;
2645
2646 printf("[%d %d]", s->state, s->substate);
2647 }
2648 #endif
2649
2650 #ifdef DEBUG_EVENTS
tprinter(void * token)2651 static void tprinter(void *token)
2652 {
2653 css_token *t = token;
2654
2655 if (t->idata) {
2656 printf("%d: %.*s", t->type,
2657 (int) lwc_string_length(t->idata),
2658 lwc_string_data(t->idata));
2659 } else if (t->data.data)
2660 printf("%d: %.*s", t->type, (int) t->data.len, t->data.data);
2661 else
2662 printf("%d", t->type);
2663 }
2664 #endif
2665 #endif
2666
2667