home *** CD-ROM | disk | FTP | other *** search
- /*
-
-
- Copyright (C) 1990 Texas Instruments Incorporated.
-
- Permission is granted to any individual or institution to use, copy, modify,
- and distribute this software, provided that this complete copyright and
- permission notice is maintained, intact, in all copies and supporting
- documentation.
-
- Texas Instruments Incorporated provides this software "as is" without
- express or implied warranty.
-
-
- *
- * Edit history
- * Created: LGO 30-Mar-89 -- Initial design and implementation.
- * Updated: MJF 21-May-90 -- Added DECLARE_ONCE for CCC -X.
- * Updated: MJF 01-Mar-91 -- Added fix when expanding member functions
- * which return a template class
- * Updated: GPD 25-Jun-91 -- Fix #line nesting. When template expansions done.
- *
- * Template, DECLARE and IMPLEMENT defmacro
- *
- * TO DO:
- * Generate a warning message when a template is encountered for a class
- * that has already had a DECLARE or IMPLEMENT run on it.
- *
- * Scan for the "inline" keyword at the beginning of the "garbage pail"
- * template.
- *
- * template needs a more robust heuristic for finding the class name
- */
-
- /*
- // C++ paramertized type macro support
- // This follows the conventions outlined in the 1988 USENIX C++ conference
- // paper titled "Parameterized Types for C++" by Bjarne Stroustrup
- //
- // To use, insert the following
- // #pragma defmacro template <template>
- //
- // template provides a specialized way of defining a macro
- // which supports parameterized classes. A parameterized class
- // is a class in which one or more pieces can be declared at
- // compile time. A typical example is a container class where
- // the type of the contained object may change. For example, a
- // Vector class would be paramertized for the type of the vec-
- // tor elements.
-
- // Templates are expanded in two parts: the declarative part
- // needed by every program file which uses the parameterized
- // class, and the implementation part which needs to be com-
- // piled once for the class. The DECLARE macro expands a
- // template's declarative part, and the IMPLEMENT macro expands
- // a template's implementation part.
- //
- // There are 3 kinds of template:
- //
- // template<parms> class NAME { class_description };
- // Defines a template the declaration of class NAME.
- // PARAMETERS is:
- // parms ::= type name [, parms]
- // where type is ignored (its included for AT&T compata-
- // bility) and name is the name of the parameter which
- // will be substituted when the template is expanded (Its
- // like a macro argument in a #define).
- //
- // template<parms> NAME result_type NAME<parms>::function { ... };
- // Defines a method for the implementation of the NAME
- // class.
- //
- // template<class ARG> NAME inline rtype NAME<parms>::func { ... };
- // Defines an inline method for the declaration of the
- // NAME class.
- //
- // template<class ARG> NAME {anything};
- // This form is used to define anything else you want
- // associated with a template. Use this form for defining
- // typedef's or overloaded friend functions. When this is
- // found before the class template, the contents will be
- // expanded before the class declaration. When this is
- //
- // Example:
- // template<class T> Vector {
- // typedef Boolean (*Compare) (T&, T&);
- // };
- //
- // template<class T> class Vector {
- // T* v;
- // int sz;
- // public:
- // Vector<T>(int);
- // T& operator[](int);
- // T& elem(int i) { return v[i]; }
- // // ...
- // };
- //
- // template<class T> Vector
- // T& vector<T>::operator[](int i)
- // {
- // if (i<0 || sz<=i) error("vector: range error");
- // return v[i];
- // }
- // vector<T>(size)
- // {
- // v = malloc(size);
- // }
- // // more method definitions
- // ; // semicolon terminates
- //
- // DECLARE Vector<char*>; // create definitions for a vector of strings
- // IMPLEMENT Vector<char*>; // generate code to support vector of strings
- // vector<char*> vs(30); // declare a vector of 30 strings
- //
- */
-
- #include "defmacio.h"
- #include "cppdef.h"
-
- /* maximum number of template parameters */
- #define max_parms 32
- /* token buffer size */
- #define BSIZE 512
-
- typedef enum { class_template,
- declare_template,
- implement_template } template_type;
-
- typedef struct Template {
- struct Template* next; /* Next template or NULL */
- template_type type; /* type of template */
- char** args; /* argument names */
- char* body; /* program text */
- int line; /* Line number */
- char* file; /* file name */
- } TEMPLATE;
-
- struct Temp_Head { TEMPLATE* head; /* First template */
- TEMPLATE* tail; /* Last template */
- int nparms; /* Number of parameters */
- };
-
- static struct Hash_Table* template_table; /* Template hash table */
-
- static char* name_n = NULL; /* Name of current implementation */
-
- TEMPLATE* append_template(self, name, nparms, head)
- char* self;
- char* name;
- int nparms;
- TEMPLATE** head;
- {
- TEMPLATE* templ = (TEMPLATE*) getmem(sizeof(TEMPLATE));
- struct Temp_Head* th;
- if (template_table == NULL)
- template_table = init_Hash_Table();
- th = get_hash(template_table, name);
- if (th == NULL) { /* New template name, add head */
- th = (struct Temp_Head *) getmem(sizeof(struct Temp_Head));
- put_hash(template_table, savestring(name), th);
- th->head = templ;
- th->nparms = nparms;
- } else { /* Else Old template */
- th->tail->next = templ; /* Link new template after last */
- if (nparms != nparms) { /* Check # parms */
- fprintf(stderr, "%s: %s wrong number of parameters, expected %d\n",
- self, name, nparms);
- return NULL;
- }
- }
- th->tail = templ; /* New template is the last */
- templ->next = NULL;
- if(head != NULL) *head = th->head; /* Return head when requested */
- return templ; /* Return new template struct */
- }
-
- /*
- * Gather up the parameters
- */
- static char** template_parms(self, nparmsp, is_template)
- char* self; /* Macro name */
- Boolean is_template; /* True when template, else implement */
- int* nparmsp; /* Place to return parameter count */
- {
- char c; /* Current character */
- char buff[BSIZE]; /* character buffer */
- char* parms[max_parms]; /* vector of parameters */
- int nparms = 0; /* number of parameters */
-
- while (TRUE) {
- if (is_template && copytoken(buff) == NULL) /* throw out the type */
- return NULL;
- c = skip_blanks();
- if (c == '*') c = skip_blanks(); /* Allow * after type */
- if(!isalnum(c) && c != '"') {
- fprintf(stderr,
- "%s: Syntax error, strange character after parameter type %s: '%c'\n",
- self, buff, c);
- return NULL;
- }
- unget();
- parms[nparms++] = scan_token(",>"); /* get a parameter */
- c = skip_blanks();
- if (c == '>') break;
- if (c != ',') {
- fprintf(stderr, "%s: Syntax error, '%c' instead of comma after parameter %s\n",
- self, c, parms[nparms-1]);
- return NULL;
- }
- if(nparms >= max_parms) {
- fprintf(stderr, "%s: More than %d parameters\n", self, max_parms);
- return NULL;
- }
- }
- { char** result = (char**) getmem(sizeof(char*) * (nparms+1));
- result[nparms] = NULL;
- if(nparmsp != NULL) *nparmsp = nparms;
- while(--nparms>=0) result[nparms] = parms[nparms];
- return result;
- }
- }
-
- /*
- * Handle the declare translation
- */
- enum Do_What { do_declare, do_implement, do_implement_n };
-
- static int declare_implement (do_what, do_line)
- enum Do_What do_what; /* What to do */
- Boolean do_line; /* When true, use #line */
- {
- extern void macro_substitute(/* char* body, int nparm,
- char** parnames, char** parvalues */);
- char c; /* current character */
- char buff[BSIZE]; /* character buffer */
- char* name; /* class name */
- char* self; /* macro name */
- char** parms; /* Buffer for parameters */
- char** parmp;
- int nparms; /* Number of parameters */
- int n; /* Template number to process */
-
- if (copytoken(buff) == NULL) /* get the macro name */
- return 1;
- self = savestring(buff);
- if (do_what == do_implement_n) {
- if (copytoken(buff) == NULL) /* get the template number */
- return 1;
- n = atoi(buff);
- }
- if (copytoken(buff) == NULL) /* get the class name */
- return 1;
- name = savestring(buff);
- if(template_table == NULL) {
- fprintf(stderr, "%s: Template for %s isn't defined\n", self, name);
- return 1;
- }
- /*
- * Templates sometimes contain IMPLEMENT statements. This fouls
- * up the implement_n mechanism, because a whole class will get
- * implemented on one iteration. To stop this, we record the
- * name of the top-level implementation, and ignore any nested ones.
- */
- switch (do_what) {
- case do_implement_n:
- name_n = name; break;
- case do_implement:
- if (name_n != NULL && strcmp(name, name_n) != 0) {
- /* Ignore nested IMPLEMENT */
- fprintf(stderr, "Skipping IMPLEMENT %s\n", name); /* DEBUG */
- return 0;
- }
- } /* end case */
- /*
- * Gather up the parameters
- */
- c = skip_blanks();
- if (c != '<') {
- fprintf(stderr, "%s: Syntax error, missing < after %s %s\n",
- self, self, name);
- return 1;
- }
- if ((parms = template_parms(self, &nparms, NULL)) == NULL)
- return 1;
- /*
- * insert typedef's for pointer types
- * (this is a work around for a cfront 1.2 bug)
- */
- for (parmp = parms; *parmp != NULL; parmp++) {
- char* pname = *parmp;
- char* ptype = pname;
- int last = strlen(pname)-1;
- if (pname[last] == '*') {
- ptype = savestring(pname);
- if(parmstring(ptype) != 0) return 1;
- *parmp = ptype;
- if (do_what == do_declare) {
- sprintf(buff, "\n#ifndef %s_defined\n", ptype); puts(buff);
- sprintf(buff, "#define %s_defined 1\n", ptype); puts(buff);
- sprintf(buff, "typedef %s %s;\n", pname, ptype); puts(buff);
- puts("#endif\n");
- }
- } /* end if a pointer parameter */
- } /* end while for all parms */
- { TEMPLATE* templ;
- struct Temp_Head* th = get_hash(template_table, name);
- if(th == NULL) {
- fprintf(stderr, "%s: Template %s isn't defined\n", self, name);
- return 1;
- }
- for(templ = th->head; templ != NULL; templ = templ->next) {
- char lbuff[256];
- FILEINFO* finfo = MacOutFile;
- switch (do_what) {
- case do_declare: if(templ->type == implement_template) continue; break;
- case do_implement: if(templ->type != implement_template) continue; break;
- case do_implement_n:
- if(templ->type != implement_template) continue;
- if (n < 0) return 0;
- if (n-- > 0) continue;
- /* else fall through to implement template n */
- }
- while (finfo && !finfo->fp)
- finfo = finfo->parent;
- if (finfo)
- {
- sprintf(lbuff,"\n#line %d \"%s\"\n", finfo->line, finfo->filename);
- puts(lbuff);
- }
- if (do_line) {
- char* bodyp = templ->body;
- int newline = 0;
- /* Check for space at beginning of body */
- for(; isspace(*bodyp); bodyp++)
- if(*bodyp == '\n') {
- newline = 1;
- break;
- }
- sprintf(buff, "\n#line %d \"%s\"", newline + templ->line, templ->file);
- puts(buff);
- if(!newline) putchar('\n');
- }
- macro_substitute(templ->body, nparms, templ->args, parms);
- if (finfo)
- puts(lbuff);
- }
- }
- if (do_what == do_implement_n && n >= 0) {
- puts("\nBarf\n**** Template number too large ****\n");
- fprintf(stderr, "Last Implementation\n");
- exit(2);
- }
-
-
- /*
- * help user do a declare only once in each file
- * by defining a variable (used by declare_once defmacro for CCC -X)
- */
- if (do_what == do_declare) {
- char ptype[512];
- strcpy(ptype,name);
- strcat(ptype,"<");
- parmp = parms;
- strcat(ptype,*parmp);
- parmp++;
- while (*parmp != NULL) {
- strcat (ptype,",");
- strcat (ptype,*parmp);
- parmp++;
- }
- strcat(ptype,">");
- parmstring(ptype);
- sprintf(buff, "\n#ifndef %s_declared\n", ptype); puts(buff);
- sprintf(buff, "#define %s_declared 1\n", ptype); puts(buff);
- puts("#endif\n");
- }
-
- /*
- * Invoke the hook macros
- */
- if(do_what == do_declare) {
- puts("\n#ifdef DECLARE_HOOK");
- puts("\nDECLARE_HOOK(");
- } else {
- puts("\n#ifdef IMPLEMENT_HOOK");
- puts("\nIMPLEMENT_HOOK(");
- }
- puts(name);
- putchar('<');
- for (parmp = parms; *parmp != NULL;) {
- puts(*parmp);
- if (*++parmp == NULL) break;
- puts(" ,");
- }
- puts(">)\n#endif\n");
- return 0;
- }
-
- /*
- * Handle the template translation
- */
- int template (argc, argv)
- int argc;
- char** argv;
- {
- char c; /* current character */
- char buff[BSIZE]; /* character buffer */
- char* self; /* our name */
- char** parms; /* template parameters */
- int nparms; /* parameter count */
- char* file = savestring(current_file);
- if(copytoken(buff) == NULL) /* Grab macro name */
- return 1;
- self = savestring(buff);
- c = skip_blanks();
- if (c != '<') {
- fprintf(stderr, "%s: Syntax error, missing < after %s\n", self, self);
- return(1);
- }
- /*
- * Get the parameters
- */
- if ((parms = template_parms(self, &nparms, TRUE)) == NULL)
- return 1;
- /*
- * Find out what kind of template
- */
- { TEMPLATE* templ; /* new template */
- STRING body = (scan_start(), work_string);
- append_blanks(body);
- unget();
- if(copytoken(buff) == NULL) /* Get first token after template parms */
- return 1;
- append_STRING(body, buff);
- c = append_blanks(body);
- unget();
- /*
- * class template
- */
- if(!strcmp(buff, "class")) {
- extern int parmtype();
- char* name;
- if (copytoken(buff) == NULL) /* token after class is the class name */
- return 1;
- name = savestring(buff);
- templ = append_template(self, buff, nparms, NULL);
- if (templ == NULL) return 1;
- append_STRING(body, buff);
- while ((c = getchar()) != EOF) append_char(body, c);
- append_char(body, ';');
- append_char(body, EOS);
- templ->type = class_template;
- templ->args = parms;
- templ->body = savestring(body->buff);
- templ->line = current_line;
- templ->file = file;
- /* define a parmtype defmacro for this class name */
- new_defmacro(name, FALSE, FALSE, '>', '<', parmtype, "parmtype", NULL);
- } else {
- /*
- * auxillary template
- */
- if (c == '{') { /* An auxillary declaration template */
- TEMPLATE* t;
- { char* buffp = body->buff; /* Update line count */
- while (buffp < body->buffp)
- if(*buffp++ == '\n') current_line++;
- }
- body->buffp = body->buff; /* Empty out body */
- { char p; /* Copy everything except surrounding {} */
- getchar();
- for (p = getchar(); (c = getchar()) != EOF; p = c)
- append_char(body, p);
- append_char(body, EOS);
- }
- templ = append_template(self, buff, nparms, &t);
- if (templ == NULL) return 1;
- templ->args = parms;
- templ->body = savestring(body->buff);
- templ->line = current_line;
- templ->file = file;
- templ->type = declare_template; /* Determine template type */
- for (; t != NULL; t = t->next)
- if(t->type == class_template) { /* If after a class template */
- templ->type = implement_template;
- break;
- }
- }
- /*
- * function template
- */
- else {
- template_type type = implement_template;
- if (!strcmp(buff, "inline")) type = declare_template;
- while ((c = getchar()) != EOF) append_char(body, c);
- append_char(body, EOS);
- { char* bodyp = body->buff;
- char* startname;
- char* endname;
- do { /* Search for name<..>:: */
- int nest;
- /*
- while((c = *bodyp++) != EOS && c != '<');
- if(c == EOS) {
- fprintf(stderr, "Can't find template class\n");
- return 1;
- }
- */
- nest=0;
- while ((c = *bodyp++) != EOS)
- {
- if (c == ':' && nest == 0)
- break;
- else if (c == '<')
- nest++;
- else if (c == '>')
- nest--;
- }
- if (c == EOS) {
- fprintf(stderr, "Can't find template class\n");
- return 1;
- }
- /* Scan back to end of name */
- nest=0;
- while (bodyp != body->buff)
- {
- c = *--bodyp;
- if (c == '>')
- nest++;
- else if (nest == 1 && c == '<')
- break;
- else if (c == '<')
- nest--;
- }
- if (bodyp == body->buff) {
- fprintf(stderr, "Can't find template class\n");
- return 1;
- }
- bodyp++;
- /* Scan backwards for name */
- for (endname = bodyp-1; isspace(*(endname-1)); endname--);
- for (startname = endname; startname>body->buff; startname--) {
- c = *(startname-1);
- if(!isalnum(c) && c!='_' && c!='$') break;
- }
- strncpy(buff, startname, endname-startname);
- buff[endname-startname] = EOS;
- /* Ensure token after < is parm */
- while(isspace(*bodyp)) bodyp++;
- } while (strncmp(bodyp, *parms, strlen(*parms)));
- templ = append_template(self, buff, nparms, NULL);
- if (templ == NULL) return 1;
- templ->type = type;
- templ->args = parms;
- templ->body = savestring(body->buff);
- templ->line = current_line;
- templ->file = file;
- }
- }
- }
- }
- free(self);
- return 0;
- }
-
- int declare (argc, argv)
- int argc;
- char** argv;
- {
- return declare_implement(do_declare, argc>1);
- }
-
- int implement (argc, argv)
- int argc;
- char** argv;
- {
- if (argc > 2 && name_n == NULL) name_n = savestring(argv[2]);
- return declare_implement(do_implement, argc>1);
- }
-
- /*
- * Implement the Nth template
- * Example:
- * IMPLEMENT 3 Vector<char*>
- */
- int implement_n (argc, argv)
- int argc;
- char** argv;
- {
- return declare_implement(do_implement_n, argc>1);
- }
-
- int declare_once (argc, argv)
- int argc;
- char** argv;
- {
- char buff[BSIZE]; /* character buffer */
- char* self; /* macro name */
- char* parmtype; /* parm type name */
- char* parmname;
-
- if (copytoken(buff) == NULL) /* get the macro name */
- return 1;
- self = savestring(buff);
-
- parmtype = scan_token(">"); /* get full parameters type name */
- parmname = savestring(parmtype);
- if(parmstring(parmtype) != 0) return 1;
- sprintf(buff, "\n#ifndef %s_declared\n", parmtype); puts(buff);
- sprintf(buff, "#define %s_declared 1\n", parmtype); puts(buff);
- sprintf(buff, "DECLARE %s\n", parmname); puts(buff);
- puts("#endif\n");
- return 0;
- }
-