home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / prog / c / cpp.lha / template.c < prev    next >
C/C++ Source or Header  |  1991-08-05  |  20KB  |  616 lines

  1. /*
  2.  
  3.  
  4.  Copyright (C) 1990 Texas Instruments Incorporated.
  5.  
  6.  Permission is granted to any individual or institution to use, copy, modify,
  7.  and distribute this software, provided that this complete copyright and
  8.  permission notice is maintained, intact, in all copies and supporting
  9.  documentation.
  10.  
  11.  Texas Instruments Incorporated provides this software "as is" without
  12.  express or implied warranty.
  13.  
  14.  
  15.  *
  16.  * Edit history
  17.  * Created: LGO 30-Mar-89 -- Initial design and implementation.
  18.  * Updated: MJF 21-May-90 -- Added DECLARE_ONCE for CCC -X.
  19.  * Updated: MJF 01-Mar-91 -- Added fix when expanding member functions
  20.  *                           which return a template class
  21.  * Updated: GPD 25-Jun-91 -- Fix #line nesting. When template expansions done.
  22.  *
  23.  * Template, DECLARE and IMPLEMENT defmacro
  24.  *
  25.  * TO DO:
  26.  *    Generate a warning message when a template is encountered for a class
  27.  *    that has already had a DECLARE or IMPLEMENT run on it.
  28.  *
  29.  *    Scan for the "inline" keyword at the beginning of the "garbage pail"
  30.  *    template.
  31.  *
  32.  *     template needs a more robust heuristic for finding the class name
  33.  */
  34.  
  35. /*
  36. // C++ paramertized type macro support
  37. // This follows the conventions outlined in the 1988 USENIX C++ conference
  38. // paper titled "Parameterized Types for C++" by Bjarne Stroustrup
  39. //
  40. // To use, insert the following
  41. //      #pragma defmacro template <template>
  42. //
  43. //   template provides a specialized  way  of  defining  a  macro
  44. //   which supports parameterized classes.  A parameterized class
  45. //   is a class in which one or more pieces can  be  declared  at
  46. //   compile  time.  A typical example is a container class where
  47. //   the type of the contained object may change.  For example, a
  48. //   Vector  class would be paramertized for the type of the vec-
  49. //   tor elements.
  50.  
  51. //   Templates are expanded in two parts:  the  declarative  part
  52. //   needed  by  every  program file which uses the parameterized
  53. //   class, and the implementation part which needs  to  be  com-
  54. //   piled  once  for  the  class.   The  DECLARE macro expands a
  55. //   template's declarative part, and the IMPLEMENT macro expands
  56. //   a template's implementation part.
  57. //
  58. //   There are 3 kinds of template:
  59. //
  60. //   template<parms> class NAME { class_description };
  61. //        Defines a  template  the  declaration  of  class  NAME.
  62. //        PARAMETERS is:
  63. //           parms ::= type name [, parms]
  64. //        where type is ignored (its included for  AT&T  compata-
  65. //        bility)  and  name  is  the name of the parameter which
  66. //        will be substituted when the template is expanded  (Its
  67. //        like a macro argument in a #define).
  68. //
  69. //   template<parms> NAME result_type NAME<parms>::function { ... };
  70. //        Defines  a  method  for  the implementation of the NAME
  71. //        class.
  72. //
  73. //   template<class ARG> NAME inline rtype NAME<parms>::func { ... };
  74. //        Defines  an  inline  method  for the declaration of the
  75. //        NAME class.
  76. //
  77. //   template<class ARG> NAME {anything};
  78. //        This form is used to  define  anything  else  you  want
  79. //        associated with a template.  Use this form for defining
  80. //        typedef's or overloaded friend functions.  When this is
  81. //        found  before  the class template, the contents will be
  82. //        expanded before the class declaration.   When  this  is
  83. //
  84. // Example:
  85. //      template<class T> Vector {
  86. //      typedef Boolean (*Compare) (T&, T&);
  87. //      };
  88. //
  89. //      template<class T> class Vector {
  90. //              T* v;
  91. //              int sz;
  92. //      public:
  93. //              Vector<T>(int);
  94. //              T& operator[](int);
  95. //              T& elem(int i) { return v[i]; }
  96. //              // ...
  97. //      };
  98. //
  99. //      template<class T> Vector
  100. //      T& vector<T>::operator[](int i)
  101. //      {
  102. //              if (i<0 || sz<=i) error("vector: range error");
  103. //              return v[i];
  104. //      }
  105. //      vector<T>(size)
  106. //      {
  107. //              v = malloc(size);
  108. //      }
  109. //      // more method definitions
  110. //      ; // semicolon terminates
  111. //
  112. //      DECLARE Vector<char*>;   // create definitions for a vector of strings
  113. //      IMPLEMENT Vector<char*>; // generate code to support  vector of strings
  114. //      vector<char*> vs(30);    // declare a vector of 30 strings
  115. //
  116. */
  117.  
  118. #include "defmacio.h"
  119. #include "cppdef.h"
  120.  
  121. /* maximum number of template parameters */
  122. #define max_parms 32
  123. /* token buffer size */
  124. #define BSIZE 512
  125.  
  126. typedef enum { class_template,
  127.                declare_template,
  128.                implement_template } template_type;
  129.  
  130. typedef struct Template {
  131.   struct Template* next;        /* Next template or NULL */
  132.   template_type type;           /* type of template */
  133.   char** args;                  /* argument names */
  134.   char* body;                   /* program text */
  135.   int line;                     /* Line number */
  136.   char* file;                   /* file name */
  137. } TEMPLATE;
  138.  
  139. struct Temp_Head { TEMPLATE* head;        /* First template */
  140.                    TEMPLATE* tail;  /* Last template */
  141.                    int nparms;      /* Number of parameters */
  142.                  };
  143.  
  144. static struct Hash_Table* template_table; /* Template hash table */
  145.  
  146. static char* name_n = NULL;     /* Name of current implementation */
  147.  
  148. TEMPLATE* append_template(self, name, nparms, head)
  149.   char* self;
  150.   char* name;
  151.   int nparms;
  152.   TEMPLATE** head;
  153. {
  154.   TEMPLATE* templ = (TEMPLATE*) getmem(sizeof(TEMPLATE));
  155.   struct Temp_Head* th;
  156.   if (template_table == NULL)
  157.     template_table = init_Hash_Table();
  158.   th = get_hash(template_table, name);
  159.   if (th == NULL) {                       /* New template name, add head */
  160.     th = (struct Temp_Head *) getmem(sizeof(struct Temp_Head));
  161.     put_hash(template_table, savestring(name), th);
  162.     th->head = templ;
  163.     th->nparms = nparms;
  164.   } else {                                /* Else Old template */
  165.     th->tail->next = templ;               /* Link new template after last */
  166.     if (nparms != nparms) {               /* Check # parms */
  167.       fprintf(stderr, "%s: %s wrong number of parameters, expected %d\n",
  168.               self, name, nparms);
  169.       return NULL;
  170.     }
  171.   }
  172.   th->tail = templ;                       /* New template is the last */
  173.   templ->next = NULL;
  174.   if(head != NULL) *head = th->head;      /* Return head when requested */
  175.   return templ;                           /* Return new template struct */
  176. }
  177.  
  178. /*
  179.  * Gather up the parameters
  180.  */
  181. static char** template_parms(self, nparmsp, is_template)
  182.   char* self;                   /* Macro name */
  183.   Boolean is_template;          /* True when template, else implement */
  184.   int* nparmsp;                 /* Place to return parameter count */
  185. {
  186.   char c;                       /* Current character */
  187.   char buff[BSIZE];             /* character buffer */
  188.   char* parms[max_parms];       /* vector of parameters */
  189.   int nparms = 0;               /* number of parameters */
  190.  
  191.   while (TRUE) {
  192.     if (is_template && copytoken(buff) == NULL) /* throw out the type */
  193.       return NULL;
  194.     c = skip_blanks();
  195.     if (c == '*') c = skip_blanks();      /* Allow * after type */
  196.     if(!isalnum(c) && c != '"') {
  197.       fprintf(stderr,
  198.          "%s: Syntax error, strange character after parameter type %s: '%c'\n",
  199.          self, buff, c);
  200.       return NULL;
  201.     }
  202.     unget();
  203.     parms[nparms++] = scan_token(",>"); /* get a parameter */
  204.     c = skip_blanks();
  205.     if (c == '>') break;
  206.     if (c != ',') {
  207.       fprintf(stderr, "%s: Syntax error, '%c' instead of comma after parameter %s\n",
  208.               self, c, parms[nparms-1]);
  209.       return NULL;
  210.     }
  211.     if(nparms >= max_parms) {
  212.       fprintf(stderr, "%s: More than %d parameters\n", self, max_parms);
  213.       return NULL;
  214.     }
  215.   }
  216.   { char** result = (char**) getmem(sizeof(char*) * (nparms+1));
  217.     result[nparms] = NULL;
  218.     if(nparmsp != NULL) *nparmsp = nparms;
  219.     while(--nparms>=0) result[nparms] = parms[nparms];
  220.     return result;
  221.   }
  222. }
  223.  
  224. /*
  225.  * Handle the declare translation
  226.  */
  227. enum Do_What { do_declare, do_implement, do_implement_n };
  228.  
  229. static int declare_implement (do_what, do_line)
  230.    enum Do_What do_what;          /* What to do */
  231.    Boolean do_line;               /* When true, use #line */
  232. {
  233.   extern void macro_substitute(/* char* body, int nparm,
  234.                                   char** parnames, char** parvalues */);
  235.   char c;                       /* current character */
  236.   char buff[BSIZE];             /* character buffer */
  237.   char* name;                   /* class name */
  238.   char* self;                   /* macro name */
  239.   char** parms;                 /* Buffer for parameters */
  240.   char** parmp;
  241.   int nparms;                   /* Number of parameters */
  242.   int n;                        /* Template number to process */
  243.  
  244.   if (copytoken(buff) == NULL)  /* get the macro name */
  245.     return 1;
  246.   self = savestring(buff);
  247.   if (do_what == do_implement_n) {
  248.     if (copytoken(buff) == NULL)        /* get the template number */
  249.       return 1;
  250.     n = atoi(buff);
  251.   }
  252.   if (copytoken(buff) == NULL)  /* get the class name */
  253.     return 1;
  254.   name = savestring(buff);
  255.   if(template_table == NULL) {
  256.     fprintf(stderr, "%s: Template for %s isn't defined\n", self, name);
  257.     return 1;
  258.   }
  259.   /*
  260.    * Templates sometimes contain IMPLEMENT statements.  This fouls
  261.    * up the implement_n mechanism, because a whole class will get
  262.    * implemented on one iteration.  To stop this, we record the
  263.    * name of the top-level implementation, and ignore any nested ones.
  264.    */
  265.   switch (do_what) {
  266.   case do_implement_n:
  267.     name_n = name; break;
  268.   case do_implement:
  269.     if (name_n != NULL && strcmp(name, name_n) != 0) {
  270.       /* Ignore nested IMPLEMENT */
  271.       fprintf(stderr, "Skipping IMPLEMENT %s\n", name); /* DEBUG */
  272.       return 0;
  273.     }
  274.   } /* end case */
  275.   /*
  276.    * Gather up the parameters
  277.    */
  278.   c = skip_blanks();
  279.   if (c != '<') {
  280.     fprintf(stderr, "%s: Syntax error, missing < after %s %s\n",
  281.             self, self, name);
  282.     return 1;
  283.   }
  284.   if ((parms = template_parms(self, &nparms, NULL)) == NULL)
  285.     return 1;
  286.   /*
  287.    * insert typedef's for pointer types
  288.    * (this is a work around for a cfront 1.2 bug)
  289.    */
  290.   for (parmp = parms; *parmp != NULL; parmp++) {
  291.     char* pname = *parmp;
  292.     char* ptype = pname;
  293.     int last = strlen(pname)-1;
  294.     if (pname[last] == '*') {
  295.       ptype = savestring(pname);
  296.       if(parmstring(ptype) != 0) return 1;
  297.       *parmp = ptype;
  298.       if (do_what == do_declare) {
  299.         sprintf(buff, "\n#ifndef %s_defined\n", ptype); puts(buff);
  300.         sprintf(buff, "#define %s_defined 1\n", ptype); puts(buff);
  301.         sprintf(buff, "typedef %s %s;\n", pname, ptype); puts(buff);
  302.         puts("#endif\n");
  303.       }
  304.     } /* end if a pointer parameter */
  305.   } /* end while for all parms */
  306.   { TEMPLATE* templ;
  307.     struct Temp_Head* th = get_hash(template_table, name);
  308.     if(th == NULL) {
  309.       fprintf(stderr, "%s: Template %s isn't defined\n", self, name);
  310.       return 1;
  311.     }
  312.     for(templ = th->head; templ != NULL; templ = templ->next) {
  313.       char lbuff[256];
  314.       FILEINFO* finfo = MacOutFile;
  315.       switch (do_what) {
  316.       case do_declare:   if(templ->type == implement_template) continue; break;
  317.       case do_implement: if(templ->type != implement_template) continue; break;
  318.       case do_implement_n:
  319.         if(templ->type != implement_template) continue;
  320.         if (n < 0) return 0;
  321.         if (n-- > 0) continue;
  322.         /* else fall through to implement template n */
  323.       }
  324.       while (finfo && !finfo->fp)
  325.         finfo = finfo->parent;
  326.       if (finfo)
  327.       {
  328.         sprintf(lbuff,"\n#line %d \"%s\"\n", finfo->line, finfo->filename);
  329.         puts(lbuff);
  330.       }
  331.       if (do_line) {
  332.         char* bodyp = templ->body;
  333.         int newline = 0;
  334.         /*  Check for space at beginning of body */
  335.         for(; isspace(*bodyp); bodyp++)
  336.           if(*bodyp == '\n') {
  337.             newline = 1;
  338.             break;
  339.           }
  340.         sprintf(buff, "\n#line %d \"%s\"", newline + templ->line, templ->file);
  341.         puts(buff);
  342.         if(!newline) putchar('\n');
  343.       }
  344.       macro_substitute(templ->body, nparms, templ->args, parms);
  345.       if (finfo)
  346.         puts(lbuff);
  347.     }
  348.   }
  349.   if (do_what == do_implement_n && n >= 0) {
  350.     puts("\nBarf\n**** Template number too large ****\n");
  351.     fprintf(stderr, "Last Implementation\n");
  352.     exit(2);
  353.   }
  354.  
  355.  
  356. /*
  357.  *  help user do a declare only once in each file
  358.  *  by defining a variable (used by declare_once defmacro for CCC -X)
  359.  */
  360.   if (do_what == do_declare) {
  361.     char ptype[512];
  362.     strcpy(ptype,name);
  363.     strcat(ptype,"<");
  364.     parmp = parms;
  365.     strcat(ptype,*parmp);
  366.     parmp++;
  367.     while (*parmp != NULL) {
  368.       strcat (ptype,",");
  369.       strcat (ptype,*parmp);
  370.       parmp++;
  371.     }
  372.     strcat(ptype,">");
  373.     parmstring(ptype);
  374.     sprintf(buff, "\n#ifndef %s_declared\n", ptype);  puts(buff);
  375.     sprintf(buff, "#define %s_declared 1\n", ptype); puts(buff);
  376.     puts("#endif\n");
  377.   }
  378.  
  379.   /*
  380.    * Invoke the hook macros
  381.    */
  382.   if(do_what == do_declare) {
  383.     puts("\n#ifdef DECLARE_HOOK");
  384.     puts("\nDECLARE_HOOK(");
  385.   } else {
  386.     puts("\n#ifdef IMPLEMENT_HOOK");
  387.     puts("\nIMPLEMENT_HOOK(");
  388.   }
  389.   puts(name);
  390.   putchar('<');
  391.   for (parmp = parms; *parmp != NULL;) {
  392.     puts(*parmp);
  393.     if (*++parmp == NULL) break;
  394.     puts(" ,");
  395.   }
  396.   puts(">)\n#endif\n");
  397.   return 0;
  398. }
  399.  
  400. /*
  401.  * Handle the template translation
  402.  */
  403. int template (argc, argv)
  404.   int argc;
  405.   char** argv;
  406. {
  407.   char c;                       /* current character */
  408.   char buff[BSIZE];             /* character buffer */
  409.   char* self;                   /* our name */
  410.   char** parms;                 /* template parameters */
  411.   int nparms;                   /* parameter count */
  412.   char* file = savestring(current_file);
  413.   if(copytoken(buff) == NULL)   /* Grab macro name */
  414.     return 1;
  415.   self = savestring(buff);
  416.   c = skip_blanks();
  417.   if (c != '<') {
  418.     fprintf(stderr, "%s: Syntax error, missing < after %s\n", self, self);
  419.     return(1);
  420.   }
  421.   /*
  422.    * Get the parameters
  423.    */
  424.   if ((parms = template_parms(self, &nparms, TRUE)) == NULL)
  425.     return 1;
  426.   /*
  427.    * Find out what kind of template
  428.    */
  429.   { TEMPLATE* templ;            /* new template */
  430.     STRING body = (scan_start(), work_string);
  431.     append_blanks(body);
  432.     unget();
  433.     if(copytoken(buff) == NULL) /* Get first token after template parms */
  434.       return 1;
  435.     append_STRING(body, buff);
  436.     c = append_blanks(body);
  437.     unget();
  438.     /*
  439.      * class template
  440.      */
  441.     if(!strcmp(buff, "class")) {
  442.       extern int parmtype();
  443.       char* name;
  444.       if (copytoken(buff) == NULL) /* token after class is the class name */
  445.         return 1;
  446.       name = savestring(buff);
  447.       templ = append_template(self, buff, nparms, NULL);
  448.       if (templ == NULL) return 1;
  449.       append_STRING(body, buff);
  450.       while ((c = getchar()) != EOF) append_char(body, c);
  451.       append_char(body, ';');
  452.       append_char(body, EOS);
  453.       templ->type = class_template;
  454.       templ->args = parms;
  455.       templ->body = savestring(body->buff);
  456.       templ->line = current_line;
  457.       templ->file = file;
  458.       /* define a parmtype defmacro for this class name */
  459.       new_defmacro(name, FALSE, FALSE, '>', '<', parmtype, "parmtype", NULL);
  460.     } else {
  461.       /*
  462.        * auxillary template
  463.        */
  464.       if (c == '{') {           /* An auxillary declaration template */
  465.         TEMPLATE* t;
  466.         { char* buffp = body->buff; /* Update line count */
  467.           while (buffp < body->buffp)
  468.             if(*buffp++ == '\n') current_line++;
  469.         }
  470.         body->buffp = body->buff; /* Empty out body */
  471.         { char p;                 /* Copy everything except surrounding {} */
  472.           getchar();
  473.           for (p = getchar(); (c = getchar()) != EOF; p = c)
  474.             append_char(body, p);
  475.           append_char(body, EOS);
  476.         }
  477.         templ = append_template(self, buff, nparms, &t);
  478.         if (templ == NULL) return 1;
  479.         templ->args = parms;
  480.         templ->body = savestring(body->buff);
  481.         templ->line = current_line;
  482.         templ->file = file;
  483.         templ->type = declare_template;   /* Determine template type */
  484.         for (; t != NULL; t = t->next)
  485.           if(t->type == class_template) { /* If after a class template */
  486.             templ->type = implement_template;
  487.             break;
  488.           }
  489.       }
  490.       /*
  491.        * function template
  492.        */
  493.       else {
  494.         template_type type = implement_template;
  495.         if (!strcmp(buff, "inline")) type = declare_template;
  496.         while ((c = getchar()) != EOF) append_char(body, c);
  497.         append_char(body, EOS);
  498.         { char* bodyp = body->buff;
  499.           char* startname;
  500.           char* endname;
  501.           do {                            /* Search for name<..>:: */
  502.             int nest;
  503. /*
  504.             while((c = *bodyp++) != EOS && c != '<');
  505.             if(c == EOS) {
  506.               fprintf(stderr, "Can't find template class\n");
  507.               return 1;
  508.             }
  509. */
  510.             nest=0;
  511.             while ((c = *bodyp++) != EOS)
  512.             {
  513.               if (c == ':' && nest == 0)
  514.                 break;
  515.               else if (c == '<')
  516.                 nest++;
  517.               else if (c == '>')
  518.                 nest--;
  519.             }
  520.             if (c == EOS) {
  521.               fprintf(stderr, "Can't find template class\n");
  522.               return 1;
  523.             }
  524.                                           /* Scan back to end of name */
  525.             nest=0;
  526.             while (bodyp != body->buff)
  527.             {
  528.               c = *--bodyp;
  529.               if (c == '>')
  530.                 nest++;
  531.               else if (nest == 1 && c == '<')
  532.                 break;
  533.               else if (c == '<')
  534.                 nest--;
  535.             }
  536.             if (bodyp == body->buff) {
  537.               fprintf(stderr, "Can't find template class\n");
  538.               return 1;
  539.             }
  540.             bodyp++;
  541.                                           /* Scan backwards for name */
  542.             for (endname = bodyp-1; isspace(*(endname-1)); endname--);
  543.             for (startname = endname; startname>body->buff; startname--) {
  544.               c = *(startname-1);
  545.               if(!isalnum(c) && c!='_' && c!='$') break;
  546.             }
  547.             strncpy(buff, startname, endname-startname);
  548.             buff[endname-startname] = EOS;
  549.                                           /* Ensure token after < is parm */
  550.             while(isspace(*bodyp)) bodyp++;
  551.           } while (strncmp(bodyp, *parms, strlen(*parms)));
  552.           templ = append_template(self, buff, nparms, NULL);
  553.           if (templ == NULL) return 1;
  554.           templ->type = type;
  555.           templ->args = parms;
  556.           templ->body = savestring(body->buff);
  557.           templ->line = current_line;
  558.           templ->file = file;
  559.         }
  560.       }
  561.     }
  562.   }
  563.   free(self);
  564.   return 0;
  565. }
  566.  
  567. int declare (argc, argv)
  568.   int argc;
  569.   char** argv;
  570. {
  571.   return declare_implement(do_declare, argc>1);
  572. }
  573.  
  574. int implement (argc, argv)
  575.   int argc;
  576.   char** argv;
  577. {
  578.   if (argc > 2 && name_n == NULL) name_n = savestring(argv[2]);
  579.   return declare_implement(do_implement, argc>1);
  580. }
  581.  
  582. /*
  583.  * Implement the Nth template
  584.  *   Example:
  585.  *      IMPLEMENT 3 Vector<char*>
  586.  */
  587. int implement_n (argc, argv)
  588.   int argc;
  589.   char** argv;
  590. {
  591.   return declare_implement(do_implement_n, argc>1);
  592. }
  593.  
  594. int declare_once (argc, argv)
  595.   int argc;
  596.   char** argv;
  597. {
  598.   char buff[BSIZE];             /* character buffer */
  599.   char* self;                   /* macro name */
  600.   char* parmtype;               /* parm type name */
  601.   char* parmname;
  602.  
  603.   if (copytoken(buff) == NULL)  /* get the macro name */
  604.     return 1;
  605.   self = savestring(buff);
  606.  
  607.   parmtype = scan_token(">"); /* get full parameters type name */
  608.   parmname = savestring(parmtype);
  609.   if(parmstring(parmtype) != 0) return 1;
  610.   sprintf(buff, "\n#ifndef %s_declared\n", parmtype); puts(buff);
  611.   sprintf(buff, "#define %s_declared 1\n", parmtype); puts(buff);
  612.   sprintf(buff, "DECLARE %s\n", parmname); puts(buff);
  613.   puts("#endif\n");
  614.   return 0;
  615. }
  616.