/*
 *                           PARSER FOR THE
 *                           ====== === ===
 *
 *                      DATA DECLARATION BUILDER
 *                      ==== =========== =======
 *
 *      This module is the parser used to interprete the high
 *      level data declarations.
 *
 ******* Grammar for Declarations
 *
 *	data   = [ tEXPORT chunk ]
 *		 [ tGLOBAL chunk ]
 *		 [ tBEGIN chunk ]
 *		 tTYPE tOPENCURLY declns tCLOSECURLY
 *	chunk  = tOPENCURLY list_of_lines tCLOSECURLY
 *	declns = list*( decln ) tEOF
 *	decln  = tID tEQ shapes tSEMI
 *	shapes = sep-list+( shape, tOR )
 *	shape  = tID [ tOPENBR params tCLOSEBR ] printlist
 *	params = sep-list+( param, tCOMMA )
 *	param  = tID tID
 *	printlist = list( tSTR | tNUM )
 */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "struct.h"
#include "lexer.h"
#include "parser.h"


#define COPYOF(new,old) {new=malloc(1+strlen(old));if(new)strcpy(new,old);}


/*
/^#ifdef HASPROTOS
!/endif$
stat %
*/


#ifdef HASPROTOS
static BOOL parse_chunk( char * );
static BOOL parse_declns( declnlist * );
static void error( char * );
static BOOL parse_decln( char ** , shapelist * );
static BOOL parse_shapes( shapelist * );
static BOOL parse_shape( char ** , paramlist * , printlist * );
static BOOL parse_params( paramlist * );
static BOOL parse_printlist( printlist * );
static BOOL parse_printitem( printitem * );
static BOOL parse_param( char ** , char ** );
#else
static BOOL parse_chunk();
static BOOL parse_declns();
static void error();
static BOOL parse_decln();
static BOOL parse_shapes();
static BOOL parse_shape();
static BOOL parse_params();
static BOOL parse_printlist();
static BOOL parse_printitem();
static BOOL parse_param();
#endif


#define MUSTBE(t,mesg)	if( nexttok() != (t) ) {error(mesg); return FALSE;}


/*
 *	data   = [ tEXPORT tOPENCURLY crap_to_} ]
 *		 [ tGLOBAL tOPENCURLY crap_to_} ]
 *		 [ tBEGIN tOPENCURLY crap_to_} ]
 *		 tTYPE tOPENCURLY declns tCLOSECURLY
 */

BOOL parse_data( exports, globals, begin, dp ) char *exports, *globals, *begin; declnlist *dp;
{
	if( nexttok() == tEXPORT )
	{
		if( ! parse_chunk( exports ) ) return FALSE;
	} else
	{
		*exports = '\0';
		ungettok();
	}

	if( nexttok() == tGLOBAL )
	{
		if( ! parse_chunk( globals ) ) return FALSE;
	} else
	{
		*globals = '\0';
		ungettok();
	}

	if( nexttok() == tBEGIN )
	{
		if( ! parse_chunk( begin ) ) return FALSE;
	} else
	{
		*begin = '\0';
		ungettok();
	}

	MUSTBE( tTYPE, "TYPE/EXPORT/GLOBAL/BEGIN expected" );
	MUSTBE( tOPENCURLY, "{ expected" );
	if( ! parse_declns( dp ) ) return FALSE;
	MUSTBE( tCLOSECURLY, "{ expected" );
	MUSTBE( tEOF, "Spurious characters found beyond EOF" );

	return TRUE;
}


/* chunk  = tOPENCURLY list_of_lines tCLOSECURLY */

static BOOL parse_chunk( s ) char *s;
{
	char line[256];
	int  startlineno;

	*s = '\0';

	MUSTBE( tOPENCURLY, "'{' expected" );

	startlineno = lineno;
	for(;;)
	{
		if( ! readnextline( line ) )
		{
			fprintf( stderr,
				 "Premature EOF after { - started at line %d\n",
				 startlineno );
			return FALSE;
		}
	if( streq( line, "}" ) ) break;
		strcat( s, line );
		strcat( s, "\n" );
	}
	return TRUE;
}


