
//--------------------------------------------------------------------------
//
//  parser.C
//
//  Ada Package Parser
//
//  Items included in this file:
//
//	Parse
//	Private_is_only_token
//	GetPackageName
//	ExtractCode
//	Get_Routine
//	Get_Parameters
//	GetFullDecl
//	LoadGenericInfo
//	UpdateGenericType
//
//--------------------------------------------------------------------------

#include "drgen.h"

// #ifdef UNIX
// extern strupr(char *);
// #endif

#define TRUE 1
#define FALSE 0

const char *Is = " IS";
const char *Comment = "--";
const char *Package = "PACKAGE ";
const char *Private = "PRIVATE";
const char *Procedure = "PROCEDURE";
const char *Function = "FUNCTION ";
const char *Return = "RETURN ";
const char *Open_Paren = "(";
const char *Close_Paren = ")";
const char *comma = ",";
const char *In = "IN ";
const char *Out = "OUT";
const char *InOut = "IN-OUT";
const char *colon = ":";
const char *semicolon = ";";
const char *Eq_Divider = "=========================\n";
const char *Dash_Divider = "&&&&&&&&&&&&&&&&&&&&&&&&&\n";

// Globals
static LinkedList GenericList;

// Function Prototypes
int Private_is_only_token(char *);
char * Get_PackageName (char *, ofstream&);
char * Extract_Code (ifstream&);
void Get_Routine(const char *, ifstream&, ofstream&);
void Get_Parameters(char *, ofstream&);
void Parse(char *filename, char *GenericInstFile);
char *GetFullDecl(const char *Code, int *formal, ifstream& inFile,
			const char *Routine);
void LoadGenericInfo(char *filename, char *GenericInstFile);
void UpdateGenericType(char *Type);


//----------------------------------------------------------------------------

void Parse (char *filename, char *GenericInstFile) {

	ifstream inFile(filename, ios::in);	//opens input file
	if (!inFile) {				//open failed?
		cerr <<"Cannot open "<<filename<<" for input.\n";
		exit (1);                      //open failed and exits
	}

	char *outfilename;
	char *p;
	outfilename = new char [strlen(filename) + 9];
	strcpy(outfilename,filename);
	p = strrchr(outfilename, '\\');

	if (p)      			// if path is specified, use it
		strcpy(++p,"lisa.dat");
	else
		strcpy(outfilename, "lisa.dat");

	ofstream outFile(outfilename, ios::out);
	if (!outFile) {				//open failed?
		cerr <<"Cannot open "<<outfilename<<" for output.\n";
		exit (1);                     //open failed and exits
	}

	// load generic TYPE parameters info. into linked list
	LoadGenericInfo(filename, GenericInstFile);

	char *Code;   // Code - BeFore Comments - holds the actual code
	char *PackageName;
	int PackageFound = FALSE;

	while (!inFile.eof()) {	// loop until EOF or package name found again
		Code = Extract_Code(inFile);   //get code from the input file

		if (!PackageFound && (strstr(Code, Package))) {
			// if the package name has not been found and the
			// string package is found then get the package name
			PackageName = Get_PackageName(Code, outFile);
			// PackageName = get_token(inFile);
			// strupr(PackageName);
			// outFile << PackageName << endl;
			PackageFound = TRUE;
		}
		// terminate parser when PRIVATE is seen on a line by itself
		else if (PackageFound && (Private_is_only_token(Code))) {
			delete [] PackageName;
			delete [] Code;
			break;
		}

		if (PackageFound) {	// if the packagename has been found
					// and procedure or function is in code
			if (strstr(Code, "PROCEDURE") ||
					strstr(Code, "FUNCTION"))
				Get_Routine(Code, inFile, outFile);
		}

		delete [] Code;
	}

	inFile.close(); 	// closes input file
	outFile.close();	// closes output file

	delete outfilename;
}

int Private_is_only_token(char *Code) {
	char *p = Code;
	// eliminate white space at front of line
	while (*p == ' ' || *p == '\t')
		p++;
	// eliminate white space at end of line
	int len = strlen(p) - 1;
	while ( *(p+len) == ' ' || *(p+len) == '\t')
		len--;
	*(p+len+1) = '\0';
	// check if PRIVATE is the only thing left on the line
	if (strcmp(p,Private) == 0)
		return 1;
	else
		return 0;
}

