On Tue, Jun 28, 2016 at 02:20:40PM +0530, bscha...@redhat.com wrote: > From: Russel Bryant <russ...@ovn.org>
This misspells Russell's name (I fixed it). > Update the OVN expression parser to support address sets. Previously, > you could have a set of IP or MAC addresses in this form: > > {addr1, addr2, ..., addrN} > > This patch adds support for a bit of indirection where we can define a > set of addresses and refer to them by name. > > $name > > This '$name' can be used in the expresssions like > > {addr1, addr2, $name, ... } > {$name} > $name > > A future patch will expose the ability to define address sets for use. > > Signed-off-by: Russell Bryant <russ...@ovn.org> > Co-authored-by: Babu Shanmugam <bscha...@redhat.com> > Signed-off-by: Babu Shanmugam <bscha...@redhat.com> Thanks a lot! The results that Kyle reported are really impressive, so I want to start getting this in. Unfortunately I think that patch 2 probably conflicts with Ryan's incremental series, which I'm trying to prioritize because it's been in progress so long. So I'm just looking at patch 1 in this review round. I've applied this to master with the following incremental folded in. Besides some style changes, this has the following notable changes: - Add tests for the new lexer functionality. - Error messages that make better sense in context (note that expr_syntax_error() takes a string that gets appended to a description of the token), along with tests that exercise each of the error messages. - Use name 'cs' for an expr_constant_set, since that's the name used elsewhere in expr.c. - Make sure that $names can be formatted correctly with lex_token_format(). - Use same syntax for $names as for identifiers. - Comment updates. --8<--------------------------cut here-------------------------->8-- diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c index 2883ca9..8c0768d 100644 --- a/ovn/lib/expr.c +++ b/ovn/lib/expr.c @@ -731,17 +731,12 @@ assign_constant_set_type(struct expr_context *ctx, static bool parse_macros(struct expr_context *ctx, struct expr_constant_set *cs, - size_t *allocated_values) + size_t *allocated_values) { - if (!ctx->macros) { - expr_syntax_error(ctx, "No macros defined."); - return false; - } - struct expr_constant_set *addr_set = shash_find_data(ctx->macros, ctx->lexer->token.s); if (!addr_set) { - expr_syntax_error(ctx, "Unknown macro: '%s'", ctx->lexer->token.s); + expr_syntax_error(ctx, "expecting address set name."); return false; } @@ -754,14 +749,8 @@ parse_macros(struct expr_context *ctx, struct expr_constant_set *cs, cs->values = xrealloc(cs->values, n_values * sizeof *cs->values); *allocated_values = n_values; } - size_t i; - for (i = 0; i < addr_set->n_values; i++) { - union expr_constant *c1 = &cs->values[cs->n_values++]; - union expr_constant *c2 = &addr_set->values[i]; - c1->value = c2->value; - c1->format = c2->format; - c1->masked = c2->masked; - c1->mask = c2->mask; + for (size_t i = 0; i < addr_set->n_values; i++) { + cs->values[cs->n_values++] = addr_set->values[i]; } return true; @@ -853,20 +842,21 @@ expr_constant_set_destroy(struct expr_constant_set *cs) } } +/* Adds a macro named 'name' to 'macros', replacing any existing macro with the + * given name. */ void expr_macros_add(struct shash *macros, const char *name, - const char * const *values, size_t n_values) + const char *const *values, size_t n_values) { /* Replace any existing entry for this name. */ expr_macros_remove(macros, name); - struct expr_constant_set *cset = xzalloc(sizeof *cset); - cset->type = EXPR_C_INTEGER; - cset->in_curlies = true; - cset->n_values = n_values; - cset->values = xmalloc(cset->n_values * sizeof *cset->values); - size_t i, errors = 0; - for (i = 0; i < n_values; i++) { + struct expr_constant_set *cs = xzalloc(sizeof *cs); + cs->type = EXPR_C_INTEGER; + cs->in_curlies = true; + cs->n_values = 0; + cs->values = xmalloc(n_values * sizeof *cs->values); + for (size_t i = 0; i < n_values; i++) { /* Use the lexer to convert each macro into the proper * integer format. */ struct lexer lex; @@ -876,9 +866,8 @@ expr_macros_add(struct shash *macros, const char *name, && lex.token.type != LEX_T_MASKED_INTEGER) { VLOG_WARN("Invalid address set entry: '%s', token type: %d", values[i], lex.token.type); - errors += 1; } else { - union expr_constant *c = &cset->values[i - errors]; + union expr_constant *c = &cs->values[cs->n_values++]; c->value = lex.token.value; c->format = lex.token.format; c->masked = lex.token.type == LEX_T_MASKED_INTEGER; @@ -888,34 +877,31 @@ expr_macros_add(struct shash *macros, const char *name, } lexer_destroy(&lex); } - cset->n_values -= errors; - shash_add(macros, name, cset); + shash_add(macros, name, cs); } void expr_macros_remove(struct shash *macros, const char *name) { - struct expr_constant_set *cset - = shash_find_and_delete(macros, name); - - if (cset) { - expr_constant_set_destroy(cset); - free(cset); + struct expr_constant_set *cs = shash_find_and_delete(macros, name); + if (cs) { + expr_constant_set_destroy(cs); + free(cs); } } -/* Destroy all contents of macros. */ +/* Destroy all contents of 'macros'. */ void expr_macros_destroy(struct shash *macros) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, macros) { - struct expr_constant_set *cset = node->data; + struct expr_constant_set *cs = node->data; shash_delete(macros, node); - expr_constant_set_destroy(cset); + expr_constant_set_destroy(cs); } } @@ -1094,7 +1080,8 @@ struct expr * expr_parse(struct lexer *lexer, const struct shash *symtab, const struct shash *macros, char **errorp) { - struct expr_context ctx = { .lexer = lexer, .symtab = symtab, + struct expr_context ctx = { .lexer = lexer, + .symtab = symtab, .macros = macros }; struct expr *e = expr_parse__(&ctx); *errorp = ctx.error; diff --git a/ovn/lib/expr.h b/ovn/lib/expr.h index 56f6bbb..ed5300f 100644 --- a/ovn/lib/expr.h +++ b/ovn/lib/expr.h @@ -456,15 +456,16 @@ char *expr_parse_constant_set(struct lexer *, const struct shash *symtab, void expr_constant_set_destroy(struct expr_constant_set *cs); -/* MACRO Variables +/* Address sets, aka "macros". * * Instead of referring to a set of value as: * {addr1, addr2, ..., addrN} * You can register a set of values and refer to them as: * $name * The macros should all have integer/masked-integer values. - * The values that don't qualify are ingnored + * The values that don't qualify are ignored. */ + void expr_macros_add(struct shash *macros, const char *name, const char * const *values, size_t n_values); void expr_macros_remove(struct shash *macros, const char *name); diff --git a/ovn/lib/lex.c b/ovn/lib/lex.c index bbbc7fc..1467720 100644 --- a/ovn/lib/lex.c +++ b/ovn/lib/lex.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Nicira, Inc. + * Copyright (c) 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -228,7 +228,7 @@ lex_token_format(const struct lex_token *token, struct ds *s) break; case LEX_T_MACRO: - ds_put_cstr(s, token->s); + ds_put_format(s, "$%s", token->s); break; case LEX_T_LPAREN: @@ -518,7 +518,7 @@ lex_is_idn(unsigned char c) } static const char * -lex_parse_id(const char *p, struct lex_token *token) +lex_parse_id(const char *p, enum lex_type type, struct lex_token *token) { const char *start = p; @@ -526,7 +526,7 @@ lex_parse_id(const char *p, struct lex_token *token) p++; } while (lex_is_idn(*p)); - token->type = LEX_T_ID; + token->type = type; lex_token_strcpy(token, start, p - start); return p; } @@ -534,9 +534,13 @@ lex_parse_id(const char *p, struct lex_token *token) static const char * lex_parse_macro(const char *p, struct lex_token *token) { - p = lex_parse_id(++p, token); - token->type = LEX_T_MACRO; - return p; + p++; + if (!lex_is_id1(*p)) { + lex_error(token, "`$' must be followed by a valid identifier."); + return p; + } + + return lex_parse_id(p, LEX_T_MACRO, token); } /* Initializes 'token' and parses the first token from the beginning of @@ -731,12 +735,12 @@ next: * digits followed by a colon, but identifiers never do. */ p = (p[strspn(p, "0123456789abcdefABCDEF")] == ':' ? lex_parse_integer(p, token) - : lex_parse_id(p, token)); + : lex_parse_id(p, LEX_T_ID, token)); break; default: if (lex_is_id1(*p)) { - p = lex_parse_id(p, token); + p = lex_parse_id(p, LEX_T_ID, token); } else { if (isprint((unsigned char) *p)) { lex_error(token, "Invalid character `%c' in input.", *p); diff --git a/ovn/lib/lex.h b/ovn/lib/lex.h index 826465c..ee71a8b 100644 --- a/ovn/lib/lex.h +++ b/ovn/lib/lex.h @@ -78,20 +78,32 @@ enum lex_format { }; const char *lex_format_to_string(enum lex_format); -/* A token. - * - * 's' may point to 'buffer'; otherwise, it points to malloc()ed memory owned - * by the token. */ +/* A token. */ struct lex_token { - enum lex_type type; /* One of LEX_*. */ - char *s; /* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR only. */ - enum lex_format format; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */ + /* One of LEX_*. */ + enum lex_type type; + + /* Meaningful for LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only. + * For these token types, 's' may point to 'buffer'; otherwise, it points + * to malloc()ed memory owned by the token. + * + * Must be NULL for other token types. + * + * For LEX_T_MACRO, 's' does not include the leading $. */ + char *s; + + /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */ + enum lex_format format; + union { + /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */ struct { union mf_subvalue value; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER. */ union mf_subvalue mask; /* LEX_T_MASKED_INTEGER only. */ }; - char buffer[256]; /* Buffer for LEX_T_ID/LEX_T_STRING. */ + + /* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only. */ + char buffer[256]; }; }; diff --git a/tests/ovn.at b/tests/ovn.at index b93251d..decc90d 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -8,6 +8,9 @@ foo bar baz quuxquuxquux _abcd_ a.b.c.d a123_.456 "abc\u0020def" => "abc def" " => error("Input ends inside quoted string.")dnl " +$foo $bar $baz $quuxquuxquux $_abcd_ $a.b.c.d $a123_.456 +$1 => error("`$' must be followed by a valid identifier.") 1 + a/*b*/c => a c a//b c => a a/**/b => a b @@ -215,6 +218,8 @@ eth.type != 0x800 => Nominal field eth.type may only be tested for equality (tak 123 == 123 => Syntax error at `123' expecting field name. +$name => Syntax error at `$name' expecting address set name. + 123 == xyzzy => Syntax error at `xyzzy' expecting field name. xyzzy == 1 => Syntax error at `xyzzy' expecting field name. diff --git a/tests/test-ovn.c b/tests/test-ovn.c index 15ebba7..87251ee 100644 --- a/tests/test-ovn.c +++ b/tests/test-ovn.c @@ -273,13 +273,13 @@ create_macros(struct shash *macros) { shash_init(macros); - const char * const addrs1[] = { + static const char *const addrs1[] = { "10.0.0.1", "10.0.0.2", "10.0.0.3", }; - const char * const addrs2[] = { + static const char *const addrs2[] = { "::1", "::2", "::3", }; - const char * const addrs3[] = { + static const char *const addrs3[] = { "00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03", }; @@ -321,8 +321,7 @@ test_parse_expr__(int steps) struct expr *expr; char *error; - expr = expr_parse_string(ds_cstr(&input), &symtab, ¯os, - &error); + expr = expr_parse_string(ds_cstr(&input), &symtab, ¯os, &error); if (!error && steps > 0) { expr = expr_annotate(expr, &symtab, &error); } _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev