/* Copyright (C) 2001 Michael Leonhard
 * Mike Leonhard
 * mike at tamale dot net
 * http://tamale.net/
 */

#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "ascorbic.h"
#include "build.h"

int Ascorbic_Error( struct Ascorbic *asc, char *text ) {
	assert( asc );
	assert( text );
	
	/* free previous error */
	if( asc->errortext != NULL ) free( asc->errortext );
	
	/* allocate for error text */
	asc->errortext = (char *)malloc( strlen( text ) + 1 );
	assert( asc->errortext ) ;
	
	/* copy error text */
	strcpy( asc->errortext, text );
	
	return -1;
	}

int Ascorbic_ErrorHere( struct Ascorbic *asc, char *here, char *text ) {
	assert( asc );
	assert( asc->source );
	assert( here );
	
	/* calculate place */
	asc->place = here - asc->source;
	assert( asc->place > -1 );
	
	return Ascorbic_Error( asc, text );
	}

int Ascorbic_ErrorParticle( struct Ascorbic *asc, struct Particle *particle, char *text ) {
	assert( particle );
	assert( particle->place > -1 );
	
	/* error is at particle place */
	asc->place = particle->place;
	
	return Ascorbic_Error( asc, text );
	}

int Ascorbic_PError( struct Ascorbic *asc, char *text ) {
	const char *syserrtext;
	
	assert( asc );
	assert( text );
	
	/* get system error description */
	syserrtext = strerror( errno );
	
	/* free previous error */
	if( asc->errortext != NULL ) free( asc->errortext );
	
	/* allocate for error text */
	asc->errortext = (char *)malloc( strlen( text ) + 2 + strlen( syserrtext ) + 1 );
	assert( asc->errortext ) ;
	
	/* copy error text */
	strcpy( asc->errortext, text );
	strcat( asc->errortext, ", " );
	strcat( asc->errortext, syserrtext );
	
	return -1;
	}

int Ascorbic_PrintError( struct Ascorbic *asc ) {
	char *etext, *emsg;

	assert( asc );
	
	/* no error text */
	if( asc->errortext == NULL ) etext = "unknown error";
	/* error text is available */
	else etext = asc->errortext;
	
	/* allocate for error msg */
	emsg = (char *)malloc( strlen( etext ) + 10 );
	assert( emsg );
	
	/* error message */
	strcpy( emsg, "error: " );
	
	/* error text */
	strcat( emsg, etext );
	
	/* end of line */
	strcat( emsg, "\n" );
	
	/* output the error */
	printf( "%s", emsg );
	
	/* clean up */
	free( emsg );

	/* 1 is the proper error code for a command line app.            */
	/* Returning 1 lets main() do return Ascorbic_PrintError( asc ); */
	return 1;
	}

int Ascorbic_PrintErrorOnLine( struct Ascorbic *asc ) {
	Ascorbic_PrintError( asc );
	Ascorbic_PrintLine( asc );
	return 1;
	}

