mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-09 20:22:36 +00:00
lib: parser: support keyword arguments
This re-adds "{foo WORD|bar WORD}" keyword-argument support to the CLI parser. Note that token graphs may now contain loops for this purpose; therefore the matching functions retain a history of already-matched tokens. Each token can thus only be consumed once. And then LINE... gets its special treatment with allowrepeat. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
parent
fa133e00c2
commit
7d5718c140
@ -28,6 +28,9 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
|
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
|
||||||
|
DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack")
|
||||||
|
|
||||||
|
#define MAXDEPTH 64
|
||||||
|
|
||||||
#ifdef TRACE_MATCHER
|
#ifdef TRACE_MATCHER
|
||||||
#define TM 1
|
#define TM 1
|
||||||
@ -40,10 +43,12 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
|
|||||||
|
|
||||||
/* matcher helper prototypes */
|
/* matcher helper prototypes */
|
||||||
static int
|
static int
|
||||||
add_nexthops (struct list *, struct graph_node *);
|
add_nexthops (struct list *, struct graph_node *,
|
||||||
|
struct graph_node **, size_t);
|
||||||
|
|
||||||
static struct list *
|
static struct list *
|
||||||
command_match_r (struct graph_node *, vector, unsigned int);
|
command_match_r (struct graph_node *, vector, unsigned int,
|
||||||
|
struct graph_node **);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
score_precedence (enum cmd_token_type);
|
score_precedence (enum cmd_token_type);
|
||||||
@ -97,6 +102,7 @@ command_match (struct graph *cmdgraph,
|
|||||||
struct list **argv,
|
struct list **argv,
|
||||||
const struct cmd_element **el)
|
const struct cmd_element **el)
|
||||||
{
|
{
|
||||||
|
struct graph_node *stack[MAXDEPTH];
|
||||||
matcher_rv = MATCHER_NO_MATCH;
|
matcher_rv = MATCHER_NO_MATCH;
|
||||||
|
|
||||||
// prepend a dummy token to match that pesky start node
|
// prepend a dummy token to match that pesky start node
|
||||||
@ -106,7 +112,7 @@ command_match (struct graph *cmdgraph,
|
|||||||
vvline->active = vline->active + 1;
|
vvline->active = vline->active + 1;
|
||||||
|
|
||||||
struct graph_node *start = vector_slot (cmdgraph->nodes, 0);
|
struct graph_node *start = vector_slot (cmdgraph->nodes, 0);
|
||||||
if ((*argv = command_match_r (start, vvline, 0))) // successful match
|
if ((*argv = command_match_r (start, vvline, 0, stack))) // successful match
|
||||||
{
|
{
|
||||||
struct listnode *head = listhead (*argv);
|
struct listnode *head = listhead (*argv);
|
||||||
struct listnode *tail = listtail (*argv);
|
struct listnode *tail = listtail (*argv);
|
||||||
@ -191,10 +197,21 @@ command_match (struct graph *cmdgraph,
|
|||||||
* If no match was found, the return value is NULL.
|
* If no match was found, the return value is NULL.
|
||||||
*/
|
*/
|
||||||
static struct list *
|
static struct list *
|
||||||
command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
command_match_r (struct graph_node *start, vector vline, unsigned int n,
|
||||||
|
struct graph_node **stack)
|
||||||
{
|
{
|
||||||
assert (n < vector_active (vline));
|
assert (n < vector_active (vline));
|
||||||
|
|
||||||
|
/* check history/stack of tokens
|
||||||
|
* this disallows matching the same one more than once if there is a
|
||||||
|
* circle in the graph (used for keyword arguments) */
|
||||||
|
if (n == MAXDEPTH)
|
||||||
|
return NULL;
|
||||||
|
if (!start->allowrepeat)
|
||||||
|
for (size_t s = 0; s < n; s++)
|
||||||
|
if (stack[s] == start)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
// get the minimum match level that can count as a full match
|
// get the minimum match level that can count as a full match
|
||||||
struct cmd_token *token = start->data;
|
struct cmd_token *token = start->data;
|
||||||
enum match_type minmatch = min_match_level (token->type);
|
enum match_type minmatch = min_match_level (token->type);
|
||||||
@ -229,13 +246,15 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
if (match_token (token, input_token) < minmatch)
|
if (match_token (token, input_token) < minmatch)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
stack[n] = start;
|
||||||
|
|
||||||
// pointers for iterating linklist
|
// pointers for iterating linklist
|
||||||
struct listnode *ln;
|
struct listnode *ln;
|
||||||
struct graph_node *gn;
|
struct graph_node *gn;
|
||||||
|
|
||||||
// get all possible nexthops
|
// get all possible nexthops
|
||||||
struct list *next = list_new();
|
struct list *next = list_new();
|
||||||
add_nexthops (next, start);
|
add_nexthops (next, start, NULL, 0);
|
||||||
|
|
||||||
// determine the best match
|
// determine the best match
|
||||||
int ambiguous = 0;
|
int ambiguous = 0;
|
||||||
@ -271,7 +290,7 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// else recurse on candidate child node
|
// else recurse on candidate child node
|
||||||
struct list *result = command_match_r (gn, vline, n+1);
|
struct list *result = command_match_r (gn, vline, n+1, stack);
|
||||||
|
|
||||||
// save the best match
|
// save the best match
|
||||||
if (result && currbest)
|
if (result && currbest)
|
||||||
@ -317,6 +336,12 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
return currbest;
|
return currbest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stack_del (void *val)
|
||||||
|
{
|
||||||
|
XFREE (MTYPE_CMD_MATCHSTACK, val);
|
||||||
|
}
|
||||||
|
|
||||||
enum matcher_rv
|
enum matcher_rv
|
||||||
command_complete (struct graph *graph,
|
command_complete (struct graph *graph,
|
||||||
vector vline,
|
vector vline,
|
||||||
@ -327,14 +352,15 @@ command_complete (struct graph *graph,
|
|||||||
|
|
||||||
struct list *current = list_new(), // current nodes to match input token against
|
struct list *current = list_new(), // current nodes to match input token against
|
||||||
*next = list_new(); // possible next hops after current input token
|
*next = list_new(); // possible next hops after current input token
|
||||||
|
current->del = next->del = stack_del;
|
||||||
|
|
||||||
// pointers used for iterating lists
|
// pointers used for iterating lists
|
||||||
struct graph_node *gn;
|
struct graph_node **gstack, **newstack;
|
||||||
struct listnode *node;
|
struct listnode *node;
|
||||||
|
|
||||||
// add all children of start node to list
|
// add all children of start node to list
|
||||||
struct graph_node *start = vector_slot (graph->nodes, 0);
|
struct graph_node *start = vector_slot (graph->nodes, 0);
|
||||||
add_nexthops (next, start);
|
add_nexthops (next, start, &start, 0);
|
||||||
|
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
for (idx = 0; idx < vector_active (vline) && next->count > 0; idx++)
|
for (idx = 0; idx < vector_active (vline) && next->count > 0; idx++)
|
||||||
@ -342,19 +368,20 @@ command_complete (struct graph *graph,
|
|||||||
list_delete (current);
|
list_delete (current);
|
||||||
current = next;
|
current = next;
|
||||||
next = list_new();
|
next = list_new();
|
||||||
|
next->del = stack_del;
|
||||||
|
|
||||||
input_token = vector_slot (vline, idx);
|
input_token = vector_slot (vline, idx);
|
||||||
|
|
||||||
int exact_match_exists = 0;
|
int exact_match_exists = 0;
|
||||||
for (ALL_LIST_ELEMENTS_RO (current,node,gn))
|
for (ALL_LIST_ELEMENTS_RO (current,node,gstack))
|
||||||
if (!exact_match_exists)
|
if (!exact_match_exists)
|
||||||
exact_match_exists = (match_token (gn->data, input_token) == exact_match);
|
exact_match_exists = (match_token (gstack[0]->data, input_token) == exact_match);
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS_RO (current,node,gn))
|
for (ALL_LIST_ELEMENTS_RO (current,node,gstack))
|
||||||
{
|
{
|
||||||
struct cmd_token *token = gn->data;
|
struct cmd_token *token = gstack[0]->data;
|
||||||
|
|
||||||
if (token->attr == CMD_ATTR_HIDDEN || token->attr == CMD_ATTR_DEPRECATED)
|
if (token->attr == CMD_ATTR_HIDDEN || token->attr == CMD_ATTR_DEPRECATED)
|
||||||
continue;
|
continue;
|
||||||
@ -371,7 +398,11 @@ command_complete (struct graph *graph,
|
|||||||
case trivial_match:
|
case trivial_match:
|
||||||
trace_matcher ("trivial_match\n");
|
trace_matcher ("trivial_match\n");
|
||||||
assert(last_token);
|
assert(last_token);
|
||||||
listnode_add (next, gn);
|
newstack = XMALLOC (MTYPE_CMD_MATCHSTACK,
|
||||||
|
sizeof(struct graph_node *));
|
||||||
|
/* we're not recursing here, just the first element is OK */
|
||||||
|
newstack[0] = gstack[0];
|
||||||
|
listnode_add (next, newstack);
|
||||||
break;
|
break;
|
||||||
case partly_match:
|
case partly_match:
|
||||||
trace_matcher ("trivial_match\n");
|
trace_matcher ("trivial_match\n");
|
||||||
@ -380,9 +411,15 @@ command_complete (struct graph *graph,
|
|||||||
case exact_match:
|
case exact_match:
|
||||||
trace_matcher ("exact_match\n");
|
trace_matcher ("exact_match\n");
|
||||||
if (last_token)
|
if (last_token)
|
||||||
listnode_add (next, gn);
|
{
|
||||||
|
newstack = XMALLOC (MTYPE_CMD_MATCHSTACK,
|
||||||
|
sizeof(struct graph_node *));
|
||||||
|
/* same as above, not recursing on this */
|
||||||
|
newstack[0] = gstack[0];
|
||||||
|
listnode_add (next, newstack);
|
||||||
|
}
|
||||||
else if (matchtype >= minmatch)
|
else if (matchtype >= minmatch)
|
||||||
add_nexthops (next, gn);
|
add_nexthops (next, gstack[0], gstack, idx + 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
trace_matcher ("no_match\n");
|
trace_matcher ("no_match\n");
|
||||||
@ -409,8 +446,9 @@ command_complete (struct graph *graph,
|
|||||||
{
|
{
|
||||||
// extract cmd_token into list
|
// extract cmd_token into list
|
||||||
*completions = list_new ();
|
*completions = list_new ();
|
||||||
for (ALL_LIST_ELEMENTS_RO (next,node,gn))
|
for (ALL_LIST_ELEMENTS_RO (next,node,gstack)) {
|
||||||
listnode_add (*completions, gn->data);
|
listnode_add (*completions, gstack[0]->data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list_delete (current);
|
list_delete (current);
|
||||||
@ -425,26 +463,52 @@ command_complete (struct graph *graph,
|
|||||||
*
|
*
|
||||||
* @param[in] list to add the nexthops to
|
* @param[in] list to add the nexthops to
|
||||||
* @param[in] node to start calculating nexthops from
|
* @param[in] node to start calculating nexthops from
|
||||||
|
* @param[in] stack listing previously visited nodes, if non-NULL.
|
||||||
|
* @param[in] stackpos how many valid entries are in stack
|
||||||
* @return the number of children added to the list
|
* @return the number of children added to the list
|
||||||
|
*
|
||||||
|
* NB: non-null "stack" means that new stacks will be added to "list" as
|
||||||
|
* output, instead of direct node pointers!
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
add_nexthops (struct list *list, struct graph_node *node)
|
add_nexthops (struct list *list, struct graph_node *node,
|
||||||
|
struct graph_node **stack, size_t stackpos)
|
||||||
{
|
{
|
||||||
int added = 0;
|
int added = 0;
|
||||||
struct graph_node *child;
|
struct graph_node *child;
|
||||||
|
struct graph_node **nextstack;
|
||||||
for (unsigned int i = 0; i < vector_active (node->to); i++)
|
for (unsigned int i = 0; i < vector_active (node->to); i++)
|
||||||
{
|
{
|
||||||
child = vector_slot (node->to, i);
|
child = vector_slot (node->to, i);
|
||||||
|
size_t j;
|
||||||
|
if (!child->allowrepeat)
|
||||||
|
{
|
||||||
|
for (j = 0; j < stackpos; j++)
|
||||||
|
if (child == stack[j])
|
||||||
|
break;
|
||||||
|
if (j != stackpos)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
struct cmd_token *token = child->data;
|
struct cmd_token *token = child->data;
|
||||||
switch (token->type)
|
switch (token->type)
|
||||||
{
|
{
|
||||||
case OPTION_TKN:
|
case OPTION_TKN:
|
||||||
case SELECTOR_TKN:
|
case SELECTOR_TKN:
|
||||||
case NUL_TKN:
|
case NUL_TKN:
|
||||||
added += add_nexthops (list, child);
|
added += add_nexthops (list, child, stack, stackpos);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
listnode_add (list, child);
|
if (stack)
|
||||||
|
{
|
||||||
|
nextstack = XMALLOC (MTYPE_CMD_MATCHSTACK,
|
||||||
|
(stackpos + 1) * sizeof(struct graph_node *));
|
||||||
|
nextstack[0] = child;
|
||||||
|
memcpy(nextstack + 1, stack, stackpos * sizeof(struct graph_node *));
|
||||||
|
|
||||||
|
listnode_add (list, nextstack);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
listnode_add (list, child);
|
||||||
added++;
|
added++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,6 +180,8 @@ start:
|
|||||||
if ((ctx->currnode = add_edge_dedup (ctx->currnode, $3)) != $3)
|
if ((ctx->currnode = add_edge_dedup (ctx->currnode, $3)) != $3)
|
||||||
graph_delete_node (ctx->graph, $3);
|
graph_delete_node (ctx->graph, $3);
|
||||||
|
|
||||||
|
ctx->currnode->allowrepeat = 1;
|
||||||
|
|
||||||
// adding a node as a child of itself accepts any number
|
// adding a node as a child of itself accepts any number
|
||||||
// of the same token, which is what we want for variadics
|
// of the same token, which is what we want for variadics
|
||||||
add_edge_dedup (ctx->currnode, ctx->currnode);
|
add_edge_dedup (ctx->currnode, ctx->currnode);
|
||||||
@ -336,6 +338,26 @@ selector_seq_seq:
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/* {keyword} productions */
|
||||||
|
selector: '{' selector_seq_seq '}'
|
||||||
|
{
|
||||||
|
$$ = malloc (sizeof (struct subgraph));
|
||||||
|
$$->start = new_token_node (ctx, SELECTOR_TKN, NULL, NULL);
|
||||||
|
$$->end = new_token_node (ctx, NUL_TKN, NULL, NULL);
|
||||||
|
graph_add_edge ($$->start, $$->end);
|
||||||
|
for (unsigned int i = 0; i < vector_active ($2->start->to); i++)
|
||||||
|
{
|
||||||
|
struct graph_node *sn = vector_slot ($2->start->to, i),
|
||||||
|
*en = vector_slot ($2->end->from, i);
|
||||||
|
graph_add_edge ($$->start, sn);
|
||||||
|
graph_add_edge (en, $$->start);
|
||||||
|
}
|
||||||
|
graph_delete_node (ctx->graph, $2->start);
|
||||||
|
graph_delete_node (ctx->graph, $2->end);
|
||||||
|
free ($2);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
selector_token_seq:
|
selector_token_seq:
|
||||||
simple_token
|
simple_token
|
||||||
{
|
{
|
||||||
|
@ -36,6 +36,7 @@ struct graph_node
|
|||||||
{
|
{
|
||||||
vector from; // nodes which have edges to this node
|
vector from; // nodes which have edges to this node
|
||||||
vector to; // nodes which this node has edges to
|
vector to; // nodes which this node has edges to
|
||||||
|
bool allowrepeat;
|
||||||
|
|
||||||
void *data; // node data
|
void *data; // node data
|
||||||
void (*del) (void *data); // deletion callback
|
void (*del) (void *data); // deletion callback
|
||||||
|
Loading…
Reference in New Issue
Block a user