/* codegen - Version 1.0 June 10, 2006 Kenny Sharma - kenny.sharma@gmail.com */ #include "std-macros++.h" #include "strlist.h" #include "new-features-stubs.h" #include "plainclist.h" #include "sym++.h" #include "symTab.h" #include "esymtab.h" #include "trans-interface.h" //#include "rsl-to-dict.h" #include "entity.h" #include "type-preds.h" //#include "typechk.hP" #include #include #include #include #include #include #include #include #include "images.h" #include "parser.h" #include "tokens.h" #include "token-mapping.h" /* macros for line size and indents */ #define LINESIZE 80 #define HEADERINDENT 2 #define BODYINDENT 9 #define METHODINDENT 5 /* create the package and translation begin */ void CreatePackage(char *package); void CheckTranslate(int count, char **files, char *folder, char *package, int v); /* Makefile generating stuff */ void CreateMakefile(char *folder, EntityStructList *packages); void PrintHeader(FILE *file, char *folder); void PrintFiles(FILE *file, char *folder, EntityStructList *packages); void PrintPackages(FILE *file, char *folder, EntityStructList *packages); void PrintRun(FILE *file); void PrintExecute(FILE *file, char *folder); void PrintPrint(FILE *file); /* folder and file creation stuff */ void CreateFolders(TransInterface *ti, char *folder, char *package, int v); void CreateFile(char *folder, char* package, char *subpackage, char *name, Sym *s, EntityStructList *objs, EntityStructList *ops, nodep im, int v); void CreateHeader(FILE *file, char *package, char *subpackage, nodep im, Sym *sym, int v); void CreateImports(FILE *file, char *package, nodep imports, int v); void CreateClassConst(FILE *file, char *name, Sym *sym, int v); void PrintComment(FILE *file, char *comment, int offset); void CreateMethods(FILE *file, EntityStructList *ops, char *subpackage, int v); void CreateObjects(FILE *file, Sym *s, int v); /* parameter, type, and ident modification stuff */ char *paramFix(FILE *file, char *type, char *param); char *atomType(char *type); char *atomReturn(char *type); int isAtom(char *type); bool IsJavaKeyword(char* s); char* SuffixJavaKeyword(char* ident); /* comment locator */ extern "C" nodep FindUserAttr(nodep attrs, char* name); extern "C" TypeStruct ResolveToBaseIdentType( TypeStruct t, /* Any type struct */ nodep errlocnode, /* Node for error message location */ bool doerror); /* True if error message should be issued */ /* docgen classes, for punting on nested fields */ TransInterface* ti; //RSLToDict* rtdd; /* new field generation stuff */ int CreateFields(FILE *file, nodep t, int level); int CreateTupleFields(FILE *file, nodep t, int level); int CreateField(FILE* file, char* name, nodep type); const char* PrintType(FILE* file, nodep type); void PrintName(FILE* file, char* name, nodep type); const char* MakeStructTypeName(nodep type); const char* MakeJavaType(nodep type, bool collectable); int CreateOpField(FILE *file, nodep t, int level); int main(int argc, char** argv) { /* alloc the doc gen classes */ ti = new TransInterface(argc, argv); // rtdd = new RSLToDict(ti); /* name of the package to be made */ char *package; /* good old verbosity flag */ int v = 0; /* check for usage requirements */ if(argc < 3) { /* print out usage and an example before exiting */ printf("usage: fs source package-name [-v]\n"); printf(" -v: prints out EVERYTHING\n"); printf(" eg.: fs /rsl/* SampleRSL\n"); exit(-1); } /* check for the verbosity flag */ if(strcmp(argv[(argc - 1)], "-v") == 0) { /* set the flag and reduce argc for later */ v = 1; argc--; } /* get the package and folder names */ package = (char *)malloc(sizeof(argv[(argc-1)])); strcpy(package, argv[(argc-1)]); package = SuffixJavaKeyword(package); char *folder = strcat(argv[(argc-1)], "/"); /* make the package folder */ CreatePackage(package); /* translate and process the files if possible */ CheckTranslate(argc - 1, argv, folder, package, v); /* exit successfully */ return(0); } /* CreatePackage(char *package) - make the folder... user should know it nukes any folder with that name... only way it works correctly Params: package - the name of the folder to make */ void CreatePackage(char *package) { /* print out the headers */ printf("***** Begin Package Folder Creation *****\n\n"); printf("Package Folder Creation... "); /* try to make it, don't want to unlink in case they have good stuff there */ if(mkdir(package, 0777) < 0) { /* print failure and exit */ printf("fail\n"); perror(package); exit(-1); } /* otherwise print success */ printf("ok\n"); } /* CheckTranslate(int count, char **files, char *folder, char *package, int v) - check the translation... really couldn't figure out how to fix it here to check if there were minor errors in the RSL that are going to segfault this translation... return type 0 never used by Translate, user beware Params: count - the number of files to process files - the list of files to process folder - the folder to write to package - thee user defined package name v - the verbosity flag */ void CheckTranslate(int count, char **files, char *folder, char *package, int v) { /* print out the header stuff */ printf("***** Begin Source Translation *****\n\n"); printf(" Source Translation... "); /* make the new TransInterface and see how it translates */ TransInterface* ti = new TransInterface(count, files); if (ti->Translate(count, files) == 1) { /* if it seems ok, that is the 0 return value should indicate minor errors but is never used so this bombs with null entry values */ ti->GetBrowserSymtab()->symt->ParentTab = Level0Symtab; printf("ok\n"); CreateFolders(ti, folder, package, v); } /* on error, tell the user so */ else { /* print fail and error message, then exit */ printf("fail\n"); printf("Codegen cannot continue, please correct errors.\n"); exit(-1); } } /* CreateFolders (ti, folder, package, v) - creates the folders for every package and their classes Params: ti - the transinterface with all symbols folder - the name of the overall output folder package - the top level package name v - the verbosity flag */ void CreateFolders(TransInterface *ti, char *folder, char *package, int v) { /* copy of the folder name for manipualtion in file creation */ char *temp = (char *)malloc(sizeof(folder)); strcpy(temp, folder); /* extract all of the symbols from the transinterface */ BrowserSymLists *bl = ti->GetAllSyms(); /* extract the modules, objects, and operations from the symbol list */ EntityStructList *mods = bl->mods; EntityStructList *objs = bl->objs; EntityStructList *ops = bl->ops; /* used for enumeration of the above lists later */ EntityStruct *e; EntityStruct *e2; /* the SymTab entry for a symbol containing parent module information */ Sym *sym; /* list of imports for a module */ nodep im; /* flag and word for loop checking when enumerating obj list, sometimes transinterface returns cyclic list */ int flag = 0; char *word = (char *)malloc(sizeof("KennySharma")); /* generate the Makefile before the files */ CreateMakefile(temp, mods); /* begin enumeration of modules -> folder and class creation for each */ printf("\n***** Begin Folder and Class Creation *****\n\n"); while(e = mods->Enum()) { /* copy the folder name again for manipulation */ strcpy(temp, folder); /* try to generate a folder for the new package */ printf("Begin %s\n ", e->GetName()); if(v) printf("Creating Folder... "); char* subpackage = SuffixJavaKeyword(newstr(e->GetName())); if(mkdir(strcat(temp, subpackage), 0777) < 0) perror("fail"); /* on success start creating a generic class for the module to house operations */ else { /* print out module class creation header */ if(v) printf("ok\n Creating Classes\n"); if(v) printf(" %s Begin\n", subpackage); /* get a fresh copy of the folder name again */ strcpy(temp, folder); /* get the symbol for the module and its imports */ sym = e->GetSym(); im = sym->Info.Module.Symtab->Imports; /* create the generic top level class, if it won't be created by virtue of an extant object of the same name as the module */ if (LookupStringIn(e->GetName(), sym->Info.Module.Symtab) == NULL) { CreateFile(temp, package, subpackage, subpackage, sym, objs, ops, im, v); } if(v) printf(" %s Complete\n\n", e->GetName()); /* set the cycle checking flag and word to default for each module pass */ flag = 0; strcpy(word, "KennySharma"); /* enumerate the objects and check for cycles */ while((e2 = objs->Enum()) && (strcmp(word, e2->GetName()) != 0)) { /* on first pass, copy the object name to check for cycles later */ if(flag == 0) { /* copy the name and offset the flag */ word = (char *)realloc(word, strlen(e2->GetName())); strcpy(word, (e2->GetName())); flag = 1; } /* get the symbol for the new class to create */ sym = e2->GetSym(); /* make sure the class belongs in the module and is not atomic */ if((strcmp(e->GetName(), sym->Info.Obj.parent->Symbol) == 0) && (sym->Type->header.name != Yident)) { /* print header, get a fresh folder copy, and create the class */ if(v) printf(" %s Begin\n", e2->GetName()); strcpy(temp, folder); CreateFile(temp, package, subpackage, e2->GetName(), sym, objs, ops, im, v); if(v) printf(" %s Complete\n\n", e2->GetName()); } } } /* print end of module creation */ if(v) printf("End %s\n\n", e->GetName()); } /* print the end of all creations */ printf("\n***** End Folder and Class Creation *****\n"); } /* CreateMakefile(char *folder, EntityStructList *packages) - generates a Makefile using the rest of the Makefile functions Params: folder - the destination path packages - an enumerable list of module names to be processed */ void CreateMakefile(char *folder, EntityStructList *packages) { /* the future Makefile to write to */ FILE *makefile; /* tack on the Makefile part to the given path */ strcat(folder, "Makefile"); /* print out the top level header */ printf("\n***** Begin Makefile Creation *****\n\n"); printf("%s\n", folder); /* try to make the file */ printf(" File Creation..."); if((makefile = fopen(folder, "w")) == NULL) printf("fail\n"); else { /* print the header creation header and generate the header */ printf("ok\n Header Creation..."); PrintHeader(makefile, folder); /* begin the major Makefile component creation */ printf("ok\n File Creation..."); /* write the file and package dependencies */ PrintFiles(makefile, folder, packages); PrintPackages(makefile, folder, packages); /* print the commands to make, execute, and print the information */ PrintRun(makefile); PrintExecute(makefile, folder); PrintPrint(makefile); /* close the Makefile and signal success */ fclose(makefile); printf("ok\n"); } } /* PrintHeader(FILE *file, char *folder) - print the basic header for the Makefile Params: file - the file pointer to write to folder - the path of the Makefile */ void PrintHeader(FILE *file, char *folder) { /* print the actual header portion */ fprintf(file, "#\n# Makefile for %.*s",(strlen(folder) - 9), folder); fprintf(file, " Java implementation.\n#\n\n"); /* print the definitions of the 206 library path */ fprintf(file, "# CSC 206 java lib directory\n"); fprintf(file, "LIB206 = /home/gfisher/classes/206/lib/JVM\n\n"); /* print the directory for the executable files */ fprintf(file, "# Directory for generated executable files\n"); fprintf(file, "# EXECUTABLES = ../../executables/JVM\n\n"); } /* PrintFiles(FILE *file, char *folder, EntityStructList *packages) - print the list of files needed in the Makefile Params: file - the file pointer to write to folder - the path of the Makefile packages - the enumerable list of packages to process */ void PrintFiles(FILE *file, char *folder, EntityStructList *packages) { /* element of the list when enumerating the list */ EntityStruct *e; /* print out the initial header and name */ fprintf(file, "# Files to compile\nFILES = "); /* enumerate the list and write to the file */ while(e = packages->Enum()) fprintf(file, "\\\n %.*s/%s/*.java ", (strlen(folder) - 9), folder, e->GetName()); } /* PrintPackages(FILE *file, char *folder, EntityStructList *packages) - print the list of packages needed in the Makefile Params: file - the file pointer to write to folder - the path of the Makefile packages - the enumerable list of packages to process */ void PrintPackages(FILE *file, char *folder, EntityStructList *packages) { /* element of the list when enumerating the list */ EntityStruct *e; /* print out the initial header and name */ fprintf(file, "\n\n# List of packages, for printing purposes.\n"); fprintf(file, "PACKAGES = "); /* enumerate the list and write to the file */ while(e = packages->Enum()) fprintf(file, "\\\n %.*s.%s ", (strlen(folder) - 9), folder, e->GetName()); } /* PrintRun(FILE *file) - print the statements to run the compiler and move Params: file - the file pointer to write to */ void PrintRun(FILE *file) { /* print the header */ fprintf(file, "\n\n# Run the Java compiler on all of the files.\n"); /* print the commands to compile all using the files and packages */ fprintf(file, "all: $(FILES)\n# echo $(FILES)\n javac -g $(FILES) \\\n"); fprintf(file, " -classpath $(LIBRARIES) \\\n"); fprintf(file, " -d $(EXECUTABLES) \\\n"); fprintf(file, "# cd $(EXECUTABLES);\n\n"); } /* PrintExecute(FILE *file, char *folder) - print the statements to execute Params: file - the file pointer to write to */ void PrintExecute(FILE *file, char *folder) { /* print out all statements, usually all commented out */ fprintf(file, "# The preceding rule results in an executable program "); fprintf(file, "that is invoked as\n# follows from the implementation"); fprintf(file, "/executables/JVM directory:\n#\n"); fprintf(file, "# java %.*s.Main\n#\n", (strlen(folder) - 9), folder); fprintf(file, "# The makefile in that dir has more information about "); fprintf(file, "altenate forms of java\n# program invocation.\n\n\n"); } /* PrintPrint(FILE *file) - print the commands to print the source files Params: file - the file pointer to write to */ void PrintPrint(FILE *file) { /* print the two statements */ fprintf(file, "# Print source files in small-font, line-numbered, "); fprintf(file, "two-column format.\n"); fprintf(file, "print: \n csh -q -c \".make-print $(PACKAGES)\"\n"); } /* CreateFile(char *folder, char* package, char *subpackage, char *name, Sym *sym, EntityStructList * objs, EntityStructList *ops, nodep im, int v) - major translation method that creates all necessary java file data Params: folder - the destination folder package - the name of the package subpackage - the name of the current subpackage being constructed name - the name of the current module being worked on sym - the Symtab of the current file being worked on objs - all of the objects in the project ops - all of the operations in the projects im - the nodep reference to the imports v - the verbosity flag */ void CreateFile(char *folder, char* package, char *subpackage, char *name, Sym *sym, EntityStructList * objs, EntityStructList *ops, nodep im, int v) { /* the file to write to */ FILE *file; /* standard symbol value */ Sym *s; /* used when iterating lists */ EntityStruct *e; /* count to know if a file is hot just a shell */ int objects = 0; /* do some path name manipulation to get it into shape */ char *path = strcat(folder, subpackage); path = strcat(path, "/"); path = strcat(path, SuffixJavaKeyword(name)); path = strcat(path, ".java"); /* print header and try to make the file */ if(v) printf(" File Creation..."); if (fopen(path, "r")) { fprintf(stderr, "File %s already exists.", path); } if((file = fopen(path, "w")) == NULL) { // if(v) fprintf(stderr, "fail\n"); perror("fopen"); } else { /* make the header stuff */ CreateHeader(file, package, subpackage, im, sym, v); /* make the class and constructor stuff */ CreateClassConst(file, name, sym, v); /* for a extant or generic file with the package name */ if(strcmp(subpackage, name) == 0) { /* dump the methods into it */ CreateMethods(file, ops, subpackage, v); /* * 14jan07 gfisher: The following chunk of commented-out code seems to * be bogus, since it starts another objs->Enum(). I'm not sure what * Kenny had in mind here. * * // start making objects if(v) printf(" Object Creation\n"); while(e = objs->Enum()) { // get the symbol and include it if it belongs s = e->GetSym(); if(strcmp(sym->Symbol, s->Info.Obj.parent->Symbol) == 0) { // create the object and increment the count CreateObjects(file, s, v); objects++; } } */ } /* for all other files */ /* * Goes with the commented-out block just above. * else { */ /* print the same header and enumerate the component list */ if(v) printf(" Object Creation\n"); objects = CreateFields(file, sym->Type, 0); /* } */ /* finish up the file */ fprintf(file, "\n}\n"); fclose(file); /* make sure the file is not empty, if so it is not the generic package file, and it's not opaque (which we keep, for now) */ if((objects == 0) && (strcmp(name, subpackage) != 0) && !isOpaqueType(sym->Type)) { /* print out some display stuff */ if(v) printf(" None\n"); if(v) printf(" Deleting Empty File..."); /* destroy the empty file */ if (unlink(path) != 0) { if(v) printf("fail\n"); } else if(v) printf("ok\n"); } } } /* CreateHeader(FILE *file, char *package, char *subpackage, nodep im, Sym *sym, int v) - create package and import declearations, and javadoc header Params: file - the file to write to package - the top level package name subpackage - the currently processed subpackage im - the traverasable imports nodep sym - the top level symbol being processed v - the verbosity flag */ void CreateHeader(FILE *file, char *package, char *subpackage, nodep im, Sym *sym, int v) { /* the attribute structure for comments */ nodep attr; /* the comment string */ char *comment; /* print the package declaration */ if(v) printf("ok\n Header Creation..."); fprintf(file, "package %s.%s;\n\n", package, SuffixJavaKeyword(subpackage)); /* print the imports */ CreateImports(file, package, im, v); /* check if there is a comment */ if (attr = FindUserAttr(sym->Info.Obj.attrs, "description")) { /* extract the comment, print some header and then the comment */ comment = attr->components.atom.val.string; fprintf(file, "\n/**\n *\n * "); PrintComment(file, comment, HEADERINDENT); } /* otherwise print a generic header */ else fprintf(file, "\n/**\n *\n * Generic JavaDoc for %s \n", sym->Symbol); /* print a message if it's an opaque type, which we'll keep around for now */ if (isOpaqueType(sym->Type)) { fprintf(file, "\n * Derived from an SpecL opaque type.\n"); } /* print the author and the version fields */ fprintf(file, " * \n * @author \n * @version \n *\n **/\n\n"); } /** Do what Kenny did plus import java.util.Collection unconditionally. CreateImports(FILE *file, char *package, nodep imports, int v) - print imports Params: file - the file to write to package - the top level package name imports - the traverasable imports nodep v - the verbosity flag */ void CreateImports(FILE *file, char *package, nodep imports, int v) { nodep n; /* print the header */ if(v) printf("ok\n Imports Creation..."); /* iterate through the imports list */ for (imports; imports; imports = imports->components.decl.next) { /* iterate through each item in a list */ for (n=imports->components.decl.kind.import.items; n; n=n->components.atom.next2) { /* print the imports out */ fprintf(file, "import %s.%s.*;\n", package, /* TODO: The following only works for a two-part import item qualident, not for three or more parts. */ SuffixJavaKeyword(n->components.atom.val.text)); /* Old code: SuffixJavaKeyword( imports->components.decl.kind.import.from-> components.atom.val.text)); */ } } /* import java.util uncondtionally */ fprintf(file, "\nimport java.util.Collection;\n"); } /* CreateClassConst(FILE *file, char *name, Sym *sym, int v) - creates the class and default constructor definitions Params: file - the file to write to name - the name of the class being processed sym - the top level symbol being processed v - the verbosity flag */ void CreateClassConst(FILE *file, char *name, Sym *sym, int v) { /* flag if this class extends */ int extends = 0; /* print the generic public class and name */ fprintf(file, "public class %s", SuffixJavaKeyword(name)); /* check to see if it extends something */ if (sym->Info.Obj.inheritsfrom) if (sym->Info.Obj.inheritsfrom->components.atom.val.text) { /* if it does print the extension data and set the flag */ fprintf(file, " extends %s", sym->Info.Obj.inheritsfrom ->components.atom.val.text); extends = 1; } /* print some clean headers */ if(v) printf("ok\n Method Creation\n"); if(v) printf(" Default Constructor..."); /* print the open brace, comment, and default constructor */ fprintf(file, " {\n\n /** Default Constructor **/"); fprintf(file, "\n public %s() {\n", SuffixJavaKeyword(name)); /* if it extends print the super part, and then close it */ if(extends) fprintf(file, " super();"); fprintf(file, "\n }\n"); /* print the success */ if(v) printf("ok\n"); } /* PrintComment(FILE *file, char *comment, int offset) - print out the comment in pseudo pretty print format Params: file - the file to write to comment - the comment to write offset - the indentation offset */ void PrintComment(FILE *file, char *comment, int offset) { /* standard i variable */ int i; /* get the initial comment length */ int len = strlen(comment); /* keep processing until the comment is consumed */ while(len > 0) { /* get the maximum chunk size of the comment */ if(len > (LINESIZE - offset)) i = LINESIZE - offset; else i = len; /* eat extra end spaces if any */ if(isspace(comment[i])) while(((isspace(comment[i])) || (comment[i] == '\n')) && (i > 0)) i--; /* or eat to the previous word to avoid mid word truncation */ else while(((!isspace(comment[i])) || (comment[i] == '\n')) && (i > 0)) i--; /* if its just one big word */ if(i == 0) { /* print the whole part and move along in the comment */ fprintf(file, "%.*s", LINESIZE - offset, comment); /* move along in the comment and decrement remaining length */ comment += (LINESIZE - offset); len -= (LINESIZE - offset); } /* otherwise assume its correct */ else { /* print out the rest of the comment */ fprintf(file, "%.*s", i, comment); /* increment the values and move on */ comment += i; len -= i; } /* indent correctly at end of lines */ if(len > (LINESIZE - offset)) { /* this acts weird not sure why */ fprintf(file, "\n"); /* move on and print the comment asterisk */ for(i = 0; i < (offset - 1); i++) fprintf(file, " "); fprintf(file, "*"); } } } /* CreateMethods(FILE *file, EntityStructList *ops, char *subpackage, int v) - do all the work necessary to generate method signatures and returns ... sorry it's a beast of a method but works like that because of the special line length considerations here but nowhere else Params: file - the file to write to ops - the list of all operations subpackage - the current sub package being worked on v - the verbosity flag */ void CreateMethods(FILE *file, EntityStructList *ops, char *subpackage, int v) { /* Symtab refs for iteration */ Sym *s, *s2; /* used for enumeration and book keeping */ EntityStruct *e, *e2, *e3; /* attributes of a comment */ nodep attr; /* the comment text, types, and return type */ char *comment, *type, *rettype; /* flag for printing ,'s between list of args */ int firstArg = 0; /* length of line being printed */ int length; /* iterator for spaces and stuff */ int i; /* enumerate the list of operations */ while(e = ops->Enum()) { /* extract the symbol */ s = e->GetSym(); /* reset line length */ length = LINESIZE - METHODINDENT; /* make sure the operation belongs in this file */ if(strcmp(subpackage, s->Info.Op.parent->Symbol) == 0) { /* print the name for verbosity */ if(v) printf(" %s...", s->Symbol); /* print the method comment if any */ if (attr = FindUserAttr(s->Info.Op.attrs, "description")) { /* same stuff to print comment correctly */ comment = attr->components.atom.val.string; fprintf(file, "\n /** "); PrintComment(file, comment, BODYINDENT); fprintf(file, " **/"); } /* print the protected part */ fprintf(file, "\n protected "); length -= strlen("protected"); /* get the correct (I think) return type */ while(e2 = s->Info.Op.brOuts->Enum()) e3 = e2; /* figure out what the return type is, if its atomic basically */ /* If not there at all, presumably from a returnless function, make the return type void. */ if ((e3 == NULL) || (s->Info.Op.brOuts->Len() == 0)) { rettype = "none"; } else if((e3->GetSym()) && (e3->GetSym()->Type->header.name == Yident)) { /* extract the atomic value and print it */ rettype = e3->GetSym()->Type->components.type.kind.ident.type-> components.atom.val.text; fprintf(file, "%s ", atomType(rettype)); length -= strlen(atomType(rettype)); } /* otherwise if not atomic */ else { /* just print the return type */ rettype = "none"; fprintf(file, "%s ", atomType(e3->GetName())); length -= strlen(atomType(e3->GetName())); } /* print the prameter name and open brace */ paramFix(file, rettype, s->Symbol); fprintf(file, "("); /* compensate approx for name and the spaces and ( */ length -= (strlen(s->Symbol) + 3); /* set flag for printing ,'s */ firstArg = 1; /* start processing the inputs */ s2 = s->Info.Op.InParms; while(e2 = s->Info.Op.brIns->Enum()) { /* print a , between each input */ if(!firstArg) fprintf(file, ", "); else firstArg = 0; /* a lot of this seems redundant... and it is but its necessary */ if(e2->GetSym()) { /* if its atomic */ if(e2->GetSym()->Type->header.name == Yident) { /* get the type and print them */ type = e2->GetSym()->Type->components.type.kind.ident.type ->components.atom.val.text; /* check line length stuff */ if((strlen(atomType(type)) + strlen(s2->Symbol) + 2) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* subtract length and print them */ length -= ((strlen(atomType(type))) + strlen(s2->Symbol) + 2); fprintf(file, "%s ", atomType(type)); paramFix(file, type, s2->Symbol); } /* for enumerations */ else if(e2->GetSym()->Type->header.name == YOR) { /* print the object stuff */ fprintf(file, "Object "); /* check line length stuff */ if((strlen("Object") + strlen(s2->Symbol) + 2) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* subtract length and print it */ length -= (strlen("Object") + strlen(s2->Symbol) + 2); paramFix(file, "Object", s2->Symbol); } /* special cases of "record" aka object or array type */ else if((e2->GetSym()->Type->header.name == YRECORD) && ((e2->GetSym()->Type->components.type.kind.record.numfields) == 1)) { /* if its an atomic record type */ if(e2->GetSym()->Type->components.type.kind.record.fields ->components.decl.kind.field.type ->components.type.kind.ident.type ->components.atom.val.text) { /* get it... should make a macro? */ type = e2->GetSym()->Type->components.type.kind.record.fields ->components.decl.kind.field.type ->components.type.kind.ident.type ->components.atom.val.text; /* not sure why... sorry Dr. Fisher this is a bad comment */ if(strcmp(atomType(type), type) != 0) { /* check line length stuff */ if((strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* print the atomic type */ length -= (strlen(atomType(type)) + strlen(e2->GetName()) + 2); fprintf(file, "%s ", atomType(type)); paramFix(file, type, e2->GetName()); } /* otherwise just use the high level symbol ? */ else { /* check line length stuff */ if((strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* print the information */ length -= (strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2); fprintf(file, "%s ", atomType(e2->GetName())); paramFix(file, e2->GetName(), s2->Symbol); } } /* otherwise just print the highest level */ else { /* check line length stuff */ if((strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* print the information */ length -= (strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2); fprintf(file, "%s ", atomType(e2->GetName())); paramFix(file, e2->GetName(), s2->Symbol); } } /* otherwise for an array */ else if(e2->GetSym()->Type->header.name == YARRAY) { /* check line length stuff */ if((strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 4) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* print the information with [] */ length -= (strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 4); fprintf(file, "%s[] ", atomType(e2->GetName())); paramFix(file, e2->GetName(), s2->Symbol); } else { /* check line length stuff */ if((strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* print the same info */ length -= (strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2); fprintf(file, "%s ", atomType(e2->GetName())); paramFix(file, e2->GetName(), s2->Symbol); } } /* final case to prevent crashes */ else { /* check line length stuff */ if((strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2) > length) { fprintf(file, "\n "); length = LINESIZE - 3 * METHODINDENT; } /* print the standard information */ length -= (strlen(atomType(e2->GetName())) + strlen(s2->Symbol) + 2); fprintf(file, "%s ", atomType(e2->GetName())); paramFix(file, e2->GetName(), s2->Symbol); } /* move to the next parameter */ s2 = s2->Info.Parm.Link; } /* make sure the correct type is returned */ if(e2 && strcmp(rettype, "none") == 0) fprintf(file, ") {\n %s;\n }\n", atomReturn(e3->GetName())); else fprintf(file, ") {\n %s;\n }\n", atomReturn(rettype)); /* print success and wrap it up */ if(v) printf("ok\n"); } } } /** * Replacement for CreateObjects. Instead of being called from a brComponents * enumeration loop, this function traverses the object type tree, a la * RSLToDict::PrettyPrintType. To integrate smoothly as the replacement for * CreateObjects, CreateFields returns the number of fields it creates, which * value was previously maintained as in the loop that called CreateObjects. */ int numFields; int CreateFields(FILE *file, nodep t, int level) { if (level == 0) { numFields = 0; } switch(t->header.name) { /* These are degenerate cases at the top level. The deal with an ident * type is that no class gets generated and subsequently refs to the * type are collapsed down to base types in generated data fields. */ case ';': case Yident: return 0; /* This is the "normal" case, here. */ case YRECORD: return CreateTupleFields(file, t, level+1); /* This should be happening inside an enum. */ case YOR: return CreateTupleFields(file, t, level+1); /* This generates single Collection data field. */ case YARRAY: return CreateField(file, "data", t); /* This generates nothing in Java, for now. */ case YOP: return CreateOpField(file, t, level+1); } } /** * This is the main deal for field creation. It traverses the record field * list, generating a new class data field for each object field. */ int CreateTupleFields(FILE *file, nodep t, int level) { nodep field, type; bool nested = false; int i; nodep vars; for (field = t->components.type.kind.record.fields, i=0; field and (i < t->components.type.kind.record.numfields); i++) { if ((type = field->components.decl.kind.field.type) and (not field->components.decl.kind.field.isinherited)) { if (nested = (type->header.name == YOR)) { fprintf(file, "\n // BEGIN NESTED COMPONENTS\n"); } CreateField(file, (vars = field->components.decl.kind.field.vars) ? vars->components.atom.val.text : null, type); if (nested) { fprintf(file, "\n // END NESTED COMPONENTS\n"); } } field = field->components.decl.next; } return numFields; } /** * Create one data field of the given name and type. If the name is empty, * make the name the same as the type, with the first char down-cased, and a * disambiguating '_' suffix if the downcased name is the same as the type * name. */ int CreateField(FILE* file, char* name, nodep type) { fprintf(file, "\n protected "); const char* tname = PrintType(file, type); PrintName(file, name, type); fprintf(file, ";\n"); return ++numFields; } const char* PrintType(FILE* file, nodep type) { const char* tname; if (isIdentType(type)) { fprintf(file, "%s ", tname = MakeJavaType(type, false)); return tname; } else if (isListType(type)) { nodep base = type->components.type.kind.arraytype.basetype; if (not isIdentType(base)) { fprintf(file, "Collection "); } else { fprintf(file, "Collection<%s> ", MakeJavaType(base, true)); } } else { fprintf(file, "Collection "); } return "Collection"; } void PrintName(FILE* file, char* name, nodep type) { char* tname, *newname; if (name == NULL) { /* We have a structured type in a name/type pair. Construct a name for it based on its top-level structure. */ if (! isIdentType(type)) { fprintf(file, "%s", MakeStructTypeName(type)); return; } /* Make the name out of the type by downcasing first letter and Java disambiguating as necessary. */ newname = new char[strlen(tname = type->components.type.kind.ident.type-> components.atom.val.text) + 2]; strcpy(tname, tname); sprintf(newname, "%c%s", tolower(tname[0]), tname + 1); if (IsJavaKeyword(newname)) { strcat(newname, "_"); } if (strcmp(newname, tname) == 0) { strcat(newname, "_"); } fprintf(file, "%s", newname); } else { fprintf(file, "%s", name); } } /** * Make a name for an SpedL strctured type simply by returning "Object". In * future, this might do something fancier, like stringifying its TYPE_NODE * .name. This would require the def of Java class place-holders for such * names, e.g., "class TupleType". */ const char* MakeStructTypeName(nodep type) { return "object"; } /** * Make a Java type out of an SpecL type. */ const char* MakeJavaType(nodep type, bool collectable) { if (! isIdentType(type)) { return MakeStructTypeName(type); } char* typestr; TypeStruct rtype = ResolveToBaseIdentType(type, null, false); /* Seen 18jan07 commentary in typechk.c:NewDerivedIdentType for what's * going on here. */ if ((rtype != null) && rtype->components.type.resolvedname) { typestr = rtype->components.type.resolvedname; } else if (isIdentType(rtype) && (type != rtype) && (rtype != null)) { typestr = rtype->components.type.kind.ident.type-> components.atom.val.text; } else { typestr = type->components.type.kind.ident.type-> components.atom.val.text; } if (strcmp(typestr, "string") == 0) { return "String"; } if (strcmp(typestr, "integer") == 0) { return collectable ? "Integer" : "int"; } if (strcmp(typestr, "real") == 0) { return collectable ? "Double" : "double"; } if (strcmp(typestr, "boolean") == 0) { return collectable ? "Boolean" : "boolean";; } return SuffixJavaKeyword(typestr); } int CreateOpField(FILE *file, nodep t, int level) { fprintf(file, "\n protected void underived_op_type"); return ++numFields; } /* CreateObjects(FILE *file, Sym *s, int v) - create objects based on their types Params: file - the file to write to s - the symbol being processed v - the verbosity flag */ void CreateObjects(FILE *file, Sym *s, int v) { /* comment attributes */ nodep attr; /* the actual text of the comment */ char *comment; /* the type being considered for an object */ char * type; /* s little thing to help try and reduce yidents more */ Sym *s2; /* print the current symbol being processed */ if(v) printf(" %s...", s->Symbol); /* try to find and print a comment */ if (s and (attr = FindUserAttr(s->Info.Obj.attrs, "description"))) { /* look it up and print it */ comment = attr->components.atom.val.string; fprintf(file, "\n /** "); PrintComment(file, comment, BODYINDENT); fprintf(file, " **/"); } /* for Yidents aka type name declarations */ if(s and (s->Type->header.name == Yident)) { /* look at the first type defined */ type = s->Type->components.type.kind.ident.type ->components.atom.val.text; /* if its not atomic, look its atomic value up */ if((!isAtom(type)) && (s2 = LookupString(type))) if(s2->Type->header.name == Yident) type = s2->Type->components.type.kind.ident.type ->components.atom.val.text; /* print out the type and name */ fprintf(file, "\n protected %s ", atomType(type)); paramFix(file, type, s->Symbol); } /* for YOR aka enumerations */ else if(s->Type->header.name == YOR) { /* use the generic Object to take care of it */ fprintf(file, "\n protected Object "); paramFix(file, "Object", s->Symbol); } /* for YRECRORDS aka normal objects or arrays */ else if(s->Type->header.name == YRECORD) { /* if its an array-ish field */ if((s->Type->components.type.kind.record.numfields) > 1) { /* if the type of array is defined somewhere */ if(s->Type->components.type.kind.record.fields-> components.decl.kind.field.type-> components.type.kind.ident.type-> components.atom.val.text) { /* store the type */ type = s->Type->components.type.kind.record.fields-> components.decl.kind.field.type-> components.type.kind.ident.type-> components.atom.val.text; /* if its not atomic, print its atomic */ if(strcmp(atomType(type), type) != 0) { /* use the atomic type */ fprintf(file, "\n protected %s ", atomType(type)); paramFix(file, atomType(type), s->Symbol); } /* failsafe... trust me this doesn't make much sense but it works and is correct (doesn't crash and looks good) */ else { fprintf(file, "\n protected %s ", atomType(s->Symbol)); paramFix(file, atomType(s->Symbol), s->Symbol); } } /* if theres no type there, the name is the type for some reason */ else { /* print them out as such */ fprintf(file, "\n protected %s[] ", atomType(s->Symbol)); paramFix(file, atomType(s->Symbol), s->Symbol); } } /* for single field records aka normal objects */ else { /* try to lookup the type here also */ if(s->Type->components.type.kind.record.fields-> components.decl.kind.field.type-> components.type.kind.ident.type-> components.atom.val.text) { /* same deal here */ type = s->Type->components.type.kind.record.fields ->components.decl.kind.field.type ->components.type.kind.ident.type ->components.atom.val.text; /* and here */ if(isAtom(type)) { fprintf(file, "\n protected %s ", atomType(type)); paramFix(file, atomType(type), s->Symbol); } /* same failsafeish */ else { fprintf(file, "\n protected %s[] ", atomType(s->Symbol)); paramFix(file, atomType(s->Symbol), s->Symbol); } } /* same default */ else { fprintf(file, "\n protected %s[] ", atomType(s->Symbol)); paramFix(file, atomType(s->Symbol), s->Symbol); } } } /* in case of an array */ else if(s->Type->header.name == YARRAY) { /* print out the array form */ fprintf(file, "\n protected %s[] ", atomType(s->Symbol)); paramFix(file, atomType(s->Symbol), s->Symbol); } /* just ignore other types... there weren't any encountered during testing and the rest don't seem to translate without much more effort if they could be used */ /* print the semicolon, newline, and tell the user it worked out */ fprintf(file, ";\n"); if(v) printf("ok\n"); } /* atomType(char *type) - return the Java equivalent atomic type Params: type - the SpecL type Return: the Java type if applicable, or the same as the input */ char *atomType(char *type) { /* for string -> String */ if(strcmp("string", type) == 0) return("String"); /* for integer or Integer -> int */ else if ((strcmp("integer", type) == 0) || (strcmp("Integer", type) == 0)) return("int"); /* bools and any other not base types are fine */ else return (type); } /* atomType(char *type) - return the correct type return Params: type - the SpecL type Return: the correct return statement */ char *atomReturn(char *type) { /* for booleans -> return false */ if(strcmp("boolean", type) == 0) return("return false"); /* for integer or Integer -> return 0 */ else if ((strcmp("integer", type) == 0) || (strcmp("Integer", type) == 0)) return("return 0"); /* otherwise for any objects return null */ else return ("return null"); } /* isAtom(char *type) - used for reducton to single atomic level Params: type - the SpecL type Return: 1 if atomic, 0 if not atomic */ int isAtom(char *type) { /* for boolean -> return 1 */ if(strcmp("boolean", type) == 0) return(1); /* for strings -> return 1 */ else if(strcmp("boolean", type) == 0) return(1); /* for integer or Integer -> return 1 */ else if ((strcmp("integer", type) == 0) || (strcmp("Integer", type) == 0)) return(1); /* for anything else return 0 */ else return (0); } /* paramFix(FILE *file, char *type, char *param) - fix and print the type and name Params: type - the SpecL type Return: 1 if atomic, 0 if not atomic */ char *paramFix(FILE *file, char *type, char *param) { /* * Unconditionally do the keyword treatment on the type name. */ type = SuffixJavaKeyword(type); /* * Alloc a string for the name ident. The two extra chars are in case we * end up with a double '_' suffix, which is the case when a type name * starts with a lower case char and is equal to a Java keyword. */ char* name = new char[strlen(param) + 2]; /* in case there is a ' in the name */ int i; /* if there is no name defined, just use the downcased type name as the name, suffixed with a '_' if identical with the type name after downcasing. */ if(strcmp(param, "??") == 0) { sprintf(name, "%c%s", tolower(type[0]), type + 1); if (IsJavaKeyword(name)) { strcat(name, "_"); } if (strcmp(type, name) == 0) { strcat(name, "_"); } fprintf(file, "%s", name); } /* handle java keywords cleanly, altering params causes a dump so both init must be caps and not */ else if((strcmp(param, "abstract") == 0) || (strcmp(param, "assert") == 0) || (strcmp(param, "boolean") == 0) || (strcmp(param, "break") == 0) || (strcmp(param, "byte") == 0) || (strcmp(param, "case") == 0) || (strcmp(param, "catch") == 0) || (strcmp(param, "char") == 0) || (strcmp(param, "class") == 0) || (strcmp(param, "const") == 0) || (strcmp(param, "continue") == 0) ||(strcmp(param, "default") == 0) || (strcmp(param, "do") == 0) ||(strcmp(param, "double") == 0) || (strcmp(param, "else") == 0) || (strcmp(param, "enum") == 0) || (strcmp(param, "extended") == 0) || (strcmp(param, "final") == 0) || (strcmp(param, "finally") == 0) || (strcmp(param, "float") == 0) || (strcmp(param, "for") == 0) || (strcmp(param, "goto") == 0) || (strcmp(param, "if") == 0) || (strcmp(param, "implements") == 0) || (strcmp(param, "import") == 0) || (strcmp(param, "instanceof") == 0) || (strcmp(param, "int") == 0) || (strcmp(param, "interface") == 0) || (strcmp(param, "long") == 0) || (strcmp(param, "native") == 0) || (strcmp(param, "new") == 0) || (strcmp(param, "package") == 0) || (strcmp(param, "private") == 0) || (strcmp(param, "protected") == 0) || (strcmp(param, "public") == 0) || (strcmp(param, "return") == 0) || (strcmp(param, "short") == 0) || (strcmp(param, "static") == 0) || (strcmp(param, "strictfp") == 0) || (strcmp(param, "super") == 0) || (strcmp(param, "switch") == 0) || (strcmp(param, "synchronized") == 0) || (strcmp(param, "this") == 0) || (strcmp(param, "throw") == 0) || (strcmp(param, "throws") == 0) || (strcmp(param, "transient") == 0) || (strcmp(param, "try") == 0) || (strcmp(param, "void") == 0) || (strcmp(param, "violate") == 0) || (strcmp(param, "while") == 0) || (strcmp(param, "Abstract") == 0) || (strcmp(param, "Assert") == 0) || (strcmp(param, "Boolean") == 0) || (strcmp(param, "Break") == 0) || (strcmp(param, "Byte") == 0) || (strcmp(param, "Case") == 0) || (strcmp(param, "Catch") == 0) || (strcmp(param, "Char") == 0) || (strcmp(param, "Class") == 0) || (strcmp(param, "Const") == 0) || (strcmp(param, "Continue") == 0) ||(strcmp(param, "Default") == 0) || (strcmp(param, "Do") == 0) ||(strcmp(param, "Double") == 0) || (strcmp(param, "Else") == 0) || (strcmp(param, "Enum") == 0) || (strcmp(param, "Extended") == 0) || (strcmp(param, "Final") == 0) || (strcmp(param, "Finally") == 0) || (strcmp(param, "Float") == 0) || (strcmp(param, "For") == 0) || (strcmp(param, "Goto") == 0) || (strcmp(param, "If") == 0) || (strcmp(param, "Implements") == 0) || (strcmp(param, "Import") == 0) || (strcmp(param, "Instanceof") == 0) || (strcmp(param, "Int") == 0) || (strcmp(param, "Interface") == 0) || (strcmp(param, "Long") == 0) || (strcmp(param, "Native") == 0) || (strcmp(param, "New") == 0) || (strcmp(param, "Package") == 0) || (strcmp(param, "Public") == 0) || (strcmp(param, "Return") == 0) || (strcmp(param, "Short") == 0) || (strcmp(param, "Static") == 0) || (strcmp(param, "Strictfp") == 0) || (strcmp(param, "Super") == 0) || (strcmp(param, "Switch") == 0) || (strcmp(param, "Synchronized") == 0) || (strcmp(param, "This") == 0) || (strcmp(param, "Throw") == 0) || (strcmp(param, "Throws") == 0) || (strcmp(param, "Transient") == 0) || (strcmp(param, "Try") == 0) || (strcmp(param, "Void") == 0) || (strcmp(param, "Violate") == 0) || (strcmp(param, "While") == 0) ) fprintf(file, "new%c%s", tolower(param[0]), param + 1); /* replace any illegal ' identifiers */ else if(strstr(param, "'")) { /* iterate until the ' is found */ for(i = 0; param[i] != '\''; i++) fprintf(file, "%c", param[i]); /* print prime instead of the ' */ fprintf(file, "prime"); } /* otherwise the name is valid in itself */ else { sprintf(name, "%c%s", tolower(param[0]), param + 1); if (IsJavaKeyword(name)) { strcat(name, "_"); } if (strcmp(type, name) == 0) { strcat(name, "_"); } fprintf(file, "%s", name); } } /** * Return true if the given string s is a Java keyword. */ bool IsJavaKeyword(char* s) { return (strcmp(s, "abstract") == 0) || (strcmp(s, "assert") == 0) || (strcmp(s, "boolean") == 0) || (strcmp(s, "break") == 0) || (strcmp(s, "byte") == 0) || (strcmp(s, "case") == 0) || (strcmp(s, "catch") == 0) || (strcmp(s, "char") == 0) || (strcmp(s, "class") == 0) || (strcmp(s, "const") == 0) || (strcmp(s, "continue") == 0) ||(strcmp(s, "default") == 0) || (strcmp(s, "do") == 0) ||(strcmp(s, "double") == 0) || (strcmp(s, "else") == 0) || (strcmp(s, "enum") == 0) || (strcmp(s, "extended") == 0) || (strcmp(s, "final") == 0) || (strcmp(s, "finally") == 0) || (strcmp(s, "float") == 0) || (strcmp(s, "for") == 0) || (strcmp(s, "goto") == 0) || (strcmp(s, "if") == 0) || (strcmp(s, "implements") == 0) || (strcmp(s, "import") == 0) || (strcmp(s, "instanceof") == 0) || (strcmp(s, "int") == 0) || (strcmp(s, "interface") == 0) || (strcmp(s, "long") == 0) || (strcmp(s, "native") == 0) || (strcmp(s, "new") == 0) || (strcmp(s, "package") == 0) || (strcmp(s, "private") == 0) || (strcmp(s, "protected") == 0) || (strcmp(s, "public") == 0) || (strcmp(s, "return") == 0) || (strcmp(s, "short") == 0) || (strcmp(s, "static") == 0) || (strcmp(s, "strictfp") == 0) || (strcmp(s, "super") == 0) || (strcmp(s, "switch") == 0) || (strcmp(s, "synchronized") == 0) || (strcmp(s, "this") == 0) || (strcmp(s, "throw") == 0) || (strcmp(s, "throws") == 0) || (strcmp(s, "transient") == 0) || (strcmp(s, "try") == 0) || (strcmp(s, "void") == 0) || (strcmp(s, "violate") == 0) || (strcmp(s, "while") == 0); } /** * Return the given ident with a "_" appended if the ident is a java keyword. * Otherwise just return the given ident as is. */ char* SuffixJavaKeyword(char* ident) { if (IsJavaKeyword(ident)) { char* rtn = new char[strlen(ident) +1 ]; strcpy(rtn, ident); strcat(rtn, "_"); return rtn; } return ident; }