home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / C++-7 / DISK8 / MFC / SAMPLES / TEMPLDEF / TEMPLDEF.CP$ / templdef
Encoding:
Text File  |  1992-03-11  |  13.5 KB  |  589 lines

  1. // templdef.cpp : A simple C++ template expansion utility.
  2. //
  3. //   This utility takes a template file which uses a syntax
  4. //   subset similar to the proposed future C++ template
  5. //   (parameterized type) syntax, and expands it into the
  6. //   equivalent current C++ syntax.
  7. //
  8. //   Note that this is intended as an example program, and
  9. //   handles only a specific syntax in the template files.
  10. //
  11. // This is a part of the Microsoft Foundation Classes C++ library.
  12. // Copyright (C) 1992 Microsoft Corporation
  13. // All rights reserved.
  14. //
  15. // This source code is only intended as a supplement to the
  16. // Microsoft Foundation Classes Reference and Microsoft
  17. // QuickHelp documentation provided with the library.
  18. // See these sources for detailed information regarding the
  19. // Microsoft Foundation Classes product.
  20.  
  21. #include <ctype.h>
  22. #include <afxcoll.h>
  23.  
  24. #define istringTypeParamMax 10
  25.  
  26. CString string_h;
  27. CString string_cpp;
  28. CString string_ctt;
  29.  
  30. CString stringTemplate = "template";
  31. CString stringClass = "class";
  32. CString stringTemplateName;
  33. CString stringTypedefName;
  34.  
  35. const char chMoreThanOne = '\0';
  36. const char chNot = '\0';
  37.  
  38. /////////////////////////////////////////////////////////////////////////////
  39.  
  40. // UsageErr:
  41. // A utility function.  Write an error message, followed by a usage summary.
  42. //
  43. void UsageErr(const char* szErrorMessage = NULL,
  44.               const char* szErrorParam = NULL)
  45. {
  46.     if (szErrorMessage != NULL)
  47.     {
  48.         fprintf(stderr, "templdef : error: ");
  49.         fprintf(stderr, szErrorMessage, szErrorParam);
  50.         fprintf(stderr, ".\n\n");
  51.     }
  52.  
  53.     fprintf(stderr, "templdef : usage:\n\n"
  54.         "  Suppose a class CMap exists in the template file map.ctt, which\n"
  55.         "  has two parameterized types Foo and Bar. To create a new class\n"
  56.         "  CMapFooToBar, declared in footobar.h and defined in footobar.cpp,\n"
  57.         "  use the following command:\n"
  58.         "\n"
  59.         "  templdef \"CMap<Foo, Bar> CMapFooToBar\" map.ctt footobar.h footobar.cpp\n"
  60.         "\n"
  61.         "  This command is similar to the proposed future C++ syntax for templates:\n"
  62.         "\n"
  63.         "  typedef CMap<Foo, Bar> CMapFooToBar;\n");
  64.  
  65.     exit(1);
  66. }
  67.  
  68. void CheckSyntax(BOOL fGoodSyntax, const char* sz)
  69. {
  70.     if (!fGoodSyntax)
  71.         UsageErr("expecting %s", sz);
  72. }
  73.  
  74. /////////////////////////////////////////////////////////////////////////////
  75. // CTokenFile
  76. // For the purpose of reading source code, this sort of CStdioFile derivative
  77. // proves quite handy.  This could be made really powerful, but this is a
  78. // start.
  79.  
  80. class CTokenFile : public CStdioFile
  81. {
  82. private:
  83.     enum { ichMax = 1024 };
  84.     char ach[ichMax];
  85.     int ich;
  86.     static CString stringToken;
  87.     BOOL fComment;
  88.     void GetBuf();
  89.     char GetChar() { if (ach[ich] == chNot) GetBuf(); return ach[ich++]; }
  90.     char PeekChar() { return ach[ich]; }
  91.     void UnGetChar() { --ich; } // note: really a single char pushback
  92. public:
  93.     void InitBuf(const char* sz);
  94.     static CString& TokenString() { return stringToken; }
  95.     CTokenFile();
  96.     char GetToken();
  97.     char GetPrintToken();
  98.     char GetTypeToken();
  99.     char GetNameToken();
  100.     void PutToken(char ch) { fputc(ch, m_pStream); }
  101.     void PutToken(const CString& string) { WriteString(string); }
  102.     void PutToken() { WriteString(stringToken); }
  103.     BOOL AtFileEnd() { return ach[0] == chNot; }
  104.     BOOL AtBufEnd() { return ach[ich] == chNot; }
  105.     void SafeOpen(const CString& string, UINT nStyleFlags);
  106. };
  107. CString CTokenFile::stringToken;
  108.  
  109. CTokenFile::CTokenFile()
  110. {
  111.     ach[0] = '\n';
  112.     ach[1] = chNot;
  113.     ich = 1;
  114.     fComment = FALSE;
  115. };
  116.  
  117. void CTokenFile::GetBuf()
  118. {
  119.     if (ReadString(ach, ichMax) == NULL)
  120.         ach[0] = chNot;
  121.  
  122.     ich = 0;
  123. }
  124.  
  125. void CTokenFile::InitBuf(const char* sz)
  126. {
  127.     strncpy(ach, sz, ichMax);
  128.     ich = 0;
  129. }
  130.  
  131. void CTokenFile::SafeOpen(const CString& string, UINT nStyleFlags)
  132. {
  133.     BOOL fSuccess = Open(string, nStyleFlags, 0);
  134.  
  135.     if (!fSuccess)
  136.         UsageErr("can't open file \"%s\"", string);
  137. }
  138.  
  139. char CTokenFile::GetToken()
  140. {
  141.     if (AtBufEnd())
  142.         GetBuf();
  143.     
  144.     if (AtFileEnd())
  145.         exit(0);
  146.  
  147.     fComment = FALSE;
  148.  
  149.     char ch = GetChar();
  150.     char* pch = stringToken.GetBuffer(1024);
  151.     char* pchInit = pch;
  152.     *pch++ = ch;
  153.     *pch = '\0';
  154.  
  155.     // assuming this doesn't "really" release the buffer!
  156.     stringToken.ReleaseBuffer(1);
  157.  
  158.     if (!isalnum(ch) &&
  159.         ch != '/'    &&
  160.         ch != '_'    &&
  161.         ch != '\''   &&
  162.         ch != '"'    &&
  163.         ch != '#')
  164.     {
  165.         return ch;
  166.     }
  167.  
  168.     if ((ch == '\'') || (ch == '"'))
  169.     {
  170.         char ch2;
  171.         while ((ch2 = GetChar()) != ch)
  172.         {
  173.             *pch++ = ch2;
  174.         }
  175.         *pch++ = ch2;
  176.         stringToken.ReleaseBuffer(pch - pchInit);
  177.         return chMoreThanOne;
  178.     }
  179.  
  180.     if (ch == '/')
  181.     {   
  182.         char ch2 = GetChar();
  183.         if (AtFileEnd()) exit(0);
  184.         if (ch2 == '/')
  185.         {
  186.             char ch3 = GetChar();
  187.             if (ch3 != '$')
  188.             {
  189.                 UnGetChar();
  190.                 *pch++ = '/';
  191.                 fComment = TRUE;
  192.                 while ((ch3 = GetChar()) != '\n')
  193.                     *pch++ = ch3;
  194.             }
  195.             else
  196.             {
  197.                 stringToken.ReleaseBuffer(0);
  198.             }
  199.             *pch++ = '\n';
  200.             stringToken.ReleaseBuffer(pch - pchInit);
  201.             return chMoreThanOne;
  202.         }
  203.         else if (ch2 == '*')
  204.         {
  205.             char ch3 = chNot;
  206.             char ch4 = chNot;
  207.             *pch++ = '*';
  208.             fComment = TRUE;
  209.             while (ch4 != '/')
  210.             {
  211.                 while ((ch3 = GetChar()) != '*')
  212.                     *pch++ = ch3;
  213.                 *pch++ = '*';   
  214.  
  215.                 ch4 = GetChar();
  216.                 if (ch4 != '/')
  217.                     UnGetChar();
  218.             }
  219.             *pch++ = '/';
  220.             stringToken.ReleaseBuffer(pch - pchInit);
  221.             return chMoreThanOne;
  222.         }
  223.         else
  224.         {
  225.             UnGetChar();
  226.             return ch;
  227.         }
  228.     }
  229.  
  230.     if (isdigit(ch))
  231.     {
  232.         char ch2;
  233.         ch2 = GetChar();
  234.         if (!isalpha(ch2))
  235.         {
  236.             UnGetChar();
  237.             return ch;
  238.         }
  239.  
  240.         *pch++ = ch2;
  241.         stringToken.ReleaseBuffer(pch - pchInit);
  242.         return chMoreThanOne;
  243.     }
  244.  
  245.     while ((!AtFileEnd()) &&
  246.         (ch = GetChar(), (isalnum(ch) || (ch == '_'))))
  247.     {
  248.         *pch++ = ch;
  249.     }
  250.  
  251.     if (!AtFileEnd())
  252.         UnGetChar();
  253.  
  254.     stringToken.ReleaseBuffer(pch - pchInit);
  255.     return chMoreThanOne;
  256. }
  257.  
  258. char CTokenFile::GetPrintToken()
  259. {
  260.     char chToken;
  261.     while (((chToken = GetToken()) != chMoreThanOne) &&
  262.            (isspace(chToken)) || fComment)
  263.         /* try again */ ;
  264.  
  265.     return chToken;
  266. }
  267.  
  268. char CTokenFile::GetTypeToken()
  269. {
  270.     char chToken = GetPrintToken();
  271.     CString typeStr = stringToken;
  272.     char chFirst = typeStr[0];
  273.  
  274.     CheckSyntax( 
  275.         isalnum(chFirst) || 
  276.         chFirst == '_'   || 
  277.         chFirst == '\''  || 
  278.         chFirst == '"',
  279.         "a type description or constant starting with an\n"
  280.         "alphanumeric or an underbar, or a string or char constant");
  281.  
  282.     while ((chToken = GetPrintToken()) != ',' && chToken != '>')
  283.     {
  284.         if (chToken != '*' && chToken != '&')
  285.             typeStr += ' ';
  286.  
  287.         typeStr += stringToken;
  288.     }
  289.  
  290.     stringToken = typeStr;
  291.     return chToken;
  292. }
  293.  
  294. char CTokenFile::GetNameToken()
  295. {
  296.     GetPrintToken();
  297.  
  298.     CheckSyntax((isalpha(stringToken[0]) || stringToken[0] == '_'),
  299.         "a name token starting in an alpha char or underbar");
  300.  
  301.     int l = stringToken.GetLength();
  302.     for (int i=1; i<l; ++i)
  303.     {
  304.         CheckSyntax(isalnum(stringToken[i]) || stringToken[i] == '_',
  305.             "a name token consisting of alphanumerics or underbars");
  306.     }
  307.  
  308.     CheckSyntax(stringToken != stringTemplate, "a name token");
  309.     CheckSyntax(stringToken != stringClass, "a name token");
  310.  
  311.     return chMoreThanOne;
  312. }
  313.  
  314. /////////////////////////////////////////////////////////////////////////////
  315.  
  316. CMapStringToString map;
  317.  
  318. BOOL isTailMatch(CString string, CString tail)
  319. {
  320.     BOOL retval = string.Right(tail.GetLength()) == tail;
  321.     ASSERT(retval);
  322.     return retval;
  323. }
  324.  
  325. CTokenFile file_h;
  326. CTokenFile file_cpp;
  327. CTokenFile file_ctt;
  328.  
  329. static BOOL fEnableIfElseEating = FALSE;
  330.  
  331. // TranslateTo:
  332. // Copy file_ctt to file_out, translating words as necessary.
  333. // On the way, swallow things outside of blocks and after
  334. // the "template" word inside of '<' '>' brackets.
  335. //
  336. // Also, on the way, any #if or #else statements that are conditional
  337. // on a constant "1" or "0" template parameter will be swallowed as
  338. // appropriate.
  339. //
  340. void TranslateTo(CTokenFile& file_out)
  341. {
  342.     char chToken;
  343.     static BOOL fswallowParams = TRUE;
  344.     BOOL fswallow = FALSE;
  345.     BOOL finIf = FALSE;
  346.     BOOL finElse = FALSE;
  347.     BOOL fswallowIf = FALSE;
  348.     BOOL fswallowElse = FALSE;
  349.     CString stringTokenOut;
  350.  
  351.     while ((chToken = file_ctt.GetToken()) != chMoreThanOne ||
  352.            file_ctt.TokenString() != "IMPLEMENT_TEMPLATE")
  353.     {
  354.         if (chToken != chMoreThanOne)
  355.         {
  356.             if (chToken == '{')
  357.                 fswallowParams = FALSE;
  358.             if (fswallowParams && (chToken == '<'))
  359.             {
  360.                 while (chToken != '>')
  361.                     chToken = file_ctt.GetToken();
  362.             }
  363.             else
  364.             {
  365.                 if (!fswallow)
  366.                     file_out.PutToken();
  367.             }
  368.         }
  369.         else
  370.         {
  371.             if (fEnableIfElseEating && file_ctt.TokenString()[0] == '#')
  372.             {
  373.                 if (file_ctt.TokenString() == "#if")
  374.                 {   
  375.                     file_ctt.GetPrintToken();
  376.                     if (map.Lookup(file_ctt.TokenString(), stringTokenOut))
  377.                     {
  378.                         if (stringTokenOut == "0")
  379.                         {
  380.                             fswallowIf = TRUE;
  381.                             fswallowElse = FALSE;
  382.                         }
  383.                         else
  384.                         {
  385.                             if (stringTokenOut == "1");
  386.                             {
  387.                                 fswallowElse = TRUE;
  388.                                 fswallowIf = FALSE;
  389.                             }
  390.                         }
  391.  
  392.                         if (fswallowIf || fswallowElse)
  393.                         {
  394.                             file_ctt.TokenString() = "";
  395.                             finIf = TRUE;
  396.                         }
  397.  
  398.                         fswallow = fswallowIf;
  399.                     }
  400.                     else
  401.                     {
  402.                         file_out.PutToken("#if ");
  403.                     }
  404.                 }
  405.                 else if (file_ctt.TokenString() == "#else")
  406.                 {
  407.                     if (finIf)
  408.                     {
  409.                         file_ctt.TokenString() = "";
  410.                         finIf = FALSE;
  411.                         finElse = TRUE;
  412.                     }
  413.  
  414.                     fswallow = fswallowElse;
  415.                 }
  416.                 else if (file_ctt.TokenString() == "#endif")
  417.                 {
  418.                     if (finIf || finElse)
  419.                     {
  420.                         file_ctt.TokenString() = "";
  421.  
  422.                         // eat any evil
  423.                         //junk after the #endif
  424.                         while ((chToken = file_ctt.GetToken()) != '\n' &&
  425.                             file_ctt.TokenString()[0] != '/')
  426.                             /* spin */ ;
  427.  
  428.                         if (chToken != '\n')
  429.                             file_ctt.GetToken();
  430.                     }
  431.                     finIf = FALSE;
  432.                     finElse = FALSE;
  433.                     fswallowIf = FALSE;
  434.                     fswallowElse = FALSE;
  435.                     fswallow = FALSE;
  436.                 }
  437.             }
  438.  
  439.             if (file_ctt.TokenString() == stringTemplate)
  440.                 fswallowParams = TRUE;
  441.  
  442.             if (map.Lookup(file_ctt.TokenString(), stringTokenOut))
  443.             {
  444.                 if (!fswallow)
  445.                     file_out.PutToken(stringTokenOut);
  446.             }
  447.             else
  448.             {
  449.                 if (!fswallow)
  450.                     file_out.PutToken();
  451.             }
  452.         }
  453.     }
  454. }
  455.  
  456. // main:
  457. // Gets the arguments, checks them, then processes the files.
  458. //
  459. main(int argc, char* argv[])
  460. {
  461.     CString stringTypes;
  462.     {
  463.         for (int i=1; i<(argc-3); ++i)
  464.         {
  465.             stringTypes += CString(argv[i]) + ' ';
  466.         }
  467.     }
  468.  
  469.     if (argc < 4)
  470.         UsageErr(NULL, NULL);
  471.  
  472.     // Copy the template, header and source file name arguments.
  473.     //
  474.     string_ctt = argv[argc-3];
  475.     string_h   = argv[argc-2];
  476.     string_cpp = argv[argc-1];
  477.  
  478.     // Check to make sure that the args are in the right order.
  479.     //
  480.     if (!isTailMatch(string_cpp, ".cpp") &&
  481.         !isTailMatch(string_cpp, ".cxx"))
  482.         UsageErr("module file should have a .cpp or .cxx extension");
  483.  
  484.     if (!isTailMatch(string_ctt, ".ctt"))
  485.         UsageErr("template file should have a .ctt extension");
  486.  
  487.     if (!isTailMatch(string_h, ".h") &&
  488.         !isTailMatch(string_h, ".hpp") &&
  489.         !isTailMatch(string_h, ".hxx"))
  490.         UsageErr("header file should have an .h, .hpp or .hxx extension");
  491.  
  492.     // Open the files.
  493.     //
  494.     file_ctt.SafeOpen(string_ctt, CTokenFile::modeRead);
  495.     file_h.SafeOpen(string_h,
  496.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  497.     file_cpp.SafeOpen(string_cpp,
  498.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  499.  
  500.     // Push the command line onto the file buffer, so that we can parse it
  501.     // using our standard tool set.
  502.     //
  503.     file_ctt.InitBuf(stringTypes);
  504.  
  505.     int chToken;
  506.     CString astringTypeParam[istringTypeParamMax];
  507.     int istringTypeParam = 0;
  508.  
  509.     file_ctt.GetNameToken();
  510.     stringTemplateName = file_ctt.TokenString();
  511.  
  512.     chToken = file_ctt.GetPrintToken();
  513.     CheckSyntax(chToken == '<', "'<'");
  514.  
  515.     do
  516.     {
  517.         chToken = file_ctt.GetTypeToken();
  518.         if ((file_ctt.TokenString() == "0") ||
  519.             (file_ctt.TokenString() == "1"))
  520.         {
  521.             fEnableIfElseEating = TRUE;
  522.         }
  523.         astringTypeParam[istringTypeParam++] = file_ctt.TokenString();
  524.         CheckSyntax(istringTypeParam < istringTypeParamMax,
  525.             "fewer parameterized types (program limit)");
  526.     } while (chToken == ',');
  527.  
  528.     CheckSyntax(chToken == '>', "'>'");
  529.  
  530.     file_ctt.GetNameToken();
  531.     stringTypedefName = file_ctt.TokenString();
  532.  
  533.     map.SetAt(stringTemplate, " ");
  534.     map.SetAt(stringTemplateName, stringTypedefName);
  535.  
  536.     // Done processing the command line part, now eat any initial comments
  537.     // appearing before the //$DECLARE_TEMPLATE flag.
  538.     //
  539.     while ((chToken = file_ctt.GetPrintToken()) != chMoreThanOne ||
  540.            file_ctt.TokenString() != "DECLARE_TEMPLATE")
  541.     {
  542.         /* spin */ ;
  543.     }
  544.  
  545.     while ((file_ctt.GetToken() != chMoreThanOne) ||
  546.            (file_ctt.TokenString() != stringTemplate))
  547.     {   
  548.         file_h.PutToken();
  549.     }
  550.  
  551.     // Token must now be "template".
  552.     
  553.     // Eat opening '<'.
  554.     //
  555.     chToken = file_ctt.GetPrintToken();
  556.  
  557.     // Now get a list of type parameters.
  558.     //
  559.     int istringTypeParamMaxPrev = istringTypeParam;
  560.     istringTypeParam = 0;
  561.     while (chToken !='>')
  562.     {
  563.         CString stringParamName;
  564.  
  565.         // The parameter name is the last thing before a ',' or '>'.
  566.         //
  567.         while ((chToken = file_ctt.GetPrintToken()) != ',' && chToken != '>')
  568.         {
  569.             stringParamName = file_ctt.TokenString();
  570.         }
  571.  
  572.         map.SetAt(stringParamName, astringTypeParam[istringTypeParam++]);
  573.     }
  574.  
  575.     CheckSyntax(istringTypeParam == istringTypeParamMaxPrev,
  576.         "same number of template parameters");
  577.  
  578.     // Copy template to header file, translating words as necessary,
  579.     // terminating when the //$IMPLEMENT_TEMPLATE flag is hit.
  580.     //
  581.     TranslateTo(file_h);
  582.  
  583.     // Copy template to source file, translating words as necessary.
  584.     //
  585.     TranslateTo(file_cpp);
  586.  
  587.     return 0;
  588. }
  589.