/* ------------------------------------------------------------------
                        MML compiler [M2]
               programmed by S.Yamamoto (SHINNOSUKE)
                  m2pre.c  -- pre process source
------------------------------------------------------------------ */

#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	<string.h>
#include	"m2.h"

#define	SUCCSESS	0
#define	FAILURE	(-1)
#define	TRUE	1
#define	FALSE	0
#define	ERR	(-1)
#define	YES	1
#define	NO	0

#define	FILENEST	8	/* #include の入れ子レベル */
#define	IFNEST	8	/* #if(n)def の入れ子レベル */
#define	MACRO_MAX	1024	/* 同時に定義できるマクロ数 */
#define	MACNAME_MAX	31	/* マクロ名の有効文字数 */
#define	MACDAT_MAX	255	/* マクロデータの最大長 */
#define	LOOP_MAX	16	/* for next のレベル */
#define	CONDITION_MAX	8	/* 条件コンパイルのレベル */
#define	RDLEN_MAX	509	/* ソース行の最高文字数（バイト） */
#define	WTLEN_MAX	4096	/* 処理後の文字数（バイト） */

typedef	struct {
	FILE	*fpi;
	char	name[FILENAME_MAX];
	unsigned	int	line;
} FILEST;

FILEST	fileStack[FILENEST];

typedef struct {
	int	line;
	long	int	offset;
	int	count;
} LOOP;

LOOP	loopStack[LOOP_MAX];

typedef struct {
	char	flag;
	char	*name;	/* マクロ名 */
	char	*data;	/* データ */
} MACRO;

MACRO	macroStack[MACRO_MAX];

int comment;    /* コメントネスト */
int fileNest;   /* ファイル */
int condNest;   /* 条件 */
int loopNest;   /* ループ */
int condition[CONDITION_MAX];   /* 条件 */
int macCount;   /* マクロ数 */

char	readBuf[RDLEN_MAX];	/* 読み込みバッファ */
char	writeBuf[WTLEN_MAX];	/* 書き込みバッファ */

char	macPara[64] = { 0 };	/* マクロ用パラメータ */

/* エラー定義 */
char	*MSG_cntopn	=	"Can't open file '%s'\n";
char	*MSG_cntcrt	=	"Can't creat file '%s'\n";
char	*MSG_cntwrt	=	"Can't open file '%s'\n";
char	*MSG_cntalc	=	"Can't allocate memory (%s)\n";
char	*MSG_misopd	=	"Missing operand (%s)\n";
char	*MSG_miscmt	=	"Missing comment\n";
char	*MSG_undmac	=	"Undefined '%s' macro\n";
char	*MSG_undmml	=	"Undefined this MML '%s'\n";
char	*MSG_undctl	=	"Undefined this control\n";
char	*MSG_undstm	=	"Undefined this statement\n";
char	*MSG_illfnc	=	"Illgel function call %s\n";
char	*MSG_toonst	=	"%s too nested\n";
char	*MSG_toodfn	=	"Too defined\n";
char	*MSG_syntax	=	"Syntax error (%s)\n";
char	*MSG_outwka	=	"Out of work-area\n";
char	*MSG_lbover	=	"Line buffer overflow\n";
char	*MSG_fndsmc	=	"Already defined '%s' macro\n";
char	*MSG_unperr	=	"Unprintable error\n";
char	*MSG_looper	=	"#next without #for\n";
char	*MSG_cdcomp	=	"#else/#endif without #if(n)def\n";
char	*MSG_freerr	=	"%s\n";
char	*MSG_errdtv	=	"Error directive: %s\n";

int	errMsg( char *s1,char *s2 ) /* エラー処理 */
{
	char	buf[255];

	if( s1 != NULL ) {
		if( s2 != NULL )
			sprintf( buf ,s1 ,s2 );
		else	strcpy( buf ,s1 );
		printf( "%s:%d:%s\n" ,fileStack[fileNest].name
			,fileStack[fileNest].line ,buf );
	}
	exit( EXIT_FAILURE );
}

char	*lineGet( void )	/* 一行読み込む */
{
	for(;;) {
		fileStack[fileNest].line++;
		if( fgets( readBuf ,RDLEN_MAX ,fileStack[fileNest].fpi ) == NULL ) {
			fclose( fileStack[fileNest].fpi );
			if( --fileNest < 0 )	return( NULL );
		}
		else	break;
	}
	return( readBuf );
}

