summaryrefslogtreecommitdiffstats
path: root/src/vlib/parse.c
blob: 1c4500ce85a857cdfe99a9bdf5d9001f664aab76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <vlib/parse.h>

#define PARSE_DEBUG 0

u16 word_type_index, number_type_index, eof_type_index, rule_eof_type_index,
  plus_type_index, minus_type_index, star_type_index, slash_type_index,
  lpar_type_index, rpar_type_index;

u8 *
format_vlib_parse_value (u8 * s, va_list * args)
{
  vlib_parse_main_t *pm = va_arg (*args, vlib_parse_main_t *);
  vlib_parse_type_t *type;
  vlib_parse_value_t *v;
  u16 type_index;

  s = format (s, "%d items:\n", vec_len (pm->parse_value));
  vec_foreach (v, pm->parse_value)
  {
    type_index = v->type;
    type = pool_elt_at_index (pm->parse_types, type_index);
    if (type->format_value)
      s = format (s, "[%d]: %U\n", v - pm->parse_value,
		  type->format_value, v);
    else
      s = format (s, "[%d]: (nofun)\n", v - pm->parse_value);
  }
  return s;
}

static u8 *
format_vlib_parse_match (u8 * s, va_list * args)
{
  vlib_parse_match_t m = va_arg (*args, vlib_parse_match_t);
  char *t = 0;
  switch (m)
    {
#define _(a) case VLIB_PARSE_##a: t = #a; break;
      foreach_parse_match_type
#undef _
    default:
      t = 0;
      break;
    }

  if (t)
    return format (s, "%s", t);
  else
    return format (s, "unknown 0x%x", m);
}

static u8 *
format_vlib_parse_item (u8 * s, va_list * args)
{
  vlib_parse_main_t *pm = va_arg (*args, vlib_parse_main_t *);
  vlib_parse_item_t *item = va_arg (*args, vlib_parse_item_t *);
  vlib_parse_type_t *type = pool_elt_at_index (pm->parse_types, item->type);

  if (item->type == word_type_index)
    s = format (s, "%s", item->value.as_pointer);
  else
    s = format (s, "<%s>", type->name);
  return s;
}

static u8 *
format_vlib_parse_graph (u8 * s, va_list * args)
{
  vlib_parse_main_t *pm = va_arg (*args, vlib_parse_main_t *);
  vlib_parse_graph_t *node = va_arg (*args, vlib_parse_graph_t *);
  vlib_parse_item_t *item;
  vlib_parse_type_t *type;

  /* $$$ hash table */
  /* *INDENT-OFF* */
  pool_foreach (type, pm->parse_types,
                ({
                  if (type->rule_index == node - pm->parse_graph)
                    s = format (s, "\n<%s>\n", type->name);
                }));
/* *INDENT-ON* */

  if (pm->root_index == (node - pm->parse_graph))
    s = format (s, "\n<root>\n");

  item = pool_elt_at_index (pm->parse_items, node->item);

  s = format (s, "[%d] %U ", node - pm->parse_graph,
	      format_vlib_parse_item, pm, item);

  if (node->peer == (u32) ~ 0)
    s = format (s, "peer nil  ");
  else
    s = format (s, "peer %4u ", node->peer);

  if (node->deeper == (u32) ~ 0)
    s = format (s, "deeper nil  ");
  else
    s = format (s, "deeper %4u ", node->deeper);

  return s;
}

void
dump_parse_graph (void)
{
  vlib_parse_main_t *pm = &vlib_parse_main;
  vlib_parse_graph_t *node;

  /* *INDENT-OFF* */
  pool_foreach (node, pm->parse_graph, ({
    fformat(stdout, "%U\n", format_vlib_parse_graph, pm, node);
  }));
/* *INDENT-ON* */
}

always_inline void
parse_cleanup_value (vlib_parse_main_t * pm, vlib_parse_value_t * pv)
{
  vlib_parse_type_t *type = pool_elt_at_index (pm->parse_types, pv->type);
  if (type->value_cleanup_function)
    type->value_cleanup_function (pv);
}

