/** Because callbacks are handled in Lua, they still have to look like a Lua callback. The Script wrapper class can't "rename" them. **/ int TestScript::Script_PrintNumber (lua_State* state) { // But we can enable Script support using a special constructor. Script script(state); // Retrieve the number passed on the stack. Script::Object numberObj = script.GetObject(1); // Verify it is a number and print it. if (numberObj.IsNumber()) printf("%f\n", numberObj.GetNumber()); // No return values. return 0; }
/** Demonstrates walking an array table. **/ void TestScript::DoScriptArrayTest() { Script script; script.DoFile("ScriptArrayTest.scr"); Script::Object testTableObj = script.GetGlobals().GetByName("TestArray"); // or script.GetGlobal("TestArray"); for (int i = 1; ; ++i) { Script::AutoBlock block(script); // Automatic stack fixups. Script::Object entryObj = testTableObj.GetByIndex(i); if (entryObj.IsNil()) break; if (entryObj.IsNumber()) // IsNumber() must ALWAYS come first. printf("%f\n", entryObj.GetNumber()); else if (entryObj.IsString()) // IsString() returns true for a number or string. printf("%s\n", entryObj.GetString()); } }
/** Writes a Lua object to a text file. **/ static void WriteObject(Script& script, FILE* file, const char* name, Script::Object value, unsigned int indentLevel) { // If there is nothing in the variable, then don't write it. if (value.IsNil()) return; // If the variable is user data or a function, then don't write it. if (value.IsUserData() || value.IsFunction()) { return; } // Indent the line the number of spaces for the current indentation level. const unsigned int INDENT_SIZE = 4; const unsigned int indentSpaces = indentLevel * INDENT_SIZE; IndentFile(file, indentSpaces); // If the object has a name, write it out. if (name) fprintf(file, "%s = ", name); // If the object's value is a number, write it as a number. if (value.IsNumber()) fprintf(file, "%.16g", value.GetNumber()); // Or if the object's value is a string, write it as a quoted string. else if (value.IsString()) fprintf(file, "\"%s\"", value.GetString()); // Otherwise, see if the object's value is a table. else if (value.IsTable()) { // Write the table header. fputs("\n", file); IndentFile(file, indentSpaces); fputs("{\n", file); // Rename, just for ease of reading. Script::Object table = value; // upperIndex is the upper index value of a sequential numerical array // items. int upperIndex = 1; bool wroteSemi = false; bool hasSequential = false; // Block to search for array items. { // Pop the stack state when done. Script::AutoBlock block (script); // Grab index 1 and index 2 of the table. Script::Object value1 = table.GetByIndex(1); Script::Object value2 = table.GetByIndex(2); // If they both exist, then there is a sequential list. if (!value1.IsNil() && !value2.IsNil()) { // Cycle through the list. bool firstSequential = true; for (; ; ++upperIndex) { // Restore the stack state each iteration. Script::AutoBlock block_ (script); // Try retrieving the table entry at upperIndex. Script::Object value = table.GetByIndex(upperIndex); // If it doesn't exist, then exit the loop. if (value.IsNil()) break; // Only add the comma and return if not on the first item. if (!firstSequential) fputs(",\n", file); // Write the object as an unnamed entry. WriteObject(script, file, NULL, value, indentLevel + 1); // We've definitely passed the first item now. firstSequential = false; } } } // Did we find any sequential table values? if (upperIndex > 1) { hasSequential = true; } // Cycle through the table. int i; script.PushNil(); while ((i = script.Next(table.GetStackIndex())) != 0) { char keyName[255]; // Retrieve the table entry's key and value. Script::Object key = script.GetObject(script.GetTop() - 1); Script::Object value = script.GetObject(script.GetTop()); // Is the key a number? if (key.IsNumber()) { // Yes, were there sequential array items in this table? if (hasSequential) { // Is the array item's key an integer? float realNum = key.GetNumber(); int intNum = (int)realNum; if (realNum == (float)intNum) { // Yes. Is it between 1 and upperIndex? if (intNum >= 1 && intNum < upperIndex) { // We already wrote it as part of the sequential // list. script.Pop(); continue; } } } // Build the table entry name for the number. sprintf(keyName, "[%.16g]", key.GetNumber()); } else { // Build the table entry name for the string key name. strcpy(keyName, key.GetString()); } // If we wrote a sequential list, the value we're about to write // is not nil, and we haven't written the semicolon to separate // the sequential table entries from the keyed table entries... if (hasSequential && !value.IsNil() && !wroteSemi) { // Then add a comma (for good measure) and the semicolon. fputs(", ;\n", file); wroteSemi = true; } // Write the table entry. WriteObject(script, file, keyName, value, indentLevel + 1); // Add a comma after the table entry. fputs(",\n", file); // Go to the next item. script.Pop(); } // If we wrote a sequential list and haven't written a semicolon, then // there were no keyed table entries. Just write the final comma. if (hasSequential && !wroteSemi) { fputs(",\n", file); } // Indent, with the intent of closing up the table. IndentFile(file, indentSpaces); // If the indentation level is 0, then we're at the root position. if (indentLevel == 0) { // Add a couple extra returns for readability's sake. fputs("}\n\n", file); } else { // Close the table. The comma is written when WriteObject() // returns from the recursive call. fputs("}", file); } } // If the indentation level is at the root, then add a return to separate // the lines. if (indentLevel == 0) { fputs("\n", file); } }