int	delComment( void )	/* コメントを取り拡張表記をスペースに */
{
	char	*rp;
	char	*wp;
	char	dat;

	rp = &readBuf[0];
	wp = rp;
	while( *rp != '\0' ) {
		if( *rp == '/' && *(rp+1) == '*' ) {
			comment++;
			rp += 2;
			continue;
		}
		if( *rp == '*' && *(rp+1) == '/' ) {
			comment--;
			rp += 2;
			if( comment < 0 )
				errMsg( MSG_miscmt ,NULL );
			continue;
		}
		if( comment == 0 ) {
			if( *rp == '/' && *(rp+1) == '/' )
				break;
			dat = *rp;
			rp++;
			if( dat == '\n' )	continue;
			*wp = ( dat >= '\a' && dat <= '\r' )?' ':dat;
			wp++;
		}
		else	rp++;
	}
	*wp = '\0';
	return( SUCCSESS );
}

/* マクロ処理サブ */

int	macCheck( char *s )	/* マクロの存在を確かめる */
{
	int	i;
	int	ret = ERR;

	if( macCount == 0)
		return( ERR );
	for( i=0;i<macCount;i++ ) {
		if( strcmp( s ,macroStack[i].name ) == 0) {
			ret = i;
			break;
		}
	}
	return( ret );	/* [RET] macNumber=found ERR=not found */
}

int	macSort( void )	/* 長いマクロ名順にソート（基本選択法）*/
{
	int	i;
	int	j;
	int	s;
	int	max;
	int	chk;

	MACRO	dummy;

	if( macCount <= 1 )
		return(SUCCSESS);

	for( i=0;i<macCount-1;i++ ) {
		max = strlen( macroStack[i].name );
		s = i;
		for( j=i+1;j<macCount;j++ ) {
			chk = strlen( macroStack[j].name );
			if( chk > max ){
				max = chk;
				s = j;
			}
		}
		dummy = macroStack[i];
		macroStack[i] = macroStack[s];
		macroStack[s] = dummy;
	}
	return( SUCCSESS );
}

int	macExpand( char *wp )	/* マクロ展開 */
{
	char	*s1;
	char	*s2;
	char	*s3;
	char	*msg = "Macro para error";

	int	i;
	int	ln;
	int	ld;
	int	n = ERR;

	if( strncmp( wp+1 ,"_PARA_" ,6 ) == 0 ) {
		ln = 6;
		ld = strlen( macPara );
		s1 = wp + 1 + ln;
		s2 = wp + ld;
		memmove( s2 ,s1 ,strlen( wp )-ln );
		memcpy( wp ,macPara ,ld );
		return( 0 );
	}

	for( i=0;i<macCount;i++ ) {
		ln = strlen( macroStack[i].name );
		if( strncmp( wp+1 ,macroStack[i].name ,ln ) == 0) {
			n = i;
			break;
		}
	}

	if( n == ERR )
		return(1);

	ld = strlen( macroStack[n].data );
	s1 = wp + 1 + ln;
	s2 = wp + ld;
	
	memmove( s2 ,s1 ,strlen(wp)-ln );
	memcpy( wp ,macroStack[n].data ,ld );

	if( macroStack[n].flag == TRUE )
	{
		s3 = s2;
		while( *s3 == ' ' )
			s3++;

		if( *s3 != '(' )
			errMsg( MSG_freerr ,msg );
		*s3 = ' ';
		s3++;
		i = 0;
		while( *s3 != ')' ) {
			if( i >= 63 )
				errMsg( MSG_freerr ,msg );
			macPara[i] = (*s3);
			*s3 = ' ';
			s3++;
			i++;
		}
		*s3 = ' ';
		macPara[i] = '\0';
	}
	return(0);
}