static void
parse_reset (vlib_parse_main_t * pm, u8 * input)
{
  vlib_lex_token_t *t;
  vlib_parse_value_t *pv;

  vlib_lex_reset (pm->lex_main, input);

  vec_foreach (t, pm->tokens) vlib_lex_cleanup_token (t);

  vec_foreach (pv, pm->parse_value) parse_cleanup_value (pm, pv);

  _vec_len (pm->parse_value) = 0;
  _vec_len (pm->tokens) = 0;
  pm->current_token_index = 0;
}

static void
parse_help (vlib_parse_main_t * pm, u32 index)
{
  vlib_parse_graph_t *node;
  vlib_parse_item_t *item;
  vlib_parse_type_t *type;
  vlib_main_t *vm = pm->vlib_main;
  u8 *help_input;
  int i;

  help_input = vec_dup (pm->lex_main->input_vector);

  for (i = vec_len (help_input) - 1; i >= 0; i--)
    if (help_input[i] == '?')
      {
	help_input[i] = 0;
	_vec_len (help_input) = i;
	break;
      }

  for (i = vec_len (help_input) - 1; i >= 0; i--)
    {
      if (help_input[i] != ' ' && help_input[i] != '\t')
	break;
      help_input[i] = 0;
      break;
    }
  _vec_len (help_input) = i + 1;

  while (index != (u32) ~ 0)
    {
      node = pool_elt_at_index (pm->parse_graph, index);
      item = pool_elt_at_index (pm->parse_items, node->item);
      type = pool_elt_at_index (pm->parse_types, item->type);

      if (item->type == eof_type_index && vec_len (pm->match_items) == 0)
	/* do nothing */ ;
      else if (item->type == word_type_index)
	vlib_cli_output (vm, "%s %s\n", help_input, item->value.as_pointer);
      else
	vlib_cli_output (vm, "%s <%s>\n", help_input, type->name);
      index = node->peer;
    }
  vec_free (help_input);
}

static vlib_parse_match_t
parse_eval_internal (vlib_parse_main_t * pm, u32 index)
{
  vlib_parse_graph_t *node;
  vlib_parse_item_t *item;
  vlib_parse_type_t *type;
  vlib_parse_value_t value, *pv;
  vlib_parse_match_t rv;
  u32 *partial_matches = 0;
  vlib_lex_token_t *t;
  u32 save_token_index = (u32) ~ 0, save_match_items = 0;
  int had_value = 0;

  if (pm->current_token_index >= vec_len (pm->tokens))
    return VLIB_PARSE_MATCH_FAIL;

  /* current token */
  t = vec_elt_at_index (pm->tokens, pm->current_token_index);

  /* Help ? */
  if (PREDICT_FALSE (t->token == VLIB_LEX_qmark))
    {
      parse_help (pm, index);
      _vec_len (pm->match_items) = 0;
      return VLIB_PARSE_MATCH_DONE;
    }

  /* Across all peers at this level of the parse graph */
  while (index != (u32) ~ 0)
    {
      node = pool_elt_at_index (pm->parse_graph, index);
      item = pool_elt_at_index (pm->parse_items, node->item);
      type = pool_elt_at_index (pm->parse_types, item->type);

      /*
       * Save the token index. We may have to back up several
       * trie plies. Type-specific match functions can consume
       * multiple tokens, and they may not be optimally careful
       */
      save_token_index = pm->current_token_index;
      save_match_items = vec_len (pm->match_items);
      vec_add1 (pm->match_items, node->item);

      if (PARSE_DEBUG > 1)
	clib_warning ("Try to match token %U against node %d",
		      format_vlib_lex_token, pm->lex_main, t, index);

      /* Call the type-specific match function */
      rv = type->match_function (pm, type, t, &value);

      if (PARSE_DEBUG > 1)
	clib_warning ("returned %U", format_vlib_parse_match, rv);

      switch (rv)
	{
	case VLIB_PARSE_MATCH_VALUE:
	  /*
	   * Matched, and returned a value to append to the
	   * set of args passed to the action function
	   */
	  value.type = item->type;
	  vec_add1 (pm->parse_value, value);
	  had_value = 1;
	  /* fallthrough */

	case VLIB_PARSE_MATCH_FULL:
	unambiguous_partial_match:
	  /* Consume the matched token */
	  pm->current_token_index++;

	  /* continue matching along this path */
	  rv = parse_eval_internal (pm, node->deeper);

	  /* this is not the right path */
	  if (rv == VLIB_PARSE_MATCH_FAIL)
	    {
	      if (had_value)
		{
		  /* Delete the value */
		  value = pm->parse_value[vec_len (pm->parse_value) - 1];
		  parse_cleanup_value (pm, &value);
		  _vec_len (pm->parse_value) -= 1;
		}
	      /* Continue with the next sibling */
	      pm->current_token_index = save_token_index;
	      _vec_len (pm->match_items) = save_match_items;
	      index = node->peer;
	      break;
	    }
	  return rv;

	case VLIB_PARSE_MATCH_PARTIAL:
	  /* Partial (substring) match, remember it but keep going */
	  vec_add1 (partial_matches, node - pm->parse_graph);
	  index = node->peer;
	  break;

	case VLIB_PARSE_MATCH_FAIL:
	  /* Continue with the next sibling */
	  index = node->peer;
	  _vec_len (pm->match_items) = save_match_items;
	  break;

	case VLIB_PARSE_MATCH_DONE:
	  /* Parse complete, invoke the action function */
	  if (PARSE_DEBUG > 0)
	    clib_warning ("parse_value: %U", format_vlib_parse_value, pm);

	  {
	    vlib_parse_eval_function_t *f = item->value.as_pointer;
	    if (f)
	      rv = f (pm, item, pm->parse_value);
	  }

	  vec_foreach (pv, pm->parse_value) parse_cleanup_value (pm, pv);
	  _vec_len (pm->parse_value) = 0;
	  _vec_len (pm->match_items) = 0;
	  return rv;

	case VLIB_PARSE_MATCH_AMBIGUOUS:
	case VLIB_PARSE_MATCH_EVAL_FAIL:
	case VLIB_PARSE_MATCH_RULE:
	  _vec_len (pm->match_items) = save_match_items;
	  return rv;
	}
    }

  /*
   * Out of siblings. If we have exactly one partial match
   * we win
   */
  if (vec_len (partial_matches) == 1)
    {
      index = partial_matches[0];
      node = pool_elt_at_index (pm->parse_graph, index);
      vec_free (partial_matches);
      goto unambiguous_partial_match;
    }

  /* Ordinary loser */
  rv = VLIB_PARSE_MATCH_FAIL;

  /* Ambiguous loser */
  if (vec_len (partial_matches) > 1)
    {
      vec_free (partial_matches);
      rv = VLIB_PARSE_MATCH_AMBIGUOUS;
    }

  _vec_len (pm->match_items) = save_match_items;
  return rv;
}

