/*
 * 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:
 */