mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-08 14:34:22 +00:00
lib: Continue matching system refactor
Most things back to working, all CLI units refactored to use improved graph implementation. Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
parent
7a6ded4096
commit
1eb5e8dcd1
@ -4191,7 +4191,7 @@ cmd_terminate_element(struct cmd_element *cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
free_cmd_element(struct cmd_element *cmd)
|
del_cmd_element(struct cmd_element *cmd)
|
||||||
{
|
{
|
||||||
if (!cmd) return;
|
if (!cmd) return;
|
||||||
free ((char*) cmd->string);
|
free ((char*) cmd->string);
|
||||||
|
@ -573,7 +573,7 @@ extern void cmd_terminate (void);
|
|||||||
|
|
||||||
/* memory management for cmd_element */
|
/* memory management for cmd_element */
|
||||||
void
|
void
|
||||||
free_cmd_element(struct cmd_element *);
|
del_cmd_element(struct cmd_element *);
|
||||||
struct cmd_element *
|
struct cmd_element *
|
||||||
copy_cmd_element(struct cmd_element *cmd);
|
copy_cmd_element(struct cmd_element *cmd);
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <zebra.h>
|
#include <zebra.h>
|
||||||
#include "command_match.h"
|
#include "command_match.h"
|
||||||
#include "command_parse.h"
|
#include "command_parse.h"
|
||||||
|
#include "grammar_sandbox.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
/* matcher helper prototypes */
|
/* matcher helper prototypes */
|
||||||
@ -35,19 +36,16 @@ static struct list *
|
|||||||
command_match_r (struct graph_node *, vector, unsigned int);
|
command_match_r (struct graph_node *, vector, unsigned int);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
score_precedence (enum graph_node_type);
|
score_precedence (enum cmd_token_type_t);
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
min_match_level (enum node_type);
|
min_match_level (enum cmd_token_type_t);
|
||||||
|
|
||||||
static struct graph_node *
|
|
||||||
copy_node (struct graph_node *);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
delete_nodelist (void *);
|
del_arglist (struct list *);
|
||||||
|
|
||||||
static struct graph_node *
|
static struct cmd_token_t *
|
||||||
disambiguate_nodes (struct graph_node *, struct graph_node *, char *);
|
disambiguate_tokens (struct cmd_token_t *, struct cmd_token_t *, char *);
|
||||||
|
|
||||||
static struct list *
|
static struct list *
|
||||||
disambiguate (struct list *, struct list *, vector, unsigned int);
|
disambiguate (struct list *, struct list *, vector, unsigned int);
|
||||||
@ -57,7 +55,7 @@ compare_completions (const void *, const void *);
|
|||||||
|
|
||||||
/* token matcher prototypes */
|
/* token matcher prototypes */
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_token (struct graph_node *, char *);
|
match_token (struct cmd_token_t *, char *);
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_ipv4 (const char *);
|
match_ipv4 (const char *);
|
||||||
@ -72,22 +70,22 @@ static enum match_type
|
|||||||
match_ipv6_prefix (const char *);
|
match_ipv6_prefix (const char *);
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_range (struct graph_node *, const char *);
|
match_range (struct cmd_token_t *, const char *);
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_word (struct graph_node *, const char *);
|
match_word (struct cmd_token_t *, const char *);
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_number (struct graph_node *, const char *);
|
match_number (struct cmd_token_t *, const char *);
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_variable (struct graph_node *node, const char *word);
|
match_variable (struct cmd_token_t *, const char *);
|
||||||
|
|
||||||
/* matching functions */
|
/* matching functions */
|
||||||
static enum matcher_rv matcher_rv;
|
static enum matcher_rv matcher_rv;
|
||||||
|
|
||||||
enum matcher_rv
|
enum matcher_rv
|
||||||
command_match (struct graph_node *start,
|
command_match (struct graph *cmdgraph,
|
||||||
vector vline,
|
vector vline,
|
||||||
struct list **argv,
|
struct list **argv,
|
||||||
struct cmd_element **el)
|
struct cmd_element **el)
|
||||||
@ -100,11 +98,19 @@ command_match (struct graph_node *start,
|
|||||||
memcpy (vvline->index + 1, vline->index, sizeof (void *) * vline->alloced);
|
memcpy (vvline->index + 1, vline->index, sizeof (void *) * vline->alloced);
|
||||||
vvline->active = vline->active + 1;
|
vvline->active = vline->active + 1;
|
||||||
|
|
||||||
|
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))) // successful match
|
||||||
{
|
{
|
||||||
|
// delete dummy start node
|
||||||
list_delete_node (*argv, listhead (*argv));
|
list_delete_node (*argv, listhead (*argv));
|
||||||
struct graph_node *end = listgetdata (listtail (*argv));
|
// get cmd_element out of list tail
|
||||||
*el = end->element;
|
struct listnode *tail = listtail (*argv);
|
||||||
|
*el = listgetdata (tail);
|
||||||
|
// delete list tail
|
||||||
|
tail->data = NULL;
|
||||||
|
list_delete_node (*argv, tail);
|
||||||
|
// now argv is an ordered list of cmd_token matching the user
|
||||||
|
// input, with each cmd_token->arg holding the corresponding input
|
||||||
assert (*el);
|
assert (*el);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +126,7 @@ command_match (struct graph_node *start,
|
|||||||
*
|
*
|
||||||
* The next step is to see if there is further input in the input line. If
|
* The next step is to see if there is further input in the input line. If
|
||||||
* there is not, the current node's children are searched to see if any of them
|
* there is not, the current node's children are searched to see if any of them
|
||||||
* are leaves (type END_GN). If this is the case, then the bottom of the
|
* are leaves (type END_TKN). If this is the case, then the bottom of the
|
||||||
* recursion stack has been reached, the leaf is pushed onto the argument list,
|
* recursion stack has been reached, the leaf is pushed onto the argument list,
|
||||||
* the current node is pushed, and the resulting argument list is
|
* the current node is pushed, and the resulting argument list is
|
||||||
* returned (MATCHER_OK). If it is not the case, NULL is returned, indicating
|
* returned (MATCHER_OK). If it is not the case, NULL is returned, indicating
|
||||||
@ -165,13 +171,14 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
assert (n < vector_active (vline));
|
assert (n < vector_active (vline));
|
||||||
|
|
||||||
// get the minimum match level that can count as a full match
|
// get the minimum match level that can count as a full match
|
||||||
enum match_type minmatch = min_match_level (start->type);
|
struct cmd_token_t *token = start->data;
|
||||||
|
enum match_type minmatch = min_match_level (token->type);
|
||||||
|
|
||||||
// get the current operating token
|
// get the current operating input token
|
||||||
char *token = vector_slot (vline, n);
|
char *input_token = vector_slot (vline, n);
|
||||||
|
|
||||||
// if we don't match this node, die
|
// if we don't match this node, die
|
||||||
if (match_token (start, token) < minmatch)
|
if (match_token (token, input_token) < minmatch)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// pointers for iterating linklist
|
// pointers for iterating linklist
|
||||||
@ -187,14 +194,22 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
struct list *currbest = NULL;
|
struct list *currbest = NULL;
|
||||||
for (ALL_LIST_ELEMENTS_RO (next,ln,gn))
|
for (ALL_LIST_ELEMENTS_RO (next,ln,gn))
|
||||||
{
|
{
|
||||||
// if we've matched all input we're looking for END_GN
|
// if we've matched all input we're looking for END_TKN
|
||||||
if (n+1 == vector_active (vline))
|
if (n+1 == vector_active (vline))
|
||||||
{
|
{
|
||||||
if (gn->type == END_GN)
|
struct cmd_token_t *tok = gn->data;
|
||||||
|
if (tok->type == END_TKN)
|
||||||
{
|
{
|
||||||
currbest = list_new();
|
currbest = list_new();
|
||||||
listnode_add (currbest, copy_node(gn));
|
// node should have one child node with the element
|
||||||
currbest->del = &delete_nodelist;
|
struct graph_node *leaf = vector_slot (gn->to, 0);
|
||||||
|
// last node in the list will hold the cmd_element;
|
||||||
|
// this is important because list_delete() expects
|
||||||
|
// that all nodes have the same data type, so when
|
||||||
|
// deleting this list the last node must be
|
||||||
|
// manually deleted
|
||||||
|
listnode_add (currbest, leaf->data);
|
||||||
|
currbest->del = (void (*)(void *)) &del_cmd_token;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else continue;
|
else continue;
|
||||||
@ -206,9 +221,17 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
// save the best match
|
// save the best match
|
||||||
if (result && currbest)
|
if (result && currbest)
|
||||||
{
|
{
|
||||||
|
// pick the best of two matches
|
||||||
struct list *newbest = disambiguate (currbest, result, vline, n+1);
|
struct list *newbest = disambiguate (currbest, result, vline, n+1);
|
||||||
|
// set ambiguity flag
|
||||||
ambiguous = !newbest || (ambiguous && newbest == currbest);
|
ambiguous = !newbest || (ambiguous && newbest == currbest);
|
||||||
list_delete ((newbest && newbest == result) ? currbest : result);
|
// choose the list to be deleted
|
||||||
|
struct list *todelete = ((newbest && newbest == result) ? currbest : result);
|
||||||
|
// manually delete the last node, which has a cmd_element
|
||||||
|
del_cmd_element (listgetdata (listtail (todelete)));
|
||||||
|
// use the list->del callback to delete the rest of the list
|
||||||
|
list_delete (todelete);
|
||||||
|
|
||||||
currbest = newbest ? newbest : currbest;
|
currbest = newbest ? newbest : currbest;
|
||||||
}
|
}
|
||||||
else if (result)
|
else if (result)
|
||||||
@ -219,16 +242,16 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
{
|
{
|
||||||
if (ambiguous)
|
if (ambiguous)
|
||||||
{
|
{
|
||||||
list_delete (currbest);
|
del_arglist (currbest);
|
||||||
currbest = NULL;
|
|
||||||
matcher_rv = MATCHER_AMBIGUOUS;
|
matcher_rv = MATCHER_AMBIGUOUS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// copy current node, set arg and prepend to currbest
|
// copy token, set arg and prepend to currbest
|
||||||
struct graph_node *curr = copy_node (start);
|
struct cmd_token_t *token = start->data;
|
||||||
curr->arg = XSTRDUP(MTYPE_CMD_TOKENS, token);
|
struct cmd_token_t *copy = copy_cmd_token (token);
|
||||||
list_add_node_prev (currbest, currbest->head, curr);
|
copy->arg = XSTRDUP(MTYPE_CMD_TOKENS, input_token);
|
||||||
|
list_add_node_prev (currbest, currbest->head, copy);
|
||||||
matcher_rv = MATCHER_OK;
|
matcher_rv = MATCHER_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,12 +265,12 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum matcher_rv
|
enum matcher_rv
|
||||||
command_complete (struct graph_node *start,
|
command_complete (struct graph *graph,
|
||||||
vector vline,
|
vector vline,
|
||||||
struct list **completions)
|
struct list **completions)
|
||||||
{
|
{
|
||||||
// pointer to next input token to match
|
// pointer to next input token to match
|
||||||
char *token;
|
char *input_token;
|
||||||
|
|
||||||
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
|
||||||
@ -257,6 +280,7 @@ command_complete (struct graph_node *start,
|
|||||||
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);
|
||||||
add_nexthops (next, start);
|
add_nexthops (next, start);
|
||||||
|
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
@ -266,11 +290,12 @@ command_complete (struct graph_node *start,
|
|||||||
current = next;
|
current = next;
|
||||||
next = list_new();
|
next = list_new();
|
||||||
|
|
||||||
token = vector_slot (vline, idx);
|
input_token = vector_slot (vline, idx);
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS_RO (current,node,gn))
|
for (ALL_LIST_ELEMENTS_RO (current,node,gn))
|
||||||
{
|
{
|
||||||
switch (match_token (gn, token))
|
struct cmd_token_t *token = gn->data;
|
||||||
|
switch (match_token (token, input_token))
|
||||||
{
|
{
|
||||||
case partly_match:
|
case partly_match:
|
||||||
if (idx == vector_active (vline) - 1)
|
if (idx == vector_active (vline) - 1)
|
||||||
@ -300,19 +325,24 @@ command_complete (struct graph_node *start,
|
|||||||
MATCHER_OK :
|
MATCHER_OK :
|
||||||
MATCHER_NO_MATCH;
|
MATCHER_NO_MATCH;
|
||||||
|
|
||||||
|
// extract cmd_token into list
|
||||||
|
*completions = list_new ();
|
||||||
|
for (ALL_LIST_ELEMENTS_RO (next,node,gn))
|
||||||
|
listnode_add (*completions, gn->data);
|
||||||
|
|
||||||
list_free (current);
|
list_free (current);
|
||||||
*completions = next;
|
list_free (next);
|
||||||
|
|
||||||
return matcher_rv;
|
return matcher_rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: move this logic to command.c
|
||||||
* Compare two completions. Tightly coupled to vector.
|
* Compare two completions. Tightly coupled to vector.
|
||||||
*
|
*
|
||||||
* @param[in] fst pointer to first item pointer in vector->index
|
* @param[in] fst pointer to first item pointer in vector->index
|
||||||
* @param[in] snd pointer to second item poitner in vector->index
|
* @param[in] snd pointer to second item poitner in vector->index
|
||||||
* @return integer compare code as determined by strcmp
|
* @return integer compare code as determined by strcmp
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
compare_completions (const void *fst, const void *snd)
|
compare_completions (const void *fst, const void *snd)
|
||||||
{
|
{
|
||||||
@ -353,10 +383,11 @@ command_complete_str (struct graph_node *start,
|
|||||||
list_delete (comps);
|
list_delete (comps);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all children that are reachable by one parser hop to the given list.
|
* Adds all children that are reachable by one parser hop to the given list.
|
||||||
* NUL_GN, SELECTOR_GN, and OPTION_GN nodes are treated as transparent.
|
* NUL_TKN, SELECTOR_TKN, and OPTION_TKN nodes are treated as transparent.
|
||||||
*
|
*
|
||||||
* @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
|
||||||
@ -367,14 +398,15 @@ add_nexthops (struct list *list, struct graph_node *node)
|
|||||||
{
|
{
|
||||||
int added = 0;
|
int added = 0;
|
||||||
struct graph_node *child;
|
struct graph_node *child;
|
||||||
for (unsigned int i = 0; i < vector_active (node->children); i++)
|
for (unsigned int i = 0; i < vector_active (node->to); i++)
|
||||||
{
|
{
|
||||||
child = vector_slot (node->children, i);
|
child = vector_slot (node->to, i);
|
||||||
switch (child->type)
|
struct cmd_token_t *token = child->data;
|
||||||
|
switch (token->type)
|
||||||
{
|
{
|
||||||
case OPTION_GN:
|
case OPTION_TKN:
|
||||||
case SELECTOR_GN:
|
case SELECTOR_TKN:
|
||||||
case NUL_GN:
|
case NUL_TKN:
|
||||||
added += add_nexthops (list, child);
|
added += add_nexthops (list, child);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -394,15 +426,15 @@ add_nexthops (struct list *list, struct graph_node *node)
|
|||||||
* @return minimum match level needed to for a token to fully match
|
* @return minimum match level needed to for a token to fully match
|
||||||
*/
|
*/
|
||||||
static enum match_type
|
static enum match_type
|
||||||
min_match_level (enum node_type type)
|
min_match_level (enum cmd_token_type_t type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
// anything matches a start node, for the sake of recursion
|
// anything matches a start node, for the sake of recursion
|
||||||
case START_GN:
|
case START_TKN:
|
||||||
return no_match;
|
return no_match;
|
||||||
// allowing words to partly match enables command abbreviation
|
// allowing words to partly match enables command abbreviation
|
||||||
case WORD_GN:
|
case WORD_TKN:
|
||||||
return partly_match;
|
return partly_match;
|
||||||
default:
|
default:
|
||||||
return exact_match;
|
return exact_match;
|
||||||
@ -416,23 +448,23 @@ min_match_level (enum node_type type)
|
|||||||
* @return precedence score
|
* @return precedence score
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
score_precedence (enum graph_node_type type)
|
score_precedence (enum cmd_token_type_t type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
// some of these are mutually exclusive, so they share
|
// some of these are mutually exclusive, so they share
|
||||||
// the same precedence value
|
// the same precedence value
|
||||||
case IPV4_GN:
|
case IPV4_TKN:
|
||||||
case IPV4_PREFIX_GN:
|
case IPV4_PREFIX_TKN:
|
||||||
case IPV6_GN:
|
case IPV6_TKN:
|
||||||
case IPV6_PREFIX_GN:
|
case IPV6_PREFIX_TKN:
|
||||||
case NUMBER_GN:
|
case NUMBER_TKN:
|
||||||
return 1;
|
return 1;
|
||||||
case RANGE_GN:
|
case RANGE_TKN:
|
||||||
return 2;
|
return 2;
|
||||||
case WORD_GN:
|
case WORD_TKN:
|
||||||
return 3;
|
return 3;
|
||||||
case VARIABLE_GN:
|
case VARIABLE_TKN:
|
||||||
return 4;
|
return 4;
|
||||||
default:
|
default:
|
||||||
return 10;
|
return 10;
|
||||||
@ -447,10 +479,10 @@ score_precedence (enum graph_node_type type)
|
|||||||
* @param[in] token the token being matched
|
* @param[in] token the token being matched
|
||||||
* @return the best-matching node, or NULL if the two are entirely ambiguous
|
* @return the best-matching node, or NULL if the two are entirely ambiguous
|
||||||
*/
|
*/
|
||||||
static struct graph_node *
|
static struct cmd_token_t *
|
||||||
disambiguate_nodes (struct graph_node *first,
|
disambiguate_tokens (struct cmd_token_t *first,
|
||||||
struct graph_node *second,
|
struct cmd_token_t *second,
|
||||||
char *token)
|
char *input_token)
|
||||||
{
|
{
|
||||||
// if the types are different, simply go off of type precedence
|
// if the types are different, simply go off of type precedence
|
||||||
if (first->type != second->type)
|
if (first->type != second->type)
|
||||||
@ -464,8 +496,8 @@ disambiguate_nodes (struct graph_node *first,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if they're the same, return the more exact match
|
// if they're the same, return the more exact match
|
||||||
enum match_type fmtype = match_token (first, token);
|
enum match_type fmtype = match_token (first, input_token);
|
||||||
enum match_type smtype = match_token (second, token);
|
enum match_type smtype = match_token (second, input_token);
|
||||||
if (fmtype != smtype)
|
if (fmtype != smtype)
|
||||||
return fmtype > smtype ? first : second;
|
return fmtype > smtype ? first : second;
|
||||||
|
|
||||||
@ -475,8 +507,8 @@ disambiguate_nodes (struct graph_node *first,
|
|||||||
/**
|
/**
|
||||||
* Picks the better of two possible matches for an input line.
|
* Picks the better of two possible matches for an input line.
|
||||||
*
|
*
|
||||||
* @param[in] first candidate list of graph_node matching vline
|
* @param[in] first candidate list of cmd_token_t matching vline
|
||||||
* @param[in] second candidate list of graph_node matching vline
|
* @param[in] second candidate list of cmd_token_t matching vline
|
||||||
* @param[in] vline the input line being matched
|
* @param[in] vline the input line being matched
|
||||||
* @param[in] n index into vline to start comparing at
|
* @param[in] n index into vline to start comparing at
|
||||||
* @return the best-matching list, or NULL if the two are entirely ambiguous
|
* @return the best-matching list, or NULL if the two are entirely ambiguous
|
||||||
@ -493,82 +525,68 @@ disambiguate (struct list *first,
|
|||||||
|
|
||||||
struct listnode *fnode = listhead (first),
|
struct listnode *fnode = listhead (first),
|
||||||
*snode = listhead (second);
|
*snode = listhead (second);
|
||||||
struct graph_node *fgn = listgetdata (fnode),
|
struct cmd_token_t *ftok = listgetdata (fnode),
|
||||||
*sgn = listgetdata (snode),
|
*stok = listgetdata (snode),
|
||||||
*best = NULL;
|
*best = NULL;
|
||||||
|
|
||||||
// compare each node, if one matches better use that one
|
// compare each token, if one matches better use that one
|
||||||
for (unsigned int i = n; i < vector_active (vline); i++)
|
for (unsigned int i = n; i < vector_active (vline); i++)
|
||||||
{
|
{
|
||||||
char *token = vector_slot(vline, i);
|
char *token = vector_slot(vline, i);
|
||||||
if ((best = disambiguate_nodes (fgn, sgn, token)))
|
if ((best = disambiguate_tokens (ftok, stok, token)))
|
||||||
return best == fgn ? first : second;
|
return best == ftok ? first : second;
|
||||||
fnode = listnextnode (fnode);
|
ftok = listgetdata (listnextnode (fnode));
|
||||||
snode = listnextnode (snode);
|
stok = listgetdata (listnextnode (snode));
|
||||||
fgn = (struct graph_node *) listgetdata (fnode);
|
|
||||||
sgn = (struct graph_node *) listgetdata (snode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Performs a deep copy on a node.
|
* Deletion function for arglist.
|
||||||
* Used to build argv node lists that can be safely deleted or modified by
|
|
||||||
* endpoint functions. Everything is copied except the children vector,
|
|
||||||
* subgraph end pointer and reference count.
|
|
||||||
*
|
*
|
||||||
* @param[in] node to copy
|
* Since list->del for arglists expects all listnode->data to hold cmd_token,
|
||||||
* @return the copy
|
* but arglists have cmd_element as the data for the tail, this function
|
||||||
*/
|
* manually deletes the tail before deleting the rest of the list as usual.
|
||||||
static struct graph_node *
|
*
|
||||||
copy_node (struct graph_node *node)
|
* @param list the arglist to delete
|
||||||
{
|
|
||||||
struct graph_node *new = graphnode_new(node->type);
|
|
||||||
new->children = NULL;
|
|
||||||
new->text = node->text ? XSTRDUP(MTYPE_CMD_TOKENS, node->text) : NULL;
|
|
||||||
new->value = node->value;
|
|
||||||
new->min = node->min;
|
|
||||||
new->max = node->max;
|
|
||||||
new->element = node->element ? copy_cmd_element(node->element) : NULL;
|
|
||||||
new->arg = node->arg ? XSTRDUP(MTYPE_CMD_TOKENS, node->arg) : NULL;
|
|
||||||
new->refs = 0;
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List deletion callback for argv lists.
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
delete_nodelist (void *node)
|
del_arglist (struct list *list)
|
||||||
{
|
{
|
||||||
graphnode_delete ((struct graph_node *) node);
|
// manually delete last node
|
||||||
|
struct listnode *tail = listtail (list);
|
||||||
|
del_cmd_element (tail->data);
|
||||||
|
tail->data = NULL;
|
||||||
|
list_delete_node (list, tail);
|
||||||
|
|
||||||
|
// delete the rest of the list as usual
|
||||||
|
list_delete (list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*---------- token level matching functions ----------*/
|
||||||
/* token level matching functions */
|
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_token (struct graph_node *node, char *token)
|
match_token (struct cmd_token_t *token, char *input_token)
|
||||||
{
|
{
|
||||||
switch (node->type) {
|
switch (token->type) {
|
||||||
case WORD_GN:
|
case WORD_TKN:
|
||||||
return match_word (node, token);
|
return match_word (token, input_token);
|
||||||
case IPV4_GN:
|
case IPV4_TKN:
|
||||||
return match_ipv4 (token);
|
return match_ipv4 (input_token);
|
||||||
case IPV4_PREFIX_GN:
|
case IPV4_PREFIX_TKN:
|
||||||
return match_ipv4_prefix (token);
|
return match_ipv4_prefix (input_token);
|
||||||
case IPV6_GN:
|
case IPV6_TKN:
|
||||||
return match_ipv6 (token);
|
return match_ipv6 (input_token);
|
||||||
case IPV6_PREFIX_GN:
|
case IPV6_PREFIX_TKN:
|
||||||
return match_ipv6_prefix (token);
|
return match_ipv6_prefix (input_token);
|
||||||
case RANGE_GN:
|
case RANGE_TKN:
|
||||||
return match_range (node, token);
|
return match_range (token, input_token);
|
||||||
case NUMBER_GN:
|
case NUMBER_TKN:
|
||||||
return match_number (node, token);
|
return match_number (token, input_token);
|
||||||
case VARIABLE_GN:
|
case VARIABLE_TKN:
|
||||||
return match_variable (node, token);
|
return match_variable (token, input_token);
|
||||||
case END_GN:
|
case END_TKN:
|
||||||
default:
|
default:
|
||||||
return no_match;
|
return no_match;
|
||||||
}
|
}
|
||||||
@ -776,9 +794,9 @@ match_ipv6_prefix (const char *str)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_range (struct graph_node *node, const char *str)
|
match_range (struct cmd_token_t *token, const char *str)
|
||||||
{
|
{
|
||||||
assert (node->type == RANGE_GN);
|
assert (token->type == RANGE_TKN);
|
||||||
|
|
||||||
char *endptr = NULL;
|
char *endptr = NULL;
|
||||||
long long val;
|
long long val;
|
||||||
@ -790,53 +808,53 @@ match_range (struct graph_node *node, const char *str)
|
|||||||
if (*endptr != '\0')
|
if (*endptr != '\0')
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (val < node->min || val > node->max)
|
if (val < token->min || val > token->max)
|
||||||
return no_match;
|
return no_match;
|
||||||
else
|
else
|
||||||
return exact_match;
|
return exact_match;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_word (struct graph_node *node, const char *word)
|
match_word (struct cmd_token_t *token, const char *word)
|
||||||
{
|
{
|
||||||
assert (node->type == WORD_GN);
|
assert (token->type == WORD_TKN);
|
||||||
|
|
||||||
// if the passed token is null or 0 length, partly match
|
// if the passed token is null or 0 length, partly match
|
||||||
if (!word || !strlen(word))
|
if (!word || !strlen(word))
|
||||||
return partly_match;
|
return partly_match;
|
||||||
|
|
||||||
// if the passed token is strictly a prefix of the full word, partly match
|
// if the passed token is strictly a prefix of the full word, partly match
|
||||||
if (strlen (word) < strlen (node->text))
|
if (strlen (word) < strlen (token->text))
|
||||||
return !strncmp (node->text, word, strlen (word)) ?
|
return !strncmp (token->text, word, strlen (word)) ?
|
||||||
partly_match :
|
partly_match :
|
||||||
no_match;
|
no_match;
|
||||||
|
|
||||||
// if they are the same length and exactly equal, exact match
|
// if they are the same length and exactly equal, exact match
|
||||||
else if (strlen (word) == strlen (node->text))
|
else if (strlen (word) == strlen (token->text))
|
||||||
return !strncmp (node->text, word, strlen (word)) ? exact_match : no_match;
|
return !strncmp (token->text, word, strlen (word)) ? exact_match : no_match;
|
||||||
|
|
||||||
return no_match;
|
return no_match;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_number (struct graph_node *node, const char *word)
|
match_number (struct cmd_token_t *token, const char *word)
|
||||||
{
|
{
|
||||||
assert (node->type == NUMBER_GN);
|
assert (token->type == NUMBER_TKN);
|
||||||
|
|
||||||
if (!strcmp ("\0", word)) return no_match;
|
if (!strcmp ("\0", word)) return no_match;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
long long num = strtoll (word, &endptr, 10);
|
long long num = strtoll (word, &endptr, 10);
|
||||||
if (endptr != '\0') return no_match;
|
if (endptr != '\0') return no_match;
|
||||||
return num == node->value ? exact_match : no_match;
|
return num == token->value ? exact_match : no_match;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VARIABLE_ALPHABET \
|
#define VARIABLE_ALPHABET \
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890:"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890:"
|
||||||
|
|
||||||
static enum match_type
|
static enum match_type
|
||||||
match_variable (struct graph_node *node, const char *word)
|
match_variable (struct cmd_token_t *token, const char *word)
|
||||||
{
|
{
|
||||||
assert (node->type == VARIABLE_GN);
|
assert (token->type == VARIABLE_TKN);
|
||||||
|
|
||||||
return strlen (word) == strspn(word, VARIABLE_ALPHABET) ?
|
return strlen (word) == strspn(word, VARIABLE_ALPHABET) ?
|
||||||
exact_match : no_match;
|
exact_match : no_match;
|
||||||
|
@ -25,10 +25,9 @@
|
|||||||
#ifndef _ZEBRA_COMMAND_MATCH_H
|
#ifndef _ZEBRA_COMMAND_MATCH_H
|
||||||
#define _ZEBRA_COMMAND_MATCH_H
|
#define _ZEBRA_COMMAND_MATCH_H
|
||||||
|
|
||||||
#include "command.h"
|
#include "graph.h"
|
||||||
#include "command_graph.h"
|
|
||||||
#include "linklist.h"
|
#include "linklist.h"
|
||||||
|
#include "command.h"
|
||||||
|
|
||||||
/* These definitions exist in command.c in the current engine but should be
|
/* These definitions exist in command.c in the current engine but should be
|
||||||
* relocated here in the new engine
|
* relocated here in the new engine
|
||||||
@ -68,14 +67,14 @@ enum match_type
|
|||||||
/**
|
/**
|
||||||
* Attempt to find an exact command match for a line of user input.
|
* Attempt to find an exact command match for a line of user input.
|
||||||
*
|
*
|
||||||
* @param[in] start start node of command graph to match against
|
* @param[in] cmdgraph command graph to match against
|
||||||
* @param[in] vline vectorized input string
|
* @param[in] vline vectorized input string
|
||||||
* @param[out] argv pointer to argument list if successful match
|
* @param[out] argv pointer to argument list if successful match
|
||||||
* @param[out] element pointer to matched cmd_element if successful match
|
* @param[out] element pointer to matched cmd_element if successful match
|
||||||
* @return matcher status
|
* @return matcher status
|
||||||
*/
|
*/
|
||||||
enum matcher_rv
|
enum matcher_rv
|
||||||
command_match (struct graph_node *start,
|
command_match (struct graph *cmdgraph,
|
||||||
vector vline,
|
vector vline,
|
||||||
struct list **argv,
|
struct list **argv,
|
||||||
struct cmd_element **element);
|
struct cmd_element **element);
|
||||||
@ -85,11 +84,11 @@ command_match (struct graph_node *start,
|
|||||||
*
|
*
|
||||||
* @param[in] start the start node of the DFA to match against
|
* @param[in] start the start node of the DFA to match against
|
||||||
* @param[in] vline vectorized input string
|
* @param[in] vline vectorized input string
|
||||||
* @param[in] completions pointer to list of possible next nodes
|
* @param[in] completions pointer to list of cmd_token representing
|
||||||
* @return matcher status
|
* acceptable next inputs
|
||||||
*/
|
*/
|
||||||
enum matcher_rv
|
enum matcher_rv
|
||||||
command_complete (struct graph_node *start,
|
command_complete (struct graph *cmdgraph,
|
||||||
vector vline,
|
vector vline,
|
||||||
struct list **completions);
|
struct list **completions);
|
||||||
|
|
||||||
@ -101,10 +100,10 @@ command_complete (struct graph_node *start,
|
|||||||
* @param[in] vline vectorized input string
|
* @param[in] vline vectorized input string
|
||||||
* @param[in] completions vector to fill with string completions
|
* @param[in] completions vector to fill with string completions
|
||||||
* @return matcher status
|
* @return matcher status
|
||||||
*/
|
|
||||||
enum matcher_rv
|
enum matcher_rv
|
||||||
command_complete_str (struct graph_node *start,
|
command_complete_str (struct graph *cmdgraph,
|
||||||
vector vline,
|
vector vline,
|
||||||
vector completions);
|
vector completions);
|
||||||
|
|
||||||
|
*/
|
||||||
#endif /* _ZEBRA_COMMAND_MATCH_H */
|
#endif /* _ZEBRA_COMMAND_MATCH_H */
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "graph.h"
|
#include "graph.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "grammar_sandbox.h"
|
||||||
|
|
||||||
extern int
|
extern int
|
||||||
yylex (void);
|
yylex (void);
|
||||||
@ -49,7 +50,7 @@
|
|||||||
|
|
||||||
/* functionality this unit exports */
|
/* functionality this unit exports */
|
||||||
%code provides {
|
%code provides {
|
||||||
struct graph *
|
void
|
||||||
command_parse_format (struct graph *, struct cmd_element *);
|
command_parse_format (struct graph *, struct cmd_element *);
|
||||||
|
|
||||||
/* maximum length of a number, lexer will not match anything longer */
|
/* maximum length of a number, lexer will not match anything longer */
|
||||||
@ -89,9 +90,11 @@
|
|||||||
%code {
|
%code {
|
||||||
/* bison declarations */
|
/* bison declarations */
|
||||||
void
|
void
|
||||||
yyerror (struct cmd_element *el, struct graph_node *sn, char const *msg);
|
yyerror (struct graph *, struct cmd_element *el, char const *msg);
|
||||||
|
|
||||||
/* state variables for a single parser run */
|
/* state variables for a single parser run */
|
||||||
|
struct graph_node *startnode; // start node of DFA
|
||||||
|
|
||||||
struct graph_node *currnode, // current position in DFA
|
struct graph_node *currnode, // current position in DFA
|
||||||
*seqhead; // sequence head
|
*seqhead; // sequence head
|
||||||
|
|
||||||
@ -108,16 +111,21 @@
|
|||||||
doc_next (void);
|
doc_next (void);
|
||||||
|
|
||||||
static struct graph_node *
|
static struct graph_node *
|
||||||
node_exists (struct graph_node *, struct graph_node *);
|
node_adjacent (struct graph_node *, struct graph_node *);
|
||||||
|
|
||||||
static struct graph_node *
|
static struct graph_node *
|
||||||
node_replace (struct graph_node *, struct graph_node *);
|
add_edge_dedup (struct graph_node *, struct graph_node *);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cmp_node (struct graph_node *, struct graph_node *);
|
cmp_token (struct cmd_token_t *, struct cmd_token_t *);
|
||||||
|
|
||||||
|
static struct graph_node *
|
||||||
|
new_token_node (struct graph *,
|
||||||
|
enum cmd_token_type_t type,
|
||||||
|
char *text, char *doc);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
terminate_graph (struct graph_node *,
|
terminate_graph (struct graph *,
|
||||||
struct graph_node *,
|
struct graph_node *,
|
||||||
struct cmd_element *);
|
struct cmd_element *);
|
||||||
|
|
||||||
@ -126,11 +134,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* yyparse parameters */
|
/* yyparse parameters */
|
||||||
%parse-param { struct cmd_element *element }
|
|
||||||
%parse-param { struct graph *graph }
|
%parse-param { struct graph *graph }
|
||||||
|
%parse-param { struct cmd_element *element }
|
||||||
|
|
||||||
/* called automatically before yyparse */
|
/* called automatically before yyparse */
|
||||||
%initial-action {
|
%initial-action {
|
||||||
|
startnode = vector_slot (graph->nodes, 0);
|
||||||
|
|
||||||
/* clear state pointers */
|
/* clear state pointers */
|
||||||
seqhead = NULL;
|
seqhead = NULL;
|
||||||
currnode = NULL;
|
currnode = NULL;
|
||||||
@ -151,28 +161,28 @@ start:
|
|||||||
sentence_root cmd_token_seq
|
sentence_root cmd_token_seq
|
||||||
{
|
{
|
||||||
// tack on the command element
|
// tack on the command element
|
||||||
terminate_graph (startnode, currnode, element);
|
terminate_graph (graph, currnode, element);
|
||||||
}
|
}
|
||||||
| sentence_root cmd_token_seq '.' placeholder_token
|
| sentence_root cmd_token_seq '.' placeholder_token
|
||||||
{
|
{
|
||||||
if ((currnode = node_replace (currnode, $4)) != $4)
|
if ((currnode = add_edge_dedup (currnode, $4)) != $4)
|
||||||
graph_delete_node ($4);
|
graph_delete_node (graph, $4);
|
||||||
|
|
||||||
// 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 varags
|
// of the same token, which is what we want for varags
|
||||||
node_replace (currnode, currnode);
|
add_edge_dedup (currnode, currnode);
|
||||||
|
|
||||||
// tack on the command element
|
// tack on the command element
|
||||||
terminate_graph (startnode, currnode, element);
|
terminate_graph (graph, currnode, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
sentence_root: WORD
|
sentence_root: WORD
|
||||||
{
|
{
|
||||||
struct graph_node *root =
|
struct graph_node *root =
|
||||||
new_token_node (WORD_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
new_token_node (graph, WORD_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
|
|
||||||
if ((currnode = node_replace (startnode, root)) != root)
|
if ((currnode = add_edge_dedup (startnode, root)) != root)
|
||||||
free (root);
|
graph_delete_node (graph, root);
|
||||||
|
|
||||||
free ($1);
|
free ($1);
|
||||||
$$ = currnode;
|
$$ = currnode;
|
||||||
@ -181,13 +191,13 @@ sentence_root: WORD
|
|||||||
cmd_token:
|
cmd_token:
|
||||||
placeholder_token
|
placeholder_token
|
||||||
{
|
{
|
||||||
if ((currnode = node_replace (currnode, $1)) != $1)
|
if ((currnode = add_edge_dedup (currnode, $1)) != $1)
|
||||||
graph_delete_node ($1);
|
graph_delete_node (graph, $1);
|
||||||
}
|
}
|
||||||
| literal_token
|
| literal_token
|
||||||
{
|
{
|
||||||
if ((currnode = node_replace (currnode, $1)) != $1)
|
if ((currnode = add_edge_dedup (currnode, $1)) != $1)
|
||||||
graph_delete_node ($1);
|
graph_delete_node (graph, $1);
|
||||||
}
|
}
|
||||||
/* selectors and options are subgraphs with start and end nodes */
|
/* selectors and options are subgraphs with start and end nodes */
|
||||||
| selector
|
| selector
|
||||||
@ -212,33 +222,33 @@ cmd_token_seq:
|
|||||||
placeholder_token:
|
placeholder_token:
|
||||||
IPV4
|
IPV4
|
||||||
{
|
{
|
||||||
$$ = new_token_node (IPV4_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
$$ = new_token_node (graph, IPV4_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
free ($1);
|
free ($1);
|
||||||
}
|
}
|
||||||
| IPV4_PREFIX
|
| IPV4_PREFIX
|
||||||
{
|
{
|
||||||
$$ = new_token_node (IPV4_PREFIX_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
$$ = new_token_node (graph, IPV4_PREFIX_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
free ($1);
|
free ($1);
|
||||||
}
|
}
|
||||||
| IPV6
|
| IPV6
|
||||||
{
|
{
|
||||||
$$ = new_token_node (IPV6_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
$$ = new_token_node (graph, IPV6_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
free ($1);
|
free ($1);
|
||||||
}
|
}
|
||||||
| IPV6_PREFIX
|
| IPV6_PREFIX
|
||||||
{
|
{
|
||||||
$$ = new_token_node (IPV6_PREFIX_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
$$ = new_token_node (graph, IPV6_PREFIX_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
free ($1);
|
free ($1);
|
||||||
}
|
}
|
||||||
| VARIABLE
|
| VARIABLE
|
||||||
{
|
{
|
||||||
$$ = new_token_node (VARIABLE_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
$$ = new_token_node (graph, VARIABLE_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
free ($1);
|
free ($1);
|
||||||
}
|
}
|
||||||
| RANGE
|
| RANGE
|
||||||
{
|
{
|
||||||
$$ = new_token_node (RANGE_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
$$ = new_token_node (graph, RANGE_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
cmd_token_t token = (cmd_token_t *) $$->data;
|
struct cmd_token_t *token = $$->data;
|
||||||
|
|
||||||
// get the numbers out
|
// get the numbers out
|
||||||
yylval.string++;
|
yylval.string++;
|
||||||
@ -247,7 +257,7 @@ placeholder_token:
|
|||||||
token->max = strtoll (yylval.string, &yylval.string, 10);
|
token->max = strtoll (yylval.string, &yylval.string, 10);
|
||||||
|
|
||||||
// validate range
|
// validate range
|
||||||
if (token->min >= token->max) yyerror (element, startnode, "Invalid range.");
|
if (token->min >= token->max) yyerror (graph, element, "Invalid range.");
|
||||||
|
|
||||||
free ($1);
|
free ($1);
|
||||||
}
|
}
|
||||||
@ -256,13 +266,13 @@ placeholder_token:
|
|||||||
literal_token:
|
literal_token:
|
||||||
WORD
|
WORD
|
||||||
{
|
{
|
||||||
$$ = new_token_node (WORD_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
$$ = new_token_node (graph, WORD_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next());
|
||||||
free ($1);
|
free ($1);
|
||||||
}
|
}
|
||||||
| NUMBER
|
| NUMBER
|
||||||
{
|
{
|
||||||
$$ = new_token_node (NUMBER_TKN, NULL, doc_next());
|
$$ = new_token_node (graph, NUMBER_TKN, NULL, doc_next());
|
||||||
cmd_token_t token = (cmd_token_t *) $$->data;
|
struct cmd_token_t *token = $$->data;
|
||||||
|
|
||||||
token->value = yylval.number;
|
token->value = yylval.number;
|
||||||
token->text = XCALLOC(MTYPE_CMD_TOKENS, DECIMAL_STRLEN_MAX+1);
|
token->text = XCALLOC(MTYPE_CMD_TOKENS, DECIMAL_STRLEN_MAX+1);
|
||||||
@ -288,14 +298,14 @@ selector_element: selector_element_root selector_token_seq
|
|||||||
// if the selector start and end do not exist, create them
|
// if the selector start and end do not exist, create them
|
||||||
if (!selnode_start || !selnode_end) {
|
if (!selnode_start || !selnode_end) {
|
||||||
assert(!selnode_start && !selnode_end);
|
assert(!selnode_start && !selnode_end);
|
||||||
selnode_start = new_token_node (SELECTOR_TKN, NULL, NULL);
|
selnode_start = new_token_node (graph, SELECTOR_TKN, NULL, NULL);
|
||||||
selnode_end = new_token_node (NUL_TKN, NULL, NULL);
|
selnode_end = new_token_node (graph, NUL_TKN, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add element head as a child of the selector
|
// add element head as a child of the selector
|
||||||
graph_add_edge (selnode_start, $1);
|
graph_add_edge (selnode_start, $1);
|
||||||
|
|
||||||
if ($2->type != NUL_GN) {
|
if (((struct cmd_token_t *) $2->data)->type != NUL_TKN) {
|
||||||
graph_add_edge ($1, seqhead);
|
graph_add_edge ($1, seqhead);
|
||||||
graph_add_edge ($2, selnode_end);
|
graph_add_edge ($2, selnode_end);
|
||||||
}
|
}
|
||||||
@ -306,12 +316,12 @@ selector_element: selector_element_root selector_token_seq
|
|||||||
}
|
}
|
||||||
|
|
||||||
selector_token_seq:
|
selector_token_seq:
|
||||||
%empty { $$ = new_token_node (NUL_TKN, NULL, NULL); }
|
%empty { $$ = new_token_node (graph, NUL_TKN, NULL, NULL); }
|
||||||
| selector_token_seq selector_token
|
| selector_token_seq selector_token
|
||||||
{
|
{
|
||||||
// if the sequence component is NUL_GN, this is a sequence start
|
// if the sequence component is NUL_TKN, this is a sequence start
|
||||||
if ($1->type == NUL_GN) {
|
if (((struct cmd_token_t *) $1->data)->type != NUL_TKN) {
|
||||||
assert(!seqhead); // sequence head should always be null here
|
assert(!seqhead);
|
||||||
seqhead = $2;
|
seqhead = $2;
|
||||||
}
|
}
|
||||||
else // chain on new node
|
else // chain on new node
|
||||||
@ -348,8 +358,8 @@ option_element:
|
|||||||
{
|
{
|
||||||
if (!optnode_start || !optnode_end) {
|
if (!optnode_start || !optnode_end) {
|
||||||
assert(!optnode_start && !optnode_end);
|
assert(!optnode_start && !optnode_end);
|
||||||
optnode_start = new_token_node (OPTION_TKN, NULL, NULL);
|
optnode_start = new_token_node (graph, OPTION_TKN, NULL, NULL);
|
||||||
optnode_end = new_token_node (NUL_TKN, NULL, NULL);
|
optnode_end = new_token_node (graph, NUL_TKN, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
graph_add_edge (optnode_start, seqhead);
|
graph_add_edge (optnode_start, seqhead);
|
||||||
@ -371,25 +381,23 @@ option_token:
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
struct graph_node *
|
void
|
||||||
command_parse_format (struct graph_node *start, struct cmd_element *cmd)
|
command_parse_format (struct graph *graph, struct cmd_element *cmd)
|
||||||
{
|
{
|
||||||
// set to 1 to enable parser traces
|
// set to 1 to enable parser traces
|
||||||
yydebug = 0;
|
yydebug = 0;
|
||||||
|
|
||||||
// parse command into DFA
|
// parse command into DFA
|
||||||
yyparse (cmd, start);
|
yyparse (graph, cmd);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
cleanup ();
|
cleanup ();
|
||||||
|
|
||||||
return start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parser helper functions */
|
/* parser helper functions */
|
||||||
|
|
||||||
void
|
void
|
||||||
yyerror (struct cmd_element *el, struct graph_node *sn, char const *msg)
|
yyerror (struct graph *graph, struct cmd_element *el, char const *msg)
|
||||||
{
|
{
|
||||||
zlog_err ("%s: FATAL parse error: %s", __func__, msg);
|
zlog_err ("%s: FATAL parse error: %s", __func__, msg);
|
||||||
zlog_err ("while parsing this command definition: \n\t%s\n", el->string);
|
zlog_err ("while parsing this command definition: \n\t%s\n", el->string);
|
||||||
@ -414,16 +422,19 @@ cleanup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
terminate_graph (struct graph_node *startnode,
|
terminate_graph (struct graph *graph, struct graph_node *finalnode, struct cmd_element *element)
|
||||||
struct graph_node *finalnode,
|
|
||||||
struct cmd_element *element)
|
|
||||||
{
|
{
|
||||||
struct graph_node *end = graph_new_node (graph, element, &cmd_delete_element);
|
// end of graph should look like this
|
||||||
|
// * -> finalnode -> END_TKN -> cmd_element
|
||||||
|
struct graph_node *end_token_node = new_token_node (graph, END_TKN, NULL, NULL);
|
||||||
|
struct graph_node *end_element_node =
|
||||||
|
graph_new_node (graph, element, (void (*)(void *)) &del_cmd_element);
|
||||||
|
|
||||||
if (node_adjacent (finalnode, end))
|
if (node_adjacent (finalnode, end_token_node))
|
||||||
yyerror (element, startnode, "Duplicate command.");
|
yyerror (graph, element, "Duplicate command.");
|
||||||
else
|
|
||||||
graph_add_edge (finalnode, end);
|
graph_add_edge (finalnode, end_token_node);
|
||||||
|
graph_add_edge (end_token_node, end_element_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
@ -432,57 +443,74 @@ doc_next()
|
|||||||
char *piece = NULL;
|
char *piece = NULL;
|
||||||
if (!docstr || !(piece = strsep (&docstr, "\n")))
|
if (!docstr || !(piece = strsep (&docstr, "\n")))
|
||||||
return NULL;
|
return NULL;
|
||||||
return XSTRDUP(MTYPE_CMD_TOKENS, piece);
|
return XSTRDUP (MTYPE_CMD_TOKENS, piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct graph_node *
|
static struct graph_node *
|
||||||
new_token_node (cmd_token_type_t type, char *text, char *doc)
|
new_token_node (struct graph *graph, enum cmd_token_type_t type, char *text, char *doc)
|
||||||
{
|
{
|
||||||
struct cmd_token_t *token =
|
struct cmd_token_t *token =
|
||||||
XMALLOC(MTYPE_CMD_TOKENS, sizeof (struct cmd_token_t));
|
XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token_t));
|
||||||
|
|
||||||
token->type = type;
|
token->type = type;
|
||||||
token->text = text;
|
token->text = text;
|
||||||
token->desc = doc;
|
token->desc = doc;
|
||||||
|
|
||||||
return graph_new_node (graph, token, &cmd_delete_token);
|
return graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a node is adjacent to another node
|
* Determines if there is an out edge from the first node to the second
|
||||||
*/
|
*/
|
||||||
static struct graph_node *
|
static struct graph_node *
|
||||||
node_adjacent (struct graph_node *node, struct graph_node *neighbor)
|
node_adjacent (struct graph_node *first, struct graph_node *second)
|
||||||
{
|
{
|
||||||
struct graph_node *adj;
|
struct graph_node *adj;
|
||||||
for (unsigned int i = 0; i < vector_active (node->to); i++)
|
for (unsigned int i = 0; i < vector_active (first->to); i++)
|
||||||
{
|
{
|
||||||
adj = vector_slot (node->to, i);
|
adj = vector_slot (first->to, i);
|
||||||
if (cmp_node (neighbor, adj))
|
struct cmd_token_t *ftok = first->data,
|
||||||
|
*stok = second->data;
|
||||||
|
if (cmp_token (ftok, stok))
|
||||||
return adj;
|
return adj;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an edge betwen two nodes, unless there is already an edge to an
|
||||||
|
* equivalent node.
|
||||||
|
*
|
||||||
|
* The first node's out edges are searched to see if any of them point to a
|
||||||
|
* node that is equivalent to the second node. If such a node exists, it is
|
||||||
|
* returned. Otherwise an edge is created from the first node to the second.
|
||||||
|
*
|
||||||
|
* @param from start node for edge
|
||||||
|
* @param to end node for edge
|
||||||
|
* @return the node which the new edge points to
|
||||||
|
*/
|
||||||
static struct graph_node *
|
static struct graph_node *
|
||||||
node_replace (struct graph_node *parent, struct graph_node *child)
|
add_edge_dedup (struct graph_node *from, struct graph_node *to)
|
||||||
{
|
{
|
||||||
struct graph_node *existing = node_adjacent (parent, child);
|
struct graph_node *existing = node_adjacent (from, to);
|
||||||
return existing ? existing : graph_add_edge (parent, child);
|
return existing ? existing : graph_add_edge (from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two cmd_token's for equality,
|
||||||
|
*
|
||||||
|
* As such, this function is the working definition of token equality
|
||||||
|
* for parsing purposes and determines overall graph structure.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
cmp_token (struct cmd_token_t *first, struct cmd_token_t *second)
|
cmp_token (struct cmd_token_t *first, struct cmd_token_t *second)
|
||||||
{
|
{
|
||||||
struct cmd_token_t *first = (cmd_token *) firstgn->data;
|
|
||||||
struct cmd_token_t *second = (cmd_token *) secondgn->data;
|
|
||||||
|
|
||||||
// compare types
|
// compare types
|
||||||
if (first->type != second->type) return 0;
|
if (first->type != second->type) return 0;
|
||||||
|
|
||||||
switch (first->type) {
|
switch (first->type) {
|
||||||
case WORD_GN:
|
case WORD_TKN:
|
||||||
case VARIABLE_GN:
|
case VARIABLE_TKN:
|
||||||
if (first->text && second->text)
|
if (first->text && second->text)
|
||||||
{
|
{
|
||||||
if (strcmp (first->text, second->text))
|
if (strcmp (first->text, second->text))
|
||||||
@ -490,28 +518,30 @@ cmp_token (struct cmd_token_t *first, struct cmd_token_t *second)
|
|||||||
}
|
}
|
||||||
else if (first->text != second->text) return 0;
|
else if (first->text != second->text) return 0;
|
||||||
break;
|
break;
|
||||||
case RANGE_GN:
|
case RANGE_TKN:
|
||||||
if (first->min != second->min || first->max != second->max)
|
if (first->min != second->min || first->max != second->max)
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
case NUMBER_GN:
|
case NUMBER_TKN:
|
||||||
if (first->value != second->value) return 0;
|
if (first->value != second->value) return 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* selectors and options should be equal if their subgraphs are equal,
|
/* selectors and options should be equal if their subgraphs are equal,
|
||||||
* but the graph isomorphism problem is not known to be solvable in
|
* but the graph isomorphism problem is not known to be solvable in
|
||||||
* polynomial time so we consider selectors and options inequal in all
|
* polynomial time so we consider selectors and options inequal in all
|
||||||
* cases; ultimately this forks the graph, but the matcher can handle
|
* cases; ultimately this forks the graph, but the matcher can handle
|
||||||
* this regardless
|
* this regardless
|
||||||
*/
|
*/
|
||||||
case SELECTOR_GN:
|
case SELECTOR_TKN:
|
||||||
case OPTION_GN:
|
case OPTION_TKN:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* end nodes are always considered equal, since each node may only
|
/* end nodes are always considered equal, since each node may only
|
||||||
* have one END_GN child at a time
|
* have one END_TKN child at a time
|
||||||
*/
|
*/
|
||||||
case START_GN:
|
case START_TKN:
|
||||||
case END_GN:
|
case END_TKN:
|
||||||
case NUL_GN:
|
case NUL_TKN:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -30,16 +30,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "command_graph.h"
|
#include "graph.h"
|
||||||
#include "command_parse.h"
|
#include "command_parse.h"
|
||||||
#include "command_match.h"
|
#include "command_match.h"
|
||||||
|
|
||||||
#define GRAMMAR_STR "CLI grammar sandbox\n"
|
#define GRAMMAR_STR "CLI grammar sandbox\n"
|
||||||
|
|
||||||
void
|
void
|
||||||
grammar_sandbox_init(void);
|
grammar_sandbox_init (void);
|
||||||
void
|
void
|
||||||
pretty_print_graph (struct graph_node *start, int level);
|
pretty_print_graph (struct graph *start, int level);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start node for testing command graph.
|
* Start node for testing command graph.
|
||||||
@ -48,7 +48,7 @@ pretty_print_graph (struct graph_node *start, int level);
|
|||||||
* The examples below show how to install a command to the graph, calculate
|
* The examples below show how to install a command to the graph, calculate
|
||||||
* completions for a given input line, and match input against the graph.
|
* completions for a given input line, and match input against the graph.
|
||||||
*/
|
*/
|
||||||
struct graph_node * nodegraph;
|
struct graph *nodegraph;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference use of parsing / command installation API
|
* Reference use of parsing / command installation API
|
||||||
@ -62,11 +62,11 @@ DEFUN (grammar_test,
|
|||||||
char *command = argv_concat(argv, argc, 0);
|
char *command = argv_concat(argv, argc, 0);
|
||||||
|
|
||||||
// initialize a pretend cmd_element
|
// initialize a pretend cmd_element
|
||||||
struct cmd_element *cmd = XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
|
struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
|
||||||
cmd->string = XSTRDUP(MTYPE_TMP, command);
|
cmd->string = XSTRDUP (MTYPE_TMP, command);
|
||||||
cmd->doc = NULL;
|
cmd->doc = NULL;
|
||||||
cmd->func = NULL;
|
cmd->func = NULL;
|
||||||
cmd->tokens = vector_init(VECTOR_MIN_SIZE);
|
cmd->tokens = vector_init (VECTOR_MIN_SIZE);
|
||||||
|
|
||||||
// parse the command and install it into the command graph
|
// parse the command and install it into the command graph
|
||||||
command_parse_format (nodegraph, cmd);
|
command_parse_format (nodegraph, cmd);
|
||||||
@ -91,22 +91,24 @@ DEFUN (grammar_test_complete,
|
|||||||
char *cmdstr = argv_concat (argv, argc, 0);
|
char *cmdstr = argv_concat (argv, argc, 0);
|
||||||
vector command = cmd_make_strvec (cmdstr);
|
vector command = cmd_make_strvec (cmdstr);
|
||||||
|
|
||||||
vector completions = vector_init (VECTOR_MIN_SIZE);
|
struct list *completions = list_new ();
|
||||||
enum matcher_rv result =
|
enum matcher_rv result =
|
||||||
command_complete_str (nodegraph, command, completions);
|
command_complete (nodegraph, command, &completions);
|
||||||
|
|
||||||
// print completions or relevant error message
|
// print completions or relevant error message
|
||||||
if (!MATCHER_ERROR(result))
|
if (!MATCHER_ERROR(result))
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < vector_active (completions); i++)
|
struct listnode *ln;
|
||||||
zlog_info ((char *) vector_slot (completions, i));
|
struct cmd_token_t *tkn;
|
||||||
|
for (ALL_LIST_ELEMENTS_RO(completions,ln,tkn))
|
||||||
|
zlog_info (tkn->text);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
zlog_info ("%% No match for \"%s\"", cmdstr);
|
zlog_info ("%% No match for \"%s\"", cmdstr);
|
||||||
|
|
||||||
// free resources
|
// free resources
|
||||||
cmd_free_strvec (command);
|
cmd_free_strvec (command);
|
||||||
cmd_free_strvec (completions);
|
list_delete (completions);
|
||||||
free (cmdstr);
|
free (cmdstr);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
@ -138,11 +140,12 @@ DEFUN (grammar_test_match,
|
|||||||
zlog_info ("Matched: %s", element->string);
|
zlog_info ("Matched: %s", element->string);
|
||||||
struct listnode *ln;
|
struct listnode *ln;
|
||||||
struct graph_node *gn;
|
struct graph_node *gn;
|
||||||
for (ALL_LIST_ELEMENTS_RO(argvv,ln,gn))
|
for (ALL_LIST_ELEMENTS_RO(argvv,ln,gn)) {
|
||||||
if (gn->type == END_GN)
|
struct cmd_token_t *token = gn->data;
|
||||||
zlog_info ("func: %p", gn->element->func);
|
zlog_info ("%s -- %s", token->text, token->arg);
|
||||||
else
|
}
|
||||||
zlog_info ("%s -- %s", gn->text, gn->arg);
|
|
||||||
|
zlog_info ("func: %p", element->func);
|
||||||
|
|
||||||
list_delete (argvv);
|
list_delete (argvv);
|
||||||
}
|
}
|
||||||
@ -182,7 +185,7 @@ DEFUN (grammar_test_doc,
|
|||||||
"Command end\n")
|
"Command end\n")
|
||||||
{
|
{
|
||||||
// create cmd_element with docstring
|
// create cmd_element with docstring
|
||||||
struct cmd_element *cmd = XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element));
|
struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
|
||||||
cmd->string = "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG";
|
cmd->string = "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG";
|
||||||
cmd->doc = "Test stuff\n"
|
cmd->doc = "Test stuff\n"
|
||||||
"docstring thing\n"
|
"docstring thing\n"
|
||||||
@ -224,7 +227,13 @@ DEFUN (grammar_test_show,
|
|||||||
/* this is called in vtysh.c to set up the testing shim */
|
/* this is called in vtysh.c to set up the testing shim */
|
||||||
void grammar_sandbox_init() {
|
void grammar_sandbox_init() {
|
||||||
zlog_info ("Initializing grammar testing shim");
|
zlog_info ("Initializing grammar testing shim");
|
||||||
nodegraph = graphnode_new (START_GN);
|
|
||||||
|
// initialize graph, add start noe
|
||||||
|
nodegraph = graph_new ();
|
||||||
|
struct cmd_token_t *token = new_cmd_token (START_TKN, NULL, NULL);
|
||||||
|
graph_new_node (nodegraph, token, (void (*)(void *)) &del_cmd_token);
|
||||||
|
|
||||||
|
// install all enable elements
|
||||||
install_element (ENABLE_NODE, &grammar_test_cmd);
|
install_element (ENABLE_NODE, &grammar_test_cmd);
|
||||||
install_element (ENABLE_NODE, &grammar_test_show_cmd);
|
install_element (ENABLE_NODE, &grammar_test_show_cmd);
|
||||||
install_element (ENABLE_NODE, &grammar_test_match_cmd);
|
install_element (ENABLE_NODE, &grammar_test_match_cmd);
|
||||||
@ -234,8 +243,9 @@ void grammar_sandbox_init() {
|
|||||||
|
|
||||||
/* recursive pretty-print for command graph */
|
/* recursive pretty-print for command graph */
|
||||||
void
|
void
|
||||||
pretty_print_graph (struct graph_node *start, int level)
|
pretty_print_graph (struct graph *graph, int level)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
// print this node
|
// print this node
|
||||||
fprintf (stdout, "%s[%d] ", start->text, vector_active (start->children));
|
fprintf (stdout, "%s[%d] ", start->text, vector_active (start->children));
|
||||||
|
|
||||||
@ -266,4 +276,38 @@ pretty_print_graph (struct graph_node *start, int level)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/** stuff that should go in command.c + command.h */
|
||||||
|
struct cmd_token_t *
|
||||||
|
new_cmd_token (enum cmd_token_type_t type, char *text, char *desc)
|
||||||
|
{
|
||||||
|
struct cmd_token_t *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token_t));
|
||||||
|
token->type = type;
|
||||||
|
token->text = text;
|
||||||
|
token->desc = desc;
|
||||||
|
token->arg = NULL;
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
del_cmd_token (struct cmd_token_t *token)
|
||||||
|
{
|
||||||
|
XFREE (MTYPE_CMD_TOKENS, token->text);
|
||||||
|
XFREE (MTYPE_CMD_TOKENS, token->desc);
|
||||||
|
XFREE (MTYPE_CMD_TOKENS, token->arg);
|
||||||
|
XFREE (MTYPE_CMD_TOKENS, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cmd_token_t *
|
||||||
|
copy_cmd_token (struct cmd_token_t *token)
|
||||||
|
{
|
||||||
|
struct cmd_token_t *copy = new_cmd_token (token->type, NULL, NULL);
|
||||||
|
copy->text = XSTRDUP (MTYPE_CMD_TOKENS, token->text);
|
||||||
|
copy->desc = XSTRDUP (MTYPE_CMD_TOKENS, token->desc);
|
||||||
|
copy->arg = copy->arg ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL;
|
||||||
|
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
#ifndef _GRAMMAR_SANDBOX_H
|
||||||
|
#define _GRAMMAR_SANDBOX_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Houses functionality for testing shim as well as code that should go into
|
||||||
|
* command.h and command.c during integration.
|
||||||
|
*/
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
grammar_sandbox_init(void);
|
grammar_sandbox_init (void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types for tokens.
|
* Types for tokens.
|
||||||
@ -11,9 +18,8 @@ grammar_sandbox_init(void);
|
|||||||
*/
|
*/
|
||||||
enum cmd_token_type_t
|
enum cmd_token_type_t
|
||||||
{
|
{
|
||||||
_TOKEN_BUG = 0,
|
WORD_TKN, // words
|
||||||
LITERAL_TKN, // words
|
NUMBER_TKN, // integral numbers
|
||||||
OPTION_TKN, // integer ranges
|
|
||||||
VARIABLE_TKN, // almost anything
|
VARIABLE_TKN, // almost anything
|
||||||
RANGE_TKN, // integer range
|
RANGE_TKN, // integer range
|
||||||
IPV4_TKN, // IPV4 addresses
|
IPV4_TKN, // IPV4 addresses
|
||||||
@ -22,13 +28,13 @@ enum cmd_token_type_t
|
|||||||
IPV6_PREFIX_TKN, // IPV6 network prefixes
|
IPV6_PREFIX_TKN, // IPV6 network prefixes
|
||||||
|
|
||||||
/* plumbing types */
|
/* plumbing types */
|
||||||
SELECTOR, // marks beginning of selector
|
SELECTOR_TKN, // marks beginning of selector
|
||||||
OPTION, // marks beginning of option
|
OPTION_TKN, // marks beginning of option
|
||||||
NUL, // dummy token
|
NUL_TKN, // dummy token
|
||||||
START, // first token in line
|
START_TKN, // first token in line
|
||||||
END; // last token in line
|
END_TKN, // last token in line
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Token struct.
|
* Token struct.
|
||||||
*/
|
*/
|
||||||
@ -41,13 +47,17 @@ struct cmd_token_t
|
|||||||
|
|
||||||
long long value; // for numeric types
|
long long value; // for numeric types
|
||||||
long long min, max; // for ranges
|
long long min, max; // for ranges
|
||||||
|
|
||||||
|
char *arg; // user input that matches this token
|
||||||
};
|
};
|
||||||
|
|
||||||
inline struct cmd_token_t *
|
struct cmd_token_t *
|
||||||
cmd_new_token (cmd_token_type_t type, char *text, char *desc)
|
new_cmd_token (enum cmd_token_type_t, char *, char *);
|
||||||
{
|
|
||||||
struct cmd_token_t *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token_t));
|
void
|
||||||
token->type = type;
|
del_cmd_token (struct cmd_token_t *);
|
||||||
token->text = text;
|
|
||||||
token->desc = desc;
|
struct cmd_token_t *
|
||||||
}
|
copy_cmd_token (struct cmd_token_t *);
|
||||||
|
|
||||||
|
#endif /* _GRAMMAR_SANDBOX_H */
|
||||||
|
38
lib/graph.c
38
lib/graph.c
@ -22,18 +22,26 @@
|
|||||||
* 02111-1307, USA.
|
* 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
#include <zebra.h>
|
#include <zebra.h>
|
||||||
#include "command_graph.h"
|
#include "graph.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
|
struct graph *
|
||||||
|
graph_new ()
|
||||||
|
{
|
||||||
|
struct graph *graph = XCALLOC (MTYPE_GRAPH, sizeof(struct graph));
|
||||||
|
graph->nodes = vector_init (VECTOR_MIN_SIZE);
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
struct graph_node *
|
struct graph_node *
|
||||||
graph_new_node (struct graph *graph, void *data, void (*del) (void*))
|
graph_new_node (struct graph *graph, void *data, void (*del) (void*))
|
||||||
{
|
{
|
||||||
struct graph_node *node =
|
struct graph_node *node =
|
||||||
XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct graph_node));
|
XCALLOC(MTYPE_GRAPH_NODE, sizeof(struct graph_node));
|
||||||
|
|
||||||
node->from = vector_init(VECTOR_MIN_SIZE);
|
node->from = vector_init (VECTOR_MIN_SIZE);
|
||||||
node->to = vector_init(VECTOR_MIN_SIZE);
|
node->to = vector_init (VECTOR_MIN_SIZE);
|
||||||
node->data = data;
|
node->data = data;
|
||||||
node->del = del;
|
node->del = del;
|
||||||
|
|
||||||
@ -51,10 +59,10 @@ graph_delete_node (struct graph *graph, struct graph_node *node)
|
|||||||
struct graph_node *adj;
|
struct graph_node *adj;
|
||||||
|
|
||||||
// for all nodes that have an edge to us, remove us from their ->to
|
// for all nodes that have an edge to us, remove us from their ->to
|
||||||
for (int i = 0; i < vector_active (node->from); i++)
|
for (unsigned int i = 0; i < vector_active (node->from); i++)
|
||||||
{
|
{
|
||||||
adj = vector_slot (node->from, i);
|
adj = vector_slot (node->from, i);
|
||||||
for (int j = 0; j < vector_active (adj->to); j++)
|
for (unsigned int j = 0; j < vector_active (adj->to); j++)
|
||||||
if (vector_slot (adj->to, j) == node)
|
if (vector_slot (adj->to, j) == node)
|
||||||
vector_unset (adj->to, j);
|
vector_unset (adj->to, j);
|
||||||
|
|
||||||
@ -62,14 +70,14 @@ graph_delete_node (struct graph *graph, struct graph_node *node)
|
|||||||
if (vector_active (adj->to) == 0 &&
|
if (vector_active (adj->to) == 0 &&
|
||||||
vector_active (adj->from) == 0 &&
|
vector_active (adj->from) == 0 &&
|
||||||
adj != node)
|
adj != node)
|
||||||
graph_delete_node (adj);
|
graph_delete_node (graph, adj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for all nodes that we have an edge to, remove us from their ->from
|
// for all nodes that we have an edge to, remove us from their ->from
|
||||||
for (int i = 0; i < vector_active (node->to); i++)
|
for (unsigned int i = 0; i < vector_active (node->to); i++)
|
||||||
{
|
{
|
||||||
adj = vector_slot (node->to, i);
|
adj = vector_slot (node->to, i);
|
||||||
for (int j = 0; j < vector_active (adj->from); j++)
|
for (unsigned int j = 0; j < vector_active (adj->from); j++)
|
||||||
if (vector_slot (adj->from, j) == node)
|
if (vector_slot (adj->from, j) == node)
|
||||||
vector_unset (adj->from, j);
|
vector_unset (adj->from, j);
|
||||||
|
|
||||||
@ -77,7 +85,7 @@ graph_delete_node (struct graph *graph, struct graph_node *node)
|
|||||||
if (vector_active (adj->to) == 0 &&
|
if (vector_active (adj->to) == 0 &&
|
||||||
vector_active (adj->from) == 0 &&
|
vector_active (adj->from) == 0 &&
|
||||||
adj != node)
|
adj != node)
|
||||||
graph_delete_node (adj);
|
graph_delete_node (graph, adj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is a deletion callback, call it!
|
// if there is a deletion callback, call it!
|
||||||
@ -89,12 +97,12 @@ graph_delete_node (struct graph *graph, struct graph_node *node)
|
|||||||
vector_free (node->from);
|
vector_free (node->from);
|
||||||
|
|
||||||
// remove node from graph->nodes
|
// remove node from graph->nodes
|
||||||
for (int i = 0; i < vector_active (graph->nodes); i++)
|
for (unsigned int i = 0; i < vector_active (graph->nodes); i++)
|
||||||
if (vector_slot (graph->nodes, i) == node)
|
if (vector_slot (graph->nodes, i) == node)
|
||||||
vector_unset (graph->nodes, i);
|
vector_unset (graph->nodes, i);
|
||||||
|
|
||||||
// free the node itself
|
// free the node itself
|
||||||
free (node);
|
XFREE (MTYPE_GRAPH_NODE, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct graph_node *
|
struct graph_node *
|
||||||
@ -109,9 +117,9 @@ void
|
|||||||
graph_delete_graph (struct graph *graph)
|
graph_delete_graph (struct graph *graph)
|
||||||
{
|
{
|
||||||
// delete each node in the graph
|
// delete each node in the graph
|
||||||
for (int i = 0; i < vector_active (graph->nodes); i++)
|
for (unsigned int i = 0; i < vector_active (graph->nodes); i++)
|
||||||
graph_delete_node (vector_slot (graph->nodes, i));
|
graph_delete_node (graph, vector_slot (graph->nodes, i));
|
||||||
|
|
||||||
vector_free (graph->nodes);
|
vector_free (graph->nodes);
|
||||||
free (graph);
|
XFREE (MTYPE_GRAPH, graph);
|
||||||
}
|
}
|
||||||
|
15
lib/graph.h
15
lib/graph.h
@ -27,26 +27,23 @@
|
|||||||
|
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Graph structure.
|
|
||||||
*/
|
|
||||||
struct graph
|
struct graph
|
||||||
{
|
{
|
||||||
vector nodes; // all nodes in the graph
|
vector nodes;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Graph node / vertex.
|
|
||||||
*/
|
|
||||||
struct graph_node
|
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
|
||||||
|
|
||||||
void *data; // node data
|
void *data; // node data
|
||||||
void (*del) (void *data) // deletion callback
|
void (*del) (void *data); // deletion callback
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct graph *
|
||||||
|
graph_new (void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new node.
|
* Creates a new node.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user