vlib_parse_match_t
rule_match (vlib_parse_main_t * pm, vlib_parse_type_t * type,
	    vlib_lex_token_t * t, vlib_parse_value_t * valuep)
{
  vlib_parse_match_t rv;
  static int recursion_level;

  if (PARSE_DEBUG > 1)
    clib_warning ("[%d]: try to match type %s graph index %d",
		  recursion_level, type->name, type->rule_index);
  recursion_level++;
  rv = parse_eval_internal (pm, type->rule_index);
  recursion_level--;

  /* Break the recusive unwind here... */
  if (rv == VLIB_PARSE_MATCH_RULE)
    {
      if (PARSE_DEBUG > 1)
	clib_warning ("[%d]: type %s matched", recursion_level, type->name);

      return VLIB_PARSE_MATCH_FULL;
    }
  else
    {
      if (PARSE_DEBUG > 1)
	clib_warning ("[%d]: type %s returns %U", recursion_level, type->name,
		      format_vlib_parse_match, rv);
    }
  return rv;
}

static int
parse_eval (vlib_parse_main_t * pm, u8 * input)
{
  vlib_lex_token_t *t;

  parse_reset (pm, input);

  /* Tokenize the entire input vector */
  do
    {
      vec_add2 (pm->tokens, t, 1);
      vlib_lex_get_token (pm->lex_main, t);
    }
  while (t->token != VLIB_LEX_eof);

  /* Feed it to the parser */
  return parse_eval_internal (pm, pm->root_index);
}

/* Temporary vlib stub */
vlib_parse_match_t
vlib_parse_eval (u8 * input)
{
  return parse_eval (&vlib_parse_main, input);
}

