struct hmap matches;
struct expr *expr;
- expr = expr_parse_string(lflow->match, &symtab, &error);
+ expr = expr_parse_string(lflow->match, &symtab, NULL, &error);
if (!error) {
if (prereqs) {
expr = expr_combine(EXPR_T_AND, expr, prereqs);
struct expr *expr;
char *error;
- expr = expr_parse_string(prerequisite, ctx->ap->symtab, &error);
+ expr = expr_parse_string(prerequisite, ctx->ap->symtab, NULL, &error);
ovs_assert(!error);
ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
}
struct expr_context {
struct lexer *lexer; /* Lexer for pulling more tokens. */
const struct shash *symtab; /* Symbol table. */
+ const struct shash *macros; /* Table of macros. */
char *error; /* Error, if any, otherwise NULL. */
bool not; /* True inside odd number of NOT operators. */
};
}
}
+static bool
+parse_macros(struct expr_context *ctx, struct expr_constant_set *cs,
+ size_t *allocated_values)
+{
+ struct expr_constant_set *addr_set
+ = shash_find_data(ctx->macros, ctx->lexer->token.s);
+ if (!addr_set) {
+ expr_syntax_error(ctx, "expecting address set name.");
+ return false;
+ }
+
+ if (!assign_constant_set_type(ctx, cs, EXPR_C_INTEGER)) {
+ return false;
+ }
+
+ size_t n_values = cs->n_values + addr_set->n_values;
+ if (n_values >= *allocated_values) {
+ cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
+ *allocated_values = n_values;
+ }
+ for (size_t i = 0; i < addr_set->n_values; i++) {
+ cs->values[cs->n_values++] = addr_set->values[i];
+ }
+
+ return true;
+}
+
static bool
parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
size_t *allocated_values)
}
lexer_get(ctx->lexer);
return true;
+ } else if (ctx->lexer->token.type == LEX_T_MACRO) {
+ if (!parse_macros(ctx, cs, allocated_values)) {
+ return false;
+ }
+ lexer_get(ctx->lexer);
+ return true;
} else {
expr_syntax_error(ctx, "expecting constant.");
return false;
}
}
+/* 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)
+{
+ /* Replace any existing entry for this name. */
+ expr_macros_remove(macros, name);
+
+ 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;
+ lexer_init(&lex, values[i]);
+ lexer_get(&lex);
+ if (lex.token.type != LEX_T_INTEGER
+ && lex.token.type != LEX_T_MASKED_INTEGER) {
+ VLOG_WARN("Invalid address set entry: '%s', token type: %d",
+ values[i], lex.token.type);
+ } else {
+ 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;
+ if (c->masked) {
+ c->mask = lex.token.mask;
+ }
+ }
+ lexer_destroy(&lex);
+ }
+
+ shash_add(macros, name, cs);
+}
+
+void
+expr_macros_remove(struct shash *macros, const char *name)
+{
+ 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'. */
+void
+expr_macros_destroy(struct shash *macros)
+{
+ struct shash_node *node, *next;
+
+ SHASH_FOR_EACH_SAFE (node, next, macros) {
+ struct expr_constant_set *cs = node->data;
+
+ shash_delete(macros, node);
+ expr_constant_set_destroy(cs);
+ }
+}
+
static struct expr *
expr_parse_primary(struct expr_context *ctx, bool *atomic)
{
* The caller must eventually free the returned expression (with
* expr_destroy()) or error (with free()). */
struct expr *
-expr_parse(struct lexer *lexer, const struct shash *symtab, char **errorp)
+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;
ovs_assert((ctx.error != NULL) != (e != NULL));
/* Like expr_parse(), but the expression is taken from 's'. */
struct expr *
-expr_parse_string(const char *s, const struct shash *symtab, char **errorp)
+expr_parse_string(const char *s, const struct shash *symtab,
+ const struct shash *macros, char **errorp)
{
struct lexer lexer;
struct expr *expr;
lexer_init(&lexer, s);
lexer_get(&lexer);
- expr = expr_parse(&lexer, symtab, errorp);
+ expr = expr_parse(&lexer, symtab, macros, errorp);
if (!*errorp && lexer.token.type != LEX_T_END) {
*errorp = xstrdup("Extra tokens at end of input.");
expr_destroy(expr);
static enum expr_level
expr_parse_level(const char *s, const struct shash *symtab, char **errorp)
{
- struct expr *expr = expr_parse_string(s, symtab, errorp);
+ struct expr *expr = expr_parse_string(s, symtab, NULL, errorp);
enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL;
expr_destroy(expr);
return level;
char *error;
struct expr *expr;
- expr = expr_parse_string(s, symtab, &error);
+ expr = expr_parse_string(s, symtab, NULL, &error);
if (expr) {
expr = expr_annotate__(expr, symtab, nesting, &error);
}
void expr_format(const struct expr *, struct ds *);
void expr_print(const struct expr *);
struct expr *expr_parse(struct lexer *, const struct shash *symtab,
+ const struct shash *macros,
char **errorp);
struct expr *expr_parse_string(const char *, const struct shash *symtab,
+ const struct shash *macros,
char **errorp);
struct expr *expr_clone(struct expr *);
OVS_WARN_UNUSED_RESULT;
void expr_constant_set_destroy(struct expr_constant_set *cs);
+\f
+/* 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 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);
+void expr_macros_destroy(struct shash *macros);
+
#endif /* ovn/expr.h */
/*
- * 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.
lex_token_format_masked_integer(token, s);
break;
+ case LEX_T_MACRO:
+ ds_put_format(s, "$%s", token->s);
+ break;
+
case LEX_T_LPAREN:
ds_put_cstr(s, "(");
break;
}
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;
p++;
} while (lex_is_idn(*p));
- token->type = LEX_T_ID;
+ token->type = type;
lex_token_strcpy(token, start, p - start);
return p;
}
+static const char *
+lex_parse_macro(const char *p, struct lex_token *token)
+{
+ 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
* null-terminated string 'p' into 'token'. Stores a pointer to the start of
* the token (after skipping white space and comments, if any) into '*startp'.
}
break;
+ case '$':
+ p = lex_parse_macro(p, token);
+ break;
+
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case ':':
* 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);
LEX_T_STRING, /* "foo" */
LEX_T_INTEGER, /* 12345 or 1.2.3.4 or ::1 or 01:02:03:04:05 */
LEX_T_MASKED_INTEGER, /* 12345/10 or 1.2.0.0/16 or ::2/127 or... */
+ LEX_T_MACRO, /* $NAME */
LEX_T_ERROR, /* invalid input */
/* Bare tokens. */
};
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];
};
};
"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
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.
])
AT_CLEANUP
+AT_SETUP([ovn -- converting expressions to flows -- address sets])
+expr_to_flow () {
+ echo "$1" | ovstest test-ovn expr-to-flows | sort
+}
+AT_CHECK([expr_to_flow 'ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3}'], [0], [dnl
+ip,nw_src=10.0.0.1
+ip,nw_src=10.0.0.2
+ip,nw_src=10.0.0.3
+])
+AT_CHECK([expr_to_flow 'ip4.src == $set1'], [0], [dnl
+ip,nw_src=10.0.0.1
+ip,nw_src=10.0.0.2
+ip,nw_src=10.0.0.3
+])
+AT_CHECK([expr_to_flow 'ip4.src == {1.2.3.4, $set1}'], [0], [dnl
+ip,nw_src=1.2.3.4
+ip,nw_src=10.0.0.1
+ip,nw_src=10.0.0.2
+ip,nw_src=10.0.0.3
+])
+AT_CHECK([expr_to_flow 'ip4.src == {1.2.0.0/20, 5.5.5.0/24, $set1}'], [0], [dnl
+ip,nw_src=1.2.0.0/20
+ip,nw_src=10.0.0.1
+ip,nw_src=10.0.0.2
+ip,nw_src=10.0.0.3
+ip,nw_src=5.5.5.0/24
+])
+AT_CHECK([expr_to_flow 'ip6.src == {::1, ::2, ::3}'], [0], [dnl
+ipv6,ipv6_src=::1
+ipv6,ipv6_src=::2
+ipv6,ipv6_src=::3
+])
+AT_CHECK([expr_to_flow 'ip6.src == {::1, $set2, ::4}'], [0], [dnl
+ipv6,ipv6_src=::1
+ipv6,ipv6_src=::2
+ipv6,ipv6_src=::3
+ipv6,ipv6_src=::4
+])
+AT_CHECK([expr_to_flow 'eth.src == {00:00:00:00:00:01, 00:00:00:00:00:02, 00:00:00:00:00:03}'], [0], [dnl
+dl_src=00:00:00:00:00:01
+dl_src=00:00:00:00:00:02
+dl_src=00:00:00:00:00:03
+])
+AT_CHECK([expr_to_flow 'eth.src == {$set3}'], [0], [dnl
+dl_src=00:00:00:00:00:01
+dl_src=00:00:00:00:00:02
+dl_src=00:00:00:00:00:03
+])
+AT_CLEANUP
+
AT_SETUP([ovn -- action parsing])
dnl Text before => is input, text after => is expected output.
AT_DATA([test-cases.txt], [[
dhcp_opt_add(dhcp_opts, "lease_time", 51, "uint32");
}
+static void
+create_macros(struct shash *macros)
+{
+ shash_init(macros);
+
+ static const char *const addrs1[] = {
+ "10.0.0.1", "10.0.0.2", "10.0.0.3",
+ };
+ static const char *const addrs2[] = {
+ "::1", "::2", "::3",
+ };
+ static const char *const addrs3[] = {
+ "00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03",
+ };
+
+ expr_macros_add(macros, "set1", addrs1, 3);
+ expr_macros_add(macros, "set2", addrs2, 3);
+ expr_macros_add(macros, "set3", addrs3, 3);
+}
+
static bool
lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
{
test_parse_expr__(int steps)
{
struct shash symtab;
+ struct shash macros;
struct simap ports;
struct ds input;
create_symtab(&symtab);
+ create_macros(¯os);
simap_init(&ports);
simap_put(&ports, "eth0", 5);
struct expr *expr;
char *error;
- expr = expr_parse_string(ds_cstr(&input), &symtab, &error);
+ expr = expr_parse_string(ds_cstr(&input), &symtab, ¯os, &error);
if (!error && steps > 0) {
expr = expr_annotate(expr, &symtab, &error);
}
simap_destroy(&ports);
expr_symtab_destroy(&symtab);
shash_destroy(&symtab);
+ expr_macros_destroy(¯os);
+ shash_destroy(¯os);
}
static void
struct expr *expr;
char *error;
- expr = expr_parse_string(ds_cstr(&input), &symtab, &error);
+ expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, &error);
if (!error) {
expr = expr_annotate(expr, &symtab, &error);
}
expr_format(expr, &s);
char *error;
- modified = expr_parse_string(ds_cstr(&s), symtab, &error);
+ modified = expr_parse_string(ds_cstr(&s), symtab, NULL, &error);
if (error) {
fprintf(stderr, "%s fails to parse (%s)\n",
ds_cstr(&s), error);