char * Get_PackageName (char * BF_Comment, ofstream& outFile) {
	char *string;
	int length;
	char *PackageName;

	PackageName = new char [40];
	string = strstr(BF_Comment, Package); // puts in string the string
				// starting with package until the comment.

	string = &string[8];	// reassigns string leaving of "package"

	while (string[0] == ' ') {       // moves string forward dropping off WS
		string = &string[1];    // as long as white space is found
	}

	//finds actual package name by dropping off is" at the end of name
	length = strlen(string) - strlen(strstr(string,Is));

	outFile.write(string,length);  // writes package name to an output file
	outFile.put('\n');

	strncpy(PackageName, string, length); // assigns name of package
	PackageName[length] = '\0';

	return(PackageName);
}


char * Extract_Code (ifstream& inFile) {
	char Line[101];

	//read a line from input file
 	inFile.getline (Line, 100, '\n');

	for (char *p = Line; *p != '\0'; p++)  // truncate off comments
		if (*p == '-' && *(p+1) == '-') {
			*p = '\0';
			break;
		}

	char *BF_Comment;
	BF_Comment = new char [strlen(Line) + 1];
	if (!BF_Comment) {
		cout << "Memory allocation error !\n";
		exit(1);
	}

	strcpy(BF_Comment, Line); // return a pointer to extracted line of code
	strupr(BF_Comment);
	return(BF_Comment);
}



void Get_Routine(const char * Code, ifstream& inFile, ofstream& outFile) {
	char *Line, *Line2;
	int formal_part;
	int more_routines = 0;
	char *p3, *p4;
	char Routine[10];

	// determine whether PROCEDURE or FUNCTION
	p3 = strstr((char *) Code, "PROCEDURE");
	p4 = strstr((char *) Code, "FUNCTION");
	if (p3 != NULL)
		strcpy(Routine, "PROCEDURE");
	else
		strcpy(Routine, "FUNCTION");

	// get complete line of 'Code'
	Line = GetFullDecl(Code, &formal_part, inFile, Routine);

	char *p1, *p2;
	p1 = strstr(Line, Routine);
	p1 = p1 + strlen(Routine);   // skip the keyword PROCEDURE or FUNCTION
	while (*p1 == ' ' || *p1 == '\t')
		p1++;

	p3 = strstr(p1, "PROCEDURE"); // if additional ROUTINEs are present
	p4 = strstr(p1, "FUNCTION");  // in `Line', save them in  `Line2'
	if (p3 != NULL)
		p2 = p3;
	else
		p2 = p4;

	if (p2) {
		Line2 = new char [strlen(p2) + 1];
		strcpy(Line2, p2);
		*p2 = '\0';
		more_routines = 1;
	}

	char RoutineName[40], ch;         // read procedure or function name
	for (p2=p1;  *p2!=' ' && *p2!='\t' && *p2!=';' && *p2!='(';  p2++)
		/* NULL */ ;

	ch = *p2;
	*p2 = ' ';
	sscanf(p1, "%s", RoutineName);
	*p2 = ch;

	outFile << Eq_Divider;     // write out procedure/function name
	outFile << RoutineName << endl;

	// if routine is a FUNCTION, write out its `return type'
	if (strcmp(Routine, "FUNCTION") == 0) {
		p3 = strstr(Line, "RETURN");
		p3 = p3 + 6;     // skip RETURN

		char RetType[40];
		for (p4=p3; *p4 != ';'; p4++)
			/* NULL */ ;
		*p4 = ' ';
		sscanf(p3, "%s", RetType);
		*p4 = ';';
		outFile << RetType;
	}

	outFile << endl;

	if (formal_part) {           // write out parameter information
		while (*p2 == ' ' || *p2 == '(' || *p2 == '\t')
			p2++; // skip '('
		Get_Parameters(p2, outFile);
	}
	delete [] Line;

	if (more_routines) {      // process additional ROUTINEs if present
		Get_Routine(Line2, inFile, outFile);
		delete [] Line2;
	}
}


