/* ----------------------------------------------------------------------------
 * Parser.C - A simple oo-parser.
 *            (c) michael koehne <kraehe@bakunin.north.de>
 */

extern "C" {
	#ifdef _BSDI
	#include <stdlib.h>
	#else
	#include <malloc.h>
	#endif

	#include <string.h>
	#include <memory.h>
	#include <ctype.h>
	#include <stdio.h>
	#include <stdlib.h>

	#include "mystring.h"
	}

#include "tw_types.h"
#include "parser.h"

int parser_debug=0;

void Parser::InitParser( )
{	p_line	= (char *)setmalloc(256);
	p_token	= (char *)setmalloc(257); /* Important because badly num-hack */

	p_debug = 0;
	p_stream = 0;
	p_text = 0;
	p_string = 0;
	p_val = 0;
	p_error = new Text(16);
	p_fatal = 0;
	p_matched = 0;

	memset(p_line,0,256);
	memset(p_token,0,257);

	p_pos			= p_line;
	p_matched_noindent	= 1;
	p_eof			= 0;
	}

Parser::Parser( )
{	InitParser ();
	}

Parser::Parser( LineStream * Init)
{	InitParser ();
	NewStream( Init );
	}

Parser::Parser( Text * Init)
{	InitParser ();
	NewStream( Init );
	}

Parser::Parser( char * Init)
{	InitParser ();
	NewStream( Init );
	}

Parser::~Parser()
{	purge();
	free(p_line);
	free(p_token);
	delete p_error;
	}

void Parser::purge()
{	
	if (p_string) {
		delete p_stream;
		delete p_text;
		free(p_string);
		}
	else
	if (p_text) {
		delete p_stream;
		}

	p_stream = 0;
	p_text   = 0;
	p_string = 0;

	if (p_val) {
		free(p_val);
		p_val = 0;
		}

	p_error->purge();
	p_fatal = 0;

	if (p_matched) {
		delete p_matched;
		p_matched = 0;
		}

	memset(p_line,0,256);
	memset(p_token,0,257);

	p_pos			= p_line;
	p_matched_noindent	= 1;
	p_eof			= 0;
	}

void Parser::NewStream ( LineStream *InitStream )
{	purge();
	p_stream = InitStream;
	}

void Parser::NewStream ( Text *InitText )
{	purge();

	p_text = InitText;
	p_stream = new TextStream(p_text);
	}

void Parser::NewStream ( char *InitString )
{	purge();

	p_string = strdup(InitString);
	p_text = new Text(2);
	p_text->add_line(p_string);
	p_stream = new TextStream(p_text);
	}

void Parser::debug(char *str)
{	if ((parser_debug || p_debug) && str && *str) {
		if (!p_debug) p_debug = 1;

		if (p_debug == 1) {
			fprintf(stderr,"Debug: ");
			p_debug=2;
			}
		fprintf(stderr,"%s",str);
		if (str[strlen(str)-1] == '\n') p_debug=1;
		}
	}

char *Parser::next()
{	char *q;

	debug("next (");
        q = p_token; memset(p_token,0,257);

	if (p_fatal) return(0);
	if (!p_stream || p_eof) return(0);

        do {    if (!*p_pos) {
                        if (p_stream->readline(p_line,255) < 0) {
				p_eof = 1;
				return(0);
				}
                        p_pos=p_line;
                        }
                
                while (*p_pos && strchr(" \t\r\n",*p_pos)) p_pos++;
                } while (!*p_pos);

        if (isdigit(*p_pos))
                while (*p_pos && (isdigit(*p_pos) || (*p_pos == '.')))
                        *q++=*p_pos++;
        else
        if (isalpha(*p_pos))
                while (*p_pos && (isalpha(*p_pos) || isdigit(*p_pos) 
                        || (*p_pos == '_') || (*p_pos < 0)))
                        *q++=*p_pos++;
        else    *q++=*p_pos++;

	debug(p_token); debug(")\n");

	comment();

        return(p_token);
        }

int Parser::terminal(char *token)
{	if (p_fatal  ) return(0);

	if (!*p_token) next();

	if (p_val) free(p_val);
	p_val=0;

	debug("terminal ("); debug(token); debug(":"); debug(p_token); 
	if (strncmp(token,p_pos-strlen(p_token),strlen(token))) {
		debug(")Failed\n");
		return(0);
		}
	debug(")OK\n");

	p_val=strdup(token);
	*p_token =0;
	return(1);
	}

char *Parser::id()
{	if (p_fatal  ) return(0);

	if (!*p_token) next();

	if (p_val) free(p_val);
	p_val=0;

	debug("id (");
	if (isalpha(*p_token)) {
		debug(")OK\n");
		p_val=strdup(p_token);
		*p_token =0;

		return(p_val);
		}
	debug(")Failed\n");
	return(0);
	}

char *Parser::num()
{	if (p_fatal  ) return(0);

	if (!*p_token) next();

	if (p_val) free(p_val);
	p_val=0;

	debug("num (");
	if ((*p_token == '-') && isdigit(p_pos[1])) {
		debug(")OK\n");
		p_token++;
		next();

		p_token--;
		p_val=strdup(p_token);
		*p_token =0;

		return(p_val);
		}
		
	if (isdigit(*p_token)) {
		debug(")OK\n");
		p_val=strdup(p_token);
		*p_token =0;

		return(p_val);
		}
	debug(")Failed\n");
	return(0);
	}

