pipeline: improve learner table timers
[dpdk.git] / lib / pipeline / rte_swx_pipeline_spec.c
index 2e867d7..904b9eb 100644 (file)
@@ -7,18 +7,30 @@
 #include <string.h>
 #include <errno.h>
 
+#include <rte_common.h>
+
 #include "rte_swx_pipeline.h"
-#include "rte_swx_ctl.h"
 
-#define MAX_LINE_LENGTH RTE_SWX_INSTRUCTION_SIZE
-#define MAX_TOKENS RTE_SWX_INSTRUCTION_TOKENS_MAX
+#ifndef MAX_LINE_LENGTH
+#define MAX_LINE_LENGTH 2048
+#endif
+
+#ifndef MAX_TOKENS
+#define MAX_TOKENS 256
+#endif
 
 #define STRUCT_BLOCK 0
 #define ACTION_BLOCK 1
 #define TABLE_BLOCK 2
 #define TABLE_KEY_BLOCK 3
 #define TABLE_ACTIONS_BLOCK 4
-#define APPLY_BLOCK 5
+#define SELECTOR_BLOCK 5
+#define SELECTOR_SELECTOR_BLOCK 6
+#define LEARNER_BLOCK 7
+#define LEARNER_KEY_BLOCK 8
+#define LEARNER_ACTIONS_BLOCK 9
+#define LEARNER_TIMEOUT_BLOCK 10
+#define APPLY_BLOCK 11
 
 /*
  * extobj.
@@ -93,7 +105,7 @@ extobj_statement_parse(struct extobj_spec *s,
  * struct.
  *
  * struct STRUCT_TYPE_NAME {
- *     bit<SIZE> FIELD_NAME
+ *     bit<SIZE> | varbit<SIZE> FIELD_NAME
  *     ...
  * }
  */
@@ -101,6 +113,7 @@ struct struct_spec {
        char *name;
        struct rte_swx_field_params *fields;
        uint32_t n_fields;
+       int varbit;
 };
 
 static void
@@ -124,6 +137,8 @@ struct_spec_free(struct struct_spec *s)
        s->fields = NULL;
 
        s->n_fields = 0;
+
+       s->varbit = 0;
 }
 
 static int
@@ -170,8 +185,9 @@ struct_block_parse(struct struct_spec *s,
                   const char **err_msg)
 {
        struct rte_swx_field_params *new_fields;
-       char *p = tokens[0], *name;
+       char *p = tokens[0], *name = NULL;
        uint32_t n_bits;
+       int varbit = 0, error = 0, error_size_invalid = 0, error_varbit_not_last = 0;
 
        /* Handle end of block. */
        if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
@@ -180,64 +196,98 @@ struct_block_parse(struct struct_spec *s,
        }
 
        /* Check format. */
-       if ((n_tokens != 2) ||
-           (strlen(p) < 6) ||
-           (p[0] != 'b') ||
-           (p[1] != 'i') ||
-           (p[2] != 't') ||
-           (p[3] != '<') ||
-           (p[strlen(p) - 1] != '>')) {
-               if (err_line)
-                       *err_line = n_lines;
-               if (err_msg)
-                       *err_msg = "Invalid struct field statement.";
-               return -EINVAL;
+       if (n_tokens != 2) {
+               error = -EINVAL;
+               goto error;
+       }
+
+       if (s->varbit) {
+               error = -EINVAL;
+               error_varbit_not_last = 1;
+               goto error;
        }
 
-       /* Remove the "bit<" and ">". */
-       p[strlen(p) - 1] = 0;
-       p += 4;
+       if (!strncmp(p, "bit<", strlen("bit<"))) {
+               size_t len = strlen(p);
+
+               if ((len < strlen("bit< >")) || (p[len - 1] != '>')) {
+                       error = -EINVAL;
+                       goto error;
+               }
+
+               /* Remove the "bit<" and ">". */
+               p[strlen(p) - 1] = 0;
+               p += strlen("bit<");
+       } else if (!strncmp(p, "varbit<", strlen("varbit<"))) {
+               size_t len = strlen(p);
+
+               if ((len < strlen("varbit< >")) || (p[len - 1] != '>')) {
+                       error = -EINVAL;
+                       goto error;
+               }
+
+               /* Remove the "varbit<" and ">". */
+               p[strlen(p) - 1] = 0;
+               p += strlen("varbit<");
+
+               /* Set the varbit flag. */
+               varbit = 1;
+       } else {
+               error = -EINVAL;
+               goto error;
+       }
 
        n_bits = strtoul(p, &p, 0);
        if ((p[0]) ||
            !n_bits ||
            (n_bits % 8) ||
-           (n_bits > 64)) {
-               if (err_line)
-                       *err_line = n_lines;
-               if (err_msg)
-                       *err_msg = "Invalid struct field size.";
-               return -EINVAL;
+           ((n_bits > 64) && !varbit)) {
+               error = -EINVAL;
+               error_size_invalid = 1;
+               goto error;
        }
 
        /* spec. */
        name = strdup(tokens[1]);
        if (!name) {
-               if (err_line)
-                       *err_line = n_lines;
-               if (err_msg)
-                       *err_msg = "Memory allocation failed.";
-               return -ENOMEM;
+               error = -ENOMEM;
+               goto error;
        }
 
-       new_fields = realloc(s->fields,
-                            (s->n_fields + 1) * sizeof(struct rte_swx_field_params));
+       new_fields = realloc(s->fields, (s->n_fields + 1) * sizeof(struct rte_swx_field_params));
        if (!new_fields) {
-               free(name);
-
-               if (err_line)
-                       *err_line = n_lines;
-               if (err_msg)
-                       *err_msg = "Memory allocation failed.";
-               return -ENOMEM;
+               error = -ENOMEM;
+               goto error;
        }
 
        s->fields = new_fields;
        s->fields[s->n_fields].name = name;
        s->fields[s->n_fields].n_bits = n_bits;
        s->n_fields++;
+       s->varbit = varbit;
 
        return 0;
+
+error:
+       free(name);
+
+       if (err_line)
+               *err_line = n_lines;
+
+       if (err_msg) {
+               *err_msg = "Invalid struct field statement.";
+
+               if ((error == -EINVAL) && error_varbit_not_last)
+                       *err_msg = "Varbit field is not the last struct field.";
+
+               if ((error == -EINVAL) && error_size_invalid)
+                       *err_msg = "Invalid struct field size.";
+
+               if (error == -ENOMEM)
+                       *err_msg = "Memory allocation failed.";
+       }
+
+       return error;
 }
 
 /*
@@ -490,16 +540,16 @@ action_block_parse(struct action_spec *s,
 /*
  * table.
  *
- * table {
+ * table TABLE_NAME {
  *     key {
  *             MATCH_FIELD_NAME exact | wildcard | lpm
  *             ...
  *     }
  *     actions {
- *             ACTION_NAME
+ *             ACTION_NAME [ @tableonly | @defaultonly ]
  *             ...
  *     }
- *     default_action ACTION_NAME args none | ARGS_BYTE_ARRAY [ const ]
+ *     default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
  *     instanceof TABLE_TYPE_NAME
  *     pragma ARGS
  *     size SIZE
@@ -516,7 +566,7 @@ struct table_spec {
 static void
 table_spec_free(struct table_spec *s)
 {
-       uintptr_t default_action_name;
+       uintptr_t default_action_name, default_action_args;
        uint32_t i;
 
        if (!s)
@@ -551,8 +601,15 @@ table_spec_free(struct table_spec *s)
        free((void *)default_action_name);
        s->params.default_action_name = NULL;
 
-       free(s->params.default_action_data);
-       s->params.default_action_data = NULL;
+       default_action_args = (uintptr_t)s->params.default_action_args;
+       free((void *)default_action_args);
+       s->params.default_action_args = NULL;
+
+       free(s->params.action_is_for_table_entries);
+       s->params.action_is_for_table_entries = NULL;
+
+       free(s->params.action_is_for_default_entry);
+       s->params.action_is_for_default_entry = NULL;
 
        s->params.default_action_is_const = 0;
 
@@ -687,8 +744,10 @@ table_actions_block_parse(struct table_spec *s,
                          uint32_t *err_line,
                          const char **err_msg)
 {
-       const char **new_action_names;
-       char *name;
+       const char **new_action_names = NULL;
+       int *new_action_is_for_table_entries = NULL, *new_action_is_for_default_entry = NULL;
+       char *name = NULL;
+       int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
 
        /* Handle end of block. */
        if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
@@ -697,7 +756,9 @@ table_actions_block_parse(struct table_spec *s,
        }
 
        /* Check input arguments. */
-       if (n_tokens != 1) {
+       if ((n_tokens > 2) ||
+           ((n_tokens == 2) && strcmp(tokens[1], "@tableonly") &&
+             strcmp(tokens[1], "@defaultonly"))) {
                if (err_line)
                        *err_line = n_lines;
                if (err_msg)
@@ -706,18 +767,30 @@ table_actions_block_parse(struct table_spec *s,
        }
 
        name = strdup(tokens[0]);
-       if (!name) {
-               if (err_line)
-                       *err_line = n_lines;
-               if (err_msg)
-                       *err_msg = "Memory allocation failed.";
-               return -ENOMEM;
+
+       if (n_tokens == 2) {
+               if (!strcmp(tokens[1], "@tableonly"))
+                       action_is_for_default_entry = 0;
+
+               if (!strcmp(tokens[1], "@defaultonly"))
+                       action_is_for_table_entries = 0;
        }
 
        new_action_names = realloc(s->params.action_names,
                                   (s->params.n_actions + 1) * sizeof(char *));
-       if (!new_action_names) {
+       new_action_is_for_table_entries = realloc(s->params.action_is_for_table_entries,
+                                                 (s->params.n_actions + 1) * sizeof(int));
+       new_action_is_for_default_entry = realloc(s->params.action_is_for_default_entry,
+                                                 (s->params.n_actions + 1) * sizeof(int));
+
+       if (!name ||
+           !new_action_names ||
+           !new_action_is_for_table_entries ||
+           !new_action_is_for_default_entry) {
                free(name);
+               free(new_action_names);
+               free(new_action_is_for_table_entries);
+               free(new_action_is_for_default_entry);
 
                if (err_line)
                        *err_line = n_lines;
@@ -728,11 +801,106 @@ table_actions_block_parse(struct table_spec *s,
 
        s->params.action_names = new_action_names;
        s->params.action_names[s->params.n_actions] = name;
+
+       s->params.action_is_for_table_entries = new_action_is_for_table_entries;
+       s->params.action_is_for_table_entries[s->params.n_actions] = action_is_for_table_entries;
+
+       s->params.action_is_for_default_entry = new_action_is_for_default_entry;
+       s->params.action_is_for_default_entry[s->params.n_actions] = action_is_for_default_entry;
+
        s->params.n_actions++;
 
        return 0;
 }
 
+static int
+table_default_action_statement_parse(struct table_spec *s,
+                                    char **tokens,
+                                    uint32_t n_tokens,
+                                    uint32_t n_lines,
+                                    uint32_t *err_line,
+                                    const char **err_msg)
+{
+       uint32_t i;
+       int status = 0, duplicate = 0;
+
+       /* Check format. */
+       if ((n_tokens < 4) ||
+           strcmp(tokens[2], "args")) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       if (s->params.default_action_name) {
+               duplicate = 1;
+               status = -EINVAL;
+               goto error;
+       }
+
+       s->params.default_action_name = strdup(tokens[1]);
+       if (!s->params.default_action_name) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       if (strcmp(tokens[3], "none")) {
+               char buffer[MAX_LINE_LENGTH];
+               uint32_t n_tokens_args = n_tokens - 3;
+
+               if (!strcmp(tokens[n_tokens - 1], "const"))
+                       n_tokens_args--;
+
+               if (!n_tokens_args) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               buffer[0] = 0;
+               for (i = 0; i < n_tokens_args; i++) {
+                       if (i)
+                               strcat(buffer, " ");
+
+                       strcat(buffer, tokens[3 + i]);
+               }
+
+               s->params.default_action_args = strdup(buffer);
+               if (!s->params.default_action_args) {
+                       status = -ENOMEM;
+                       goto error;
+               }
+       } else {
+               if (((n_tokens != 4) && (n_tokens != 5)) ||
+                   ((n_tokens == 5) && (strcmp(tokens[4], "const")))) {
+                       status = -EINVAL;
+                       goto error;
+               }
+       }
+
+       if (!strcmp(tokens[n_tokens - 1], "const"))
+               s->params.default_action_is_const = 1;
+
+       return 0;
+
+error:
+       if (err_line)
+               *err_line = n_lines;
+
+       if (err_msg)
+               switch (status) {
+               case -ENOMEM:
+                       *err_msg = "Memory allocation failed.";
+                       break;
+
+               default:
+                       if (duplicate)
+                               *err_msg = "Duplicate default_action statement.";
+
+                       *err_msg = "Invalid default_action statement.";
+               }
+
+       return status;
+}
+
 static int
 table_statement_parse(struct table_spec *s,
                      uint32_t *block_mask,
@@ -816,40 +984,13 @@ table_block_parse(struct table_spec *s,
                                                     err_line,
                                                     err_msg);
 
-       if (!strcmp(tokens[0], "default_action")) {
-               if (((n_tokens != 4) && (n_tokens != 5)) ||
-                   strcmp(tokens[2], "args") ||
-                   strcmp(tokens[3], "none") ||
-                   ((n_tokens == 5) && strcmp(tokens[4], "const"))) {
-                       if (err_line)
-                               *err_line = n_lines;
-                       if (err_msg)
-                               *err_msg = "Invalid default_action statement.";
-                       return -EINVAL;
-               }
-
-               if (s->params.default_action_name) {
-                       if (err_line)
-                               *err_line = n_lines;
-                       if (err_msg)
-                               *err_msg = "Duplicate default_action stmt.";
-                       return -EINVAL;
-               }
-
-               s->params.default_action_name = strdup(tokens[1]);
-               if (!s->params.default_action_name) {
-                       if (err_line)
-                               *err_line = n_lines;
-                       if (err_msg)
-                               *err_msg = "Memory allocation failed.";
-                       return -ENOMEM;
-               }
-
-               if (n_tokens == 5)
-                       s->params.default_action_is_const = 1;
-
-               return 0;
-       }
+       if (!strcmp(tokens[0], "default_action"))
+               return table_default_action_statement_parse(s,
+                                                           tokens,
+                                                           n_tokens,
+                                                           n_lines,
+                                                           err_line,
+                                                           err_msg);
 
        if (!strcmp(tokens[0], "instanceof")) {
                if (n_tokens != 2) {
@@ -941,44 +1082,81 @@ table_block_parse(struct table_spec *s,
 }
 
 /*
- * regarray.
+ * selector.
  *
- * regarray NAME size SIZE initval INITVAL
+ * selector SELECTOR_NAME {
+ *     group_id FIELD_NAME
+ *     selector {
+ *             FIELD_NAME
+ *             ...
+ *     }
+ *     member_id FIELD_NAME
+ *     n_groups N_GROUPS
+ *     n_members_per_group N_MEMBERS_PER_GROUP
+ * }
  */
-struct regarray_spec {
+struct selector_spec {
        char *name;
-       uint64_t init_val;
-       uint32_t size;
+       struct rte_swx_pipeline_selector_params params;
 };
 
 static void
-regarray_spec_free(struct regarray_spec *s)
+selector_spec_free(struct selector_spec *s)
 {
+       uintptr_t field_name;
+       uint32_t i;
+
        if (!s)
                return;
 
+       /* name. */
        free(s->name);
        s->name = NULL;
+
+       /* params->group_id_field_name. */
+       field_name = (uintptr_t)s->params.group_id_field_name;
+       free((void *)field_name);
+       s->params.group_id_field_name = NULL;
+
+       /* params->selector_field_names. */
+       for (i = 0; i < s->params.n_selector_fields; i++) {
+               field_name = (uintptr_t)s->params.selector_field_names[i];
+
+               free((void *)field_name);
+       }
+
+       free(s->params.selector_field_names);
+       s->params.selector_field_names = NULL;
+
+       s->params.n_selector_fields = 0;
+
+       /* params->member_id_field_name. */
+       field_name = (uintptr_t)s->params.member_id_field_name;
+       free((void *)field_name);
+       s->params.member_id_field_name = NULL;
+
+       /* params->n_groups_max. */
+       s->params.n_groups_max = 0;
+
+       /* params->n_members_per_group_max. */
+       s->params.n_members_per_group_max = 0;
 }
 
 static int
-regarray_statement_parse(struct regarray_spec *s,
+selector_statement_parse(struct selector_spec *s,
+                        uint32_t *block_mask,
                         char **tokens,
                         uint32_t n_tokens,
                         uint32_t n_lines,
                         uint32_t *err_line,
                         const char **err_msg)
 {
-       char *p;
-
        /* Check format. */
-       if ((n_tokens != 6) ||
-            strcmp(tokens[2], "size") ||
-            strcmp(tokens[4], "initval")) {
+       if ((n_tokens != 3) || strcmp(tokens[2], "{")) {
                if (err_line)
                        *err_line = n_lines;
                if (err_msg)
-                       *err_msg = "Invalid regarray statement.";
+                       *err_msg = "Invalid selector statement.";
                return -EINVAL;
        }
 
@@ -992,71 +1170,64 @@ regarray_statement_parse(struct regarray_spec *s,
                return -ENOMEM;
        }
 
-       p = tokens[3];
-       s->size = strtoul(p, &p, 0);
-       if (p[0] || !s->size) {
-               if (err_line)
-                       *err_line = n_lines;
-               if (err_msg)
-                       *err_msg = "Invalid size argument.";
-               return -EINVAL;
-       }
+       /* block_mask. */
+       *block_mask |= 1 << SELECTOR_BLOCK;
 
-       p = tokens[5];
-       s->init_val = strtoull(p, &p, 0);
-       if (p[0]) {
+       return 0;
+}
+
+static int
+selector_selector_statement_parse(uint32_t *block_mask,
+                                 char **tokens,
+                                 uint32_t n_tokens,
+                                 uint32_t n_lines,
+                                 uint32_t *err_line,
+                                 const char **err_msg)
+{
+       /* Check format. */
+       if ((n_tokens != 2) || strcmp(tokens[1], "{")) {
                if (err_line)
                        *err_line = n_lines;
                if (err_msg)
-                       *err_msg = "Invalid initval argument.";
+                       *err_msg = "Invalid selector statement.";
                return -EINVAL;
        }
 
-       return 0;
-}
-
-/*
- * metarray.
- *
- * metarray NAME size SIZE
- */
-struct metarray_spec {
-       char *name;
-       uint32_t size;
-};
-
-static void
-metarray_spec_free(struct metarray_spec *s)
-{
-       if (!s)
-               return;
+       /* block_mask. */
+       *block_mask |= 1 << SELECTOR_SELECTOR_BLOCK;
 
-       free(s->name);
-       s->name = NULL;
+       return 0;
 }
 
 static int
-metarray_statement_parse(struct metarray_spec *s,
-                        char **tokens,
-                        uint32_t n_tokens,
-                        uint32_t n_lines,
-                        uint32_t *err_line,
-                        const char **err_msg)
+selector_selector_block_parse(struct selector_spec *s,
+                             uint32_t *block_mask,
+                             char **tokens,
+                             uint32_t n_tokens,
+                             uint32_t n_lines,
+                             uint32_t *err_line,
+                             const char **err_msg)
 {
-       char *p;
+       const char **new_fields;
+       char *name;
 
-       /* Check format. */
-       if ((n_tokens != 4) || strcmp(tokens[2], "size")) {
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << SELECTOR_SELECTOR_BLOCK);
+               return 0;
+       }
+
+       /* Check input arguments. */
+       if (n_tokens != 1) {
                if (err_line)
                        *err_line = n_lines;
                if (err_msg)
-                       *err_msg = "Invalid metarray statement.";
+                       *err_msg = "Invalid selector field statement.";
                return -EINVAL;
        }
 
-       /* spec. */
-       s->name = strdup(tokens[1]);
-       if (!s->name) {
+       name = strdup(tokens[0]);
+       if (!name) {
                if (err_line)
                        *err_line = n_lines;
                if (err_msg)
@@ -1064,28 +1235,872 @@ metarray_statement_parse(struct metarray_spec *s,
                return -ENOMEM;
        }
 
-       p = tokens[3];
-       s->size = strtoul(p, &p, 0);
-       if (p[0] || !s->size) {
+       new_fields = realloc(s->params.selector_field_names,
+                            (s->params.n_selector_fields + 1) * sizeof(char *));
+       if (!new_fields) {
+               free(name);
+
                if (err_line)
                        *err_line = n_lines;
                if (err_msg)
-                       *err_msg = "Invalid size argument.";
-               return -EINVAL;
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
        }
 
+       s->params.selector_field_names = new_fields;
+       s->params.selector_field_names[s->params.n_selector_fields] = name;
+       s->params.n_selector_fields++;
+
        return 0;
 }
 
-/*
- * apply.
- *
- * apply {
- *     INSTRUCTION
- *     ...
- * }
- */
-struct apply_spec {
+static int
+selector_block_parse(struct selector_spec *s,
+                    uint32_t *block_mask,
+                    char **tokens,
+                    uint32_t n_tokens,
+                    uint32_t n_lines,
+                    uint32_t *err_line,
+                    const char **err_msg)
+{
+       if (*block_mask & (1 << SELECTOR_SELECTOR_BLOCK))
+               return selector_selector_block_parse(s,
+                                                    block_mask,
+                                                    tokens,
+                                                    n_tokens,
+                                                    n_lines,
+                                                    err_line,
+                                                    err_msg);
+
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << SELECTOR_BLOCK);
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "group_id")) {
+               if (n_tokens != 2) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid group_id statement.";
+                       return -EINVAL;
+               }
+
+               s->params.group_id_field_name = strdup(tokens[1]);
+               if (!s->params.group_id_field_name) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Memory allocation failed.";
+                       return -ENOMEM;
+               }
+
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "selector"))
+               return selector_selector_statement_parse(block_mask,
+                                                        tokens,
+                                                        n_tokens,
+                                                        n_lines,
+                                                        err_line,
+                                                        err_msg);
+
+       if (!strcmp(tokens[0], "member_id")) {
+               if (n_tokens != 2) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid member_id statement.";
+                       return -EINVAL;
+               }
+
+               s->params.member_id_field_name = strdup(tokens[1]);
+               if (!s->params.member_id_field_name) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Memory allocation failed.";
+                       return -ENOMEM;
+               }
+
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "n_groups_max")) {
+               char *p = tokens[1];
+
+               if (n_tokens != 2) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid n_groups statement.";
+                       return -EINVAL;
+               }
+
+               s->params.n_groups_max = strtoul(p, &p, 0);
+               if (p[0]) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid n_groups argument.";
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "n_members_per_group_max")) {
+               char *p = tokens[1];
+
+               if (n_tokens != 2) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid n_members_per_group statement.";
+                       return -EINVAL;
+               }
+
+               s->params.n_members_per_group_max = strtoul(p, &p, 0);
+               if (p[0]) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid n_members_per_group argument.";
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       /* Anything else. */
+       if (err_line)
+               *err_line = n_lines;
+       if (err_msg)
+               *err_msg = "Invalid statement.";
+       return -EINVAL;
+}
+
+/*
+ * learner.
+ *
+ * learner LEARNER_NAME {
+ *     key {
+ *             MATCH_FIELD_NAME
+ *             ...
+ *     }
+ *     actions {
+ *             ACTION_NAME [ @tableonly | @defaultonly]
+ *             ...
+ *     }
+ *     default_action ACTION_NAME args none | ARG0_NAME ARG0_VALUE ... [ const ]
+ *     size SIZE
+ *     timeout {
+ *             TIMEOUT_IN_SECONDS
+ *             ...
+ *     }
+ * }
+ */
+struct learner_spec {
+       char *name;
+       struct rte_swx_pipeline_learner_params params;
+       uint32_t size;
+       uint32_t *timeout;
+       uint32_t n_timeouts;
+};
+
+static void
+learner_spec_free(struct learner_spec *s)
+{
+       uintptr_t default_action_name, default_action_args;
+       uint32_t i;
+
+       if (!s)
+               return;
+
+       free(s->name);
+       s->name = NULL;
+
+       for (i = 0; i < s->params.n_fields; i++) {
+               uintptr_t name = (uintptr_t)s->params.field_names[i];
+
+               free((void *)name);
+       }
+
+       free(s->params.field_names);
+       s->params.field_names = NULL;
+
+       s->params.n_fields = 0;
+
+       for (i = 0; i < s->params.n_actions; i++) {
+               uintptr_t name = (uintptr_t)s->params.action_names[i];
+
+               free((void *)name);
+       }
+
+       free(s->params.action_names);
+       s->params.action_names = NULL;
+
+       s->params.n_actions = 0;
+
+       default_action_name = (uintptr_t)s->params.default_action_name;
+       free((void *)default_action_name);
+       s->params.default_action_name = NULL;
+
+       default_action_args = (uintptr_t)s->params.default_action_args;
+       free((void *)default_action_args);
+       s->params.default_action_args = NULL;
+
+       free(s->params.action_is_for_table_entries);
+       s->params.action_is_for_table_entries = NULL;
+
+       free(s->params.action_is_for_default_entry);
+       s->params.action_is_for_default_entry = NULL;
+
+       s->params.default_action_is_const = 0;
+
+       s->size = 0;
+
+       free(s->timeout);
+       s->timeout = NULL;
+
+       s->n_timeouts = 0;
+}
+
+static int
+learner_key_statement_parse(uint32_t *block_mask,
+                           char **tokens,
+                           uint32_t n_tokens,
+                           uint32_t n_lines,
+                           uint32_t *err_line,
+                           const char **err_msg)
+{
+       /* Check format. */
+       if ((n_tokens != 2) || strcmp(tokens[1], "{")) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid key statement.";
+               return -EINVAL;
+       }
+
+       /* block_mask. */
+       *block_mask |= 1 << LEARNER_KEY_BLOCK;
+
+       return 0;
+}
+
+static int
+learner_key_block_parse(struct learner_spec *s,
+                       uint32_t *block_mask,
+                       char **tokens,
+                       uint32_t n_tokens,
+                       uint32_t n_lines,
+                       uint32_t *err_line,
+                       const char **err_msg)
+{
+       const char **new_field_names = NULL;
+       char *field_name = NULL;
+
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << LEARNER_KEY_BLOCK);
+               return 0;
+       }
+
+       /* Check input arguments. */
+       if (n_tokens != 1) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid match field statement.";
+               return -EINVAL;
+       }
+
+       field_name = strdup(tokens[0]);
+       new_field_names = realloc(s->params.field_names, (s->params.n_fields + 1) * sizeof(char *));
+       if (!field_name || !new_field_names) {
+               free(field_name);
+               free(new_field_names);
+
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
+       }
+
+       s->params.field_names = new_field_names;
+       s->params.field_names[s->params.n_fields] = field_name;
+       s->params.n_fields++;
+
+       return 0;
+}
+
+static int
+learner_actions_statement_parse(uint32_t *block_mask,
+                               char **tokens,
+                               uint32_t n_tokens,
+                               uint32_t n_lines,
+                               uint32_t *err_line,
+                               const char **err_msg)
+{
+       /* Check format. */
+       if ((n_tokens != 2) || strcmp(tokens[1], "{")) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid actions statement.";
+               return -EINVAL;
+       }
+
+       /* block_mask. */
+       *block_mask |= 1 << LEARNER_ACTIONS_BLOCK;
+
+       return 0;
+}
+
+static int
+learner_actions_block_parse(struct learner_spec *s,
+                           uint32_t *block_mask,
+                           char **tokens,
+                           uint32_t n_tokens,
+                           uint32_t n_lines,
+                           uint32_t *err_line,
+                           const char **err_msg)
+{
+       const char **new_action_names = NULL;
+       int *new_action_is_for_table_entries = NULL, *new_action_is_for_default_entry = NULL;
+       char *name = NULL;
+       int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
+
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << LEARNER_ACTIONS_BLOCK);
+               return 0;
+       }
+
+       /* Check input arguments. */
+       if ((n_tokens > 2) ||
+           ((n_tokens == 2) && strcmp(tokens[1], "@tableonly") &&
+             strcmp(tokens[1], "@defaultonly"))) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid action name statement.";
+               return -EINVAL;
+       }
+
+       name = strdup(tokens[0]);
+
+       if (n_tokens == 2) {
+               if (!strcmp(tokens[1], "@tableonly"))
+                       action_is_for_default_entry = 0;
+
+               if (!strcmp(tokens[1], "@defaultonly"))
+                       action_is_for_table_entries = 0;
+       }
+
+       new_action_names = realloc(s->params.action_names,
+                                  (s->params.n_actions + 1) * sizeof(char *));
+       new_action_is_for_table_entries = realloc(s->params.action_is_for_table_entries,
+                                                 (s->params.n_actions + 1) * sizeof(int));
+       new_action_is_for_default_entry = realloc(s->params.action_is_for_default_entry,
+                                                 (s->params.n_actions + 1) * sizeof(int));
+
+       if (!name ||
+           !new_action_names ||
+           !new_action_is_for_table_entries ||
+           !new_action_is_for_default_entry) {
+               free(name);
+               free(new_action_names);
+               free(new_action_is_for_table_entries);
+               free(new_action_is_for_default_entry);
+
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
+       }
+
+       s->params.action_names = new_action_names;
+       s->params.action_names[s->params.n_actions] = name;
+
+       s->params.action_is_for_table_entries = new_action_is_for_table_entries;
+       s->params.action_is_for_table_entries[s->params.n_actions] = action_is_for_table_entries;
+
+       s->params.action_is_for_default_entry = new_action_is_for_default_entry;
+       s->params.action_is_for_default_entry[s->params.n_actions] = action_is_for_default_entry;
+
+       s->params.n_actions++;
+
+       return 0;
+}
+
+static int
+learner_default_action_statement_parse(struct learner_spec *s,
+                                      char **tokens,
+                                      uint32_t n_tokens,
+                                      uint32_t n_lines,
+                                      uint32_t *err_line,
+                                      const char **err_msg)
+{
+       uint32_t i;
+       int status = 0, duplicate = 0;
+
+       /* Check format. */
+       if ((n_tokens < 4) ||
+           strcmp(tokens[2], "args")) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       if (s->params.default_action_name) {
+               duplicate = 1;
+               status = -EINVAL;
+               goto error;
+       }
+
+       s->params.default_action_name = strdup(tokens[1]);
+       if (!s->params.default_action_name) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       if (strcmp(tokens[3], "none")) {
+               char buffer[MAX_LINE_LENGTH];
+               uint32_t n_tokens_args = n_tokens - 3;
+
+               if (!strcmp(tokens[n_tokens - 1], "const"))
+                       n_tokens_args--;
+
+               if (!n_tokens_args) {
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               buffer[0] = 0;
+               for (i = 0; i < n_tokens_args; i++) {
+                       if (i)
+                               strcat(buffer, " ");
+
+                       strcat(buffer, tokens[3 + i]);
+               }
+
+               s->params.default_action_args = strdup(buffer);
+               if (!s->params.default_action_args) {
+                       status = -ENOMEM;
+                       goto error;
+               }
+       } else {
+               if (((n_tokens != 4) && (n_tokens != 5)) ||
+                   ((n_tokens == 5) && (strcmp(tokens[4], "const")))) {
+                       status = -EINVAL;
+                       goto error;
+               }
+       }
+
+       if (!strcmp(tokens[n_tokens - 1], "const"))
+               s->params.default_action_is_const = 1;
+
+       return 0;
+
+error:
+       if (err_line)
+               *err_line = n_lines;
+
+       if (err_msg)
+               switch (status) {
+               case -ENOMEM:
+                       *err_msg = "Memory allocation failed.";
+                       break;
+
+               default:
+                       if (duplicate)
+                               *err_msg = "Duplicate default_action statement.";
+
+                       *err_msg = "Invalid default_action statement.";
+               }
+
+       return status;
+}
+
+static int
+learner_timeout_statement_parse(uint32_t *block_mask,
+                               char **tokens,
+                               uint32_t n_tokens,
+                               uint32_t n_lines,
+                               uint32_t *err_line,
+                               const char **err_msg)
+{
+       /* Check format. */
+       if ((n_tokens != 2) || strcmp(tokens[1], "{")) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid timeout statement.";
+               return -EINVAL;
+       }
+
+       /* block_mask. */
+       *block_mask |= 1 << LEARNER_TIMEOUT_BLOCK;
+
+       return 0;
+}
+
+static int
+learner_timeout_block_parse(struct learner_spec *s,
+                           uint32_t *block_mask,
+                           char **tokens,
+                           uint32_t n_tokens,
+                           uint32_t n_lines,
+                           uint32_t *err_line,
+                           const char **err_msg)
+{
+       uint32_t *new_timeout = NULL;
+       char *str;
+       uint32_t val;
+       int status = 0;
+
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << LEARNER_TIMEOUT_BLOCK);
+               return 0;
+       }
+
+       /* Check input arguments. */
+       if (n_tokens != 1) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       str = tokens[0];
+       val = strtoul(str, &str, 0);
+       if (str[0]) {
+               status = -EINVAL;
+               goto error;
+       }
+
+       new_timeout = realloc(s->timeout, (s->n_timeouts + 1) * sizeof(uint32_t));
+       if (!new_timeout) {
+               status = -ENOMEM;
+               goto error;
+       }
+
+       s->timeout = new_timeout;
+       s->timeout[s->n_timeouts] = val;
+       s->n_timeouts++;
+
+       return 0;
+
+error:
+       free(new_timeout);
+
+       if (err_line)
+               *err_line = n_lines;
+
+       if (err_msg)
+               switch (status) {
+               case -ENOMEM:
+                       *err_msg = "Memory allocation failed.";
+                       break;
+
+               default:
+                       *err_msg = "Invalid timeout value statement.";
+                       break;
+               }
+
+       return status;
+}
+
+
+static int
+learner_statement_parse(struct learner_spec *s,
+                     uint32_t *block_mask,
+                     char **tokens,
+                     uint32_t n_tokens,
+                     uint32_t n_lines,
+                     uint32_t *err_line,
+                     const char **err_msg)
+{
+       /* Check format. */
+       if ((n_tokens != 3) || strcmp(tokens[2], "{")) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid learner statement.";
+               return -EINVAL;
+       }
+
+       /* spec. */
+       s->name = strdup(tokens[1]);
+       if (!s->name) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
+       }
+
+       /* block_mask. */
+       *block_mask |= 1 << LEARNER_BLOCK;
+
+       return 0;
+}
+
+static int
+learner_block_parse(struct learner_spec *s,
+                   uint32_t *block_mask,
+                   char **tokens,
+                   uint32_t n_tokens,
+                   uint32_t n_lines,
+                   uint32_t *err_line,
+                   const char **err_msg)
+{
+       if (*block_mask & (1 << LEARNER_KEY_BLOCK))
+               return learner_key_block_parse(s,
+                                              block_mask,
+                                              tokens,
+                                              n_tokens,
+                                              n_lines,
+                                              err_line,
+                                              err_msg);
+
+       if (*block_mask & (1 << LEARNER_ACTIONS_BLOCK))
+               return learner_actions_block_parse(s,
+                                                  block_mask,
+                                                  tokens,
+                                                  n_tokens,
+                                                  n_lines,
+                                                  err_line,
+                                                  err_msg);
+
+       if (*block_mask & (1 << LEARNER_TIMEOUT_BLOCK))
+               return learner_timeout_block_parse(s,
+                                                  block_mask,
+                                                  tokens,
+                                                  n_tokens,
+                                                  n_lines,
+                                                  err_line,
+                                                  err_msg);
+
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << LEARNER_BLOCK);
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "key"))
+               return learner_key_statement_parse(block_mask,
+                                                  tokens,
+                                                  n_tokens,
+                                                  n_lines,
+                                                  err_line,
+                                                  err_msg);
+
+       if (!strcmp(tokens[0], "actions"))
+               return learner_actions_statement_parse(block_mask,
+                                                      tokens,
+                                                      n_tokens,
+                                                      n_lines,
+                                                      err_line,
+                                                      err_msg);
+
+       if (!strcmp(tokens[0], "default_action"))
+               return learner_default_action_statement_parse(s,
+                                                             tokens,
+                                                             n_tokens,
+                                                             n_lines,
+                                                             err_line,
+                                                             err_msg);
+
+       if (!strcmp(tokens[0], "size")) {
+               char *p = tokens[1];
+
+               if (n_tokens != 2) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid size statement.";
+                       return -EINVAL;
+               }
+
+               s->size = strtoul(p, &p, 0);
+               if (p[0]) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid size argument.";
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "timeout"))
+               return learner_timeout_statement_parse(block_mask,
+                                                      tokens,
+                                                      n_tokens,
+                                                      n_lines,
+                                                      err_line,
+                                                      err_msg);
+
+       /* Anything else. */
+       if (err_line)
+               *err_line = n_lines;
+       if (err_msg)
+               *err_msg = "Invalid statement.";
+       return -EINVAL;
+}
+
+/*
+ * regarray.
+ *
+ * regarray NAME size SIZE initval INITVAL
+ */
+struct regarray_spec {
+       char *name;
+       uint64_t init_val;
+       uint32_t size;
+};
+
+static void
+regarray_spec_free(struct regarray_spec *s)
+{
+       if (!s)
+               return;
+
+       free(s->name);
+       s->name = NULL;
+}
+
+static int
+regarray_statement_parse(struct regarray_spec *s,
+                        char **tokens,
+                        uint32_t n_tokens,
+                        uint32_t n_lines,
+                        uint32_t *err_line,
+                        const char **err_msg)
+{
+       char *p;
+
+       /* Check format. */
+       if ((n_tokens != 6) ||
+            strcmp(tokens[2], "size") ||
+            strcmp(tokens[4], "initval")) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid regarray statement.";
+               return -EINVAL;
+       }
+
+       /* spec. */
+       s->name = strdup(tokens[1]);
+       if (!s->name) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
+       }
+
+       p = tokens[3];
+       s->size = strtoul(p, &p, 0);
+       if (p[0] || !s->size) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid size argument.";
+               return -EINVAL;
+       }
+
+       p = tokens[5];
+       s->init_val = strtoull(p, &p, 0);
+       if (p[0]) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid initval argument.";
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * metarray.
+ *
+ * metarray NAME size SIZE
+ */
+struct metarray_spec {
+       char *name;
+       uint32_t size;
+};
+
+static void
+metarray_spec_free(struct metarray_spec *s)
+{
+       if (!s)
+               return;
+
+       free(s->name);
+       s->name = NULL;
+}
+
+static int
+metarray_statement_parse(struct metarray_spec *s,
+                        char **tokens,
+                        uint32_t n_tokens,
+                        uint32_t n_lines,
+                        uint32_t *err_line,
+                        const char **err_msg)
+{
+       char *p;
+
+       /* Check format. */
+       if ((n_tokens != 4) || strcmp(tokens[2], "size")) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid metarray statement.";
+               return -EINVAL;
+       }
+
+       /* spec. */
+       s->name = strdup(tokens[1]);
+       if (!s->name) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
+       }
+
+       p = tokens[3];
+       s->size = strtoul(p, &p, 0);
+       if (p[0] || !s->size) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid size argument.";
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * apply.
+ *
+ * apply {
+ *     INSTRUCTION
+ *     ...
+ * }
+ */
+struct apply_spec {
        const char **instructions;
        uint32_t n_instructions;
 };
