[ovs-dev] [PATCH 2/4] expr: Refactor parsing of assignments and exchanges.

Ben Pfaff blp at ovn.org
Thu Jun 9 05:37:59 UTC 2016


As written, it was difficult for the OVN logical action code to add support
for new actions of the form "dst = ...", because the code to parse the left
side of the assignment was a monolithic part of the expr library.  This
commit refactors the code division so that an upcoming patch can support a
new "dst = func(args);" kind of action.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 ovn/lib/actions.c |  52 +++++++++++-----
 ovn/lib/expr.c    | 173 +++++++++++++++++++++++++++++++-----------------------
 ovn/lib/expr.h    |  36 ++++++++++--
 tests/ovn.at      |   2 +-
 4 files changed, 165 insertions(+), 98 deletions(-)

diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 5f0bf19..f9309da 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -106,19 +106,35 @@ action_syntax_error(struct action_context *ctx, const char *message, ...)
 static void
 parse_set_action(struct action_context *ctx)
 {
-    struct expr *prereqs;
+    struct expr *prereqs = NULL;
+    struct expr_field dst;
     char *error;
 
-    error = expr_parse_assignment(ctx->lexer, ctx->ap->symtab,
-                                  ctx->ap->lookup_port, ctx->ap->aux,
-                                  ctx->ofpacts, &prereqs);
+    error = expr_parse_field(ctx->lexer, ctx->ap->symtab, &dst);
     if (error) {
+        goto exit;
+    }
+
+    if (lexer_match(ctx->lexer, LEX_T_EXCHANGE)) {
+        error = expr_parse_exchange(ctx->lexer, &dst, ctx->ap->symtab,
+                                    ctx->ap->lookup_port, ctx->ap->aux,
+                                    ctx->ofpacts, &prereqs);
+    } else if (lexer_match(ctx->lexer, LEX_T_EQUALS)) {
+        error = expr_parse_assignment(
+            ctx->lexer, &dst, ctx->ap->symtab, ctx->ap->lookup_port,
+            ctx->ap->aux, ctx->ofpacts, &prereqs);
+    } else {
+        action_syntax_error(ctx, "expecting `=' or `<->'");
+    }
+
+exit:
+    if (!error) {
+        ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs);
+    } else {
+        expr_destroy(prereqs);
         action_error(ctx, "%s", error);
         free(error);
-        return;
     }
-
-    ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs);
 }
 
 static void
@@ -282,19 +298,23 @@ static bool
 action_parse_field(struct action_context *ctx,
                    int n_bits, struct mf_subfield *sf)
 {
-    struct expr *prereqs;
+    struct expr_field field;
     char *error;
 
-    error = expr_parse_field(ctx->lexer, n_bits, false, ctx->ap->symtab, sf,
-                             &prereqs);
-    if (error) {
-        action_error(ctx, "%s", error);
-        free(error);
-        return false;
+    error = expr_parse_field(ctx->lexer, ctx->ap->symtab, &field);
+    if (!error) {
+        struct expr *prereqs;
+        error = expr_expand_field(ctx->lexer, ctx->ap->symtab,
+                                  &field, n_bits, false, sf, &prereqs);
+        if (!error) {
+            ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs);
+            return true;
+        }
     }
 
-    ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs);
-    return true;
+    action_error(ctx, "%s", error);
+    free(error);
+    return false;
 }
 
 static void
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index 7ff9538..31b94d8 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -438,15 +438,6 @@ struct expr_constant_set {
     bool in_curlies;              /* Whether the constants were in {}. */
 };
 
-/* A reference to a symbol or a subfield of a symbol.
- *
- * For string fields, ofs and n_bits are 0. */
-struct expr_field {
-    const struct expr_symbol *symbol; /* The symbol. */
-    int ofs;                          /* Starting bit offset. */
-    int n_bits;                       /* Number of bits. */
-};
-
 /* Context maintained during expr_parse(). */
 struct expr_context {
     struct lexer *lexer;        /* Lexer for pulling more tokens. */
@@ -2698,49 +2689,44 @@ init_stack_action(const struct expr_field *f, struct ofpact_stack *stack)
     mf_subfield_from_expr_field(f, &stack->subfield);
 }
 
