File:  [WindowsNT SDKs] / mstools / mfc / samples / templdef / templdef.cpp
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:21:00 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntsdk-oct-1992, ntsdk-jun-1992, ntsdk-jul-1993, HEAD
Microsoft Windows NT Build 297 06-28-1992

// templdef.cpp : A simple C++ template expansion utility.
//
//   This utility takes a template file which uses a syntax
//   subset similar to the proposed future C++ template
//   (parameterized type) syntax, and expands it into the
//   equivalent current C++ syntax.
//
//   Note that this is intended as an example program, and
//   handles only a specific syntax in the template files.
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and Microsoft
// QuickHelp documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include <ctype.h>
#include <afxcoll.h>

#define istringTypeParamMax 10

CString string_h;
CString string_cpp;
CString string_ctt;

CString stringTemplate = "template";
CString stringClass = "class";
CString stringTemplateName;
CString stringTypedefName;

const char chMoreThanOne = '\0';
const char chNot = '\0';

/////////////////////////////////////////////////////////////////////////////

// UsageErr:
// A utility function.  Write an error message, followed by a usage summary.
//
void UsageErr(const char* szErrorMessage = NULL,
			  const char* szErrorParam = NULL)
{
	if (szErrorMessage != NULL)
	{
		fprintf(stderr, "templdef : error: ");
		fprintf(stderr, szErrorMessage, szErrorParam);
		fprintf(stderr, ".\n\n");
	}

	fprintf(stderr, "templdef : usage:\n\n"
		"  Suppose a class CMap exists in the template file map.ctt, which\n"
		"  has two parameterized types Foo and Bar. To create a new class\n"
		"  CMapFooToBar, declared in footobar.h and defined in footobar.cpp,\n"
		"  use the following command:\n"
		"\n"
		"  templdef \"CMap<Foo, Bar> CMapFooToBar\" map.ctt footobar.h footobar.cpp\n"
		"\n"
		"  This command is similar to the proposed future C++ syntax for templates:\n"
		"\n"
		"  typedef CMap<Foo, Bar> CMapFooToBar;\n");

	exit(1);
}

void CheckSyntax(BOOL fGoodSyntax, const char* sz)
{
	if (!fGoodSyntax)
		UsageErr("expecting %s", sz);
}

/////////////////////////////////////////////////////////////////////////////
// CTokenFile
// For the purpose of reading source code, this sort of CStdioFile derivative
// proves quite handy.  This could be made really powerful, but this is a
// start.

class CTokenFile : public CStdioFile
{
private:
	enum { ichMax = 1024 };
	char ach[ichMax];
	int ich;
	static CString stringToken;
	BOOL fComment;
	void GetBuf();
	char GetChar() { if (ach[ich] == chNot) GetBuf(); return ach[ich++]; }
	char PeekChar() { return ach[ich]; }
	void UnGetChar() { --ich; } // note: really a single char pushback
public:
	void InitBuf(const char* sz);
	static CString& TokenString() { return stringToken; }
	CTokenFile();
	char GetToken();
	char GetPrintToken();
	char GetTypeToken();
	char GetNameToken();
	void PutToken(char ch) { fputc(ch, m_pStream); }
	void PutToken(const CString& string) { WriteString(string); }
	void PutToken() { WriteString(stringToken); }
	BOOL AtFileEnd() { return ach[0] == chNot; }
	BOOL AtBufEnd() { return ach[ich] == chNot; }
	void SafeOpen(const CString& string, UINT nStyleFlags);
};
CString CTokenFile::stringToken;

CTokenFile::CTokenFile()
{
	ach[0] = '\n';
	ach[1] = chNot;
	ich = 1;
	fComment = FALSE;
};

void CTokenFile::GetBuf()
{
	if (ReadString(ach, ichMax) == NULL)
		ach[0] = chNot;

	ich = 0;
}

void CTokenFile::InitBuf(const char* sz)
{
	strncpy(ach, sz, ichMax);
	ich = 0;
}

