diff --git a/CMakeLists.txt b/CMakeLists.txt index caf4f63d0b12dec4fbd1882f70cddd441a7e5d91..621f88fbedbae5ef1a3f728cb4a8fcd87b51e38c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ ADD_REQUIRED_DEPENDENCY("eigen3 >= 3.0.5") ADD_OPTIONAL_DEPENDENCY("eigenpy >= 1.2.0") ADD_OPTIONAL_DEPENDENCY("metapod >= 1.0.7") ADD_OPTIONAL_DEPENDENCY("urdfdom >= 0.2.10") +ADD_OPTIONAL_DEPENDENCY("lua5.1") SET(BOOST_COMPONENTS filesystem unit_test_framework) SEARCH_FOR_BOOST() @@ -142,6 +143,18 @@ IF(URDFDOM_FOUND) ) ENDIF(URDFDOM_FOUND) +IF(LUA5_1_FOUND) + LIST(APPEND HEADERS + multibody/parser/lua.hpp + multibody/parser/lua/lua_tables.hpp + ) + + SET(${PROJECT_NAME}_MULTIBODY_PARSER_LUA_HEADERS + multibody/parser/lua.hpp + multibody/parser/lua/lua_tables.hpp + ) +ENDIF(LUA5_1_FOUND) + PKG_CONFIG_APPEND_LIBS (${PROJECT_NAME}) MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio") @@ -150,6 +163,7 @@ MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/spatial") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/multibody") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/multibody/joint") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/multibody/parser") +MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/multibody/parser/lua") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/tools") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/algorithm") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/simulation") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5d4854d8eed4b97d893f6382abdce1771c65c355..ae99dfab1c8d11d269e133a2f47127cd15c56040 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,36 @@ MACRO(ADD_SOURCE_GROUP FILENAMES) ADD_GROUP("Source Files" ${FILENAMES}) ENDMACRO(ADD_SOURCE_GROUP FILENAMES) +# ---------------------------------------------------- +# --- C++ -------------------------------------------- +# ---------------------------------------------------- + +IF(LUA5_1_FOUND) + SET(${PROJECT_NAME}_MULTIBODY_PARSER_LUA_SOURCES + multibody/parser/lua/lua_tables.cpp + multibody/parser/lua.cpp + ) +ENDIF(LUA5_1_FOUND) + +SET(${PROJECT_NAME}_SOURCES ${${PROJECT_NAME}_MULTIBODY_PARSER_LUA_SOURCES}) + +IF (UNIX ) + # Create target libpinocchio.so + ADD_LIBRARY ( ${PROJECT_NAME} SHARED ${HEADERS} ${${PROJECT_NAME}_SOURCES} ) + SET_TARGET_PROPERTIES( ${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX) + + IF(LUA5_1_FOUND) + PKG_CONFIG_USE_DEPENDENCY(${PROJECT_NAME} lua5.1) + ENDIF(LUA5_1_FOUND) + + ADD_HEADER_GROUP(HEADERS) + ADD_SOURCE_GROUP(${PROJECT_NAME}_SOURCES) + + # Install target libpinocchio.so + INSTALL(FILES + "${CMAKE_BINARY_DIR}/src/lib${PROJECT_NAME}.so" + DESTINATION ${CMAKE_INSTALL_LIBDIR}) +ENDIF ( ) # ---------------------------------------------------- # --- PYTHON ----------------------------------------- @@ -33,9 +63,11 @@ IF(EIGENPY_FOUND) PKG_CONFIG_USE_DEPENDENCY(${PYWRAP} eigenpy) IF(URDFDOM_FOUND) PKG_CONFIG_USE_DEPENDENCY(${PYWRAP} urdfdom) - ENDIF(URDFDOM_FOUND) - TARGET_LINK_LIBRARIES(${PYWRAP} ${Boost_LIBRARIES} eigenpy) + IF(LUA5_1_FOUND) + PKG_CONFIG_USE_DEPENDENCY(${PYWRAP} lua5.1) + ENDIF(LUA5_1_FOUND) + TARGET_LINK_LIBRARIES(${PYWRAP} ${Boost_LIBRARIES} ${PROJECT_NAME}) SET_TARGET_PROPERTIES(${PYWRAP} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/python/${PROJECT_NAME}") INSTALL(FILES @@ -81,17 +113,3 @@ IF(EIGENPY_FOUND) ENDIF(EIGENPY_FOUND) -IF (UNIX ) - # Create target libpinocchio.so - ADD_LIBRARY ( ${PROJECT_NAME} SHARED ${HEADERS} ${${PROJECT_NAME}_SOURCES} ) - SET_TARGET_PROPERTIES( ${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX) - - ADD_HEADER_GROUP(HEADERS) - ADD_SOURCE_GROUP(${PROJECT_NAME}_SOURCES) - - # Install target libpinocchio.so - INSTALL(FILES - "${CMAKE_BINARY_DIR}/src/lib${PROJECT_NAME}.so" - DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -ENDIF ( ) diff --git a/src/multibody/parser/lua.cpp b/src/multibody/parser/lua.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f3a880c231e97d33de509bdf14e0a3c6f4e9fde --- /dev/null +++ b/src/multibody/parser/lua.cpp @@ -0,0 +1,313 @@ + +#include "pinocchio/multibody/parser/lua/lua_tables.hpp" +#include "pinocchio/multibody/parser/lua.hpp" + +#include <lua.hpp> +#include <iostream> +#include <map> +#include <sstream> + +#include "pinocchio/spatial/se3.hpp" +#include "pinocchio/spatial/motion.hpp" +#include "pinocchio/spatial/inertia.hpp" +#include "pinocchio/multibody/model.hpp" + +typedef se3::SE3::Vector3 Vector3; +typedef se3::SE3::Matrix3 Matrix3; + +template<> Vector3 LuaTableNode::getDefault<Vector3> (const Vector3 & default_value) +{ + Vector3 result (default_value); + + if (stackQueryValue()) { + LuaTable vector_table = LuaTable::fromLuaState (luaTable->L); + + if (vector_table.length() != 3) { + std::cerr << "LuaModel Error: invalid 3d vector!" << std::endl; + abort(); + } + + result[0] = vector_table[1]; + result[1] = vector_table[2]; + result[2] = vector_table[3]; + } + + stackRestore(); + + return result; +} + +template<> Matrix3 LuaTableNode::getDefault<Matrix3> (const Matrix3 & default_value) +{ + Matrix3 result (default_value); + + if (stackQueryValue()) { + LuaTable vector_table = LuaTable::fromLuaState (luaTable->L); + + if (vector_table.length() != 3) { + std::cerr << "LuaModel Error: invalid 3d matrix!" << std::endl; + abort(); + } + + if (vector_table[1].length() != 3 + || vector_table[2].length() != 3 + || vector_table[3].length() != 3) { + std::cerr << "LuaModel Error: invalid 3d matrix!" << std::endl; + abort(); + } + + result(0,0) = vector_table[1][1]; + result(0,1) = vector_table[1][2]; + result(0,2) = vector_table[1][3]; + + result(1,0) = vector_table[2][1]; + result(1,1) = vector_table[2][2]; + result(1,2) = vector_table[2][3]; + + result(2,0) = vector_table[3][1]; + result(2,1) = vector_table[3][2]; + result(2,2) = vector_table[3][3]; + } + + stackRestore(); + + return result; +} + +template<> se3::SE3 LuaTableNode::getDefault<se3::SE3> (const se3::SE3 & default_value) +{ + se3::SE3 result (default_value); + + if (stackQueryValue()) { + LuaTable vector_table = LuaTable::fromLuaState (luaTable->L); + + result.translation() = vector_table["r"].getDefault<Vector3> (Vector3::Zero (3)); + result.rotation().transpose() = vector_table["E"].getDefault<Matrix3> (Matrix3::Identity (3,3)); + } + + stackRestore(); + + return result; +} + +template<> se3::Inertia LuaTableNode::getDefault<se3::Inertia> (const se3::Inertia & default_value) +{ + se3::Inertia result (default_value); + + if (stackQueryValue()) { + LuaTable vector_table = LuaTable::fromLuaState (luaTable->L); + + double mass = 0.; + Vector3 com; + Matrix3 inertia_matrix; + + mass = vector_table["mass"]; + com = vector_table["com"].getDefault<Vector3> (Vector3::Zero ()); + inertia_matrix = vector_table["inertia"].getDefault<Matrix3> (Matrix3::Identity ()); + + result = se3::Inertia (mass, com, inertia_matrix); + } + + stackRestore(); + + return result; +} + +namespace se3 +{ + namespace lua + { + bool LuaModelReadFromTable (LuaTable & model_table, Model & model, bool freeFlyer, bool verbose) + { + typedef std::map<std::string, Model::Index> mapStringIndex_t; + mapStringIndex_t body_table_id_map; + mapStringIndex_t fixed_body_table_id_map; + + typedef std::map<std::string, SE3> mapStringSE3_t; + mapStringSE3_t fixed_placement_map; + + if (model_table["gravity"].exists()) + { + model.gravity.linear() = model_table["gravity"].get<Vector3> (); + + if (verbose) + std::cout << "gravity = " << model.gravity.linear().transpose() << std::endl; + } + + if (! model_table["frames"].exists()) + { + std::cerr << "Frames table missing from model table - Abort" << std::endl; + abort(); + } + + size_t frame_count = model_table["frames"].length(); + + body_table_id_map["ROOT"] = 0; + + for (int i = 1; i <= (int) frame_count; i++) { + + std::stringstream body_name_default; + body_name_default << "body " << i; + std::string body_name = model_table["frames"][i]["name"].getDefault<std::string> (body_name_default.str()); + + std::string parent_name; + if (model_table["frames"][i]["parent"].exists ()) + { + parent_name = model_table["frames"][i]["parent"].get<std::string> (); + } + else if (i == 1) // first body of the tree + { + parent_name = "ROOT"; + } + else // no parent defined + { + std::cerr << "Parent not defined for frame " << i << "." << std::endl; + abort(); + } + + Model::Index parent_id; + SE3 fixed_placement_offset (SE3::Identity());; + + if (body_table_id_map.find (parent_name) != body_table_id_map.end ()) + { + parent_id = body_table_id_map[parent_name]; + } + else if (fixed_body_table_id_map.find(parent_name) != fixed_body_table_id_map.end ()) // the parent is fixed in the kinematic tree + { + parent_id = fixed_body_table_id_map[parent_name]; + fixed_placement_offset = fixed_placement_map[parent_name]; + } + else + { + std::cerr << parent_name << " is not in the tree." << std::endl; + abort (); + } + + + std::stringstream joint_name_default; + joint_name_default << "joint " << i; + std::string joint_name = model_table["frames"][i]["joint_name"].getDefault<std::string> (joint_name_default.str()); + + + SE3 joint_placement = model_table["frames"][i]["joint_frame"].getDefault<SE3> (SE3::Identity ()); + SE3 global_placement (fixed_placement_offset * joint_placement); // placement due to the existence of fixed bodies + + if (! model_table["frames"][i]["body"].exists()) { + std::cerr << "body field not defined for frame " << i << "." << std::endl; + abort(); + } + + Inertia Y = model_table["frames"][i]["body"].getDefault<Inertia> (Inertia::Identity ()); + + std::string joint_type; + if (model_table["frames"][i]["joint"].exists()) + { + if (model_table["frames"][i]["joint"].length() == 1) + joint_type = model_table["frames"][i]["joint"][1].getDefault<std::string> (""); + else + std::cerr << "Joint compouned not implemented yet. Take only the first joint." << std::endl; + } + else if (i == 1) // first body of the tree + { + if (freeFlyer) + joint_type = "jointFreeFlyer"; + else + { + std::cerr << "The first segment is defined without any definition of joint type relatily to the environment." << std::endl; + abort (); + } + } + else + { + std::cerr << "joint field not defined for frame " << i << "." << std::endl; + abort(); + } + + + Model::Index body_id; + bool is_body_fixed = false; + if (joint_type == "jointRX") + { + body_id = model.addBody (parent_id, JointModelRX (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointRY") + { + body_id = model.addBody (parent_id, JointModelRY (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointRZ") + { + body_id = model.addBody (parent_id, JointModelRZ (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointPX") + { + body_id = model.addBody (parent_id, JointModelPX (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointPY") + { + body_id = model.addBody (parent_id, JointModelPY (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointPZ") + { + body_id = model.addBody (parent_id, JointModelPZ (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointFreeFlyer") + { + body_id = model.addBody (parent_id, JointModelFreeFlyer (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointSpherical") + { + body_id = model.addBody (parent_id, JointModelSpherical (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointSphericalZYX") + { + body_id = model.addBody (parent_id, JointModelSphericalZYX (), global_placement, Y, joint_name, body_name, false); + } + else if (joint_type == "jointFixed") + { + model.mergeFixedBody(parent_id, global_placement, Y); + + fixed_body_table_id_map[body_name] = parent_id; + fixed_placement_map[body_name] = global_placement; + + body_id = model.addFixedBody(parent_id, global_placement, body_name, false); + is_body_fixed = true; + } + else + { + std::cerr << joint_type << " is not supported.." << std::endl; + abort (); + } + + body_table_id_map[body_name] = body_id; + + if (verbose) { + std::cout << "==== Added Body ====" << std::endl; + std::cout << " body name : " << body_name << std::endl; + if (! is_body_fixed) + std::cout << " body id : " << body_id << std::endl; + else + std::cout << " fixed body id : " << body_id << std::endl; + std::cout << " closest parent id : " << parent_id << std::endl; + std::cout << " joint frame:\n" << joint_placement << std::endl; + std::cout << " joint type : " << joint_type << std::endl; + std::cout << " joint name : " << joint_name << std::endl; + std::cout << " body inertia:\n" << Y << std::endl; + } + } + + return true; + } + + Model buildModel (const std::string & filename, bool freeFlyer, bool verbose) + { + Model model; + + LuaTable model_table = LuaTable::fromFile (filename.c_str ()); + LuaModelReadFromTable (model_table, model, freeFlyer, verbose); + + return model; + } + + } // namespace lua + +} // namespace se3 diff --git a/src/multibody/parser/lua.hpp b/src/multibody/parser/lua.hpp new file mode 100644 index 0000000000000000000000000000000000000000..259598442a351827d62bb3109258230eb8dfc613 --- /dev/null +++ b/src/multibody/parser/lua.hpp @@ -0,0 +1,15 @@ +#ifndef __se3_lua_hpp__ +#define __se3_lua_hpp__ + +#include <iostream> +#include "pinocchio/multibody/model.hpp" + +namespace se3 +{ + namespace lua + { + Model buildModel (const std::string & filename, bool freeFlyer = false, bool verbose = false); + } // namespace lua +} // namespace se3 + +#endif // ifndef __se3_lua_hpp__ diff --git a/src/multibody/parser/lua/lua_tables.cpp b/src/multibody/parser/lua/lua_tables.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11f4b6235e982f312a030e05ca019a593db38ee6 --- /dev/null +++ b/src/multibody/parser/lua/lua_tables.cpp @@ -0,0 +1,850 @@ +/* + * LuaTables++ + * Code comming from https://bitbucket.org/rbdl/rbdl + * Copyright (c) 2013-2014 Martin Felis <martin@fyxs.org>. + * Copyright (c) 2015 Justin Carpentier <jcarpent@laas.fr> + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pinocchio/multibody/parser/lua/lua_tables.hpp" + +#include <assert.h> +#include <iostream> +#include <cstdlib> +#include <vector> +#include <sstream> +#include <cmath> + +#include <lua.hpp> + +#include <stdio.h> /* defines FILENAME_MAX */ +#ifdef WINDOWS +#include <direct.h> +#define get_current_dir _getcwd +#else +#include <unistd.h> +#define get_current_dir getcwd +#endif + +#if defined(WIN32) || defined (_WIN32) +#define DIRECTORY_SEPARATOR "\\" +#elif defined(linux) || defined (__linux) || defined(__linux__) || defined(__APPLE__) +#define DIRECTORY_SEPARATOR "/" +#else +#error Platform not supported! +#endif + +using namespace std; + +std::string get_file_directory (const char* filename) +{ + string name (filename); + string result = name.substr(0, name.find_last_of (DIRECTORY_SEPARATOR) + 1); + + if (result == "") + result = "./"; +#if defined (WIN32) || defined (_WIN32) +#warning get_file_directory() not yet tested under Windows! + else if (result.substr(1,2) != ":\\") + result = string(".\\") + result; +#else + else if (result.substr(0,string(DIRECTORY_SEPARATOR).size()) != DIRECTORY_SEPARATOR && result[0] != '.') + result = string("./") + result; +#endif + + return result; +} + +// char encoded serialize function that is available in plaintext in +// utils/serialize.lua. Converted using lua auto.lua serialize.lua +const char serialize_std[] = { + + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x6c, + 0x69, 0x73, 0x74, 0x20, 0x28, 0x74, 0x29, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x3d, + 0x20, 0x30, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, + 0x20, 0x6e, 0x69, 0x6c, 0x0a, + 0x09, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, + 0x74, 0x29, 0x20, 0x64, 0x6f, 0x0a, + 0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x2b, 0x20, 0x31, 0x0a, + 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x20, + 0x6e, 0x69, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x28, 0x76, 0x29, 0x0a, + 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, + 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22, + 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, + 0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, + 0x6e, 0x0a, + 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x0a, + 0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x28, 0x76, 0x29, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x7e, 0x3d, 0x20, 0x23, + 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, + 0x65, 0x6e, 0x64, 0x0a, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6d, 0x70, + 0x5f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x28, 0x61, 0x2c, 0x20, + 0x62, 0x29, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x28, 0x61, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x62, 0x29, + 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x28, 0x61, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x62, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3c, 0x20, 0x62, 0x0a, + 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x61, 0x29, 0x20, 0x3c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x62, 0x29, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x65, 0x6e, 0x64, 0x0a, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x6e, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x20, 0x28, + 0x74, 0x29, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, + 0x09, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x28, 0x74, + 0x29, 0x20, 0x64, 0x6f, 0x0a, + 0x09, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x28, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x20, 0x6b, 0x29, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x20, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x20, 0x63, 0x6d, 0x70, 0x5f, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x29, 0x0a, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x0a, + 0x65, 0x6e, 0x64, 0x0a, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x20, 0x28, 0x74, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x29, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x20, 0x74, + 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x20, 0x28, 0x74, 0x29, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x2e, 0x5f, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x31, 0x5d, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74, 0x5b, 0x6b, 0x65, + 0x79, 0x5d, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, + 0x09, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x67, 0x65, 0x74, 0x6e, 0x28, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x29, 0x20, 0x64, 0x6f, 0x0a, + 0x09, 0x09, 0x69, 0x66, 0x20, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x09, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x0a, + 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74, 0x5b, 0x6b, 0x65, + 0x79, 0x5d, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x0a, + 0x65, 0x6e, 0x64, 0x0a, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x28, 0x74, 0x29, 0x0a, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6e, 0x65, + 0x78, 0x74, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x6e, 0x69, 0x6c, 0x0a, + 0x65, 0x6e, 0x64, 0x0a, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x20, 0x28, 0x6f, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x0a, + 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x22, + 0x22, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x20, + 0x3d, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x70, 0x61, 0x69, 0x72, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x73, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x20, 0x74, + 0x68, 0x65, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x0a, + 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6f, 0x29, 0x0a, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d, + 0x3d, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6f, 0x29, 0x0a, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d, + 0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x28, 0x22, 0x25, 0x71, 0x22, 0x2c, 0x20, 0x6f, 0x29, 0x0a, + 0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d, 0x3d, + 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x6c, 0x69, 0x73, + 0x74, 0x28, 0x6f, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x2e, 0x2e, 0x20, 0x22, 0x7b, 0x22, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73, + 0x75, 0x62, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x70, 0x61, 0x69, 0x72, + 0x73, 0x28, 0x6f, 0x29, 0x20, 0x64, 0x6f, 0x0a, + 0x09, 0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x6e, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, + 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x73, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x28, 0x76, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, + 0x22, 0x20, 0x20, 0x22, 0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, + 0x2c, 0x22, 0x0a, + 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x28, 0x76, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x22, 0x0a, + 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73, 0x75, 0x62, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x6e, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x0a, + 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x2e, 0x2e, 0x20, 0x22, 0x7d, 0x22, 0x0a, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d, + 0x3d, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6f, 0x2e, 0x64, 0x6f, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x5f, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x7b, 0x7d, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x7b, 0x5c, 0x6e, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69, + 0x72, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x29, 0x20, 0x64, 0x6f, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e, + 0x3d, 0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x73, 0x75, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x65, 0x64, 0x20, 0x6b, + 0x65, 0x79, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x6c, 0x79, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x69, 0x66, 0x69, 0x65, 0x64, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6b, 0x29, + 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x3d, + 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, + 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5b, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, + 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6b, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5d, 0x20, 0x3d, + 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x76, 0x29, 0x20, + 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x5c, 0x6e, 0x22, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, + 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5b, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, + 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6b, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5d, 0x20, 0x3d, + 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x28, 0x76, 0x2c, + 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22, 0x2c, 0x20, 0x73, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x5c, 0x6e, 0x22, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, + 0x20, 0x22, 0x20, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x6b, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x3d, 0x20, + 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x28, 0x76, 0x2c, 0x20, + 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22, 0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x5c, 0x6e, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x7d, 0x22, 0x0a, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x20, 0x28, 0x22, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, + 0x20, 0x29, 0x0a, + 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x0a, + 0x65, 0x6e, 0x64, 0x0a, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x0a, +}; + +// +// Lua Helper Functions +// +void bail(lua_State * L, const char *msg) +{ + std::cerr << msg << lua_tostring(L, -1) << endl; + abort(); +} + +void stack_print (const char *file, int line, lua_State * L) +{ + cout << file << ":" << line << ": stack size: " << lua_gettop(L) << endl;; + for (int i = 1; i < lua_gettop(L) + 1; i++) { + cout << file << ":" << line << ": "; + cout << i << ": "; + if (lua_istable (L, i)) + cout << " table" << endl; + else if (lua_isnumber (L, i)) + cout << " number: " << lua_tonumber (L, i) << endl; + else if (lua_isuserdata (L, i)) { + void* userdata = (void*) lua_touserdata (L, i); + cout << " userdata (" << userdata << ")" << endl; + } else if (lua_isstring (L, i)) + cout << " string: " << lua_tostring(L, i) << endl; + else if (lua_isfunction (L, i)) + cout << " function" << endl; + else if (lua_isnil (L, i)) + cout << " nil" << endl; + else + cout << " unknown: " << lua_typename (L, lua_type (L, i)) << endl; + } +} + +void l_push_LuaKey (lua_State * L, const LuaKey & key) +{ + if (key.type == LuaKey::Integer) + lua_pushnumber (L, key.int_value); + else + lua_pushstring(L, key.string_value.c_str()); +} + +bool query_key_stack (lua_State * L, std::vector<LuaKey> key_stack) +{ + for (int i = (int)key_stack.size() - 1; i >= 0; i--) { + // get the global value when the result of a lua expression was not + // pushed onto the stack via the return statement. + if (lua_gettop(L) == 0) { + lua_getglobal (L, key_stack[key_stack.size() - 1].string_value.c_str()); + + if (lua_isnil(L, -1)) { + return false; + } + + continue; + } + + l_push_LuaKey (L, key_stack[(size_t) i]); + + lua_gettable (L, -2); + + // return if key is not found + if (lua_isnil(L, -1)) { + return false; + } + } + + return true; +} + +void create_key_stack (lua_State * L, std::vector<LuaKey> key_stack) +{ + for (size_t i = key_stack.size() - 1; i > 0; i--) { + // get the global value when the result of a lua expression was not + // pushed onto the stack via the return statement. + if (lua_gettop(L) == 0) { + lua_getglobal (L, key_stack[key_stack.size() - 1].string_value.c_str()); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setglobal(L, key_stack[key_stack.size() - 1].string_value.c_str()); + } + + continue; + } + + l_push_LuaKey (L, key_stack[i]); + + lua_pushvalue (L, -1); + lua_gettable (L, -3); + + if (lua_isnil(L, -1)) { + // parent, key, nil + lua_pop(L, 1); // parent, key + lua_newtable(L); // parent, key, table + lua_insert(L, -2); // parent, table, key + lua_pushvalue(L, -2); // parent, table, key, table + lua_settable (L, -4); // parent, table + } + } +} + +// +// LuaTableNode +// +std::vector<LuaKey> LuaTableNode::getKeyStack() +{ + std::vector<LuaKey> result; + + const LuaTableNode *node_ptr = this; + + do { + result.push_back (node_ptr->key); + node_ptr = node_ptr->parent; + } while (node_ptr != NULL); + + return result; +} + +std::string LuaTableNode::keyStackToString() +{ + std::vector<LuaKey> key_stack = getKeyStack(); + + ostringstream result_stream (""); + for (int i = (int)key_stack.size() - 1; i >= 0; i--) { + if (key_stack[(size_t) i].type == LuaKey::String) + result_stream << "[\"" << key_stack[(size_t) i].string_value << "\"]"; + else + result_stream << "[" << key_stack[(size_t) i].int_value << "]"; + } + + return result_stream.str(); +} + +bool LuaTableNode::stackQueryValue() +{ + lua_State * L = luaTable->L; + stackTop = lua_gettop(L); + + std::vector<LuaKey> key_stack = getKeyStack(); + + return query_key_stack (L, key_stack); +} + +void LuaTableNode::stackCreateValue() +{ + lua_State * L = luaTable->L; + stackTop = lua_gettop(L); + + std::vector<LuaKey> key_stack = getKeyStack(); + + create_key_stack (L, key_stack); +} + +LuaTable LuaTableNode::stackQueryTable() +{ + lua_State * L = luaTable->L; + stackTop = lua_gettop(L); + + std::vector<LuaKey> key_stack = getKeyStack(); + + if (!query_key_stack (L, key_stack)) { + std::cerr << "Error: could not query table " << key << "." << std::endl; + abort(); + } + + return LuaTable::fromLuaState (L); +} + +LuaTable LuaTableNode::stackCreateLuaTable() +{ + lua_State * L = luaTable->L; + stackTop = lua_gettop(L); + + std::vector<LuaKey> key_stack = getKeyStack(); + + create_key_stack (L, key_stack); + + // create new table for the CustomType + lua_newtable(luaTable->L); // parent, CustomTable + // add table of CustomType to the parent + stackPushKey(); // parent, CustomTable, key + lua_pushvalue(luaTable->L, -2); // parent, CustomTable, key, CustomTable + lua_settable(luaTable->L, -4); + + return LuaTable::fromLuaState (L); +} + +void LuaTableNode::stackPushKey() +{ + l_push_LuaKey (luaTable->L, key); +} + +void LuaTableNode::stackRestore() +{ + lua_pop (luaTable->L, lua_gettop(luaTable->L) - stackTop); +} + +bool LuaTableNode::exists() +{ + bool result = true; + + if (!stackQueryValue()) + result = false; + + stackRestore(); + + return result; +} + +void LuaTableNode::remove() +{ + if (stackQueryValue()) { + lua_pop(luaTable->L, 1); + + if (lua_gettop(luaTable->L) != 0) { + l_push_LuaKey (luaTable->L, key); + lua_pushnil (luaTable->L); + lua_settable (luaTable->L, -3); + } else { + lua_pushnil (luaTable->L); + lua_setglobal (luaTable->L, key.string_value.c_str()); + } + } + + stackRestore(); +} + +size_t LuaTableNode::length() +{ + size_t result = 0; + + if (stackQueryValue()) { + result = lua_objlen(luaTable->L, -1); + } + + stackRestore(); + + return result; +} + +std::vector<LuaKey> LuaTableNode::keys() +{ + std::vector<LuaKey> result; + + if (stackQueryValue()) { + // loop over all keys + lua_pushnil(luaTable->L); + while (lua_next(luaTable->L, -2) != 0) { + if (lua_isnumber(luaTable->L, -2)) { + double number = lua_tonumber (luaTable->L, -2); + double frac; + if (modf (number, &frac) == 0) { + LuaKey key (static_cast<int>(number)); + result.push_back (key); + } + } else if (lua_isstring (luaTable->L, -2)) { + LuaKey key (lua_tostring(luaTable->L, -2)); + result.push_back (key); + } else { + cerr << "Warning: invalid LuaKey type for key " << lua_typename(luaTable->L, lua_type(luaTable->L, -2)) << "!" << endl; + } + + lua_pop(luaTable->L, 1); + } + } + + stackRestore(); + + return result; +} + + +template<> bool LuaTableNode::getDefault<bool>(const bool & default_value) +{ + bool result = default_value; + + if (stackQueryValue()) { + result = lua_toboolean (luaTable->L, -1); + } + + stackRestore(); + + return result; +} + +template<> float LuaTableNode::getDefault<float>(const float & default_value) +{ + float result = default_value; + + if (stackQueryValue()) { + result = static_cast<float>(lua_tonumber (luaTable->L, -1)); + } + + stackRestore(); + + return result; +} + +template<> double LuaTableNode::getDefault<double>(const double & default_value) +{ + double result = default_value; + + if (stackQueryValue()) { + result = lua_tonumber (luaTable->L, -1); + } + + stackRestore(); + + return result; +} + +template<> std::string LuaTableNode::getDefault<std::string>(const std::string & default_value) +{ + std::string result = default_value; + + if (stackQueryValue() && lua_isstring(luaTable->L, -1)) { + result = lua_tostring (luaTable->L, -1); + } + + stackRestore(); + + return result; +} + +template<> void LuaTableNode::set<bool>(const bool & value) +{ + stackCreateValue(); + + l_push_LuaKey (luaTable->L, key); + lua_pushboolean(luaTable->L, value); + // stack: parent, key, value + lua_settable (luaTable->L, -3); + + stackRestore(); +} + +template<> void LuaTableNode::set<float>(const float & value) +{ + stackCreateValue(); + + l_push_LuaKey (luaTable->L, key); + lua_pushnumber(luaTable->L, static_cast<double>(value)); + // stack: parent, key, value + lua_settable (luaTable->L, -3); + + stackRestore(); +} + +template<> void LuaTableNode::set<double>(const double & value) +{ + stackCreateValue(); + + l_push_LuaKey (luaTable->L, key); + lua_pushnumber(luaTable->L, value); + // stack: parent, key, value + lua_settable (luaTable->L, -3); + + stackRestore(); +} + +template<> void LuaTableNode::set<std::string>(const std::string & value) +{ + stackCreateValue(); + + l_push_LuaKey (luaTable->L, key); + lua_pushstring(luaTable->L, value.c_str()); + // stack: parent, key, value + lua_settable (luaTable->L, -3); + + stackRestore(); +} + +// +// LuaTable +// +LuaTable::~LuaTable() +{ + if (deleteLuaState) { + lua_close(L); + L = NULL; + } +} + +int LuaTable::length() +{ + if ((lua_gettop(L) == 0) || (lua_type (L, -1) != LUA_TTABLE)) { + cerr << "Error: cannot query table length. No table on stack!" << endl; + abort(); + } + size_t result = 0; + + result = lua_objlen(L, -1); + + return (int)result; +} + +LuaTable & LuaTable::operator= (const LuaTable & luatable) +{ + if (this != &luatable) { + if (deleteLuaState && L != luatable.L) { + lua_close (luatable.L); + } + filename = luatable.filename; + L = luatable.L; + deleteLuaState = luatable.deleteLuaState; + } + + return *this; +} + +LuaTable LuaTable::fromFile (const char* _filename) +{ + LuaTable result; + + result.filename = _filename; + result.L = luaL_newstate(); + result.deleteLuaState = true; + luaL_openlibs(result.L); + + // Add the directory of _filename to package.path + result.addSearchPath(get_file_directory (_filename).c_str()); + + // run the file we + if (luaL_dofile (result.L, _filename)) { + bail (result.L, "Error running file: "); + } + + return result; +} + +LuaTable LuaTable::fromLuaExpression (const char* lua_expr) +{ + LuaTable result; + + result.L = luaL_newstate(); + result.deleteLuaState = true; + luaL_openlibs(result.L); + + if (luaL_loadstring (result.L, lua_expr)) { + bail (result.L, "Error compiling expression!"); + } + + if (lua_pcall (result.L, 0, LUA_MULTRET, 0)) { + bail (result.L, "Error running expression!"); + } + + return result; +} + +LuaTable LuaTable::fromLuaState (lua_State* L) +{ + LuaTable result; + + result.L = L; + result.deleteLuaState = false; + + return result; +} + +void LuaTable::addSearchPath(const char* path) +{ + if (L == NULL) { + cerr << "Error: Cannot add search path: Lua state is not initialized!" << endl; + abort(); + } + + lua_getglobal(L, "package"); + lua_getfield (L, -1, "path"); + if (lua_type(L, -1) != LUA_TSTRING) { + cerr << "Error: could not get package.path!" << endl; + abort(); + } + + string package_path = lua_tostring (L, -1); + package_path = package_path + string(path) + "?.lua;"; + + lua_pushstring(L, package_path.c_str()); + lua_setfield (L, -3, "path"); + + lua_pop(L, 2); +} + +std::string LuaTable::serialize() +{ + std::string result; + + int current_top = lua_gettop(L); + if (lua_gettop(L) != 0) { + if (luaL_loadstring(L, serialize_std)) { + bail (L, "Error loading serialization function: "); + } + + if (lua_pcall(L, 0, 0, 0)) { + bail (L, "Error compiling serialization function: " ); + } + + lua_getglobal (L, "serialize"); + assert (lua_isfunction (L, -1)); + lua_pushvalue (L, -2); + if (lua_pcall (L, 1, 1, 0)) { + bail (L, "Error while serializing: "); + } + result = string("return ") + lua_tostring (L, -1); + } else { + cerr << "Cannot serialize global Lua state!" << endl; + abort(); + } + + lua_pop (L, lua_gettop(L) - current_top); + + return result; +} + +std::string LuaTable::orderedSerialize() +{ + std::string result; + + int current_top = lua_gettop(L); + if (lua_gettop(L) != 0) { + if (luaL_loadstring(L, serialize_std)) { + bail (L, "Error loading serialization function: "); + } + + if (lua_pcall(L, 0, 0, 0)) { + bail (L, "Error compiling serialization function: " ); + } + + lua_getglobal (L, "serialize"); + assert (lua_isfunction (L, -1)); + lua_pushvalue (L, -2); + lua_pushstring (L, ""); + lua_pushboolean (L, true); + if (lua_pcall (L, 3, 1, 0)) { + bail (L, "Error while serializing: "); + } + result = string("return ") + lua_tostring (L, -1); + } else { + cerr << "Cannot serialize global Lua state!" << endl; + abort(); + } + + lua_pop (L, lua_gettop(L) - current_top); + + return result; +} diff --git a/src/multibody/parser/lua/lua_tables.hpp b/src/multibody/parser/lua/lua_tables.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cc814a4b3275b2ee55051161ca3db290e35f644b --- /dev/null +++ b/src/multibody/parser/lua/lua_tables.hpp @@ -0,0 +1,223 @@ +/* + * LuaTables++ + * Code comming from https://bitbucket.org/rbdl/rbdl + * Copyright (c) 2013-2014 Martin Felis <martin@fyxs.org> + * Copyright (c) 2015 Justin Carpentier <jcarpent@laas.fr> + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __se3_parser_lua_tables_hpp____ +#define __se3_parser_lua_tables_hpp____ + +#include <iostream> +#include <assert.h> +#include <cstdlib> +#include <string> +#include <vector> + + +// Forward declaration for Lua +extern "C" { + struct lua_State; +} + +struct LuaKey { + enum Type { + String, + Integer + }; + + Type type; + int int_value; + std::string string_value; + + bool operator<( const LuaKey & rhs ) const { + if (type == String && rhs.type == Integer) { + return false; + } else if (type == Integer && rhs.type == String) { + return true; + } else if (type == Integer && rhs.type == Integer) { + return int_value < rhs.int_value; + } + + return string_value < rhs.string_value; + } + + LuaKey (const char* key_value) : + type (String), + int_value (0), + string_value (key_value) { } + LuaKey (int key_value) : + type (Integer), + int_value (key_value), + string_value ("") {} +}; + +inline std::ostream & operator<<(std::ostream & output, const LuaKey & key) { + if (key.type == LuaKey::Integer) + output << key.int_value << "(int)"; + else + output << key.string_value << "(string)"; + return output; +} +struct LuaTable; +struct LuaTableNode; + +struct LuaTableNode { + LuaTableNode() : + parent (NULL), + luaTable (NULL), + key("") + { } + LuaTableNode operator[](const char* child_str) { + LuaTableNode child_node; + + child_node.luaTable = luaTable; + child_node.parent = this; + child_node.key = LuaKey (child_str); + + return child_node; + } + LuaTableNode operator[](int child_index) { + LuaTableNode child_node; + + child_node.luaTable = luaTable; + child_node.parent = this; + child_node.key = LuaKey (child_index); + + return child_node; + } + bool stackQueryValue(); + void stackPushKey(); + void stackCreateValue(); + void stackRestore(); + LuaTable stackQueryTable(); + LuaTable stackCreateLuaTable(); + + std::vector<LuaKey> getKeyStack(); + std::string keyStackToString(); + + bool exists(); + void remove(); + size_t length(); + std::vector<LuaKey> keys(); + + // Templates for setters and getters. Can be specialized for custom + // types. + template <typename T> + void set (const T & value); + template <typename T> + T getDefault (const T & default_value); + + template <typename T> + T get() { + if (!exists()) { + std::cerr << "Error: could not find value " << keyStackToString() << "." << std::endl; + abort(); + } + return getDefault (T()); + } + + // convenience operators (assignment, conversion, comparison) + template <typename T> + void operator=(const T & value) { + set<T>(value); + } + template <typename T> + operator T() { + return get<T>(); + } + template <typename T> + bool operator==(T value) { + return value == get<T>(); + } + template <typename T> + bool operator!=(T value) { + return value != get<T>(); + } + + LuaTableNode *parent; + LuaTable *luaTable; + LuaKey key; + int stackTop; +}; + +template<typename T> +bool operator==(T value, LuaTableNode node) { + return value == (T) node; +} +template<typename T> +bool operator!=(T value, LuaTableNode node) { + return value != (T) node; +} + +template<> bool LuaTableNode::getDefault<bool>(const bool & default_value); +template<> double LuaTableNode::getDefault<double>(const double & default_value); +template<> float LuaTableNode::getDefault<float>(const float & default_value); +template<> std::string LuaTableNode::getDefault<std::string>(const std::string & default_value); + +template<> void LuaTableNode::set<bool>(const bool & value); +template<> void LuaTableNode::set<float>(const float & value); +template<> void LuaTableNode::set<double>(const double & value); +template<> void LuaTableNode::set<std::string>(const std::string & value); + +struct LuaTable { + LuaTable() : + filename (""), + L (NULL), + deleteLuaState (false) + {} + LuaTable & operator= (const LuaTable & luatable); + ~LuaTable(); + + LuaTableNode operator[] (const char* key) { + LuaTableNode root_node; + root_node.key = LuaKey (key); + root_node.parent = NULL; + root_node.luaTable = this; + + return root_node; + } + LuaTableNode operator[] (int key) { + LuaTableNode root_node; + root_node.key = LuaKey (key); + root_node.parent = NULL; + root_node.luaTable = this; + + return root_node; + } + int length(); + void addSearchPath (const char* path); + std::string serialize (); + std::string orderedSerialize (); + + static LuaTable fromFile (const char *_filename); + static LuaTable fromLuaExpression (const char* lua_expr); + static LuaTable fromLuaState (lua_State *L); + + std::string filename; + lua_State *L; + bool deleteLuaState; +}; + + +#endif // ifndef __se3_parser_lua_tables_hpp____