int Ascorbic_PrintLine( struct Ascorbic *asc ) {
	char *etext, *line, *here, *place;
	int num, first, p;

	assert( asc );
	assert( asc->source );
	
	/* derive place from first unprocessed particle */
	if( asc->place < 0 ) {
		assert( asc->list );
		assert( asc->i > -1 );
		if( asc->i >= asc->list->childnum ) asc->i = asc->list->childnum - 1;
		asc->place = asc->list->child[ asc->i ]->place;
		/*printf( "derived place %d\n", asc->place );/**/
		}
	/*else printf( "place is %d\n", asc->place );/**/
	
	/* place */
	place = asc->source + asc->place;
	
	/* find the line */
	num = 0;
	line = asc->source;
	here = asc->source;
	while( 1 ) {
		/* traverse line */
		while( *here && *here != '\n' && here != place ) {
			/* change tab to space */
			if( *here == '\t' ) *here = ' ';
			here++;
			}
		
		/* reached place */
		if( here == place ) break;
		
		/* end of source */
		if( *here == 0 ) break;
		
		/* end of line */
		if( *here == '\n' ) {
			here++;
			line = here;
			num++;
			}
		
		/* move on */
		else here++;
		}
	
	/* place must be in source */
	assert( here == place );
	
	/*printf( "line is at %d\n", line - asc->source );/**/
	
	/* go to end of line */
	while( *here && *here != '\n' ) here++;
	
	/* terminate line */
	*here = 0;
		
	/* allocate for printout */
	etext = (char *)malloc( strlen( asc->filename ) + 32 + strlen( line ) );
	assert( etext );

	/* render first part of printout */
	sprintf( etext, "%s, line %d: ", asc->filename, num + 1 );
	
	/* first part len */
	first = strlen( etext );
	
	/* add line to text */
	strcat( etext, line );
	
	/* print etext */
	printf( "%s\n", etext );
	
	/* place column */
	first += place - line;
	
	/* render arrow shaft */
	for( p = 0; p < first; p++ ) etext[p] = '-';
	
	/* arrow head */
	etext[p] = '^';
	p++;
	
	/* null terminate arrow */
	etext[p] = 0;

	/* print arrow */
	printf( "%s\n", etext );
	
	/* clean up */
	free( etext );

	/* 1 is the proper error code for a command line app.            */
	/* Returning 1 lets main() do return Ascorbic_PrintError( asc ); */
	return 1;
	}

int main( int argc, char *argv[] ) {
	struct Ascorbic *asc;
	int ret;
	
	/* check command line */
        if( argc != 3 ) return usage();

	/* allocate Ascorbic struct */
	asc = (struct Ascorbic *)malloc( sizeof( struct Ascorbic ) );
	assert( asc );
	
	/* initialize Ascorbic struct */
	asc->filename = argv[1];
	asc->source = NULL;
	asc->out = NULL;
	asc->errortext = NULL;
	asc->place = -1;
	asc->list = NULL;
	asc->tree = NULL;
	asc->i = -1;
	asc->symbols = NULL;
	asc->defncount = -1;
	asc->frameheight = -1;
	
	/* open output file */
	ret = File_OpenOutput( asc, argv[2] );
	
	/* open failed */
	if( ret == -1 ) return Ascorbic_PrintError( asc );
	
	/* read the source code */
	ret = File_ReadSource( asc );
	
	/* read failed */
	if( ret == -1 ) return Ascorbic_PrintError( asc );

	/* parse source code into tokens */
	ret = Tokenize_Source( asc );
	
	/* parse failed */
	if( ret == -1 ) return Ascorbic_PrintErrorOnLine( asc );
	
	/* print token list */
	printf( "\n" );
	Particle_Print( asc, asc->list, 0 );
	
	/* build tree from tokens */
	ret = Parse_CreateTree( asc );
	
	/* tree build failed */
	if( ret == -1 ) return Ascorbic_PrintErrorOnLine( asc );
	
	/* fix place marker in case of error */
	asc->place = strlen( asc->source );
	
	/* print tree */
	printf( "\n" );
	Particle_Print( asc, asc->tree, 0 );
	printf( "\n" );

	/* write assembly code */
	ret = Assembly_Write( asc );
	
	/* assembly writing failed */
	if( ret == -1 ) return Ascorbic_PrintErrorOnLine( asc );
	
	/* close the output file */
	ret = File_CloseOutput( asc );
	
	/* close failed */
	if( ret == -1 ) return Ascorbic_PrintError( asc );

	/* compiler success*/
	return 0;
        }

int usage() {
	printf( "Ascorbic version %s build %d, %s\n", VERSION, BUILDNUM, BUILDDATE );
	printf( "Mike Leonhard, mike at tamale dot net, http://tamale.net/\n\n" );
	printf( "usage: ascorbic in.asc out.sasm\n" );
	return 1;
        }
