// Copyright, 2001-2003, Astra Network Inc.  All Rights Reserved

// This source code has been published by Astra Network Inc. However, any
// use, reproduction, modification, distribution or transfer of this
// software, or any software which includes or is based upon any of this
// code, is only permitted if expressly authorized by a written license
// agreement from Astra. Contact your Astra representative directly for
// more information.

/*
 * node.c - A set of helper functions used to create nodes which
 *          correspond to statements / expressions in procs.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "defines.h"

state_node_t *
op (opcode_t opcode, unsigned int num_ops, ...)
{
  va_list args;
  state_node_t *new_op;
  state_node_t *tempnode;
  unsigned int index;
  unsigned int new_ops = num_ops;

  /* Optimization -- account for yacc's recursive tendencies.  We DON'T
     want recursive lists of opcodes for every proc.  Flatten them. */

  if (opcode == OP_OPLIST)
    {
      va_start (args, num_ops);
      for (index = 0; index < num_ops; index++)
	{
	  tempnode = va_arg (args, state_node_t *);
	  if (tempnode != NULL &&
	      tempnode->type == STATE_OP &&
	      tempnode->d.op.opcode == OP_OPLIST)
	    {
	      new_ops += (tempnode->d.op.num_ops - 1);
	    }
	}
    }

  new_op = calloc (1, sizeof (state_node_t));
  if (!new_op)
    {
      yyfatalerror ("Out of memory\n");
    }

  new_op->type = STATE_OP;

  errno = 0;
  if (new_ops > 0)
    {
      new_op->d.op.nodes = malloc (sizeof (state_node_t *) * new_ops);
      if (!new_op->d.op.nodes)
	{
	  yyfatalerror ("Out of memory allocating %d bytes: errno %d\n",
			sizeof (state_node_t *) * new_ops, errno);
	}
    }

  new_op->d.op.opcode = opcode;
  new_op->d.op.num_ops = new_ops;

  va_start (args, num_ops);

  index = 0;
  while (index < new_ops)
    {
      tempnode = va_arg (args, state_node_t *);

      /* Remember -- flatten oplists! */
      if (opcode == OP_OPLIST &&
	  tempnode != NULL &&
	  tempnode->type == STATE_OP && tempnode->d.op.opcode == OP_OPLIST)
	{
	  memcpy (&new_op->d.op.nodes[index], tempnode->d.op.nodes,
		  sizeof (state_node_t *) * tempnode->d.op.num_ops);
	  index += tempnode->d.op.num_ops;
	  free (tempnode->d.op.nodes);
	  free (tempnode);
	}
      else
	{
	  new_op->d.op.nodes[index] = tempnode;
	  index++;
	}
    }
  va_end (args);
  return (new_op);
}

state_node_t *
con (int number)
{
  state_node_t *new_node;

  new_node = malloc (sizeof (state_node_t));
  if (new_node == NULL)
    yyfatalerror ("Out of memory\n");

  new_node->type = STATE_CONST;
  new_node->d.con.value = number;

  return (new_node);
}

state_node_t *
ident (char *name)
{
  state_node_t *new_node;

  new_node = malloc (sizeof (state_node_t));
  if (new_node == NULL)
    yyfatalerror ("Out of memory\n");

  new_node->type = STATE_IDENT;
  new_node->d.ident.name = strdup (name);

  if (new_node->d.ident.name == NULL)
    {
      yyfatalerror ("Out of memory\n");
    }

  return (new_node);
}