void Get_Parameters(char *pars, ofstream& outFile) {
	char *start_ptr, *mid_ptr, *end_ptr;
	char Mode[8], Type[40], Var[40];

	start_ptr = pars;           // fix position of start pointer
				    // fix position of mid pointer at COLON
	mid_ptr = strstr(start_ptr, ":");
	if (!mid_ptr) return;

	// fix position of end pointer
	for (end_ptr = mid_ptr; *end_ptr!=';' && *end_ptr!=')'; end_ptr++)
		/* NULL */ ;

	*end_ptr = '\0';                    // get MODE and TYPE.
	char buf1[40], buf2[40], buf3[40]; //They are between mid_ptr & end_ptr
	sscanf(mid_ptr+1, "%s%s%s", buf1, buf2, buf3);
	if (strcmp(buf2, "OUT") == 0) {
		strcpy(Mode, "IN-OUT");
		strcpy(Type, buf3);
	}
	else if (strcmp(buf1, "OUT") == 0  || strcmp(buf1, "IN") == 0) {
		strcpy(Mode, buf1);
		strcpy(Type, buf2);
	}
	else {
		strcpy(Mode, "IN");
		strcpy(Type, buf1);
	}

	UpdateGenericType(Type);  // if Type is generic, put actual type instead

	// get all variable names. They are between start_ptr and mid_ptr
	int last_var;
	char *p;
	for (p=start_ptr, last_var=0; last_var!=1; p++) {
		if (*p == ':')
			last_var = 1;
		if (*p == ':' || *p == ',') {
			*p = ' ';
			sscanf(start_ptr, "%s", Var);
			outFile << Dash_Divider;     // write Var Mode and Type
			outFile << Var << endl << Mode << endl << Type << endl;
			start_ptr = p+1;
		}
	}
	Get_Parameters(++end_ptr, outFile);   // get the next set of parameters
}


char *GetFullDecl(const char *Code, int *formal, ifstream& inFile,
							const char *Routine) {
	char *p;
	char *Line1, *Line2, *Code1;

	p = strstr((char *) Code, Routine);
	while (*p != '(' &&  *p != ';' &&  *p != '\0')
		p++;

	switch(*p) {  //if no formal parameters, declaration is complete
		case ';':	*formal = 0;
				Line1 = new char[strlen(Code) + 1];
				strcpy(Line1, Code);
				break;

		case '(':	*formal = 1;
				while (*p != ')' && *p != '\0')
					p++;

				// declaration is complete only if ')' is found
				// and then ';' is found.
				if (*p == ')') {
					while (*p != ';' && *p != '\0')
						p++;
					if (*p == ';') {
						Line1=new char[strlen(Code)+1];
						strcpy(Line1, Code);
						break;
					}
				}

		// otherwise, get and attach another line of code
		case '\0' :	Code1 = Extract_Code(inFile);
				Line2 = new char [strlen(Code)+strlen(Code1)+2];
				strcpy(Line2, Code);
				strcat(Line2, Code1);
				delete [] Code1;

				Line1=GetFullDecl(Line2,formal,inFile,Routine);
				delete [] Line2;
				break;
	}
	return (Line1);
}


//-------------------------------------------------------------------------
//  Input: GenericInstFile (file containing generic instantiation)
//  Output: A global (global to this file only) Linked list containing info
//          about generic TYPE parameters.
//-------------------------------------------------------------------------
void LoadGenericInfo(char *filename, char *GenericInstFile) {
	ifstream fin(GenericInstFile, ios :: in);    // open input file
	if (!fin)
		return;

	char str[GENERIC_LINE] = "";                 // goto the marker
	while (strcmp(str, GENERIC_MARKER) != 0  && !fin.eof() )
		// fin >> str;
		strcpy(str, get_token(fin));

	// fin >> str;          // verify instantiation is for VALID input file
	strcpy(str, get_token(fin));
	if (strcmp(str, filename) != 0)
		return;

	char value[TYPENAME_LEN] = "";
	char par_kind[15] = "";
	GenericNode *gptr;
	while (1) {         // build the linked list of only TYPE parameters
		// fin >> str >> value >> par_kind;
		strcpy(str, get_token(fin));
		strupr(str);
		strcpy(value, get_token(fin));
		strupr(value);
		strcpy(par_kind, get_token(fin));
		strupr(par_kind);
		if ( fin.eof() ) {
			fin.close();
			return;
		}
		if (strcmp(par_kind, "TYPE") == 0) {
			gptr = new GenericNode(str, value);
			GenericList.InsertAtTail(gptr);
		}
	}
}

//-------------------------------------------------------------------------
// Input: Global linked list containing generic TYPE info
// Output: Substitutes actual TYPE parameter for generic TYPE parameter
//-------------------------------------------------------------------------
void UpdateGenericType(char *Type) {
	GenericNode *p1, *p2;
	p1 = new GenericNode(Type, "");

	if ((p2 = (GenericNode *)GenericList.GetNode(p1)) != NULL){
		strcpy(Type, p2 -> Substitute);
		}
	delete p1;
}