-static struct expr *
-parse_assignment(struct expr_context *ctx,
+static char * OVS_WARN_UNUSED_RESULT
+parse_assignment(struct lexer *lexer, struct expr_field *dst,
+                 const struct shash *symtab, bool exchange,
                  bool (*lookup_port)(const void *aux, const char *port_name,
                                      unsigned int *portp),
-                 const void *aux, struct ofpbuf *ofpacts)
+                 const void *aux, struct ofpbuf *ofpacts,
+                 struct expr **prereqsp)
 {
+    struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
     struct expr *prereqs = NULL;
 
     /* Parse destination and do basic checking. */
-    struct expr_field dst;
-    if (!parse_field(ctx, &dst)) {
-        goto exit;
-    }
-    bool exchange = lexer_match(ctx->lexer, LEX_T_EXCHANGE);
-    if (!exchange && !lexer_match(ctx->lexer, LEX_T_EQUALS)) {
-        expr_syntax_error(ctx, "expecting `='.");
-        goto exit;
-    }
-    const struct expr_symbol *orig_dst = dst.symbol;
-    if (!expand_symbol(ctx, true, &dst, &prereqs)) {
+    const struct expr_symbol *orig_dst = dst->symbol;
+    if (!expand_symbol(&ctx, true, dst, &prereqs)) {
         goto exit;
     }
 
-    if (exchange || ctx->lexer->token.type == LEX_T_ID) {
+    if (exchange || ctx.lexer->token.type == LEX_T_ID) {
         struct expr_field src;
-        if (!parse_field(ctx, &src)) {
+        if (!parse_field(&ctx, &src)) {
             goto exit;
         }
         const struct expr_symbol *orig_src = src.symbol;
-        if (!expand_symbol(ctx, exchange, &src, &prereqs)) {
+        if (!expand_symbol(&ctx, exchange, &src, &prereqs)) {
             goto exit;
         }
 
-        if ((dst.symbol->width != 0) != (src.symbol->width != 0)) {
+        if ((dst->symbol->width != 0) != (src.symbol->width != 0)) {
             if (exchange) {
-                expr_error(ctx,
+                expr_error(&ctx,
                            "Can't exchange %s field (%s) with %s field (%s).",
                            orig_dst->width ? "integer" : "string",
                            orig_dst->name,
                            orig_src->width ? "integer" : "string",
                            orig_src->name);
             } else {
-                expr_error(ctx, "Can't assign %s field (%s) to %s field (%s).",
+                expr_error(&ctx,
+                           "Can't assign %s field (%s) to %s field (%s).",
                            orig_src->width ? "integer" : "string",
                            orig_src->name,
                            orig_dst->width ? "integer" : "string",
@@ -2749,20 +2735,20 @@ parse_assignment(struct expr_context *ctx,
             goto exit;
         }
 
-        if (dst.n_bits != src.n_bits) {
+        if (dst->n_bits != src.n_bits) {
             if (exchange) {
-                expr_error(ctx,
+                expr_error(&ctx,
                            "Can't exchange %d-bit field with %d-bit field.",
-                           dst.n_bits, src.n_bits);
+                           dst->n_bits, src.n_bits);
             } else {
-                expr_error(ctx,
+                expr_error(&ctx,
                            "Can't assign %d-bit value to %d-bit destination.",
-                           src.n_bits, dst.n_bits);
+                           src.n_bits, dst->n_bits);
             }
             goto exit;
-        } else if (!dst.n_bits
-                   && dst.symbol->field->n_bits != src.symbol->field->n_bits) {
-            expr_error(ctx, "String fields %s and %s are incompatible for "
+        } else if (!dst->n_bits &&
+                   dst->symbol->field->n_bits != src.symbol->field->n_bits) {
+            expr_error(&ctx, "String fields %s and %s are incompatible for "
                        "%s.", orig_dst->name, orig_src->name,
                        exchange ? "exchange" : "assignment");
             goto exit;
@@ -2770,38 +2756,38 @@ parse_assignment(struct expr_context *ctx,
 
         if (exchange) {
             init_stack_action(&src, ofpact_put_STACK_PUSH(ofpacts));
-            init_stack_action(&dst, ofpact_put_STACK_PUSH(ofpacts));
+            init_stack_action(dst, ofpact_put_STACK_PUSH(ofpacts));
             init_stack_action(&src, ofpact_put_STACK_POP(ofpacts));
-            init_stack_action(&dst, ofpact_put_STACK_POP(ofpacts));
+            init_stack_action(dst, ofpact_put_STACK_POP(ofpacts));
         } else {
             struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
             mf_subfield_from_expr_field(&src, &move->src);
-            mf_subfield_from_expr_field(&dst, &move->dst);
+            mf_subfield_from_expr_field(dst, &move->dst);
         }
     } else {
         struct expr_constant_set cs;
-        if (!parse_constant_set(ctx, &cs)) {
+        if (!parse_constant_set(&ctx, &cs)) {
             goto exit;
         }
 
-        if (!type_check(ctx, &dst, &cs)) {
+        if (!type_check(&ctx, dst, &cs)) {
             goto exit_destroy_cs;
         }
         if (cs.in_curlies) {
-            expr_error(ctx, "Assignments require a single value.");
+            expr_error(&ctx, "Assignments require a single value.");
             goto exit_destroy_cs;
         }
 
         union expr_constant *c = cs.values;
         struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
-        sf->field = dst.symbol->field;
-        if (dst.symbol->width) {
-            mf_subvalue_shift(&c->value, dst.ofs);
+        sf->field = dst->symbol->field;
+        if (dst->symbol->width) {
+            mf_subvalue_shift(&c->value, dst->ofs);
             if (!c->masked) {
                 memset(&c->mask, 0, sizeof c->mask);
-                bitwise_one(&c->mask, sizeof c->mask, dst.ofs, dst.n_bits);
+                bitwise_one(&c->mask, sizeof c->mask, dst->ofs, dst->n_bits);
             } else {
-                mf_subvalue_shift(&c->mask, dst.ofs);
+                mf_subvalue_shift(&c->mask, dst->ofs);
             }
 
             memcpy(&sf->value,
@@ -2835,65 +2821,102 @@ parse_assignment(struct expr_context *ctx,
     }
 
 exit:
-    return prereqs;
+    if (ctx.error) {
+        expr_destroy(prereqs);
+        prereqs = NULL;
+    }
+    *prereqsp = prereqs;
+    return ctx.error;
 }
 
 /* A helper for actions_parse(), to parse an OVN assignment action in the form
- * "field = value" or "field1 = field2", or a "exchange" action in the form
- * "field1 <-> field2", into 'ofpacts'.  The parameters and return value match
- * those for actions_parse(). */
-char *
-expr_parse_assignment(struct lexer *lexer, const struct shash *symtab,
+ * "field = value" or "field = field2" into 'ofpacts'.  The caller must have
+ * already parsed and skipped the left-hand side "field =" and pass in the
+ * field as 'dst'.  Other parameters and return value match those for
+ * actions_parse(). */
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_assignment(struct lexer *lexer, struct expr_field *dst,
+                      const struct shash *symtab,
                       bool (*lookup_port)(const void *aux,
                                           const char *port_name,
                                           unsigned int *portp),
                       const void *aux,
                       struct ofpbuf *ofpacts, struct expr **prereqsp)
 {
+    return parse_assignment(lexer, dst, symtab, false, lookup_port, aux,
+                            ofpacts, prereqsp);
+}
+
+/* A helper for actions_parse(), to parse an OVN exchange action in the form
+ * "field1 <-> field2" into 'ofpacts'.  The caller must have already parsed and
+ * skipped the left-hand side "field1 <->" and pass in 'field1'.  Other
+ * parameters and return value match those for actions_parse(). */
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_exchange(struct lexer *lexer, struct expr_field *field,
+                    const struct shash *symtab,
+                    bool (*lookup_port)(const void *aux,
+                                        const char *port_name,
+                                        unsigned int *portp),
+                    const void *aux,
+                    struct ofpbuf *ofpacts, struct expr **prereqsp)
+{
+    return parse_assignment(lexer, field, symtab, true, lookup_port, aux,
+                            ofpacts, prereqsp);
+}
+
+/* Parses a field or subfield from 'lexer' into 'field', obtaining field names
+ * from 'symtab'.  Returns NULL if successful, otherwise an error message owned
+ * by the caller. */
+char * OVS_WARN_UNUSED_RESULT
+expr_parse_field(struct lexer *lexer, const struct shash *symtab,
+                 struct expr_field *field)
+{
     struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
-    struct expr *prereqs = parse_assignment(&ctx, lookup_port, aux, ofpacts);
-    if (ctx.error) {
-        expr_destroy(prereqs);
-        prereqs = NULL;
+    if (!parse_field(&ctx, field)) {
+        memset(field, 0, sizeof *field);
     }
-    *prereqsp = prereqs;
     return ctx.error;
 }
 
-char *
-expr_parse_field(struct lexer *lexer, int n_bits, bool rw,
-                 const struct shash *symtab,
-                 struct mf_subfield *sf, struct expr **prereqsp)
+/* Takes 'field', which was presumably parsed by expr_parse_field(), and
+ * converts it into mf_subfield 'sf' and a set of prerequisites in '*prereqsp'.
+ *
+ * 'n_bits' specifies the number of bits that the field must have, and 0
+ * indicates a string field; reports an error if 'field' has a different type
+ * or width.  If 'rw' is true, it is an error if 'field' is read-only.  Uses
+ * 'symtab 'for expanding references and 'lexer' for error reporting.
+ *
+ * Returns NULL if successful, otherwise an error message owned by the
+ * caller. */
+char * OVS_WARN_UNUSED_RESULT
+expr_expand_field(struct lexer *lexer, const struct shash *symtab,
+                  const struct expr_field *orig_field, int n_bits, bool rw,
+                  struct mf_subfield *sf, struct expr **prereqsp)
 {
     struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
     struct expr *prereqs = NULL;
 
-    struct expr_field field;
-    if (!parse_field(&ctx, &field)) {
-        goto exit;
-    }
-
-    const struct expr_field orig_field = field;
+    struct expr_field field = *orig_field;
     if (!expand_symbol(&ctx, rw, &field, &prereqs)) {
         goto exit;
     }
-    ovs_assert(field.n_bits == orig_field.n_bits);
+    ovs_assert(field.n_bits == orig_field->n_bits);
 
     if (n_bits != field.n_bits) {
         if (n_bits && field.n_bits) {
             expr_error(&ctx, "Cannot use %d-bit field %s[%d..%d] "
                        "where %d-bit field is required.",
-                       orig_field.n_bits, orig_field.symbol->name,
-                       orig_field.ofs, orig_field.ofs + orig_field.n_bits - 1,
-                       n_bits);
+                       orig_field->n_bits, orig_field->symbol->name,
+                       orig_field->ofs,
+                       orig_field->ofs + orig_field->n_bits - 1, n_bits);
         } else if (n_bits) {
             expr_error(&ctx, "Cannot use string field %s where numeric "
                        "field is required.",
-                       orig_field.symbol->name);
+                       orig_field->symbol->name);
         } else {
             expr_error(&ctx, "Cannot use numeric field %s where string "
                        "field is required.",
-                       orig_field.symbol->name);
+                       orig_field->symbol->name);
         }
     }
 
diff --git a/ovn/lib/expr.h b/ovn/lib/expr.h
index 1327789..351d0ff 100644
--- a/ovn/lib/expr.h
+++ b/ovn/lib/expr.h
@@ -248,6 +248,15 @@ struct expr_symbol {
     bool must_crossproduct;
 };
 
+/* A reference to a symbol or a subfield of a symbol.
+ *
+ * For string fields, ofs and n_bits are 0. */
+struct expr_field {
+    const struct expr_symbol *symbol; /* The symbol. */
+    int ofs;                          /* Starting bit offset. */
+    int n_bits;                       /* Number of bits. */
+};
+
 struct expr_symbol *expr_symtab_add_field(struct shash *symtab,
                                           const char *name, enum mf_field_id,
                                           const char *prereqs,
@@ -381,14 +390,29 @@ void expr_matches_print(const struct hmap *matches, FILE *);
 
 /* Action parsing helper. */
 
-char *expr_parse_assignment(struct lexer *lexer, const struct shash *symtab,
+char *expr_parse_assignment(struct lexer *lexer, struct expr_field *dst,
+                            const struct shash *symtab,
                             bool (*lookup_port)(const void *aux,
                                                 const char *port_name,
                                                 unsigned int *portp),
-                            const void *aux, struct ofpbuf *ofpacts,
-                            struct expr **prereqsp);
-char *expr_parse_field(struct lexer *, int n_bits, bool rw,
-                       const struct shash *symtab, struct mf_subfield *,
-                       struct expr **prereqsp);
+                            const void *aux,
+                            struct ofpbuf *ofpacts, struct expr **prereqsp)
+    OVS_WARN_UNUSED_RESULT;
+char *expr_parse_exchange(struct lexer *lexer, struct expr_field *dst,
+                          const struct shash *symtab,
+                          bool (*lookup_port)(const void *aux,
+                                              const char *port_name,
+                                              unsigned int *portp),
+                          const void *aux,
+                          struct ofpbuf *ofpacts, struct expr **prereqsp)
+    OVS_WARN_UNUSED_RESULT;
+char *expr_parse_field(struct lexer *lexer, const struct shash *symtab,
+                       struct expr_field *field)
+    OVS_WARN_UNUSED_RESULT;
+char *expr_expand_field(struct lexer *lexer, const struct shash *symtab,
+                        const struct expr_field *orig_field,
+                        int n_bits, bool rw,
+                        struct mf_subfield *sf, struct expr **prereqsp)
+    OVS_WARN_UNUSED_RESULT;
 
 #endif /* ovn/expr.h */
diff --git a/tests/ovn.at b/tests/ovn.at
index 633cf35..a05f1ef 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -465,7 +465,7 @@ outport="eth0"; next; outport="LOCAL"; next; => actions=set_field:0x5->reg7,resu
 
 inport[1] = 1; => Cannot select subfield of string field inport.
 ip.proto[1] = 1; => Cannot select subfield of nominal field ip.proto.
-eth.dst[40] == 1; => Syntax error at `==' expecting `='.
+eth.dst[40] == 1; => Syntax error at `==' expecting `=' or `<->'.
 ip = 1; => Predicate symbol ip used where lvalue required.
 ip.proto = 6; => Field ip.proto is not modifiable.
 inport = {"a", "b"}; => Assignments require a single value.
-- 
2.1.3




More information about the dev mailing list