diff options
author | John Mark Bell <jmb@netsurf-browser.org> | 2008-10-19 12:22:13 +0000 |
---|---|---|
committer | John Mark Bell <jmb@netsurf-browser.org> | 2008-10-19 12:22:13 +0000 |
commit | 0aa91129557556fcc8dbb92c699c641c161e777c (patch) | |
tree | bf6257ac5b621625b884536b848a01d99aa162dc /src | |
parent | c2d6c66c89118cd9f32343ece82cb43e8687c7aa (diff) | |
download | libcss-0aa91129557556fcc8dbb92c699c641c161e777c.tar.gz libcss-0aa91129557556fcc8dbb92c699c641c161e777c.tar.bz2 |
Finish selector list parsing
svn path=/trunk/libcss/; revision=5599
Diffstat (limited to 'src')
-rw-r--r-- | src/parse/css21.c | 486 | ||||
-rw-r--r-- | src/stylesheet.c | 3 | ||||
-rw-r--r-- | src/stylesheet.h | 3 |
3 files changed, 415 insertions, 77 deletions
diff --git a/src/parse/css21.c b/src/parse/css21.c index ed07b1d..501b4b6 100644 --- a/src/parse/css21.c +++ b/src/parse/css21.c @@ -61,6 +61,7 @@ struct css_css21 { void *pw; /**< Client's private data */ }; +/* Event handlers */ static css_error css21_handle_event(css_parser_event type, const parserutils_vector *tokens, void *pw); static inline css_error handleStartStylesheet(css_css21 *c, @@ -81,11 +82,42 @@ static inline css_error handleEndBlock(css_css21 *c, const parserutils_vector *vector); static inline css_error handleBlockContent(css_css21 *c, const parserutils_vector *vector); -static inline css_error handleSelectorList(css_css21 *c, - const parserutils_vector *vector); static inline css_error handleDeclaration(css_css21 *c, const parserutils_vector *vector); +/* Selector list parsing */ +static inline css_error parseClass(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector **specific); +static inline css_error parseAttrib(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector **specific); +static inline css_error parsePseudo(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector **specific); +static inline css_error parseSpecific(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector *parent); +static inline css_error parseSelectorSpecifics(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector *parent); +static inline css_error parseSimpleSelector(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector **result); +static inline css_error parseCombinator(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_combinator *result); +static inline css_error parseSelector(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector **result); +static inline css_error parseSelectorList(css_css21 *c, + const parserutils_vector *vector, css_rule *rule); + +/* Helpers */ +static inline void consumeWhitespace(const parserutils_vector *vector, + int *ctx); +static inline bool tokenIsChar(const css_token *token, uint8_t c); + /** * Create a CSS 2.1 parser * @@ -248,18 +280,35 @@ css_error handleStartRuleset(css_css21 *c, const parserutils_vector *vector) parserutils_error perror; css_error error; context_entry entry = { CSS_PARSER_START_RULESET, NULL }; + css_rule *rule = NULL; assert(c != NULL); - error = handleSelectorList(c, vector); - if (error != CSS_OK) + rule = css_stylesheet_rule_create(c->sheet, CSS_RULE_SELECTOR); + if (rule == NULL) + return CSS_NOMEM; + + error = parseSelectorList(c, vector, rule); + if (error != CSS_OK) { + css_stylesheet_rule_destroy(c->sheet, rule); return error; + } perror = parserutils_stack_push(c->context, (void *) &entry); if (perror != PARSERUTILS_OK) { + css_stylesheet_rule_destroy(c->sheet, rule); return css_error_from_parserutils_error(perror); } + error = css_stylesheet_add_rule(c->sheet, rule); + if (error != CSS_OK) { + parserutils_stack_pop(c->context, NULL); + css_stylesheet_rule_destroy(c->sheet, rule); + return error; + } + + /* Rule is now owned by the sheet, so no need to destroy it */ + return CSS_OK; } @@ -294,16 +343,11 @@ css_error handleStartAtRule(css_css21 *c, const parserutils_vector *vector) /* vector contains: ATKEYWORD ws any0 */ const css_token *token = NULL; const css_token *atkeyword = NULL; - int32_t any = 0; int32_t ctx = 0; atkeyword = parserutils_vector_iterate(vector, &ctx); - /* Skip whitespace */ - do { - any = ctx; - token = parserutils_vector_iterate(vector, &ctx); - } while (token != NULL && token->type == CSS_TOKEN_S); + consumeWhitespace(vector, &ctx); /* We now have an ATKEYWORD and the context for the start of any0, if * there is one */ @@ -312,14 +356,14 @@ css_error handleStartAtRule(css_css21 *c, const parserutils_vector *vector) if (atkeyword->lower.ptr == c->strings[CHARSET]) { if (c->state == BEFORE_CHARSET) { /* any0 = STRING */ - if (any == 0) + if (ctx == 0) return CSS_INVALID; - token = parserutils_vector_iterate(vector, &any); + token = parserutils_vector_iterate(vector, &ctx); if (token == NULL || token->type != CSS_TOKEN_STRING) return CSS_INVALID; - token = parserutils_vector_iterate(vector, &any); + token = parserutils_vector_iterate(vector, &ctx); if (token != NULL) return CSS_INVALID; @@ -332,21 +376,15 @@ css_error handleStartAtRule(css_css21 *c, const parserutils_vector *vector) /* any0 = (STRING | URI) ws * (IDENT ws (',' ws IDENT ws)* )? */ const css_token *uri = - parserutils_vector_iterate(vector, &any); - if (uri == NULL || (token->type != CSS_TOKEN_STRING && - token->type != CSS_TOKEN_URI)) + parserutils_vector_iterate(vector, &ctx); + if (uri == NULL || (uri->type != CSS_TOKEN_STRING && + uri->type != CSS_TOKEN_URI)) return CSS_INVALID; - /* Whitespace */ - do { - token = parserutils_vector_iterate(vector, - &any); - if (token == NULL || token->type != CSS_TOKEN_S) - break; - } while (token != NULL); + consumeWhitespace(vector, &ctx); /** \todo Media list */ - if (token != NULL) { + if (parserutils_vector_peek(vector, ctx) != NULL) { } /** \todo trigger fetch of imported sheet */ @@ -435,48 +473,304 @@ css_error handleBlockContent(css_css21 *c, const parserutils_vector *vector) return CSS_OK; } -static bool tokenIsChar(const css_token *token, uint8_t c) +css_error handleDeclaration(css_css21 *c, const parserutils_vector *vector) { - return token != NULL && token->type == CSS_TOKEN_CHAR && - token->lower.len == 1 && token->lower.ptr[0] == c; + UNUSED(c); + UNUSED(vector); + + /* Locations where declarations are permitted: + * + * + In @page + * + In ruleset + */ + + /* IDENT ws ':' ws value + * + * In CSS 2.1, value is any1, so '{' or ATKEYWORD => parse error + */ + + return CSS_OK; } -static css_error parseSimpleSelector(css_css21 *c, const parserutils_vector *vector, - int *ctx, css_selector **result) +/****************************************************************************** + * Selector list parsing functions * + ******************************************************************************/ + +css_error parseClass(css_css21 *c, const parserutils_vector *vector, int *ctx, + css_selector **specific) +{ + const css_token *token; + + /* class -> '.' IDENT */ + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || tokenIsChar(token, '.') == false) + return CSS_INVALID; + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || token->type != CSS_TOKEN_IDENT) + return CSS_INVALID; + + *specific = css_stylesheet_selector_create(c->sheet, + CSS_SELECTOR_CLASS, &token->data, NULL); + if (*specific == NULL) + return CSS_NOMEM; + + return CSS_OK; +} + +css_error parseAttrib(css_css21 *c, const parserutils_vector *vector, int *ctx, + css_selector **specific) +{ + const css_token *token, *name, *value = NULL; + css_selector_type type = CSS_SELECTOR_ATTRIBUTE; + + /* attrib -> '[' ws IDENT ws [ + * [ '=' | INCLUDES | DASHMATCH ] ws + * [ IDENT | STRING ] ws ]? ']' + */ + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || tokenIsChar(token, '[') == false) + return CSS_INVALID; + + consumeWhitespace(vector, ctx); + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || token->type != CSS_TOKEN_IDENT) + return CSS_INVALID; + + name = token; + + consumeWhitespace(vector, ctx); + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL) + return CSS_INVALID; + + if (tokenIsChar(token, ']') == false) { + if (tokenIsChar(token, '=')) + type = CSS_SELECTOR_ATTRIBUTE_EQUAL; + else if (token->type == CSS_TOKEN_INCLUDES) + type = CSS_SELECTOR_ATTRIBUTE_INCLUDES; + else if (token->type == CSS_TOKEN_DASHMATCH) + type = CSS_SELECTOR_ATTRIBUTE_DASHMATCH; + else + return CSS_INVALID; + + consumeWhitespace(vector, ctx); + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || (token->type != CSS_TOKEN_IDENT && + token->type != CSS_TOKEN_STRING)) + return CSS_INVALID; + + value = token; + + consumeWhitespace(vector, ctx); + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || tokenIsChar(token, ']') == false) + return CSS_INVALID; + } + + *specific = css_stylesheet_selector_create(c->sheet, type, + &name->data, value != NULL ? &value->data : NULL); + if (*specific == NULL) + return CSS_NOMEM; + + return CSS_OK; +} + +css_error parsePseudo(css_css21 *c, const parserutils_vector *vector, int *ctx, + css_selector **specific) +{ + const css_token *token, *name, *value = NULL; + + /* pseudo -> ':' [ IDENT | FUNCTION ws IDENT? ws ')' ] */ + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || tokenIsChar(token, ':') == false) + return CSS_INVALID; + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || (token->type != CSS_TOKEN_IDENT && + token->type != CSS_TOKEN_FUNCTION)) + return CSS_INVALID; + + name = token; + + if (token->type == CSS_TOKEN_FUNCTION) { + consumeWhitespace(vector, ctx); + + token = parserutils_vector_iterate(vector, ctx); + + if (token != NULL && token->type == CSS_TOKEN_IDENT) { + value = token; + + consumeWhitespace(vector, ctx); + + token = parserutils_vector_iterate(vector, ctx); + } + + if (token == NULL || tokenIsChar(token, ')') == false) + return CSS_INVALID; + } + + *specific = css_stylesheet_selector_create(c->sheet, + CSS_SELECTOR_PSEUDO, &name->data, + value != NULL ? &value->data : NULL); + if (*specific == NULL) + return CSS_NOMEM; + + return CSS_OK; +} + +css_error parseSpecific(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector *parent) +{ + css_error error; + const css_token *token; + css_selector *specific = NULL; + + /* specific -> [ HASH | class | attrib | pseudo ] */ + + token = parserutils_vector_peek(vector, *ctx); + if (token == NULL) + return CSS_INVALID; + + if (token->type == CSS_TOKEN_HASH) { + specific = css_stylesheet_selector_create(c->sheet, + CSS_SELECTOR_ID, &token->data, NULL); + if (specific == NULL) + return CSS_NOMEM; + + parserutils_vector_iterate(vector, ctx); + } else if (tokenIsChar(token, '.')) { + error = parseClass(c, vector, ctx, &specific); + if (error != CSS_OK) + return error; + } else if (tokenIsChar(token, '[')) { + error = parseAttrib(c, vector, ctx, &specific); + if (error != CSS_OK) + return error; + } else if (tokenIsChar(token, ':')) { + error = parsePseudo(c, vector, ctx, &specific); + if (error != CSS_OK) + return error; + } else { + return CSS_INVALID; + } + + error = css_stylesheet_selector_append_specific(c->sheet, parent, + specific); + if (error != CSS_OK) { + css_stylesheet_selector_destroy(c->sheet, specific); + return error; + } + + return CSS_OK; +} + +css_error parseSelectorSpecifics(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector *parent) +{ + css_error error; + const css_token *token; + + /* specifics -> specific* */ + while ((token = parserutils_vector_peek(vector, *ctx)) != NULL && + token->type != CSS_TOKEN_S && + tokenIsChar(token, '+') == false && + tokenIsChar(token, '>') == false && + tokenIsChar(token, ',') == false) { + error = parseSpecific(c, vector, ctx, parent); + if (error != CSS_OK) + return error; + } + + return CSS_OK; +} + +css_error parseSimpleSelector(css_css21 *c, + const parserutils_vector *vector, int *ctx, + css_selector **result) { + css_error error; + const css_token *token; + css_selector *selector; - /* simple_selector -> element_name [ HASH | class | attrib | pseudo ]* - * -> [ HASH | class | attrib | pseudo ]+ + /* simple_selector -> element_name specifics + * -> specific specifics * element_name -> IDENT | '*' - * class -> '.' IDENT - * attrib -> '[' ws IDENT ws [ - * [ '=' | INCLUDES | DASHMATCH ] ws - * [ IDENT | STRING ] ws ]? ']' - * pseudo -> ':' [ IDENT | FUNCTION ws IDENT? ws ')' ] */ - UNUSED(c); - UNUSED(vector); - UNUSED(ctx); - UNUSED(result); + token = parserutils_vector_peek(vector, *ctx); + if (token == NULL) + return CSS_INVALID; + + if (token->type == CSS_TOKEN_IDENT || tokenIsChar(token, '*')) { + /* Have element name */ + selector = css_stylesheet_selector_create(c->sheet, + CSS_SELECTOR_ELEMENT, &token->data, NULL); + if (selector == NULL) + return CSS_NOMEM; + + parserutils_vector_iterate(vector, ctx); + } else { + /* Universal selector */ + css_string name = { (uint8_t *) "*", 1 }; + + selector = css_stylesheet_selector_create(c->sheet, + CSS_SELECTOR_ELEMENT, &name, NULL); + if (selector == NULL) + return CSS_NOMEM; + + /* Ensure we have at least one specific selector */ + error = parseSpecific(c, vector, ctx, selector); + if (error != CSS_OK) { + css_stylesheet_selector_destroy(c->sheet, selector); + return error; + } + } + + *result = selector; + + error = parseSelectorSpecifics(c, vector, ctx, selector); + if (error != CSS_OK) + return error; return CSS_OK; } -static css_error parseCombinator(css_css21 *c, const parserutils_vector *vector, +css_error parseCombinator(css_css21 *c, const parserutils_vector *vector, int *ctx, css_combinator *result) { + const css_token *token; + /* combinator -> '+' ws | '>' ws | ws1 */ UNUSED(c); - UNUSED(vector); - UNUSED(ctx); - UNUSED(result); + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL) + return CSS_INVALID; + + if (tokenIsChar(token, '+')) + *result = CSS_COMBINATOR_SIBLING; + else if (tokenIsChar(token, '>')) + *result = CSS_COMBINATOR_PARENT; + else if (token->type == CSS_TOKEN_S) + *result = CSS_COMBINATOR_ANCESTOR; + else + return CSS_INVALID; + + consumeWhitespace(vector, ctx); return CSS_OK; } -static css_error parseSelector(css_css21 *c, const parserutils_vector *vector, +css_error parseSelector(css_css21 *c, const parserutils_vector *vector, int *ctx, css_selector **result) { css_error error; @@ -490,7 +784,8 @@ static css_error parseSelector(css_css21 *c, const parserutils_vector *vector, return error; *result = selector; - do { + while ((token = parserutils_vector_peek(vector, *ctx)) != NULL && + tokenIsChar(token, ',') == false) { css_combinator comb = CSS_COMBINATOR_NONE; css_selector *other = NULL; @@ -510,56 +805,97 @@ static css_error parseSelector(css_css21 *c, const parserutils_vector *vector, return error; selector = other; - - token = parserutils_vector_peek(vector, *ctx); - } while (token != NULL && tokenIsChar(token, ',') == false); + } return CSS_OK; } -css_error handleSelectorList(css_css21 *c, const parserutils_vector *vector) +css_error parseSelectorList(css_css21 *c, const parserutils_vector *vector, + css_rule *rule) { css_error error; const css_token *token = NULL; css_selector *selector = NULL; int ctx = 0; - UNUSED(c); - UNUSED(vector); + /* selector_list -> selector [ ',' ws selector ]* */ + + error = parseSelector(c, vector, &ctx, &selector); + if (error != CSS_OK) { + if (selector != NULL) + css_stylesheet_selector_destroy(c->sheet, selector); + return error; + } + + assert(selector != NULL); + + error = css_stylesheet_rule_add_selector(c->sheet, rule, selector); + if (error != CSS_OK) { + css_stylesheet_selector_destroy(c->sheet, selector); + return error; + } + + while ((token = parserutils_vector_peek(vector, ctx)) != NULL) { + token = parserutils_vector_iterate(vector, &ctx); + if (tokenIsChar(token, ',') == false) + return CSS_INVALID; + + consumeWhitespace(vector, &ctx); + + selector = NULL; - /* CSS 2.1 selector syntax: - * - * selector_list -> selector [ ',' ws selector ]* - */ - do { error = parseSelector(c, vector, &ctx, &selector); - if (error != CSS_OK) + if (error != CSS_OK) { + if (selector != NULL) { + css_stylesheet_selector_destroy(c->sheet, + selector); + } return error; + } - /** \todo Finish this */ + assert(selector != NULL); - token = parserutils_vector_peek(vector, ctx); - } while(token != NULL); + error = css_stylesheet_rule_add_selector(c->sheet, rule, + selector); + if (error != CSS_OK) { + css_stylesheet_selector_destroy(c->sheet, selector); + return error; + } + } return CSS_OK; } -css_error handleDeclaration(css_css21 *c, const parserutils_vector *vector) -{ - UNUSED(c); - UNUSED(vector); +/****************************************************************************** + * Helper functions * + ******************************************************************************/ - /* Locations where declarations are permitted: - * - * + In @page - * + In ruleset - */ +/** + * Consume all leading whitespace tokens + * + * \param vector The vector to consume from + * \param ctx The vector's context + */ +void consumeWhitespace(const parserutils_vector *vector, int *ctx) +{ + const css_token *token = NULL; - /* IDENT ws ':' ws value - * - * In CSS 2.1, value is any1, so '{' or ATKEYWORD => parse error - */ + while ((token = parserutils_vector_peek(vector, *ctx)) != NULL && + token->type == CSS_TOKEN_S) + token = parserutils_vector_iterate(vector, ctx); +} - return CSS_OK; +/** + * Determine if a token is a character + * + * \param token The token to consider + * \param c The character to match (lowercase ASCII only) + * \return True if the token matches, false otherwise + */ +bool tokenIsChar(const css_token *token, uint8_t c) +{ + return token != NULL && token->type == CSS_TOKEN_CHAR && + token->lower.len == 1 && token->lower.ptr[0] == c; } + diff --git a/src/stylesheet.c b/src/stylesheet.c index eb8de29..fa32d76 100644 --- a/src/stylesheet.c +++ b/src/stylesheet.c @@ -278,7 +278,8 @@ css_error css_stylesheet_set_disabled(css_stylesheet *sheet, bool disabled) * \return Pointer to selector object, or NULL on failure */ css_selector *css_stylesheet_selector_create(css_stylesheet *sheet, - css_selector_type type, css_string *name, css_string *value) + css_selector_type type, const css_string *name, + const css_string *value) { css_selector *sel; diff --git a/src/stylesheet.h b/src/stylesheet.h index ea1c9c4..bcb7b17 100644 --- a/src/stylesheet.h +++ b/src/stylesheet.h @@ -149,7 +149,8 @@ struct css_stylesheet { }; css_selector *css_stylesheet_selector_create(css_stylesheet *sheet, - css_selector_type type, css_string *name, css_string *value); + css_selector_type type, const css_string *name, + const css_string *value); void css_stylesheet_selector_destroy(css_stylesheet *sheet, css_selector *selector); |