void	macRepeat( char *wp )	/* リピート */
{
	char	*msg = "Repeat";
	char	*s1;
	char	*s2;
	char	*buf1;
	char	*buf2;

	int	i;
	int	dummy = 0;
	int	num;
	int	l1;
	int	l2 = 1;

	s1 = wp + 2;

	for(;;) {
		if( *s1 == '\0' )
			errMsg( MSG_syntax ,msg );
		if( *s1 == '(' )	dummy++;
		if( *s1 == ')' )	dummy--;
		if( dummy < 0 ) {
			break;
		}
		s1++;
		l2++;
	}
	*s1 = '\0';
	s1++;
	num = strToInt( &s1 ,0 ,16 ,2 ,msg );
	l1 = strlen(s1) + 1;

	if(( buf1 = (char*)malloc( l1 )) == NULL )
		errMsg( MSG_cntalc ,msg );
	if(( buf2 = (char*)malloc( l2 )) == NULL)
		errMsg( MSG_cntalc ,msg );
	strcpy( buf1 ,s1 );

	s2 = wp + 2;
	strcpy( buf2 ,s2 );

	*wp = '\0';
	for( i=0;i<num;i++ )
		strcat(wp,buf2);
	strcat( wp ,buf1 );

	free( buf1 );
	free( buf2 );
	return;
}

/* 前処理指令 */

int	preInclude( char *p )	/* インクルードファイルのオープン */
{
	char	*msg = "#include";
	char	*s1;
	char	*s2;

	s1 = strchr( p ,'<' );
	if( s1 == NULL )
		errMsg( MSG_misopd ,msg );
	s2 = strchr( ++s1 ,'>' );
	if( s2 == NULL || s1 == s2 )
		errMsg( MSG_misopd ,msg );
	*s2 = '\0';
	if( ++fileNest >= FILENEST )
		errMsg( MSG_toonst ,msg );
	strncpy( fileStack[fileNest].name ,s1 ,FILENAME_MAX - 1 );

	fileStack[fileNest].line = 0;
	if(( fileStack[fileNest].fpi = fopen(fileStack[fileNest].name , "rt")) == NULL )
		errMsg( MSG_cntopn ,fileStack[fileNest].name );
	return( SUCCSESS );
}

int	preDefine( char *p )	/* マクロ定義 */
{
	char	*msg = "#define";
	char	*s;
	char	mn[MACNAME_MAX + 1];
	char	md[MACDAT_MAX + 1];
	char	flag = FALSE;

	if( macCount >= MACRO_MAX )
		errMsg( MSG_toodfn ,NULL );
	if( *p == '&' ) {
		flag = TRUE;
		p++;
	}

	if( *p == '\0' )
		errMsg( MSG_misopd ,msg );

	s = strchr( p ,' ' );
	if( s != NULL ) {
		*s = '\0';
		strncpy( mn ,p ,MACNAME_MAX );
		*s = ' ';
		s = skipSpace( s );
		strncpy( md ,s ,MACDAT_MAX );
	}
	else {
		strncpy( mn ,p ,MACNAME_MAX );
		md[0] = '\0';
	}
	if( macCheck( mn ) != ERR )
		errMsg( MSG_fndsmc ,mn );
	if(( macroStack[macCount].name = malloc( strlen(mn)+1 )) == NULL )
		errMsg( MSG_cntalc ,"macro" );
	if(( macroStack[macCount].data = malloc( strlen(md)+1 )) == NULL )
		errMsg( MSG_cntalc ,"macro" );

	strcpy( macroStack[macCount].name ,mn );
	strcpy( macroStack[macCount].data ,md );

	macroStack[macCount].flag = flag;
	macCount++;
	macSort();
	return(SUCCSESS);
}

int	preUndef( char *p )	/* マクロ解除 */
{
	char	*msg = "#undef";
	char	*s;
	int	n;

	if( *p == '\0' )
		errMsg( MSG_misopd ,msg );
	s = strchr( p ,' ' );
	if( s != NULL )
		*s = '\0';
	if(( n = macCheck( p )) == ERR )
		errMsg( MSG_undmac ,p );

	free( macroStack[n].name );
	free( macroStack[n].data );
	macCount--;
	if( macCount != 0 ) {
		macroStack[n] = macroStack[macCount];
		macSort();
	}
	return( SUCCSESS );
}       

int	preCondition( char *p ,int m )	/* 条件組み込み */
{
	char	*msg = "(conditional compile)";
	char	*s;
	int	n;

	if( m == 4 || m == 5 ) {
		if( condNest >= IFNEST )
			errMsg( MSG_toonst ,msg );
		if( *p == '\0' )
			errMsg( MSG_misopd ,msg );
		s = strchr( p ,' ' );
		if( s != NULL )
			*s = '\0';
		n = macCheck( p );
	}
	switch( m ) {
		case 4:
			if( n == ERR )
				condition[condNest] = 1;
			else	condition[condNest] = 0;
			condNest++;
			break;
		case 5:
			if( n == ERR )
				condition[condNest] = 0;
			else	condition[condNest] = 1;
			condNest++;
			break;
		case 6:
			if( condNest == 0 )
				errMsg( MSG_cdcomp ,NULL );
			condition[condNest-1] = ( condition[condNest-1] == 0 )?1:0;
			break;
		case 7:
			if( condNest == 0 )
				errMsg( MSG_cdcomp ,NULL );
			condNest--;
			break;
	}
	return( SUCCSESS );
}

int	preForNext( char *p ,int n )	/* ループ */
{
	int	dummy;

	switch( n ) {
		case 8:
			if( loopNest >= LOOP_MAX )
				errMsg( MSG_toonst ,"#for/#next" );
			loopStack[loopNest].line = fileStack[fileNest].line;
			loopStack[loopNest].offset = ftell( fileStack[fileNest].fpi );
			loopStack[loopNest].count = strToInt( &p,1,256,0,NULL );
			loopNest++;
			break;
		case 9:
			if( loopNest == 0 )
				errMsg( MSG_looper ,NULL );
			dummy = loopNest - 1;
			loopStack[dummy].count--;
			if( loopStack[dummy].count >= 1 ) {
				fseek( fileStack[fileNest].fpi,loopStack[dummy].offset,SEEK_SET );
				fileStack[fileNest].line = loopStack[dummy].line;
			}
			else	loopNest--;
			break;
	}
	return( SUCCSESS );
}

int	preDirective( void )	/* 前処理指令メイン */
{
	char	*p;

	int	n;
	int	dummy;

	static	char	*preCommand[] = {
		"include",
		"define",
		"undef",
		"error",
		"ifdef",
		"ifndef",
		"else",
		"endif",
		"for",
		"next",
		"message",
		"\0"
	};

	p = skipSpace( &readBuf[1] );
	n = comSearch( preCommand ,p );
	if( n == ERR )
		errMsg( MSG_undstm ,NULL );

	p += strlen( preCommand[n] );
	p = skipSpace( p );

	if( n >=4 && n <= 7 ) {
		preCondition( p ,n );
		return( SUCCSESS );
	}

	dummy = ( condNest==0 )?0 :condition[condNest-1];
	if( dummy != 0)
		return( SUCCSESS );

	switch( n ) {
		case 0:
			preInclude( p );
			break;
		case 1:
			preDefine( p );
			break;
		case 2:
			preUndef( p );
			break;
		case 3:
			errMsg( MSG_errdtv ,p );
			break;
		case 8:
		case 9:
			preForNext( p ,n );
			break;
		case 10:
			printf( "%s\n" ,p );
			break;
	}
	return( SUCCSESS );
}

/* 前処理メイン関数 */

int	Pre_open( const char *fn )	/* ファイルオープン */
{
	if(( fileStack[0].fpi = fopen( fn ,"rt" )) == NULL ) {
		printf( MSG_cntopn ,fn );
		errMsg( NULL,NULL );
	}

	comment = 0;
	fileNest = 0;
	condNest = 0;
	loopNest = 0;
	macCount = 0;

	strcpy( fileStack[0].name ,fn );
	fileStack[0].line = 0;
	return( SUCCSESS );
}

char	*Pre_fgets( void )	/* 一行読み込み */
{
	char	*rp;
	char	*wp;
	char	*wb;
	int	dummy;

	wb = &writeBuf[0];

	for(;;) {
		if( lineGet() == NULL )
			return( NULL );
		delComment();	/* コメントを取る */

		dummy = ( condNest == 0 ) ? 0 :condition[condNest-1];
		if( readBuf[0] != '#' && dummy == 0 )
			break;
		if( readBuf[0] == '#' )
			preDirective();	/* 前処理指令 */
	}

	rp = &readBuf[0];
	wp = wb;
	strcpy( wp ,rp );
	
	while( *wp != '\0' ) {
		if( *wp == '$' ) {
			if( *(wp+1) != '(' )
				wp += macExpand( wp );
			else	macRepeat( wp );
		}
		else	wp++;
	}
	return( wb );
}
