/************* symbol table *****************
*** just a list of identifiers and values ***
*
* no support for multiple tables (not difficult)
*
*/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef MAC
#include "foreign.h"
#endif
#include "symbols.h"
#include "options.h"


/* from interface.c */
Boolean longHexScan(char *str,long *val);

/** bad, just temporary **/
extern long n_chars;
extern char *msg;
typedef long word;
typedef unsigned char byte;

/* void Error(const char *fmt, ...); */

typedef struct sym_r{
	struct sym_r *next;
	char id[MAX_CHARS];
	long val;
} sym_r;
typedef sym_r *sym_p;

sym_p table = NULL, last = NULL;
char symSource[256],
	*curSource = symSource;

/* prototypes */
short load_equates(FILE * f, char *fname);
short load_table(FILE * f, char *fname);
char * eval_word(char * s, word *val);
char * eval_byte(char * s, byte *val);


void init_symbols() /** for the future **/
{}

Boolean noSymbols() 
{ return table == NULL; }  /** for external use **/

void trash_sep(char *str)
{
char *tmp =str;
	if (tmp)
	{	while (*tmp==' ' || *tmp=='\t') tmp++;
		strcpy(str,tmp);
	}
}

/* parser of EQUates from asm file */

short load_equates(FILE * f, char *fname)
{
char s[1000],
	 id[500],
	 op[500],
	 args[500];
long val;
short	 line_cnt = 0,
	 sym_cnt = 0;

	rewind(f);
	while (fgets(s,sizeof(s),f)) {
		line_cnt++;
		if (!(*s=='*' || *s=='\n' || *s==' ' || *s=='\t') ) 
		{
			strcpy(id,strtok(s," \t"));
			strcpy(op,strtok(NULL," \t")); 
			/* trash_sep(op); */
			strcpy(args,strtok(NULL,";\r\n")); 
			trash_sep(args);
			if (*args=='$') strcpy(args,args+1);
			trash_sep(args);
			
			if (!strcmp(op,"EQU") || !strcmp(op,"equ")) {
				if (sscanf(args,"%04lx",&val)) 
				{
					if (!insert(id,val)) sym_cnt++;
					else {
						/* Error("Check for duplicates or bad labels\rat line %d of file %s :\r %s = %lx",line_cnt,fname,id,val); */
						break;
					}
				}
			}/*  else return 0; */
		}
	}
	/*  adding filename to sources... */
	if (curSource!=symSource) curSource += sprintf(curSource,"%s",", ");
	curSource += sprintf(curSource,"%s",fname);
	return sym_cnt;
}

/*  parser of strict table format: 
   <id><separators><val>\n
   <id><separators>$<separators><val>\n
   *-initiated comments are accepted
   <val> must not exceed 4 characters (separators included) */

short load_table(FILE * f, char *fname)
{
char s[1000],
	 id[500],
	 rest[500],
	 aux[500],
	 *p,
	 c;
long val;
short	 line_cnt = 0,
	 sym_cnt = 0;
Boolean badDigit = FALSE;

	rewind(f);
	while (fgets(s,sizeof(s),f)) {
		line_cnt++;
		if (!(*s=='*' || *s=='\n') ) 
		{
			strcpy(id,strtok(s," \n\t"));
			strcpy(rest,strtok(NULL,"\n")); 
			trash_sep(rest);
			if (*rest=='$') strcpy(rest,rest+1);
			trash_sep(rest);
			
			/*  id is a label? */
			c = *id;
			if (!isalpha(c) && c!='.' && c!='_') {
				/* Error("Check for non-labels\rat line %d of file %s :\r %s",line_cnt,fname,id); */
				break;
			}
			
			if (strlen(rest)==0) {
				/* Error("Check for bad values\rat line %d of file %s :\r %s = %s",line_cnt,fname,id,rest); */
				break;
			}
			for (p=rest; *p; p++) {
				if (!isxdigit(*p) || (p-rest)>=4 ) { badDigit = TRUE; break; }
			}
			if (badDigit) {
				/* Error("Check for bad hex value\rat line %d of file %s :\r %s",line_cnt,fname,rest); */
				break; /*  out of while */
			}
				
			if  (strlen(id) && sscanf(rest,"%04lx%s",&val,aux))
			{
				if (!insert(id,val)) sym_cnt++;
				else {
					/* Error("Check for duplicates or bad labels\rat line %d of file %s :\r %s = %lx",line_cnt,fname,id,val); */
					break;
				}
			}
			else return 0;
		}
	}
	/*  adding filename to sources... */
	if (curSource!=symSource) curSource += sprintf(curSource,"%s",", ");
	curSource += sprintf(curSource,"%s",fname);
	return sym_cnt;
}
	
