consoleDoc.cpp

Engine/source/console/consoleDoc.cpp

For specifics on using the consoleDoc functionality, see console_autodoc.

More...

Public Variables

const char *

Helper table to convert type ids to human readable names.

Public Functions

ConsoleFunctionGroupBegin(ConsoleDoc , "Console self-documentation functions. These output psuedo C++ suitable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> feeeding through Doxygen or another auto documentation tool." )
DefineEngineFunction(dumpConsoleClasses , void , (bool dumpScript, bool dumpEngine) , (true, true) , "@brief Dumps all declared console classes <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param dumpScript Optional parameter specifying whether or not classes defined in script should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@param dumpEngine Optional parameter specifying whether or not classes defined in the engine should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@ingroup Logging" )
DefineEngineFunction(dumpConsoleFunctions , void , (bool dumpScript, bool dumpEngine) , (true, true) , "@brief Dumps all declared console functions <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param dumpScript Optional parameter specifying whether or not functions defined in script should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@param dumpEngine Optional parameter specitying whether or not functions defined in the engine should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@ingroup Logging" )
printClassHeader(const char * usage, const char * className, const char * superClassName, const bool stub)
printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs, S32 aElementCount)
printClassMethod(const bool isVirtual, const char * retType, const char * methodName, const char * args, const char * usage)
printGroupStart(const char * aName, const char * aDocs)

Detailed Description

For specifics on using the consoleDoc functionality, see console_autodoc.

Public Variables

const char * typeNames []

Helper table to convert type ids to human readable names.

Public Functions

ConsoleFunctionGroupBegin(ConsoleDoc , "Console self-documentation functions. These output psuedo C++ suitable <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> feeeding through Doxygen or another auto documentation tool." )

ConsoleFunctionGroupEnd(ConsoleDoc )

DefineEngineFunction(dumpConsoleClasses , void , (bool dumpScript, bool dumpEngine) , (true, true) , "@brief Dumps all declared console classes <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param dumpScript Optional parameter specifying whether or not classes defined in script should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@param dumpEngine Optional parameter specifying whether or not classes defined in the engine should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@ingroup Logging" )

DefineEngineFunction(dumpConsoleFunctions , void , (bool dumpScript, bool dumpEngine) , (true, true) , "@brief Dumps all declared console functions <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" "@param dumpScript Optional parameter specifying whether or not functions defined in script should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@param dumpEngine Optional parameter specitying whether or not functions defined in the engine should be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dumped.\n</a>" "@ingroup Logging" )

printClassFooter()

printClassHeader(const char * usage, const char * className, const char * superClassName, const bool stub)

printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs, S32 aElementCount)

printClassMethod(const bool isVirtual, const char * retType, const char * methodName, const char * args, const char * usage)

printGroupEnd()

