//
// Copyright(c) 2001 Simon Howard
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License
// as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//------------------------------------------------------------------------------
//
// Parser and tree construction
//
//------------------------------------------------------------------------------

#include "lexer.h"
#include "parser.h"

extern void print_plusnode(treenode_t *node, FILE *fstream);
extern void print_prevnode(treenode_t *node, FILE *fstream);
extern void print_nextnode(treenode_t *node, FILE *fstream);
extern void print_loopnode(treenode_t *node, FILE *fstream);
extern void print_readnode(treenode_t *node, FILE *fstream);
extern void print_printnode(treenode_t *node, FILE *fstream);

extern void print_head(FILE *fstream);
extern void print_tail(FILE *fstream);

static treenode_t *read_treenodelist();

static void print_treenodelist(treenode_t *node, FILE *fstream)
{
	treenodelist_t *list = (treenodelist_t *) node->data.v;
	int i;

	for (i=0; i<list->num_nodes; ++i) {
		list->nodes[i]->print (list->nodes[i], fstream);
	}
}

static void print_tree(treenode_t *node, FILE *fstream)
{
	treenode_t *realtree = (treenode_t *) node->data.v;

	print_head(fstream);

	realtree->print(realtree, fstream);
	
	print_tail(fstream);
}

static int loop_num = 0;

static treenode_t *read_loopnode()
{
	treenode_t *node = (treenode_t *) malloc(sizeof(*node));
	loopnode_t *loop = (loopnode_t *) malloc(sizeof(*loop));

	node->print = print_loopnode;
	node->data.v = loop;

	loop->loop_num = loop_num++;
	loop->list = read_treenodelist();
	
	return node;
}

static treenode_t *read_treenode()
{
	token_t token = Lexer_NextToken();
	treenode_t *node;
	
	if (!token || token == TOKEN_LOOPEND)
		return NULL;

	if (token == TOKEN_LOOPSTART)
		return read_loopnode();
	
	node = (treenode_t *) malloc(sizeof(*node));
	
	switch (token) {
	case TOKEN_PLUS:
		node->print = print_plusnode;
		node->data.i = 1;
		break;
	case TOKEN_MINUS:
		node->print = print_plusnode;
		node->data.i = -1;
		break;
	case TOKEN_PREV:
		node->print = print_nextnode;
		node->data.i = -1;
		break;
	case TOKEN_NEXT:
		node->print = print_nextnode;
		node->data.i = 1;
		break;
	case TOKEN_READ:
		node->print = print_readnode;
		break;
	case TOKEN_WRITE:
		node->print = print_printnode;
		break;
	}

	return node;
}

static treenode_t *read_treenodelist()
{
	treenode_t *ret = (treenode_t *) malloc(sizeof(ret));
	treenodelist_t *list = (treenodelist_t *) malloc(sizeof(list));
	treenode_t *lastnode = NULL;

	ret->print = print_treenodelist;
	ret->data.v = list;
	list->num_nodes = 0;
	list->nodes = NULL;
	
	while (1) {
		treenode_t *node = read_treenode();

		if (!node)
			break;

		// optimisation
		
		if (lastnode) {

			// if we get two +/-'s in a row, combine them into
			// one add
			// ditto with </>
			
			if ((lastnode->print == print_plusnode &&
			     node->print == print_plusnode)
			    ||
			    (lastnode->print == print_nextnode &&
			     node->print == print_nextnode)
				) {
				lastnode->data.i += node->data.i;
				free(node);
				continue;
			}
		}
		
		if (list->nodes)
			list->nodes = (treenode_t **)
				realloc(list->nodes,
					sizeof(*list->nodes)
					* (list->num_nodes + 1));
		else
			list->nodes = (treenode_t **)
				malloc(sizeof(*list->nodes));

		list->nodes[list->num_nodes++] = node;
		lastnode = node;
	}

	return ret;
}

treenode_t *Parser_ParseFile(FILE *fstream)
{
	treenode_t *tree = (treenode_t *) malloc(sizeof(*tree));
	treenode_t *treehead;
	
	Lexer_ParseFile(fstream);
	loop_num = 0;

	treehead = read_treenodelist();

	tree->print = print_tree;
	tree->data.v = treehead;

	return tree;
}