short removeAll()
{
sym_p p,q;

	p=table; 
	while (p!=NULL) {
		q = p->next;
		p->next = NULL;
		free(p);
		p = q;
	}
	table = NULL; last = NULL;
	curSource = symSource;
	return 0;
}

short load_symbols(FILE * f, char *fname)
{
short cnt = 0;
	
	removeAll(); /*  to cut down duplicates' problem */
	
	if ((cnt = load_equates(f,fname)) ||
		(cnt = load_table(f,fname))) 
	{
		/*  adding filename to sources... */
		if (curSource!=symSource) curSource += sprintf(curSource,"%s",", ");
		curSource += sprintf(curSource,"%s",fname);
	} 
	return cnt;
}
		
char * print_symbols()
{
char * buf = msg;
char s[30];
short cnt = 0,
	line = 1;
sym_p p;
	for( p=table; p!=NULL; p=p->next) {
		strcpy(s,p->id); strncat(s,"..............",14-strlen(s));
		buf += sprintf( buf, " %-14s %04lx ",s,p->val); /* %-14s for left indentation */
		if ((++cnt % 3)==0) { ++line; buf += sprintf(buf,"\r"); }
	}
	if (cnt==0) 
		buf+= sprintf(buf,"\r( NO SYMBOLS )");
	else
		buf += sprintf(buf,"\r( %d SYMBOL%c FROM %s)\r",cnt,(cnt>1 ? 'S' : ' '),symSource);
	
	return msg;
}


short insert(char *id, long val) /** tail-insert with identity check (only on id) **/
{
sym_p aux ;
long  dummy;

	if (val==NO_SYM) return(-1);
	
	/*  error if the ID is an hex value or a duplicate label */
	if (longHexScan(id,&dummy) || lookup_id(id,&dummy)) return -1; 
	
	/** unique val only => insert **/
	aux = (sym_p)malloc(sizeof(sym_r));
	if (aux==NULL) /** something failed? **/
	{	/* Error("can't allocate new symbol\r"); */
		return(-1); }
	
	if (last==NULL) table = aux;
	else last->next = aux;
	
	aux->next = NULL;
	strcpy(aux->id,id);
	aux->val = val;
	last = aux;
	return 0;
}

Boolean lookup_id(char *id,long *out) /*  first-match search */
{
sym_p p;
	if ( 1 /*Options.symbols */) 
		for( p=table; p!=NULL; p=p->next ) {
			if (!strcmp(p->id,id))
			{ *out = p->val; return TRUE; }
		}
	return FALSE; 
}
	
char *lookup_val(long val) /*  first-match search */
{
sym_p p;
	if (1 /* Options.symbols */)
		for( p=table; p!=NULL; p=p->next ) {
			if (p->val==val)
				return p->id;
		}
	return NULL;
}

/** symbolic evaluation **
** all eval_xx return the rest (unparsed) of the string
** first call: *s must be a pointer to the string to be parsed 
** next calls: s must be NULL
**
** similar to the use of strtok(..) [see string.h]
*/

char * eval_word(char * s, word *val)
{
char *aux;
char *ct = " \t\n";
	aux = strtok(s,ct);
	if (aux && lookup_id(aux,val) ) ;
	else sscanf(aux,"%04lx",val); 
	return strtok(NULL,"");
}

char * eval_byte(char * s, byte *val)
{
char *aux;
char *ct = " \t\n";
long v = *val;
	aux = strtok(s,ct);
	if (aux && lookup_id(aux,&v)) ;
	else { v = *val; sscanf(aux,"%02lx",&v); }
		
	*val = (byte)v;
	return strtok(NULL,"");
}