Text *Parser::match()
{	char *q;
	char ign[64];
	char *i;
	int escflag=0;
	int stringflag=0;

	if (p_fatal  ) return(0);

	debug("match (");
	if (!strchr("\"'`{[(;",*p_val)) return(0);

	if (p_matched)
		p_matched->purge();
	else	p_matched = new Text(1);

	if (*p_token) {
		p_pos -= strlen(p_token);
		*p_token = 0;
		}

	if (strchr("\"'`",*p_val)) {
		*ign = *p_val;

		q = p_token; memset(p_token,0,256);

		while (*p_pos && (escflag || (*ign != *p_pos))) {
			escflag=0;
			if (*p_pos == '\\') escflag=1;
			*q++=*p_pos++;
			}

		if (*ign == *p_pos) {
			p_matched->add_line(p_token);
			if (p_val) {
				free(p_val);
				p_val=strdup(p_token);
				}
			*p_token =0;

			debug(p_matched->lines[0]); debug(")OK\n");
			return(p_matched);
			}

		*p_token =0;
		delete p_matched; p_matched=0;
		debug(")Failed\n");
		return(0);
		}

	while (*p_pos && strchr(" \t\r\n",*p_pos)) p_pos++;

	while (!*p_pos) {
		if (p_stream->readline(p_line,255) < 0) {
			p_eof = 1;
			delete p_matched;
			p_matched = 0;
			debug(")Failed\n");
			return(0);
			}
		p_pos = p_line;
		while (*p_pos && strchr(" \t\r\n",*p_pos)) p_pos++;
		}

	if (p_matched_noindent) {
		p_matched_noindent = 0;
		q=p_line;
		while (q < p_pos) {
			p_matched_noindent++;
			if (*q == '\t') 
				p_matched_noindent =
					((p_matched_noindent+7) / 8) *8;
			q++;
			}
		if (!p_matched_noindent) p_matched_noindent++;
		}

	memset(ign,0,64); i=ign; q=p_pos;

	*i = *p_val;
	if (*p_val== '(') *i=')';
	if (*p_val== '[') *i=']';
	if (*p_val== '{') *i='}';

	while (!*q || (i > ign) || (*i != *q)) {
		/* fprintf(stderr,"%d,%d,%s?%s\n",escflag,stringflag,i,q); */
		if (!escflag && (*i == *q)) {
			i--; q++;
			stringflag=0;
			}
		else
		if (!escflag && *q && strchr("([{\"'`",*q)) {
			if (!stringflag && (*q== '('))	*++i=')';
			if (!stringflag && (*q== '['))	*++i=']';
			if (!stringflag && (*q== '{'))	*++i='}';
			if (*q== '"')	{ *++i='"'; stringflag=1; }
			if (*q== '`')	{ *++i='`'; stringflag=1; }
			if (*q== '\'')	{ *++i='\''; stringflag=1; }
			q++;
			}
		else
		if (!*q) {
			p_matched->add_line(p_pos);

			if (!p_stream || p_eof) return(0);
			if (p_stream->readline(p_line,255) < 0) {
				p_eof = 1;
				delete p_matched;
				p_matched = 0;
				debug(")Failed\n");
				return(0);
				}
			p_pos=p_line; q=p_pos;
			if (p_matched_noindent) {
				int pmni;

				pmni = p_matched_noindent;
				while ((pmni > 0) && *q && strchr(" \t",*q)) {
					pmni --;
					if (*q == '\t') pmni -= 7;
					q++; p_pos++;
					}
				}
			}
		else {	if (*q == '\\')
				escflag=!escflag;
			else	escflag=0;
			q++;
			}
		}
	if (p_pos<q) {
		*ign = *q;
		*q = 0;
		p_matched->add_line(p_pos);
		*q = *ign; p_pos=q;
		}
	debug(p_matched->lines[0]); debug(")OK\n");

	return(p_matched);
	}

Text *Parser::matchexp()
{	if (p_val) free(p_val);
	p_val = (char *)malloc(2);
	strcpy(p_val,";");
	return(match());
	}

int Parser::line_comment()
{	debug("line_comment"); debug(p_pos); debug("\n");
	if (p_fatal || !p_stream || p_eof ||
	   (p_stream->readline(p_line,255) < 0)) {
		p_eof = 1;
		memset(p_line,0,256);
		memset(p_token,0,257);
		p_pos = p_line;
		return(0);
		}
	else {	p_pos = p_line;
		next();
		}
	return(1);
	}

int Parser::comment()
{	if (p_fatal  ) return(0);

	while (*p_token == '#') {
		line_comment();
		}
	return(1);
	}

int Parser::message(char *str,char *s1,char *s2,char *s3,
			      char *s4,char *s5,char *s6,
			      char *s7,char *s8,char *s9)
{	char line[256];

	if (p_fatal) return(0);

	if (str && *str) {
		sprintf(line,str,s1,s2,s3,s4,s5,s6,s7,s8,s9);
		p_error->add_line(line);
		}

	return(1);
	}

int Parser::error(
	char *s1,char *s2,char *s3,
	char *s4,char *s5,char *s6,
	char *s7,char *s8,char *s9)
{	if (p_fatal  ) return(0);

	if (p_stream) {
		message("%s:%d: Error at token >%s< line %s",
			p_stream->StreamName, (char*)p_stream->pos,
			p_token,p_pos);

		if (s1) {
			message("%s:%d:%s %s %s %s %s %s",
				p_stream->StreamName, (char*)p_stream->pos,
				s1 ? s1 : "",
				s2 ? s2 : "",
				s3 ? s3 : "",
				s4 ? s4 : "",
				s5 ? s5 : "",
				s6 ? s6 : "",
				s7 ? s6 : ""
				);
			}
		}
	else	message("Error but no Stream");

	p_fatal = 1;

	return(0);
	}

int Parser::eof()
{	if (p_fatal  ) return(0);

	return(p_eof);
	}

int Parser::true()
{	return(1);
	}

int Parser::false()
{	return(0);
	}
