--- /dev/null
+#include <command.h>
+#include <vector.h>
+
+enum tree_node_type
+{
+ WORD_TN,
+ IPV4_TN,
+ IPV4_PREFIX_TN,
+ IPV6_TN,
+ IPV6_PREFIX_TN,
+ VARIABLE_TN,
+ RANGE_TN,
+ NUMBER_TN,
+ SELECTOR_TN,
+ OPTION_TN
+}
+
+struct tree_node
+{
+ enum tree_node_type type;
+ vector children;
+ int leaf;
+ (int) (*func(struct cmd_info *, struct vty *, int, const char *[]));
+}
+
+void add_node(struct tree_node *parent, struct tree_node *child)
+{
+
+}
+
+// checks nodes for equivalence; definition of equivalence depends
+// on node type (WORD_TN strcmps words, etc)
+int cmp_node(struct tree_node *first, struct tree_node *second)
+{
+
+}
+
+int merge_tree(struct tree_node *first, struct tree_node *second)
+{
+
+}
--- /dev/null
+%{
+#include <string.h>
+#include <stdlib.h>
+
+#include "command.tab.h"
+%}
+
+WORD [a-z][-_a-z0-9]+
+IPV4 A\.B\.C\.D
+IPV4_PREFIX A\.B\.C\.D\/M
+IPV6 X:X::X:X
+IPV6_PREFIX X:X::X:X\/M
+VARIABLE [A-Z][A-Z_]+
+NUMBER [0-9]{1,20}
+RANGE \({NUMBER}\-{NUMBER}\)
+
+/* yytext shall be a pointer */
+%pointer
+%option noyywrap
+
+%%
+"<" return '<';
+">" return '>';
+
+[ /t] /* ignore whitespace */;
+{WORD} {yylval.string = strdup(yytext); return WORD;}
+{IPV4} {yylval.string = strdup(yytext); return IPV4;}
+{IPV4_PREFIX} {yylval.string = strdup(yytext); return IPV4_PREFIX;}
+{IPV6} {yylval.string = strdup(yytext); return IPV6;}
+{IPV6_PREFIX} {yylval.string = strdup(yytext); return IPV6_PREFIX;}
+{VARIABLE} {yylval.string = strdup(yytext); return VARIABLE;}
+{NUMBER} {yylval.integer = atoi(yytext); return NUMBER;}
+{RANGE} {yylval.string = strdup(yytext); return RANGE;}
+. {return yytext[0];}
+%%
--- /dev/null
+%{
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+extern int yylex(void);
+extern void yyerror(const char *);
+%}
+
+%union{
+ int integer;
+ char *string;
+}
+
+%token <string> WORD
+%token <string> IPV4
+%token <string> IPV4_PREFIX
+%token <string> IPV6
+%token <string> IPV6_PREFIX
+%token <string> VARIABLE
+%token <string> RANGE
+%token <integer> NUMBER
+
+/* grammar proper */
+%%
+
+start: sentence_root {printf("Matched sentence root\n");}
+ cmd_token_seq {printf("Matched sentence\n");};
+
+sentence_root: WORD {printf("Sentence root: %s\n", $1);};
+
+/* valid top level tokens */
+cmd_token: placeholder_token
+ | literal_token
+ | selector
+ | option
+ ;
+cmd_token_seq: /* empty */
+ | cmd_token_seq cmd_token;
+
+placeholder_token: IPV4 {printf("Matched placeholder\n");}
+ | IPV4_PREFIX {printf("Matched placeholder\n");}
+ | IPV6 {printf("Matched placeholder\n");}
+ | IPV6_PREFIX {printf("Matched placeholder\n");}
+ | VARIABLE {printf("Matched placeholder\n");}
+ | RANGE {printf("Matched placeholder\n");}
+
+literal_token: WORD
+ | NUMBER
+ ;
+/* range: '(' NUMBER '-' NUMBER ')' {printf("Matched range\n");}; */
+
+
+/* <selector|token> productions */
+selector: '<' selector_part '|'
+ selector_element '>' {printf("Matched selector\n");};
+selector_part: selector_part '|'
+ selector_element
+ | selector_element
+ {printf("Matched selector part\n");};
+selector_element: WORD
+ selector_token_seq
+selector_token_seq: /* empty */
+ | selector_token_seq
+ selector_token
+ ;
+selector_token: literal_token
+ | placeholder_token
+ | option
+ ;
+
+/* [option|set] productions */
+option: '[' option_part ']' {printf("Matched option\n");};
+option_part: option_part '|'
+ option_element_seq
+ | option_element_seq
+ ;
+option_element_seq: option_token
+ | option_element_seq
+ option_token
+ ;
+option_token: literal_token
+ | placeholder_token
+ ;
+%%
+
+int
+main (void)
+{
+ return yyparse ();
+}
+
+void yyerror(char const *message) {
+ printf("Grammar error: %s\n", message);
+ exit(EXIT_FAILURE);
+}