/* This file is part of the Project Athena Zephyr Notification System.
 * It is one of the source files comprising zwgc, the Zephyr WindowGram
 * client.
 *
 *      Created by:     Marc Horowitz <marc@athena.mit.edu>
 *
 *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
 *      For copying and distribution information, see the file
 *      "mit-copyright.h".
 */

#include <sysdep.h>

RCSID("$Id: node.c,v 1.6 2000/02/18 12:12:10 lha Exp $");

#include <zephyr/mit-copyright.h>

#include "new_memory.h"
#include "node.h"

/****************************************************************************/
/*                                                                          */
/*            Internal node construction & destruction functions:           */
/*                                                                          */
/****************************************************************************/

/*
 * NODE_BATCH_SIZE - the number of nodes to malloc at once to save overhead:
 */

#define  NODE_BATCH_SIZE    100

/*
 * The nodes we have malloced are kept in a linked list of bunches of
 * NODE_BATCH_SIZE nodes.  Nodes points to the first bunch on the list
 * and current_bunch to the last.  All nodes from the first one in the first
 * bunch to the last_node_in_current_bunch_used'th one in the last bunch
 * are in use.  The others have not been used yet.
 */

static struct _bunch_of_nodes {
    struct _bunch_of_nodes *next_bunch;
    Node nodes[NODE_BATCH_SIZE];
} *nodes = NULL;
static struct _bunch_of_nodes *current_bunch = NULL;
static int last_node_in_current_bunch_used = -1;

/*
 *  Internal Routine:
 *
 *    Node *node_create(int opcode)
 *        Effects: Creates a node with opcode opcode and returns a pointer
 *                 to it.  The next pointer of the returned node is NULL.
 *                 If the opcode is STRING_CONSTANT_OPCODE the caller must
 *                 ensure that the string_constant field points to a valid
 *                 string on the heap when node_DestroyAllNodes is called.
 */

static Node *node_create(int opcode)
{
    Node *result;

    if (!nodes) {
	/*
	 * Handle special case where no nodes allocated yet:
	 */
	current_bunch = nodes = (struct _bunch_of_nodes *)
	  malloc(sizeof(struct _bunch_of_nodes));
	nodes->next_bunch = NULL;
	last_node_in_current_bunch_used = -1;
    }

    /*
     * If all nodes allocated so far in use, allocate another
     * bunch of NODE_BATCH_SIZE nodes:
     */
    if (last_node_in_current_bunch_used == NODE_BATCH_SIZE-1) {
	current_bunch->next_bunch = (struct _bunch_of_nodes *)
	  malloc(sizeof(struct _bunch_of_nodes));
	current_bunch = current_bunch->next_bunch;
	current_bunch->next_bunch = NULL;
	last_node_in_current_bunch_used = -1;
    }

    /*
     * Get next not already used node & ready it for use:
     */
    last_node_in_current_bunch_used++;
    result = &(current_bunch->nodes[last_node_in_current_bunch_used]);
    result->opcode = opcode;
    result->next = NULL;

    return(result);
}

/*
 *
 */

void node_DestroyAllNodes(void)
{
    struct _bunch_of_nodes *next_bunch;
    int i, last_node_used_in_this_bunch;

    while (nodes) {
	next_bunch = nodes->next_bunch;
	last_node_used_in_this_bunch = next_bunch ?
	  NODE_BATCH_SIZE-1 : last_node_in_current_bunch_used;
	for (i=0; i<=last_node_used_in_this_bunch; i++) {
	    if (nodes->nodes[i].opcode==STRING_CONSTANT_OPCODE)
	      free(nodes->nodes[i].d.string_constant);
	    else if (nodes->nodes[i].opcode==VARREF_OPCODE)
	      free(nodes->nodes[i].d.string_constant);
	    else if (nodes->nodes[i].opcode==VARNAME_OPCODE)
	      free(nodes->nodes[i].d.string_constant);
	}
	free(nodes);
	nodes = next_bunch;
    }

    current_bunch = nodes;
}

/****************************************************************************/
/*                                                                          */
/*                     Node construction functions:                         */
/*                                                                          */
/****************************************************************************/

