// 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.

#include <stdio.h>
#include <strings.h>
#include "defines.h"
#include "version.h"

extern system_t *sys;

unsigned int stacksize = 0;
sp_t stackpol = SP_NONE;

/*
 * Internally-generated nebuloid naming conventions:
 *
 * All internally-generated names begin with a dot, because it is
 * impossible in our language for the *user* to create any name
 * beginning with a dot.  The ".alive" nebuloid is instantiated for
 * every process to perform the checks to see whether or not the process
 * is still running.  The internal procedures for starting, restarting,
 * dependfails, etc. are called "process..start", etc. since procs are
 * named "process.procname" and we wish to avoid conflicts.  As well, with
 * the addition of variable dependencies, the code generator creates
 * internal VARs with names corresponding to the various processes
 * that it modally depends on.  These are named "._process" in case
 * someone decides to call a process polldeath or something (not likely,
 * but legal.)  That's about all.
 */

void
generate_system (FILE * asmfile, system_t * syst)
{
  policy_t *pol;
  process_t *process;
  nebuloid_t *neb;
  proc_t *proc;
  dependency_t *dep;
  namelist_t *nlist;
  argtype_t *arglist;
  sighandler_t *sig;

  /* Print out compiler version and module definitions */
  fprintf (asmfile, ":olc_ver:%d:\n", VERSION);

  nlist = name_list;
  while (nlist && nlist->modules.name)
    {
      fprintf (asmfile, ":proto:%s,", nlist->modules.name);
      arglist = get_argtype (nlist->modules.name, AT_STATIC);
      while (arglist)
	{
	  if (arglist->type == AT_NUM)
	    fprintf (asmfile, "n");
	  else
	    fprintf (asmfile, "s");
	  arglist = arglist->next;
	}
      fprintf (asmfile, ",");
      arglist = get_argtype (nlist->modules.name, AT_DYNAMIC);
      while (arglist)
	{
	  if (arglist->type == AT_NUM)
	    fprintf (asmfile, "n");
	  else
	    fprintf (asmfile, "s");
	  arglist = arglist->next;
	}
      fprintf (asmfile, ":\n");
      nlist = nlist->next;
    }

  if (stacksize == 0)
    {
      if (stackpol == SP_GROW)
	fprintf (asmfile, ":stack:256,grow:\n");
      else
	fprintf (asmfile, ":stack:256,die:\n");
    }
  else
    {
      if (stackpol == SP_GROW)
	fprintf (asmfile, ":stack:%u,grow:\n", stacksize);
      else
	fprintf (asmfile, ":stack:%u,die:\n", stacksize);
    }

  /* XXX: Should I be setting the alarms AFTER starting everything? * */
  pol = syst->policy;
  while (pol)
    {
      switch (pol->type)
	{
	case PT_PROCESS:
	  break;
	case PT_NEBULOID:
	  neb = pol->pol_data;
	  generate_arguments (asmfile, NULL, neb->args);
	  fprintf (asmfile, "#0 &%s (%s) inst\n", neb->plugin, neb->name);
	  if (neb->interval != 0)
	    {
	      fprintf (asmfile, "#%u /%s alarm\n", neb->interval, neb->name);
	    }
	  break;
	case PT_PROC:
	  proc = pol->pol_data;
	  if (proc->interval != 0)
	    {
	      fprintf (asmfile, "#%u /%s alarm\n", proc->interval,
		       proc->name);
	    }
	  break;
	case PT_SIGNAL:
	  sig = pol->pol_data;
	  fprintf (asmfile, "#%u /.sigusr%u signal\n",
		   (sig->type == SH_SIGUSR1) ? 1 : 2,
		   (sig->type == SH_SIGUSR1) ? 1 : 2);
	  break;
	default:
	  fprintf (stderr, "Impossible policy type in system, %d\n",
		   pol->type);
	  break;
	}
      pol = pol->next;
    }

  /** Walk the dependency tree, figuring out what order to start processes in.
   **/

  dep = syst->depends;
  push_start_dependencies (syst->depends, syst);

  process = pop_start_process ();
  if (process)
    fprintf (asmfile, "/%s..start call drop\n", process->name);

  while (process)
    {
      process->nextstart = pop_start_process ();
      process = process->nextstart;
    }

  fprintf (asmfile, "#0 return\n");

  /* Generate code for all policy */
  generate_policy (asmfile, NULL, syst->policy);
}

