Fawkes API  Fawkes Development Version
tolua_generator.cpp
00001  
00002 /***************************************************************************
00003  *  tolua_generator.cpp - ToLua++ Interface generator
00004  *
00005  *  Created: Tue Mar 11 15:33:26 2006
00006  *  Copyright  2006-2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include "tolua_generator.h"
00024 #include "exceptions.h"
00025 
00026 #include <utils/misc/string_conversions.h>
00027 
00028 #include <algorithm>
00029 #include <iostream>
00030 #include <vector>
00031 #include <time.h>
00032 #include <fstream>
00033 
00034 using namespace std;
00035 
00036 
00037 /** @class ToLuaInterfaceGenerator <interfaces/generator/tolua_generator.h>
00038  * Generator that transforms input from the InterfaceParser into valid
00039  * ToLua++ package file.
00040  * @author Tim Niemueller
00041  */
00042 
00043 /** Constructor.
00044  * @param directory Directory where to create the files
00045  * @param interface_name name of the interface, should end with Interface
00046  * @param config_basename basename of the config without suffix
00047  * @param author author of interface
00048  * @param year year of copyright
00049  * @param creation_date user-supplied creation date of interface
00050  * @param data_comment comment in data block.
00051  * @param hash MD5 hash of the config file that was used to generate the interface
00052  * @param hash_size size in bytes of hash
00053  * @param constants constants
00054  * @param enum_constants constants defined as an enum
00055  * @param data_fields data fields of the interface
00056  * @param pseudo_maps pseudo maps of the interface
00057  * @param messages messages defined in the interface
00058  */
00059 ToLuaInterfaceGenerator::ToLuaInterfaceGenerator(std::string directory, std::string interface_name,
00060                                                  std::string config_basename, std::string author,
00061                                                  std::string year, std::string creation_date,
00062                                                  std::string data_comment,
00063                                                  const unsigned char *hash, size_t hash_size,
00064                                                  const std::vector<InterfaceConstant> &constants,
00065                                                  const std::vector<InterfaceEnumConstant> &enum_constants,
00066                                                  const std::vector<InterfaceField> &data_fields,
00067                                                  const std::vector<InterfacePseudoMap> &pseudo_maps,
00068                                                  const std::vector<InterfaceMessage> &messages
00069                                                  )
00070 {
00071   this->dir    = directory;
00072   if ( dir.find_last_of("/") != (dir.length() - 1) ) {
00073     dir += "/";
00074   }
00075   this->author = author;
00076   this->year   = year;
00077   this->creation_date = creation_date;
00078   this->data_comment  = data_comment;
00079   this->hash = hash;
00080   this->hash_size = hash_size;
00081   this->constants = constants;
00082   this->enum_constants = enum_constants;
00083   this->data_fields = data_fields;
00084   this->pseudo_maps = pseudo_maps;
00085   this->messages = messages;
00086 
00087   filename_tolua = config_basename + ".tolua";
00088   filename_h     = config_basename + ".h";
00089 
00090   if ( interface_name.find("Interface", 0) == string::npos ) {
00091     // append Interface
00092     class_name = interface_name + "Interface";
00093   } else {
00094     class_name = interface_name;
00095   }
00096 }
00097 
00098 
00099 /** Destructor */
00100 ToLuaInterfaceGenerator::~ToLuaInterfaceGenerator()
00101 {
00102 }
00103 
00104 
00105 /** Convert C type to Lua type.
00106  * tolua++ does not deal well with stdint types, therefore we convert them
00107  * to "traditional" types.
00108  * @param c_type C type to convert
00109  * @return constant string of the Lua compatible type
00110  */
00111 const char *
00112 ToLuaInterfaceGenerator::convert_type(std::string c_type)
00113 {
00114   if (c_type == "uint8_t") {
00115     return "unsigned char";
00116   } else if (c_type == "uint16_t") {
00117     return "unsigned short";
00118   } else if (c_type == "uint32_t") {
00119     return "unsigned int";
00120   } else if (c_type == "uint64_t") {
00121 #if __WORDSIZE == 64
00122     return "unsigned long";
00123 #else
00124     return "unsigned long long";
00125 #endif
00126   } else if (c_type == "int8_t") {
00127     return "char";
00128   } else if (c_type == "int16_t") {
00129     return "short";
00130   } else if (c_type == "int32_t") {
00131     return "int";
00132   } else if (c_type == "int64_t") {
00133 #if __WORDSIZE == 64
00134     return "long";
00135 #else
00136     return "long long";
00137 #endif
00138   } else if (c_type == "uint8_t *") {
00139     return "unsigned char *";
00140   } else if (c_type == "uint16_t *") {
00141     return "unsigned short *";
00142   } else if (c_type == "uint32_t *") {
00143     return "unsigned int *";
00144   } else if (c_type == "uint64_t *") {
00145 #if __WORDSIZE == 64
00146     return "unsigned long *";
00147 #else
00148     return "unsigned long long *";
00149 #endif
00150   } else if (c_type == "int8_t *") {
00151     return "char *";
00152   } else if (c_type == "int16_t *") {
00153     return "short *";
00154   } else if (c_type == "int32_t *") {
00155     return "int *";
00156   } else if (c_type == "int64_t *") {
00157 #if __WORDSIZE == 64
00158     return "long *";
00159 #else
00160     return "long long *";
00161 #endif
00162   } else {
00163     return c_type.c_str();
00164   }
00165 }
00166 
00167 
00168 
00169 /** Write header to file.
00170  * @param f file to write to
00171  * @param filename name of file
00172  */
00173 void
00174 ToLuaInterfaceGenerator::write_header(FILE *f, std::string filename)
00175 {
00176   fprintf(f, "\n/***************************************************************************\n");
00177   fprintf(f, " *  %s - Fawkes BlackBoard Interface - %s - tolua++ wrapper\n", filename.c_str(), class_name.c_str());
00178   fprintf(f, " *\n");
00179   if ( creation_date.length() > 0 ) {
00180     fprintf(f, " *  Interface created: %s\n", creation_date.c_str());
00181   }
00182   fprintf(f, " *  Templated created:   Thu Oct 12 10:49:19 2006\n");
00183   fprintf(f, " *  Copyright  %s  %s\n", year.c_str(),
00184           ((author.length() > 0) ? author.c_str() : "AllemaniACs RoboCup Team") );
00185   fprintf(f, " *\n");
00186   fprintf(f, " ****************************************************************************/\n\n");
00187   fprintf(f, "/*\n");
00188   fprintf(f, " *  This program is free software; you can redistribute it and/or modify\n");
00189   fprintf(f, " *  it under the terms of the GNU General Public License as published by\n");
00190   fprintf(f, " *  the Free Software Foundation; either version 2 of the License, or\n");
00191   fprintf(f, " *  (at your option) any later version.\n");
00192   fprintf(f, " *\n");
00193   fprintf(f, " *  This program is distributed in the hope that it will be useful,\n");
00194   fprintf(f, " *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
00195   fprintf(f, " *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
00196   fprintf(f, " *  GNU Library General Public License for more details.\n");
00197   fprintf(f, " *\n");
00198   fprintf(f, " *  You should have received a copy of the GNU General Public License\n");
00199   fprintf(f, " *  along with this program; if not, write to the Free Software Foundation,\n");
00200   fprintf(f, " *  Inc., 51 Franklin Street, Fifth floor, Boston, MA 02111-1307, USA.\n");
00201   fprintf(f, " */\n\n");
00202 }
00203 
00204 
00205 /** Write constants to h file
00206  * @param f file to write to
00207  */
00208 void
00209 ToLuaInterfaceGenerator::write_constants_h(FILE *f)
00210 {
00211   for ( vector<InterfaceConstant>::iterator i = constants.begin(); i != constants.end(); ++i) {
00212     fprintf(f, "  static const %s %s;\n", convert_type(i->getType()),
00213             i->getName().c_str());
00214   }
00215   fprintf(f, "\n");
00216 
00217   for ( vector<InterfaceEnumConstant>::iterator i = enum_constants.begin(); i != enum_constants.end(); ++i) {
00218     fprintf(f, "  typedef enum {\n");
00219     vector<InterfaceEnumConstant::EnumItem> items = (*i).get_items();
00220     vector<InterfaceEnumConstant::EnumItem>::iterator j = items.begin();
00221     while (j != items.end()) {
00222       if (j->has_custom_value) {
00223         fprintf(f, "    %s = %i", j->name.c_str(), j->custom_value);
00224       } else {
00225         fprintf(f, "    %s", j->name.c_str());
00226       }
00227       ++j;
00228       if ( j != items.end() ) {
00229         fprintf(f, ",\n");
00230       } else {
00231         fprintf(f, "\n");
00232       }
00233     }
00234     fprintf(f, "  } %s;\n\n", (*i).get_name().c_str());
00235   }
00236 }
00237 
00238 
00239 /** Write messages to h file.
00240  * @param f file to write to
00241  */
00242 void
00243 ToLuaInterfaceGenerator::write_messages_h(FILE *f)
00244 {
00245   for (vector<InterfaceMessage>::iterator i = messages.begin(); i != messages.end(); ++i) {
00246     fprintf(f, "  class %s : public Message\n"
00247             "  {\n", (*i).getName().c_str());
00248     write_message_ctor_dtor_h(f, "    ", (*i).getName(), (*i).getFields());
00249     write_methods_h(f, "    ", (*i).getFields());
00250 
00251     fprintf(f, "  };\n\n");
00252   }
00253 
00254 }
00255 
00256 
00257 /** Write constructor and destructor to h file.
00258  * @param f file to write to
00259  * @param is indentation space
00260  * @param classname name of class
00261  */
00262 void
00263 ToLuaInterfaceGenerator::write_ctor_dtor_h(FILE *f, std::string /* indent space */ is,
00264                                          std::string classname)
00265 {
00266   fprintf(f,
00267           "%s%s();\n"
00268           "%s~%s();\n\n",
00269           is.c_str(), classname.c_str(),
00270           is.c_str(), classname.c_str());
00271 }
00272 
00273 
00274 /** Write constructor and destructor for message to h file.
00275  * @param f file to write to
00276  * @param is indentation space
00277  * @param classname name of class
00278  * @param fields vector of data fields of message
00279  */
00280 void
00281 ToLuaInterfaceGenerator::write_message_ctor_dtor_h(FILE *f, std::string /* indent space */ is,
00282                                                  std::string classname,
00283                                                  std::vector<InterfaceField> fields)
00284 {
00285   vector<InterfaceField>::iterator i;
00286 
00287   if ( fields.size() > 0 ) {
00288 
00289     fprintf(f, "%s%s(", is.c_str(), classname.c_str());
00290 
00291     i = fields.begin();
00292     while (i != fields.end()) {
00293       fprintf(f, "%s ini_%s",
00294               convert_type(i->getAccessType()), i->getName().c_str());
00295       ++i;
00296       if ( i != fields.end() ) {
00297         fprintf(f, ", ");
00298       }
00299     }
00300 
00301     fprintf(f, ");\n");
00302   }
00303 
00304 
00305   write_ctor_dtor_h(f, is, classname);
00306 }
00307 
00308 /** Write superclass methods.
00309  * @param f file to write to
00310  */
00311 void
00312 ToLuaInterfaceGenerator::write_superclass_h(FILE *f)
00313 {
00314   fprintf(f,
00315           "  bool                    oftype(const char *interface_type) const;\n"
00316           "  const void *            datachunk() const;\n"
00317           "  unsigned int            datasize() const;\n"
00318           "  const char *            type() const;\n"
00319           "  const char *            id() const;\n"
00320           "  const char *            uid() const;\n"
00321           "  unsigned int            serial() const;\n"
00322           "  unsigned int            mem_serial() const;\n"
00323           "  bool                    operator== (Interface &comp) const;\n"
00324           "  const unsigned char *   hash() const;\n"
00325           "  int                     hash_size() const;\n"
00326           "  const char *            hash_printable() const;\n"
00327           "  bool                    is_writer() const;\n"
00328 
00329           "  void                    set_from_chunk(void *chunk);\n"
00330 
00331           "  virtual Message *   create_message(const char *type) const = 0;\n"
00332 
00333           "  void          read();\n"
00334           "  void          write();\n"
00335 
00336           "  bool          has_writer() const;\n"
00337           "  unsigned int  num_readers() const;\n"
00338 
00339 
00340           "  unsigned int  msgq_enqueue_copy(Message *message);\n"
00341           "  void          msgq_remove(Message *message);\n"
00342           "  void          msgq_remove(unsigned int message_id);\n"
00343           "  unsigned int  msgq_size();\n"
00344           "  void          msgq_flush();\n"
00345           "  void          msgq_lock();\n"
00346           "  bool          msgq_try_lock();\n"
00347           "  void          msgq_unlock();\n"
00348           "  void          msgq_pop();\n"
00349           "  Message *     msgq_first();\n"
00350           "  bool          msgq_empty();\n"
00351           "\n");
00352 }
00353 
00354 /** Write methods to h file.
00355  * @param f file to write to
00356  * @param is indentation space.
00357  * @param fields fields to write accessor methods for.
00358  */
00359 void
00360 ToLuaInterfaceGenerator::write_methods_h(FILE *f, std::string /* indent space */ is,
00361                                          std::vector<InterfaceField> fields)
00362 {
00363   for (vector<InterfaceField>::iterator i = fields.begin(); i != fields.end(); ++i) {
00364 
00365     if ( (i->getLengthValue() > 0) && (i->getType() != "string" ) ) {
00366       fprintf(f,
00367               "%s%s %s%s(int index);\n",
00368               is.c_str(),
00369               (i->getType() == "byte") ? "unsigned int" : convert_type(i->getPlainAccessType()),
00370               ( ((*i).getType() == "bool" ) ? "is_" : ""),
00371               (*i).getName().c_str());
00372 
00373       fprintf(f,
00374               "%svoid set_%s(unsigned int index, const %s new_%s);\n",
00375               is.c_str(), (*i).getName().c_str(),
00376               convert_type(i->getPlainAccessType()), i->getName().c_str());
00377     } else {
00378       fprintf(f,
00379               "%s%s %s%s();\n",
00380               is.c_str(), convert_type(i->getAccessType()),
00381               ( ((*i).getType() == "bool" ) ? "is_" : ""),
00382               (*i).getName().c_str());
00383 
00384       fprintf(f,
00385               "%svoid set_%s(const %s new_%s);\n",
00386               is.c_str(), (*i).getName().c_str(),
00387               convert_type(i->getAccessType()), i->getName().c_str());
00388     }
00389     fprintf(f,
00390             "%sint maxlenof_%s() const;\n",
00391             is.c_str(), (*i).getName().c_str()
00392             );
00393   }
00394 }
00395 
00396 
00397 /** Write methods to h file.
00398  * @param f file to write to
00399  * @param is indentation space.
00400  * @param fields fields to write accessor methods for.
00401  * @param pseudo_maps pseudo maps
00402  */
00403 void
00404 ToLuaInterfaceGenerator::write_methods_h(FILE *f, std::string /* indent space */ is,
00405                                          std::vector<InterfaceField> fields,
00406                                          std::vector<InterfacePseudoMap> pseudo_maps)
00407 {
00408   write_methods_h(f, is, fields);
00409 
00410   for (vector<InterfacePseudoMap>::iterator i = pseudo_maps.begin(); i != pseudo_maps.end(); ++i) {
00411     fprintf(f,
00412             "%s%s %s(%s key) const;\n"
00413             "%svoid set_%s(const %s key, const %s new_value);\n",
00414             is.c_str(), convert_type(i->getType()),
00415             (*i).getName().c_str(), convert_type(i->getKeyType()),
00416             is.c_str(), (*i).getName().c_str(),
00417             convert_type(i->getKeyType()), convert_type(i->getType()));
00418   }
00419 }
00420 
00421 
00422 /** Write h file.
00423  * @param f file to write to
00424  */
00425 void
00426 ToLuaInterfaceGenerator::write_toluaf(FILE *f)
00427 {
00428   fprintf(f,
00429           "$#include <interfaces/%s>\n"
00430           "$using namespace fawkes;\n"
00431           "namespace fawkes {\n"
00432           "class %s : public Interface\n"
00433           "{\n",
00434           filename_h.c_str(),
00435           class_name.c_str());
00436 
00437   write_constants_h(f);
00438   write_messages_h(f);
00439   //write_ctor_dtor_h(f, "  ", class_name);
00440   write_methods_h(f, "  ", data_fields, pseudo_maps);
00441   write_superclass_h(f);
00442   fprintf(f, "\n};\n\n}\n");
00443 }
00444 
00445 
00446 /** Generator cpp and h files.
00447  */
00448 void
00449 ToLuaInterfaceGenerator::generate()
00450 {
00451   char timestring[26]; // 26 is mentioned in man asctime_r
00452   struct tm timestruct;
00453   time_t t = time(NULL);
00454   localtime_r(&t, &timestruct);
00455   asctime_r(&timestruct, timestring);
00456   gendate = timestring;
00457 
00458   FILE *toluaf;
00459 
00460   toluaf = fopen(string(dir + filename_tolua).c_str(), "w");
00461 
00462   if ( toluaf == NULL ) {
00463     printf("Cannot open tolua file %s%s\n", dir.c_str(), filename_tolua.c_str());
00464   }
00465 
00466   write_toluaf(toluaf);
00467 
00468   fclose(toluaf);
00469 }