Node *node_create_string_constant(int opcode, string text)
{
    Node *n;

    n = node_create(opcode);
    n->d.string_constant = text;
    return(n);
}

Node *node_create_noary(int opcode)
{
    Node *n;

    n = node_create(opcode);
    return(n);
}

Node *node_create_unary(int opcode, Node *arg)
{
    Node *n;

    n = node_create(opcode);
    n->d.nodes.first = arg;
    return(n);
}

Node *node_create_binary(int opcode, Node *first_arg, Node *second_arg)
{
    Node *n;

    n = node_create(opcode);
    n->d.nodes.first = first_arg;
    n->d.nodes.second = second_arg;
    return(n);
}

/****************************************************************************/
/*                                                                          */
/*                        Node utility functions:                           */
/*                                                                          */
/****************************************************************************/

/*
 *    Node *reverse_list_of_nodes(Node *list)
 *        Modifies: the nodes on the linked list list
 *        Effects: Reverses the linked list list and returns it.
 *                 This is done by modifing the next pointers of the
 *                 list elements to point to the previous node & returning
 *                 the address of the (previously) last node.
 */

Node *reverse_list_of_nodes(Node *list)
{
    Node *next_node;
    Node *head = NULL;

    while (list) {
	next_node = list->next;

	/*
	 * Add the node list to the beginning of linked list head:
	 */
	list->next = head;
	head = list;

	list = next_node;
    }

    return(head);
}

/****************************************************************************/
/*                                                                          */
/*                        Node display functions:                           */
/*                                                                          */
/****************************************************************************/

#ifdef DEBUG

static void print_stuff(Node *node, string format_string)
{
    char c;

    for (c=(*(format_string++)); c; c=(*(format_string++))) {
	if (c!='%') {
	    putchar(c);
	    continue;
	}
	c=(*(format_string++));
	if (!c) {
	    format_string--;
	    continue;
	}
	if (c=='s')
	  printf("%s", node->d.string_constant);
	else if (c=='1')
	  node_display(node->d.nodes.first);
	else if (c=='2')
	  node_display(node->d.nodes.second);
	else
	  putchar(c);
    }
}

static string how_to_print[] = {
    "\"%s\"",        /* constant string */
    "$%s",           /* varref */
    "%s",            /* varname */

    "!%1",

    "( %1 + %2 )",
    "( %1 and %2 )",
    "( %1 or %2 )",
    "( %1 == %2 )",
    "( %1 != %2 )",
    "( %1 =~ %2 )",
    "( %1 !~ %2 )",

    "buffer()",
    
    "substitute(%1)",
    "protect(%1)",
    "verbatim(%1)",
    "stylestrip(%1)",
    "getenv(%1)",
    "upcase(%1)",
    "downcase(%1)",
    "zvar(%1)",
    "get(%1)",

    "lany(%1, %2)",
    "rany(%1, %2)",
    "lbreak(%1, %2)",
    "rbreak(%1, %2)",
    "lspan(%1, %2)",
    "rspan(%1, %2)",

    "noop\n",
    "set %1 = %2\n",
    "fields %1\n",

    "print %1\n",
    "clearbuf\n",
    
    "appendport %1 %2\n",
    "execport %1 %2\n",
    "inputport %1 %2\n",
    "outputport %1 %2\n",
    "put %1 %2\n",
    "closeinput %1\n",
    "closeoutput %1\n",
    "closeport %1\n",

    "exec %1 %2\n",

    "%1endif\n",
    "case %1\n%2endcase\n",
    "while %1 do\n%2endwhile\n",
    "break\n",
    "exit\n",
    
    "if %1 then\n%2",
    "elseif %1 then\n%2",
    "else\n%2",
    "match %1\n%2",
    "default\n%2" };

void node_display(Node *node)
{
    int opcode = LAST_EXPR_OPCODE + 1;

    for (; node; node=node->next) {
	if (opcode<=LAST_EXPR_OPCODE)
	  printf(" ");

	opcode = node->opcode;
	if (opcode>=0 && opcode<NUMBER_OF_OPCODES)
	  print_stuff(node, how_to_print[opcode]);
	else
	  printf("[opcode %d]", opcode);
    }
}

#endif