void CTokenFile::SafeOpen(const CString& string, UINT nStyleFlags)
{
	BOOL fSuccess = Open(string, nStyleFlags, 0);

	if (!fSuccess)
		UsageErr("can't open file \"%s\"", string);
}

char CTokenFile::GetToken()
{
	if (AtBufEnd())
		GetBuf();
	
	if (AtFileEnd())
		exit(0);

	fComment = FALSE;

	char ch = GetChar();
	char* pch = stringToken.GetBuffer(1024);
	char* pchInit = pch;
	*pch++ = ch;
	*pch = '\0';

	// assuming this doesn't "really" release the buffer!
	stringToken.ReleaseBuffer(1);

	if (!isalnum(ch) &&
		ch != '/'    &&
		ch != '_'    &&
		ch != '\''   &&
		ch != '"'    &&
		ch != '#')
	{
		return ch;
	}

	if ((ch == '\'') || (ch == '"'))
	{
		char ch2;
		while ((ch2 = GetChar()) != ch)
		{
			*pch++ = ch2;
		}
		*pch++ = ch2;
		stringToken.ReleaseBuffer(pch - pchInit);
		return chMoreThanOne;
	}

	if (ch == '/')
	{   
		char ch2 = GetChar();
		if (AtFileEnd()) exit(0);
		if (ch2 == '/')
		{
			char ch3 = GetChar();
			if (ch3 != '$')
			{
				UnGetChar();
				*pch++ = '/';
				fComment = TRUE;
				while ((ch3 = GetChar()) != '\n')
					*pch++ = ch3;
			}
			else
			{
				stringToken.ReleaseBuffer(0);
			}
			*pch++ = '\n';
			stringToken.ReleaseBuffer(pch - pchInit);
			return chMoreThanOne;
		}
		else if (ch2 == '*')
		{
			char ch3 = chNot;
			char ch4 = chNot;
			*pch++ = '*';
			fComment = TRUE;
			while (ch4 != '/')
			{
				while ((ch3 = GetChar()) != '*')
					*pch++ = ch3;
				*pch++ = '*';   

				ch4 = GetChar();
				if (ch4 != '/')
					UnGetChar();
			}
			*pch++ = '/';
			stringToken.ReleaseBuffer(pch - pchInit);
			return chMoreThanOne;
		}
		else
		{
			UnGetChar();
			return ch;
		}
	}

	if (isdigit(ch))
	{
		char ch2;
		ch2 = GetChar();
		if (!isalpha(ch2))
		{
			UnGetChar();
			return ch;
		}

		*pch++ = ch2;
		stringToken.ReleaseBuffer(pch - pchInit);
		return chMoreThanOne;
	}

	while ((!AtFileEnd()) &&
		(ch = GetChar(), (isalnum(ch) || (ch == '_'))))
	{
		*pch++ = ch;
	}

	if (!AtFileEnd())
		UnGetChar();

	stringToken.ReleaseBuffer(pch - pchInit);
	return chMoreThanOne;
}

char CTokenFile::GetPrintToken()
{
	char chToken;
	while (((chToken = GetToken()) != chMoreThanOne) &&
		   (isspace(chToken)) || fComment)
		/* try again */ ;

	return chToken;
}

char CTokenFile::GetTypeToken()
{
	char chToken = GetPrintToken();
	CString typeStr = stringToken;
	char chFirst = typeStr[0];

	CheckSyntax( 
		isalnum(chFirst) || 
		chFirst == '_'   || 
		chFirst == '\''  || 
		chFirst == '"',
		"a type description or constant starting with an\n"
		"alphanumeric or an underbar, or a string or char constant");

	while ((chToken = GetPrintToken()) != ',' && chToken != '>')
	{
		if (chToken != '*' && chToken != '&')
			typeStr += ' ';

		typeStr += stringToken;
	}

	stringToken = typeStr;
	return chToken;
}