u16
parse_type_find_or_create (vlib_parse_main_t * pm, vlib_parse_type_t * t)
{
  uword *p;
  vlib_parse_type_t *n;
  u8 *name_copy;

  p = hash_get_mem (pm->parse_type_by_name_hash, t->name);
  if (p)
    return p[0];

  pool_get (pm->parse_types, n);
  *n = *t;
  n->rule_index = (u32) ~ 0;

  name_copy = format (0, "%s%c", n->name, 0);

  hash_set_mem (pm->parse_type_by_name_hash, name_copy, n - pm->parse_types);
  return n - pm->parse_types;
}

u16
parse_type_find_by_name (vlib_parse_main_t * pm, char *name)
{
  uword *p;

  p = hash_get_mem (pm->parse_type_by_name_hash, name);
  if (p)
    return p[0];

  return (u16) ~ 0;
}

u32
parse_item_find_or_create (vlib_parse_main_t * pm, vlib_parse_item_t * item)
{
  uword *p;
  vlib_parse_item_t *i;

  /* Exact match the entire item */
  p = mhash_get (&pm->parse_item_hash, item);
  if (p)
    return p[0];

  pool_get (pm->parse_items, i);
  *i = *item;

  mhash_set (&pm->parse_item_hash, i, i - pm->parse_items, 0);
  return i - pm->parse_items;
}

static void
parse_type_and_graph_init (vlib_parse_main_t * pm)
{
  u32 eof_index;
  vlib_parse_type_t type;
  vlib_parse_item_t item;

  memset (&type, 0, sizeof (type));

#define foreach_token_type                      \
  _ (eof)                                       \
  _ (rule_eof)                                  \
  _ (word)                                      \
  _ (number)                                    \
  _ (plus)                                      \
  _ (minus)                                     \
  _ (star)                                      \
  _ (slash)                                     \
  _ (lpar)                                      \
  _ (rpar)

#define _(a) a##_type_index = parse_type_find_by_name (pm, #a);
  foreach_token_type
#undef _
    memset (&item, 0, sizeof (item));
  item.type = eof_type_index;

  eof_index = parse_item_find_or_create (pm, &item);
  pm->root_index = (u32) ~ 0;

#if 0
  pool_get (pm->parse_graph, g);
  memset (g, 0xff, sizeof (*g));
  g->item = eof_index;
  pm->root_index = 0;
#endif
}



static void
tokenize (vlib_parse_main_t * pm, parse_registration_t * pr)
{
  vlib_lex_token_t *t;
  pm->register_input = format (pm->register_input,
			       "%s%c", pr->initializer, 0);

  parse_reset (pm, pm->register_input);

  do
    {
      vec_add2 (pm->tokens, t, 1);
      vlib_lex_get_token (pm->lex_main, t);
    }
  while (t->token != VLIB_LEX_eof);
  _vec_len (pm->register_input) = 0;
}

static int
is_typed_rule (vlib_parse_main_t * pm)
{
  vlib_lex_token_t *t = vec_elt_at_index (pm->tokens, 0);

  /* <mytype> = blah blah blah */
  if (vec_len (pm->tokens) >= 4
      && t[0].token == VLIB_LEX_lt
      && t[1].token == VLIB_LEX_word
      && t[2].token == VLIB_LEX_gt && t[3].token == VLIB_LEX_equals)
    return 1;
  return 0;
}

static int
token_matches_graph_node (vlib_parse_main_t * pm,
			  vlib_lex_token_t * t,
			  vlib_parse_graph_t * node,
			  vlib_parse_item_t * item,
			  vlib_parse_type_t * type, u32 * token_increment)
{
  /* EOFs don't match */
  if (t->token == VLIB_LEX_eof)
    return 0;

  /* New chain element is a word */
  if (t->token == VLIB_LEX_word)
    {
      /* but the item in hand is not a word */
      if (item->type != word_type_index)
	return 0;

      /* Or it's not this particular word */
      if (strcmp (t->value.as_pointer, item->value.as_pointer))
	return 0;
      *token_increment = 1;
      return 1;
    }
  /* New chain element is a type-name: < TYPE-NAME > */
  if (t->token == VLIB_LEX_lt)
    {
      u16 token_type_index;

      /* < TYPE > */
      if (t[1].token != VLIB_LEX_word || t[2].token != VLIB_LEX_gt)
	{
	  clib_warning (0, "broken type name in '%s'", pm->register_input);
	  return 0;
	}

      token_type_index = parse_type_find_by_name (pm, t[1].value.as_pointer);
      if (token_type_index == (u16) ~ 0)
	{
	  clib_warning (0, "unknown type '%s'", t[1].value.as_pointer);
	  return 0;
	}

      /* Its a known type but does not match. */
      if (item->type != token_type_index)
	return 0;

      *token_increment = 3;
      return 1;
    }
  clib_warning ("BUG: t->token = %d", t->token);
  return 0;
}