void
generate_policy (FILE * fp, process_t * proc, policy_t * pol)
{
  while (pol)
    {
      switch (pol->type)
	{
	case PT_PROCESS:
	  proc = pol->pol_data;
	  generate_policy (fp, proc, proc->policy);
	  proc = NULL;
	  break;
	case PT_NEBULOID:
	  generate_nebuloid (fp, proc, pol->pol_data);
	  break;
	case PT_PROC:
	  generate_proc (fp, proc, pol->pol_data);
	  break;
	case PT_START:
	  generate_start (fp, proc, pol->pol_data);
	  break;
	case PT_RESTART:
	  generate_restart (fp, proc, pol->pol_data);
	  break;
	case PT_DEPENDFAIL:
	  generate_dependfail (fp, proc, pol->pol_data);
	  break;
	case PT_ISALIVE:
	  generate_isalive (fp, proc, pol->pol_data);
	  break;
	case PT_ISUP:
	  generate_isup (fp, proc, pol->pol_data);
	  break;
	case PT_SIGNAL:
	  generate_sighandler (fp, proc, pol->pol_data);
	  break;
	}
      pol = pol->next;
    }
}

void
generate_start (FILE * fp, process_t * proc, start_t * start)
{
  policy_t *pol;
  nebuloid_t *neb;
  proc_t *procedure;
  dependency_t *dep;
  isup_t *isup;

  fprintf (fp, "\n@%s..start\n", proc->name);
  fprintf (fp, "/%s..startproc call drop\n", proc->name);
  fprintf (fp, "getpid { ; if it started\n");
  fprintf (fp, "  /%s..startsuccess call drop\n", proc->name);
  fprintf (fp, "}{ ; if it didn't start\n");
  isup = find_policy_bytype (proc->policy, PT_ISUP);
  if (isup)
    fprintf (fp, "  #%u /%s..isup alarm", isup->interval, proc->name);
  // XXX: What to do if it fails and there is no isup?
  fprintf (fp, "} return\n\n");

  fprintf (fp, "@%s..startsuccess\n", proc->name);
  if (proc->interval > 0) // Allow processes to only be polled via dependencies
    fprintf (fp, "#%u /%s..polldeath alarm\n", proc->interval, proc->name);
  if (!find_policy_bytype (proc->policy, PT_ISALIVE))
    fprintf (fp, "#1 &alive (.alive) inst\n");
  // XXX: VARIABLE NUMBER OF DEPENDANTS
  fprintf (fp, "#1 #1 &var (.dependants) inst\n");

  pol = proc->policy;

  while (pol)
    {
      switch (pol->type)
	{
	case PT_NEBULOID:
	  neb = pol->pol_data;
	  generate_arguments (fp, proc, neb->args);
	  fprintf (fp, "#1 &%s (%s) inst\n", neb->plugin, neb->name);
	  if (neb->interval != 0)
	    {
	      fprintf (fp, "#%u /%s.%s alarm\n", neb->interval, proc->name,
		       neb->name);
	    }
	  break;
	case PT_PROC:
	  procedure = pol->pol_data;
	  if (procedure->interval != 0)
	    {
	      fprintf (fp, "#%u /%s.%s alarm\n", procedure->interval,
		       proc->name, procedure->name);
	    }
	  break;
	case PT_SIGNAL:
	/** The only possible signal handler in a process is SIGCHLD **/
	  fprintf (fp, "/%s..sigchld sigchild\n", proc->name);
	default:
	  break;
	}
      pol = pol->next;
    }

  if (proc->nextstart)
    fprintf (fp, "  /%s..start call drop ; call next to be started\n",
	     proc->nextstart->name);

  fprintf (fp, "#1 return\n\n");

  fprintf (fp, "@%s..startproc\n", proc->name);
  fprintf (fp, "%%%s\n", proc->name);

  // Create variables for each dynamic dependency
  dep = find_dyn_depends (proc->context);
  while (dep)
    {
      // XXX: Already-started static dependencies?
      fprintf (fp, "#0 #1 &var (._%s) inst\n", dep->name);
      dep = dep->next;
    }

  fprintf (fp, "%%%s\n", proc->name);

  generate_op (fp, proc, start->statements);

  fprintf (fp, "#0 return\n");

  /** POLLDEATH **/

  fprintf (fp, "\n@%s..polldeath\n", proc->name);
  fprintf (fp, "; check for death of dependants\n");
  dep = proc->depends;
  while (dep)
    {
      fprintf (fp, "/%s..checkalive call drop\n", dep->name);
      dep = dep->next;
    }
  fprintf (fp, "%%%s\n", proc->name);
  fprintf (fp, "(.dependants) get     ; If still depended on\n");
  fprintf (fp, "{\n");
  fprintf (fp, "  #%u /%s..polldeath alarm ; keep checking for death\n",
	   proc->interval, proc->name);
  fprintf (fp, "  /%s..checkalive call drop ; check for death\n", proc->name);
  fprintf (fp, "}\n");
  fprintf (fp,
	   "{ /%s..restart call drop } drop ; Clean yourself up if not depended on\n",
	   proc->name);
  fprintf (fp, "#0 return\n");

  /** CHECKALIVE **/
  fprintf (fp, "\n@%s..checkalive\n", proc->name);
  fprintf (fp, "%%%s\n", proc->name);
  fprintf (fp, "getpid ! { ; If we've already made it not alive\n");
  fprintf (fp,
	   "#0 return } drop ; We're already in the process of restarting\n");
  fprintf (fp, "/%s..isalive call ! ; Check if not alive\n", proc->name);
  fprintf (fp, "{\n");
  fprintf (fp, "  /%s..restart call drop\n", proc->name);
  fprintf (fp, "} drop\n");
  fprintf (fp, "#0 return\n");

  /** DEFAULT ISALIVE **/
  if (!find_policy_bytype (proc->policy, PT_ISALIVE))
    {
      fprintf (fp, "\n@%s..isalive\n", proc->name);
      fprintf (fp, "%%%s\n", proc->name);
      fprintf (fp, "(.alive) get return\n");
    }
}