char CTokenFile::GetNameToken()
{
	GetPrintToken();

	CheckSyntax((isalpha(stringToken[0]) || stringToken[0] == '_'),
		"a name token starting in an alpha char or underbar");

	int l = stringToken.GetLength();
	for (int i=1; i<l; ++i)
	{
		CheckSyntax(isalnum(stringToken[i]) || stringToken[i] == '_',
			"a name token consisting of alphanumerics or underbars");
	}

	CheckSyntax(stringToken != stringTemplate, "a name token");
	CheckSyntax(stringToken != stringClass, "a name token");

	return chMoreThanOne;
}

/////////////////////////////////////////////////////////////////////////////

CMapStringToString map;

BOOL isTailMatch(CString string, CString tail)
{
	BOOL retval = string.Right(tail.GetLength()) == tail;
	ASSERT(retval);
	return retval;
}

CTokenFile file_h;
CTokenFile file_cpp;
CTokenFile file_ctt;

static BOOL fEnableIfElseEating = FALSE;

// TranslateTo:
// Copy file_ctt to file_out, translating words as necessary.
// On the way, swallow things outside of blocks and after
// the "template" word inside of '<' '>' brackets.
//
// Also, on the way, any #if or #else statements that are conditional
// on a constant "1" or "0" template parameter will be swallowed as
// appropriate.
//
void TranslateTo(CTokenFile& file_out)
{
	char chToken;
	static BOOL fswallowParams = TRUE;
	BOOL fswallow = FALSE;
	BOOL finIf = FALSE;
	BOOL finElse = FALSE;
	BOOL fswallowIf = FALSE;
	BOOL fswallowElse = FALSE;
	CString stringTokenOut;

	while ((chToken = file_ctt.GetToken()) != chMoreThanOne ||
		   file_ctt.TokenString() != "IMPLEMENT_TEMPLATE")
	{
		if (chToken != chMoreThanOne)
		{
			if (chToken == '{')
				fswallowParams = FALSE;
			if (fswallowParams && (chToken == '<'))
			{
				while (chToken != '>')
					chToken = file_ctt.GetToken();
			}
			else
			{
				if (!fswallow)
					file_out.PutToken();
			}
		}
		else
		{
			if (fEnableIfElseEating && file_ctt.TokenString()[0] == '#')
			{
				if (file_ctt.TokenString() == "#if")
				{   
					file_ctt.GetPrintToken();
					if (map.Lookup(file_ctt.TokenString(), stringTokenOut))
					{
						if (stringTokenOut == "0")
						{
							fswallowIf = TRUE;
							fswallowElse = FALSE;
						}
						else
						{
							if (stringTokenOut == "1");
							{
								fswallowElse = TRUE;
								fswallowIf = FALSE;
							}
						}

						if (fswallowIf || fswallowElse)
						{
							file_ctt.TokenString() = "";
							finIf = TRUE;
						}

						fswallow = fswallowIf;
					}
					else
					{
						file_out.PutToken("#if ");
					}
				}
				else if (file_ctt.TokenString() == "#else")
				{
					if (finIf)
					{
						file_ctt.TokenString() = "";
						finIf = FALSE;
						finElse = TRUE;
					}

					fswallow = fswallowElse;
				}
				else if (file_ctt.TokenString() == "#endif")
				{
					if (finIf || finElse)
					{
						file_ctt.TokenString() = "";

						// eat any evil
						//junk after the #endif
						while ((chToken = file_ctt.GetToken()) != '\n' &&
							file_ctt.TokenString()[0] != '/')
							/* spin */ ;

						if (chToken != '\n')
							file_ctt.GetToken();
					}
					finIf = FALSE;
					finElse = FALSE;
					fswallowIf = FALSE;
					fswallowElse = FALSE;
					fswallow = FALSE;
				}
			}

			if (file_ctt.TokenString() == stringTemplate)
				fswallowParams = TRUE;

			if (map.Lookup(file_ctt.TokenString(), stringTokenOut))
			{
				if (!fswallow)
					file_out.PutToken(stringTokenOut);
			}
			else
			{
				if (!fswallow)
					file_out.PutToken();
			}
		}
	}
}