u32
generate_subgraph_from_tokens (vlib_parse_main_t * pm,
			       vlib_lex_token_t * t,
			       u32 * new_subgraph_depth,
			       parse_registration_t * pr, int not_a_rule)
{
  vlib_parse_graph_t *g, *last_g;
  vlib_parse_item_t new_item;
  u32 rv = (u32) ~ 0, new_item_index, last_index = (u32) ~ 0;
  u16 token_type_index;
  u32 depth = 0;

  while (t < pm->tokens + vec_len (pm->tokens))
    {
      memset (&new_item, 0, sizeof (new_item));

      if (t->token == VLIB_LEX_word)
	{
	  new_item.type = word_type_index;
	  new_item.value.as_pointer = vec_dup ((u8 *) t->value.as_pointer);
	  new_item_index = parse_item_find_or_create (pm, &new_item);
	  t++;
	}
      else if (t->token == VLIB_LEX_lt)
	{
	  if (t[1].token != VLIB_LEX_word || t[2].token != VLIB_LEX_gt)
	    {
	      clib_warning ("broken type name in '%s'", pm->register_input);
	      goto screwed;
	    }
	  token_type_index = parse_type_find_by_name (pm,
						      t[1].value.as_pointer);
	  if (token_type_index == (u16) ~ 0)
	    {
	      clib_warning ("unknown type 2 '%s'", t[1].value.as_pointer);
	      goto screwed;
	    }

	  new_item.type = token_type_index;
	  new_item.value.as_pointer = 0;
	  new_item_index = parse_item_find_or_create (pm, &new_item);
	  t += 3;		/* skip < <type-name> and > */
	}
      else if (t->token == VLIB_LEX_eof)
	{
	screwed:
	  new_item.type = not_a_rule ? eof_type_index : rule_eof_type_index;
	  new_item.value.as_pointer = pr->eof_match;
	  new_item_index = parse_item_find_or_create (pm, &new_item);
	  t++;
	}
      else
	{
	  clib_warning ("unexpected token %U index %d in '%s'",
			format_vlib_lex_token, pm->lex_main, t,
			t - pm->tokens, pm->register_input);
	  goto screwed;
	}

      pool_get (pm->parse_graph, g);
      memset (g, 0xff, sizeof (*g));
      g->item = new_item_index;
      depth++;

      if (rv == (u32) ~ 0)
	{
	  rv = g - pm->parse_graph;
	  last_index = rv;
	}
      else
	{
	  last_g = pool_elt_at_index (pm->parse_graph, last_index);
	  last_index = last_g->deeper = g - pm->parse_graph;
	}
    }
  *new_subgraph_depth = depth;
  return rv;
}

static u32
measure_depth (vlib_parse_main_t * pm, u32 index)
{
  vlib_parse_graph_t *node;
  vlib_parse_item_t *item;
  u32 max = 0;
  u32 depth;

  if (index == (u32) ~ 0)
    return 0;

  node = pool_elt_at_index (pm->parse_graph, index);
  item = pool_elt_at_index (pm->parse_items, node->item);

  if (item->type == eof_type_index)
    return 1;

  while (index != (u32) ~ 0)
    {
      node = pool_elt_at_index (pm->parse_graph, index);
      depth = measure_depth (pm, node->deeper);
      if (max < depth)
	max = depth;
      index = node->peer;
    }

  return max + 1;
}

