|
|
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.