void
generate_nebuloid (FILE * fp, process_t * proc, nebuloid_t * nebuloid)
{
  if (nebuloid->interval != 0)
    {
      if (proc)
	{
	  fprintf (fp, "\n@%s.%s\n", proc->name, nebuloid->name);
	  fprintf (fp, "#%u /%s.%s alarm\n", nebuloid->interval, proc->name,
		   nebuloid->name);
	  fprintf (fp, "%%%s\n", proc->name);
	  fprintf (fp, "getpid ! { ; process isn't running\n");
	  fprintf (fp, "#0 return } drop ; return immediately\n");
	}
      else
	{
	  fprintf (fp, "\n@%s\n", nebuloid->name);
	  fprintf (fp, "#%u /%s alarm\n", nebuloid->interval, nebuloid->name);
	}

      generate_op (fp, proc, nebuloid->statements);

      fprintf (fp, "#1 return\n");
    }
}

void
generate_restart (FILE * fp, process_t * proc, restart_t * restart)
{
  policy_t *pol;
  nebuloid_t *neb;
  dependency_t *dep;
  process_t *process;
  isup_t *isup;

  fprintf (fp, "\n@%s..restart\n", proc->name);
  fprintf (fp, "%%%s\n", proc->name);

  fprintf (fp, "#0 setpid\n");

  fprintf (fp, "; First, deinstantiate all our nebuloids.\n");
  pol = proc->policy;
  while (pol)
    {
      if (pol->type == PT_NEBULOID)
	{
	  neb = pol->pol_data;
	  fprintf (fp, "(%s) dinst\n", neb->name);
	}
      pol = pol->next;
    }

  // Deinstantiate variable dependencies, a restart implies a mode change.
  dep = find_dyn_depends (proc->context);
  while (dep)
    {
      fprintf (fp, "(._%s) dinst\n", dep->name);
      dep = dep->next;
    }

  // Don't forget our ALIVE nebuloid!
  if (!find_policy_bytype (proc->policy, PT_ISALIVE))
    fprintf (fp, "(.alive) dinst\n");

  /** If no one depends on us, don't bother restarting. **/

  fprintf (fp, "(.dependants) get ! ; If no one depends on us...\n");
  fprintf (fp, "{ (.dependants) dinst ; deinstantiate dependants\n");
  fprintf (fp, "#0 return } drop  ; return\n");

  /** Call relevant dependfails. **/
  fprintf (fp, "; Call relevent dependfails\n");
  pol = sys->policy;
  while (pol)
    {
      // Loop through all processes looking for dependencies
      // XXX: Deal with adding and removing of static dependencies!
      if (pol->type == PT_PROCESS)
	{
	  process = pol->pol_data;
	  dep = process->depends;
	  while (dep)
	    {
	      // Does this process depend on us?
	      if (strcasecmp (dep->name, proc->name) == 0)
		{
		  fprintf (fp, "/%s..dependfail call drop\n", process->name);
		  break;
		}
	      dep = dep->next;
	    }
	  // Also check dynamic dependencies!
	  dep = find_dyn_depends (process->context);
	  while (dep)
	    {
	      // Might this process depend on us?
	      if (strcasecmp (dep->name, proc->name) == 0)
		{
		  fprintf (fp,
			   "%%%s (._%s) get { ; do they currently depend on us?\n",
			   process->name, proc->name);
		  fprintf (fp, "  /%s..dependfail call drop\n",
			   process->name);
		  fprintf (fp, "} drop\n");
		  break;
		}
	      dep = dep->next;
	    }
	}
      pol = pol->next;
    }

  fprintf (fp, "/%s..restartproc call drop ; Actually do the restart\n",
	   proc->name);

  fprintf (fp, "getpid ! { ; Failed to start\n");
  isup = find_policy_bytype (proc->policy, PT_ISUP);
  if (isup)
    fprintf (fp, "  #%u /%s..isup_restart alarm ; Wait until it's up\n",
	     isup->interval, proc->name);
  fprintf (fp, "  #0 return\n} drop\n");

  fprintf (fp,
	   "@%s..restartsuccess ; if it fails, the ISUP will start from here\n",
	   proc->name);

  /** Now, reinstantiate everything. **/
  pol = proc->policy;
  while (pol)
    {
      if (pol->type == PT_NEBULOID)
	{
	  neb = pol->pol_data;
	  generate_arguments (fp, proc, neb->args);
	  fprintf (fp, "#1 &%s (%s) inst\n", neb->plugin, neb->name);
	}
      pol = pol->next;
    }
  dep = find_dyn_depends (proc->context);
  while (dep)
    {
      // XXX: Already-started static dependencies?
      fprintf (fp, "#0 #1 &var (._%s) inst\n", dep->name);
      dep = dep->next;
    }
  if (!find_policy_bytype (proc->policy, PT_ISALIVE))
    fprintf (fp, "#1 &alive (.alive) inst\n");
  fprintf (fp, "#0 return\n");

  /** do whatever it is the user wants to do to restart the program. **/
  fprintf (fp, "\n@%s..restartproc\n", proc->name);
  fprintf (fp, "%%%s\n", proc->name);

  generate_op (fp, proc, restart->statements);
  fprintf (fp, "#0 return\n");
}

void
generate_dependfail (FILE * fp, process_t * proc, dependfail_t * depfail)
{
  fprintf (fp, "\n@%s..dependfail\n", proc->name);
  fprintf (fp, "%%%s\n", proc->name);

  generate_op (fp, proc, depfail->statements);

  fprintf (fp, "#0 return\n");
}

void
generate_isalive (FILE * fp, process_t * proc, isalive_t * isalive)
{
  fprintf (fp, "\n@%s..isalive\n", proc->name);
  fprintf (fp, "%%%s\n", proc->name);

  generate_op (fp, proc, isalive->statements);

  fprintf (fp, "#0 return\n");
}

void
generate_isup (FILE * fp, process_t * proc, isup_t * isup)
{
  fprintf (fp, "\n@%s..isup\n", proc->name);
  fprintf (fp, "/%s..isupproc call drop\n", proc->name);
  fprintf (fp, "getpid { ; if started successfully \n");
  fprintf (fp, "  /%s..startsuccess call return ; do happy started things\n",
	   proc->name);
  fprintf (fp, "}{ ; else\n");
  fprintf (fp, "  #%u /%s..isup alarm ; Keep calling 'till it's up\n",
	   isup->interval, proc->name);
  fprintf (fp, "} return\n\n");

  fprintf (fp, "\n@%s..isup_restart\n", proc->name);
  fprintf (fp, "/%s..isupproc call drop\n", proc->name);
  fprintf (fp, "getpid { ; if started successfully \n");
  fprintf (fp, "  /%s..restartsuccess call return ; Success!\n", proc->name);
  fprintf (fp, "}{ ; else\n");
  fprintf (fp, "  #%u /%s..isup_restart alarm ; Keep calling 'till it's up\n",
	   isup->interval, proc->name);
  fprintf (fp, "} return\n\n");

  fprintf (fp, "@%s..isupproc\n", proc->name);
  fprintf (fp, "%%%s\n", proc->name);

  generate_op (fp, proc, isup->statements);

  fprintf (fp, "#0 return\n");
}

