00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "cartographer/common/lua_parameter_dictionary.h"
00031
00032 #include <algorithm>
00033 #include <cmath>
00034 #include <functional>
00035 #include <memory>
00036
00037 namespace cartographer {
00038 namespace common {
00039
00040 namespace {
00041
00042
00043
00044 void QuoteStringOnStack(lua_State* L) {
00045 CHECK(lua_isstring(L, -1)) << "Top of stack is not a string value.";
00046 int current_index = lua_gettop(L);
00047
00048
00049 lua_pushglobaltable(L);
00050 lua_getfield(L, -1, "string");
00051 lua_getfield(L, -1,
00052 "format");
00053 lua_pushstring(L, "%q");
00054 lua_pushvalue(L, current_index);
00055
00056
00057 lua_call(L, 2, 1);
00058 lua_replace(L, current_index);
00059
00060 lua_pop(L, 2);
00061 }
00062
00063
00064
00065 void SetDictionaryInRegistry(lua_State* L, LuaParameterDictionary* dictionary) {
00066 lua_pushstring(L, "this");
00067 lua_pushlightuserdata(L, dictionary);
00068 lua_settable(L, LUA_REGISTRYINDEX);
00069 }
00070
00071
00072 LuaParameterDictionary* GetDictionaryFromRegistry(lua_State* L) {
00073 lua_pushstring(L, "this");
00074 lua_gettable(L, LUA_REGISTRYINDEX);
00075 void* return_value = lua_isnil(L, -1) ? nullptr : lua_touserdata(L, -1);
00076 lua_pop(L, 1);
00077 CHECK(return_value != nullptr);
00078 return reinterpret_cast<LuaParameterDictionary*>(return_value);
00079 }
00080
00081
00082 void CheckForLuaErrors(lua_State* L, int status) {
00083 CHECK_EQ(status, 0) << lua_tostring(L, -1);
00084 }
00085
00086
00087 int LuaChoose(lua_State* L) {
00088 CHECK_EQ(lua_gettop(L), 3) << "choose() takes (condition, a, b).";
00089 CHECK(lua_isboolean(L, 1)) << "condition is not a boolean value.";
00090
00091 const bool condition = lua_toboolean(L, 1);
00092 if (condition) {
00093 lua_pushvalue(L, 2);
00094 } else {
00095 lua_pushvalue(L, 3);
00096 }
00097 return 1;
00098 }
00099
00100
00101 void PushValue(lua_State* L, const int key) { lua_pushinteger(L, key); }
00102 void PushValue(lua_State* L, const std::string& key) {
00103 lua_pushstring(L, key.c_str());
00104 }
00105
00106
00107
00108 template <typename T>
00109 void GetValueFromLuaTable(lua_State* L, const T& key) {
00110 PushValue(L, key);
00111 lua_rawget(L, -2);
00112 }
00113
00114
00115 void CheckTableIsAtTopOfStack(lua_State* L) {
00116 CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
00117 }
00118
00119
00120 template <typename T>
00121 bool HasKeyOfType(lua_State* L, const T& key) {
00122 CheckTableIsAtTopOfStack(L);
00123 PushValue(L, key);
00124 lua_rawget(L, -2);
00125 const bool key_not_found = lua_isnil(L, -1);
00126 lua_pop(L, 1);
00127 return !key_not_found;
00128 }
00129
00130
00131
00132
00133 void GetArrayValues(lua_State* L, const std::function<void()>& pop_value) {
00134 int idx = 1;
00135 while (true) {
00136 GetValueFromLuaTable(L, idx);
00137 if (lua_isnil(L, -1)) {
00138 lua_pop(L, 1);
00139 break;
00140 }
00141 pop_value();
00142 ++idx;
00143 }
00144 }
00145
00146 }
00147
00148 std::unique_ptr<LuaParameterDictionary>
00149 LuaParameterDictionary::NonReferenceCounted(
00150 const std::string& code, std::unique_ptr<FileResolver> file_resolver) {
00151 return std::unique_ptr<LuaParameterDictionary>(new LuaParameterDictionary(
00152 code, ReferenceCount::NO, std::move(file_resolver)));
00153 }
00154
00155 LuaParameterDictionary::LuaParameterDictionary(
00156 const std::string& code, std::unique_ptr<FileResolver> file_resolver)
00157 : LuaParameterDictionary(code, ReferenceCount::YES,
00158 std::move(file_resolver)) {}
00159
00160 LuaParameterDictionary::LuaParameterDictionary(
00161 const std::string& code, ReferenceCount reference_count,
00162 std::unique_ptr<FileResolver> file_resolver)
00163 : L_(luaL_newstate()),
00164 index_into_reference_table_(-1),
00165 file_resolver_(std::move(file_resolver)),
00166 reference_count_(reference_count) {
00167 CHECK_NOTNULL(L_);
00168 SetDictionaryInRegistry(L_, this);
00169
00170 luaL_openlibs(L_);
00171
00172 lua_register(L_, "choose", LuaChoose);
00173 lua_register(L_, "include", LuaInclude);
00174 lua_register(L_, "read", LuaRead);
00175
00176 CheckForLuaErrors(L_, luaL_loadstring(L_, code.c_str()));
00177 CheckForLuaErrors(L_, lua_pcall(L_, 0, 1, 0));
00178 CheckTableIsAtTopOfStack(L_);
00179 }
00180
00181 LuaParameterDictionary::LuaParameterDictionary(
00182 lua_State* const L, ReferenceCount reference_count,
00183 std::shared_ptr<FileResolver> file_resolver)
00184 : L_(lua_newthread(L)),
00185 file_resolver_(std::move(file_resolver)),
00186 reference_count_(reference_count) {
00187 CHECK_NOTNULL(L_);
00188
00189
00190 CHECK(lua_isthread(L, -1));
00191 index_into_reference_table_ = luaL_ref(L, LUA_REGISTRYINDEX);
00192
00193 CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
00194 lua_xmove(L, L_, 1);
00195 CheckTableIsAtTopOfStack(L_);
00196 }
00197
00198 LuaParameterDictionary::~LuaParameterDictionary() {
00199 if (reference_count_ == ReferenceCount::YES) {
00200 CheckAllKeysWereUsedExactlyOnceAndReset();
00201 }
00202 if (index_into_reference_table_ > 0) {
00203 luaL_unref(L_, LUA_REGISTRYINDEX, index_into_reference_table_);
00204 } else {
00205 lua_close(L_);
00206 }
00207 }
00208
00209 std::vector<std::string> LuaParameterDictionary::GetKeys() const {
00210 CheckTableIsAtTopOfStack(L_);
00211 std::vector<std::string> keys;
00212
00213 lua_pushnil(L_);
00214 while (lua_next(L_, -2) != 0) {
00215 lua_pop(L_, 1);
00216 if (!lua_isnumber(L_, -1)) {
00217 keys.emplace_back(lua_tostring(L_, -1));
00218 }
00219 }
00220 return keys;
00221 }
00222
00223 bool LuaParameterDictionary::HasKey(const std::string& key) const {
00224 return HasKeyOfType(L_, key);
00225 }
00226
00227 std::string LuaParameterDictionary::GetString(const std::string& key) {
00228 CheckHasKeyAndReference(key);
00229 GetValueFromLuaTable(L_, key);
00230 return PopString(Quoted::NO);
00231 }
00232
00233 std::string LuaParameterDictionary::PopString(Quoted quoted) const {
00234 CHECK(lua_isstring(L_, -1)) << "Top of stack is not a string value.";
00235 if (quoted == Quoted::YES) {
00236 QuoteStringOnStack(L_);
00237 }
00238
00239 const std::string value = lua_tostring(L_, -1);
00240 lua_pop(L_, 1);
00241 return value;
00242 }
00243
00244 double LuaParameterDictionary::GetDouble(const std::string& key) {
00245 CheckHasKeyAndReference(key);
00246 GetValueFromLuaTable(L_, key);
00247 return PopDouble();
00248 }
00249
00250 double LuaParameterDictionary::PopDouble() const {
00251 CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
00252 const double value = lua_tonumber(L_, -1);
00253 lua_pop(L_, 1);
00254 return value;
00255 }
00256
00257 int LuaParameterDictionary::GetInt(const std::string& key) {
00258 CheckHasKeyAndReference(key);
00259 GetValueFromLuaTable(L_, key);
00260 return PopInt();
00261 }
00262
00263 int LuaParameterDictionary::PopInt() const {
00264 CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
00265 const int value = lua_tointeger(L_, -1);
00266 lua_pop(L_, 1);
00267 return value;
00268 }
00269
00270 bool LuaParameterDictionary::GetBool(const std::string& key) {
00271 CheckHasKeyAndReference(key);
00272 GetValueFromLuaTable(L_, key);
00273 return PopBool();
00274 }
00275
00276 bool LuaParameterDictionary::PopBool() const {
00277 CHECK(lua_isboolean(L_, -1)) << "Top of stack is not a boolean value.";
00278 const bool value = lua_toboolean(L_, -1);
00279 lua_pop(L_, 1);
00280 return value;
00281 }
00282
00283 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::GetDictionary(
00284 const std::string& key) {
00285 CheckHasKeyAndReference(key);
00286 GetValueFromLuaTable(L_, key);
00287 return PopDictionary(reference_count_);
00288 }
00289
00290 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::PopDictionary(
00291 ReferenceCount reference_count) const {
00292 CheckTableIsAtTopOfStack(L_);
00293 std::unique_ptr<LuaParameterDictionary> value(
00294 new LuaParameterDictionary(L_, reference_count, file_resolver_));
00295
00296 CheckTableIsAtTopOfStack(L_);
00297 return value;
00298 }
00299
00300 std::string LuaParameterDictionary::DoToString(
00301 const std::string& indent) const {
00302 std::string result = "{";
00303 bool dictionary_is_empty = true;
00304
00305 const auto top_of_stack_to_string = [this, indent,
00306 &dictionary_is_empty]() -> std::string {
00307 dictionary_is_empty = false;
00308
00309 const int value_type = lua_type(L_, -1);
00310 switch (value_type) {
00311 case LUA_TBOOLEAN:
00312 return PopBool() ? "true" : "false";
00313 break;
00314 case LUA_TSTRING:
00315 return PopString(Quoted::YES);
00316 break;
00317 case LUA_TNUMBER: {
00318 const double value = PopDouble();
00319 if (std::isinf(value)) {
00320 return value < 0 ? "-math.huge" : "math.huge";
00321 } else {
00322 return std::to_string(value);
00323 }
00324 } break;
00325 case LUA_TTABLE: {
00326 std::unique_ptr<LuaParameterDictionary> subdict(
00327 PopDictionary(ReferenceCount::NO));
00328 return subdict->DoToString(indent + " ");
00329 } break;
00330 default:
00331 LOG(FATAL) << "Unhandled type " << lua_typename(L_, value_type);
00332 }
00333 };
00334
00335
00336 for (int i = 1; i; ++i) {
00337 GetValueFromLuaTable(L_, i);
00338 if (lua_isnil(L_, -1)) {
00339 lua_pop(L_, 1);
00340 break;
00341 }
00342 result.append("\n");
00343 result.append(indent);
00344 result.append(" ");
00345 result.append(top_of_stack_to_string());
00346 result.append(",");
00347 }
00348
00349
00350 std::vector<std::string> keys = GetKeys();
00351 if (!keys.empty()) {
00352 std::sort(keys.begin(), keys.end());
00353 for (const std::string& key : keys) {
00354 GetValueFromLuaTable(L_, key);
00355 result.append("\n");
00356 result.append(indent);
00357 result.append(" ");
00358 result.append(key);
00359 result.append(" = ");
00360 result.append(top_of_stack_to_string());
00361 result.append(",");
00362 }
00363 }
00364 result.append("\n");
00365 result.append(indent);
00366 result.append("}");
00367
00368 if (dictionary_is_empty) {
00369 return "{}";
00370 }
00371 return result;
00372 }
00373
00374 std::string LuaParameterDictionary::ToString() const { return DoToString(""); }
00375
00376 std::vector<double> LuaParameterDictionary::GetArrayValuesAsDoubles() {
00377 std::vector<double> values;
00378 GetArrayValues(L_, [&values, this] { values.push_back(PopDouble()); });
00379 return values;
00380 }
00381
00382 std::vector<std::unique_ptr<LuaParameterDictionary>>
00383 LuaParameterDictionary::GetArrayValuesAsDictionaries() {
00384 std::vector<std::unique_ptr<LuaParameterDictionary>> values;
00385 GetArrayValues(L_, [&values, this] {
00386 values.push_back(PopDictionary(reference_count_));
00387 });
00388 return values;
00389 }
00390
00391 std::vector<std::string> LuaParameterDictionary::GetArrayValuesAsStrings() {
00392 std::vector<std::string> values;
00393 GetArrayValues(L_,
00394 [&values, this] { values.push_back(PopString(Quoted::NO)); });
00395 return values;
00396 }
00397
00398 void LuaParameterDictionary::CheckHasKey(const std::string& key) const {
00399 CHECK(HasKey(key)) << "Key '" << key << "' not in dictionary:\n"
00400 << ToString();
00401 }
00402
00403 void LuaParameterDictionary::CheckHasKeyAndReference(const std::string& key) {
00404 CheckHasKey(key);
00405 reference_counts_[key]++;
00406 }
00407
00408 void LuaParameterDictionary::CheckAllKeysWereUsedExactlyOnceAndReset() {
00409 for (const auto& key : GetKeys()) {
00410 CHECK_EQ(1, reference_counts_.count(key))
00411 << "Key '" << key << "' was used the wrong number of times.";
00412 CHECK_EQ(1, reference_counts_.at(key))
00413 << "Key '" << key << "' was used the wrong number of times.";
00414 }
00415 reference_counts_.clear();
00416 }
00417
00418 int LuaParameterDictionary::GetNonNegativeInt(const std::string& key) {
00419 const int temp = GetInt(key);
00420 CHECK_GE(temp, 0) << temp << " is negative.";
00421 return temp;
00422 }
00423
00424
00425
00426 int LuaParameterDictionary::LuaInclude(lua_State* L) {
00427 CHECK_EQ(lua_gettop(L), 1);
00428 CHECK(lua_isstring(L, -1)) << "include takes a filename.";
00429
00430 LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
00431 const std::string basename = lua_tostring(L, -1);
00432 const std::string filename =
00433 parameter_dictionary->file_resolver_->GetFullPathOrDie(basename);
00434 if (std::find(parameter_dictionary->included_files_.begin(),
00435 parameter_dictionary->included_files_.end(),
00436 filename) != parameter_dictionary->included_files_.end()) {
00437 std::string error_msg =
00438 "Tried to include " + filename +
00439 " twice. Already included files in order of inclusion: ";
00440 for (const std::string& filename : parameter_dictionary->included_files_) {
00441 error_msg.append(filename);
00442 error_msg.append("\n");
00443 }
00444 LOG(FATAL) << error_msg;
00445 }
00446 parameter_dictionary->included_files_.push_back(filename);
00447 lua_pop(L, 1);
00448 CHECK_EQ(lua_gettop(L), 0);
00449
00450 const std::string content =
00451 parameter_dictionary->file_resolver_->GetFileContentOrDie(basename);
00452 CheckForLuaErrors(
00453 L, luaL_loadbuffer(L, content.c_str(), content.size(), filename.c_str()));
00454 CheckForLuaErrors(L, lua_pcall(L, 0, LUA_MULTRET, 0));
00455
00456 return lua_gettop(L);
00457 }
00458
00459
00460 int LuaParameterDictionary::LuaRead(lua_State* L) {
00461 CHECK_EQ(lua_gettop(L), 1);
00462 CHECK(lua_isstring(L, -1)) << "read takes a filename.";
00463
00464 LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
00465 const std::string file_content =
00466 parameter_dictionary->file_resolver_->GetFileContentOrDie(
00467 lua_tostring(L, -1));
00468 lua_pushstring(L, file_content.c_str());
00469 return 1;
00470 }
00471
00472 }
00473 }