@@ -1203,6 +2218,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
        struct metadata_spec metadata_spec = {0};
        struct action_spec action_spec = {0};
        struct table_spec table_spec = {0};
+       struct selector_spec selector_spec = {0};
+       struct learner_spec learner_spec = {0};
        struct regarray_spec regarray_spec = {0};
        struct metarray_spec metarray_spec = {0};
        struct apply_spec apply_spec = {0};
@@ -1215,7 +2232,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
                if (err_line)
                        *err_line = 0;
                if (err_msg)
-                       *err_msg = "Null pipeline arument.";
+                       *err_msg = "Null pipeline argument.";
                status = -EINVAL;
                goto error;
        }
@@ -1255,7 +2272,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
                        }
 
                        /* Handle excessively long lines. */
-                       if (n_tokens >= MAX_TOKENS) {
+                       if (n_tokens >= RTE_DIM(tokens)) {
                                if (err_line)
                                        *err_line = n_lines;
                                if (err_msg)
@@ -1303,7 +2320,8 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
                        status = rte_swx_pipeline_struct_type_register(p,
                                struct_spec.name,
                                struct_spec.fields,
-                               struct_spec.n_fields);
+                               struct_spec.n_fields,
+                               struct_spec.varbit);
                        if (status) {
                                if (err_line)
                                        *err_line = n_lines;
@@ -1386,6 +2404,73 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
                        continue;
                }
 
+               /* selector block. */
+               if (block_mask & (1 << SELECTOR_BLOCK)) {
+                       status = selector_block_parse(&selector_spec,
+                                                     &block_mask,
+                                                     tokens,
+                                                     n_tokens,
+                                                     n_lines,
+                                                     err_line,
+                                                     err_msg);
+                       if (status)
+                               goto error;
+
+                       if (block_mask & (1 << SELECTOR_BLOCK))
+                               continue;
+
+                       /* End of block. */
+                       status = rte_swx_pipeline_selector_config(p,
+                               selector_spec.name,
+                               &selector_spec.params);
+                       if (status) {
+                               if (err_line)
+                                       *err_line = n_lines;
+                               if (err_msg)
+                                       *err_msg = "Selector configuration error.";
+                               goto error;
+                       }
+
+                       selector_spec_free(&selector_spec);
+
+                       continue;
+               }
+
+               /* learner block. */
+               if (block_mask & (1 << LEARNER_BLOCK)) {
+                       status = learner_block_parse(&learner_spec,
+                                                    &block_mask,
+                                                    tokens,
+                                                    n_tokens,
+                                                    n_lines,
+                                                    err_line,
+                                                    err_msg);
+                       if (status)
+                               goto error;
+
+                       if (block_mask & (1 << LEARNER_BLOCK))
+                               continue;
+
+                       /* End of block. */
+                       status = rte_swx_pipeline_learner_config(p,
+                               learner_spec.name,
+                               &learner_spec.params,
+                               learner_spec.size,
+                               learner_spec.timeout,
+                               learner_spec.n_timeouts);
+                       if (status) {
+                               if (err_line)
+                                       *err_line = n_lines;
+                               if (err_msg)
+                                       *err_msg = "Learner table configuration error.";
+                               goto error;
+                       }
+
+                       learner_spec_free(&learner_spec);
+
+                       continue;
+               }
+
                /* apply block. */
                if (block_mask & (1 << APPLY_BLOCK)) {
                        status = apply_block_parse(&apply_spec,
@@ -1544,6 +2629,36 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
                        continue;
                }
 
+               /* selector. */
+               if (!strcmp(tokens[0], "selector")) {
+                       status = selector_statement_parse(&selector_spec,
+                                                         &block_mask,
+                                                         tokens,
+                                                         n_tokens,
+                                                         n_lines,
+                                                         err_line,
+                                                         err_msg);
+                       if (status)
+                               goto error;
+
+                       continue;
+               }
+
+               /* learner. */
+               if (!strcmp(tokens[0], "learner")) {
+                       status = learner_statement_parse(&learner_spec,
+                                                        &block_mask,
+                                                        tokens,
+                                                        n_tokens,
+                                                        n_lines,
+                                                        err_line,
+                                                        err_msg);
+                       if (status)
+                               goto error;
+
+                       continue;
+               }
+
                /* regarray. */
                if (!strcmp(tokens[0], "regarray")) {
                        status = regarray_statement_parse(&regarray_spec,
@@ -1651,6 +2766,8 @@ error:
        metadata_spec_free(&metadata_spec);
        action_spec_free(&action_spec);
        table_spec_free(&table_spec);
+       selector_spec_free(&selector_spec);
+       learner_spec_free(&learner_spec);
        regarray_spec_free(&regarray_spec);
        metarray_spec_free(&metarray_spec);
        apply_spec_free(&apply_spec);