void
generate_sighandler (FILE * fp, process_t * proc, sighandler_t * sig)
{
  if (proc)
    {
      fprintf (fp, "@%s..sigchld\n", proc->name);
      fprintf (fp, "%%%s\n", proc->name);
      fprintf (fp, "/%s..sigchld sigchild\n", proc->name);
    }
  else
    {
      fprintf (fp, "@.sigusr%u\n", (sig->type == SH_SIGUSR1) ? 1 : 2);
      fprintf (fp, "#%u /.sigusr%u signal\n",
	       (sig->type == SH_SIGUSR1) ? 1 : 2,
	       (sig->type == SH_SIGUSR1) ? 1 : 2);
    }

  generate_op (fp, proc, sig->statements);

  fprintf (fp, "#0 return\n");
}

void
generate_proc (FILE * fp, process_t * process, proc_t * proc)
{
  if (process)
    {
      fprintf (fp, "\n@%s.%s\n", process->name, proc->name);
      fprintf (fp, "%%%s\n", process->name);
      if (proc->interval != 0)
	{
	  fprintf (fp, "#%u /%s.%s alarm\n", proc->interval, process->name,
		   proc->name);
	  fprintf (fp, "getpid ! { ; process isn't running\n");
	  fprintf (fp, "#0 return } drop ; return immediately\n");
	}
    }
  else
    {
      fprintf (fp, "\n@%s\n", proc->name);
      if (proc->interval != 0)
	fprintf (fp, "#%u /%s alarm\n", proc->interval, proc->name);
    }

  generate_op (fp, process, proc->statements);

  fprintf (fp, "#1 return\n");	// Procs MUST return a value; by default,
  // success.
}