// main:
// Gets the arguments, checks them, then processes the files.
//
main(int argc, char* argv[])
{
	CString stringTypes;
	{
		for (int i=1; i<(argc-3); ++i)
		{
			stringTypes += CString(argv[i]) + ' ';
		}
	}

	if (argc < 4)
		UsageErr(NULL, NULL);

	// Copy the template, header and source file name arguments.
	//
	string_ctt = argv[argc-3];
	string_h   = argv[argc-2];
	string_cpp = argv[argc-1];

	// Check to make sure that the args are in the right order.
	//
	if (!isTailMatch(string_cpp, ".cpp") &&
		!isTailMatch(string_cpp, ".cxx"))
		UsageErr("module file should have a .cpp or .cxx extension");

	if (!isTailMatch(string_ctt, ".ctt"))
		UsageErr("template file should have a .ctt extension");

	if (!isTailMatch(string_h, ".h") &&
		!isTailMatch(string_h, ".hpp") &&
		!isTailMatch(string_h, ".hxx"))
		UsageErr("header file should have an .h, .hpp or .hxx extension");

	// Open the files.
	//
	file_ctt.SafeOpen(string_ctt, CTokenFile::modeRead);
	file_h.SafeOpen(string_h,
		CTokenFile::modeWrite | CTokenFile::modeCreate);
	file_cpp.SafeOpen(string_cpp,
		CTokenFile::modeWrite | CTokenFile::modeCreate);

	// Push the command line onto the file buffer, so that we can parse it
	// using our standard tool set.
	//
	file_ctt.InitBuf(stringTypes);

	int chToken;
	CString astringTypeParam[istringTypeParamMax];
	int istringTypeParam = 0;

	file_ctt.GetNameToken();
	stringTemplateName = file_ctt.TokenString();

	chToken = file_ctt.GetPrintToken();
	CheckSyntax(chToken == '<', "'<'");

	do
	{
		chToken = file_ctt.GetTypeToken();
		if ((file_ctt.TokenString() == "0") ||
			(file_ctt.TokenString() == "1"))
		{
			fEnableIfElseEating = TRUE;
		}
		astringTypeParam[istringTypeParam++] = file_ctt.TokenString();
		CheckSyntax(istringTypeParam < istringTypeParamMax,
			"fewer parameterized types (program limit)");
	} while (chToken == ',');

	CheckSyntax(chToken == '>', "'>'");

	file_ctt.GetNameToken();
	stringTypedefName = file_ctt.TokenString();

	map.SetAt(stringTemplate, " ");
	map.SetAt(stringTemplateName, stringTypedefName);

	// Done processing the command line part, now eat any initial comments
	// appearing before the //$DECLARE_TEMPLATE flag.
	//
	while ((chToken = file_ctt.GetPrintToken()) != chMoreThanOne ||
		   file_ctt.TokenString() != "DECLARE_TEMPLATE")
	{
		/* spin */ ;
	}

	while ((file_ctt.GetToken() != chMoreThanOne) ||
		   (file_ctt.TokenString() != stringTemplate))
	{   
		file_h.PutToken();
	}

	// Token must now be "template".
	
	// Eat opening '<'.
	//
	chToken = file_ctt.GetPrintToken();

	// Now get a list of type parameters.
	//
	int istringTypeParamMaxPrev = istringTypeParam;
	istringTypeParam = 0;
	while (chToken !='>')
	{
		CString stringParamName;

		// The parameter name is the last thing before a ',' or '>'.
		//
		while ((chToken = file_ctt.GetPrintToken()) != ',' && chToken != '>')
		{
			stringParamName = file_ctt.TokenString();
		}

		map.SetAt(stringParamName, astringTypeParam[istringTypeParam++]);
	}

	CheckSyntax(istringTypeParam == istringTypeParamMaxPrev,
		"same number of template parameters");

	// Copy template to header file, translating words as necessary,
	// terminating when the //$IMPLEMENT_TEMPLATE flag is hit.
	//
	TranslateTo(file_h);

	// Copy template to source file, translating words as necessary.
	//
	TranslateTo(file_cpp);

	return 0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.