|
|
1.1 ! root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.