printGroupStart(const char * aName, const char * aDocs)

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "console/console.h"
 26
 27#include "console/ast.h"
 28#include "core/tAlgorithm.h"
 29
 30#include "core/strings/findMatch.h"
 31#include "console/consoleInternal.h"
 32#include "console/consoleObject.h"
 33#include "core/stream/fileStream.h"
 34#include "console/compiler.h"
 35#include "core/frameAllocator.h"
 36#include "console/engineAPI.h"
 37
 38//--- Information pertaining to this page... ------------------
 39/// @file
 40///
 41/// For specifics on using the consoleDoc functionality, see @ref console_autodoc
 42
 43ConsoleFunctionGroupBegin(ConsoleDoc, "Console self-documentation functions. These output psuedo C++ suitable for feeeding through Doxygen or another auto documentation tool.");
 44
 45DefineEngineFunction( dumpConsoleClasses, void, (bool dumpScript, bool dumpEngine), ( true, true ),
 46            "@brief Dumps all declared console classes to the console.\n\n"
 47            "@param dumpScript Optional parameter specifying whether or not classes defined in script should be dumped.\n"
 48            "@param dumpEngine Optional parameter specifying whether or not classes defined in the engine should be dumped.\n"
 49         "@ingroup Logging")
 50{
 51   Namespace::dumpClasses( dumpScript, dumpEngine );
 52}
 53
 54DefineEngineFunction(dumpConsoleFunctions, void, ( bool dumpScript, bool dumpEngine ), ( true, true ),
 55            "@brief Dumps all declared console functions to the console.\n"
 56            "@param dumpScript Optional parameter specifying whether or not functions defined in script should be dumped.\n"
 57            "@param dumpEngine Optional parameter specitying whether or not functions defined in the engine should be dumped.\n"
 58         "@ingroup Logging")
 59{
 60   Namespace::dumpFunctions( dumpScript, dumpEngine );
 61}
 62
 63ConsoleFunctionGroupEnd(ConsoleDoc);
 64
 65/// Helper table to convert type ids to human readable names.
 66const char *typeNames[] = 
 67{
 68      "Script",
 69      "string",
 70      "int",
 71      "float",
 72      "void",
 73      "bool",
 74      "",
 75      "",
 76      "unknown_overload"
 77};
 78
 79void printClassHeader(const char* usage, const char * className, const char * superClassName, const bool stub)
 80{
 81   if(stub) 
 82   {
 83      Con::printf("/// Stub class");
 84      Con::printf("/// ");
 85      Con::printf("/// @note This is a stub class to ensure a proper class hierarchy. No ");
 86      Con::printf("///       information was available for this class.");
 87   }
 88
 89   if((usage != NULL) && strlen(usage))
 90   {
 91      // Copy Usage Document
 92      S32 usageLen = dStrlen( usage ) + 1;
 93      FrameTemp<char> usageStr( usageLen );
 94      dStrcpy( usageStr, usage, usageLen );
 95
 96      // Print Header
 97      Con::printf( "/*!" );
 98
 99      // Print line by line, skipping the @field lines.
100      //
101      // fetch first line end
102      char *newLine = dStrchr( usageStr, '\n' );
103      char *usagePtr = usageStr;
104      do 
105      {
106         // Copy of one line
107         static char lineStr[2048] = {0};
108         // Keyword will hold the last keyword (word following '@' or '\') encountered.
109         static char keyword[8] = {0};
110
111         S32 lineLen = 0;
112
113         // If not the last line, increment pointer
114         if( newLine != NULL )
115         {
116            *newLine = '\0';
117            newLine ++;
118         }
119         
120         // Copy line and update usagePtr
121         dStrcpy( lineStr, usagePtr, 2048 );
122         usagePtr = (newLine != NULL ) ? newLine : usagePtr;
123         lineLen = dStrlen( lineStr );
124
125         // Get the keyword. This is the first word after an '@' or '\'.
126         const char* tempkw = dStrchr( lineStr, '@' );
127         if( !tempkw )
128            tempkw = dStrchr( lineStr, '\\' );
129
130         // If we found a new keyword, set it, otherwise, keep using the
131         // most recently found.
132         if( tempkw )
133         {
134            dStrncpy( keyword, tempkw + 1, 5 );
135            keyword[5] = '\0';
136         }
137
138         // Print all fields that aren't associated with the 'field' keyword.
139         if( String::compare( keyword, "field" ) )
140            Con::printf( "%s", lineStr );    // print lineStr as an unformatted string (otherwise '%' characters in the string could cause problems)
141
142
143         // Fetch next line ending
144         newLine = dStrchr( usagePtr, '\n' );
145      } while( newLine != NULL );
146
147      // DocBlock Footer
148      Con::printf( " */" );
149
150   }
151
152   // Print out appropriate class header
153   if(superClassName)
154      Con::printf("class  %s : public %s {", className, superClassName ? superClassName : "");
155   else if(!className)
156      Con::printf("namespace Global {");
157   else
158      Con::printf("class  %s {", className);
159
160   if(className)
161      Con::printf("  public:");
162
163}
164
165void printClassMethod(const bool isVirtual, const char *retType, const char *methodName, const char* args, const char*usage)
166{
167   if(usage && usage[0] != ';' && usage[0] != 0)
168      Con::printf("   /*! %s */", usage);
169   Con::printf("   %s%s %s(%s) {}", isVirtual ? "virtual " : "", retType, methodName, args);
170}
171
172void printGroupStart(const char * aName, const char * aDocs)
173{
174   Con::printf("");
175   Con::printf("   /*! @name %s", aName);
176
177   if(aDocs)
178   {
179      Con::printf("   ");
180      Con::printf("   %s", aDocs);
181   }
182
183   Con::printf("   @{ */");
184
185   // Add a blank comment in order to make sure groups are parsed properly.
186   Con::printf("   /*! */");
187}
188
189void printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs, S32 aElementCount)
190{
191   Con::printf("   /*!");
192
193   if(aDocs)
194   {
195      Con::printf("   %s", aDocs);
196      Con::printf("   ");
197   }
198
199   if(isDeprec)
200      Con::printf("   @deprecated This member is deprecated, which means that its value is always undefined.");
201
202   Con::printf("    */");
203
204   if (aElementCount == 1)
205   {
206      Con::printf("   %s %s;", isDeprec ? "deprecated" : aType, aName);
207   }
208   else
209   {
210      Con::printf("   %s %s[%i];", isDeprec ? "deprecated" : aType, aName, aElementCount);
211   }
212}
213
214void printGroupEnd()
215{
216   Con::printf("   /// @}");
217   Con::printf("");
218}
219
220void printClassFooter()
221{
222   Con::printf("};");
223   Con::printf("");
224}
225
226void Namespace::printNamespaceEntries(Namespace * g, bool dumpScript, bool dumpEngine )
227{
228   static bool inGroup = false;
229
230   // Go through all the entries.
231   // Iterate through the methods of the namespace...
232   for(Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext)
233   {
234      S32 eType = ewalk->mType;
235      const char * funcName = ewalk->mFunctionName;
236
237      if( ( eType == Entry::ConsoleFunctionType ) && !dumpScript )
238         continue;
239
240      if( ( eType != Entry::ConsoleFunctionType ) && !dumpEngine )
241         continue;
242
243      // If it's a function
244      if( eType >= Entry::ConsoleFunctionType )
245      {
246         if (ewalk->mHeader != NULL)
247         {
248            // The function was defined with types, so we can print out the actual return type
249            printClassMethod(true, ewalk->mHeader->mReturnString, funcName, ewalk->getArgumentsString().c_str(),
250               ewalk->getDocString().c_str());
251         }
252         else
253         {
254            printClassMethod(true, typeNames[eType], funcName, (ewalk->getArgumentsString() + "...").c_str(),
255               ewalk->getDocString().c_str());
256         }
257      }
258      else if(ewalk->mType == Entry::GroupMarker)
259      {
260         if(!inGroup)
261            printGroupStart(ewalk->cb.mGroupName, ewalk->mUsage);
262         else 
263            printGroupEnd();
264
265         inGroup = !inGroup;
266      }
267      else if(ewalk->mType == Entry::ScriptCallbackType)
268      {
269         // It's a script callback - emit some sort of appropriate info.
270         Con::printf("      /*! %s */", ewalk->getDocString().c_str());
271         Con::printf("      %s;", ewalk->getPrototypeString().c_str());
272         Con::printf("");
273      }
274      else if(ewalk->mFunctionOffset)                 // If it's a builtin function...
275      {
276         String args = ewalk->mCode->getFunctionArgs(ewalk->mFunctionOffset);
277         printClassMethod(false, typeNames[ewalk->mType], ewalk->mFunctionName, args, "");
278      }
279      else
280      {
281         Con::printf("   // got an unknown thing?? %d", ewalk->mType );
282      }
283   }
284}
285
286void Namespace::dumpClasses( bool dumpScript, bool dumpEngine )
287{
288   VectorPtr<Namespace*> vec;
289   trashCache();
290   vec.reserve( 1024 );
291
292   // We use mHashSequence to mark if we have traversed...
293   // so mark all as zero to start.
294   for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
295      walk->mHashSequence = 0;
296
297   for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
298   {
299      VectorPtr<Namespace*> stack;
300      stack.reserve( 1024 );
301
302      // Get all the parents of this namespace... (and mark them as we go)
303      Namespace *parentWalk = walk;
304      while(parentWalk)
305      {
306         if(parentWalk->mHashSequence != 0)
307            break;
308         if(parentWalk->mPackage == 0)
309         {
310            parentWalk->mHashSequence = 1;   // Mark as traversed.
311            stack.push_back(parentWalk);
312         }
313         parentWalk = parentWalk->mParent;
314      }
315
316      // Load stack into our results vector.
317      while(stack.size())
318      {
319         vec.push_back(stack[stack.size() - 1]);
320         stack.pop_back();
321      }
322   }
323
324   // Go through previously discovered classes
325   U32 i;
326   for(i = 0; i < vec.size(); i++)
327   {
328      const char *className = vec[i]->mName;
329      const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL;
330
331      // Skip the global namespace, that gets dealt with in dumpFunctions
332      if(!className) continue;
333
334      // If we're just dumping script functions, then we don't want to dump
335      // a class that only contains script functions. So, we iterate over all
336      // the functions.
337      if( !dumpScript )
338      {
339         bool found = false;
340         for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
341         {
342            if( ewalk->mType != Entry::ConsoleFunctionType )
343            {
344               found = true;
345               break;
346            }
347         }
348
349         // If we don't have engine functions and the namespace name
350         // doesn't match the class name... then its a script class.
351         if ( !found && !vec[i]->isClass() )
352            continue;
353      }
354
355      // And we do the same for engine functions.
356      if( !dumpEngine )
357      {
358         bool found = false;
359         for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
360         {
361            if( ewalk->mType == Entry::ConsoleFunctionType )
362            {
363               found = true;
364               break;
365            }
366         }
367         if( !found )
368            continue;
369      }
370
371      // If we hit a class with no members and no classRep, do clever filtering.
372      if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL)
373      {
374         // Print out a short stub so we get a proper class hierarchy.
375         if(superClassName) { // Filter hack; we don't want non-inheriting classes...
376            printClassHeader( NULL, className,superClassName, true);
377            printClassFooter();
378         }
379         continue;
380      }
381
382      // Print the header for the class..
383      printClassHeader(vec[i]->mUsage, className, superClassName, false);
384
385      // Deal with entries.
386      printNamespaceEntries(vec[i], dumpScript, dumpEngine);
387
388      // Deal with the classRep (to get members)...
389      AbstractClassRep *rep = vec[i]->mClassRep;
390      AbstractClassRep::FieldList emptyList;
391      AbstractClassRep::FieldList *parentList = &emptyList;
392      AbstractClassRep::FieldList *fieldList = &emptyList;
393
394      // Since all fields are defined in the engine, if we're not dumping
395      // engine stuff, than we shouldn't dump the fields.
396      if(dumpEngine && rep)
397      {
398         // Get information about the parent's fields...
399         AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL;
400         if(parentRep)
401            parentList = &(parentRep->mFieldList);
402
403         // Get information about our fields
404         fieldList = &(rep->mFieldList);
405
406         // Go through all our fields...
407         for(U32 j = 0; j < fieldList->size(); j++)
408         {
409            switch((*fieldList)[j].type)
410            {
411            case AbstractClassRep::StartArrayFieldType:
412            case AbstractClassRep::EndArrayFieldType:
413               break;
414            case AbstractClassRep::StartGroupFieldType:
415               printGroupStart((*fieldList)[j].pGroupname, (*fieldList)[j].pFieldDocs);
416               break;
417            case AbstractClassRep::EndGroupFieldType:
418               printGroupEnd();
419               break;
420            default:
421            case AbstractClassRep::DeprecatedFieldType:
422               {
423                  // Skip over fields that are already defined in
424                  // our parent class.
425                  if ( parentRep && parentRep->findField( (*fieldList)[j].pFieldname ) )
426                     continue;
427
428                  bool isDeprecated = ((*fieldList)[j].type == AbstractClassRep::DeprecatedFieldType);
429
430                  if(isDeprecated)
431                  {
432                     printClassMember(
433                        true,
434                        "<deprecated>",
435                        (*fieldList)[j].pFieldname,
436                        (*fieldList)[j].pFieldDocs,
437                        (*fieldList)[j].elementCount
438                        );
439                  }
440                  else
441                  {
442                     ConsoleBaseType *cbt = ConsoleBaseType::getType((*fieldList)[j].type);
443
444                     printClassMember(
445                        false,
446                        cbt ? cbt->getTypeClassName() : "<unknown>",
447                        (*fieldList)[j].pFieldname,
448                        (*fieldList)[j].pFieldDocs,
449                        (*fieldList)[j].elementCount
450                        );
451                  }
452               }
453            }
454         }
455      }
456
457      if( dumpScript )
458      {
459         // Print out fields defined in script docs for this namespace.
460         // These fields are specified by the 'field' keyword in the usage
461         // string.
462
463         // The field type and name.
464         char fieldName[256];
465         char fieldDoc[1024];
466
467         // Usage string iterator.
468         const char* field = vec[i]->mUsage;
469
470         while( field )
471         {
472            // Find the first field keyword.
473            const char* tempField = dStrstr( field, "@field" );
474            if( !tempField )
475               tempField = dStrstr( field, "\\field" );
476
477            field = tempField;
478
479            if( !field )
480               break;
481
482            // Move to the field name.
483            field += 7;
484
485            // Copy the field type and name. These should both be followed by a
486            // space so only in this case will we actually store it.
487            S32 spaceCount = 0;
488            S32 index = 0;
489            bool valid = false;
490            while( field && ( *field != '\n' ) )
491            {
492               if( index >= 255 )
493                  break;
494
495               if( *field == ' ' )
496                  spaceCount++;
497
498               if( spaceCount == 2 )
499               {
500                  valid = true;
501                  break;
502               }
503
504               fieldName[index++] = *field;
505               field++;
506            }
507
508            if( !valid )
509               continue;
510
511            fieldName[index] = '\0';
512
513            // Now copy from field to the next keyword.
514            const char* nextKeyword = dStrchr( field, '@' );
515            if( !nextKeyword )
516               nextKeyword = dStrchr( field, '\\' );
517
518            // Grab the length of the doc string.
519            S32 docLen = dStrlen( field );
520            if( nextKeyword )
521               docLen = nextKeyword - field;
522
523            // Make sure it will fit in the buffer.
524            if( docLen > 1023 )
525               docLen = 1023;
526
527            // Copy.
528            dStrncpy( fieldDoc, field, docLen );
529            fieldDoc[docLen] = '\0';
530            field += docLen;
531
532            // Print
533            Con::printf( "   /*!" );
534            Con::printf( "   %s", fieldDoc );
535            Con::printf( "    */" );
536            Con::printf( "   %s;", fieldName );
537         }
538      }
539
540      // Close the class/namespace.
541      printClassFooter();
542   }
543}
544
545void Namespace::dumpFunctions( bool dumpScript, bool dumpEngine )
546{
547   // Get the global namespace.
548   Namespace* g = find(NULL); //->mParent;
549
550   printClassHeader(NULL, NULL,NULL, false);
551
552   while(g) 
553   {
554      printNamespaceEntries(g, dumpScript, dumpEngine );
555      g = g->mParent;
556   }
557
558   printClassFooter();
559}
560