/* declns = list*( decln )       */
/* NB: a decln starts with a tID */

static BOOL parse_declns( dp ) declnlist *dp;
{
	char		*name;
	shapelist	shapes;

	while( nexttok() == tID )
	{
		ungettok();
		if( ! parse_decln( &name, &shapes ) ) return FALSE;
		*dp = build_declnlist( name, shapes, (declnlist) NULL );
		dp = &( (*dp)->next );
	}
	ungettok();
	return TRUE;
}


static void error( s ) char *s;
{
	fprintf( stderr, "%s at line %d\n", s, lineno );
}


/* decln  = tID tEQ shapes tSEMI */

static BOOL parse_decln( name, shapes ) char **name; shapelist *shapes;
{
	MUSTBE( tID, "declaration expected" );
	COPYOF( *name, lexidval );

	MUSTBE( tEQ, "'=' expected" );

	if( ! parse_shapes( shapes ) ) return FALSE;

	MUSTBE( tSEMI, "';' expected" );

	return TRUE;
}


/* shapes = sep-list+( shape, tOR ) */

static BOOL parse_shapes( sp ) shapelist *sp;
{
	char		*tagname;
	paramlist	paras;
	printlist	print;

	for(;;) {
		if( ! parse_shape( &tagname, &paras, &print ) ) return FALSE;
		*sp = build_shapelist( tagname, paras,
				       print, (shapelist) NULL );
	if( nexttok() != tOR ) break;
		sp = &( (*sp)->next );
	}
	ungettok();
	return TRUE;
}


/* shape = tID [ tOPENBR params tCLOSEBR ] printlist */

static BOOL parse_shape( tagname, pl, print ) char **tagname; paramlist *pl; printlist *print;
{
	*pl    = (paramlist) NULL;
	*print = (printlist) NULL;

	MUSTBE( tID, "shape name expected" );
	COPYOF( *tagname, lexidval );

	if( nexttok() == tOPENBR )
	{
		if( ! parse_params( pl ) ) return FALSE;

		MUSTBE( tCLOSEBR, "')' expected" );
	} else
	{
		ungettok();
	}

	return parse_printlist( print );
}


/* params = sep-list+( param, tCOMMA ) */

static BOOL parse_params( pp ) paramlist *pp;
{
	char *type;
	char *name;

	for(;;) {
		if( !parse_param( &type, &name ) ) return FALSE;
		*pp = build_paramlist( type, name, (paramlist) NULL );
	if( nexttok() != tCOMMA ) break;
		pp = &( (*pp)->next );
	}
	ungettok();
	return TRUE;
}


/* printlist = list( printitem ) */
/* NB: Never fails, cos items are one token long */

static BOOL parse_printlist( pp ) printlist *pp;
{
	printitem item;

	*pp = (printlist) NULL;

	while( parse_printitem( &item ) )
	{
		*pp = build_printlist( item, (printlist) NULL );
		pp = &( (*pp)->next );
	}
	return TRUE;
}


/* printitem = tSTR | tNUM */

static BOOL parse_printitem( item ) printitem *item;
{
	char *temp;

	switch( nexttok() ) {
	case tSTR:
		COPYOF( temp, lexidval );
		*item = build_printitem_str( temp );
		return TRUE;
	case tNUM:
		*item = build_printitem_num( lexintval );
		return TRUE;
	default:
		ungettok();
		return FALSE;
	}
}


/* param     = tID tID */

static BOOL parse_param( type, name ) char **type, **name;
{
	MUSTBE( tID, "Field type expected" );
	COPYOF( *type, lexidval );

	MUSTBE( tID, "Field name expected" );
	COPYOF( *name, lexidval );

	return TRUE;
}
