match_variable (struct graph_node *, const char *);
/* matching functions */
+static enum matcher_rv matcher_result_value;
-struct cmd_element *
-match_command (struct graph_node *start, const char *line, struct list **argv)
+enum matcher_rv
+match_command (struct graph_node *start,
+ const char *line,
+ struct list **argvv,
+ struct cmd_element **el)
{
+ matcher_result_value = MATCHER_NO_MATCH;
// parse command
vector vline = cmd_make_strvec (line);
for (unsigned int i = 0; i < vector_active(start->children); i++)
{
- // call recursive builder on each starting child
- *argv = match_command_r(vector_slot(start->children, i), vline, 0);
- // if any of them succeed, return their argv
- // since all command DFA's must begin with a word, there can only be
- // one valid return value
- if (*argv) break;
+ // call recursive matcher on each starting child
+ *argvv = match_command_r(vector_slot(start->children, i), vline, 0);
+ if (*argvv) break;
}
- // walk the list, find the END_GN, return that
- if (*argv) {
+ if (*argvv) {
struct listnode *ln;
struct graph_node *gn;
- char buf[50];
- for (ALL_LIST_ELEMENTS_RO(*argv,ln,gn)) {
- describe_node(gn, buf, 50);
- fprintf(stderr, "%s[%d]\n", buf, gn->type);
- if (gn->type == END_GN)
- return gn->element;
- }
- assert(0);
+ for (ALL_LIST_ELEMENTS_RO(*argvv,ln,gn))
+ if (gn->type == END_GN) {
+ *el = gn->element;
+ break;
+ }
+ assert(el);
}
- return NULL;
+ return matcher_result_value;
}
/**
- * Matches a given input line against a DFA.
- *
- * Builds an argument list given a DFA and a matching input line. This function
- * should be passed the start node of the DFA, a matching input line, and the
- * index of the first token in the input line.
+ * Builds an argument list given a DFA and a matching input line.
*
* First the function determines if the node it is passed matches the first
* token of input. If it does not, it returns NULL. If it does match, then it
else continue;
}
- // else recurse on node
+ // else recurse on candidate child node
struct list *result = match_command_r (gn, vline, n+1);
// save the best match, subtle logic at play here
}
}
- if (ambiguous) {
- list_delete(bestmatch);
- bestmatch = NULL;
- }
-
if (bestmatch) {
- // copy current node, set arg and prepend to bestmatch
- struct graph_node *curr = copy_node(start);
- curr->arg = XSTRDUP(MTYPE_CMD_TOKENS, token);
- list_add_node_prev (bestmatch, bestmatch->head, curr);
+ if (ambiguous) {
+ list_delete(bestmatch);
+ bestmatch = NULL;
+ matcher_result_value = MATCHER_AMBIGUOUS;
+ }
+ else {
+ // copy current node, set arg and prepend to bestmatch
+ struct graph_node *curr = copy_node(start);
+ curr->arg = XSTRDUP(MTYPE_CMD_TOKENS, token);
+ list_add_node_prev (bestmatch, bestmatch->head, curr);
+ matcher_result_value = MATCHER_OK;
+ }
+ }
+ else {
+ if (n+1 == vector_active(vline) && matcher_result_value == MATCHER_NO_MATCH)
+ matcher_result_value = MATCHER_INCOMPLETE;
}
// cleanup
* next = set of all nodes reachable from all nodes in `matched`
*/
list_free (current);
-
cmd_free_strvec(vline);
+ matcher_result_value =
+ idx + 1 == vector_active(vline) && next->count ?
+ MATCHER_OK :
+ MATCHER_NO_MATCH;
+
return next;
}
{
switch (type)
{
- // some of these are mutually exclusive, order is important
+ // some of these are mutually exclusive, so they share
+ // the same precedence value
case IPV4_GN:
case IPV4_PREFIX_GN:
case IPV6_GN:
case IPV6_PREFIX_GN:
- case RANGE_GN:
case NUMBER_GN:
+ return 1;
+ case RANGE_GN:
return 2;
case WORD_GN:
return 3;
/* matcher result value. */
enum matcher_rv
{
- MATCHER_OK,
- MATCHER_COMPLETE,
- MATCHER_INCOMPLETE,
MATCHER_NO_MATCH,
+ MATCHER_INCOMPLETE,
MATCHER_AMBIGUOUS,
- MATCHER_EXCEED_ARGC_MAX
+ MATCHER_OK,
};
/* Completion match types. */
( (matcher_rv) == MATCHER_INCOMPLETE \
|| (matcher_rv) == MATCHER_NO_MATCH \
|| (matcher_rv) == MATCHER_AMBIGUOUS \
- || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
)
/**
*
* @param DFA to match against
* @param input string
- * @param pointer to argv pointer
- * @return cmd_element found, or NULL if there is no match.
+ * @param pointer which will be pointed at argv upon match
+ * @param pointer which will be pointed at matching cmd_element upon match
+ * @return result of matcher run
*/
-struct cmd_element *
-match_command (struct graph_node *, const char *, struct list **);
+enum matcher_rv
+match_command (struct graph_node *, const char *, struct list **, struct cmd_element **);
/**
* Compiles next-hops for a given line of user input.
"attempt to match input on DFA\n"
"command to match")
{
+ const char *line = argv_concat(argv, argc, 0);
+
struct list *argvv = NULL;
- const char *command = argv_concat(argv, argc, 0);
- struct cmd_element *element = match_command (nodegraph, command, &argvv);
+ struct cmd_element *element = NULL;
+ enum matcher_rv result = match_command (nodegraph, line, &argvv, &element);
if (element) {
fprintf(stderr, "Matched: %s\n", element->string);
fprintf(stderr, "%s -- %s\n", gn->text, gn->arg);
}
else {
- fprintf(stderr, "Returned NULL\n");
- return CMD_SUCCESS;
+ switch (result) {
+ case MATCHER_NO_MATCH:
+ fprintf(stderr, "%% Unknown command\n");
+ break;
+ case MATCHER_INCOMPLETE:
+ fprintf(stderr, "%% Incomplete command\n");
+ break;
+ case MATCHER_AMBIGUOUS:
+ fprintf(stderr, "%% Ambiguous command\n");
+ break;
+ default:
+ fprintf(stderr, "%% Unknown error\n");
+ break;
+ }
}
return CMD_SUCCESS;