Main Page   Compound List   File List   Compound Members   File Members  

Script.cpp

Go to the documentation of this file.
00001 #include "Script.h"
00002 #ifdef WIN32
00003 #include <windows.h>
00004 #undef GetObject
00005 #endif
00006 #include <stdio.h>
00007 #include <string.h>
00008 
00009 extern "C"
00010 {
00011 #include "lualib.h"
00012 }
00013 
00014 
00015 static int Script_LOG(lua_State* state)
00016 {
00017     Script script(state);
00018     Script::Object obj = script.GetObject(script.GetTop());
00019 #ifdef WIN32
00020     OutputDebugStringA(obj.GetString());
00021     OutputDebugStringA("\n");
00022 #else
00023     printf("%s\n", obj.GetString());
00024 #endif
00025     return 0;
00026 }
00027 
00028 
00029 static void FatalError()
00030 {
00031     throw -1;
00032 }
00033 
00034 static const char* LuaScript_CopyTable = 
00035 "function CopyTable(table) "
00036 "local newTable = {} "
00037 "for key, value in table do "
00038 "if type(value) == \"table\" then "
00039 "newTable[key] = CopyTable(value) "
00040 "else "
00041 "newTable[key] = value "
00042 "end "
00043 "end "
00044 "return newTable "
00045 "end";
00046 
00047 
00050 Script::Script(bool initStandardLibrary) :
00051     m_ownState(false)
00052 {
00053     m_state = lua_open(0);
00054     m_ownState = true;
00055     if (initStandardLibrary)
00056         lua_baselibopen(m_state);
00057 
00058     // Register some basic functions with Lua.
00059     Register("LOG", Script_LOG);
00060     Register("_ERRORMESSAGE", Script_LOG);
00061     DoString(LuaScript_CopyTable);
00062 //  lua_setfatalerrorhandler(FatalError);
00063 }
00064 
00065 
00070 int Script::ConfigGetInteger(const char* section, const char* entry,
00071                              int defaultValue)
00072 {
00073     return static_cast<int>(ConfigGetReal(section, entry, defaultValue));
00074 }
00075 
00076 
00081 float Script::ConfigGetReal(const char* section, const char* entry,
00082                             double defaultValue)
00083 {
00084     AutoBlock block(*this);
00085 
00086     Object obj = GetGlobal(section);
00087     if (obj.IsNil())
00088         return (float)defaultValue;
00089     obj = obj.GetByName(entry);
00090     if (obj.IsNumber())
00091         return obj.GetNumber();
00092     return (float)defaultValue;
00093 }
00094 
00095 
00100 const char* Script::ConfigGetString(const char* section, const char* entry,
00101                                     const char* defaultValue)
00102 {
00103     AutoBlock block(*this);
00104 
00105     Object obj = GetGlobal(section);
00106     if (obj.IsNil())
00107         return defaultValue;
00108     obj = obj.GetByName(entry);
00109     if (obj.IsString())
00110         return obj.GetString();
00111     return defaultValue;
00112 }
00113 
00114 
00118 void Script::ConfigSetInteger(const char* section, const char* entry, int value)
00119 {
00120     AutoBlock block(*this);
00121 
00122     // section.entry = value
00123     // Difficult in code.  Do it this way.
00124     Object sectionTable = GetGlobal(section);
00125 
00126     // If the global table isn't there, then create it.
00127     if (sectionTable.IsNil())
00128     {
00129         sectionTable = GetGlobals().CreateTable(section);
00130     }
00131 
00132     sectionTable.SetNumber(entry, value);
00133 }
00134 
00135 
00139 void Script::ConfigSetReal(const char* section, const char* entry, double value)
00140 {
00141     AutoBlock block(*this);
00142 
00143     // section.entry = value
00144     // Difficult in code.  Do it this way.
00145     Object sectionTable = GetGlobal(section);
00146 
00147     // If the global table isn't there, then create it.
00148     if (sectionTable.IsNil())
00149     {
00150         sectionTable = GetGlobals().CreateTable(section);
00151     }
00152 
00153     sectionTable.SetNumber(entry, value);
00154 }
00155 
00156 
00160 void Script::ConfigSetString(const char* section, const char* entry, const char* value)
00161 {
00162     AutoBlock block(*this);
00163 
00164     // section.entry = value
00165     // Difficult in code.  Do it this way.
00166     Object sectionTable = GetGlobal(section);
00167 
00168     // If the global table isn't there, then create it.
00169     if (sectionTable.IsNil())
00170     {
00171         sectionTable = GetGlobals().CreateTable(section);
00172     }
00173 
00174     sectionTable.SetString(entry, value);
00175 }
00176 
00177 
00181 static void IndentFile(FILE* file, unsigned int indentLevel)
00182 {
00183     // Write out indentation.
00184     char spaces[500];
00185     for (unsigned int i = 0; i < indentLevel; ++i)
00186         spaces[i] = ' ';
00187     spaces[i] = 0;
00188     fputs(spaces, file);
00189 }
00190 
00191 
00195 static void WriteObject(Script& script, FILE* file, const char* name,
00196                         Script::Object value, unsigned int indentLevel)
00197 {
00198     // If there is nothing in the variable, then don't write it.
00199     if (value.IsNil())
00200         return;
00201 
00202     // If the variable is user data or a function, then don't write it.
00203     if (value.IsUserData()  ||  value.IsFunction())
00204     {
00205         return;
00206     }
00207 
00208     using Script::Object;
00209 
00210     // Indent the line the number of spaces for the current indentation level.
00211     const unsigned int INDENT_SIZE = 4;
00212     const unsigned int indentSpaces = indentLevel * INDENT_SIZE;
00213     IndentFile(file, indentSpaces);
00214     
00215     // If the object has a name, write it out.
00216     if (name)
00217         fprintf(file, "%s = ", name);
00218 
00219     // If the object's value is a number, write it as a number.
00220     if (value.IsNumber())
00221         fprintf(file, "%.16g", value.GetNumber());
00222 
00223     // Or if the object's value is a string, write it as a quoted string.
00224     else if (value.IsString())
00225         fprintf(file, "\"%s\"", value.GetString());
00226 
00227     // Otherwise, see if the object's value is a table.
00228     else if (value.IsTable())
00229     {
00230         // Write the table header.
00231         fputs("\n", file);
00232         IndentFile(file, indentSpaces);
00233         fputs("{\n", file);
00234 
00235         // Rename, just for ease of reading.
00236         Script::Object table = value;
00237 
00238         // upperIndex is the upper index value of a sequential numerical array
00239         // items.
00240         int upperIndex = 1;
00241         bool wroteSemi = false;
00242         bool hasSequential = false;
00243 
00244         // Block to search for array items.
00245         {
00246             // Pop the stack state when done.
00247             Script::AutoBlock block(script);
00248 
00249             // Grab index 1 and index 2 of the table.
00250             Object value1 = table.GetByIndex(1);
00251             Object value2 = table.GetByIndex(2);
00252 
00253             // If they both exist, then there is a sequential list.
00254             if (!value1.IsNil()  &&  !value2.IsNil())
00255             {
00256                 // Cycle through the list.
00257                 bool firstSequential = true;
00258                 for (; ; ++upperIndex)
00259                 {
00260                     // Restore the stack state each iteration.
00261                     Script::AutoBlock block(script);
00262 
00263                     // Try retrieving the table entry at upperIndex.
00264                     Object value = table.GetByIndex(upperIndex);
00265 
00266                     // If it doesn't exist, then exit the loop.
00267                     if (value.IsNil())
00268                         break;
00269 
00270                     // Only add the comma and return if not on the first item.
00271                     if (!firstSequential)
00272                         fputs(",\n", file);
00273                     
00274                     // Write the object as an unnamed entry.
00275                     WriteObject(script, file, NULL, value, indentLevel + 1);
00276 
00277                     // We've definitely passed the first item now.
00278                     firstSequential = false;
00279                 }
00280             }
00281         }
00282 
00283         // Did we find any sequential table values?
00284         if (upperIndex > 1)
00285         {
00286             hasSequential = true;
00287         }
00288         
00289         // Cycle through the table.
00290         int i;
00291         script.PushNil();
00292         while ((i = script.Next(table.GetStackIndex())) != 0)
00293         {
00294             char keyName[255];
00295 
00296             // Retrieve the table entry's key and value.
00297             Object key = script.GetObject(script.GetTop() - 1);
00298             Object value = script.GetObject(script.GetTop());
00299 
00300             // Is the key a number?
00301             if (key.IsNumber())
00302             {
00303                 // Yes, were there sequential array items in this table?
00304                 if (hasSequential)
00305                 {
00306                     // Is the array item's key an integer?
00307                     float realNum = key.GetNumber();
00308                     int intNum = (int)realNum;
00309                     if (realNum == (float)intNum)
00310                     {
00311                         // Yes.  Is it between 1 and upperIndex?
00312                         if (intNum >= 1  &&  intNum < upperIndex)
00313                         {
00314                             // We already wrote it as part of the sequential
00315                             // list.
00316                             script.Pop();
00317                             continue;
00318                         }
00319                     }
00320                 }
00321 
00322                 // Build the table entry name for the number.
00323                 sprintf(keyName, "[%.16g]", key.GetNumber());
00324             }
00325             else
00326             {
00327                 // Build the table entry name for the string key name.
00328                 strcpy(keyName, key.GetString());
00329             }
00330 
00331             // If we wrote a sequential list, the value we're about to write
00332             // is not nil, and we haven't written the semicolon to separate
00333             // the sequential table entries from the keyed table entries...
00334             if (hasSequential  &&  !value.IsNil()  &&  !wroteSemi)
00335             {
00336                 // Then add a comma (for good measure) and the semicolon.
00337                 fputs(", ;\n", file);
00338                 wroteSemi = true;
00339             }
00340 
00341             // Write the table entry.
00342             WriteObject(script, file, keyName, value, indentLevel + 1);
00343 
00344             // Add a comma after the table entry.
00345             fputs(",\n", file);
00346 
00347             // Go to the next item.
00348             script.Pop();
00349         }
00350 
00351         // If we wrote a sequential list and haven't written a semicolon, then
00352         // there were no keyed table entries.  Just write the final comma.
00353         if (hasSequential  &&  !wroteSemi)
00354         {
00355             fputs(",\n", file);
00356         }
00357         
00358         // Indent, with the intent of closing up the table.
00359         IndentFile(file, indentSpaces);
00360 
00361         // If the indentation level is 0, then we're at the root position.
00362         if (indentLevel == 0)
00363         {
00364             // Add a couple extra returns for readability's sake.
00365             fputs("}\n\n", file);
00366         }
00367         else
00368         {
00369             // Close the table.  The comma is written when WriteObject()
00370             // returns from the recursive call.
00371             fputs("}", file);
00372         }
00373     }
00374 
00375     // If the indentation level is at the root, then add a return to separate
00376     // the lines.
00377     if (indentLevel == 0)
00378     {
00379         fputs("\n", file);
00380     }
00381 }
00382 
00383 
00387 void Script::SaveText(const char* filename)
00388 {
00389     // Open the text file to write the script state to.
00390     FILE* file = fopen(filename, "wt");
00391 
00392     // For safety, just in case we leave something behind on the script stack.
00393     AutoBlock block(*this);
00394 
00395     // Run through all the globals.
00396     int i;
00397     Object table = GetGlobals();
00398     PushNil();
00399     while ((i = Next(table.GetStackIndex())) != 0)
00400     {
00401         // Retrieve the global's key and value.
00402         Object key = GetObject(GetTop() - 1);
00403         Object value = GetObject(GetTop());
00404 
00405         // Save the global to the text file.
00406         if (strcmp(key.GetString(), "_VERSION") != 0)
00407         {
00408             WriteObject(*this, file, key.GetString(), value, 0);
00409         }
00410 
00411         // Go to the next global.
00412         Pop();
00413     }
00414 
00415     // Close the text file.
00416     fclose(file);
00417 }

Generated at Wed Dec 13 00:55:51 2000 for Script (Lua Wrapper) by doxygen1.2.2 written by Dimitri van Heesch, © 1997-2000