diff options
author | David Lamparter <equinox@opensourcerouting.org> | 2017-03-22 06:56:17 +0100 |
---|---|---|
committer | Quentin Young <qlyoung@users.noreply.github.com> | 2017-05-15 16:27:43 +0200 |
commit | c09c46ae3c2702b3553e558f723e6de4fea3e05d (patch) | |
tree | 0f880205691729b8157a38c5df638df3eebe714e /lib/command_graph.c | |
parent | lib: parser: split off & rename graph handling (diff) | |
download | frr-c09c46ae3c2702b3553e558f723e6de4fea3e05d.tar.xz frr-c09c46ae3c2702b3553e558f723e6de4fea3e05d.zip |
lib: parser: add pre-merge varname propagation step
Fills token->varname based on context. WORD tokens use the WORD - if it
isn't actually "WORD". Other than that, a preceding constant token is
used as name.
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'lib/command_graph.c')
-rw-r--r-- | lib/command_graph.c | 131 |
1 files changed, 130 insertions, 1 deletions
diff --git a/lib/command_graph.c b/lib/command_graph.c index 7e611e362..62d0610e1 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -81,7 +81,28 @@ cmd_token_dup (struct cmd_token *token) void cmd_token_varname_set(struct cmd_token *token, const char *varname) { XFREE (MTYPE_CMD_VAR, token->varname); - token->varname = varname ? XSTRDUP (MTYPE_CMD_VAR, varname) : NULL; + if (!varname) + { + token->varname = NULL; + return; + } + + size_t len = strlen (varname), i; + token->varname = XMALLOC (MTYPE_CMD_VAR, len + 1); + + for (i = 0; i < len; i++) + switch (varname[i]) + { + case '-': + case '+': + case '*': + case ':': + token->varname[i] = '_'; + break; + default: + token->varname[i] = tolower (varname[i]); + } + token->varname[len] = '\0'; } static bool @@ -162,6 +183,10 @@ cmd_nodes_equal (struct graph_node *ga, struct graph_node *gb) /* one a ..., the other not. */ if (cmd_nodes_link (ga, ga) != cmd_nodes_link (gb, gb)) return false; + if (!a->varname != !b->varname) + return false; + if (a->varname && strcmp (a->varname, b->varname)) + return false; switch (a->type) { @@ -349,3 +374,107 @@ cmd_graph_merge (struct graph *old, struct graph *new, int direction) vector_slot (old->nodes, 0), vector_slot (new->nodes, 0), direction); } + +static void +cmd_node_names (struct graph_node *gn, struct graph_node *join, + const char *prevname) +{ + size_t i; + struct cmd_token *tok = gn->data, *jointok; + struct graph_node *stop = cmd_loopstop (gn); + + switch (tok->type) + { + case WORD_TKN: + prevname = tok->text; + break; + + case VARIABLE_TKN: + if (!tok->varname + && strcmp (tok->text, "WORD") + && strcmp (tok->text, "NAME")) + cmd_token_varname_set (tok, tok->text); + /* fallthrough */ + case RANGE_TKN: + case IPV4_TKN: + case IPV4_PREFIX_TKN: + case IPV6_TKN: + case IPV6_PREFIX_TKN: + if (!tok->varname && prevname) + cmd_token_varname_set (tok, prevname); + prevname = NULL; + break; + + case START_TKN: + case END_TKN: + case JOIN_TKN: + /* "<foo|bar> WORD" -> word is not "bar" or "foo" */ + prevname = NULL; + break; + + case FORK_TKN: + /* apply "<A.B.C.D|X:X::X:X>$name" */ + jointok = tok->forkjoin->data; + if (!jointok->varname) + break; + for (i = 0; i < vector_active (tok->forkjoin->from); i++) + { + struct graph_node *tail = vector_slot (tok->forkjoin->from, i); + struct cmd_token *tailtok = tail->data; + if (tail == gn || tailtok->varname) + continue; + cmd_token_varname_set (tailtok, jointok->varname); + } + break; + } + + for (i = 0; i < vector_active (gn->to); i++) + { + struct graph_node *next = vector_slot (gn->to, i); + if (next == stop || next == join) + continue; + cmd_node_names (next, join, prevname); + } + + if (tok->type == FORK_TKN && tok->forkjoin != join) + cmd_node_names (tok->forkjoin, join, NULL); +} + +void +cmd_graph_names (struct graph *graph) +{ + struct graph_node *start; + + assert (vector_active (graph->nodes) >= 1); + start = vector_slot (graph->nodes, 0); + + /* apply varname on initial "[no]" */ + do + { + if (vector_active (start->to) != 1) + break; + + struct graph_node *first = vector_slot (start->to, 0); + struct cmd_token *tok = first->data; + /* looking for an option with 2 choices, nothing or "no" */ + if (tok->type != FORK_TKN || vector_active (first->to) != 2) + break; + + struct graph_node *next0 = vector_slot (first->to, 0); + struct graph_node *next1 = vector_slot (first->to, 1); + /* one needs to be empty */ + if (next0 != tok->forkjoin && next1 != tok->forkjoin) + break; + + struct cmd_token *tok0 = next0->data; + struct cmd_token *tok1 = next1->data; + /* the other one needs to be "no" (only one will match here) */ + if ((tok0->type == WORD_TKN && !strcmp(tok0->text, "no"))) + cmd_token_varname_set (tok0, "no"); + if ((tok1->type == WORD_TKN && !strcmp(tok1->text, "no"))) + cmd_token_varname_set (tok1, "no"); + } + while (0); + + cmd_node_names (start, NULL, NULL); +} |