
/************************************************************************
 *                   bascy.c                                            *
 ************************************************************************
 *   BASIC to C Language Translator - Main File                         *
 ************************************************************************
 *   Part of the project : BASCY  Version 1.0                           *
 *   Copyright (C) 2001 - S. Narasimhan, No.11, Sathya Nagar,           *
 *                        MADURAI-625018. INDIA                         *
 *                       email: narasimhan_s_@hotmail.com               *
 ************************************************************************
 * This program is free software; you can redistribute it and/or modify *
 * it under the terms of the GNU General Public License as published by *
 * the Free Software Foundation; either version 2 of the License, or    *
 * (at your option) any later version.                                  *
 *                                                                      *
 * This program is distributed in the hope that it will be useful,      *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 * GNU General Public License for more details.                         *
 *                                                                      *
 * You should have received a copy of the GNU General Public License    *
 * along with this program; if not, write to the Free Software          *
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                      *
 ************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "basic.h"
#define  prgname        "BASCY"
#define  dotbas 	".bas"
#define	 dotc		".c"
#define  EXTNC          "c"
extern int fileopcount;
extern unsigned int datablkount;
char    datfil[MAXFNLEN];
char	word[MAXWORDLEN];
char    indent[MAXINDENT+1] = "";
unsigned int    lincount, errcount, wrncount, forcount, whilecount;
unsigned int    docount, linestart, gosubcount, retcount, prvlinestart;
int timehcount = 0;
int AlreadyDefined = FALSE;
int NeedSource = YES;  /* By default, Source Line Number Info is included */
struct symbol symtab[HTSIZE];
FILE  *fin, *fout, *ffun, *ftmp;
int main(int argc, char *argv[])
{
	char source[MAXFNLEN];
        char objfile[MAXFNLEN];
        char funfile[MAXFNLEN];
        char tmpfile[MAXFNLEN];
        int i;
        init();  /*Initialize the symbol table */
	printf("\n***********************************************");
        printf("\nBASCY : Basic Language to C Language Translator");
        printf("\n          Version 1.0");
        printf("\n(c)Copyright 2001 - S. Narasimhan");
	printf("\nAll Rights Reserved");
	printf("\n***********************************************");
	if (argc == 1){
                printf("\nUsage: %s file [option]\n\n", prgname);
		printf("        Where file - Basic source file\n");
                printf("        option: -x - Exclude Source Line Number Info\n");
                printf("        On output - the translated file.c will be produced\n");
		exit (1);
	}
        if(argc > 2) { /* There are options */
           if(strcmp(argv[2],"-x") == 0)
              NeedSource = NO;
        }
        strcpy(source, argv[1]);
        for(i = 0; ((datfil[i] = funfile[i] = tmpfile[i] = objfile[i] = source[i]) != '\0'); i++)
	{
                if (objfile[i] == '.')
		   break;
	}

	if (objfile[i] != '.')
	{
                strcat(datfil,".fdt");
                strcat(tmpfile,".tmp");
                strcat(funfile,".fun");
                strcat(objfile, dotc);
		strcat(source,dotbas);
	}
        else {
                datfil[i+1] = funfile[i+1] = tmpfile[i+1] = objfile[i+1] = '\0';
                strcat(objfile,EXTNC);
                strcat(funfile,"fun");
                strcat(tmpfile,"tmp");
                strcat(datfil,"fdt");
        }
        fin  = efopen(source,"r");
        fout = efopen(objfile,"w");
	fputs("\n/*Original Basic Language Source File Name :",fout);
	fputs(source,fout);
	fputs("*****/",fout);
        fputs("\n/*Translated file produced by BASCY program Ver1.0*******/",fout);
	fputs("\n/*The Basic to C - Language Translator (c)copyright2001*/",fout);
	fputs("\n/***** By S. Narasimhan ********************************/\n",fout);
        fprintf(fout,"\n/************************************************/");
        fprintf(fout,"\n/*       MANIFEST CONSTANTS                     */");
        fprintf(fout,"\n/************************************************/");
        fputs("\n#define  MAXSTRLEN   255", fout);
        fflush(fout);
        fprintf(fout,"\n#define DEGRAD  3.141592653589793227020265931059839203954/180.0");
        fprintf(fout,"\n#define ERRLIMIT  1.0E-15");
        fflush(fout);
        fprintf(fout,"\n/************************************************/");
        fprintf(fout,"\n/*   GLOBAL VARIABLES/DECLARATIONS/HEADERS      */");
        fprintf(fout,"\n/************************************************/");
        fputs("\nchar  _tmp_[MAXSTRLEN+1] = \"\";", fout);
        fputs("\nchar  _tmp1_[MAXSTRLEN+1] = \"\";", fout);
        fputs("\nchar  COMMAND_[MAXSTRLEN+1] = \"\";", fout);
        fputs("\nint   _err = 0;", fout);
        fputs("\ndouble prand = -1.0;",fout);
        fputs("\nint   ERR  = 0;", fout);
        fflush(fout);
        ftmp = efopen(tmpfile,"w");
        ffun = efopen(funfile,"w");
        fputs("\nmain(int  argc, char  **argv)",ftmp);
        fputs("\n{",ftmp);
        fflush(ftmp);
        SetIndent();
        commandprc(); /* set up command tail in the string COMMAND_ */
        fprintf(ftmp,"\n%sgetDataQ();",indent);
        errcount = wrncount = 0;
	lincount = 1;
	linestart = TRUE;
        forcount = 0;
        whilecount = 0;
        docount = 0;
        gosubcount = retcount = 0;
        xlatline();
        fprintf(ftmp,"\n%sreturn 0;",indent); 
        DeIndent();
        fprintf(ftmp,"\n%s} /*End of main*/",indent);
        fprintf(ftmp,"\n/************************************************/");
        fprintf(ftmp,"\n/*  CODE FOR LIBRARY AND USER DEFINED FUNCTIONS */");
        fprintf(ftmp,"\n/************************************************/");
        fflush(ftmp);
        AddHeaders();        /* Add the additional header files as reqd. */
        if(fileopcount > 0)  /* If there are file operations */
           InitFilePrc();    /* add the filehandler routine */
        fclose(fin);
        fclose(fout);
        fclose(ffun);
        fclose(ftmp);
        if (errcount == 0)
                cleanup(); /* Miscellaneous diagnostics given now */
        if (errcount == 0)
                combine(objfile, tmpfile, funfile);
        else
                remove(objfile);
        remove(tmpfile);
        remove(funfile);
        remove(datfil);
        printf("\n%d line(s) processed", lincount);
	printf("\n%d  error(s) found", errcount);
        printf("\n%d warning(s) issued\n", wrncount);
        if (errcount > 0) exit(1);
	exit(0);
        return 0; /* Unreachable. But added to satisfy the compiler */
}   /* End - Main() */
FILE *efopen(char *fil, char *mode)
/* Opens the file whose name is contained in the string 'fil' in
 * 'mode' and returns the file pointer of the opened file.
 * In case of error, error message is printed in stderr and
 * program is terminated/ aborted */
{
        FILE *fp;
        if ((fp = fopen(fil,mode)) == NULL){
                fprintf(stderr,"\nCan't Open %s\n",  fil);
                exit (1);
	}
	return fp;
} /* End of efopen() */
int lex(void)
/* Gets the next lexical element from the source file stream and returns its type *
 * This routine is repeatedly called by the Parse routine(xlatline)****************/
{
int c;
while ((c=getc(fin)) == ' ' || c == '\t' || c == '\r')
	;
if (c == EOF)
	return 0;
if (c == '"')  { /* Quoted string literal */
	int i;
        for(i = 0 ; (c = getc(fin)) != '"'; i++) {
		if (c == '\n' || c == EOF) {
			error(253);
			return(-1);
		}
		if ( i >= MAXWORDLEN -1 ) {
			error(254);
			return(-1);
		}
		word[i] = c;
	}
	word[i] = '\0';
        return STRING;
}
if (isalpha(c)) {
	char *p = word;
	int  s;
	do {
		if ( p >= word + MAXWORDLEN - 1) {
			error(255);
			return -1;
		}
		*p++ = c;
	} while ((c = getc(fin)) != EOF && isalnum(c));
	if ( c == '$' )   /* May be a string VAR */
		*p++ = c;
	else  ungetc(c,fin);
	*p = '\0';
	if ( p > word + MAXIDLEN - 1) {
		error(255);
		return(-1);
	}
        word2upper(word); /* Capitalize the word */
        {  /* Test if the first 3 chars of the line indicate a comment i.e. REM */
                char t[4];
                t[0] = word[0];
                t[1] = word[1];
                t[2] = word[2];
                t[3] = '\0';
                if(strcmp(t,"REM") == 0)
                        strcpy(word,t);
        }
        if (( s = lookup(word)) < 0) { /*If Word not previously available in Symbol table */
                AlreadyDefined = FALSE;
                /* it is assumed to be VAR */
                if (*(p-1) == '$')
                        s = instal(word, STRVAR,0);
                else
                        s = instal(word, NUMVAR,0);
                if (s == -1) {
			error(248);
			return -1;
                }
	}
        else { /*If word available in SymTab, what is its u.ival*/
                int uval, typ;
                typ = symtab[s].type;
                uval = symtab[s].u.ival;
                AlreadyDefined = TRUE;
                if (((typ == NUMVAR) || (typ == STRVAR)) &&
                    (uval == 0))  /*VAR previously defined */
                        symtab[s].u.ival = lincount;
        }
        return symtab[s].type;
}
if ((c == '.') || isdigit(c)) {
	ungetc(c,fin);
	if (getnumber() == -1)
		return -1;
	if (linestart) { /* It should be a statement label */
                int s, val;
                if ((s = lookup(word)) >= 0 ) { /*Is it already there in symbol table it is DUPLICATE*/
                        val = symtab[s].u.ival;
                        if (val > 0) /*Yes.But it was only referred in goto and being defined now only*/
                                symtab[s].u.ival = 0;
                        else { /*If duplicate label definition, control reaches here*/
                                error(247);
                                return (-1);
                        }
		}
                else if (instal(word, LABEL, -lincount) == -1) {
			error(248);
			return -1;
		}
		return LABEL ;
	}
	/*else it's type is NUMBER*/
	return NUMBER;
}
switch(c) {
        int rtnval;
        case '+'  :
        case '-'  : word[0] = c;
                    word[1] = '\0';
                    return UNOP;
        case '>'  : rtnval = follow('=', GE, GT);
                    if(rtnval == GE)
                        strcpy(word,">=");
                    else strcpy(word,">");
                    return (rtnval);
        case '!'  : rtnval = follow('=', NE, c);
                    if(rtnval == NE)
                        strcpy(word, "!=");
                    else { word[0] = c; word[1] = '\0'; }
                    return (rtnval);
        case '<'  : rtnval = follow('=', LE, follow('>', NE, LT));
                    if(rtnval == LE)
                        strcpy(word, "<=");
                    if(rtnval == NE)
                        strcpy(word, "!=");
                    if(rtnval == LT)
                        strcpy(word, "<");
                    return (rtnval);
        case '^'  :
        case '*'  :
        case '\\' : /* integer division */
        case '/'  : word[0] = c;
                    word[1] = '\0';
                    return BINOP;
        default   : word[0] = c;
                    word[1] = '\0';
                    return c;
}
} /* End - lex() */
int xlatline(void)
/* Translate the BASIC language statements into C language statements
 * and write the translated string into the objfile ( i.e. file pointer-
 * fout or ftmp as appropriate
 */
{
        int i;
	do {
                word[0] = '\0';
		i = lex();
                if (i < 0) return -1; /*Take care of error in lex() */
                prvlinestart = linestart;
                linestart = FALSE;
                switch(i) {
                     case STATKEY : if(statement() < 0)
                                        return -1;
                                    break;
                     case LABEL : labelprc();
                                  break;
                     case NUMARR :
                     case STRARR :
                     case NUMVAR :
                     case STRVAR : asgn1();
                                   break;
                     case BUILTIN  :
                     case OTHERKEY :
                     case STRING :
                     case NUMBER : error(246);
                                   return -1;

                     case '\'' : remstat();
                                 break;
                     case '\n' : lincount++ ; linestart = TRUE ;
                                 break;
                     case ':'  : 
                     case  0   : break;
                     default :
                                  error(246);
                                  return -1;
                }
        } while (i > 0);
        return 0;
}  /* End - xlatline() */
int statement(void)
/* This routine parses for a statement subsequent to the statement keyword */
{
           int s, funid;
           s = lookup(word);
           funid = symtab[s].u.ival;
           switch(funid) {
                case  1 : return(remstat());
                case  2 : return(letprc());
                case  3 : return(ifprc());
                case  4 : return(forprc());
                case  5 : return(dimstat());
                case  6 : return(nextprc());
                case  7 : return(gotoprc());
                case  8 : return(gosubprc());
                case  9 : return(onprc());
                case 10 : return(cls());
                case 11 : return(readprc());
                case 12 : return(dataprc());
                case 13 : return(printprc());
                case 14 : return(inputprc());
                case 15 : return(endprc());
                case 16 : return(returnprc());
                case 17 : return(randomprc());
                case 18 : return(defstat());
                case 19 : return(restoreprc());
                case 20 : beep();
                          break;
                case 21 : return(haltprc());
                case 22 : return(swaprc());
                case 23 : return(killprc());
                case 24 : return(shellprc());
                case 25 : return(nameprc());
                case 26 : return(whileprc());
                case 27 : return(wendprc());
                case 28 : return(doprc());
                case 29 : return(looprc());
                case 30 : return(exitprc());
                case 31 : return(writeprc());
                case 32 : return(lineprc());
                case 33 : return(envirstat());
                case 34 : return(sleeprc());
                case 35 : return(closeprc());
                case 36 : return(openprc());
                case 37 : return(elseprc());
                case 38 : return(elseifprc());
                case 39 : return(endifprc());
                case 40 : return(RmChDirprc("chdir"));
                case 41 : return(RmChDirprc("rmdir"));
                case 42 : return(chainprc());
           }
}
int getnumber()
/* This routine expects the next lexical token to be UNSIGNED NUMBER and reads the same into word */
{
	char *p = word;
	char c;
	while ((c = getc(fin)) != EOF && isdigit(c)) {
		if ( p >= word + MAXIDLEN - 1) {
			error(255);
			return (-1);
		}
		*p++ = c;
	}
	if ( c == '.' ) {
		if ( p >= word + MAXIDLEN - 1) {
			error(255);
			return (-1);
		}
		*p++ = c;
		while ((c = getc(fin)) != EOF && isdigit(c)) {
		  	if ( p >= word + MAXIDLEN - 1) {
				error(255);
				return (-1);
		  	}
			*p++ = c;
		}
	}
	*p = '\0';
	ungetc(c,fin);
	return 0;
} /* End - getnumber() */
void word2upper(char *word)
/* Converts the word to uppercase */
{

        char c;
        while(*word != 0) {
                c = *word;
                *word++ = toupper(c);
        }
}  /*End - word2upper() */
void cleanup(void)
/*Cleanup routine done after completion of parsing*/
{
        int i, val, oldline;
        for ( i = 0; i < HTSIZE; i++) {
                if (symtab[i].type == LABEL || symtab[i].type == PROC) {
                        oldline = lincount ; strcpy(word,symtab[i].name);
                        val = symtab[i].u.ival;
                        lincount = (val < 0) ? -val : val;
                        if (val > 0)
                            error(244);
                        else if (-val > 0)
                                warning("Label Not Used");
                        lincount = oldline;
                }
        }
        if(forcount != 0) /* FOR without NEXT */
                error(235);
        if(whilecount != 0) /* WHILE without WEND */
                error(225);
        if(docount != 0) /* DO without LOOP */
                error(220);
        if(poploop() == BLOCKIF) /* Block IF without END IF */
                error(202);
        if(datablkount > 0) {
         /* Test for the condition of READ/RESTORE statement
          * with no DATA statement which is an error.
          * On the other hand, DATA statements may be present
          * without any READ statement which is not an error */
           FILE  *fdat;
           if((fdat = fopen(datfil,"r")) == NULL)
               error(201);
           else
               fclose(fdat);
        }
}   /* End - cleanup() */
int follow(int expect, int ifyes, int ifno)
/* Look ahead for >=, <>, (* etc.  */
{
	int c = getc(fin);
        if ( c == expect )
		return ifyes;
	ungetc(c,fin);
	return ifno;
}  /* End - follow() */
char *emalloc(unsigned n)  /*check return from malloc() */
{
	char *p;
	if((p = malloc(n)) == NULL) 
		error(249);
	return p;
} /* End - emalloc() */
void combine(char *filobj, char *filtmp, char *filfun)
/* Combines filobj,filtmp,filfun files appropriately to create the
 * final translated filobj file. The file 'datfil' is added at the
 * end of the final file.
 */
{
        if ((fout = fopen(filobj,"a")) == NULL){
                printf("\nCan't Open %s\n",  filobj);
		 exit (1);
	}
        if ((ftmp = fopen(filtmp,"r")) == NULL){
                printf("\nCan't Open %s\n",  filtmp);
		 exit (1);
	}
        append(fout, ftmp);
        fclose(ftmp);
        if ((ffun = fopen(filfun,"r")) == NULL){
                printf("\nCan't Open %s\n",  filfun);
		 exit (1);
	}
        append(fout, ffun);
        fclose(ffun);
        /* At the end, insert the file 'datfil' as a function */
        fprintf(fout,"\nint getDataQ()");
        fprintf(fout,"\n{");
        fflush(fout);
        if(datablkount > 0) {
           FILE  *fdat;
           if((fdat = fopen(datfil,"r")) == NULL){
                printf("\nCan't Open %s\n",  datfil);
		 exit (1);
           }
           append(fout, fdat);
           fclose(fdat);
        }
        fprintf(fout,"\n  return 0;");
        fprintf(fout,"\n}");
        fflush(fout);
        fclose(fout);
} /* End - combine() */
void append(FILE *fo, FILE *fi)
/* Appends the file fi to file fo . Both the files should be open
 * fi for read mode and fo for append mode
 */
{
   int c;
   while (( c = getc(fi) ) != EOF )
        putc(c, fo);
   fflush(fo);
} /* End - append */
void ungetw(char *s, FILE *fp)
/* Pushes the word/string s read into the input stream fp */
{
        int i, len;
        len = strlen(s);
        for(i = len - 1; i >= 0 ; i--)
                ungetc(s[i], fp);
}  /* End - ungetw() */

int StatEnd(int tok)
/* Tests if the token 'tok' is End of Statement.
 * If so, returns TRUE otherwise returns FALSE */
{
   return(tok == '\n' || tok == ':' ||
           tok == '\'' || tok == 0);
} /* End - StatEnd */

void EmitSource(void)
/* Includes the Source Line Number Info in the translated/target file */
{
     if(NeedSource){
        fprintf(ftmp,"\n/************** Line = %d ************/", lincount);
        fflush(ftmp);
     }
} /* End of EmitSource */