void
generate_op (FILE * fp, process_t * currproc, state_node_t * oper)
{
  op_node_t *ope;
  unsigned int index;
  unsigned int currloop = 0;
  static unsigned int numloops = 0;

  if (!oper)
    return;

  switch (oper->type)
    {
    case STATE_CONST:
      fprintf (fp, "#%d ", oper->d.con.value);
      break;
    case STATE_IDENT:
      // We'll get these when we generate arguments.
      fprintf (fp, "(%s) ", oper->d.ident.name);
      break;
    case STATE_ARG:
      // We shouldn't get one of these on its own.  Flag it.
      yyerror ("STATE_ARG occurred in generate_op");
      break;
    case STATE_OP:
      ope = &(oper->d.op);
      switch (ope->opcode)
	{
	case OP_RETURN_TRUE:
	  fprintf (fp, "#1 return");
	  break;
	case OP_RETURN_FALSE:
	  fprintf (fp, "#0 return\n");
	  break;
	case OP_IF:
	  generate_op (fp, currproc, ope->nodes[0]);
	  fprintf (fp, "{");
	  generate_op (fp, currproc, ope->nodes[1]);
	  fprintf (fp, "}\n");
	  if (ope->nodes[2])	// else clause
	    {
	      fprintf (fp, "{");
	      generate_op (fp, currproc, ope->nodes[2]);
	      fprintf (fp, "}\n");
	    }
	  fprintf (fp, "drop\n");
	  break;

	case OP_ASSIGN:
	  generate_op (fp, currproc, ope->nodes[1]);
	  if (find_name (NT_PROCESS, ope->nodes[0]->d.ident.name, 0))
	    {
	      fprintf (fp, "%%%s setpid\n", ope->nodes[0]->d.ident.name);
	      if (currproc)
		fprintf (fp, "%%%s\n", currproc->name);
	    }
	  else
	    fprintf (fp, "(%s) set\n", ope->nodes[0]->d.ident.name);
	  break;

	case OP_CALL:
	  // Processs?
	  if (find_name (NT_PROCESS, ope->nodes[0]->d.ident.name, 0))
	    {
	      fprintf (fp, "%%%s getpid\n", ope->nodes[0]->d.ident.name);
	      if (currproc)
		fprintf (fp, "%%%s\n", currproc->name);
	    }
	  else
	    {
	      // global proc?
	      if (find_name (NT_PROC, ope->nodes[0]->d.ident.name, 0))
		{
		  fprintf (fp, "/%s call\n", ope->nodes[0]->d.ident.name);
		  if (currproc)
		    fprintf (fp, "%%%s\n", currproc->name);	// set context back!
		}
	      else
		{
		  // local proc?
		  if ((currproc != NULL) &&
		      find_name (NT_PROC, ope->nodes[0]->d.ident.name,
				 currproc->context))
		    {
		      fprintf (fp, "/%s.%s call\n", currproc->name,
			       ope->nodes[0]->d.ident.name);
		      if (currproc)
			fprintf (fp, "%%%s\n", currproc->name);	// set context back!
		    }
		  else
		    {
		      // Nebuloid?
		      if (find_name
			  (NT_NEBULOID, ope->nodes[0]->d.ident.name, 0)
			  || ((currproc != NULL)
			      && find_name (NT_NEBULOID,
					    ope->nodes[0]->d.ident.name,
					    currproc->context)))
			{
			  generate_arguments (fp, currproc, ope->nodes[1]);
			  fprintf (fp, "(%s) get\n",
				   ope->nodes[0]->d.ident.name);
			}
		      else
			{
			  // Implicitly instantiated nebuloid?
			  if (find_name
			      (NT_MODULE, ope->nodes[0]->d.ident.name, 0))
			    {
			      fprintf (fp,
				       "; Nebuloid \"%s\" implicitly instantiated\n",
				       ope->nodes[0]->d.ident.name);

			      if (currproc)
				fprintf (fp, "#1 ");
			      else
				fprintf (fp, "#0 ");
			      fprintf (fp, "&%s (.%s) inst\n",
				       ope->nodes[0]->d.ident.name,
				       ope->nodes[0]->d.ident.name);
			      generate_arguments (fp, currproc,
						  ope->nodes[1]);
			      fprintf (fp, "(.%s) get\n",
				       ope->nodes[0]->d.ident.name);
			      fprintf (fp, "(.%s) dinst\n",
				       ope->nodes[0]->d.ident.name);
			    }
			  else
			    {
			      yyfatalerror ("Unable to generate code!");
			    }
			}
		    }
		}
	    }

	  if (ope->expr == '\0')
	    fprintf (fp, "drop\n");
	  break;

	case OP_PLUS:
	  generate_op (fp, currproc, ope->nodes[0]);
	  generate_op (fp, currproc, ope->nodes[1]);
	  fprintf (fp, " +\n");
	  break;
	case OP_MINUS:
	  generate_op (fp, currproc, ope->nodes[0]);
	  generate_op (fp, currproc, ope->nodes[1]);
	  fprintf (fp, " -\n");
	  break;
	case OP_TIMES:
	  generate_op (fp, currproc, ope->nodes[0]);
	  generate_op (fp, currproc, ope->nodes[1]);
	  fprintf (fp, " *\n");
	  break;
	case OP_DIVIDE:
	  generate_op (fp, currproc, ope->nodes[0]);
	  generate_op (fp, currproc, ope->nodes[1]);
	  fprintf (fp, " /\n");
	  break;
	case OP_OR:
	  generate_op (fp, currproc, ope->nodes[1]);
	  generate_op (fp, currproc, ope->nodes[0]);
	  fprintf (fp, " |\n");
	  break;
	case OP_AND:
	  generate_op (fp, currproc, ope->nodes[1]);
	  generate_op (fp, currproc, ope->nodes[0]);
	  fprintf (fp, " &\n");
	  break;
	case OP_GT:
	  generate_op (fp, currproc, ope->nodes[1]);
	  generate_op (fp, currproc, ope->nodes[0]);
	  fprintf (fp, " >\n");
	  break;
	case OP_EQ:
	  generate_op (fp, currproc, ope->nodes[1]);
	  generate_op (fp, currproc, ope->nodes[0]);
	  fprintf (fp, " =\n");
	  break;
	case OP_GTE:
	  generate_op (fp, currproc, ope->nodes[1]);
	  generate_op (fp, currproc, ope->nodes[0]);
	  fprintf (fp, " < !\n");
	  break;
	case OP_NE:
	  generate_op (fp, currproc, ope->nodes[1]);
	  generate_op (fp, currproc, ope->nodes[0]);
	  fprintf (fp, " = !\n");
	  break;
	case OP_OPLIST:
	  for (index = 0; index < ope->num_ops; index++)
	    {
	      generate_op (fp, currproc, ope->nodes[index]);
	    }
	  break;
	case OP_DEPADD:
	  fprintf (fp,
		   "(._%s) get { ; Ensure that we haven't already added it\n",
		   ope->nodes[0]->d.ident.name);
	  fprintf (fp,
		   "  /%s..start call drop ; Adding dependancy; ensure it's started\n",
		   ope->nodes[0]->d.ident.name);
	  fprintf (fp, "  ; Increment their dependants.\n");
	  fprintf (fp,
		   "  %%%s (.dependants) get #1 + (.dependants) set %%%s\n",
		   ope->nodes[0]->d.ident.name, currproc->name);
	  fprintf (fp,
		   "  #1 (._%s) set ; Make sure we know we depend on it\n",
		   ope->nodes[0]->d.ident.name);
	  fprintf (fp, "} drop\n");
	  break;
	case OP_DEPREMOVE:
	  fprintf (fp,
		   "(._%s) get { ; If we ARE currently depending on this\n",
		   ope->nodes[0]->d.ident.name);
	  fprintf (fp, "  ; decrement its dependants variable\n");
	  fprintf (fp,
		   "  %%%s (.dependants) get #1 - (.dependants) set %%%s\n",
		   ope->nodes[0]->d.ident.name, currproc->name);
	  fprintf (fp, "  #0 (._%s) set ; and don't depend on it anymore.\n",
		   ope->nodes[0]->d.ident.name);
	  fprintf (fp, "} drop\n");
	  break;
	case OP_LOOP:
	  currloop = numloops;
	  numloops++;
	  generate_op (fp, currproc, ope->nodes[0]);	// Expression
	  fprintf (fp, "#1 &var (.loop%u) inst\n", currloop);
	  fprintf (fp, "#0 (.loop%u) get >\n", currloop);
	  fprintf (fp, "{ [\n");
	  generate_op (fp, currproc, ope->nodes[1]);	// Statements
	  fprintf (fp, "(.loop%u) get #1 - (.loop%u) set\n", currloop,
		   currloop);
	  fprintf (fp, "#0 (.loop%u) get >\n", currloop);
	  fprintf (fp, "] } drop\n");
	  fprintf (fp, "(.loop%u) dinst\n", currloop);
	  break;
	default:
	  break;
	}
      break;
    }
}

void
generate_arguments (FILE * fp, process_t * proc, state_node_t * args)
{
  arg_node_t *arg;
  //  state_node_t *neb;

  if (args)			// has arguments
    {
      arg = &(args->d.arg);
      while (arg)
	{
	  generate_op (fp, proc, arg->data);

	  if (arg->next)
	    arg = &(arg->next->d.arg);
	  else
	    arg = NULL;
	}
    }
}
