#include "dynamic-string.h"
#include "fatal-signal.h"
#include "match.h"
+#include "ofp-actions.h"
+#include "ofpbuf.h"
+#include "ovn/lib/actions.h"
#include "ovn/lib/expr.h"
#include "ovn/lib/lex.h"
#include "ovs-thread.h"
#include "ovstest.h"
#include "shash.h"
+#include "simap.h"
#include "util.h"
#include "openvswitch/vlog.h"
/* --relops: Bitmap of the relational operators to test, in exhaustive test. */
static unsigned int test_relops;
-/* --vars: Number of variables to test, in exhaustive test. */
-static int test_vars = 2;
+/* --nvars: Number of numeric variables to test, in exhaustive test. */
+static int test_nvars = 2;
+
+/* --svars: Number of string variables to test, in exhaustive test. */
+static int test_svars = 2;
/* --bits: Number of bits per variable, in exhaustive test. */
static int test_bits = 3;
ds_init(&input);
ds_init(&output);
- while (!ds_get_line(&input, stdin)) {
+ while (!ds_get_test_line(&input, stdin)) {
struct lexer lexer;
lexer_init(&lexer, ds_cstr(&input));
{
shash_init(symtab);
- expr_symtab_add_string(symtab, "inport", NULL);
- expr_symtab_add_string(symtab, "outport", NULL);
+ /* Reserve a pair of registers for the logical inport and outport. A full
+ * 32-bit register each is bigger than we need, but the expression code
+ * doesn't yet support string fields that occupy less than a full OXM. */
+ expr_symtab_add_string(symtab, "inport", MFF_REG6, NULL);
+ expr_symtab_add_string(symtab, "outport", MFF_REG7, NULL);
expr_symtab_add_field(symtab, "xreg0", MFF_XREG0, NULL, false);
expr_symtab_add_field(symtab, "xreg1", MFF_XREG1, NULL, false);
expr_symtab_add_field(symtab, "xreg2", MFF_XREG2, NULL, false);
- expr_symtab_add_field(symtab, "xreg3", MFF_XREG3, NULL, false);
expr_symtab_add_subfield(symtab, "reg0", NULL, "xreg0[32..63]");
expr_symtab_add_subfield(symtab, "reg1", NULL, "xreg0[0..31]");
expr_symtab_add_subfield(symtab, "reg3", NULL, "xreg1[0..31]");
expr_symtab_add_subfield(symtab, "reg4", NULL, "xreg2[32..63]");
expr_symtab_add_subfield(symtab, "reg5", NULL, "xreg2[0..31]");
- expr_symtab_add_subfield(symtab, "reg6", NULL, "xreg3[32..63]");
- expr_symtab_add_subfield(symtab, "reg7", NULL, "xreg3[0..31]");
expr_symtab_add_field(symtab, "eth.src", MFF_ETH_SRC, NULL, false);
expr_symtab_add_field(symtab, "eth.dst", MFF_ETH_DST, NULL, false);
"mutual_recurse_2 != 0", false);
expr_symtab_add_field(symtab, "mutual_recurse_2", MFF_XREG0,
"mutual_recurse_1 != 0", false);
+ expr_symtab_add_string(symtab, "big_string", MFF_XREG0, NULL);
}
static void
test_parse_expr__(int steps)
{
struct shash symtab;
+ struct simap ports;
struct ds input;
create_symtab(&symtab);
+
+ simap_init(&ports);
+ simap_put(&ports, "eth0", 5);
+ simap_put(&ports, "eth1", 6);
+ simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
+
ds_init(&input);
while (!ds_get_test_line(&input, stdin)) {
struct expr *expr;
}
}
if (!error) {
- struct ds output = DS_EMPTY_INITIALIZER;
- expr_format(expr, &output);
- puts(ds_cstr(&output));
- ds_destroy(&output);
+ if (steps > 3) {
+ struct hmap matches;
+
+ expr_to_matches(expr, &ports, &matches);
+ expr_matches_print(&matches, stdout);
+ expr_matches_destroy(&matches);
+ } else {
+ struct ds output = DS_EMPTY_INITIALIZER;
+ expr_format(expr, &output);
+ puts(ds_cstr(&output));
+ ds_destroy(&output);
+ }
} else {
puts(error);
free(error);
}
ds_destroy(&input);
+ simap_destroy(&ports);
expr_symtab_destroy(&symtab);
shash_destroy(&symtab);
}
{
test_parse_expr__(3);
}
+
+static void
+test_expr_to_flows(struct ovs_cmdl_context *ctx OVS_UNUSED)
+{
+ test_parse_expr__(4);
+}
\f
/* Evaluate an expression. */
static bool
evaluate_cmp_expr(const struct expr *expr, unsigned int subst, int n_bits)
{
- int var_idx = expr->cmp.symbol->name[0] - 'a';
- unsigned var_mask = (1u << n_bits) - 1;
- unsigned int arg1 = (subst >> (var_idx * n_bits)) & var_mask;
- unsigned int arg2 = ntohll(expr->cmp.value.integer);
- unsigned int mask = ntohll(expr->cmp.mask.integer);
-
- ovs_assert(!(mask & ~var_mask));
- ovs_assert(!(arg2 & ~var_mask));
- ovs_assert(!(arg2 & ~mask));
-
- arg1 &= mask;
- switch (expr->cmp.relop) {
- case EXPR_R_EQ:
- return arg1 == arg2;
+ int var_idx = atoi(expr->cmp.symbol->name + 1);
+ if (expr->cmp.symbol->name[0] == 'n') {
+ unsigned var_mask = (1u << n_bits) - 1;
+ unsigned int arg1 = (subst >> (var_idx * n_bits)) & var_mask;
+ unsigned int arg2 = ntohll(expr->cmp.value.integer);
+ unsigned int mask = ntohll(expr->cmp.mask.integer);
- case EXPR_R_NE:
- return arg1 != arg2;
+ ovs_assert(!(mask & ~var_mask));
+ ovs_assert(!(arg2 & ~var_mask));
+ ovs_assert(!(arg2 & ~mask));
- case EXPR_R_LT:
- return arg1 < arg2;
+ arg1 &= mask;
+ switch (expr->cmp.relop) {
+ case EXPR_R_EQ:
+ return arg1 == arg2;
- case EXPR_R_LE:
- return arg1 <= arg2;
+ case EXPR_R_NE:
+ return arg1 != arg2;
- case EXPR_R_GT:
- return arg1 > arg2;
+ case EXPR_R_LT:
+ return arg1 < arg2;
- case EXPR_R_GE:
- return arg1 >= arg2;
+ case EXPR_R_LE:
+ return arg1 <= arg2;
- default:
+ case EXPR_R_GT:
+ return arg1 > arg2;
+
+ case EXPR_R_GE:
+ return arg1 >= arg2;
+
+ default:
+ OVS_NOT_REACHED();
+ }
+ } else if (expr->cmp.symbol->name[0] == 's') {
+ unsigned int arg1 = (subst >> (test_nvars * n_bits + var_idx)) & 1;
+ unsigned int arg2 = atoi(expr->cmp.string);
+ return arg1 == arg2;
+ } else {
OVS_NOT_REACHED();
}
}
/* Sets 'expr' to the first possible terminal expression. 'var' should be the
* first variable in the ones to be tested. */
static void
-init_terminal(struct expr *expr, const struct expr_symbol *var)
+init_terminal(struct expr *expr, int phase,
+ const struct expr_symbol *nvars[], int n_nvars,
+ const struct expr_symbol *svars[], int n_svars)
{
- expr->type = EXPR_T_CMP;
- expr->cmp.symbol = var;
- expr->cmp.relop = rightmost_1bit_idx(test_relops);
- memset(&expr->cmp.value, 0, sizeof expr->cmp.value);
- memset(&expr->cmp.mask, 0, sizeof expr->cmp.mask);
- expr->cmp.value.integer = htonll(0);
- expr->cmp.mask.integer = htonll(1);
+ if (phase < 1 && n_nvars) {
+ expr->type = EXPR_T_CMP;
+ expr->cmp.symbol = nvars[0];
+ expr->cmp.relop = rightmost_1bit_idx(test_relops);
+ memset(&expr->cmp.value, 0, sizeof expr->cmp.value);
+ memset(&expr->cmp.mask, 0, sizeof expr->cmp.mask);
+ expr->cmp.value.integer = htonll(0);
+ expr->cmp.mask.integer = htonll(1);
+ return;
+ }
+
+ if (phase < 2 && n_svars) {
+ expr->type = EXPR_T_CMP;
+ expr->cmp.symbol = svars[0];
+ expr->cmp.relop = EXPR_R_EQ;
+ expr->cmp.string = xstrdup("0");
+ return;
+ }
+
+ expr->type = EXPR_T_BOOLEAN;
+ expr->boolean = false;
}
/* Returns 'x' with the rightmost contiguous string of 1s changed to 0s,
/* Advances 'expr' to the next possible terminal expression within the 'n_vars'
* variables of 'n_bits' bits each in 'vars[]'. */
static bool
-next_terminal(struct expr *expr, const struct expr_symbol *vars[], int n_vars,
- int n_bits)
+next_terminal(struct expr *expr,
+ const struct expr_symbol *nvars[], int n_nvars, int n_bits,
+ const struct expr_symbol *svars[], int n_svars)
{
if (expr->type == EXPR_T_BOOLEAN) {
if (expr->boolean) {
}
}
+ if (!expr->cmp.symbol->width) {
+ int next_value = atoi(expr->cmp.string) + 1;
+ free(expr->cmp.string);
+ if (next_value > 1) {
+ expr->cmp.symbol = next_var(expr->cmp.symbol, svars, n_svars);
+ if (!expr->cmp.symbol) {
+ init_terminal(expr, 2, nvars, n_nvars, svars, n_svars);
+ return true;
+ }
+ next_value = 0;
+ }
+ expr->cmp.string = xasprintf("%d", next_value);
+ return true;
+ }
+
unsigned int next;
next = (ntohll(expr->cmp.value.integer)
enum expr_relop old_relop = expr->cmp.relop;
expr->cmp.relop = next_relop(old_relop);
if (expr->cmp.relop <= old_relop) {
- expr->cmp.symbol = next_var(expr->cmp.symbol,vars, n_vars);
+ expr->cmp.symbol = next_var(expr->cmp.symbol, nvars, n_nvars);
if (!expr->cmp.symbol) {
- expr->type = EXPR_T_BOOLEAN;
- expr->boolean = false;
+ init_terminal(expr, 1, nvars, n_nvars, svars, n_svars);
return true;
}
}
static int
test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
struct expr *terminals[], int n_terminals,
- const struct expr_symbol *vars[], int n_vars,
- int n_bits)
+ const struct expr_symbol *nvars[], int n_nvars,
+ int n_bits,
+ const struct expr_symbol *svars[], int n_svars)
{
+ struct simap string_map = SIMAP_INITIALIZER(&string_map);
+ simap_put(&string_map, "0", 0);
+ simap_put(&string_map, "1", 1);
+
int n_tested = 0;
const unsigned int var_mask = (1u << n_bits) - 1;
for (int i = 0; i < n_terminals; i++) {
- init_terminal(terminals[i], vars[0]);
+ init_terminal(terminals[i], 0, nvars, n_nvars, svars, n_svars);
}
struct ds s = DS_EMPTY_INITIALIZER;
for (int i = n_terminals - 1; ; i--) {
if (!i) {
ds_destroy(&s);
+ simap_destroy(&string_map);
return n_tested;
}
- if (next_terminal(terminals[i], vars, n_vars, n_bits)) {
+ if (next_terminal(terminals[i], nvars, n_nvars, n_bits,
+ svars, n_svars)) {
break;
}
- init_terminal(terminals[i], vars[0]);
+ init_terminal(terminals[i], 0, nvars, n_nvars, svars, n_svars);
}
ovs_assert(expr_honors_invariants(expr));
if (operation >= OP_FLOW) {
struct expr_match *m;
struct test_rule *test_rule;
- uint32_t n_conjs;
- n_conjs = expr_to_matches(modified, &matches);
+ expr_to_matches(modified, &string_map, &matches);
classifier_init(&cls, NULL);
HMAP_FOR_EACH (m, hmap_node, &matches) {
test_rule = xmalloc(sizeof *test_rule);
cls_rule_init(&test_rule->cr, &m->match, 0);
- classifier_insert(&cls, &test_rule->cr, m->conjunctions, m->n);
- }
- for (uint32_t conj_id = 1; conj_id <= n_conjs; conj_id++) {
- struct match match;
- match_init_catchall(&match);
- match_set_conj_id(&match, conj_id);
-
- test_rule = xmalloc(sizeof *test_rule);
- cls_rule_init(&test_rule->cr, &match, 0);
- classifier_insert(&cls, &test_rule->cr, NULL, 0);
+ classifier_insert(&cls, &test_rule->cr, CLS_MIN_VERSION,
+ m->conjunctions, m->n);
}
}
- for (int subst = 0; subst < 1 << (n_bits * n_vars); subst++) {
+ for (int subst = 0; subst < 1 << (n_bits * n_nvars + n_svars);
+ subst++) {
bool expected = evaluate_expr(expr, subst, n_bits);
bool actual = evaluate_expr(modified, subst, n_bits);
if (actual != expected) {
"%s evaluates to %d, but %s evaluates to %d, for",
ds_cstr(&expr_s), expected,
ds_cstr(&modified_s), actual);
- for (int i = 0; i < n_vars; i++) {
+ for (int i = 0; i < n_nvars; i++) {
if (i > 0) {
fputs(",", stderr);
}
- fprintf(stderr, " %c = 0x%x", 'a' + i,
+ fprintf(stderr, " n%d = 0x%x", i,
(subst >> (n_bits * i)) & var_mask);
}
+ for (int i = 0; i < n_svars; i++) {
+ fprintf(stderr, ", s%d = \"%d\"", i,
+ (subst >> (n_bits * n_nvars + i)) & 1);
+ }
putc('\n', stderr);
exit(EXIT_FAILURE);
}
if (operation >= OP_FLOW) {
- for (int i = 0; i < n_vars; i++) {
+ for (int i = 0; i < n_nvars; i++) {
f.regs[i] = (subst >> (i * n_bits)) & var_mask;
}
- bool found = classifier_lookup(&cls, &f, NULL) != NULL;
+ for (int i = 0; i < n_svars; i++) {
+ f.regs[n_nvars + i] = ((subst >> (n_nvars * n_bits + i))
+ & 1);
+ }
+ bool found = classifier_lookup(&cls, CLS_MIN_VERSION,
+ &f, NULL) != NULL;
if (expected != found) {
struct ds expr_s, modified_s;
fprintf(stderr,
"%s and %s evaluate to %d, for",
ds_cstr(&expr_s), ds_cstr(&modified_s), expected);
- for (int i = 0; i < n_vars; i++) {
+ for (int i = 0; i < n_nvars; i++) {
if (i > 0) {
fputs(",", stderr);
}
- fprintf(stderr, " %c = 0x%x", 'a' + i,
+ fprintf(stderr, " n%d = 0x%x", i,
(subst >> (n_bits * i)) & var_mask);
}
+ for (int i = 0; i < n_svars; i++) {
+ fprintf(stderr, ", s%d = \"%d\"", i,
+ (subst >> (n_bits * n_nvars + i)) & 1);
+ }
fputs(".\n", stderr);
fprintf(stderr, "Converted to classifier:\n");
-
- struct expr_match *m;
- HMAP_FOR_EACH (m, hmap_node, &matches) {
- char *s = match_to_string(&m->match,
- OFP_DEFAULT_PRIORITY);
- fputs(s, stderr);
- if (m->n) {
- for (int i = 0; i < m->n; i++) {
- const struct cls_conjunction *c
- = &m->conjunctions[i];
- fprintf(stderr,
- "%c conjunction(%"PRIu32", %d/%d)",
- i == 0 ? ':' : ',',
- c->id, c->clause, c->n_clauses);
- }
- }
- putc('\n', stderr);
- }
-
+ expr_matches_print(&matches, stderr);
fprintf(stderr,
"However, %s flow was found in the classifier.\n",
found ? "a" : "no");
}
}
if (operation >= OP_FLOW) {
- struct expr_match *m, *n;
struct test_rule *test_rule;
CLS_FOR_EACH (test_rule, cr, &cls) {
classifier_destroy(&cls);
ovsrcu_quiesce();
- HMAP_FOR_EACH_SAFE (m, n, hmap_node, &matches) {
- hmap_remove(&matches, &m->hmap_node);
- free(m->conjunctions);
- free(m);
- }
- hmap_destroy(&matches);
+ expr_matches_destroy(&matches);
}
expr_destroy(modified);
}
int n_tses;
struct shash symtab;
- const struct expr_symbol *vars[4];
+ const struct expr_symbol *nvars[4];
+ const struct expr_symbol *svars[4];
- ovs_assert(test_vars <= ARRAY_SIZE(vars));
+ ovs_assert(test_nvars <= ARRAY_SIZE(nvars));
+ ovs_assert(test_svars <= ARRAY_SIZE(svars));
+ ovs_assert(test_nvars + test_svars <= FLOW_N_REGS);
shash_init(&symtab);
- for (int i = 0; i < test_vars; i++) {
- char name[2] = { 'a' + i, '\0' };
-
- vars[i] = expr_symtab_add_field(&symtab, name, MFF_REG0 + i, NULL,
- false);
+ for (int i = 0; i < test_nvars; i++) {
+ char *name = xasprintf("n%d", i);
+ nvars[i] = expr_symtab_add_field(&symtab, name, MFF_REG0 + i, NULL,
+ false);
+ free(name);
+ }
+ for (int i = 0; i < test_svars; i++) {
+ char *name = xasprintf("s%d", i);
+ svars[i] = expr_symtab_add_string(&symtab, name,
+ MFF_REG0 + test_nvars + i, NULL);
+ free(name);
}
#ifndef _WIN32
if (!pid) {
test_tree_shape_exhaustively(expr, &symtab,
terminals, n_terminals,
- vars, test_vars, test_bits);
+ nvars, test_nvars, test_bits,
+ svars, test_svars);
expr_destroy(expr);
exit(0);
} else {
{
n_tested += test_tree_shape_exhaustively(
expr, &symtab, terminals, n_terminals,
- vars, test_vars, test_bits);
+ nvars, test_nvars, test_bits,
+ svars, test_svars);
}
expr_destroy(expr);
}
} else {
printf(" all %d-terminal expressions", n_terminals);
}
- printf(" with %d vars each of %d bits in terms of operators",
- test_vars, test_bits);
- for (unsigned int relops = test_relops; relops;
- relops = zero_rightmost_1bit(relops)) {
- enum expr_relop r = rightmost_1bit_idx(relops);
- printf(" %s", expr_relop_to_string(r));
+ if (test_nvars || test_svars) {
+ printf(" with");
+ if (test_nvars) {
+ printf(" %d numeric vars (each %d bits) in terms of operators",
+ test_nvars, test_bits);
+ for (unsigned int relops = test_relops; relops;
+ relops = zero_rightmost_1bit(relops)) {
+ enum expr_relop r = rightmost_1bit_idx(relops);
+ printf(" %s", expr_relop_to_string(r));
+ }
+ }
+ if (test_nvars && test_svars) {
+ printf (" and");
+ }
+ if (test_svars) {
+ printf(" %d string vars", test_svars);
+ }
+ } else {
+ printf(" in terms of Boolean constants only");
}
printf(".\n");
shash_destroy(&symtab);
}
\f
+/* Actions. */
+
+static void
+test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
+{
+ struct shash symtab;
+ struct simap ports, ct_zones;
+ struct ds input;
+
+ create_symtab(&symtab);
+
+ simap_init(&ports);
+ simap_put(&ports, "eth0", 5);
+ simap_put(&ports, "eth1", 6);
+ simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
+ simap_init(&ct_zones);
+
+ ds_init(&input);
+ while (!ds_get_test_line(&input, stdin)) {
+ struct ofpbuf ofpacts;
+ struct expr *prereqs;
+ char *error;
+
+ ofpbuf_init(&ofpacts, 0);
+
+ struct action_params ap = {
+ .symtab = &symtab,
+ .ports = &ports,
+ .ct_zones = &ct_zones,
+
+ .n_tables = 16,
+ .first_ptable = 16,
+ .cur_ltable = 10,
+ .output_ptable = 64,
+ };
+ error = actions_parse_string(ds_cstr(&input), &ap, &ofpacts, &prereqs);
+ if (!error) {
+ struct ds output;
+
+ ds_init(&output);
+ ds_put_cstr(&output, "actions=");
+ ofpacts_format(ofpacts.data, ofpacts.size, &output);
+ ds_put_cstr(&output, ", prereqs=");
+ if (prereqs) {
+ expr_format(prereqs, &output);
+ } else {
+ ds_put_char(&output, '1');
+ }
+ puts(ds_cstr(&output));
+ ds_destroy(&output);
+ } else {
+ puts(error);
+ free(error);
+ }
+
+ expr_destroy(prereqs);
+ ofpbuf_uninit(&ofpacts);
+ }
+ ds_destroy(&input);
+
+ simap_destroy(&ports);
+ simap_destroy(&ct_zones);
+ expr_symtab_destroy(&symtab);
+ shash_destroy(&symtab);
+}
+\f
static unsigned int
parse_relops(const char *s)
{
annotate-expr\n\
simplify-expr\n\
normalize-expr\n\
+expr-to-flows\n\
Parses OVN expressions from stdin and print them back on stdout after\n\
differing degrees of analysis. Available fields are based on packet\n\
headers.\n\
exhaustive N\n\
Tests that all possible Boolean expressions with N terminals are properly\n\
simplified, normalized, and converted to flows. Available options:\n\
- --relops=OPERATORS Test only the specified Boolean operators.\n\
- OPERATORS may include == != < <= > >=, space or\n\
- comma separated. Default is all operators.\n\
- --vars=N Number of variables to test, in range 1...4, default 2.\n\
- --bits=N Number of bits per variable, in range 1...3, default 3.\n\
+ Overall options:\n\
--operation=OPERATION Operation to test, one of: convert, simplify,\n\
normalize, flow. Default: flow. 'normalize' includes 'simplify',\n\
- 'flow' includes 'simplify' and 'normaize'.\n\
+ 'flow' includes 'simplify' and 'normalize'.\n\
--parallel=N Number of processes to use in parallel, default 1.\n\
+ Numeric vars:\n\
+ --nvars=N Number of numeric vars to test, in range 0...4, default 2.\n\
+ --bits=N Number of bits per variable, in range 1...3, default 3.\n\
+ --relops=OPERATORS Test only the specified Boolean operators.\n\
+ OPERATORS may include == != < <= > >=, space or\n\
+ comma separated. Default is all operators.\n\
+ String vars:\n\
+ --svars=N Number of string vars to test, in range 0...4, default 2.\n\
",
program_name, program_name);
exit(EXIT_SUCCESS);
static void
test_ovn_main(int argc, char *argv[])
{
+ enum {
+ OPT_RELOPS = UCHAR_MAX + 1,
+ OPT_NVARS,
+ OPT_SVARS,
+ OPT_BITS,
+ OPT_OPERATION,
+ OPT_PARALLEL
+ };
+ static const struct option long_options[] = {
+ {"relops", required_argument, NULL, OPT_RELOPS},
+ {"nvars", required_argument, NULL, OPT_NVARS},
+ {"svars", required_argument, NULL, OPT_SVARS},
+ {"bits", required_argument, NULL, OPT_BITS},
+ {"operation", required_argument, NULL, OPT_OPERATION},
+ {"parallel", required_argument, NULL, OPT_PARALLEL},
+ {"more", no_argument, NULL, 'm'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0},
+ };
+ char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
+
set_program_name(argv[0]);
test_relops = parse_relops("== != < <= > >=");
for (;;) {
- enum {
- OPT_RELOPS = UCHAR_MAX + 1,
- OPT_VARS,
- OPT_BITS,
- OPT_OPERATION,
- OPT_PARALLEL
- };
-
- static const struct option options[] = {
- {"relops", required_argument, NULL, OPT_RELOPS},
- {"vars", required_argument, NULL, OPT_VARS},
- {"bits", required_argument, NULL, OPT_BITS},
- {"operation", required_argument, NULL, OPT_OPERATION},
- {"parallel", required_argument, NULL, OPT_PARALLEL},
- {"more", no_argument, NULL, 'm'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0},
- };
int option_index = 0;
- int c = getopt_long (argc, argv, "", options, &option_index);
+ int c = getopt_long (argc, argv, short_options, long_options,
+ &option_index);
if (c == -1) {
break;
test_relops = parse_relops(optarg);
break;
- case OPT_VARS:
- test_vars = atoi(optarg);
- if (test_vars < 1 || test_vars > 4) {
- ovs_fatal(0, "number of variables must be between 1 and 4");
+ case OPT_NVARS:
+ test_nvars = atoi(optarg);
+ if (test_nvars < 0 || test_nvars > 4) {
+ ovs_fatal(0, "number of numeric variables must be "
+ "between 0 and 4");
+ }
+ break;
+
+ case OPT_SVARS:
+ test_svars = atoi(optarg);
+ if (test_svars < 0 || test_svars > 4) {
+ ovs_fatal(0, "number of string variables must be "
+ "between 0 and 4");
}
break;
abort();
}
}
+ free(short_options);
static const struct ovs_cmdl_command commands[] = {
+ /* Lexer. */
{"lex", NULL, 0, 0, test_lex},
+
+ /* Expressions. */
{"parse-expr", NULL, 0, 0, test_parse_expr},
{"annotate-expr", NULL, 0, 0, test_annotate_expr},
{"simplify-expr", NULL, 0, 0, test_simplify_expr},
{"normalize-expr", NULL, 0, 0, test_normalize_expr},
+ {"expr-to-flows", NULL, 0, 0, test_expr_to_flows},
{"evaluate-expr", NULL, 1, 1, test_evaluate_expr},
{"composition", NULL, 1, 1, test_composition},
{"tree-shape", NULL, 1, 1, test_tree_shape},
{"exhaustive", NULL, 1, 1, test_exhaustive},
+
+ /* Actions. */
+ {"parse-actions", NULL, 0, 0, test_parse_actions},
+
{NULL, NULL, 0, 0, NULL},
};
struct ovs_cmdl_context ctx;