From 5f133d3d7df09dcd05be70a57a2008674eeef318 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 3 Oct 2020 17:12:32 +0100 Subject: parse: Add calc() parser. Co-authored-by: Michael Drake --- src/parse/properties/css_property_parser_gen.c | 10 + src/parse/properties/utils.c | 265 +++++++++++++++++++++++++ src/parse/properties/utils.h | 24 +++ src/parse/propstrings.c | 1 + src/parse/propstrings.h | 2 +- 5 files changed, 301 insertions(+), 1 deletion(-) diff --git a/src/parse/properties/css_property_parser_gen.c b/src/parse/properties/css_property_parser_gen.c index 24cc536..3d88cef 100644 --- a/src/parse/properties/css_property_parser_gen.c +++ b/src/parse/properties/css_property_parser_gen.c @@ -296,6 +296,16 @@ void output_length_unit(FILE *outputf, struct keyval *parseid, struct keyval_lis struct keyval *ckv = kvlist->item[0]; int ident_count; + fprintf(outputf, + "if ((token->type == CSS_TOKEN_IDENT) && " + "(lwc_string_caseless_isequal(token->idata, c->strings[CALC], &match) == lwc_error_ok && match))" + " {\n" + "\t\terror = css__parse_calc(c, vector, ctx, result, buildOPV(%s, 0, %s /* _CALC */), %s);\n" + "\t} else ", + parseid->val, + ckv->val, + ckv->key + ); fprintf(outputf, "{\n" diff --git a/src/parse/properties/utils.c b/src/parse/properties/utils.c index 0e49853..b1864cb 100644 --- a/src/parse/properties/utils.c +++ b/src/parse/properties/utils.c @@ -1334,3 +1334,268 @@ cleanup: return error; } +/******************************************************************************/ + +/* CALC + * + * calc( ) + * + * where + * = [ [ '+' | '-' ] ]* + * + * where + * = [ '*' | '/' ]* + * + * where + * = | | | ( ) + * + * + * calc(10px + (4rem / 2)) => + * U 10 px + * U 4 rem + * N 2 + * / + * + + * = + */ + +static css_error +css__parse_calc_sum(css_language *c, + const parserutils_vector *vector, int *ctx, + css_style *result, + uint32_t unit); + +static css_error +css__parse_calc_value(css_language *c, + const parserutils_vector *vector, int *ctx, + css_style *result, + uint32_t default_unit) +{ + css_error error; + int orig_ctx = *ctx; + const css_token *token; + + token = parserutils_vector_iterate(vector, ctx); + if (tokenIsChar(token, '(')) { + error = css__parse_calc_sum(c, vector, ctx, result, default_unit); + if (error != CSS_OK) { + return error; + } + + token = parserutils_vector_peek(vector, *ctx); + if (!tokenIsChar(token, ')')) { + return CSS_INVALID; + } + + } else switch (token->type) { + case CSS_TOKEN_NUMBER: /* Fall through */ + case CSS_TOKEN_DIMENSION: /* Fall through */ + case CSS_TOKEN_PERCENTAGE: + { + css_fixed length = 0; + uint32_t unit = 0; + *ctx = orig_ctx; + + error = css__parse_unit_specifier(c, vector, ctx, default_unit, &length, &unit); + if (error != CSS_OK) { + *ctx = orig_ctx; + return error; + } + + error = css__stylesheet_style_vappend(result, 3, (css_code_t) 'U', length, unit); + } + break; + + default: + error = CSS_INVALID; + break; + } + + return error; +} + +/* Both this, and css_parse_calc_sum must stop when it encounters a close-paren. + * If it hasn't had any useful tokens before that, it's an error. It does not + * need to restore ctx before returning an error but it does need to ensure that + * the close paren has not been consumed + */ +static css_error +css__parse_calc_product(css_language *c, + const parserutils_vector *vector, int *ctx, + css_style *result, + uint32_t unit) +{ + css_error error = CSS_OK; + const css_token *token; + bool multiplication; + + + /* First parse a value */ + error = css__parse_calc_value(c, vector, ctx, result, unit); + if (error != CSS_OK) { + return error; + } + + do { + /* What is our next token? */ + token = parserutils_vector_peek(vector, *ctx); + if (token == NULL) { + error = CSS_INVALID; + break; + } else if (tokenIsChar(token, ')')) + break; + else if (tokenIsChar(token, '*')) + multiplication = true; + else if (tokenIsChar(token, '/')) + multiplication = false; + else { + error = CSS_INVALID; + break; + } + /* Consume that * or / now */ + token = parserutils_vector_iterate(vector, ctx); + + if (multiplication) { + /* parse another value */ + error = css__parse_calc_value(c, vector, ctx, result, unit); + if (error != CSS_OK) + break; + } else { + css_fixed num; + size_t consumed; + + token = parserutils_vector_iterate(vector, ctx); + if (token->type != CSS_TOKEN_NUMBER) { + error = CSS_INVALID; + break; + } + num = css__number_from_lwc_string(token->idata, false, &consumed); + if (consumed != lwc_string_length(token->idata)) { + error = CSS_INVALID; + break; + } + + error = css__stylesheet_style_append(result, (css_code_t) 'N'); + if (error != CSS_OK) + break; + error = css__stylesheet_style_append(result, (css_code_t) num); + if (error != CSS_OK) + break; + } + + /* emit the multiplication/division operator */ + error = css__stylesheet_style_append(result, (css_code_t) (multiplication ? '*' : '/')); + } while (1); + /* We've fallen off, either we had an error or we're left with ')' */ + return error; +} + + +css_error +css__parse_calc_sum(css_language *c, + const parserutils_vector *vector, int *ctx, + css_style *result, + uint32_t unit) +{ + css_error error = CSS_OK; + const css_token *token; + bool addition; + + + /* First parse a product */ + error = css__parse_calc_product(c, vector, ctx, result, unit); + if (error != CSS_OK) { + return error; + } + + do { + /* What is our next token? */ + token = parserutils_vector_peek(vector, *ctx); + if (token == NULL) { + error = CSS_INVALID; + break; + } else if (tokenIsChar(token, ')')) + break; + else if (tokenIsChar(token, '+')) + addition = true; + else if (tokenIsChar(token, '-')) + addition = false; + else { + error = CSS_INVALID; + break; + } + /* Consume that + or - now */ + token = parserutils_vector_iterate(vector, ctx); + + /* parse another product */ + error = css__parse_calc_product(c, vector, ctx, result, unit); + if (error != CSS_OK) + break; + + /* emit the addition/subtraction operator */ + error = css__stylesheet_style_append(result, (css_code_t) (addition ? '+' : '-')); + } while (1); + /* We've fallen off, either we had an error or we're left with ')' */ + return error; +} + +/* Documented in utils.h */ +css_error css__parse_calc(css_language *c, + const parserutils_vector *vector, int *ctx, + css_style *result, + css_code_t property, + uint32_t unit) +{ + int orig_ctx = *ctx; + const css_token *token; + css_error error = CSS_OK; + css_style *calc_style = NULL; + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL) { + *ctx = orig_ctx; + return CSS_INVALID; + } + + if (!tokenIsChar(token, '(')) { + /* If we don't get an open-paren, give up now */ + *ctx = orig_ctx; + return CSS_INVALID; + } + + error = css__stylesheet_style_create(c->sheet, &calc_style); + if (error != CSS_OK) + goto cleanup; + + error = css__stylesheet_style_append(calc_style, property); + if (error != CSS_OK) + goto cleanup; + + error = css__parse_calc_sum(c, vector, ctx, calc_style, unit); + if (error != CSS_OK) + goto cleanup; + + token = parserutils_vector_iterate(vector, ctx); + if (!tokenIsChar(token, ')')) { + /* If we don't get a close-paren, give up now */ + error = CSS_INVALID; + goto cleanup; + } + + /* Append the indicator that the calc is finished */ + error = css__stylesheet_style_append(calc_style, (css_code_t) '='); + if (error != CSS_OK) + goto cleanup; + + /* TODO: Once we're OK to do so, merge the style */ + (void)result; + /* error = css__stylesheet_style_merge_style(result, calc_style); */ + +cleanup: + css__stylesheet_style_destroy(calc_style); + if (error != CSS_OK) { + *ctx = orig_ctx; + } + + return error; +} \ No newline at end of file diff --git a/src/parse/properties/utils.h b/src/parse/properties/utils.h index e4c97c7..e5331d2 100644 --- a/src/parse/properties/utils.h +++ b/src/parse/properties/utils.h @@ -199,4 +199,28 @@ css_error css__comma_list_to_style(css_language *c, bool first), css_style *result); +/** + * Parse a CSS calc() invocation + * + * Calc can generate a number of kinds of units, so we have to tell the + * parser the kind of unit we're aiming for (e.g. UNIT_PX, UNIT_ANGLE, etc.) + * + * \param[in] c Parsing context + * \param[in] vector Vector of tokens to process + * \param[in] ctx Pointer to vector iteration context + * \param[in] result Pointer to location to receive resulting style + * \param[in] property The CSS property we are calculating for + * \param[in] unit The kind of unit which we want to come out of this calc() + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion, + CSS_INVALID if the input is not valid + * + * Post condition: \a *ctx is updated with the next token to process + * If the input is invalid, then \a *ctx remains unchanged. + */ +css_error css__parse_calc(css_language *c, + const parserutils_vector *vector, int *ctx, + css_style *result, + css_code_t property, + uint32_t unit); #endif diff --git a/src/parse/propstrings.c b/src/parse/propstrings.c index 3c9401b..0156ea4 100644 --- a/src/parse/propstrings.c +++ b/src/parse/propstrings.c @@ -443,6 +443,7 @@ const stringmap_entry stringmap[LAST_KNOWN] = { { "or", SLEN("or") }, { "only", SLEN("only") }, { "infinite", SLEN("infinite") }, + { "calc", SLEN("calc") }, { "aliceblue", SLEN("aliceblue") }, { "antiquewhite", SLEN("antiquewhite") }, diff --git a/src/parse/propstrings.h b/src/parse/propstrings.h index 24b681b..c5dd113 100644 --- a/src/parse/propstrings.h +++ b/src/parse/propstrings.h @@ -101,7 +101,7 @@ enum { AVOID_PAGE, AVOID_COLUMN, BALANCE, HORIZONTAL_TB, VERTICAL_RL, VERTICAL_LR, CONTENT_BOX, BORDER_BOX, STRETCH, INLINE_FLEX, FLEX_START, FLEX_END, SPACE_BETWEEN, SPACE_AROUND, SPACE_EVENLY, ROW, ROW_REVERSE, - COLUMN_REVERSE, WRAP_STRING, WRAP_REVERSE, AND, OR, ONLY, INFINITE, + COLUMN_REVERSE, WRAP_STRING, WRAP_REVERSE, AND, OR, ONLY, INFINITE, CALC, /* Named colours */ FIRST_COLOUR, -- cgit v1.2.3