static void
add_subgraph_to_graph (vlib_parse_main_t * pm,
		       u32 last_matching_index,
		       u32 graph_root_index,
		       u32 new_subgraph_index, u32 new_subgraph_depth)
{
  vlib_parse_graph_t *parent_node;
  int new_subgraph_longest = 1;
  u32 current_peer_index;
  u32 current_depth;
  vlib_parse_graph_t *current_peer = 0;
  vlib_parse_graph_t *new_subgraph_node =
    pool_elt_at_index (pm->parse_graph, new_subgraph_index);

  /*
   * Case 1: top-level peer. Splice into the top-level
   * peer chain according to rule depth
   */
  if (last_matching_index == (u32) ~ 0)
    {
      u32 index = graph_root_index;
      while (1)
	{
	  current_peer = pool_elt_at_index (pm->parse_graph, index);
	  current_depth = measure_depth (pm, index);
	  if (current_depth < new_subgraph_depth
	      || current_peer->peer == (u32) ~ 0)
	    break;
	  index = current_peer->peer;
	}
      new_subgraph_node->peer = current_peer->peer;
      current_peer->peer = new_subgraph_index;
      return;
    }

  parent_node = pool_elt_at_index (pm->parse_graph, last_matching_index);
  current_peer_index = parent_node->deeper;

  while (current_peer_index != (u32) ~ 0)
    {
      current_peer = pool_elt_at_index (pm->parse_graph, current_peer_index);
      current_depth = measure_depth (pm, current_peer_index);
      if (current_depth < new_subgraph_depth)
	break;
      new_subgraph_longest = 0;
      current_peer_index = current_peer->peer;
    }

  ASSERT (current_peer);

  if (new_subgraph_longest)
    {
      new_subgraph_node->peer = parent_node->deeper;
      parent_node->deeper = new_subgraph_index;
    }
  else
    {
      new_subgraph_node->peer = current_peer->peer;
      current_peer->peer = new_subgraph_index;
    }
}

static clib_error_t *
parse_register_one (vlib_parse_main_t * pm, parse_registration_t * pr)
{
  u32 graph_root_index;
  u16 subgraph_type_index = (u16) ~ 0;
  vlib_parse_type_t *subgraph_type = 0;
  vlib_lex_token_t *t;
  vlib_parse_graph_t *node;
  u32 node_index, last_index, token_increment, new_subgraph_index;
  u32 new_subgraph_depth, last_matching_index;
  vlib_parse_item_t *item;
  vlib_parse_type_t *type;

  int use_main_graph = 1;

  tokenize (pm, pr);

  /* A typed rule? */
  if (is_typed_rule (pm))
    {
      /* Get the type and its current subgraph root, if any */
      t = vec_elt_at_index (pm->tokens, 1);
      subgraph_type_index = parse_type_find_by_name (pm, t->value.as_pointer);
      if (subgraph_type_index == (u16) ~ 0)
	return clib_error_return (0, "undeclared type '%s'",
				  t->value.as_pointer);
      subgraph_type =
	pool_elt_at_index (pm->parse_types, subgraph_type_index);
      graph_root_index = subgraph_type->rule_index;
      /* Skip "mytype> = */
      t += 3;
      use_main_graph = 0;
    }
  else
    {
      /* top-level graph */
      graph_root_index = pm->root_index;
      t = vec_elt_at_index (pm->tokens, 0);
    }

  last_matching_index = (u32) ~ 0;
  last_index = node_index = graph_root_index;

  /* Find the first token which isn't already being parsed */
  while (t < pm->tokens + vec_len (pm->tokens) && node_index != (u32) ~ 0)
    {
      node = pool_elt_at_index (pm->parse_graph, node_index);
      item = pool_elt_at_index (pm->parse_items, node->item);
      type = pool_elt_at_index (pm->parse_types, item->type);
      last_index = node_index;

      if (token_matches_graph_node
	  (pm, t, node, item, type, &token_increment))
	{
	  t += token_increment;
	  last_matching_index = node_index;
	  node_index = node->deeper;
	}
      else
	node_index = node->peer;
    }

  new_subgraph_index =
    generate_subgraph_from_tokens (pm, t, &new_subgraph_depth, pr,
				   use_main_graph);

  /* trivial cases: first graph node or first type rule */
  if (graph_root_index == (u32) ~ 0)
    {
      if (use_main_graph)
	pm->root_index = new_subgraph_index;
      else
	subgraph_type->rule_index = new_subgraph_index;
      return 0;
    }

  add_subgraph_to_graph (pm, last_matching_index, graph_root_index,
			 new_subgraph_index, new_subgraph_depth);
  return 0;
}

static clib_error_t *
parse_register (vlib_main_t * vm,
		parse_registration_t * lo,
		parse_registration_t * hi, vlib_parse_main_t * pm)
{
  parse_registration_t *pr;

  for (pr = lo; pr < hi; pr = vlib_elf_section_data_next (pr, 0))
    vec_add1 (pm->parse_registrations, pr);

  return 0;
}

static clib_error_t *
parse_register_one_type (vlib_parse_main_t * pm, vlib_parse_type_t * rp)
{
  (void) parse_type_find_or_create (pm, (vlib_parse_type_t *) rp);
  return 0;
}

static clib_error_t *
parse_type_register (vlib_main_t * vm,
		     vlib_parse_type_t * lo,
		     vlib_parse_type_t * hi, vlib_parse_main_t * pm)
{
  clib_error_t *error = 0;
  vlib_parse_type_t *ptr;

  for (ptr = lo; ptr < hi; ptr = vlib_elf_section_data_next (ptr, 0))
    {
      error = parse_register_one_type (pm, ptr);
      if (error)
	goto done;
    }

done:
  return error;
}

clib_error_t *vlib_stdlex_init (vlib_main_t * vm) __attribute__ ((weak));
clib_error_t *
vlib_stdlex_init (vlib_main_t * vm)
{
  (void) vlib_lex_add_table ("ignore_everything");
  return 0;
}

static int
compute_rule_length (parse_registration_t * r)
{
  int length, i;
  vlib_parse_main_t *pm = &vlib_parse_main;

  if (r->rule_length)
    return r->rule_length;

  length = 0;

  tokenize (pm, r);
  length = vec_len (pm->tokens);

  /* Account for "<foo> = " in "<foo> = bar" etc. */
  if (is_typed_rule (pm))
    length -= 2;

  for (i = 0; i < vec_len (pm->tokens); i++)
    {
      switch (pm->tokens[i].token)
	{
	case VLIB_LEX_lt:
	case VLIB_LEX_gt:
	  length -= 1;

	default:
	  break;
	}
    }

  ASSERT (length > 0);
  r->rule_length = length;
  return length;
}

static int
rule_length_compare (parse_registration_t * r1, parse_registration_t * r2)
{
  compute_rule_length (r1);
  compute_rule_length (r2);
  /* Descending sort */
  return r2->rule_length - r1->rule_length;
}


static clib_error_t *
parse_init (vlib_main_t * vm)
{
  vlib_parse_main_t *pm = &vlib_parse_main;
  vlib_lex_main_t *lm = &vlib_lex_main;
  vlib_elf_section_bounds_t *b, *bounds;
  clib_error_t *error = 0;
  parse_registration_t *rule;
  int i;

  if ((error = vlib_call_init_function (vm, lex_onetime_init)))
    return error;

  if ((error = vlib_stdlex_init (vm)))
    return error;

  if ((error = vlib_call_init_function (vm, parse_builtin_init)))
    return error;

  pm->vlib_main = vm;
  pm->lex_main = lm;

  mhash_init (&pm->parse_item_hash, sizeof (u32), sizeof (vlib_parse_item_t));
  pm->parse_type_by_name_hash = hash_create_string (0, sizeof (u32));

  vec_validate (pm->parse_value, 16);
  vec_validate (pm->tokens, 16);
  vec_validate (pm->register_input, 32);
  vec_validate (pm->match_items, 16);

  _vec_len (pm->parse_value) = 0;
  _vec_len (pm->tokens) = 0;
  _vec_len (pm->register_input) = 0;
  _vec_len (pm->match_items) = 0;

  bounds = vlib_get_elf_section_bounds (vm, "parse_type_registrations");
  vec_foreach (b, bounds)
  {
    error = parse_type_register (vm, b->lo, b->hi, pm);
    if (error)
      break;
  }
  vec_free (bounds);

  parse_type_and_graph_init (pm);

  bounds = vlib_get_elf_section_bounds (vm, "parse_registrations");
  vec_foreach (b, bounds)
  {
    error = parse_register (vm, b->lo, b->hi, pm);
    if (error)
      break;
  }
  vec_free (bounds);

  vec_sort_with_function (pm->parse_registrations, rule_length_compare);

  for (i = 0; i < vec_len (pm->parse_registrations); i++)
    {
      rule = pm->parse_registrations[i];
      parse_register_one (pm, rule);
    }

  return error;
}

VLIB_INIT_FUNCTION (parse_init);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */